From: (no author) <(no author)@unknown> Date: Fri, 5 May 2000 23:28:26 +0000 (+0000) Subject: This commit was manufactured by cvs2svn to create branch 'avendor'. X-Git-Tag: INITIAL~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9003fde19f9d9aefafa4dde58077596b5b807507;p=thirdparty%2Fapache%2Fhttpd.git This commit was manufactured by cvs2svn to create branch 'avendor'. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/avendor@85155 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 00000000000..564d9c3a255 --- /dev/null +++ b/.gdbinit @@ -0,0 +1,28 @@ +# gdb macros which may be useful for folks using gdb to debug +# apache. Delete it if it bothers you. + +define dump_table + set $t = (table_entry *)((array_header *)$arg0)->elts + set $n = ((array_header *)$arg0)->nelts + set $i = 0 + while $i < $n + printf "[%u] '%s'='%s'\n", $i, $t[$i].key, $t[$i].val + set $i = $i + 1 + end +end +document dump_table + Print the key/value pairs in a table. +end + +define dump_string_array + set $a = (char **)((array_header *)$arg0)->elts + set $n = (int)((array_header *)$arg0)->nelts + set $i = 0 + while $i < $n + printf "[%u] '%s'\n", $i, $a[$i] + set $i = $i + 1 + end +end +document dump_string_array + Print all of the elements in an array of strings. +end diff --git a/Apache.dsp b/Apache.dsp new file mode 100644 index 00000000000..bbb644ded95 --- /dev/null +++ b/Apache.dsp @@ -0,0 +1,109 @@ +# Microsoft Developer Studio Project File - Name="Apache" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Apache - 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 "Apache.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 "Apache.mak" CFG="Apache - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Apache - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Apache - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Apache - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir ".\ApacheLa" +# PROP BASE Intermediate_Dir ".\ApacheLa" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\ApacheR" +# PROP Intermediate_Dir ".\ApacheR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /FD /c +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "Apache - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\ApacheL0" +# PROP BASE Intermediate_Dir ".\ApacheL0" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\ApacheD" +# PROP Intermediate_Dir ".\ApacheD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /FD /c +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 +# ADD LINK32 CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "Apache - Win32 Release" +# Name "Apache - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\os\win32\main_win32.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\os\win32\apache.ico +# End Source File +# Begin Source File + +SOURCE=.\os\win32\apache.rc +# End Source File +# End Group +# End Target +# End Project diff --git a/Apache.dsw b/Apache.dsw new file mode 100644 index 00000000000..7f33c59a9e9 --- /dev/null +++ b/Apache.dsw @@ -0,0 +1,460 @@ +Microsoft Developer Studio Workspace File, Format Version 5.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Apache"=".\Apache.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheCore"=".\ApacheCore.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ap + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency + Begin Project Dependency + Project_Dep_Name regex + End Project Dependency + Begin Project Dependency + Project_Dep_Name gen_test_char + End Project Dependency + Begin Project Dependency + Project_Dep_Name gen_uri_delims + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleAuthAnon"=".\os\win32\ApacheModuleAuthAnon.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleCERNMeta"=".\os\win32\ApacheModuleCERNMeta.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleDigest"=".\os\win32\ApacheModuleDigest.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleExpires"=".\os\win32\ApacheModuleExpires.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleHeaders"=".\os\win32\ApacheModuleHeaders.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleInfo"=".\os\win32\ApacheModuleInfo.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleProxy"=".\modules\proxy\ApacheModuleProxy.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleRewrite"=".\os\win32\ApacheModuleRewrite.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleSpeling"=".\os\win32\ApacheModuleSpeling.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleStatus"=".\os\win32\ApacheModuleStatus.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "ApacheModuleUserTrack"=".\os\win32\ApacheModuleUserTrack.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency +}}} + +############################################################################### + +Project: "InstallBin"=".\InstallBin.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name Apache + End Project Dependency + Begin Project Dependency + Project_Dep_Name ApacheCore + End Project Dependency + Begin Project Dependency + Project_Dep_Name ApacheModuleAuthAnon + End Project Dependency + Begin Project Dependency + Project_Dep_Name ApacheModuleCERNMeta + End Project Dependency + Begin Project Dependency + Project_Dep_Name ApacheModuleDigest + End Project Dependency + Begin Project Dependency + Project_Dep_Name ApacheModuleExpires + End Project Dependency + Begin Project Dependency + Project_Dep_Name ApacheModuleHeaders + End Project Dependency + Begin Project Dependency + Project_Dep_Name ApacheModuleRewrite + End Project Dependency + Begin Project Dependency + Project_Dep_Name ApacheModuleSpeling + End Project Dependency + Begin Project Dependency + Project_Dep_Name ApacheModuleStatus + End Project Dependency + Begin Project Dependency + Project_Dep_Name ApacheModuleUserTrack + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlibdll + End Project Dependency + Begin Project Dependency + Project_Dep_Name htpasswd + End Project Dependency + Begin Project Dependency + Project_Dep_Name rotatelogs + End Project Dependency +}}} + +############################################################################### + +Project: "ap"=".\ap\ap.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "aprlib"=".\lib\apr\aprlib.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "aprlibdll"=".\lib\apr\aprlibdll.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name aprlib + End Project Dependency +}}} + +############################################################################### + +Project: "gen_test_char"=".\main\gen_test_char.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "gen_uri_delims"=".\main\gen_uri_delims.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "htdigest"=".\support\htdigest.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name aprlib + End Project Dependency +}}} + +############################################################################### + +Project: "htpasswd"=".\support\htpasswd.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name ap + End Project Dependency + Begin Project Dependency + Project_Dep_Name aprlib + End Project Dependency +}}} + +############################################################################### + +Project: "install"=".\os\win32\installer\installdll\install.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name aprlib + End Project Dependency +}}} + +############################################################################### + +Project: "logresolve"=".\support\logresolve.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "regex"=".\regex\regex.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "rotatelogs"=".\support\rotatelogs.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### diff --git a/InstallBin.dsp b/InstallBin.dsp new file mode 100644 index 00000000000..cb5b7c67b9f --- /dev/null +++ b/InstallBin.dsp @@ -0,0 +1,96 @@ +# Microsoft Developer Studio Project File - Name="InstallBin" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=InstallBin - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "InstallBin.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 "InstallBin.mak" CFG="InstallBin - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "InstallBin - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "InstallBin - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "InstallBin - Win32 Release" + +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f InstallBin.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "InstallBin.exe" +# PROP BASE Bsc_Name "InstallBin.bsc" +# PROP BASE Target_Dir "" +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Cmd_Line "NMAKE /f makefile.win INSTDIR=.. SHORT=D LONG=Debug _install" +# PROP Rebuild_Opt "" +# PROP Target_File "..\Apache.exe" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "InstallBin - Win32 Debug" + +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Cmd_Line "NMAKE /f InstallBin.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "InstallBin.exe" +# PROP BASE Bsc_Name "InstallBin.bsc" +# PROP BASE Target_Dir "" +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Cmd_Line "NMAKE /f makefile.win INSTDIR=.. SHORT=D LONG=Debug _install" +# PROP Rebuild_Opt "" +# PROP Target_File "..\Apache.exe" +# PROP Bsc_Name ".\apache.bsc" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "InstallBin - Win32 Release" +# Name "InstallBin - Win32 Debug" + +!IF "$(CFG)" == "InstallBin - Win32 Release" + +!ELSEIF "$(CFG)" == "InstallBin - Win32 Debug" + +!ENDIF + +# Begin Source File + +SOURCE=..\logs\access.log +# End Source File +# Begin Source File + +SOURCE=..\logs\error.log +# End Source File +# Begin Source File + +SOURCE=..\conf\httpd.conf +# End Source File +# Begin Source File + +SOURCE=.\Makefile.win +# End Source File +# End Target +# End Project diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..6ac6538b3ca --- /dev/null +++ b/LICENSE @@ -0,0 +1,59 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + + diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 00000000000..7892c08c008 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,73 @@ + +DEPTH = . +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +SUBDIRS = ap lib main modules os $(REGEX_DIR) + +PROGRAM_NAME = apache +PROGRAM_SOURCES = modules.c buildmark.c +PROGRAM_LDADD = $(EXTRA_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(EXTRA_LIBS) +PROGRAM_DEPENDENCIES = \ + $(BUILTIN_LIBS) \ + $(MPM_LIB) \ + main/libmain.la \ + $(OS_DIR)/libos.la \ + ap/libap.la \ + lib/apr/libapr.a \ + $(REGEX_LIB) + +PROGRAMS = $(PROGRAM_NAME) +targets = $(PROGRAMS) +phony_targets = buildmark.c +install_targets = install-conf install-htdocs install-icons install-other \ + install-cgi install-include + +include $(topsrcdir)/build/rules.mk +include $(topsrcdir)/build/program.mk + +install-conf: + @echo Installing configuration files + @test -d $(sysconfdir) || $(mkinstalldirs) $(sysconfdir) + @cd $(srcdir)/../conf; \ + for i in mime.types magic; do \ + $(INSTALL_DATA) $$i $(sysconfdir); \ + done; \ + for i in *-dist; do \ + sed -e 's#@@ServerRoot@@#$(prefix)#g' \ + < $$i > $(sysconfdir)/$$i; \ + chmod 0644 $(sysconfdir)/$$i; \ + file=`echo $$i|sed s/-dist//`; \ + if test "$$file" != "$$i" && test ! -f $(sysconfdir)/$$file; then \ + $(INSTALL_DATA) $(sysconfdir)/$$i $(sysconfdir)/$$file; \ + fi; \ + done + +install-htdocs: + @echo Installing HTML documents + @test -d $(htdocsdir) || $(mkinstalldirs) $(htdocsdir) + @cp -rp $(srcdir)/../htdocs/* $(htdocsdir) + +install-icons: + @echo Installing icons + @test -d $(iconsdir) || $(mkinstalldirs) $(iconsdir) + @cp -rp $(srcdir)/../icons/* $(iconsdir) + +install-cgi: + @echo Installing CGIs + @test -d $(cgidir) || $(mkinstalldirs) $(cgidir) + @cp -rp $(srcdir)/../cgi-bin/* $(cgidir) + +install-other: + @test -d $(logdir) || $(mkinstalldirs) $(logdir) + +install-include: + @echo Installing header files + @test -d $(includedir) || $(mkinstalldirs) $(includedir) + @test -d $(includedir)/xml || $(mkinstalldirs) $(includedir)/xml + @test -d $(includedir)/apr || $(mkinstalldirs) $(includedir)/apr + @cp include/*.h $(srcdir)/include/*.h $(includedir) + @cp $(srcdir)/lib/expat-lite/*.h $(includedir)/xml + @cp lib/apr/include/*.h $(srcdir)/lib/apr/include/*.h $(includedir)/apr + @chmod 644 $(includedir)/*.h $(includedir)/xml/*.h $(includedir)/apr/*.h diff --git a/Makefile.win b/Makefile.win new file mode 100644 index 00000000000..6e7bf6c5a39 --- /dev/null +++ b/Makefile.win @@ -0,0 +1,166 @@ +# Makefile for Windows NT and Windows 95/98 + +# Targets are: +# _apacher - build Apache in Release mode +# _apached - build Apache in Debug mode +# installr - build and install a Release build +# installd - build and install a Debug build +# clean - remove (most) generated files +# _cleanr - remove (most) files generated by a Release build +# _cleand - remove (most) files generated by a Debug build +# +# The default installation directory is \Apache. This can be changed +# with the INSTDIR macro, for example: +# +# nmake /f Makefile.win INSTDIR="d:\Program Files\Apache" installr +# +# Note: this does *NOT* change the compiled in default "server root" + +!IF "$(INSTDIR)" == "" +INSTDIR=\Apache +!MESSAGE Using default install directory \Apache +!ENDIF + +default: _apacher + +_apacher: + $(MAKE) /f Makefile.win SHORT=R LONG=Release _build + +_apached: + $(MAKE) /f Makefile.win SHORT=D LONG=Debug _build + +installr: + $(MAKE) /f Makefile.win SHORT=R LONG=Release _build _install + +installd: + $(MAKE) /f Makefile.win SHORT=D LONG=Debug _build _install + +_cleanr: + $(MAKE) /f Makefile.win SHORT=R LONG=Release _clean + +_cleand: + $(MAKE) /f Makefile.win SHORT=D LONG=Debug _clean + +clean: + $(MAKE) /f Makefile.win _cleanr + $(MAKE) /f Makefile.win _cleand + +installdll: + $(MAKE) /f Makefile.win SHORT=R LONG=Release _installdll + +_build: + echo Building Win32 $(LONG) targets ($(SHORT) suffixes) + cd lib\apr + nmake /nologo CFG="aprlib - Win32 $(LONG)" -f aprlib.mak + cd ..\.. + cd os\win32 + nmake /nologo CFG="ApacheOS - Win32 $(LONG)" -f ApacheOS.mak + cd ..\.. + cd regex + nmake /nologo CFG="regex - Win32 $(LONG)" -f regex.mak + cd .. + cd ap + nmake /nologo CFG="ap - Win32 $(LONG)" -f ap.mak + cd .. + cd support +# nmake /nologo CFG="htpasswd - Win32 $(LONG)" -f htpasswd.mak +# nmake /nologo CFG="htdigest - Win32 $(LONG)" -f htdigest.mak + nmake /nologo CFG="rotatelogs - Win32 $(LONG)" -f rotatelogs.mak + cd .. + cd main + nmake /nologo CFG="gen_uri_delims - Win32 $(LONG)" -f gen_uri_delims.mak + nmake /nologo CFG="gen_test_char - Win32 $(LONG)" -f gen_test_char.mak + cd .. + -del Core$(SHORT)\buildmark.obj + nmake /nologo CFG="ApacheCore - Win32 $(LONG)" -f ApacheCore.mak + nmake /nologo CFG="Apache - Win32 $(LONG)" -f Apache.mak + cd os\win32 +# set CFG=ApacheModuleStatus - Win32 $(LONG) +# nmake /nologo CFG="ApacheModuleStatus - Win32 $(LONG)" -f ApacheModuleStatus.mak +# nmake /nologo CFG"=ApacheModuleInfo - Win32 $(LONG)" -f ApacheModuleInfo.mak + nmake /nologo CFG="ApacheModuleAuthAnon - Win32 $(LONG)" -f ApacheModuleAuthAnon.mak + nmake /nologo CFG="ApacheModuleDigest - Win32 $(LONG)" -f ApacheModuleDigest.mak + nmake /nologo CFG="ApacheModuleCERNMeta - Win32 $(LONG)" -f ApacheModuleCERNMeta.mak + nmake /nologo CFG="ApacheModuleExpires - Win32 $(LONG)" -f ApacheModuleExpires.mak + nmake /nologo CFG="ApacheModuleHeaders - Win32 $(LONG)" -f ApacheModuleHeaders.mak + nmake /nologo CFG="ApacheModuleSpeling - Win32 $(LONG)" -f ApacheModuleSpeling.mak + nmake /nologo CFG="ApacheModuleUserTrack - Win32 $(LONG)" -f ApacheModuleUserTrack.mak +# nmake /nologo CFG="ApacheModuleRewrite - Win32 $(LONG)" -f ApacheModuleRewrite.mak + cd ..\.. +# cd modules\proxy +# nmake /nologo CFG="ApacheModuleProxy - Win32 $(LONG)" -f ApacheModuleProxy.mak +# cd ..\.. + +_install: + -mkdir $(INSTDIR) + -mkdir $(INSTDIR)\modules + -mkdir $(INSTDIR)\logs + -mkdir $(INSTDIR)\conf + -mkdir $(INSTDIR)\bin + copy Apache$(SHORT)\Apache.exe $(INSTDIR) + copy Core$(SHORT)\ApacheCore.dll $(INSTDIR) + copy lib\apr\$(LONG)\aprlib.dll $(INSTDIR) +# copy os\win32\ApacheModuleStatus$(SHORT)\ApacheModuleStatus.dll $(INSTDIR)\modules +# copy os\win32\ApacheModuleInfo$(SHORT)\ApacheModuleInfo.dll $(INSTDIR)\modules + copy os\win32\ApacheModuleAuthAnon$(SHORT)\ApacheModuleAuthAnon.dll $(INSTDIR)\modules + copy os\win32\ApacheModuleDigest$(SHORT)\ApacheModuleDigest.dll $(INSTDIR)\modules + copy os\win32\ApacheModuleCERNMeta$(SHORT)\ApacheModuleCERNMeta.dll $(INSTDIR)\modules + copy os\win32\ApacheModuleExpires$(SHORT)\ApacheModuleExpires.dll $(INSTDIR)\modules + copy os\win32\ApacheModuleHeaders$(SHORT)\ApacheModuleHeaders.dll $(INSTDIR)\modules +# copy os\win32\ApacheModuleRewrite$(SHORT)\ApacheModuleRewrite.dll $(INSTDIR)\modules + copy os\win32\ApacheModuleSpeling$(SHORT)\ApacheModuleSpeling.dll $(INSTDIR)\modules + copy os\win32\ApacheModuleUserTrack$(SHORT)\ApacheModuleUserTrack.dll $(INSTDIR)\modules +# copy modules\proxy\$(LONG)\ApacheModuleProxy.dll $(INSTDIR)\modules +# copy support\$(LONG)\htpasswd.exe $(INSTDIR)\bin +# copy support\$(LONG)\htdigest.exe $(INSTDIR)\bin + copy support\$(LONG)\rotatelogs.exe $(INSTDIR)\bin + +_clean: + cd lib\apr + nmake /nologo CFG="aprlib - Win32 $(LONG)" -f aprlib.mak clean + cd ..\.. + cd os\win32 + nmake /nologo CFG="ApacheOS - Win32 $(LONG)" -f ApacheOS.mak clean + cd ..\.. + cd regex + nmake /nologo CFG="regex - Win32 $(LONG)" -f regex.mak clean + cd .. + cd ap + nmake /nologo CFG="ap - Win32 $(LONG)" -f ap.mak clean + cd .. + cd support + nmake /nologo CFG="htpasswd - Win32 $(LONG)" -f htpasswd.mak clean + nmake /nologo CFG="htdigest - Win32 $(LONG)" -f htdigest.mak clean + nmake /nologo CFG="rotatelogs - Win32 $(LONG)" -f rotatelogs.mak clean + cd .. + cd main + nmake /nologo CFG="gen_uri_delims - Win32 $(LONG)" -f gen_uri_delims.mak clean + del uri_delims.h + nmake /nologo CFG="gen_test_char - Win32 $(LONG)" -f gen_test_char.mak clean + del test_char.h + cd .. + nmake /nologo CFG="ApacheCore - Win32 $(LONG)" -f ApacheCore.mak clean + nmake /nologo CFG="Apache - Win32 $(LONG)" -f Apache.mak clean + cd os\win32 +# nmake /nologo CFG="ApacheModuleStatus - Win32 $(LONG)" -f ApacheModuleStatus.mak clean +# nmake /nologo CFG="ApacheModuleInfo - Win32 $(LONG)" -f ApacheModuleInfo.mak clean + nmake /nologo CFG="ApacheModuleAuthAnon - Win32 $(LONG)" -f ApacheModuleAuthAnon.mak clean + nmake /nologo CFG="ApacheModuleDigest - Win32 $(LONG)" -f ApacheModuleDigest.mak clean + nmake /nologo CFG="ApacheModuleCERNMeta - Win32 $(LONG)" -f ApacheModuleCERNMeta.mak clean + nmake /nologo CFG="ApacheModuleExpires - Win32 $(LONG)" -f ApacheModuleExpires.mak clean + nmake /nologo CFG="ApacheModuleHeaders - Win32 $(LONG)" -f ApacheModuleHeaders.mak clean + nmake /nologo CFG="ApacheModuleSpeling - Win32 $(LONG)" -f ApacheModuleSpeling.mak clean + nmake /nologo CFG="ApacheModuleUserTrack - Win32 $(LONG)" -f ApacheModuleUserTrack.mak clean +# nmake /nologo CFG="ApacheModuleRewrite - Win32 $(LONG)" -f ApacheModuleRewrite.mak clean + cd ..\.. +# cd modules\proxy +# nmake /nologo CFG="ApacheModuleProxy - Win32 $(LONG)" -f ApacheModuleProxy.mak clean +# cd ..\.. + cd os\win32\installer\installdll + nmake /nologo CFG="install - Win32 $(LONG)" -f install.mak clean + cd ..\..\.. + +_installdll: + cd os\win32\installer\installdll + nmake /nologo CFG="install - Win32 $(LONG)" -f install.mak + cd ..\..\.. diff --git a/STATUS b/STATUS new file mode 100644 index 00000000000..114c3cad34a --- /dev/null +++ b/STATUS @@ -0,0 +1,96 @@ +Apache 2.0 STATUS: +Last modified at [$Date: 1999/08/27 18:57:17 $] + +Release: + + 2.0: In pre-alpha development + +Plan: + + * Vague goal of an alpha or beta release in 1999. Commit-then-review + is active. + +RELEASE SHOWSTOPPERS: + + * CGI doesn't work + Status: Ben Laurie is working on this. + + * mod_status doesn't work + Status: Manoj Kasichainula is working on this. A + patch has been posted that starts to fix this: + (<19990825025653.A19065@samosa.mindspring.com> and + <19990825022146.A10096@io.com>) + + * suEXEC doesn't work + Ben Laurie's work to fix CGIs will also fix this. + + * Windows NT port isn't done + Status: Bill is working on this through his MPM work, and Ryan + is with APR. + + * Current 2.0 code is not tested on many Unix platforms. Make 2.0 + work on most, if not all the systems 1.3 did + + Status: Autoconf and APR will get us most of the way there. + + * The module API is a weird combination of the old table and the new + hook system. Switch completely to the new hook system + Status: Ben Laurie is working on this. + +RELEASE NON-SHOWSTOPPERS BUT WOULD BE REAL NICE TO WRAP THESE UP: + + (none listed) + +Other bugs that need fixing: + + * MaxRequestsPerChild measures connections, not requests. + + * Regex containers don't work in an intutive way + Status: No one has come up with an efficient way to fix this + behavior. Dean has suggested getting rid of regex containers + completely. + +Other features that need writing: + + * Switch to autoconf, et al. for configuration. + + * Use APR to get rid of more platform dependancies. + Status: Ryan Bloom is working on this. + + * Finish infrastructure in core for async MPMs + Status: ? + + * Work on other MPMs. Possible MPMs discussed or in progress include: + + - David Reid's Beos MPM + Status: in progress + + - Dean Gaudet's async MPM + Status: ? + + - Zach Brown's Linux siginfo MPM + Status: ? + +Documentation that needs writing: + + * The concept of MPMs, especially if we ship more than one MPM for a + given platform + + * New directives in the various MPMs + + * API documentation + Status: Ben Laurie has written some hooks documentation + (apache-2.0/htdocs/hooks.html) + +Available Patches: + + * Mike Abbott's patches to improve + performance + Status: These were written for 1.3, and are awaiting a port to + 2.0 + +Open issues: + + * What do we do about mod_proxy? + + * Which MPMs will be included with Apache 2.0? diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 00000000000..aa97f724ae4 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,59 @@ +dnl +dnl APACHE_MODULE(modname [, shared]) +dnl +dnl Includes an extension in the build. +dnl +dnl "modname" is the name of the modules/ subdir where the extension resides +dnl "shared" can be set to "shared" or "yes" to build the extension as +dnl a dynamically loadable library. +dnl +AC_DEFUN(APACHE_MODULE,[ + if test -d "$cwd/$srcdir/modules/$1" ; then + MOD_SUBDIRS="$MOD_SUBDIRS $1" + if test "$2" != "shared" -a "$2" != "yes"; then + libname=$(basename $1) + _extlib="lib${libname}.a" + MOD_LTLIBS="$MOD_LTLIBS modules/$1/lib${libname}.la" + MOD_LIBS="$MOD_LIBS $1/$_extlib" + MOD_STATIC="$MOD_STATIC $1" + else + MOD_SHARED="$MOD_SHARED $1" + fi + APACHE_OUTPUT(modules/$1/Makefile) + fi +]) + +AC_SUBST(MOD_LTLIBS) + +dnl ## APACHE_OUTPUT(file) +dnl ## adds "file" to the list of files generated by AC_OUTPUT +dnl ## This macro can be used several times. +AC_DEFUN(APACHE_OUTPUT, [ + APACHE_OUTPUT_FILES="$APACHE_OUTPUT_FILES $1" +]) + +dnl +dnl AC_ADD_LIBRARY(library) +dnl +dnl add a library to the link line +dnl +AC_DEFUN(AC_ADD_LIBRARY,[ + APACHE_ONCE(LIBRARY, $1, [ + EXTRA_LIBS="$EXTRA_LIBS -l$1" + ]) +]) + +dnl +dnl APACHE_ONCE(namespace, variable, code) +dnl +dnl execute code, if variable is not set in namespace +dnl +AC_DEFUN(APACHE_ONCE,[ + unique=`echo $ac_n "$2$ac_c" | tr -c -d a-zA-Z0-9` + cmd="echo $ac_n \"\$$1$unique$ac_c\"" + if test -n "$unique" && test "`eval $cmd`" = "" ; then + eval "$1$unique=set" + $3 + fi +]) + diff --git a/build/PrintPath b/build/PrintPath new file mode 100755 index 00000000000..908d2740083 --- /dev/null +++ b/build/PrintPath @@ -0,0 +1,105 @@ +#!/bin/sh +# Look for program[s] somewhere in $PATH. +# +# Options: +# -s +# Do not print out full pathname. (silent) +# -pPATHNAME +# Look in PATHNAME instead of $PATH +# +# Usage: +# PrintPath [-s] [-pPATHNAME] program [program ...] +# +# Initially written by Jim Jagielski for the Apache configuration mechanism +# (with kudos to Kernighan/Pike) +# +# This script falls under the Apache License. +# See http://www.apache.org/docs/LICENSE + +## +# Some "constants" +## +pathname=$PATH +echo="yes" + +## +# Find out what OS we are running for later on +## +os=`(uname) 2>/dev/null` + +## +# Parse command line +## +for args in $* +do + case $args in + -s ) echo="no" ;; + -p* ) pathname="`echo $args | sed 's/^..//'`" ;; + * ) programs="$programs $args" ;; + esac +done + +## +# Now we make the adjustments required for OS/2 and everyone +# else :) +# +# First of all, all OS/2 programs have the '.exe' extension. +# Next, we adjust PATH (or what was given to us as PATH) to +# be whitespace seperated directories. +# Finally, we try to determine the best flag to use for +# test/[] to look for an executable file. OS/2 just has '-r' +# but with other OSs, we do some funny stuff to check to see +# if test/[] knows about -x, which is the prefered flag. +## + +if [ "x$os" = "xOS/2" ] +then + ext=".exe" + pathname=`echo -E $pathname | + sed 's/^;/.;/ + s/;;/;.;/g + s/;$/;./ + s/;/ /g + s/\\\\/\\//g' ` + test_exec_flag="-r" +else + ext="" # No default extensions + pathname=`echo $pathname | + sed 's/^:/.:/ + s/::/:.:/g + s/:$/:./ + s/:/ /g' ` + # Here is how we test to see if test/[] can handle -x + testfile="pp.t.$$" + + cat > $testfile </dev/null`; then + test_exec_flag="-x" + else + test_exec_flag="-r" + fi + rm -f $testfile +fi + +for program in $programs +do + for path in $pathname + do + if [ $test_exec_flag $path/${program}${ext} ] && \ + [ ! -d $path/${program}${ext} ]; then + if [ "x$echo" = "xyes" ]; then + echo $path/${program}${ext} + fi + exit 0 + fi + done +done +exit 1 + diff --git a/build/build-modules-c.awk b/build/build-modules-c.awk new file mode 100644 index 00000000000..0da842152e1 --- /dev/null +++ b/build/build-modules-c.awk @@ -0,0 +1,48 @@ +BEGIN { + RS = " " + modules[n++] = "core" + pmodules[pn++] = "core" +} +{ modules[n++] = $1 ; pmodules[pn++] = $1 } +END { + print "/*" + print " * modules.c --- automatically generated by Apache" + print " * configuration script. DO NOT HAND EDIT!!!!!" + print " */" + print "" + print "#include \"httpd.h\"" + print "#include \"http_config.h\"" + print "" + for (i = 0; i < pn; ++i) { + printf ("extern module %s_module;\n", pmodules[i]) + } + print "" + print "/*" + print " * Modules which implicitly form the" + print " * list of activated modules on startup," + print " * i.e. these are the modules which are" + print " * initially linked into the Apache processing" + print " * [extendable under run-time via AddModule]" + print " */" + print "module *ap_prelinked_modules[] = {" + for (i = 0; i < n; ++i) { + printf " &%s_module,\n", modules[i] + } + print " NULL" + print "};" + print "" + print "/*" + print " * Modules which initially form the" + print " * list of available modules on startup," + print " * i.e. these are the modules which are" + print " * initially loaded into the Apache process" + print " * [extendable under run-time via LoadModule]" + print " */" + print "module *ap_preloaded_modules[] = {" + for (i = 0; i < pn; ++i) { + printf " &%s_module,\n", pmodules[i] + } + print " NULL" + print "};" + print "" +} diff --git a/build/config-stubs b/build/config-stubs new file mode 100755 index 00000000000..aff00a57d48 --- /dev/null +++ b/build/config-stubs @@ -0,0 +1,8 @@ +#!/bin/sh + +dir=$1 +for stubdir in `find $dir -type d`; do + if [ -r $stubdir/config.m4 ]; then + echo "sinclude($stubdir/config.m4)" + fi +done diff --git a/build/fastgen.sh b/build/fastgen.sh new file mode 100755 index 00000000000..9a59ab7949d --- /dev/null +++ b/build/fastgen.sh @@ -0,0 +1,53 @@ +#! /bin/sh +# +# Copyright (c) 1999, 2000 Sascha Schumann. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY SASCHA SCHUMANN ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL SASCHA SCHUMANN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +############################################################################## +# $Id: fastgen.sh,v 1.1 2000/01/11 13:10:58 sascha Exp $ +# + +srcdir=$1 +shift + +topsrcdir=`(cd $srcdir; pwd)` + +mkdir_p=$1 +shift + +if test "$mkdir_p" = "yes"; then + mkdir_p="mkdir -p" +else + mkdir_p="$topsrcdir/helpers/mkdir.sh" +fi + +base="\$(DEPTH)/$srcdir" + +for i in $@ ; do + echo "creating $i" + dir=`dirname $i` + $mkdir_p $dir + sed \ + -e s#@topsrcdir@#$base# \ + -e s#@srcdir@#$base/$dir# \ + < $topsrcdir/$i.in > $i +done diff --git a/build/install.sh b/build/install.sh new file mode 100755 index 00000000000..9a8821fa290 --- /dev/null +++ b/build/install.sh @@ -0,0 +1,112 @@ +#!/bin/sh +## +## install.sh -- install a program, script or datafile +## +## Based on `install-sh' from the X Consortium's X11R5 distribution +## as of 89/12/18 which is freely available. +## Cleaned up for Apache's Autoconf-style Interface (APACI) +## by Ralf S. Engelschall +## +# +# This script falls under the Apache License. +# See http://www.apache.org/docs/LICENSE + + +# +# put in absolute paths if you don't have them in your path; +# or use env. vars. +# +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" + +# +# parse argument line +# +instcmd="$mvprog" +chmodcmd="" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +ext="" +src="" +dst="" +while [ "x$1" != "x" ]; do + case $1 in + -c) instcmd="$cpprog" + shift; continue + ;; + -m) chmodcmd="$chmodprog $2" + shift; shift; continue + ;; + -o) chowncmd="$chownprog $2" + shift; shift; continue + ;; + -g) chgrpcmd="$chgrpprog $2" + shift; shift; continue + ;; + -s) stripcmd="$stripprog" + shift; continue + ;; + -S) stripcmd="$stripprog $2" + shift; shift; continue + ;; + -e) ext="$2" + shift; shift; continue + ;; + *) if [ "x$src" = "x" ]; then + src=$1 + else + dst=$1 + fi + shift; continue + ;; + esac +done +if [ "x$src" = "x" ]; then + echo "install.sh: no input file specified" + exit 1 +fi +if [ "x$dst" = "x" ]; then + echo "install.sh: no destination specified" + exit 1 +fi + +# +# If destination is a directory, append the input filename; if +# your system does not like double slashes in filenames, you may +# need to add some logic +# +if [ -d $dst ]; then + dst="$dst/`basename $src`" +fi + +# Add a possible extension (such as ".exe") to src and dst +src="$src$ext" +dst="$dst$ext" + +# Make a temp file name in the proper directory. +dstdir=`dirname $dst` +dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name +$instcmd $src $dsttmp + +# And set any options; do chmod last to preserve setuid bits +if [ "x$chowncmd" != "x" ]; then $chowncmd $dsttmp; fi +if [ "x$chgrpcmd" != "x" ]; then $chgrpcmd $dsttmp; fi +if [ "x$stripcmd" != "x" ]; then $stripcmd $dsttmp; fi +if [ "x$chmodcmd" != "x" ]; then $chmodcmd $dsttmp; fi + +# Now rename the file to the real destination. +$rmcmd $dst +$mvcmd $dsttmp $dst + +exit 0 + diff --git a/build/library.mk b/build/library.mk new file mode 100644 index 00000000000..abadb6340c5 --- /dev/null +++ b/build/library.mk @@ -0,0 +1,42 @@ +# Copyright (c) 1999, 2000 Sascha Schumann. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY SASCHA SCHUMANN ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL SASCHA SCHUMANN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +############################################################################## +# $Id: library.mk,v 1.1 2000/01/11 13:10:58 sascha Exp $ +# + +LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) + +$(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) + $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD) + +$(LTLIBRARY_SHARED_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) + @test -d $(phplibdir) || $(mkinstalldirs) $(phplibdir) + $(LINK) -avoid-version -module -rpath $(phplibdir) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_SHARED_LIBADD) + $(SHLIBTOOL) --mode=install install $@ $(phplibdir) + +shared: + @if test '$(SHLIBTOOL)' != '$(LIBTOOL)'; then \ + $(MAKE) 'LIBTOOL=$(SHLIBTOOL)' $(LTLIBRARY_SHARED_NAME); \ + else \ + $(MAKE) $(LTLIBRARY_SHARED_NAME); \ + fi; \ diff --git a/build/ltlib.mk b/build/ltlib.mk new file mode 100644 index 00000000000..42bbb7cb1b7 --- /dev/null +++ b/build/ltlib.mk @@ -0,0 +1,31 @@ +# Copyright (c) 1999, 2000 Sascha Schumann. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY SASCHA SCHUMANN ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL SASCHA SCHUMANN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +############################################################################## +# $Id: ltlib.mk,v 1.1 2000/01/11 13:10:58 sascha Exp $ +# + +targets = $(LTLIBRARY_NAME) + +include $(topsrcdir)/build/rules.mk +include $(topsrcdir)/build/library.mk + diff --git a/build/mkdep.perl b/build/mkdep.perl new file mode 100644 index 00000000000..f9e06ea041e --- /dev/null +++ b/build/mkdep.perl @@ -0,0 +1,91 @@ +#!/usr/bin/perl +# +# $Id: mkdep.perl,v 1.1 2000/01/11 13:10:59 sascha Exp $ +# +# Created: Thu Aug 15 11:57:33 1996 too +# Last modified: Mon Dec 27 09:23:56 1999 too +# +# Copyright (c) 1996-1999 Tomi Ollila. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +die "Usage: mkdep CPP-command [CPP options] file1 [file2...]\n" + if ($#ARGV < 1); + +$cmdl = shift(@ARGV); + +$cmdl = "$cmdl " . shift (@ARGV) while ($ARGV[0] =~ /^-[A-Z]/); + +while ($file = shift(@ARGV)) +{ + $file =~ s/\.o$/.c/; + + open(F, "$cmdl $file|"); + + &parseout; + + close(F); +} + + +sub initinit +{ + %used = (); + $of = $file; + $of =~ s/\.c$/.lo/; + $str = "$of:\t$file"; + $len = length $str; +} + +sub initstr +{ + $str = "\t"; + $len = length $str; +} + +sub parseout +{ + &initinit; + while () + { + next unless (/^# [0-9]* "(.*\.h)"/); + + next if ($1 =~ /^\//); + + next if $used{$1}; + + $used{$1} = 1; + + $nlen = length($1) + 1; + + if ($len + $nlen > 72) + { + print $str, "\\\n"; + &initstr; + $str = $str . $1; + } + else { $str = $str . " " . $1; } + + $len += $nlen; + + } + print $str, "\n"; +} diff --git a/build/mkdir.sh b/build/mkdir.sh new file mode 100755 index 00000000000..4cd33c5671c --- /dev/null +++ b/build/mkdir.sh @@ -0,0 +1,35 @@ +#!/bin/sh +## +## mkdir.sh -- make directory hierarchy +## +## Based on `mkinstalldirs' from Noah Friedman +## as of 1994-03-25, which was placed in the Public Domain. +## Cleaned up for Apache's Autoconf-style Interface (APACI) +## by Ralf S. Engelschall +## +# +# This script falls under the Apache License. +# See http://www.apache.org/docs/LICENSE + + +umask 022 +errstatus=0 +for file in ${1+"$@"} ; do + set fnord `echo ":$file" |\ + sed -e 's/^:\//%/' -e 's/^://' -e 's/\// /g' -e 's/^%/\//'` + shift + pathcomp= + for d in ${1+"$@"}; do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" 1>&2 + mkdir "$pathcomp" || errstatus=$? + fi + pathcomp="$pathcomp/" + done +done +exit $errstatus + diff --git a/build/program.mk b/build/program.mk new file mode 100644 index 00000000000..bb52d8832c0 --- /dev/null +++ b/build/program.mk @@ -0,0 +1,30 @@ +# Copyright (c) 1999, 2000 Sascha Schumann. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY SASCHA SCHUMANN ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL SASCHA SCHUMANN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +############################################################################## +# $Id: program.mk,v 1.1 2000/01/11 13:10:59 sascha Exp $ +# + +PROGRAM_OBJECTS = $(PROGRAM_SOURCES:.c=.lo) + +$(PROGRAM_NAME): $(PROGRAM_DEPENDENCIES) $(PROGRAM_OBJECTS) + $(LINK) $(PROGRAM_LDFLAGS) $(PROGRAM_OBJECTS) $(PROGRAM_LDADD) diff --git a/build/special.mk b/build/special.mk new file mode 100644 index 00000000000..0068836ff3b --- /dev/null +++ b/build/special.mk @@ -0,0 +1,45 @@ +# Copyright (c) 1999, 2000 Sascha Schumann. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY SASCHA SCHUMANN ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL SASCHA SCHUMANN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +############################################################################## +# $Id: special.mk,v 1.1 2000/01/11 13:10:59 sascha Exp $ +# + + + +all: all-recursive + +include modules.mk +targets = $(static) $(shared) +install_targets = install-modules + +install-modules: + @shared='$(shared)'; \ + if test -n "$$shared"; then \ + $(mkinstalldirs) $(libexecdir); \ + for i in $$shared; do \ + $(SHLIBTOOL) --mode=install cp $$i $(libexecdir); \ + done; \ + fi + +include $(topsrcdir)/build/rules.mk + diff --git a/buildconf b/buildconf new file mode 100755 index 00000000000..123c717a22b --- /dev/null +++ b/buildconf @@ -0,0 +1,107 @@ +#!/bin/sh + +# TODO - Steal the new PHP buildconf and build.mk. +# This will do for now, though + +supplied_flag=$1 + +# do some version checking for the tools we use +if test "$1" = "--force"; then + shift +# this is a posix correct "test -nt" +elif test "`ls -t buildconf buildconf.stamp 2>/dev/null |head -1`" != "buildconf"; then + : +else + echo "buildconf: checking installation..." + + # autoconf will check for the version itself + # automake 1.4 or newer + am_version=`automake --version 2>/dev/null|head -1|sed -e 's/^[^0-9]*//' -e 's/[a-z]* *$//'` + if test "$am_version" = ""; then + echo "buildconf: automake not found." + echo " You need automake version 1.4 or newer installed" + echo " to build APACHE from CVS." + exit 1 + fi + IFS=.; set $am_version; IFS=' ' + if test "$1" = "1" -a "$2" -lt "4" || test "$1" -lt "1"; then + echo "buildconf: automake version $am_version found." + echo " You need automake version 1.4 or newer installed" + echo " to build APACHE from CVS." + exit 1 + else + echo "buildconf: automake version $am_version (ok)" + fi + + # libtool 1.3.3 or newer + lt_pversion=`libtool --version 2>/dev/null|sed -e 's/^[^0-9]*//' -e 's/[- ].*//'` + if test "$lt_pversion" = ""; then + echo "buildconf: libtool not found." + echo " You need libtool version 1.3 or newer installed" + echo " to build APACHE from CVS." + exit 1 + fi + lt_version=`echo $lt_pversion|sed -e 's/\([a-z]*\)$/.\1/'` + IFS=.; set $lt_version; IFS=' ' + if test "$1" -gt "1" || test "$2" -gt "3" || test "$2" = "3" -a "$3" -ge "3" + then + echo "buildconf: libtool version $lt_pversion (ok)" + else + echo "buildconf: libtool version $lt_pversion found." + echo " You need libtool version 1.3.3 or newer installed" + echo " to build APACHE from CVS." + exit 1 + fi + touch buildconf.stamp +fi + +am_prefix=`which automake | sed -e 's#/[^/]*/[^/]*$##'` +lt_prefix=`which libtool | sed -e 's#/[^/]*/[^/]*$##'` +if test "$am_prefix" != "$lt_prefix"; then + echo "buildconf: WARNING: automake and libtool are installed in different" + echo " directories. This may cause aclocal to fail." + echo "buildconf: continuing anyway" +fi + +if test "$supplied_flag" = "--copy"; then + automake_flags=--copy +fi + +./helpers/build-acconfig-header + +mv aclocal.m4 aclocal.m4.old 2>/dev/null +aclocal +if test "$?" != "0" -a "$am_prefix" != "$lt_prefix"; then + echo "buildconf: ERROR: aclocal failed, probably because automake and" + echo " libtool are installed with different prefixes;" + echo " automake is installed in $am_prefix, but libtool in $lt_prefix." + echo " Please re-install automake and/or libtool with a common prefix" + echo " and try again." + exit 1 +fi + +if cmp aclocal.m4.old aclocal.m4 > /dev/null 2>&1; then + echo "buildconf: keeping aclocal.m4" + mv aclocal.m4.old aclocal.m4 +else + echo "buildconf: created or modified aclocal.m4" +fi + +autoheader + +# find all Makefile.ams +files="Makefile `find ap lib main modules regex os -name Makefile.am | sed 's#\.am##' | tr '\n' ' '`" + +# suppress stupid automake warning +automake --add-missing --include-deps $automake_flags $files 2>&1 | grep -v \$APACHE_OUTPUT_FILES >&2 + + +mv configure configure.old 2>/dev/null +autoconf +if cmp configure.old configure > /dev/null 2>&1; then + echo "buildconf: keeping configure" + mv configure.old configure +else + echo "buildconf: created or modified configure" +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 00000000000..952328afe76 --- /dev/null +++ b/configure.in @@ -0,0 +1,92 @@ +dnl ## Process this file with autoconf to produce a configure script + +AC_PREREQ(2.13) +AC_INIT(main/http_main.c) +AM_INIT_AUTOMAKE(apache, 2.0-dev) + +dnl This would be in src/include, but I think I've found an autoheader bug +AM_CONFIG_HEADER(ap_config_auto.h) + +dnl ## This is the central place where Apache's version should be kept. + +APACHE_VERSION=$VERSION +AC_SUBST(APACHE_VERSION) + +dnl XXX - doesn't actually work, because of the APACHE_OUTPUT_FILES error +AM_MAINTAINER_MODE + +dnl We want this one before the checks, so the checks can modify CFLAGS +test -z "$CFLAGS" && auto_cflags=1 + +dnl Needed for APACHE_MODULE() to work +cwd=`pwd` + +dnl ## Check for programs + +dnl AC_PROG_AWK +AC_PROG_CC +AC_PROG_CPP +AC_PROG_INSTALL +dnl AC_PROG_RANLIB +dnl AC_PATH_PROG(PERL_PATH, perl) + +dnl info autoconf says AIX needs this. Okay. +AC_AIX + +dnl ## Check for libraries + +dnl ## Check for header files + +dnl I think these are just used all over the place, so just check for +dnl them at the base of the tree. If some are specific to a single +dnl directory, they should be moved (Comment #Spoon) + +AC_CHECK_HEADERS( \ +unistd.h \ +sys/times.h \ +sys/time.h \ +) + +dnl ## Check for typedefs, structures, and compiler characteristics. + +AC_C_INLINE + +dnl ## Check for library functions + +dnl See Comment #Spoon + +AC_CHECK_FUNCS( \ +strdup \ +strcasecmp \ +strncasecmp \ +strstr \ +strerror \ +initgroups \ +waitpid \ +difftime \ +gettimeofday \ +memmove \ +) + +AM_PROG_LIBTOOL + +dnl ## Checking command-line options +test -n "$GCC" && test "$USE_MAINTAINER_MODE" = "yes" && \ + CFLAGS="$CFLAGS -g -Wall -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations" + +INCLUDES="-I\$(top_srcdir)/include -I\$(top_srcdir)/lib/apr/include" +AC_SUBST(INCLUDES) + +dnl reading config stubs +esyscmd(./helpers/config-stubs .) + +INCLUDES="$INCLUDES -I\$(top_srcdir)/\$(OS_DIR)" +AC_SUBST(EXTRA_LIBS) +AC_SUBST(REGEX_DIR) +AC_SUBST(REGEX_LIB) +AC_SUBST(MPM_LIB) +AC_SUBST(OS) +AC_SUBST(OS_DIR) + +AC_OUTPUT([Makefile ap/Makefile main/Makefile lib/apr/Makefile os/Makefile + $APACHE_OUTPUT_FILES]) diff --git a/docs/cgi-examples/printenv b/docs/cgi-examples/printenv new file mode 100644 index 00000000000..7d389e0ac56 --- /dev/null +++ b/docs/cgi-examples/printenv @@ -0,0 +1,7 @@ +#!/usr/local/bin/perl + +print "Content-type: text/html\n\n"; +while (($key, $val) = each %ENV) { + print "$key = $val
\n"; +} + diff --git a/docs/cgi-examples/test-cgi b/docs/cgi-examples/test-cgi new file mode 100644 index 00000000000..a85631e3aa2 --- /dev/null +++ b/docs/cgi-examples/test-cgi @@ -0,0 +1,31 @@ +#!/bin/sh + +# disable filename globbing +set -f + +echo Content-type: text/plain +echo + +echo CGI/1.0 test script report: +echo + +echo argc is $#. argv is "$*". +echo + +echo SERVER_SOFTWARE = $SERVER_SOFTWARE +echo SERVER_NAME = $SERVER_NAME +echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE +echo SERVER_PROTOCOL = $SERVER_PROTOCOL +echo SERVER_PORT = $SERVER_PORT +echo REQUEST_METHOD = $REQUEST_METHOD +echo HTTP_ACCEPT = "$HTTP_ACCEPT" +echo PATH_INFO = "$PATH_INFO" +echo PATH_TRANSLATED = "$PATH_TRANSLATED" +echo SCRIPT_NAME = "$SCRIPT_NAME" +echo QUERY_STRING = "$QUERY_STRING" +echo REMOTE_HOST = $REMOTE_HOST +echo REMOTE_ADDR = $REMOTE_ADDR +echo REMOTE_USER = $REMOTE_USER +echo AUTH_TYPE = $AUTH_TYPE +echo CONTENT_TYPE = $CONTENT_TYPE +echo CONTENT_LENGTH = $CONTENT_LENGTH diff --git a/docs/conf/httpd-win.conf b/docs/conf/httpd-win.conf new file mode 100644 index 00000000000..3eca7ef3dea --- /dev/null +++ b/docs/conf/httpd-win.conf @@ -0,0 +1,781 @@ +# +# Based upon the NCSA server configuration files originally by Rob McCool. +# +# This is the main Apache server configuration file. It contains the +# configuration directives that give the server its instructions. +# See for detailed information about +# the directives. +# +# Do NOT simply read the instructions in here without understanding +# what they do. They're here only as hints or reminders. If you are unsure +# consult the online docs. You have been warned. +# +# After this file is processed, the server will look for and process +# @@ServerRoot@@/conf/srm.conf and then @@ServerRoot@@/conf/access.conf +# unless you have overridden these with ResourceConfig and/or +# AccessConfig directives here. +# +# The configuration directives are grouped into three basic sections: +# 1. Directives that control the operation of the Apache server process as a +# whole (the 'global environment'). +# 2. Directives that define the parameters of the 'main' or 'default' server, +# which responds to requests that aren't handled by a virtual host. +# These directives also provide default values for the settings +# of all virtual hosts. +# 3. Settings for virtual hosts, which allow Web requests to be sent to +# different IP addresses or hostnames and have them handled by the +# same Apache server process. +# +# Configuration and logfile names: If the filenames you specify for many +# of the server's control files begin with "/" (or "drive:/" for Win32), the +# server will use that explicit path. If the filenames do *not* begin +# with "/", the value of ServerRoot is prepended -- so "logs/foo.log" +# with ServerRoot set to "/usr/local/apache" will be interpreted by the +# server as "/usr/local/apache/logs/foo.log". +# +# NOTE: Where filenames are specified, you must use forward slashes +# instead of backslashes (e.g., "c:/apache" instead of "c:\apache"). +# If a drive letter is omitted, the drive on which Apache.exe is located +# will be used by default. It is recommended that you always supply +# an explicit drive letter in absolute paths, however, to avoid +# confusion. +# + +### Section 1: Global Environment +# +# The directives in this section affect the overall operation of Apache, +# such as the number of concurrent requests it can handle or where it +# can find its configuration files. +# + +# +# ServerType is either inetd, or standalone. Inetd mode is only supported on +# Unix platforms. +# +ServerType standalone + +# +# ServerRoot: The top of the directory tree under which the server's +# configuration, error, and log files are kept. +# +# Do NOT add a slash at the end of the directory path. +# +ServerRoot "@@ServerRoot@@" + +# +# PidFile: The file in which the server should record its process +# identification number when it starts. +# +PidFile logs/httpd.pid + +# +# ScoreBoardFile: File used to store internal server process information. +# Not all architectures require this. But if yours does (you'll know because +# this file will be created when you run Apache) then you *must* ensure that +# no two invocations of Apache share the same scoreboard file. +# +ScoreBoardFile logs/apache_status + +# +# In the standard configuration, the server will process httpd.conf, +# srm.conf, and access.conf in that order. The latter two files are +# now distributed empty, as it is recommended that all directives +# be kept in a single file for simplicity. The commented-out values +# below are the built-in defaults. You can have the server ignore +# these files altogether by using "/dev/null" (for Unix) or +# "nul" (for Win32) for the arguments to the directives. +# +#ResourceConfig conf/srm.conf +#AccessConfig conf/access.conf + +# +# Timeout: The number of seconds before receives and sends time out. +# +Timeout 300 + +# +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. +# +KeepAlive On + +# +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We reccomend you leave this number high, for maximum performance. +# +MaxKeepAliveRequests 100 + +# +# KeepAliveTimeout: Number of seconds to wait for the next request from the +# same client on the same connection. +# +KeepAliveTimeout 15 + +# +# Apache on Win32 always creates one child process to handle requests. If it +# dies, another child process is created automatically. Within the child +# process multiple threads handle incoming requests. The next two +# directives control the behaviour of the threads and processes. +# + +# +# MaxRequestsPerChild: the number of requests each child process is +# allowed to process before the child dies. The child will exit so +# as to avoid problems after prolonged use when Apache (and maybe the +# libraries it uses) leak memory or other resources. On most systems, this +# isn't really needed, but a few (such as Solaris) do have notable leaks +# in the libraries. For Win32, set this value to zero (unlimited) +# unless advised otherwise. +# +MaxRequestsPerChild 0 + +# +# Number of concurrent threads (i.e., requests) the server will allow. +# Set this value according to the responsiveness of the server (more +# requests active at once means they're all handled more slowly) and +# the amount of system resources you'll allow the server to consume. +# +ThreadsPerChild 50 + +# +# Listen: Allows you to bind Apache to specific IP addresses and/or +# ports, in addition to the default. See also the +# directive. +# +#Listen 3000 +#Listen 12.34.56.78:80 + +# +# BindAddress: You can support virtual hosts with this option. This directive +# is used to tell the server which IP address to listen to. It can either +# contain "*", an IP address, or a fully qualified Internet domain name. +# See also the and Listen directives. +# +#BindAddress * + +# +# Dynamic Shared Object (DSO) Support +# +# To be able to use the functionality of a module which was built as a DSO you +# have to place corresponding `LoadModule' lines at this location so the +# directives contained in it are actually available _before_ they are used. +# Please read the file README.DSO in the Apache 1.3 distribution for more +# details about the DSO mechanism and run `apache -l' for the list of already +# built-in (statically linked and thus always available) modules in your Apache +# binary. +# +# Note: The order in which modules are loaded is important. Don't change +# the order below without expert advice. +# +#LoadModule anon_auth_module modules/ApacheModuleAuthAnon.dll +#LoadModule cern_meta_module modules/ApacheModuleCERNMeta.dll +#LoadModule digest_module modules/ApacheModuleDigest.dll +#LoadModule expires_module modules/ApacheModuleExpires.dll +#LoadModule headers_module modules/ApacheModuleHeaders.dll +#LoadModule proxy_module modules/ApacheModuleProxy.dll +#LoadModule rewrite_module modules/ApacheModuleRewrite.dll +#LoadModule speling_module modules/ApacheModuleSpeling.dll +#LoadModule status_module modules/ApacheModuleStatus.dll +#LoadModule usertrack_module modules/ApacheModuleUserTrack.dll + +# +# ExtendedStatus controls whether Apache will generate "full" status +# information (ExtendedStatus On) or just basic information (ExtendedStatus +# Off) when the "server-status" handler is called. The default is Off. +# +#ExtendedStatus On + +### Section 2: 'Main' server configuration +# +# The directives in this section set up the values used by the 'main' +# server, which responds to any requests that aren't handled by a +# definition. These values also provide defaults for +# any containers you may define later in the file. +# +# All of these directives may appear inside containers, +# in which case these default settings will be overridden for the +# virtual host being defined. +# + +# +# If your ServerType directive (set earlier in the 'Global Environment' +# section) is set to "inetd", the next few directives don't have any +# effect since their settings are defined by the inetd configuration. +# Skip ahead to the ServerAdmin directive. +# + +# +# Port: The port to which the standalone server listens. +# +Port 80 + +# +# ServerAdmin: Your address, where problems with the server should be +# e-mailed. This address appears on some server-generated pages, such +# as error documents. +# +ServerAdmin you@your.address + +# +# ServerName allows you to set a host name which is sent back to clients for +# your server if it's different than the one the program would get (i.e., use +# "www" instead of the host's real name). +# +# Note: You cannot just invent host names and hope they work. The name you +# define here must be a valid DNS name for your host. If you don't understand +# this, ask your network administrator. +# If your host doesn't have a registered DNS name, enter its IP address here. +# You will have to access it by its address (e.g., http://123.45.67.89/) +# anyway, and this will make redirections work in a sensible way. +# +#ServerName new.host.name + +# +# DocumentRoot: The directory out of which you will serve your +# documents. By default, all requests are taken from this directory, but +# symbolic links and aliases may be used to point to other locations. +# +DocumentRoot "@@ServerRoot@@/htdocs" + +# +# Each directory to which Apache has access, can be configured with respect +# to which services and features are allowed and/or disabled in that +# directory (and its subdirectories). +# +# First, we configure the "default" to be a very restrictive set of +# permissions. +# + + Options FollowSymLinks + AllowOverride None + + +# +# Note that from this point forward you must specifically allow +# particular features to be enabled - so if something's not working as +# you might expect, make sure that you have specifically enabled it +# below. +# + +# +# This should be changed to whatever you set DocumentRoot to. +# + + +# +# This may also be "None", "All", or any combination of "Indexes", +# "Includes", "FollowSymLinks", "ExecCGI", or "MultiViews". +# +# Note that "MultiViews" must be named *explicitly* --- "Options All" +# doesn't give it to you. +# + Options Indexes FollowSymLinks + +# +# This controls which options the .htaccess files in directories can +# override. Can also be "All", or any combination of "Options", "FileInfo", +# "AuthConfig", and "Limit" +# + AllowOverride None + +# +# Controls who can get stuff from this server. +# + Order allow,deny + Allow from all + + +# +# UserDir: The name of the directory which is appended onto a user's home +# directory if a ~user request is received. +# +# Under Win32, we do not currently try to determine the home directory of +# a Windows login, so a format such as that below needs to be used. See +# the UserDir documentation for details. +# +UserDir "@@ServerRoot@@/users/" + +# +# DirectoryIndex: Name of the file or files to use as a pre-written HTML +# directory index. Separate multiple entries with spaces. +# +DirectoryIndex index.html + +# +# AccessFileName: The name of the file to look for in each directory +# for access control information. +# +AccessFileName .htaccess + +# +# The following lines prevent .htaccess files from being viewed by +# Web clients. Since .htaccess files often contain authorization +# information, access is disallowed for security reasons. Comment +# these lines out if you want Web visitors to see the contents of +# .htaccess files. If you change the AccessFileName directive above, +# be sure to make the corresponding changes here. +# + + Order allow,deny + Deny from all + + +# +# CacheNegotiatedDocs: By default, Apache sends "Pragma: no-cache" with each +# document that was negotiated on the basis of content. This asks proxy +# servers not to cache the document. Uncommenting the following line disables +# this behavior, and proxies will be allowed to cache the documents. +# +#CacheNegotiatedDocs + +# +# UseCanonicalName: (new for 1.3) With this setting turned on, whenever +# Apache needs to construct a self-referencing URL (a URL that refers back +# to the server the response is coming from) it will use ServerName and +# Port to form a "canonical" name. With this setting off, Apache will +# use the hostname:port that the client supplied, when possible. This +# also affects SERVER_NAME and SERVER_PORT in CGI scripts. +# +UseCanonicalName On + +# +# TypesConfig describes where the mime.types file (or equivalent) is +# to be found. +# +TypesConfig conf/mime.types + +# +# DefaultType is the default MIME type the server will use for a document +# if it cannot otherwise determine one, such as from filename extensions. +# If your server contains mostly text or HTML documents, "text/plain" is +# a good value. If most of your content is binary, such as applications +# or images, you may want to use "application/octet-stream" instead to +# keep browsers from trying to display binary files as though they are +# text. +# +DefaultType text/plain + +# +# The mod_mime_magic module allows the server to use various hints from the +# contents of the file itself to determine its type. The MIMEMagicFile +# directive tells the module where the hint definitions are located. +# mod_mime_magic is not part of the default server (you have to add +# it yourself with a LoadModule [see the DSO paragraph in the 'Global +# Environment' section], or recompile the server and include mod_mime_magic +# as part of the configuration), so it's enclosed in an container. +# This means that the MIMEMagicFile directive will only be processed if the +# module is part of the server. +# + + MIMEMagicFile conf/magic + + +# +# HostnameLookups: Log the names of clients or just their IP addresses +# e.g., www.apache.org (on) or 204.62.129.132 (off). +# The default is off because it'd be overall better for the net if people +# had to knowingly turn this feature on, since enabling it means that +# each client request will result in AT LEAST one lookup request to the +# nameserver. +# +HostnameLookups Off + +# +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +ErrorLog logs/error.log + +# +# LogLevel: Control the number of messages logged to the error.log. +# Possible values include: debug, info, notice, warn, error, crit, +# alert, emerg. +# +LogLevel warn + +# +# The following directives define some format nicknames for use with +# a CustomLog directive (see below). +# +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %b" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent + +# +# The location and format of the access logfile (Common Logfile Format). +# If you do not define any access logfiles within a +# container, they will be logged here. Contrariwise, if you *do* +# define per- access logfiles, transactions will be +# logged therein and *not* in this file. +# +CustomLog logs/access.log common + +# +# If you would like to have agent and referer logfiles, uncomment the +# following directives. +# +#CustomLog logs/referer.log referer +#CustomLog logs/agent.log agent + +# +# If you prefer a single logfile with access, agent, and referer information +# (Combined Logfile Format) you can use the following directive. +# +#CustomLog logs/access.log combined + +# +# Optionally add a line containing the server version and virtual host +# name to server-generated pages (error documents, FTP directory listings, +# mod_status and mod_info output etc., but not CGI generated documents). +# Set to "EMail" to also include a mailto: link to the ServerAdmin. +# Set to one of: On | Off | EMail +# +ServerSignature On + +# +# Aliases: Add here as many aliases as you need (with no limit). The format is +# Alias fakename realname +# +# Note that if you include a trailing / on fakename then the server will +# require it to be present in the URL. So "/icons" isn't aliased in this +# example, only "/icons/".. +# +Alias /icons/ "@@ServerRoot@@/icons/" + +# +# ScriptAlias: This controls which directories contain server scripts. +# ScriptAliases are essentially the same as Aliases, except that +# documents in the realname directory are treated as applications and +# run by the server when requested rather than as documents sent to the client. +# The same rules about trailing "/" apply to ScriptAlias directives as to +# Alias. +# +ScriptAlias /cgi-bin/ "@@ServerRoot@@/cgi-bin/" + +# +# "@@ServerRoot@@/cgi-bin" should be changed to whatever your ScriptAliased +# CGI directory exists, if you have that configured. +# + + AllowOverride None + Options None + + +# +# Redirect allows you to tell clients about documents which used to exist in +# your server's namespace, but do not anymore. This allows you to tell the +# clients where to look for the relocated document. +# Format: Redirect old-URI new-URL +# + +# +# Directives controlling the display of server-generated directory listings. +# + +# +# FancyIndexing is whether you want fancy directory indexing or standard +# +IndexOptions FancyIndexing + +# +# AddIcon* directives tell the server which icon to show for different +# files or filename extensions. These are only displayed for +# FancyIndexed directories. +# +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +AddIcon /icons/bomb.gif core + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +# +# DefaultIcon is which icon to show for files which do not have an icon +# explicitly set. +# +DefaultIcon /icons/unknown.gif + +# +# AddDescription allows you to place a short description after a file in +# server-generated indexes. These are only displayed for FancyIndexed +# directories. +# Format: AddDescription "description" filename +# +#AddDescription "GZIP compressed document" .gz +#AddDescription "tar archive" .tar +#AddDescription "GZIP compressed tar archive" .tgz + +# +# ReadmeName is the name of the README file the server will look for by +# default, and append to directory listings. +# +# HeaderName is the name of a file which should be prepended to +# directory indexes. +# +# The server will first look for name.html and include it if found. +# If name.html doesn't exist, the server will then look for name.txt +# and include it as plaintext if found. +# +ReadmeName README +HeaderName HEADER + +# +# IndexIgnore is a set of filenames which directory indexing should ignore +# and not include in the listing. Shell-style wildcarding is permitted. +# +IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t + +# +# AddEncoding allows you to have certain browsers (Mosaic/X 2.1+) uncompress +# information on the fly. Note: Not all browsers support this. +# Despite the name similarity, the following Add* directives have nothing +# to do with the FancyIndexing customisation directives above. +# +AddEncoding x-compress Z +AddEncoding x-gzip gz tgz + +# +# AddLanguage allows you to specify the language of a document. You can +# then use content negotiation to give a browser a file in a language +# it can understand. Note that the suffix does not have to be the same +# as the language keyword --- those with documents in Polish (whose +# net-standard language code is pl) may wish to use "AddLanguage pl .po" +# to avoid the ambiguity with the common suffix for perl scripts. +# +AddLanguage en .en +AddLanguage fr .fr +AddLanguage de .de +AddLanguage da .da +AddLanguage el .el +AddLanguage it .it + +# +# LanguagePriority allows you to give precedence to some languages +# in case of a tie during content negotiation. +# Just list the languages in decreasing order of preference. +# +LanguagePriority en fr de + +# +# AddType allows you to tweak mime.types without actually editing it, or to +# make certain files to be certain types. +# +# For example, the PHP3 module (not part of the Apache distribution) +# will typically use: +# +#AddType application/x-httpd-php3 .phtml +#AddType application/x-httpd-php3-source .phps + +AddType application/x-tar .tgz + +# +# AddHandler allows you to map certain file extensions to "handlers", +# actions unrelated to filetype. These can be either built into the server +# or added with the Action command (see below) +# +# If you want to use server side includes, or CGI outside +# ScriptAliased directories, uncomment the following lines. +# +# To use CGI scripts: +# +#AddHandler cgi-script .cgi + +# +# To use server-parsed HTML files +# +#AddType text/html .shtml +#AddHandler server-parsed .shtml + +# +# Uncomment the following line to enable Apache's send-asis HTTP file +# feature +# +#AddHandler send-as-is asis + +# +# If you wish to use server-parsed imagemap files, use +# +#AddHandler imap-file map + +# +# To enable type maps, you might want to use +# +#AddHandler type-map var + +# +# Action lets you define media types that will execute a script whenever +# a matching file is called. This eliminates the need for repeated URL +# pathnames for oft-used CGI file processors. +# Format: Action media/type /cgi-script/location +# Format: Action handler-name /cgi-script/location +# + +# +# MetaDir: specifies the name of the directory in which Apache can find +# meta information files. These files contain additional HTTP headers +# to include when sending the document +# +#MetaDir .web + +# +# MetaSuffix: specifies the file name suffix for the file containing the +# meta information. +# +#MetaSuffix .meta + +# +# Customizable error response (Apache style) +# these come in three flavors +# +# 1) plain text +#ErrorDocument 500 "The server made a boo boo. +# n.b. the (") marks it as text, it does not get output +# +# 2) local redirects +#ErrorDocument 404 /missing.html +# to redirect to local URL /missing.html +#ErrorDocument 404 /cgi-bin/missing_handler.pl +# N.B.: You can redirect to a script or a document using server-side-includes. +# +# 3) external redirects +#ErrorDocument 402 http://some.other_server.com/subscription_info.html +# N.B.: Many of the environment variables associated with the original +# request will *not* be available to such a script. + +# +# The following directives disable keepalives and HTTP header flushes. +# The first directive disables it for Netscape 2.x and browsers which +# spoof it. There are known problems with these. +# The second directive is for Microsoft Internet Explorer 4.0b2 +# which has a broken HTTP/1.1 implementation and does not properly +# support keepalive when it is used on 301 or 302 (redirect) responses. +# +BrowserMatch "Mozilla/2" nokeepalive +BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 + +# +# The following directive disables HTTP/1.1 responses to browsers which +# are in violation of the HTTP/1.0 spec by not being able to grok a +# basic 1.1 response. +# +BrowserMatch "RealPlayer 4\.0" force-response-1.0 +BrowserMatch "Java/1\.0" force-response-1.0 +BrowserMatch "JDK/1\.0" force-response-1.0 + +# +# Allow server status reports, with the URL of http://servername/server-status +# Change the ".your_domain.com" to match your domain to enable. +# +# +# SetHandler server-status +# Order deny,allow +# Deny from all +# Allow from .your_domain.com +# + +# +# Allow remote server configuration reports, with the URL of +# http://servername/server-info (requires that mod_info.c be loaded). +# Change the ".your_domain.com" to match your domain to enable. +# +# +# SetHandler server-info +# Order deny,allow +# Deny from all +# Allow from .your_domain.com +# + +# +# There have been reports of people trying to abuse an old bug from pre-1.1 +# days. This bug involved a CGI script distributed as a part of Apache. +# By uncommenting these lines you can redirect these attacks to a logging +# script on phf.apache.org. Or, you can record them yourself, using the script +# support/phf_abuse_log.cgi. +# +# +# Deny from all +# ErrorDocument 403 http://phf.apache.org/phf_abuse_log.cgi +# + +# +# Proxy Server directives. Uncomment the following line to +# enable the proxy server: +# +#ProxyRequests On + +# +# Enable/disable the handling of HTTP/1.1 "Via:" headers. +# ("Full" adds the server version; "Block" removes all outgoing Via: headers) +# Set to one of: Off | On | Full | Block +# +#ProxyVia On + +# +# To enable the cache as well, edit and uncomment the following lines: +# (no cacheing without CacheRoot) +# +#CacheRoot "@@ServerRoot@@/proxy" +#CacheSize 5 +#CacheGcInterval 4 +#CacheMaxExpire 24 +#CacheLastModifiedFactor 0.1 +#CacheDefaultExpire 1 +#NoCache a_domain.com another_domain.edu joes.garage_sale.com + +### Section 3: Virtual Hosts +# +# VirtualHost: If you want to maintain multiple domains/hostnames on your +# machine you can setup VirtualHost containers for them. +# Please see the documentation at +# for further details before you try to setup virtual hosts. +# You may use the command line option '-S' to verify your virtual host +# configuration. + +# +# If you want to use name-based virtual hosts you need to define at +# least one IP address (and port number) for them. +# +#NameVirtualHost 12.34.56.78:80 +#NameVirtualHost 12.34.56.78 + +# +# VirtualHost example: +# Almost any Apache directive may go into a VirtualHost container. +# +# +# ServerAdmin webmaster@host.some_domain.com +# DocumentRoot /www/docs/host.some_domain.com +# ServerName host.some_domain.com +# ErrorLog logs/host.some_domain.com-error_log +# CustomLog logs/host.some_domain.com-access_log common +# + +# +# diff --git a/docs/conf/magic b/docs/conf/magic new file mode 100644 index 00000000000..885b793aecb --- /dev/null +++ b/docs/conf/magic @@ -0,0 +1,371 @@ +# Magic data for mod_mime_magic Apache module (originally for file(1) command) +# The module is described in htdocs/manual/mod/mod_mime_magic.html +# +# The format is 4-5 columns: +# Column #1: byte number to begin checking from, ">" indicates continuation +# Column #2: type of data to match +# Column #3: contents of data to match +# Column #4: MIME type of result +# Column #5: MIME encoding of result (optional) + +#------------------------------------------------------------------------------ +# Localstuff: file(1) magic for locally observed files +# Add any locally observed files here. + +#------------------------------------------------------------------------------ +# end local stuff +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Java + +0 short 0xcafe +>2 short 0xbabe application/java + +#------------------------------------------------------------------------------ +# audio: file(1) magic for sound formats +# +# from Jan Nicolai Langfeldt , +# + +# Sun/NeXT audio data +0 string .snd +>12 belong 1 audio/basic +>12 belong 2 audio/basic +>12 belong 3 audio/basic +>12 belong 4 audio/basic +>12 belong 5 audio/basic +>12 belong 6 audio/basic +>12 belong 7 audio/basic + +>12 belong 23 audio/x-adpcm + +# DEC systems (e.g. DECstation 5000) use a variant of the Sun/NeXT format +# that uses little-endian encoding and has a different magic number +# (0x0064732E in little-endian encoding). +0 lelong 0x0064732E +>12 lelong 1 audio/x-dec-basic +>12 lelong 2 audio/x-dec-basic +>12 lelong 3 audio/x-dec-basic +>12 lelong 4 audio/x-dec-basic +>12 lelong 5 audio/x-dec-basic +>12 lelong 6 audio/x-dec-basic +>12 lelong 7 audio/x-dec-basic +# compressed (G.721 ADPCM) +>12 lelong 23 audio/x-dec-adpcm + +# Bytes 0-3 of AIFF, AIFF-C, & 8SVX audio files are "FORM" +# AIFF audio data +8 string AIFF audio/x-aiff +# AIFF-C audio data +8 string AIFC audio/x-aiff +# IFF/8SVX audio data +8 string 8SVX audio/x-aiff + +# Creative Labs AUDIO stuff +# Standard MIDI data +0 string MThd audio/unknown +#>9 byte >0 (format %d) +#>11 byte >1 using %d channels +# Creative Music (CMF) data +0 string CTMF audio/unknown +# SoundBlaster instrument data +0 string SBI audio/unknown +# Creative Labs voice data +0 string Creative\ Voice\ File audio/unknown +## is this next line right? it came this way... +#>19 byte 0x1A +#>23 byte >0 - version %d +#>22 byte >0 \b.%d + +# [GRR 950115: is this also Creative Labs? Guessing that first line +# should be string instead of unknown-endian long...] +#0 long 0x4e54524b MultiTrack sound data +#0 string NTRK MultiTrack sound data +#>4 long x - version %ld + +# Microsoft WAVE format (*.wav) +# [GRR 950115: probably all of the shorts and longs should be leshort/lelong] +# Microsoft RIFF +0 string RIFF audio/unknown +# - WAVE format +>8 string WAVE audio/x-wav + +#------------------------------------------------------------------------------ +# c-lang: file(1) magic for C programs or various scripts +# + +# XPM icons (Greg Roelofs, newt@uchicago.edu) +# ideally should go into "images", but entries below would tag XPM as C source +0 string /*\ XPM image/x-xbm 7bit + +# this first will upset you if you're a PL/1 shop... (are there any left?) +# in which case rm it; ascmagic will catch real C programs +# C or REXX program text +0 string /* text/plain +# C++ program text +0 string // text/plain + +#------------------------------------------------------------------------------ +# compress: file(1) magic for pure-compression formats (no archives) +# +# compress, gzip, pack, compact, huf, squeeze, crunch, freeze, yabba, whap, etc. +# +# Formats for various forms of compressed data +# Formats for "compress" proper have been moved into "compress.c", +# because it tries to uncompress it to figure out what's inside. + +# standard unix compress +0 string \037\235 application/octet-stream x-compress + +# gzip (GNU zip, not to be confused with [Info-ZIP/PKWARE] zip archiver) +0 string \037\213 application/octet-stream x-gzip + +# According to gzip.h, this is the correct byte order for packed data. +0 string \037\036 application/octet-stream +# +# This magic number is byte-order-independent. +# +0 short 017437 application/octet-stream + +# XXX - why *two* entries for "compacted data", one of which is +# byte-order independent, and one of which is byte-order dependent? +# +# compacted data +0 short 0x1fff application/octet-stream +0 string \377\037 application/octet-stream +# huf output +0 short 0145405 application/octet-stream + +# Squeeze and Crunch... +# These numbers were gleaned from the Unix versions of the programs to +# handle these formats. Note that I can only uncrunch, not crunch, and +# I didn't have a crunched file handy, so the crunch number is untested. +# Keith Waclena +#0 leshort 0x76FF squeezed data (CP/M, DOS) +#0 leshort 0x76FE crunched data (CP/M, DOS) + +# Freeze +#0 string \037\237 Frozen file 2.1 +#0 string \037\236 Frozen file 1.0 (or gzip 0.5) + +# lzh? +#0 string \037\240 LZH compressed data + +#------------------------------------------------------------------------------ +# frame: file(1) magic for FrameMaker files +# +# This stuff came on a FrameMaker demo tape, most of which is +# copyright, but this file is "published" as witness the following: +# +0 string \ +# and Anna Shergold +# +0 string \14 byte 12 (OS/2 1.x format) +#>14 byte 64 (OS/2 2.x format) +#>14 byte 40 (Windows 3.x format) +#0 string IC icon +#0 string PI pointer +#0 string CI color icon +#0 string CP color pointer +#0 string BA bitmap array + + +#------------------------------------------------------------------------------ +# lisp: file(1) magic for lisp programs +# +# various lisp types, from Daniel Quinlan (quinlan@yggdrasil.com) +0 string ;; text/plain 8bit +# Emacs 18 - this is always correct, but not very magical. +0 string \012( application/x-elc +# Emacs 19 +0 string ;ELC\023\000\000\000 application/x-elc + +#------------------------------------------------------------------------------ +# mail.news: file(1) magic for mail and news +# +# There are tests to ascmagic.c to cope with mail and news. +0 string Relay-Version: message/rfc822 7bit +0 string #!\ rnews message/rfc822 7bit +0 string N#!\ rnews message/rfc822 7bit +0 string Forward\ to message/rfc822 7bit +0 string Pipe\ to message/rfc822 7bit +0 string Return-Path: message/rfc822 7bit +0 string Path: message/news 8bit +0 string Xref: message/news 8bit +0 string From: message/rfc822 7bit +0 string Article message/news 8bit +#------------------------------------------------------------------------------ +# msword: file(1) magic for MS Word files +# +# Contributor claims: +# Reversed-engineered MS Word magic numbers +# + +0 string \376\067\0\043 application/msword +0 string \333\245-\0\0\0 application/msword + +# disable this one because it applies also to other +# Office/OLE documents for which msword is not correct. See PR#2608. +#0 string \320\317\021\340\241\261 application/msword + + + +#------------------------------------------------------------------------------ +# printer: file(1) magic for printer-formatted files +# + +# PostScript +0 string %! application/postscript +0 string \004%! application/postscript + +# Acrobat +# (due to clamen@cs.cmu.edu) +0 string %PDF- application/pdf + +#------------------------------------------------------------------------------ +# sc: file(1) magic for "sc" spreadsheet +# +38 string Spreadsheet application/x-sc + +#------------------------------------------------------------------------------ +# tex: file(1) magic for TeX files +# +# XXX - needs byte-endian stuff (big-endian and little-endian DVI?) +# +# From + +# Although we may know the offset of certain text fields in TeX DVI +# and font files, we can't use them reliably because they are not +# zero terminated. [but we do anyway, christos] +0 string \367\002 application/x-dvi +#0 string \367\203 TeX generic font data +#0 string \367\131 TeX packed font data +#0 string \367\312 TeX virtual font data +#0 string This\ is\ TeX, TeX transcript text +#0 string This\ is\ METAFONT, METAFONT transcript text + +# There is no way to detect TeX Font Metric (*.tfm) files without +# breaking them apart and reading the data. The following patterns +# match most *.tfm files generated by METAFONT or afm2tfm. +#2 string \000\021 TeX font metric data +#2 string \000\022 TeX font metric data +#>34 string >\0 (%s) + +# Texinfo and GNU Info, from Daniel Quinlan (quinlan@yggdrasil.com) +#0 string \\input\ texinfo Texinfo source text +#0 string This\ is\ Info\ file GNU Info text + +# correct TeX magic for Linux (and maybe more) +# from Peter Tobias (tobias@server.et-inf.fho-emden.de) +# +0 leshort 0x02f7 application/x-dvi + +# RTF - Rich Text Format +0 string {\\rtf application/rtf + +#------------------------------------------------------------------------------ +# animation: file(1) magic for animation/movie formats +# +# animation formats, originally from vax@ccwf.cc.utexas.edu (VaX#n8) +# MPEG file +0 string \000\000\001\263 video/mpeg +# +# The contributor claims: +# I couldn't find a real magic number for these, however, this +# -appears- to work. Note that it might catch other files, too, +# so BE CAREFUL! +# +# Note that title and author appear in the two 20-byte chunks +# at decimal offsets 2 and 22, respectively, but they are XOR'ed with +# 255 (hex FF)! DL format SUCKS BIG ROCKS. +# +# DL file version 1 , medium format (160x100, 4 images/screen) +0 byte 1 video/unknown +0 byte 2 video/unknown + diff --git a/docs/conf/mime.types b/docs/conf/mime.types new file mode 100644 index 00000000000..d53db0f4e88 --- /dev/null +++ b/docs/conf/mime.types @@ -0,0 +1,276 @@ +# This is a comment. I love comments. + +# This file controls what Internet media types are sent to the client for +# given file extension(s). Sending the correct media type to the client +# is important so they know how to handle the content of the file. +# Extra types can either be added here or by using an AddType directive +# in your config files. For more information about Internet media types, +# please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type +# registry is at . + +# MIME type Extension +application/EDI-Consent +application/EDI-X12 +application/EDIFACT +application/activemessage +application/andrew-inset ez +application/applefile +application/atomicmail +application/cals-1840 +application/commonground +application/cybercash +application/dca-rft +application/dec-dx +application/eshop +application/hyperstudio +application/iges +application/mac-binhex40 hqx +application/mac-compactpro cpt +application/macwriteii +application/marc +application/mathematica +application/msword doc +application/news-message-id +application/news-transmission +application/octet-stream bin dms lha lzh exe class +application/oda oda +application/pdf pdf +application/pgp-encrypted +application/pgp-keys +application/pgp-signature +application/pkcs10 +application/pkcs7-mime +application/pkcs7-signature +application/postscript ai eps ps +application/prs.alvestrand.titrax-sheet +application/prs.cww +application/prs.nprend +application/remote-printing +application/riscos +application/rtf rtf +application/set-payment +application/set-payment-initiation +application/set-registration +application/set-registration-initiation +application/sgml +application/sgml-open-catalog +application/slate +application/smil smi smil +application/vemmi +application/vnd.3M.Post-it-Notes +application/vnd.FloGraphIt +application/vnd.acucobol +application/vnd.anser-web-certificate-issue-initiation +application/vnd.anser-web-funds-transfer-initiation +application/vnd.audiograph +application/vnd.businessobjects +application/vnd.claymore +application/vnd.comsocaller +application/vnd.dna +application/vnd.dxr +application/vnd.ecdis-update +application/vnd.ecowin.chart +application/vnd.ecowin.filerequest +application/vnd.ecowin.fileupdate +application/vnd.ecowin.series +application/vnd.ecowin.seriesrequest +application/vnd.ecowin.seriesupdate +application/vnd.enliven +application/vnd.epson.salt +application/vnd.fdf +application/vnd.ffsns +application/vnd.framemaker +application/vnd.fujitsu.oasys +application/vnd.fujitsu.oasys2 +application/vnd.fujitsu.oasys3 +application/vnd.fujitsu.oasysgp +application/vnd.fujitsu.oasysprs +application/vnd.fujixerox.docuworks +application/vnd.hp-HPGL +application/vnd.hp-PCL +application/vnd.hp-PCLXL +application/vnd.hp-hps +application/vnd.ibm.MiniPay +application/vnd.ibm.modcap +application/vnd.intercon.formnet +application/vnd.intertrust.digibox +application/vnd.intertrust.nncp +application/vnd.is-xpr +application/vnd.japannet-directory-service +application/vnd.japannet-jpnstore-wakeup +application/vnd.japannet-payment-wakeup +application/vnd.japannet-registration +application/vnd.japannet-registration-wakeup +application/vnd.japannet-setstore-wakeup +application/vnd.japannet-verification +application/vnd.japannet-verification-wakeup +application/vnd.koan +application/vnd.lotus-1-2-3 +application/vnd.lotus-approach +application/vnd.lotus-freelance +application/vnd.lotus-organizer +application/vnd.lotus-screencam +application/vnd.lotus-wordpro +application/vnd.meridian-slingshot +application/vnd.mif mif +application/vnd.minisoft-hp3000-save +application/vnd.mitsubishi.misty-guard.trustweb +application/vnd.ms-artgalry +application/vnd.ms-asf +application/vnd.ms-excel xls +application/vnd.ms-powerpoint ppt +application/vnd.ms-project +application/vnd.ms-tnef +application/vnd.ms-works +application/vnd.music-niff +application/vnd.musician +application/vnd.netfpx +application/vnd.noblenet-directory +application/vnd.noblenet-sealer +application/vnd.noblenet-web +application/vnd.novadigm.EDM +application/vnd.novadigm.EDX +application/vnd.novadigm.EXT +application/vnd.osa.netdeploy +application/vnd.powerbuilder6 +application/vnd.powerbuilder6-s +application/vnd.rapid +application/vnd.seemail +application/vnd.shana.informed.formtemplate +application/vnd.shana.informed.interchange +application/vnd.shana.informed.package +application/vnd.street-stream +application/vnd.svd +application/vnd.swiftview-ics +application/vnd.truedoc +application/vnd.visio +application/vnd.webturbo +application/vnd.wrq-hp3000-labelled +application/vnd.wt.stf +application/vnd.xara +application/vnd.yellowriver-custom-menu +application/wita +application/wordperfect5.1 +application/x-bcpio bcpio +application/x-cdlink vcd +application/x-chess-pgn pgn +application/x-compress +application/x-cpio cpio +application/x-csh csh +application/x-director dcr dir dxr +application/x-dvi dvi +application/x-futuresplash spl +application/x-gtar gtar +application/x-gzip +application/x-hdf hdf +application/x-javascript js +application/x-koan skp skd skt skm +application/x-latex latex +application/x-netcdf nc cdf +application/x-sh sh +application/x-shar shar +application/x-shockwave-flash swf +application/x-stuffit sit +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-tar tar +application/x-tcl tcl +application/x-tex tex +application/x-texinfo texinfo texi +application/x-troff t tr roff +application/x-troff-man man +application/x-troff-me me +application/x-troff-ms ms +application/x-ustar ustar +application/x-wais-source src +application/x400-bp +application/xml +application/zip zip +audio/32kadpcm +audio/basic au snd +audio/midi mid midi kar +audio/mpeg mpga mp2 mp3 +audio/vnd.qcelp +audio/x-aiff aif aiff aifc +audio/x-pn-realaudio ram rm +audio/x-pn-realaudio-plugin rpm +audio/x-realaudio ra +audio/x-wav wav +chemical/x-pdb pdb xyz +image/bmp bmp +image/cgm +image/g3fax +image/gif gif +image/ief ief +image/jpeg jpeg jpg jpe +image/naplps +image/png png +image/prs.btif +image/tiff tiff tif +image/vnd.dwg +image/vnd.dxf +image/vnd.fpx +image/vnd.net-fpx +image/vnd.svf +image/vnd.xiff +image/x-cmu-raster ras +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +message/delivery-status +message/disposition-notification +message/external-body +message/http +message/news +message/partial +message/rfc822 +model/iges igs iges +model/mesh msh mesh silo +model/vnd.dwf +model/vrml wrl vrml +multipart/alternative +multipart/appledouble +multipart/byteranges +multipart/digest +multipart/encrypted +multipart/form-data +multipart/header-set +multipart/mixed +multipart/parallel +multipart/related +multipart/report +multipart/signed +multipart/voice-message +text/css css +text/directory +text/enriched +text/html html htm +text/plain asc txt +text/prs.lines.tag +text/rfc822-headers +text/richtext rtx +text/rtf rtf +text/sgml sgml sgm +text/tab-separated-values tsv +text/uri-list +text/vnd.abc +text/vnd.flatland.3dml +text/vnd.fmi.flexstor +text/vnd.in3d.3dml +text/vnd.in3d.spot +text/vnd.latex-z +text/x-setext etx +text/xml xml +video/mpeg mpeg mpg mpe +video/quicktime qt mov +video/vnd.motorola.video +video/vnd.motorola.videop +video/vnd.vivo +video/x-msvideo avi +video/x-sgi-movie movie +x-conference/x-cooltalk ice diff --git a/docs/docroot/index.html.ca b/docs/docroot/index.html.ca new file mode 100644 index 00000000000..a71a6853569 --- /dev/null +++ b/docs/docroot/index.html.ca @@ -0,0 +1,51 @@ + + + + Pàgina de prova de l'instal·lació d'Apache + + + +

+ Funciona ! El servidor web Apache està instal·lat + en aquest lloc  ! +

+

+ Si veu aquesta pàgina, es que els propietaris d'aquest domini + acaben d'instal·lar satisfactòriament el + servidor web Apache. + Ara han d'afegir contingut en aquest directori i substituir aquesta pàgina, + o bè dirigir aquest servidor cap al contingut real. +

+
+
+ Si està veien aquesta pàgina i no es la que esperava, posis en + contacte amb + l'administrador web d'aquest lloc. + (Provi d'enviar un correu electrònic a <Webmaster@domain>.) + Encara que aquest lloc està utilitzant el programari Apache, es gairebé segur + que no tè res a veure amb el Grup Apache, per tant + no enviï cap correu electrònic als autors d'Apache referent a aquest lloc o al seu contingut. + Si es que ho fa, el seu missatge serà ignorat. +
+
+

+ La + documentació + d'Apache ha estat inclosa en aquesta distribució. +

+

+ L'administrador de web pot utilitzar lliurement la imatge d'aquí sota + en el servidor web equipat amb el programari Apache. + Gràcies per utilitzar Apache ! +

+
+ +
+ + diff --git a/docs/docroot/index.html.de b/docs/docroot/index.html.de new file mode 100644 index 00000000000..a5420e933a0 --- /dev/null +++ b/docs/docroot/index.html.de @@ -0,0 +1,61 @@ + + + + Testseite fuer die Apache-Installation auf dieser Web Site + + + + +

+ Es klappt! Der Apache Web-Server ist auf dieser Web-Site installiert! +

+

+ Wenn Sie diese Seite sehen, dann bedeutet das, dass die + Eigentümer dieser Domäne soeben einen neuen + Apache Web-Server + erfolgreich installiert haben. Jetzt muss noch der + richtige Web-Inhalt zugefügt und diese Platzhalter-Seite + ersetzt werden (oder der Web-Server für den Zugriff auf den + richtigen Inhalt umkonfiguriert werden). +

+
+
+ Wenn Sie diese Seite an Stelle einer anderen erwarteten Web-Site + sehen sollten, dann nehmen Sie bitte Kontakt mit dem + Eigentümer dieser Site auf (Versuchen Sie, eine EMail + an <Webmaster@domänenname> + zu senden)!
+

+ Obwohl der Eigentümer dieser Domäne die Apache + Web-Server Software verwendet, hat diese Web-Site ziemlich sicher + keinerlei Verbindung mit der Apache Software Foundation + (die diese Software kostenlos vertreibt). Es besteht also + keinerlei Veranlassung, eine EMail an + die Entwickler der Software zu senden. Sollten Sie das dennoch + tun, wird Ihre Mail stillschweigend + ignoriert. +

+
+

+ Die englische Online-Dokumentation für die + Apache Web-Server Software ist Bestandteil dieser + Software-Distribution. +

+

+ Dem WebMaster dieser Web-Site steht es frei, das + untenstehende "Powered by Apache"-Logo auf einem Apache-basierten + Web-Server zu verwenden.
+ Vielen Dank, dass Sie Apache gewählt haben! +

+
+ Powered by Apache! +
+ + diff --git a/docs/docroot/index.html.dk b/docs/docroot/index.html.dk new file mode 100644 index 00000000000..22b3931aac5 --- /dev/null +++ b/docs/docroot/index.html.dk @@ -0,0 +1,47 @@ + + + + Prøveside for Apache installationen + + + +

+ Det virkede! Apache Webserveren er installeret på denne maskine! +

+

+ Hvis du kan se denne side, så har dem som ejer dette domæne lige + installeret Apache Webserveren. + Du må nu placere web sider i dette directory og fjerne denne side, eller + konfigurere serveren til at anvende et andet directory. +

+
+
+ Hvis du ser denne side istedet for en anden forventet side, kontakt + venligst administratoren for den server du prøver at kontakte. + (Prøv at sende mail til <Webmaster@domain>.) + Apachegruppen har ikke noget at gøre med denne site, så det vil ikke + hjælpe at sende mail to Apache udviklerne angående denne site. +
+
+

+ Apache- + documentationen + er inkluderet med denne distribution. +

+

+ Webmasteren af denne site må gerne bruge den følgende grafik + på en Apache-drevet webserver. Mange tak for at bruge Apache! +

+
+ +
+ + diff --git a/docs/docroot/index.html.ee b/docs/docroot/index.html.ee new file mode 100644 index 00000000000..879c30e3815 --- /dev/null +++ b/docs/docroot/index.html.ee @@ -0,0 +1,53 @@ + + + + Apache veebiserveri installatsiooni testlehekülg + + + + +

+ Kujuta pilti, käima läks! Apache veebiserver on installeeritud! +

+

+ Kui Sa näed seda lehekülge, siis selle domeeni omanikud on + edukalt hakkama saanud Apache veebiserveri + installeerimisega. Järgmiseks peaks nad selle lehekülje asendama + misiganes materjaliga, mida nad oma veebisaidis näidata tahavad, + või siis juhatama veebiserverile kätte tegeliku materjali + asukoha. +

+
+
+ Kui Sa ootasid siin leheküljel näha hoopis midagi muud, + siis palun võta ühendust selle veebisaidi + administraatoriga. (Võid näiteks kirjutada aadressil + <Webmaster@domain>.) Kuigi see veebisait + kasutab Apache tarkvara, pole ta peaaegu kindlasti mitte Apache Group'iga + mingil muul moel seotud. Seega, palun ära kirjuta selle saidi + asjus Apache autoritele. Kui sa seda teed, siis sinu kirja + ignoreeritakse. +
+
+

+ Ka Apache + dokumentatsioon + on selles distributsioonis olemas. +

+

+ Kohalik veebmeister võib vabalt kasutada allpool olevat + pilti oma Apache veebiserveril. Aitäh Apachet kasutamast! +

+
+ +
+ + diff --git a/docs/docroot/index.html.en b/docs/docroot/index.html.en new file mode 100644 index 00000000000..73f5eca36c7 --- /dev/null +++ b/docs/docroot/index.html.en @@ -0,0 +1,38 @@ + + + + Test Page for Apache Installation + + + + + +

+If you can see this, it means that the installation of the Apache web server software on this system was successful. You may now add content to this directory and replace this page. + +


+ +

Seeing this instead of the website you expected?

+ +

+This page is here because the site administrator has changed the configuration of this web server. +Please contact the person responsible for maintaining this server with questions. The Apache Software Foundation, which wrote the web server software this site administrator is using, has nothing to do with maintaining this site and cannot help resolve configuration issues. + +


+ + +

+The Apache documentation has been included with this distribution. + +

+You are free to use the image below on an Apache-powered web server. Thanks for using Apache! + +

+ + diff --git a/docs/docroot/index.html.es b/docs/docroot/index.html.es new file mode 100644 index 00000000000..b5f81070b74 --- /dev/null +++ b/docs/docroot/index.html.es @@ -0,0 +1,51 @@ + + + + Pagína para probar la instalación de Apache + + + +

+ ¡Funcionó! ¡El Servidor de Red Apache ha sido instalado en ese sitio! +

+

+ Si usted puede ver esta pagína, entonces los dueños de esta + maquína han instalado el Servidor de + Red Apache con exíto. Ahora deben añadir contenido a este directorio + y reemplazar esta pagína, ó apuntar este servidor al contenido real. +

+
+
+ Si usted esta leyendo esta pagína y no es lo que esperaba, por favor + contacte el administrador de este sitio. + (Trate de enviar correo electrónico a <Webmaster@domain>.) + Aunque este sitio esta utilizando el programa Apache es casi seguro + que no tiene ninguna conexión con el Apache Group, por eso favor de + no enviar correo sobre este sitio o su contenido a los autores de + Apache. Si lo hace, su mensaje sera + ignorado. +
+
+

+ La + documentación + de Apache ha sido incluida en esta distribución. +

+

+ El administrador del sitio esta invitado a usar la siguiente + imagen para indicar que su sitio es servido por Apache. + ¡Gracias por usar Apache! +

+
+ +
+ + diff --git a/docs/docroot/index.html.et b/docs/docroot/index.html.et new file mode 100644 index 00000000000..879c30e3815 --- /dev/null +++ b/docs/docroot/index.html.et @@ -0,0 +1,53 @@ + + + + Apache veebiserveri installatsiooni testlehekülg + + + + +

+ Kujuta pilti, käima läks! Apache veebiserver on installeeritud! +

+

+ Kui Sa näed seda lehekülge, siis selle domeeni omanikud on + edukalt hakkama saanud Apache veebiserveri + installeerimisega. Järgmiseks peaks nad selle lehekülje asendama + misiganes materjaliga, mida nad oma veebisaidis näidata tahavad, + või siis juhatama veebiserverile kätte tegeliku materjali + asukoha. +

+
+
+ Kui Sa ootasid siin leheküljel näha hoopis midagi muud, + siis palun võta ühendust selle veebisaidi + administraatoriga. (Võid näiteks kirjutada aadressil + <Webmaster@domain>.) Kuigi see veebisait + kasutab Apache tarkvara, pole ta peaaegu kindlasti mitte Apache Group'iga + mingil muul moel seotud. Seega, palun ära kirjuta selle saidi + asjus Apache autoritele. Kui sa seda teed, siis sinu kirja + ignoreeritakse. +
+
+

+ Ka Apache + dokumentatsioon + on selles distributsioonis olemas. +

+

+ Kohalik veebmeister võib vabalt kasutada allpool olevat + pilti oma Apache veebiserveril. Aitäh Apachet kasutamast! +

+
+ +
+ + diff --git a/docs/docroot/index.html.fr b/docs/docroot/index.html.fr new file mode 100644 index 00000000000..7ffa0979d61 --- /dev/null +++ b/docs/docroot/index.html.fr @@ -0,0 +1,53 @@ + + + + Page de test de l'installation d'Apache + + + +

+ Ça fonctionne ! Le serveur web Apache est installé sur + ce site ! +

+

+ Si vous lisez cette page, c'est que les propriétaires de ce domaine + viennent d'installer le + serveur web Apache + avec succès. Ils doivent maintenant ajouter du contenu à ce + répertoire et remplacer cette page, ou bien faire pointer le serveur + vers l'endroit où se trouve le contenu réel du site. +

+
+
+ Si vous voyez cette page au lieu du site attendu, veuillez contacter + l'administrateur du site concerné. + (Essayez d'envoyer un email à + <Webmaster@domaine>.) + Bien que ce site utilise le logiciel Apache, il n'a certainement + rien à voir avec le Groupe Apache, par conséquent + veuillez ne pas envoyer d'email aux + auteurs d'Apache concernant ce site ou son contenu. Si vous le faites, + votre message sera ignoré. +
+
+

+ La + documentation + Apache est incluse dans cette distribution. +

+

+ Le webmaster de ce site peut librement utiliser l'image ci-dessous + sur un site web utilisant le logiciel Apache. Merci d'avoir choisi + Apache ! +

+
+ +
+ + diff --git a/docs/docroot/index.html.he.iso8859-8 b/docs/docroot/index.html.he.iso8859-8 new file mode 100644 index 00000000000..84254886bc7 --- /dev/null +++ b/docs/docroot/index.html.he.iso8859-8 @@ -0,0 +1,62 @@ + + + + + Apache-ä úøù úð÷úäì ïåéñð óã + + + + +
+ +

+ +
äæä ãåîòä úà äàåø äúà íà +
Apache èðøèðéàä úøù ìù äð÷úääù øîà äæ +
!!!äçìöäá äøáò +
êìù íéöá÷ä úà óéñåäì ìåëé äúà åéùëò +
.äæä óãä úà óéìçäìå úàæä äöéçîì + +

+ +


+ +

?úéôéöù øúàä íå÷îá úàæ àåø

+
+

+
äæä øúàä ìò éàøçà éë ,ïàë äöîð äæä ãåîòä +
.äæ èðøèðéàä úøùä ìù äéöøåâéôðå÷ä úà äðéù +
.úåìàù íà úøùä éàøçà íà øù÷ øåöéì àð +
úà äøöé øùà Apache Software Foundation +
úøùä ú÷åæçú ìò úéàøçà äðéà ,äæ úøùä úðëåú +
äéöøåâéôðå÷ úåéòá ïåøúôá äééòñì äìåëé äðéàå + .íäùìë +

+ +


+ +
+ + +

+
êåúá íéòöîð äéöèðîå÷åãä éëîñî + .úøùä úðëåú úìéáç + + +

+
ãåîòä úéúçúá äöîðä ìîñá ùîúùäì éàùø äúà +
.Apache é''ò ìòôåîù úøùä ìò ÷ø + + !!!Apache-á ùåîéùä ìò äãåú + +

+ +
+ + diff --git a/docs/docroot/index.html.it b/docs/docroot/index.html.it new file mode 100644 index 00000000000..ab32d04a509 --- /dev/null +++ b/docs/docroot/index.html.it @@ -0,0 +1,38 @@ + + + + + Pagina di prova dell'installazione di un sito Web con Apache + + + + +
+

+Funziona! Il Server Web Apache e' stato installato su questo sito Web!

+Se riuscite a vedere questa pagina, allora vuol dire che coloro che gestiscono +questo dominio hanno appena installato il software Web Server +Apache correttamente. Ora e' necessario aggiungere il vostro materiale +in questo direttorio e sostituire questa pagina di prova, oppure configurare +il server per far riferimento al vostro materiale se collocato altrove. +

+


+
Se state vedendo questa pagina invece del sito che pensavate, +vi preghiamo di contattare l'amministratore del sito in questione. +(Provate ad inviare un email a <Webmaster@domain>.)  +Nonostante questo sito stia utilizzando il software Apache, questo non +vi garantisce nessun tipo di contatto diretto al Gruppo Apache, quindi +vi preghiamo di non inviare email riguardanti questo sito o al materiale +in esso contenuto agli autori di Apache. Ogni messaggio del genere verra' +ignorato.
+ +
+
La documentazione relativa di Apache +e' inclusa nella distribuzione. +

Il Webmaster di questo sito e' libero di utilizzare l'immagine qui sotto +su qualsiasi Web server potenziato con Apache. Grazie per aver scelto Apache! +

+

+ + + diff --git a/docs/docroot/index.html.ja.iso2022-jp b/docs/docroot/index.html.ja.iso2022-jp new file mode 100644 index 00000000000..0a4e5452613 --- /dev/null +++ b/docs/docroot/index.html.ja.iso2022-jp @@ -0,0 +1,42 @@ + + + + + Apache $B%$%s%9%H!<%k;~$N%F%9%H%Z!<%8(B + + + + + +

+$B$b$7$3$N%Z!<%8$,FI$a$?$N$G$"$l$P!"(BApache $B%&%'%V%5!<%P(B$B$N%$%s%9%H!<%k$,$3$N7W;;5!$GL5;v$K=*N;$7$?$3$H$r0UL#$7$^$9!#$"$J$?$O!"$3$N%G%#%l%/%H%j$KJ8=q$r2C$($?$j!"$3$N%Z!<%8$rCV$-$+$($k$3$H$,$G$-$^$9!#(B +

+
+ +

$B$"$J$?$NM=A[$KH?$7$F!"$3$N%Z!<%8$,8+$($F$$$k$G$7$g$&$+(B?

+ +

+$B$3$N%Z!<%8$O!"%5%$%H4IM}$B$3$N%5!<%P$r4IM}$9$k@UG$$r;}$C$F$$$kJ}$KO"Mm$r$H$C$F(B$B$/$@$5$$!#$3$N%5%$%H4IM} +


+ + +

+Apache $B$K4X$9$k(B$BJ8=q(B $B$O!"$3$N(B web $B%5!<%PG[I[J*$NCf$K4^$^$l$F$$$^$9!#(B +

+

+$B0J2<$N2hA|$O!"(BApache $B$rMxMQ$7$F$$$k(B web $B%5!<%P$G<+M3$K;H$&$3$H$,$G$-$^$9!#(BApache $B$r$4MxMQ$$$?$@$-!"$"$j$,$H$&$4$6$$$^$9(B! +

+
+ +
+ + diff --git a/docs/docroot/index.html.nl b/docs/docroot/index.html.nl new file mode 100644 index 00000000000..b00747cd2a9 --- /dev/null +++ b/docs/docroot/index.html.nl @@ -0,0 +1,54 @@ + + + + Test Pagina voor de Apache Installatie op deze Web Site + + + + +

+ Alles Werkt! De Apache Web Server is geinstalleerd op deze Web Site +

+

+ Mocht u deze pagina zien, dan betekent dat, dat de eigenaren van dit + domein zojuist een nieuwe Apache Web + Server hebben geinstalleerd. Nu moet men nog de echte inhoud gaan + toevoegen, en moet men deze pagina gaan vervangen door de echte web + site. +

+
+
+ Mocht u deze pagina zien, in plaats van de pagina of web site die u + verwachtte, neem dan contact op met de beheerder van + deze site. Bijvoorbeeld door een berichtje te sturen naar + <Webmaster@dit-domain>. +

+ Alhoewel deze web site gebruik maakt van de Apache Software is er + verder geen enkele relatie tussen de beheerders van deze web site en + de Apache Groep (die de web software geschreven heeft). Het heeft dus + absoluut + geen zin de web software auteurs een emailtje te sturen. Mocht u dat + toch doen, dan wordt uw bericht gewoon genegeerd. +

+
+

+ De + handleiding voor de apache web + server software maakt deel uit van deze distributie. +

+

+ Als beheerder of webmaster is het u toegestaan het onderstaande + plaatje vrijelijk te gebruiken op uw 'Apache Powered' web site. Bedankt + voor het kiezen voor, en gebruiken van, Apache! +

+
+ Klein 'Powered by Apache' Logotje +
+ + diff --git a/docs/docroot/index.html.no b/docs/docroot/index.html.no new file mode 100644 index 00000000000..b69c4ce163c --- /dev/null +++ b/docs/docroot/index.html.no @@ -0,0 +1,49 @@ + + + + Testside for Apache Installasjon på Webside + + + +

+ Det gikk bra! Det Apache Web Server Er Nå Installert her! +

+

+ Hvis du kan se denne siden, det betyr at de som eier denne domene (domain) + har nettopp greid å installere Apache Web server + software. De må tilføye innhold til denne katalog og erstatte denne + plassholder siden, ellers peke serveren til de virkelige innhold. +

+
+
+ Hvis du ser denne siden istedenfor den du ventet for, vennligst + ta kontakt med websever administratøren. + (Prøv å sende e-post til <Webmaster@domene>.) + Selv om denne hjemmesiden driver med Apache software, har den ingen annen + tilknytting til Apache Gruppen. Vennligst ikke send post angående denne hjemmesiden eller + dets innhold til Apache programmerere. Hvis du gjør det, din beskjed blir + oversett. +
+
+

+ Den Apache + dokumentasjon + er inkludert i denne distribusjonen. +

+

+ Denne sidens Webmaster kan bruke det grafikk nedenfør på en Apache-drevet Webserver. + Takk for at du bruker Apache! +

+
+ +
+ + diff --git a/docs/docroot/index.html.pt b/docs/docroot/index.html.pt new file mode 100644 index 00000000000..4e2645f5d8a --- /dev/null +++ b/docs/docroot/index.html.pt @@ -0,0 +1,42 @@ + + + + Página de teste da instalação do Apache Web Server + + + + + + +

Se está a ver esta página significa que a instalação + do Apache Web Server foi efectuada + com sucesso. Pode agora adicionar conteúdo a esta directoria e substituir + esta página. +


+ +

Está a ver esta página em vez do website que + esperava?

+ +

Esta página está neste momento activa porque o administrador + deste site alterou a configuração do web server. Por favor contacte + a pessoa responsável pela manutenção deste servidor. + A Fundação Apache Software (Apache Software Foundation), apesar + de ter produzido o web server que o administrador deste site está a usar, + não tem qualquer tipo de responsabilidade pela manutenção + deste nem pode ajudar a resolver problemas de configuração. +


+ + +

A documentação foi incluída + juntamente com esta distribuição. +

A imagem abaixo pode ser usada livremente em qualquer site presente num servidor + com o Apache Web Server instalado. Obrigado por usar o Apache Web Server! +

+ + diff --git a/docs/docroot/index.html.pt-br b/docs/docroot/index.html.pt-br new file mode 100644 index 00000000000..72428d5a2a9 --- /dev/null +++ b/docs/docroot/index.html.pt-br @@ -0,0 +1,55 @@ + + + + Pagina teste para a instalacao do Apache no Web Site + + + + +

+ Funcionou! O Apache Web Server esta instalado neste Web Site! +

+

+ Se conseguir ver esta pagina, entao as pessoas que possuem este + dominio acabaram + de instalar o software Apache Web + server + com sucesso. Agora tem de adicionar o conteudo a esta directoria + e substituir esta pagina "placeholder", ou apontar o servidor para o + seu conteudo real. +

+
+
+ Se esta a ver esta pagina em vez do site que esperava, por favor + contacte o administrador do site envolvido. + (Tente enviar um e-mail para + <Webmaster@domain>.) + Apesar de neste site estar a + correr o software Apache e quase certo que nao tem + outra ligacao com o Grupo Apache, por isso por favor nao envie + e-mails acerca deste + site ou do seu conteudo para os autores do Apache. Se o fizer, a + sua mensagem vai ser + ignorada. +
+
+

+ O Apache documentation + foi incluido com esta distribuicao. +

+

+ O Webmaster deste site e livre para utilizar a imagem abaixo num + Web server instalado com o Apache. Obrigado por utilizar o Apache! +

+
+ +
+ + diff --git a/docs/docroot/index.html.ru.cp-1251 b/docs/docroot/index.html.ru.cp-1251 new file mode 100644 index 00000000000..bfb8a166351 --- /dev/null +++ b/docs/docroot/index.html.ru.cp-1251 @@ -0,0 +1,34 @@ + + + + + + Òåñòîâàÿ ñòðàíèöà óñòàíîâêè Apache + + + +Åñëè Âû ýòî âèäèòå, ýòî çíà÷èò, ÷òî óñòàíîâêà +ÏÎ âåá-ñåðâåðà Apache íà ýòîé +ñèñòåìå çàâåðøèëàñü óñïåøíî. Âû ìîæåòå òåïåðü äîáàâëÿòü ñîäåðæèìîå â ýòó +äèðåêòîðèþ è çàìåíèòü ýòó ñòðàíèöó. +

+


+
+

+Âû âèäèòå ýòî âìåñòî îæèäàåìîé ñòðàíèöû?

+Ýòà ñòðàíèöà íàõîäèòñÿ çäåñü ïîòîìó, ÷òî àäìèíèñòðàòîð ñèñòåìû èçìåíèë +êîíôèãóðàöèþ ýòîãî âåá-ñåðâåðà. Ïîæàëóéñòà, ñâÿæèòåñü ñ ëèöîì, îòâåòñâåííûì +çà ïîääåðæêó ýòîãî ñåðâåðà äëÿ âûÿñíåíèÿ âàøèõ âîïðîñîâ. Apache Software +Foundation, àâòîð ÏÎ âåá-ñåðâåðà, êîòîðûì ïîëüçóåòñÿ àäìèíèñòðàòîð ýòîé +ñèñòåìû, íå ñâÿçàí ñ ïîääåðæêîé ýòîé ñèñòåìû è íå ìîæåò ïîìî÷ü Âàì +ðàçðåøèòü ïðîáëåìû êîíôèãóðàöèè. +

+


+

Äîêóìåíòàöèÿ ïî âåá-ñåðâåðó Apache ïðèëàãàåòñÿ +ê êîìïëåêòó ÏÎ. +

Âû ìîæåòå ñâîáîäíî èñïîëüçîâàòü ïèêòîãðàììó, íàõîäÿùóþñÿ íèæå, íà âåá-ñåðâåðå, +èñïîëüçóþùèì ÏÎ Apache. Ñïàñèáî çà èñïîëüçîâàíèå Apache! +

+ + + diff --git a/docs/docroot/index.html.ru.cp866 b/docs/docroot/index.html.ru.cp866 new file mode 100644 index 00000000000..b9f2615f379 --- /dev/null +++ b/docs/docroot/index.html.ru.cp866 @@ -0,0 +1,34 @@ + + + + + + ’¥á⮢ ï áâà ­¨æ  ãáâ ­®¢ª¨ Apache + + + +…᫨ ‚ë íâ® ¢¨¤¨â¥, íâ® §­ ç¨â, çâ® ãáâ ­®¢ª  +Ž ¢¥¡-á¥à¢¥à  Apache ­  í⮩ +á¨á⥬¥ § ¢¥à訫 áì ãᯥ譮. ‚ë ¬®¦¥â¥ ⥯¥àì ¤®¡ ¢«ïâì ᮤ¥à¦¨¬®¥ ¢ íâã +¤¨à¥ªâ®à¨î ¨ § ¬¥­¨âì íâã áâà ­¨æã. +

+


+
+

+‚ë ¢¨¤¨â¥ íâ® ¢¬¥áâ® ®¦¨¤ ¥¬®© áâà ­¨æë?

+â  áâà ­¨æ  ­ å®¤¨âáï §¤¥áì ¯®â®¬ã, çâ®  ¤¬¨­¨áâà â®à á¨áâ¥¬ë ¨§¬¥­¨« +ª®­ä¨£ãà æ¨î í⮣® ¢¥¡-á¥à¢¥à . ®¦ «ã©áâ , á¢ï¦¨â¥áì á «¨æ®¬, ®â¢¥âᢥ­­ë¬ +§  ¯®¤¤¥à¦ªã í⮣® á¥à¢¥à  ¤«ï ¢ëïá­¥­¨ï ¢ è¨å ¢®¯à®á®¢. Apache Software +Foundation,  ¢â®à Ž ¢¥¡-á¥à¢¥à , ª®â®àë¬ ¯®«ì§ã¥âáï  ¤¬¨­¨áâà â®à í⮩ +á¨á⥬ë, ­¥ á¢ï§ ­ á ¯®¤¤¥à¦ª®© í⮩ á¨áâ¥¬ë ¨ ­¥ ¬®¦¥â ¯®¬®ç젂 ¬ +à §à¥è¨âì ¯à®¡«¥¬ë ª®­ä¨£ãà æ¨¨. +

+


+

„®ªã¬¥­â æ¨ï ¯® ¢¥¡-á¥à¢¥àã Apache ¯à¨« £ ¥âáï +ª ª®¬¯«¥ªâ㠏Ž. +

‚ë ¬®¦¥â¥ ᢮¡®¤­® ¨á¯®«ì§®¢ âì ¯¨ªâ®£à ¬¬ã, ­ å®¤ïéãîáï ­¨¦¥, ­  ¢¥¡-á¥à¢¥à¥, +¨á¯®«ì§ãî騬 Ž Apache. ‘¯ á¨¡® §  ¨á¯®«ì§®¢ ­¨¥ Apache! +

+ + + diff --git a/docs/docroot/index.html.ru.iso-ru b/docs/docroot/index.html.ru.iso-ru new file mode 100644 index 00000000000..114d9f75054 --- /dev/null +++ b/docs/docroot/index.html.ru.iso-ru @@ -0,0 +1,34 @@ + + + + + + ÂÕáâÞÒÐï áâàÐÝØæÐ ãáâÐÝÞÒÚØ Apache + + + +µáÛØ ²ë íâÞ ÒØÔØâÕ, íâÞ ×ÝÐçØâ, çâÞ ãáâÐÝÞÒÚÐ +¿¾ ÒÕÑ-áÕàÒÕàÐ Apache ÝÐ íâÞÙ +áØáâÕÜÕ ×ÐÒÕàèØÛÐáì ãáßÕèÝÞ. ²ë ÜÞÖÕâÕ âÕßÕàì ÔÞÑÐÒÛïâì áÞÔÕàÖØÜÞÕ Ò íâã +ÔØàÕÚâÞàØî Ø ×ÐÜÕÝØâì íâã áâàÐÝØæã. +

+


+
+

+²ë ÒØÔØâÕ íâÞ ÒÜÕáâÞ ÞÖØÔÐÕÜÞÙ áâàÐÝØæë?

+ÍâÐ áâàÐÝØæÐ ÝÐåÞÔØâáï ×ÔÕáì ßÞâÞÜã, çâÞ ÐÔÜØÝØáâàÐâÞà áØáâÕÜë Ø×ÜÕÝØÛ +ÚÞÝäØÓãàÐæØî íâÞÓÞ ÒÕÑ-áÕàÒÕàÐ. ¿ÞÖÐÛãÙáâÐ, áÒïÖØâÕáì á ÛØæÞÜ, ÞâÒÕâáÒÕÝÝëÜ +×Ð ßÞÔÔÕàÖÚã íâÞÓÞ áÕàÒÕàÐ ÔÛï ÒëïáÝÕÝØï ÒÐèØå ÒÞßàÞáÞÒ. Apache Software +Foundation, ÐÒâÞà ¿¾ ÒÕÑ-áÕàÒÕàÐ, ÚÞâÞàëÜ ßÞÛì×ãÕâáï ÐÔÜØÝØáâàÐâÞà íâÞÙ +áØáâÕÜë, ÝÕ áÒï×ÐÝ á ßÞÔÔÕàÖÚÞÙ íâÞÙ áØáâÕÜë Ø ÝÕ ÜÞÖÕâ ßÞÜÞçì ²ÐÜ +àÐ×àÕèØâì ßàÞÑÛÕÜë ÚÞÝäØÓãàÐæØØ. +

+


+

´ÞÚãÜÕÝâÐæØï ßÞ ÒÕÑ-áÕàÒÕàã Apache ßàØÛÐÓÐÕâáï +Ú ÚÞÜßÛÕÚâã ¿¾. +

²ë ÜÞÖÕâÕ áÒÞÑÞÔÝÞ ØáßÞÛì×ÞÒÐâì ߨÚâÞÓàÐÜÜã, ÝÐåÞÔïéãîáï ÝØÖÕ, ÝÐ ÒÕÑ-áÕàÒÕàÕ, +ØáßÞÛì×ãîéØÜ ¿¾ Apache. ÁßÐáØÑÞ ×Ð ØáßÞÛì×ÞÒÐÝØÕ Apache! +

+ + + diff --git a/docs/docroot/index.html.ru.koi8-r b/docs/docroot/index.html.ru.koi8-r new file mode 100644 index 00000000000..685b56207d0 --- /dev/null +++ b/docs/docroot/index.html.ru.koi8-r @@ -0,0 +1,34 @@ + + + + + + ôÅÓÔÏ×ÁÑ ÓÔÒÁÎÉÃÁ ÕÓÔÁÎÏ×ËÉ Apache + + + +åÓÌÉ ÷Ù ÜÔÏ ×ÉÄÉÔÅ, ÜÔÏ ÚÎÁÞÉÔ, ÞÔÏ ÕÓÔÁÎÏ×ËÁ +ðï ×ÅÂ-ÓÅÒ×ÅÒÁ Apache ÎÁ ÜÔÏÊ +ÓÉÓÔÅÍÅ ÚÁ×ÅÒÛÉÌÁÓØ ÕÓÐÅÛÎÏ. ÷Ù ÍÏÖÅÔÅ ÔÅÐÅÒØ ÄÏÂÁ×ÌÑÔØ ÓÏÄÅÒÖÉÍÏÅ × ÜÔÕ +ÄÉÒÅËÔÏÒÉÀ É ÚÁÍÅÎÉÔØ ÜÔÕ ÓÔÒÁÎÉÃÕ. +

+


+
+

+÷Ù ×ÉÄÉÔÅ ÜÔÏ ×ÍÅÓÔÏ ÏÖÉÄÁÅÍÏÊ ÓÔÒÁÎÉÃÙ?

+üÔÁ ÓÔÒÁÎÉÃÁ ÎÁÈÏÄÉÔÓÑ ÚÄÅÓØ ÐÏÔÏÍÕ, ÞÔÏ ÁÄÍÉÎÉÓÔÒÁÔÏÒ ÓÉÓÔÅÍÙ ÉÚÍÅÎÉÌ +ËÏÎÆÉÇÕÒÁÃÉÀ ÜÔÏÇÏ ×ÅÂ-ÓÅÒ×ÅÒÁ. ðÏÖÁÌÕÊÓÔÁ, Ó×ÑÖÉÔÅÓØ Ó ÌÉÃÏÍ, ÏÔ×ÅÔÓ×ÅÎÎÙÍ +ÚÁ ÐÏÄÄÅÒÖËÕ ÜÔÏÇÏ ÓÅÒ×ÅÒÁ ÄÌÑ ×ÙÑÓÎÅÎÉÑ ×ÁÛÉÈ ×ÏÐÒÏÓÏ×. Apache Software +Foundation, Á×ÔÏÒ ðï ×ÅÂ-ÓÅÒ×ÅÒÁ, ËÏÔÏÒÙÍ ÐÏÌØÚÕÅÔÓÑ ÁÄÍÉÎÉÓÔÒÁÔÏÒ ÜÔÏÊ +ÓÉÓÔÅÍÙ, ÎÅ Ó×ÑÚÁÎ Ó ÐÏÄÄÅÒÖËÏÊ ÜÔÏÊ ÓÉÓÔÅÍÙ É ÎÅ ÍÏÖÅÔ ÐÏÍÏÞØ ÷ÁÍ +ÒÁÚÒÅÛÉÔØ ÐÒÏÂÌÅÍÙ ËÏÎÆÉÇÕÒÁÃÉÉ. +

+


+

äÏËÕÍÅÎÔÁÃÉÑ ÐÏ ×ÅÂ-ÓÅÒ×ÅÒÕ Apache ÐÒÉÌÁÇÁÅÔÓÑ +Ë ËÏÍÐÌÅËÔÕ ðï. +

÷Ù ÍÏÖÅÔÅ Ó×ÏÂÏÄÎÏ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÉËÔÏÇÒÁÍÍÕ, ÎÁÈÏÄÑÝÕÀÓÑ ÎÉÖÅ, ÎÁ ×ÅÂ-ÓÅÒ×ÅÒÅ, +ÉÓÐÏÌØÚÕÀÝÉÍ ðï Apache. óÐÁÓÉÂÏ ÚÁ ÉÓÐÏÌØÚÏ×ÁÎÉÅ Apache! +

+ + + diff --git a/docs/docroot/index.html.ru.utf8 b/docs/docroot/index.html.ru.utf8 new file mode 100644 index 00000000000..f8a93c30206 --- /dev/null +++ b/docs/docroot/index.html.ru.utf8 @@ -0,0 +1,34 @@ + + + + + + Тестовая страница установки Apache + + + +Если Вы это видите, это значит, что установка +ПО веб-сервера Apache на этой +системе завершилась успешно. Вы можете теперь добавлять содержимое в эту +директорию и заменить эту страницу. +

+


+
+

+Вы видите это вместо ожидаемой страницы?

+Эта страница находится здесь потому, что администратор системы изменил +конфигурацию этого веб-сервера. Пожалуйста, свяжитесь с лицом, ответсвенным +за поддержку этого сервера для выяснения ваших вопросов. Apache Software +Foundation, автор ПО веб-сервера, которым пользуется администратор этой +системы, не связан с поддержкой этой системы и не может помочь Ð’ам +разрешить проблемы конфигурации. +

+


+

Документация по веб-серверу Apache прилагается +к комплекту ПО. +

Вы можете свободно использовать пиктограмму, находящуюся ниже, на веб-сервере, +использующим ПО Apache. Спасибо за использование Apache! +

+ + + diff --git a/docs/icons/README b/docs/icons/README new file mode 100644 index 00000000000..74b2970b9e8 --- /dev/null +++ b/docs/icons/README @@ -0,0 +1,158 @@ +Public Domain Icons + + These icons were originally made for Mosaic for X and have been + included in the NCSA httpd and Apache server distributions in the + past. They are in the public domain and may be freely included in any + application. The originals were done by Kevin Hughes + (kevinh@kevcom.com). + + Many thanks to Andy Polyakov for tuning the icon colors and adding a + few new images. If you'd like to contribute additions or ideas to + this set, please let me know. + + Almost all of these icons are 20x22 pixels in size. There are + alternative icons in the "small" directory that are 16x16 in size, + provided by Mike Brown (mike@hyperreal.org). + +Suggested Uses + +The following are a few suggestions, to serve as a starting point for ideas. +Please feel free to tweak and rename the icons as you like. + + a.gif + This might be used to represent PostScript or text layout + languages. + + alert.black.gif, alert.red.gif + These can be used to highlight any important items, such as a + README file in a directory. + + back.gif, forward.gif + These can be used as links to go to previous and next areas. + + ball.gray.gif, ball.red.gif + These might be used as bullets. + + binary.gif + This can be used to represent binary files. + + binhex.gif + This can represent BinHex-encoded data. + + blank.gif + This can be used as a placeholder or a spacing element. + + bomb.gif + This can be used to repreesnt core files. + + box1.gif, box2.gif + These icons can be used to represent generic 3D applications and + related files. + + broken.gif + This can represent corrupted data. + + burst.gif + This can call attention to new and important items. + + c.gif + This might represent C source code. + + comp.blue.gif, comp.red.gif + These little computer icons can stand for telnet or FTP + sessions. + + compressed.gif + This may represent compressed data. + + continued.gif + This can be a link to a continued listing of a directory. + + down.gif, up.gif, left.gif, right.gif + These can be used to scroll up, down, left and right in a + listing or may be used to denote items in an outline. + + dvi.gif + This can represent DVI files. + + f.gif + This might represent FORTRAN or Forth source code. + + folder.gif, folder.open.gif, folder.sec.gif + The folder can represent directories. There is also a version + that can represent secure directories or directories that cannot + be viewed. + + generic.gif, generic.sec.gif, generic.red.gif + These can represent generic files, secure files, and important + files, respectively. + + hand.right.gif, hand.up.gif + These can point out important items (pun intended). + + image1.gif, image2.gif, image3.gif + These can represent image formats of various types. + + index.gif + This might represent a WAIS index or search facility. + + layout.gif + This might represent files and formats that contain graphics as + well as text layout, such as HTML and PDF files. + + link.gif + This might represent files that are symbolic links. + + movie.gif + This can represent various movie formats. + + p.gif + This may stand for Perl or Python source code. + + pie0.gif ... pie8.gif + These icons can be used in applications where a list of + documents is returned from a search. The little pie chart images + can denote how relevant the documents may be to your search + query. + + patch.gif + This may stand for patches and diff files. + + portal.gif + This might be a link to an online service or a 3D world. + + ps.gif, quill.gif + These may represent PostScript files. + + screw1.gif, screw2.gif + These may represent CAD or engineering data and formats. + + script.gif + This can represent any of various interpreted languages, such as + Perl, python, TCL, and shell scripts, as well as server + configuration files. + + sound1.gif, sound2.gif + These can represent sound files. + + sphere1.gif, sphere2.gif + These can represent 3D worlds or rendering applications and + formats. + + tex.gif + This can represent TeX files. + + text.gif + This can represent generic (plain) text files. + + transfer.gif + This can represent FTP transfers or uploads/downloads. + + unknown.gif + This may represent a file of an unknown type. + + uuencoded.gif + This can stand for uuencoded data. + + world1.gif, world2.gif + These can represent 3D worlds or other 3D formats. diff --git a/docs/icons/a.gif b/docs/icons/a.gif new file mode 100644 index 00000000000..bb23d971f4c Binary files /dev/null and b/docs/icons/a.gif differ diff --git a/docs/icons/alert.black.gif b/docs/icons/alert.black.gif new file mode 100644 index 00000000000..eaecd2172a0 Binary files /dev/null and b/docs/icons/alert.black.gif differ diff --git a/docs/icons/alert.red.gif b/docs/icons/alert.red.gif new file mode 100644 index 00000000000..a4238940433 Binary files /dev/null and b/docs/icons/alert.red.gif differ diff --git a/docs/icons/apache_pb.gif b/docs/icons/apache_pb.gif new file mode 100644 index 00000000000..3a1c139fc42 Binary files /dev/null and b/docs/icons/apache_pb.gif differ diff --git a/docs/icons/back.gif b/docs/icons/back.gif new file mode 100644 index 00000000000..a694ae1ec3f Binary files /dev/null and b/docs/icons/back.gif differ diff --git a/docs/icons/ball.gray.gif b/docs/icons/ball.gray.gif new file mode 100644 index 00000000000..eb84268c4cc Binary files /dev/null and b/docs/icons/ball.gray.gif differ diff --git a/docs/icons/ball.red.gif b/docs/icons/ball.red.gif new file mode 100644 index 00000000000..a8425cb574b Binary files /dev/null and b/docs/icons/ball.red.gif differ diff --git a/docs/icons/binary.gif b/docs/icons/binary.gif new file mode 100644 index 00000000000..9a15cbae04c Binary files /dev/null and b/docs/icons/binary.gif differ diff --git a/docs/icons/binhex.gif b/docs/icons/binhex.gif new file mode 100644 index 00000000000..62d0363108d Binary files /dev/null and b/docs/icons/binhex.gif differ diff --git a/docs/icons/blank.gif b/docs/icons/blank.gif new file mode 100644 index 00000000000..0ccf01e1983 Binary files /dev/null and b/docs/icons/blank.gif differ diff --git a/docs/icons/bomb.gif b/docs/icons/bomb.gif new file mode 100644 index 00000000000..270fdb1c064 Binary files /dev/null and b/docs/icons/bomb.gif differ diff --git a/docs/icons/box1.gif b/docs/icons/box1.gif new file mode 100644 index 00000000000..65dcd002eaf Binary files /dev/null and b/docs/icons/box1.gif differ diff --git a/docs/icons/box2.gif b/docs/icons/box2.gif new file mode 100644 index 00000000000..c43bc4faecf Binary files /dev/null and b/docs/icons/box2.gif differ diff --git a/docs/icons/broken.gif b/docs/icons/broken.gif new file mode 100644 index 00000000000..9f8cbe9f760 Binary files /dev/null and b/docs/icons/broken.gif differ diff --git a/docs/icons/burst.gif b/docs/icons/burst.gif new file mode 100644 index 00000000000..fbdcf575f78 Binary files /dev/null and b/docs/icons/burst.gif differ diff --git a/docs/icons/c.gif b/docs/icons/c.gif new file mode 100644 index 00000000000..7555b6c164f Binary files /dev/null and b/docs/icons/c.gif differ diff --git a/docs/icons/comp.blue.gif b/docs/icons/comp.blue.gif new file mode 100644 index 00000000000..f8d76a8c23f Binary files /dev/null and b/docs/icons/comp.blue.gif differ diff --git a/docs/icons/comp.gray.gif b/docs/icons/comp.gray.gif new file mode 100644 index 00000000000..7664cd03649 Binary files /dev/null and b/docs/icons/comp.gray.gif differ diff --git a/docs/icons/compressed.gif b/docs/icons/compressed.gif new file mode 100644 index 00000000000..39e732739f5 Binary files /dev/null and b/docs/icons/compressed.gif differ diff --git a/docs/icons/continued.gif b/docs/icons/continued.gif new file mode 100644 index 00000000000..b0ffb7e0cc0 Binary files /dev/null and b/docs/icons/continued.gif differ diff --git a/docs/icons/dir.gif b/docs/icons/dir.gif new file mode 100644 index 00000000000..48264601ae0 Binary files /dev/null and b/docs/icons/dir.gif differ diff --git a/docs/icons/down.gif b/docs/icons/down.gif new file mode 100644 index 00000000000..a354c871cd0 Binary files /dev/null and b/docs/icons/down.gif differ diff --git a/docs/icons/dvi.gif b/docs/icons/dvi.gif new file mode 100644 index 00000000000..791be33105d Binary files /dev/null and b/docs/icons/dvi.gif differ diff --git a/docs/icons/f.gif b/docs/icons/f.gif new file mode 100644 index 00000000000..fbe353c2822 Binary files /dev/null and b/docs/icons/f.gif differ diff --git a/docs/icons/folder.gif b/docs/icons/folder.gif new file mode 100644 index 00000000000..48264601ae0 Binary files /dev/null and b/docs/icons/folder.gif differ diff --git a/docs/icons/folder.open.gif b/docs/icons/folder.open.gif new file mode 100644 index 00000000000..30979cb5285 Binary files /dev/null and b/docs/icons/folder.open.gif differ diff --git a/docs/icons/folder.sec.gif b/docs/icons/folder.sec.gif new file mode 100644 index 00000000000..75332d9e59b Binary files /dev/null and b/docs/icons/folder.sec.gif differ diff --git a/docs/icons/forward.gif b/docs/icons/forward.gif new file mode 100644 index 00000000000..b2959b4c85c Binary files /dev/null and b/docs/icons/forward.gif differ diff --git a/docs/icons/generic.gif b/docs/icons/generic.gif new file mode 100644 index 00000000000..de60b2940f9 Binary files /dev/null and b/docs/icons/generic.gif differ diff --git a/docs/icons/generic.red.gif b/docs/icons/generic.red.gif new file mode 100644 index 00000000000..94743981d93 Binary files /dev/null and b/docs/icons/generic.red.gif differ diff --git a/docs/icons/generic.sec.gif b/docs/icons/generic.sec.gif new file mode 100644 index 00000000000..88d5240c3c3 Binary files /dev/null and b/docs/icons/generic.sec.gif differ diff --git a/docs/icons/hand.right.gif b/docs/icons/hand.right.gif new file mode 100644 index 00000000000..5cdbc7206da Binary files /dev/null and b/docs/icons/hand.right.gif differ diff --git a/docs/icons/hand.up.gif b/docs/icons/hand.up.gif new file mode 100644 index 00000000000..85a5d683177 Binary files /dev/null and b/docs/icons/hand.up.gif differ diff --git a/docs/icons/icon.sheet.gif b/docs/icons/icon.sheet.gif new file mode 100644 index 00000000000..ad1686e4480 Binary files /dev/null and b/docs/icons/icon.sheet.gif differ diff --git a/docs/icons/image1.gif b/docs/icons/image1.gif new file mode 100644 index 00000000000..01e442bfa92 Binary files /dev/null and b/docs/icons/image1.gif differ diff --git a/docs/icons/image2.gif b/docs/icons/image2.gif new file mode 100644 index 00000000000..751faeea364 Binary files /dev/null and b/docs/icons/image2.gif differ diff --git a/docs/icons/image3.gif b/docs/icons/image3.gif new file mode 100644 index 00000000000..4f30484ff64 Binary files /dev/null and b/docs/icons/image3.gif differ diff --git a/docs/icons/index.gif b/docs/icons/index.gif new file mode 100644 index 00000000000..162478fb3a7 Binary files /dev/null and b/docs/icons/index.gif differ diff --git a/docs/icons/layout.gif b/docs/icons/layout.gif new file mode 100644 index 00000000000..c96338a1522 Binary files /dev/null and b/docs/icons/layout.gif differ diff --git a/docs/icons/left.gif b/docs/icons/left.gif new file mode 100644 index 00000000000..279e6710d49 Binary files /dev/null and b/docs/icons/left.gif differ diff --git a/docs/icons/link.gif b/docs/icons/link.gif new file mode 100644 index 00000000000..c5b6889a76d Binary files /dev/null and b/docs/icons/link.gif differ diff --git a/docs/icons/movie.gif b/docs/icons/movie.gif new file mode 100644 index 00000000000..00351837741 Binary files /dev/null and b/docs/icons/movie.gif differ diff --git a/docs/icons/p.gif b/docs/icons/p.gif new file mode 100644 index 00000000000..7b917b4e91e Binary files /dev/null and b/docs/icons/p.gif differ diff --git a/docs/icons/patch.gif b/docs/icons/patch.gif new file mode 100644 index 00000000000..39bc90e7953 Binary files /dev/null and b/docs/icons/patch.gif differ diff --git a/docs/icons/pdf.gif b/docs/icons/pdf.gif new file mode 100644 index 00000000000..c88fd777c4b Binary files /dev/null and b/docs/icons/pdf.gif differ diff --git a/docs/icons/pie0.gif b/docs/icons/pie0.gif new file mode 100644 index 00000000000..6f7a0ae7a70 Binary files /dev/null and b/docs/icons/pie0.gif differ diff --git a/docs/icons/pie1.gif b/docs/icons/pie1.gif new file mode 100644 index 00000000000..03aa6be71eb Binary files /dev/null and b/docs/icons/pie1.gif differ diff --git a/docs/icons/pie2.gif b/docs/icons/pie2.gif new file mode 100644 index 00000000000..b04c5e09086 Binary files /dev/null and b/docs/icons/pie2.gif differ diff --git a/docs/icons/pie3.gif b/docs/icons/pie3.gif new file mode 100644 index 00000000000..4db9d023eda Binary files /dev/null and b/docs/icons/pie3.gif differ diff --git a/docs/icons/pie4.gif b/docs/icons/pie4.gif new file mode 100644 index 00000000000..93471fdd885 Binary files /dev/null and b/docs/icons/pie4.gif differ diff --git a/docs/icons/pie5.gif b/docs/icons/pie5.gif new file mode 100644 index 00000000000..57aee93f070 Binary files /dev/null and b/docs/icons/pie5.gif differ diff --git a/docs/icons/pie6.gif b/docs/icons/pie6.gif new file mode 100644 index 00000000000..0dc327b5697 Binary files /dev/null and b/docs/icons/pie6.gif differ diff --git a/docs/icons/pie7.gif b/docs/icons/pie7.gif new file mode 100644 index 00000000000..8661337f067 Binary files /dev/null and b/docs/icons/pie7.gif differ diff --git a/docs/icons/pie8.gif b/docs/icons/pie8.gif new file mode 100644 index 00000000000..59ddb34ce0f Binary files /dev/null and b/docs/icons/pie8.gif differ diff --git a/docs/icons/portal.gif b/docs/icons/portal.gif new file mode 100644 index 00000000000..0e6e506e004 Binary files /dev/null and b/docs/icons/portal.gif differ diff --git a/docs/icons/ps.gif b/docs/icons/ps.gif new file mode 100644 index 00000000000..0f565bc1db7 Binary files /dev/null and b/docs/icons/ps.gif differ diff --git a/docs/icons/quill.gif b/docs/icons/quill.gif new file mode 100644 index 00000000000..818a5cdc7e0 Binary files /dev/null and b/docs/icons/quill.gif differ diff --git a/docs/icons/right.gif b/docs/icons/right.gif new file mode 100644 index 00000000000..b256e5f75fb Binary files /dev/null and b/docs/icons/right.gif differ diff --git a/docs/icons/screw1.gif b/docs/icons/screw1.gif new file mode 100644 index 00000000000..af6ba2b097b Binary files /dev/null and b/docs/icons/screw1.gif differ diff --git a/docs/icons/screw2.gif b/docs/icons/screw2.gif new file mode 100644 index 00000000000..06dccb3e44c Binary files /dev/null and b/docs/icons/screw2.gif differ diff --git a/docs/icons/script.gif b/docs/icons/script.gif new file mode 100644 index 00000000000..d8a853bc582 Binary files /dev/null and b/docs/icons/script.gif differ diff --git a/docs/icons/small/README.txt b/docs/icons/small/README.txt new file mode 100644 index 00000000000..deb96702b76 --- /dev/null +++ b/docs/icons/small/README.txt @@ -0,0 +1,6 @@ + +These icons are provided as an alternative to the standard Apache +icon graphics. All graphics in this directory, with the exception +of rainbow.gif, are 16x16 pixels in size, rather than the 20x22 +dimension icons which are the normal defaults for Apache and are +in the parent directory of this one. diff --git a/docs/icons/small/back.gif b/docs/icons/small/back.gif new file mode 100644 index 00000000000..e331454726b Binary files /dev/null and b/docs/icons/small/back.gif differ diff --git a/docs/icons/small/binary.gif b/docs/icons/small/binary.gif new file mode 100644 index 00000000000..995f79b9b10 Binary files /dev/null and b/docs/icons/small/binary.gif differ diff --git a/docs/icons/small/binhex.gif b/docs/icons/small/binhex.gif new file mode 100644 index 00000000000..3d54a5458e6 Binary files /dev/null and b/docs/icons/small/binhex.gif differ diff --git a/docs/icons/small/blank.gif b/docs/icons/small/blank.gif new file mode 100644 index 00000000000..606787a8399 Binary files /dev/null and b/docs/icons/small/blank.gif differ diff --git a/docs/icons/small/broken.gif b/docs/icons/small/broken.gif new file mode 100644 index 00000000000..1bcc57f25c5 Binary files /dev/null and b/docs/icons/small/broken.gif differ diff --git a/docs/icons/small/burst.gif b/docs/icons/small/burst.gif new file mode 100644 index 00000000000..d882ceba9cb Binary files /dev/null and b/docs/icons/small/burst.gif differ diff --git a/docs/icons/small/comp1.gif b/docs/icons/small/comp1.gif new file mode 100644 index 00000000000..712f36afdb2 Binary files /dev/null and b/docs/icons/small/comp1.gif differ diff --git a/docs/icons/small/comp2.gif b/docs/icons/small/comp2.gif new file mode 100644 index 00000000000..7759eb11f95 Binary files /dev/null and b/docs/icons/small/comp2.gif differ diff --git a/docs/icons/small/compressed.gif b/docs/icons/small/compressed.gif new file mode 100644 index 00000000000..d3b156072ac Binary files /dev/null and b/docs/icons/small/compressed.gif differ diff --git a/docs/icons/small/continued.gif b/docs/icons/small/continued.gif new file mode 100644 index 00000000000..e1c9f2cfa68 Binary files /dev/null and b/docs/icons/small/continued.gif differ diff --git a/docs/icons/small/dir.gif b/docs/icons/small/dir.gif new file mode 100644 index 00000000000..7b37b099177 Binary files /dev/null and b/docs/icons/small/dir.gif differ diff --git a/docs/icons/small/dir2.gif b/docs/icons/small/dir2.gif new file mode 100644 index 00000000000..425d6e4b77c Binary files /dev/null and b/docs/icons/small/dir2.gif differ diff --git a/docs/icons/small/doc.gif b/docs/icons/small/doc.gif new file mode 100644 index 00000000000..0fcf18db2a8 Binary files /dev/null and b/docs/icons/small/doc.gif differ diff --git a/docs/icons/small/forward.gif b/docs/icons/small/forward.gif new file mode 100644 index 00000000000..2997466eb4d Binary files /dev/null and b/docs/icons/small/forward.gif differ diff --git a/docs/icons/small/generic.gif b/docs/icons/small/generic.gif new file mode 100644 index 00000000000..f8da6ff92c3 Binary files /dev/null and b/docs/icons/small/generic.gif differ diff --git a/docs/icons/small/generic2.gif b/docs/icons/small/generic2.gif new file mode 100644 index 00000000000..992f13331bb Binary files /dev/null and b/docs/icons/small/generic2.gif differ diff --git a/docs/icons/small/generic3.gif b/docs/icons/small/generic3.gif new file mode 100644 index 00000000000..85aa275e25a Binary files /dev/null and b/docs/icons/small/generic3.gif differ diff --git a/docs/icons/small/image.gif b/docs/icons/small/image.gif new file mode 100644 index 00000000000..dc3d95ced76 Binary files /dev/null and b/docs/icons/small/image.gif differ diff --git a/docs/icons/small/image2.gif b/docs/icons/small/image2.gif new file mode 100644 index 00000000000..a5c40f15508 Binary files /dev/null and b/docs/icons/small/image2.gif differ diff --git a/docs/icons/small/index.gif b/docs/icons/small/index.gif new file mode 100644 index 00000000000..526df6b0645 Binary files /dev/null and b/docs/icons/small/index.gif differ diff --git a/docs/icons/small/key.gif b/docs/icons/small/key.gif new file mode 100644 index 00000000000..8dfd6c09de3 Binary files /dev/null and b/docs/icons/small/key.gif differ diff --git a/docs/icons/small/movie.gif b/docs/icons/small/movie.gif new file mode 100644 index 00000000000..7b4a42e7a0e Binary files /dev/null and b/docs/icons/small/movie.gif differ diff --git a/docs/icons/small/patch.gif b/docs/icons/small/patch.gif new file mode 100644 index 00000000000..100484e5982 Binary files /dev/null and b/docs/icons/small/patch.gif differ diff --git a/docs/icons/small/ps.gif b/docs/icons/small/ps.gif new file mode 100644 index 00000000000..fa4bcfce30f Binary files /dev/null and b/docs/icons/small/ps.gif differ diff --git a/docs/icons/small/rainbow.gif b/docs/icons/small/rainbow.gif new file mode 100644 index 00000000000..8216b89bade Binary files /dev/null and b/docs/icons/small/rainbow.gif differ diff --git a/docs/icons/small/sound.gif b/docs/icons/small/sound.gif new file mode 100644 index 00000000000..a7a89ffd9ed Binary files /dev/null and b/docs/icons/small/sound.gif differ diff --git a/docs/icons/small/sound2.gif b/docs/icons/small/sound2.gif new file mode 100644 index 00000000000..07706e07b86 Binary files /dev/null and b/docs/icons/small/sound2.gif differ diff --git a/docs/icons/small/tar.gif b/docs/icons/small/tar.gif new file mode 100644 index 00000000000..59c3ffb9a5f Binary files /dev/null and b/docs/icons/small/tar.gif differ diff --git a/docs/icons/small/text.gif b/docs/icons/small/text.gif new file mode 100644 index 00000000000..66ceefbc8c4 Binary files /dev/null and b/docs/icons/small/text.gif differ diff --git a/docs/icons/small/transfer.gif b/docs/icons/small/transfer.gif new file mode 100644 index 00000000000..d460d3fffe6 Binary files /dev/null and b/docs/icons/small/transfer.gif differ diff --git a/docs/icons/small/unknown.gif b/docs/icons/small/unknown.gif new file mode 100644 index 00000000000..7bf0bbc10a6 Binary files /dev/null and b/docs/icons/small/unknown.gif differ diff --git a/docs/icons/small/uu.gif b/docs/icons/small/uu.gif new file mode 100644 index 00000000000..8c793f8a7f9 Binary files /dev/null and b/docs/icons/small/uu.gif differ diff --git a/docs/icons/sound1.gif b/docs/icons/sound1.gif new file mode 100644 index 00000000000..8efb49f55d6 Binary files /dev/null and b/docs/icons/sound1.gif differ diff --git a/docs/icons/sound2.gif b/docs/icons/sound2.gif new file mode 100644 index 00000000000..48e6a7fb2fa Binary files /dev/null and b/docs/icons/sound2.gif differ diff --git a/docs/icons/sphere1.gif b/docs/icons/sphere1.gif new file mode 100644 index 00000000000..7067070da27 Binary files /dev/null and b/docs/icons/sphere1.gif differ diff --git a/docs/icons/sphere2.gif b/docs/icons/sphere2.gif new file mode 100644 index 00000000000..a9e462a377c Binary files /dev/null and b/docs/icons/sphere2.gif differ diff --git a/docs/icons/tar.gif b/docs/icons/tar.gif new file mode 100644 index 00000000000..4032c1bd3d4 Binary files /dev/null and b/docs/icons/tar.gif differ diff --git a/docs/icons/tex.gif b/docs/icons/tex.gif new file mode 100644 index 00000000000..45e43233b84 Binary files /dev/null and b/docs/icons/tex.gif differ diff --git a/docs/icons/text.gif b/docs/icons/text.gif new file mode 100644 index 00000000000..4c623909fbf Binary files /dev/null and b/docs/icons/text.gif differ diff --git a/docs/icons/transfer.gif b/docs/icons/transfer.gif new file mode 100644 index 00000000000..33697dbb667 Binary files /dev/null and b/docs/icons/transfer.gif differ diff --git a/docs/icons/unknown.gif b/docs/icons/unknown.gif new file mode 100644 index 00000000000..32b1ea23fb6 Binary files /dev/null and b/docs/icons/unknown.gif differ diff --git a/docs/icons/up.gif b/docs/icons/up.gif new file mode 100644 index 00000000000..6d6d6d1ebf8 Binary files /dev/null and b/docs/icons/up.gif differ diff --git a/docs/icons/uu.gif b/docs/icons/uu.gif new file mode 100644 index 00000000000..4387d529f69 Binary files /dev/null and b/docs/icons/uu.gif differ diff --git a/docs/icons/uuencoded.gif b/docs/icons/uuencoded.gif new file mode 100644 index 00000000000..4387d529f69 Binary files /dev/null and b/docs/icons/uuencoded.gif differ diff --git a/docs/icons/world1.gif b/docs/icons/world1.gif new file mode 100644 index 00000000000..05b4ec20588 Binary files /dev/null and b/docs/icons/world1.gif differ diff --git a/docs/icons/world2.gif b/docs/icons/world2.gif new file mode 100644 index 00000000000..e3203f7a881 Binary files /dev/null and b/docs/icons/world2.gif differ diff --git a/docs/man/ab.8 b/docs/man/ab.8 new file mode 100644 index 00000000000..5a06f360557 --- /dev/null +++ b/docs/man/ab.8 @@ -0,0 +1,210 @@ +.TH ab 1 "March 1998" +.\" $Id: ab.8,v 1.1 1999/08/24 06:45:53 fielding Exp $ +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +ab \- Apache HTTP server benchmarking tool +.SH SYNOPSIS +.B ab +[ +.B \-k +] [ +.BI \-n " requests" +] [ +.BI \-t " timelimit" +] [ +.BI \-c " concurrency" +] [ +.BI \-p " POST file" +] [ +.BI \-A " Authenticate username:password" +] [ +.BI \-P " Proxy Authenticate username:password" +] [ +.BI \-H " Custom header" +] [ +.BI \-C " Cookie name=value" +] [ +.BI \-T " content-type" +] [ +.BI \-v " verbosity" +] +] [ +.BI \-w " output HTML" +] +] [ +.BI \-x " attributes" +] +] [ +.BI \-y " attributes" +] +] [ +.BI \-z "
attributes" +] +.I [http://]hostname[:port]/path + +.B ab +[ +.B \-V +] [ +.B \-h +] +.PP +.SH DESCRIPTION +.B ab +is a tool for benchmarking your Apache HyperText Transfer Protocol (HTTP) +server. It is designed to give you an impression on how performant is your +current Apache installation. This especially shows you how much requests per +time your Apache installation is capable to serve. +.PP +.SH OPTIONS +.TP 12 +.B \-k +Enable the HTTP KeepAlive feature, i.e. perform multiple requests within one +HTTP session instead. Default is no KeepAlive. +.TP 12 +.BI \-n " requests" +Number of requests to perform for the benchmarking session. The default is to +just perform one single request which usually leads to not very representative +benchmarking results. +.TP 12 +.BI \-t " timelimit" +Seconds to max. spend for benchmarking. This implies +a +.B \-n +.B 50000 +internally. Use this to benchmark the server within a fixed total amount of +time. Per default there is no timelimit. +.TP 12 +.BI \-c " concurrency" +Number of multiple requests per time to perform. +Default is one request per time. + +.TP 12 +.BI \-p " POST file" +File containing data to POST. + +.TP 12 +.BI \-A " Authorization username:password" +Supply BASIC Authentification credentials to the server. The username +and password are separated by a single ':' and send on the wire uuencoded. +The string is send regardless of wether the server needs it; (i.e. has +send an 401. Authentifcation needed). + +.TP 12 +.BI \-p " Proxy-Authorization username:password" +Supply BASIC Authentification credentials to a proxy en-route. The username +and password are separated by a single ':' and send on the wire uuencoded. +The string is send regardless of wether the proxy needs it; (i.e. has +send an 407 Proxy authentifcation needed). + +.TP 12 +.BI \-C " Cookie name=value" +Add a 'Cookie:' line to the request. The argument is typically in the form +of a 'name=value' pair. This field is repeatable. + +.TP 12 +.BI \-p " Header string" +Postfix extra headers to the request. The argument is typically in the form +of a valid header line; containing a colon separated field value pair. (i.e. +'Accept-Encoding: zip/zop;8bit'). + +.TP 12 +.BI \-T " content-type" +Content-type header to use for POST data. + +.TP 12 +.B \-v +Set verbosity level - 4 and above prints information on headers, 3 and +above prints response codes (404, 200, etc.), 2 and above prints +warnings and info. + +.TP 12 +.BI \-w +Print out results in HTML tables. Default table is two columns wide, +with a white background. +.TP 12 +.BI \-x " attributes" +String to use as attributes for . Attributes are inserted +
+.TP 12 +.BI \-y " attributes" +String to use as attributes for . +.TP 12 +.BI \-z " attributes" +String to use as attributes for
. +.TP 12 +.B \-V +Display version number and exit. +.TP 12 +.B \-h +Display usage information. +.PD +.SH BUGS +There are various statically declared buffers of fixed length. Combined +with the lazy parsing of the command line arguments, the response headers +from the server and other external inputs this might bite you. +.P +It does not implement HTTP/1.x fully; only accepts some 'expected' forms +of responses. The rather heavy use of +.BR strstr(3) +shows up top in profile, +which might indicate a performance problem; i.e. you would measure the +.BR ab +performance rather than the server's. + +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/apachectl.8 b/docs/man/apachectl.8 new file mode 100644 index 00000000000..72300eb98b2 --- /dev/null +++ b/docs/man/apachectl.8 @@ -0,0 +1,133 @@ +.TH apachectl 1 "September 1997" +.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +apachectl \- Apache HTTP server control interface +.SH SYNOPSIS +.B apachectl +\fIcommand\fP [...] +.SH DESCRIPTION +.B apachectl +is a front end to the Apache HyperText Transfer Protocol (HTTP) +server. It is designed to help the administrator control the +functioning of the Apache +.B httpd +daemon. +.PP +.B NOTE: +If your Apache installation uses non-standard paths, you will need to +edit the +.B apachectl +script to set the appropriate paths to your PID file and your +.B httpd +binary. See the comments in the script for details. +.PP +The +.B apachectl +script returns a 0 exit value on success, and >0 if an error +occurs. For more details, view the comments in the script. +.PP +Full documentation for Apache is available at +.B http://www.apache.org/ +. +.SH OPTIONS +The \fIcommand\fP can be any one or more of the following options: +.TP 12 +.BI start +Start the Apache daemon. Gives an error if it is already running. +.TP +.BI stop +Stops the Apache daemon. +.TP +.BI restart +Restarts the Apache daemon by sending it a SIGHUP. If the daemon +is not running, it is started. +This command automatically checks the configuration files via +.BI configtest +before initiating the restart to make sure Apache doesn't die. +.TP +.BI fullstatus +Displays a full status report from +.B mod_status. +For this to work, you need to have mod_status enabled on your server +and a text-based browser such as \fIlynx\fP available on your system. The +URL used to access the status report can be set by editing the +.B STATUSURL +variable in the script. +.TP +.BI status +Displays a brief status report. Similar to the fullstatus option, +except that the list of requests currently being served is omitted. +.TP +.BI graceful +Gracefully restarts the Apache daemon by sending it a SIGUSR1. If +the daemon is not running, it is started. This differs from a +normal restart in that currently open connections are not aborted. +A side effect is that old log files will not be closed immediately. +This means that if used in a log rotation script, a substantial delay may be +necessary to ensure that the old log files are closed before processing them. +This command automatically checks the configuration files via +.BI configtest +before initiating the restart to make sure Apache doesn't die. +.TP +.BI configtest +Run a configuration file syntax test. It parses the configuration +files and either reports +.B "Syntax Ok" +or detailed information about the particular syntax error. +.TP +.BI help +Displays a short help message. +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/apxs.8 b/docs/man/apxs.8 new file mode 100644 index 00000000000..464766c75fe --- /dev/null +++ b/docs/man/apxs.8 @@ -0,0 +1,459 @@ +.TH apxs 8 "April 1998" +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +apxs \- APache eXtenSion tool +.SH SYNOPSIS +.B apxs +.B \-g +[ +.BI \-S " name=value +] +.BI \-n " modname" + +.B apxs +.B \-q +[ +.BI \-S " name=value +] +.IR query " ..." + +.B apxs +.B \-c +[ +.BI \-S " name=value +] +[ +.BI \-o " dsofile" +] +[ +.BI \-I " incdir" +] +[ +.BI \-D " name=value" +] +[ +.BI \-L " libdir" +] +[ +.BI \-l " libname" +] +[ +.BI \-Wc, "compiler-flags" +] +[ +.BI \-Wl, "linker-flags" +] +.IR files " ..." + +.B apxs +.B \-i +[ +.BI \-S " name=value +] +[ +.BI \-n " modname" +] +[ +.B \-a +] +[ +.B \-A +] +.IR dsofile " ..." + +.B apxs +.B \-e +[ +.BI \-S " name=value +] +[ +.BI \-n " modname" +] +[ +.B \-a +] +[ +.B \-A +] +.IR dsofile " ..." +.PP +.SH DESCRIPTION +.B apxs +is a tool for building and installing extension modules for the Apache +HyperText Transfer Protocol (HTTP) server. This is achieved by building a +dynamic shared object (DSO) from one or more source or object +.I files +which then can be loaded into +the Apache server under runtime via the +.B LoadModule +directive from +.BR mod_so. + +So to use this extension mechanism your platform has +to support the DSO feature and your +Apache +.B httpd +binary has to be built with the +.B mod_so +module. +The +.B apxs +tool automatically complains if this is not the case. +You can check this yourself by manually running the command + +.nf + $ httpd -l +.fi + +The module +.B mod_so +should be part of the displayed list. +If these requirements are fulfilled you can easily extend +your Apache server's functionality by installing your own +modules with the DSO mechanism by the help of this +.B apxs +tool: + +.nf + $ apxs -i -a -c mod_foo.c + gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c + ld -Bshareable -o mod_foo.so mod_foo.o + cp mod_foo.so /path/to/apache/libexec/mod_foo.so + chmod 755 /path/to/apache/libexec/mod_foo.so + [activating module `foo' in /path/to/apache/etc/httpd.conf] + $ apachectl restart + /path/to/apache/sbin/apachectl restart: httpd not running, trying to start + [Tue Mar 31 11:27:55 1998] [debug] mod_so.c(303): loaded module foo_module + /path/to/apache/sbin/apachectl restart: httpd started + $ _ +.fi + +The arguments +.I files +can be any C source file (.c), a object file (.o) or +even a library archive (.a). The +.B apxs +tool automatically recognizes these extensions and automtaically used the C +source files for compilation while just using the object and archive files for +the linking phase. But when using such pre-compiled objects make sure they are +compiled for position independend code (PIC) to be able to use them for a +dynamically loaded shared object. +For instance with GCC you always just have to use +.BR -fpic . +For other +C compilers consult its manual +page or at watch for the flags +.B apxs +uses to compile the object files. + +For more details about DSO support in Apache read the documentation +of +.B mod_so +or perhaps even read the +.B src/modules/standard/mod_so.c +source file. + +.PP +.SH OPTIONS +Common options: +.TP 12 +.BI \-n " modname" +This explicitly sets the module name for the +.B \-i +(install) +and +.B \-g +(template generation) option. Use this to explicitly specify the module name. +For option +.B \-g +this is required, for option +.B \-i +the +.B apxs +tool tries to determine the name from the source or (as a fallback) at least +by guessing it from the filename. +.PP +Query options: +.TP 12 +.B \-q +Performs a query for +.BR apxs 's +knowledge about certain settings. The +.I query +parameters can be one or more of the following strings: +.nf + CC TARGET + CFLAGS SBINDIR + CFLAGS_SHLIB INCLUDEDIR + LD_SHLIB LIBEXECDIR + LDFLAGS_SHLIB SYSCONFDIR + LIBS_SHLIB +.fi +Use this for manually determining settings. For instance use +.nf + INC=-I`apxs -q INCLUDEDIR` +.fi +inside your own Makefiles if you need manual access +to Apache's C header files. +.PP +Configuration options: +.TP 12 +.BI \-S " name=value" +This option changes the apxs settings described above. +.PP +Template Generation options: +.TP 12 +.B \-g +This generates a subdirectory +.I name +(see option +.BR \-n ")" +and there two files: A sample module source file named +.BI mod_ name.c +which can be used as a template for creating your own modules or +as a quick start for playing with the APXS mechanism. +And a corresponding +.B Makefile +for even easier build and installing of this module. +.PP +DSO compilation options: +.TP 12 +.B \-c +This indicates the compilation operation. It first compiles the C source +files (.c) of +.I files +into corresponding object files (.o) and then builds a dynamically shared object in +.I dsofile +by linking these object files plus the remaining +object files (.o and .a) of +.I files +If no +.B \-o +option is specified +the output file is guessed from the first filename in +.I files +and thus usually defaults to +.BI mod_ name.so +.TP 12 +.BI \-o " dsofile" +Explicitly specifies the filename of the created dynamically shared object. If +not specified and the name cannot be guessed from the +.I files +list, the fallback name +.B mod_unknown.so +is used. +.TP 12 +.BI \-D " name=value" +This option is directly passed through to the compilation command(s). +Use this to add your own defines to the build process. +.TP 12 +.BI \-I " incdir" +This option is directly passed through to the compilation command(s). +Use this to add your own include directories to search to the build process. +.TP 12 +.BI \-L " libdir" +This option is directly passed through to the linker command. +Use this to add your own library directories to search to the build process. +.TP 12 +.BI \-l " libname" +This option is directly passed through to the linker command. +Use this to add your own libraries to search to the build process. +.TP 12 +.BI \-Wc, "compiler-flags" +This option passes +.I compiler-flags +as additional flags to the compiler command. +Use this to add local compiler-specific options. +.TP 12 +.BI \-Wl, "linker-flags" +This option passes +.I linker-flags +as additional flags to the linker command. +Use this to add local linker-specific options. +.PP +DSO installation and configuration options: +.TP 12 +.B \-i +This indicates the installation operation and installs one or more +dynamically shared objects into the +server's +.I libexec +directory. +.TP 12 +.B \-a +This activates the module by automatically adding a corresponding +.B LoadModule +line to Apache's +.B httpd.conf +configuration file, or by enabling it if it already exists. +.TP 12 +.B \-A +Same as option +.B \-a +but the created +.B LoadModule +directive is prefixed with a hash sign (#), i.e. the module is +just prepared for later activation but initially disabled. +.TP 12 +.B \-e +This indicates the editing operation, which can be used with the +.B \-a +and +.B \-A +options similarly to the +.B \-i +operation to edit Apache's +.B httpd.conf +configuration file without attempting to install the module. +.PD +.SH EXAMPLES +Assume you have an Apache module named mod_foo.c available which should extend +Apache's server functionality. To accomplish this you first have to compile +the C source into a shared object suitable for loading into the Apache server +under runtime via the following command: + +.nf + $ apxs -c mod_foo.c + gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c + ld -Bshareable -o mod_foo.so mod_foo.o + $ _ +.fi + +Then you have to update the Apache configuration by making sure a +.B LoadModule +directive is present to load this shared object. To simplify this +step +.B apxs +provides an automatic way to install the shared object in its +"libexec" directory and updating the +.B httpd.conf +file accordingly. This can be achieved by running: + +.nf + $ apxs -i -a mod_foo.c + cp mod_foo.so /path/to/apache/libexec/mod_foo.so + chmod 755 /path/to/apache/libexec/mod_foo.so + [activating module `foo' in /path/to/apache/etc/httpd.conf] + $ _ +.fi + +This way a line named + +.nf + LoadModule foo_module libexec/mod_foo.so +.fi + +is added to the configuration file if still not present. +If you want to have this this disabled per default use the +.B \-A +option, i.e. + +.nf + $ apxs -i -A mod_foo.c +.fi + +For a quick test of the APXS mechanism you can create a sample Apache module +template plus a corresponding Makefile via: + +.nf + $ apxs -g -n foo + Creating [DIR] foo + Creating [FILE] foo/Makefile + Creating [FILE] foo/mod_foo.c + $ _ +.fi + +Then you can immediately compile this sample module into a shared object and +load it into the Apache server: + +.nf + $ cd foo + $ make all reload + apxs -c mod_foo.c + gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c + ld -Bshareable -o mod_foo.so mod_foo.o + apxs -i -a -n "foo" mod_foo.so + cp mod_foo.so /path/to/apache/libexec/mod_foo.so + chmod 755 /path/to/apache/libexec/mod_foo.so + [activating module `foo' in /path/to/apache/etc/httpd.conf] + apachectl restart + /path/to/apache/sbin/apachectl restart: httpd not running, trying to start + [Tue Mar 31 11:27:55 1998] [debug] mod_so.c(303): loaded module foo_module + /path/to/apache/sbin/apachectl restart: httpd started + $ _ +.fi + +You can even use +.B apxs +to compile complex modules outside the Apache source tree, like PHP3: + +.nf + $ cd php3 + $ ./configure --with-shared-apache=../apache-1.3 + $ apxs -c -o libphp3.so mod_php3.c libmodphp3-so.a + gcc -fpic -DSHARED_MODULE -I/tmp/apache/include -c mod_php3.c + ld -Bshareable -o libphp3.so mod_php3.o libmodphp3-so.a + $ _ +.fi + +because +.B apxs +automatically recognized C source files and object files. Only C source files +are compiled while remaining object files are used for the linking phase. + +.PD +.SH SEE ALSO +.BR apachectl(1), +.BR httpd(8). +. diff --git a/docs/man/dbmmanage.1 b/docs/man/dbmmanage.1 new file mode 100644 index 00000000000..06380ff0f59 --- /dev/null +++ b/docs/man/dbmmanage.1 @@ -0,0 +1,171 @@ +.TH dbmmanage 1 "March 1998" +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +dbmmanage \- Create and update user authentication files in DBM format +.SH SYNOPSIS +.B dbmmanage +.I filename +[ +.I command +] [ +.I username +[ +.I encpasswd +] ] +.PP +.SH DESCRIPTION +.B dbmmanage +is used to create and update the DBM format files used to store +usernames and password for basic authentication of HTTP users. +Resources available from the +.B httpd +Apache web server can be restricted to just the users listed +in the files created by +.B dbmmanage. +This program can only be used +when the usernames are stored in a DBM file. To use a +flat-file database see +\fBhtpasswd\fP. +.PP +This manual page only lists the command line arguments. For details of +the directives necessary to configure user authentication in +.B httpd +see +the Apache manual, which is part of the Apache distribution or can be +found at http://www.apache.org/. +.SH OPTIONS +.IP \fB\fIfilename\fP +The filename of the DBM format file. Usually without the +extension .db, .pag, or .dir. +.IP \fB\fIcommand\fP +This selects the operation to perform: +.TP 12 +.B add +Adds an entry for \fIusername\fP to \fIfilename\fP using the encrypted +password \fIencpassword\fP. +.TP 12 +.B adduser +Asks for a password and then adds an entry for \fIusername\fP to +\fIfilename\fP . +.TP 12 +.B check +Asks for a password and then checks if +\fIusername\fP is in \fIfilename\fP and if it's password matches +the specified one. +.TP 12 +.B delete +Deletes the \fIusername\fP entry from \fIfilename\fP. +.TP 12 +.B import +Reads username:password entries (one per line) from STDIN and adds them to +\fIfilename\fP. The passwords already has to be crypted. +.TP 12 +.B update +Same as the "adduser" command, except that it makes sure \fIusername\fP +already exists in \fIfilename\fP. +.TP 12 +.B view +Just displays the complete contents of the DBM file. +.IP \fB\fIusername\fP +The user for which the update operation is performed. +.PD +.SH BUGS +.PP +One should be aware that there are a number of different DBM file +formats in existance, and with all likelihood, libraries for more than +one format may exist on your system. The three primary examples are +NDBM, the GNU project's GDBM, and Berkeley DB 2. Unfortunately, all +these libraries use different file formats, and you must make sure +that the file format used by +.I filename +is the same format that +.B dbmmanage +expects to see. +.B dbmmanage +currently has no way of determining what type of DBM file it is +looking at. If used against the wrong format, +.dbmmanage +will simply return nothing, or may create a different DBM file with a +different name, or at worst, it may corrupt the DBM file if you were +attempting to write to it. +.PP +.B dbmmanage +has a list of DBM format preferences, defined by the +.B @AnyDBM::ISA +array near the beginning of the program. Since we prefer the Berkeley +DB 2 file format, the order in which +.B dbmmanage +will look for system libraries is Berkeley DB 2, then NDBM, and then +GDBM. The first library found will be the library +.B dbmmanage +will attempt to use for all DBM file transactions. This ordering is +slightly different than the standard +.B @AnyDBM::ISA +ordering in perl, as well as the ordering used by the simple dbmopen() +call in Perl, so if you use any other utilities to manage your DBM +files, they must also follow this preference ordering. Similar care +must be taken if using programs in other languages, like C, to +access these files. +.PP +Apache's +.B mod_auth_db.c +module corresponds to Berkeley DB 2 library, while +.B mod_auth_dbm.c +corresponds to the NDBM library. Also, one can usually use the +.B file +program supplied with most Unix systems to see what format a DBM file is in. +.PD +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/htdigest.1 b/docs/man/htdigest.1 new file mode 100644 index 00000000000..c719e456ce9 --- /dev/null +++ b/docs/man/htdigest.1 @@ -0,0 +1,97 @@ +.TH htdigest 1 "March 1998" +.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. For written permission, please contact +.\" apache@apache.org. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +htdigest \- Create and update user authentication files +.SH SYNOPSIS +.B htdigest +[ +.B \-c +] +.I passwdfile +.I realm +.I username +.SH DESCRIPTION +.B htdigest +is used to create and update the flat-files used to store +usernames, realm and password for digest authentication of HTTP users. +Resources available from the +.B httpd +Apache web server can be restricted to just the users listed +in the files created by +.B htdigest. +.PP +This manual page only lists the command line arguments. For details of +the directives necessary to configure digest authentication in +.B httpd +see +the Apache manual, which is part of the Apache distribution or can be +found at http://www.apache.org/. +.SH OPTIONS +.IP \-c +Create the \fIpasswdfile\fP. If \fIpasswdfile\fP already exists, it +is deleted first. +.IP \fB\fIpasswdfile\fP +Name of the file to contain the username, realm and password. If \-c +is given, this file is created if it does not already exist, +or deleted and recreated if it does exist. +.IP \fB\fIrealm\fP +The realm name to which the user name belongs to. +.IP \fB\fIusername\fP +The user name to create or update in \fBpasswdfile\fP. If +\fIusername\fP does not exist is this file, an entry is added. If it +does exist, the password is changed. +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/htpasswd.1 b/docs/man/htpasswd.1 new file mode 100644 index 00000000000..1687cde41c2 --- /dev/null +++ b/docs/man/htpasswd.1 @@ -0,0 +1,213 @@ +.TH htpasswd 1 "February 1997" +.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. For written permission, please contact +.\" apache@apache.org. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +htpasswd \- Create and update user authentication files +.SH SYNOPSIS +.B htpasswd +[ +.B \-c +] +[ +.B \-m +] +.I passwdfile +.I username +.br +.B htpasswd +.B \-b +[ +.B \-c +] +[ +.B \-m +.B \-d +.B \-p +.B \-s +] +.I passwdfile +.I username +.I password +.SH DESCRIPTION +.B htpasswd +is used to create and update the flat-files used to store +usernames and password for basic authentication of HTTP users. +If +.B htpasswd +cannot access a file, such as not being able to write to the output +file or not being able to read the file in order to update it, +it returns an error status and makes no changes. +.PP +Resources available from the +.B httpd +Apache web server can be restricted to just the users listed +in the files created by +.B htpasswd. +This program can only be used +when the usernames are stored in a flat-file. To use a +DBM database see +\fBdbmmanage\fP. +.PP +.B htpasswd +encrypts passwords using either a version of MD5 modified for Apache, +or the system's \fIcrypt()\fP routine. Files managed by +.B htpasswd +may contain both types of passwords; some user records may have +MD5-encrypted passwords while others in the same file may have passwords +encrypted with \fIcrypt()\fP. +.PP +This manual page only lists the command line arguments. For details of +the directives necessary to configure user authentication in +.B httpd +see +the Apache manual, which is part of the Apache distribution or can be +found at . +.SH OPTIONS +.IP \-b +Use batch mode; \fIi.e.\fP, get the password from the command line +rather than prompting for it. \fBThis option should be used with +extreme care, since the password is clearly visible on the command +line.\fP +.IP \-c +Create the \fIpasswdfile\fP. If \fIpasswdfile\fP already exists, it +is rewritten and truncated. +.IP \-m +Use MD5 encryption for passwords. On Windows and TPF, this is the default. +.IP \-d +Use crypt() encryption for passwords. The default on all platforms but +Windows and TPF. Though possibly supported by +.B htpasswd +onm all platforms, it is not supported by the +.B httpd +server on Windows and TPF. +.IP \-s +Use SHA encryption for passwords. Faciliates migration from/to Netscape +servers using the LDAP Directory Interchange Format (ldif). +.IP \-p +Use plaintext passwords. Though +.B htpasswd +will support creation on all platofrms, the +.B httpd +deamon will only accept plain text passwords on Windows and TPF. +.IP \fB\fIpasswdfile\fP +Name of the file to contain the user name and password. If \-c +is given, this file is created if it does not already exist, +or rewritten and truncated if it does exist. +.IP \fB\fIusername\fP +The username to create or update in \fBpasswdfile\fP. If +\fIusername\fP does not exist in this file, an entry is added. If it +does exist, the password is changed. +.IP \fB\fIpassword\fP +The plaintext password to be encrypted and stored in the file. Only used +with the \fI-b\fP flag. +.SH EXIT STATUS +.B htpasswd +returns a zero status ("true") if the username and password have +been successfully added or updated in the \fIpasswdfile\fP. +.B htpasswd +returns 1 if it encounters some problem accessing files, 2 if there +was a syntax problem with the command line, 3 if the password was +entered interactively and the verification entry didn't match, 4 if +its operation was interrupted, 5 if a value is too long (username, +filename, password, or final computed record), and 6 if the username +contains illegal characters (see the \fBRESTRICTIONS\fP section). +.SH EXAMPLES +\fBhtpasswd /usr/local/etc/apache/.htpasswd-users jsmith\fP +.IP +Adds or modifies the password for user \fIjsmith\fP. +The user is prompted for the password. If executed +on a Windows system, the password will be encrypted using the +modified Apache MD5 algorithm; otherwise, the system's +\fIcrypt()\fP routine will be used. If the file does not +exist, +.B htpasswd +will do nothing except return an error. +.LP +\fBhtpasswd -c /home/doe/public_html/.htpasswd jane\fP +.IP +Creates a new file and stores a record in it for user \fIjane\fP. +The user is prompted for the password. +If the file exists and cannot be read, or cannot be written, +it is not altered and +.B htpasswd +will display a message and return an error status. +.LP +\fBhtpasswd -mb /usr/web/.htpasswd-all jones Pwd4Steve\fP +.IP +Encrypts the password from the command line (\fIPwd4Steve\fP) using +the MD5 algorithm, and stores it in the specified file. +.LP +.SH SECURITY CONSIDERATIONS +Web password files such as those managed by +.B htpasswd +should \fBnot\fP be within the Web server's URI space -- that is, +they should not be fetchable with a browser. +.PP +The use of the \fI-b\fP option is discouraged, since when it is +used the unencrypted password appears on the command line. +.SH RESTRICTIONS +On the Windows and MPE platforms, passwords encrypted with +.B htpasswd +are limited to no more than 255 characters in length. Longer +passwords will be truncated to 255 characters. +.PP +The MD5 algorithm used by +.B htpasswd +is specific to the Apache software; passwords encrypted using it will not be +usable with other Web servers. +.PP +Usernames are limited to 255 bytes and may not include the character ':'. +.SH SEE ALSO +.BR httpd(8) +and the scripts in support/SHA1 which come with the distribution. diff --git a/docs/man/httpd.8 b/docs/man/httpd.8 new file mode 100644 index 00000000000..ae320bad2ad --- /dev/null +++ b/docs/man/httpd.8 @@ -0,0 +1,211 @@ +.TH httpd 8 "February 1997" +.\" Copyright (c) 1995-1997 David Robinson. All rights reserved. +.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. For written permission, please contact +.\" apache@apache.org. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +httpd \- Apache hypertext transfer protocol server +.SH SYNOPSIS +.B httpd +[ +.B \-X +] [ +.BI \-R " libexecdir" +] [ +.BI \-d " serverroot" +] [ +.BI \-f " config" +] [ +.BI \-C " directive" +] [ +.BI \-c " directive" +] [ +.BI \-D " parameter" +] + +.B httpd +[ +.B \-h +] +[ +.B \-l +] +[ +.B \-L +] +[ +.B \-v +] +[ +.B \-V +] +[ +.B \-S +] +[ +.B \-t +] +[ +.B \-T +] + +.SH DESCRIPTION +.B httpd +is the Apache HyperText Transfer Protocol (HTTP) server program. It is +designed to be run as a standalone daemon process. When used like this +it will create a pool of child processes to handle requests. To stop +it, send a TERM signal to the initial (parent) process. The PID of +this process is written to a file as given in the configuration file. +Alternatively +.B httpd +may be invoked by the Internet daemon inetd(8) each +time a connection to the HTTP service is made. +.PP +This manual page only lists the command line arguments. For details +of the directives necessary to configure +.B httpd +see the Apache manual, +which is part of the Apache distribution or can be found at +http://www.apache.org/. Paths in this manual may not reflect those +compiled into +.B httpd. +.SH OPTIONS +.TP 12 +.BI \-R " libexecdir" +This option is only available if Apache was built with +the +.I SHARED_CORE +rule enabled which forces the Apache core code to be +placed into a dynamic shared object (DSO) file. This file +is searched in a hardcoded path under ServerRoot per default. Use this +option if you want to override it. +.TP 12 +.BI \-d " serverroot" +Set the initial value for the ServerRoot directive to \fIserverroot\fP. This +can be overridden by the ServerRoot command in the configuration file. The +default is \fB/usr/local/apache\fP. +.TP +.BI \-f " config" +Execute the commands in the file \fIconfig\fP on startup. If \fIconfig\fP +does not begin with a /, then it is taken to be a path relative to +the ServerRoot. The default is \fBconf/httpd.conf\fP. +.TP +.BI \-C " directive" +Process the configuration \fIdirective\fP before reading config files. +.TP +.BI \-c " directive" +Process the configuration \fIdirective\fP after reading config files. +.TP +.BI \-D " parameter" +Sets a configuration \fIparameter\fP which can be used with +... sections in the configuration files +to conditionally skip or process commands. +.TP +.B \-h +Output a short summary of available command line options. +.TP +.B \-l +Output a list of modules compiled into the server. +.TP +.B \-L +Output a list of directives together with expected arguments and +places where the directive is valid. +.TP +.B \-S +Show the settings as parsed from the config file (currently only shows the +virtualhost settings). +.TP +.B \-t +Run syntax tests for configuration files only. The program immediately exits +after these syntax parsing with either a return code of 0 (Syntax OK) or +return code not equal to 0 (Syntax Error). +.TP +.B \-T +Same as option +.B \-t +but does not check the configured document roots. +.TP +.B \-X +Run in single-process mode, for internal debugging purposes only; the daemon +does not detach from the terminal or fork any children. Do NOT use this mode +to provide ordinary web service. +.TP +.B \-v +Print the version of +.B httpd +, and then exit. +.TP +.B \-V +Print the version and build parameters of +.B httpd +, and then exit. +.SH FILES +.PD 0 +.B /usr/local/apache/conf/httpd.conf +.br +.B /usr/local/apache/conf/srm.conf +.br +.B /usr/local/apache/conf/access.conf +.br +.B /usr/local/apache/conf/mime.types +.br +.B /usr/local/apache/conf/magic +.br +.B /usr/local/apache/logs/error_log +.br +.B /usr/local/apache/logs/access_log +.br +.B /usr/local/apache/logs/httpd.pid +.PD +.SH SEE ALSO +.BR inetd (8). diff --git a/docs/man/logresolve.8 b/docs/man/logresolve.8 new file mode 100644 index 00000000000..4851269ad61 --- /dev/null +++ b/docs/man/logresolve.8 @@ -0,0 +1,87 @@ +.TH logresolve 8 "March 1998" +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +logresolve \- resolve hostnames for IP-adresses in Apache logfiles +.SH SYNOPSIS +.B logresolve +[ +.BI \-s " filename" +] [ +.B \-c +] < +.I access_log +> +.I access_log.new +.PP +.SH DESCRIPTION +.B logresolve +is a post-processing program to resolve IP-adresses in Apache's access +logfiles. To minimize impact on your nameserver, logresolve has its very own +internal hash-table cache. This means that each IP number will only be looked +up the first time it is found in the log file. +.SH OPTIONS +.TP 12 +.BI \-s " filename" +Specifies a filename to record statistics. +.TP 12 +.B \-c +This causes +.B logresolve +to apply some DNS checks: after finding the hostname from the IP address, it +looks up the IP addresses for the hostname and checks that one of these +matches the original address. +.PD +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/rotatelogs.8 b/docs/man/rotatelogs.8 new file mode 100644 index 00000000000..aee02e11ecd --- /dev/null +++ b/docs/man/rotatelogs.8 @@ -0,0 +1,83 @@ +.TH rotatelogs 8 "March 1998" +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +rotatelogs \- rotate Apache logs without having to kill the server +.SH SYNOPSIS +.B rotatelogs +.I logfile +.I rotationtime +.PP +.SH DESCRIPTION +.B rotatelogs +is a simple program for use in conjunction with Apache's piped logfile +feature which can be used like this: + +.fi + TransferLog "|rotatelogs /path/to/logs/access_log 86400" +.mf + +This creates the files /path/to/logs/access_log.nnnn where nnnn is the system +time at which the log nominally starts (this time will always be a multiple of +the rotation time, so you can synchronize cron scripts with it). At the end +of each rotation time (here after 24 hours) a new log is started. +.SH OPTIONS +.IP \fB\fIlogfile\fP +The path plus basename of the logfile. The suffix .nnnn is automatically +added. +.IP \fB\fIrotationtime\fP +The rotation time in seconds. +.PD +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/suexec.8 b/docs/man/suexec.8 new file mode 100644 index 00000000000..801d7abce91 --- /dev/null +++ b/docs/man/suexec.8 @@ -0,0 +1,70 @@ +.TH suexec 8 "March 1998" +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +suexec \- Switch User For Exec +.SH SYNOPSIS +No synopsis for usage, because this program +is used internally by Apache only. +.PP +.SH DESCRIPTION +.B suexec +is the "wrapper" support program for the suEXEC behaviour for Apache. +It is run from within Apache automatically to switch the user when +an external program has to be run under a different user. For more +information about suEXEC see the document `Apache suEXEC Support' +under http://www.apache.org/docs/suexec.html . +.PD +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/manual/developer/hooks.html b/docs/manual/developer/hooks.html new file mode 100644 index 00000000000..39f4e8bad17 --- /dev/null +++ b/docs/manual/developer/hooks.html @@ -0,0 +1,202 @@ + + + +Apache 2.0 Hook Functions + + + + + +

Apache Hook Functions

+ +

In general, a hook function is one that Apache will call at some +point during the processing of a request. Modules can provide +functions that are called, and specify when they get called in +comparison to other modules.

+ +

Creating a hook function

+ +

In order to create a new hook, four things need to be done:

+ +

Declare the hook function

+ +

Use the DECLARE_HOOK macro, which needs to be given the name of the +hook, the return type of the hook function and the arguments. For +example, if the hook returns an int and takes a +request_rec * and an int and is called +"do_something", then declare it like this:

+ +DECLARE_HOOK(int,do_something,(request_rec *r,int n)) + +

This should go in a header which modules will include if they want +to use the hook.

+ +

Create the hook structure

+ +

Each source file that exports a hook has a private structure which +is used to record the module functions that use the hook. This is +declared as follows:

+ +
+HOOK_STRUCT(
+            HOOK_LINK(do_something)
+            ...
+           )
+
+ +

Implement the hook caller

+ +

The source file that exports the hook has to implement a function +that will call the hook. There are currently three possible ways to do +this. In all cases, the calling function is called +ap_run_hookname().

+ +

Void hooks

+ +

If the return value of a hook is void, then all the hooks are +called, and the caller is implemented like this:

+ +IMPLEMENT_HOOK_VOID(do_something,(request_rec *r,int +n),(r,n)) + +

The second and third arguments are the dummy argument declaration and +the dummy arguments as they will be used when calling the hook. In +other words, this macro expands to something like this:

+ +
+void ap_run_do_something(request_rec *r,int n)
+{
+    ...
+    do_something(r,n);
+}
+
+ +

Hooks that return a value

+ +

If the hook returns a value, then it can either be run until the first +hook that does something interesting, like so:

+ +IMPLEMENT_HOOK_RUN_FIRST(int,do_something,(request_rec *r,int n),(r,n),DECLINED) + +

The first hook that doesn't return DECLINED stops +the loop and its return value is returned from the hook caller. Note +that DECLINED is the tradition Apache hook return meaning "I +didn't do anything", but it can be whatever suits you.

+ +

Alternatively, all hooks can be run until an error occurs. This +boils down to permitting two return values, one of which means +"I did something, and it was OK" and the other meaning "I did +nothing". The first function that returns a value other than one of +those two stops the loop, and its return is the return value. Declare +these like so:

+ +IMPLEMENT_HOOK_RUN_ALL(int,do_something,(request_rec *r,int +n),(r,n),OK,DECLINED) + +

Again, OK and DECLINED are the traditional +values. You can use what you want.

+ +

Call the hook callers

+ +

At appropriate moments in the code, call the hook caller, like +so:

+ +
+    int n,ret;
+    request_rec *r;
+
+    ret=ap_run_do_something(r,n);
+
+ +

Hooking the hook

+ +

A module that wants a hook to be called needs to do two +things.

+ +

Implement the hook function

+ +

Include the appropriate header, and define a static function of the +correct type:

+ +
+static int my_something_doer(request_rec *r,int n)
+{
+    ...
+    return OK;
+}
+
+ +

Add a hook registering function

+ +

During initialisation, Apache will call each modules hook +registering function, which is included in the module structure:

+ +
+static void my_register_hooks()
+{
+    ap_hook_do_something(my_something_doer,NULL,NULL,HOOK_MIDDLE);
+}
+
+mode MODULE_VAR_EXPORT my_module =
+{
+    ...
+    my_register_hooks       /* register hooks */
+};
+
+ +

Controlling hook calling order

+ +

In the example above, we didn't use the three arguments in the hook +registration function that control calling order. There are two +mechanisms for doing this. The first, rather crude, method, allows us +to specify roughly where the hook is run relative to other +modules. The final argument control this. There are three possible +values:

+ +
+HOOK_FIRST
+HOOK_MIDDLE
+HOOK_LAST
+
+ +

All modules using any particular value may be run in any order +relative to each other, but, of course, all modules using +HOOK_FIRST will be run before HOOK_MIDDLE which are +before HOOK_LAST. Modules that don't care when they are run +should use HOOK_MIDDLE. (I spaced these out so people +could do stuff like HOOK_FIRST-2 to get in slightly earlier, +but is this wise? - Ben)

+ +

Note that there are two more values, HOOK_REALLY_FIRST and +HOOK_REALLY_LAST. These should only be used by the hook +exporter.

+ +

The other method allows finer control. When a module knows that it +must be run before (or after) some other modules, it can specify them +by name. The second (third) argument is a NULL-terminated array of +strings consisting of the names of modules that must be run before +(after) the current module. For example, suppose we want "mod_xyz.c" +and "mod_abc.c" to run before we do, then we'd hook as follows:

+ +
+static void register_hooks()
+{
+    static const char * const aszPre[]={ "mod_xyz.c", "mod_abc.c", NULL };
+
+    ap_hook_do_something(my_something_doer,aszPre,NULL,HOOK_MIDDLE);
+}
+
+ +

Note that the sort used to achieve this is stable, so ordering set +by HOOK_ORDER is preserved, as far as is +possible.

+ +Ben Laurie, 15th August 1999 + + diff --git a/docs/manual/developer/index.html b/docs/manual/developer/index.html new file mode 100644 index 00000000000..a72dd44b212 --- /dev/null +++ b/docs/manual/developer/index.html @@ -0,0 +1,19 @@ + + + +Apache-2.0 Developer Documentation + + + +

Developer Documentation for Apache-2.0

+

Apache Hook Functions

+

Converting Apache 1.3 Modules to Apache 2.0

+

MPM listing

+ + diff --git a/docs/manual/developer/modules.html b/docs/manual/developer/modules.html new file mode 100644 index 00000000000..b066223466f --- /dev/null +++ b/docs/manual/developer/modules.html @@ -0,0 +1,200 @@ + + + +Converting Modules from Apache 1.3 to Apache 2.0 + + + + + +

From Apache 1.3 to Apache 2.0
Modules

+ +

+This is a first attempt at writing the lessons I learned when trying to convert the mod_mmap_static module to Apache 2.0. It's by no means definitive and probably won't even be correct in some ways, but it's a start. +

+
+

The easier changes... +

+ +

Cleanup Routines

+

+These now need to be of type ap_status_t and return a value of that type. Normally the return value will be APR_SUCCESS unless there is some need to signal an error in the cleanup. Be aware that even though you signal an error not all code yet checks and acts upon the error. +

+ +

Initialisation Routines

+ +

+These should now be renamed to better signify where they sit in the overall process. So the name gets a small change from mmap_init to mmap_post_config. The arguments passed have undergone a radical change and now look like +

+
    +
  • ap_context_t *p,
  • +
  • ap_context_t *plog,
  • +
  • ap_context_t *ptemp,
  • +
  • server_rec *s
  • +
+

+Throughout Apache the old pools have been replced by the ap_context_t, though their use remains remarkably similar. +

+ +

Data Types

+

+A lot of the data types have been moved into the APR. This means that some have had a name change, such as the one shown above. The following is a brief list of some of the changes that you are likely to have to make. +

    +
  • pool becomes ap_context_t
  • +
  • table becomes ap_table_t
  • +
+
+ +

+The messier changes... +

+

Register Hooks

+

+The new architecture uses a series of hooks to provide for calling your functions. These you'll need to add to your module by way of a new function, static void register_hooks(void). The function is really reasonably straightforward once you understand what needs to be done. Each function that needs calling at some stage in the processing of a request needs to be registered, handlers do not. There are a number of phases where functions can be added, and for each you can specify with a high degree of control the relative order that the function will be called in. +

+

+This is the code that was added to mod_mmap_static +

+
+static void register_hooks(void)
+{
+    static const char * const aszPre[]={ "http_core.c",NULL };
+    ap_hook_post_config(mmap_post_config,NULL,NULL,HOOK_MIDDLE);
+    ap_hook_translate_name(mmap_static_xlat,aszPre,NULL,HOOK_LAST);
+};
+
+

+This registers 2 functions that need to be called, one in the post_config stage (virtually every module will need this one) and one for the translate_name phase. note that while there are different function names the format of each is identical. So what is the format? +

+

+ap_hook_[phase_name](function_name, predecessors, successors, position); +

+

+There are 3 hook positions defined... +

+
    +
  • HOOK_FIRST
  • +
  • HOOK_MIDDLE
  • +
  • HOOK_LAST
  • +
+

+To define the position you use the position and then modify it with the predecessors and successors. each of the modifiers can be a list of functions that should be called, either before the function is run (predecessors) or after the function has run (successors). +

+

+In the mod_mmap_static case I didn't care about the post_config stage, but the mmap_static_xlat MUST be called after the core module had done it's name translation, hence the use of the aszPre to define a modifier to the position HOOK_LAST. +

+

Module Definition

+ +

+There are now a lot fewer stages to worry about when creating your module definition. The old defintion looked like +

+
+module MODULE_VAR_EXPORT [module_name]_module =
+{
+    STANDARD_MODULE_STUFF,
+    /* initializer */
+    /* dir config creater */
+    /* dir merger --- default is to override */
+    /* server config */
+    /* merge server config */
+    /* command handlers */
+    /* handlers */
+    /* filename translation */
+    /* check_user_id */
+    /* check auth */
+    /* check access */
+    /* type_checker */
+    /* fixups */
+    /* logger */
+    /* header parser */
+    /* child_init */
+    /* child_exit */
+    /* post read-request */
+};
+
+

+The new structure is a great deal simpler... +

+
+module MODULE_VAR_EXPORT [module_name]_module =
+{
+    STANDARD20_MODULE_STUFF,
+    /* create per-directory config structures */
+    /* merge per-directory config structures  */
+    /* create per-server config structures    */
+    /* merge per-server config structures     */
+    /* command handlers */
+    /* handlers */
+    /* register hooks */
+ };
+
+

+Some of these read directly across, some don't. I'll try to summarise what should be done below. +

+

+The stages that read directly across : +

+
    +
  • +/* dir config creater */ ==> /* create per-directory config structures */ +
  • +
  • +/* server config */ ==> /* create per-server config structures */ +
  • +
  • +/* dir merger */ ==> /* merge per-directory config structures */ +
  • +
  • +/* merge server config */ ==> /* merge per-server config structures */ +
  • +
  • +/* command table */ ==> /* command ap_table_t */ +
  • +
  • +/* handlers */ ==> /* handlers */ +
  • +
+

+The remainder of the old functions should be registered as hooks. There are the following hook stages defined so far... +

+ +
    +
  • +ap_hook_post_config (this is where the old _init routines get registered) +
  • +
  • +ap_hook_http_method +
  • +
  • +ap_hook_open_logs +
  • +
  • +ap_hook_auth_checker +
  • +
  • +ap_hook_default_port +
  • +
  • +ap_hook_access_checker +
  • +
  • +ap_hook_process_connection +
  • +
  • +ap_hook_child_init_hook +
  • + + + + + + + + + diff --git a/docs/manual/developer/modules.html.en b/docs/manual/developer/modules.html.en new file mode 100644 index 00000000000..b066223466f --- /dev/null +++ b/docs/manual/developer/modules.html.en @@ -0,0 +1,200 @@ + + + +Converting Modules from Apache 1.3 to Apache 2.0 + + + + + +

    From Apache 1.3 to Apache 2.0
    Modules

    + +

    +This is a first attempt at writing the lessons I learned when trying to convert the mod_mmap_static module to Apache 2.0. It's by no means definitive and probably won't even be correct in some ways, but it's a start. +

    +
    +

    The easier changes... +

    + +

    Cleanup Routines

    +

    +These now need to be of type ap_status_t and return a value of that type. Normally the return value will be APR_SUCCESS unless there is some need to signal an error in the cleanup. Be aware that even though you signal an error not all code yet checks and acts upon the error. +

    + +

    Initialisation Routines

    + +

    +These should now be renamed to better signify where they sit in the overall process. So the name gets a small change from mmap_init to mmap_post_config. The arguments passed have undergone a radical change and now look like +

    +
      +
    • ap_context_t *p,
    • +
    • ap_context_t *plog,
    • +
    • ap_context_t *ptemp,
    • +
    • server_rec *s
    • +
    +

    +Throughout Apache the old pools have been replced by the ap_context_t, though their use remains remarkably similar. +

    + +

    Data Types

    +

    +A lot of the data types have been moved into the APR. This means that some have had a name change, such as the one shown above. The following is a brief list of some of the changes that you are likely to have to make. +

      +
    • pool becomes ap_context_t
    • +
    • table becomes ap_table_t
    • +
    +
    + +

    +The messier changes... +

    +

    Register Hooks

    +

    +The new architecture uses a series of hooks to provide for calling your functions. These you'll need to add to your module by way of a new function, static void register_hooks(void). The function is really reasonably straightforward once you understand what needs to be done. Each function that needs calling at some stage in the processing of a request needs to be registered, handlers do not. There are a number of phases where functions can be added, and for each you can specify with a high degree of control the relative order that the function will be called in. +

    +

    +This is the code that was added to mod_mmap_static +

    +
    +static void register_hooks(void)
    +{
    +    static const char * const aszPre[]={ "http_core.c",NULL };
    +    ap_hook_post_config(mmap_post_config,NULL,NULL,HOOK_MIDDLE);
    +    ap_hook_translate_name(mmap_static_xlat,aszPre,NULL,HOOK_LAST);
    +};
    +
    +

    +This registers 2 functions that need to be called, one in the post_config stage (virtually every module will need this one) and one for the translate_name phase. note that while there are different function names the format of each is identical. So what is the format? +

    +

    +ap_hook_[phase_name](function_name, predecessors, successors, position); +

    +

    +There are 3 hook positions defined... +

    +
      +
    • HOOK_FIRST
    • +
    • HOOK_MIDDLE
    • +
    • HOOK_LAST
    • +
    +

    +To define the position you use the position and then modify it with the predecessors and successors. each of the modifiers can be a list of functions that should be called, either before the function is run (predecessors) or after the function has run (successors). +

    +

    +In the mod_mmap_static case I didn't care about the post_config stage, but the mmap_static_xlat MUST be called after the core module had done it's name translation, hence the use of the aszPre to define a modifier to the position HOOK_LAST. +

    +

    Module Definition

    + +

    +There are now a lot fewer stages to worry about when creating your module definition. The old defintion looked like +

    +
    +module MODULE_VAR_EXPORT [module_name]_module =
    +{
    +    STANDARD_MODULE_STUFF,
    +    /* initializer */
    +    /* dir config creater */
    +    /* dir merger --- default is to override */
    +    /* server config */
    +    /* merge server config */
    +    /* command handlers */
    +    /* handlers */
    +    /* filename translation */
    +    /* check_user_id */
    +    /* check auth */
    +    /* check access */
    +    /* type_checker */
    +    /* fixups */
    +    /* logger */
    +    /* header parser */
    +    /* child_init */
    +    /* child_exit */
    +    /* post read-request */
    +};
    +
    +

    +The new structure is a great deal simpler... +

    +
    +module MODULE_VAR_EXPORT [module_name]_module =
    +{
    +    STANDARD20_MODULE_STUFF,
    +    /* create per-directory config structures */
    +    /* merge per-directory config structures  */
    +    /* create per-server config structures    */
    +    /* merge per-server config structures     */
    +    /* command handlers */
    +    /* handlers */
    +    /* register hooks */
    + };
    +
    +

    +Some of these read directly across, some don't. I'll try to summarise what should be done below. +

    +

    +The stages that read directly across : +

    +
      +
    • +/* dir config creater */ ==> /* create per-directory config structures */ +
    • +
    • +/* server config */ ==> /* create per-server config structures */ +
    • +
    • +/* dir merger */ ==> /* merge per-directory config structures */ +
    • +
    • +/* merge server config */ ==> /* merge per-server config structures */ +
    • +
    • +/* command table */ ==> /* command ap_table_t */ +
    • +
    • +/* handlers */ ==> /* handlers */ +
    • +
    +

    +The remainder of the old functions should be registered as hooks. There are the following hook stages defined so far... +

    + +
      +
    • +ap_hook_post_config (this is where the old _init routines get registered) +
    • +
    • +ap_hook_http_method +
    • +
    • +ap_hook_open_logs +
    • +
    • +ap_hook_auth_checker +
    • +
    • +ap_hook_default_port +
    • +
    • +ap_hook_access_checker +
    • +
    • +ap_hook_process_connection +
    • +
    • +ap_hook_child_init_hook +
    • + + + + + + + + + diff --git a/docs/manual/index.html b/docs/manual/index.html new file mode 100644 index 00000000000..5bfef758c07 --- /dev/null +++ b/docs/manual/index.html @@ -0,0 +1,31 @@ + + + +Apache-2.0 Documentation + + + +

      Manual Index for Apache-2.0

      +

      The documentation has been split into 2 sections. One is for people installing and configuring an Apache server, the other is for those who are developing the server.

      +

      Where do you want to go today?

      + + + + + +
      +I'm configuring/installing a server + +I'm a developer +
      +
      +David Reid, 15th January 2000 + + + diff --git a/docs/manual/index.html.en b/docs/manual/index.html.en new file mode 100644 index 00000000000..5bfef758c07 --- /dev/null +++ b/docs/manual/index.html.en @@ -0,0 +1,31 @@ + + + +Apache-2.0 Documentation + + + +

      Manual Index for Apache-2.0

      +

      The documentation has been split into 2 sections. One is for people installing and configuring an Apache server, the other is for those who are developing the server.

      +

      Where do you want to go today?

      + + + + + +
      +I'm configuring/installing a server + +I'm a developer +
      +
      +David Reid, 15th January 2000 + + + diff --git a/docs/manual/misc/rewriteguide.html b/docs/manual/misc/rewriteguide.html new file mode 100644 index 00000000000..b9c10e2aacc --- /dev/null +++ b/docs/manual/misc/rewriteguide.html @@ -0,0 +1,1905 @@ + + +Apache 1.3 URL Rewriting Guide + + + + +
      + + +
      + +

      +Apache 1.3
      +URL Rewriting Guide
      +

      + +
      Originally written by
      +Ralf S. Engelschall <rse@apache.org>
      +December 1997
      + +
      + +

      +This document supplements the mod_rewrite reference documentation. It describes +how one can use Apache's mod_rewrite to solve typical URL-based problems +webmasters are usually confronted with in practice. I give detailed +descriptions on how to solve each problem by configuring URL rewriting +rulesets. + +

      Introduction to mod_rewrite

      + +The Apache module mod_rewrite is a killer one, i.e. it is a really +sophisticated module which provides a powerful way to do URL manipulations. +With it you can nearly do all types of URL manipulations you ever dreamed +about. The price you have to pay is to accept complexity, because +mod_rewrite's major drawback is that it is not easy to understand and use for +the beginner. And even Apache experts sometimes discover new aspects where +mod_rewrite can help. +

      +In other words: With mod_rewrite you either shoot yourself in the foot the +first time and never use it again or love it for the rest of your life because +of its power. This paper tries to give you a few initial success events to +avoid the first case by presenting already invented solutions to you. + +

      Practical Solutions

      + +Here come a lot of practical solutions I've either invented myself or +collected from other peoples solutions in the past. Feel free to learn the +black magic of URL rewriting from these examples. + +

      +ATTENTION: Depending on your server-configuration it can be necessary to +slightly change the examples for your situation, e.g. adding the [PT] flag +when additionally using mod_alias and mod_userdir, etc. Or rewriting a ruleset +to fit in .htaccess context instead of per-server context. Always try +to understand what a particular ruleset really does before you use it. It +avoid problems. + +

      URL Layout

      + +

      +

      Canonical URLs

      +

      + +

      +
      Description: +
      +On some webservers there are more than one URL for a resource. Usually there +are canonical URLs (which should be actually used and distributed) and those +which are just shortcuts, internal ones, etc. Independed which URL the user +supplied with the request he should finally see the canonical one only. + +

      +

      Solution: +
      +We do an external HTTP redirect for all non-canonical URLs to fix them in the +location view of the Browser and for all subsequent requests. In the example +ruleset below we replace /~user by the canonical /u/user and +fix a missing trailing slash for /u/user. + +

      +RewriteRule   ^/~([^/]+)/?(.*)    /u/$1/$2  [R]
      +RewriteRule   ^/([uge])/([^/]+)$  /$1/$2/   [R]
      +
      + +

      + +

      +

      Canonical Hostnames

      +

      + +

      +
      Description: +
      +... + +

      +

      Solution: +
      + +

      +RewriteCond %{HTTP_HOST}   !^fully\.qualified\.domain\.name [NC]
      +RewriteCond %{HTTP_HOST}   !^$
      +RewriteCond %{SERVER_PORT} !^80$
      +RewriteRule ^/(.*)         http://fully.qualified.domain.name:%{SERVER_PORT}/$1 [L,R]
      +RewriteCond %{HTTP_HOST}   !^fully\.qualified\.domain\.name [NC]
      +RewriteCond %{HTTP_HOST}   !^$
      +RewriteRule ^/(.*)         http://fully.qualified.domain.name/$1 [L,R]
      +
      + +

      + +

      +

      Moved DocumentRoot

      +

      + +

      +
      Description: +
      +Usually the DocumentRoot of the webserver directly relates to the URL +``/''. But often this data is not really of top-level priority, it is +perhaps just one entity of a lot of data pools. For instance at our Intranet +sites there are /e/www/ (the homepage for WWW), /e/sww/ (the +homepage for the Intranet) etc. Now because the data of the DocumentRoot stays +at /e/www/ we had to make sure that all inlined images and other +stuff inside this data pool work for subsequent requests. + +

      +

      Solution: +
      +We just redirect the URL / to /e/www/. While is seems +trivial it is actually trivial with mod_rewrite, only. Because the typical +old mechanisms of URL Aliases (as provides by mod_alias and friends) +only used prefix matching. With this you cannot do such a redirection +because the DocumentRoot is a prefix of all URLs. With mod_rewrite it is +really trivial: + +

      +RewriteEngine on
      +RewriteRule   ^/$  /e/www/  [R]
      +
      + +

      + +

      +

      Trailing Slash Problem

      +

      + +

      +
      Description: +
      +Every webmaster can sing a song about the problem of the trailing slash on +URLs referencing directories. If they are missing, the server dumps an error, +because if you say /~quux/foo instead of +/~quux/foo/ then the server searches for a file named +foo. And because this file is a directory it complains. Actually +is tries to fix it themself in most of the cases, but sometimes this mechanism +need to be emulated by you. For instance after you have done a lot of +complicated URL rewritings to CGI scripts etc. + +

      +

      Solution: +
      +The solution to this subtle problem is to let the server add the trailing +slash automatically. To do this correctly we have to use an external redirect, +so the browser correctly requests subsequent images etc. If we only did a +internal rewrite, this would only work for the directory page, but would go +wrong when any images are included into this page with relative URLs, because +the browser would request an in-lined object. For instance, a request for +image.gif in /~quux/foo/index.html would become +/~quux/image.gif without the external redirect! +

      +So, to do this trick we write: + +

      +RewriteEngine  on
      +RewriteBase    /~quux/
      +RewriteRule    ^foo$  foo/  [R]
      +
      + +

      +The crazy and lazy can even do the following in the top-level +.htaccess file of their homedir. But notice that this creates some +processing overhead. + +

      +RewriteEngine  on
      +RewriteBase    /~quux/
      +RewriteCond    %{REQUEST_FILENAME}  -d
      +RewriteRule    ^(.+[^/])$           $1/  [R]
      +
      + +

      + +

      +

      Webcluster through Homogeneous URL Layout

      +

      + +

      +
      Description: +
      +We want to create a homogenous and consistent URL layout over all WWW servers +on a Intranet webcluster, i.e. all URLs (per definition server local and thus +server dependent!) become actually server independed! What we want is +to give the WWW namespace a consistent server-independend layout: no URL +should have to include any physically correct target server. The cluster +itself should drive us automatically to the physical target host. + +

      +

      Solution: +
      +First, the knowledge of the target servers come from (distributed) external +maps which contain information where our users, groups and entities stay. +The have the form + +

      +user1  server_of_user1
      +user2  server_of_user2
      +:      :
      +

      + +We put them into files map.xxx-to-host. Second we need to instruct +all servers to redirect URLs of the forms + +

      +/u/user/anypath
      +/g/group/anypath
      +/e/entity/anypath
      +

      + +to + +

      +http://physical-host/u/user/anypath
      +http://physical-host/g/group/anypath
      +http://physical-host/e/entity/anypath
      +

      + +when the URL is not locally valid to a server. The following ruleset does +this for us by the help of the map files (assuming that server0 is a default +server which will be used if a user has no entry in the map): + +

      +RewriteEngine on
      +
      +RewriteMap      user-to-host   txt:/path/to/map.user-to-host
      +RewriteMap     group-to-host   txt:/path/to/map.group-to-host
      +RewriteMap    entity-to-host   txt:/path/to/map.entity-to-host
      +
      +RewriteRule   ^/u/([^/]+)/?(.*)   http://${user-to-host:$1|server0}/u/$1/$2
      +RewriteRule   ^/g/([^/]+)/?(.*)  http://${group-to-host:$1|server0}/g/$1/$2
      +RewriteRule   ^/e/([^/]+)/?(.*) http://${entity-to-host:$1|server0}/e/$1/$2
      +
      +RewriteRule   ^/([uge])/([^/]+)/?$          /$1/$2/.www/
      +RewriteRule   ^/([uge])/([^/]+)/([^.]+.+)   /$1/$2/.www/$3\
      +
      + +

      + +

      +

      Move Homedirs to Different Webserver

      +

      + +

      +
      Description: +
      +A lot of webmaster aksed for a solution to the following situation: They +wanted to redirect just all homedirs on a webserver to another webserver. +They usually need such things when establishing a newer webserver which will +replace the old one over time. + +

      +

      Solution: +
      +The solution is trivial with mod_rewrite. On the old webserver we just +redirect all /~user/anypath URLs to +http://newserver/~user/anypath. + +

      +RewriteEngine on
      +RewriteRule   ^/~(.+)  http://newserver/~$1  [R,L]
      +
      + +

      + +

      +

      Structured Homedirs

      +

      + +

      +
      Description: +
      +Some sites with thousend of users usually use a structured homedir layout, +i.e. each homedir is in a subdirectory which begins for instance with the +first character of the username. So, /~foo/anypath is +/home/f/foo/.www/anypath while /~bar/anypath is +/home/b/bar/.www/anypath. + +

      +

      Solution: +
      +We use the following ruleset to expand the tilde URLs into exactly the above +layout. + +

      +RewriteEngine on
      +RewriteRule   ^/~(([a-z])[a-z0-9]+)(.*)  /home/$2/$1/.www$3
      +
      + +

      + +

      +

      Filesystem Reorganisation

      +

      + +

      +
      Description: +
      +This really is a hardcore example: a killer application which heavily uses +per-directory RewriteRules to get a smooth look and feel on the Web +while its data structure is never touched or adjusted. + +Background: net.sw is my archive of freely available Unix +software packages, which I started to collect in 1992. It is both my hobby and +job to to this, because while I'm studying computer science I have also worked +for many years as a system and network administrator in my spare time. Every +week I need some sort of software so I created a deep hierarchy of +directories where I stored the packages: + +

      +drwxrwxr-x   2 netsw  users    512 Aug  3 18:39 Audio/
      +drwxrwxr-x   2 netsw  users    512 Jul  9 14:37 Benchmark/
      +drwxrwxr-x  12 netsw  users    512 Jul  9 00:34 Crypto/
      +drwxrwxr-x   5 netsw  users    512 Jul  9 00:41 Database/
      +drwxrwxr-x   4 netsw  users    512 Jul 30 19:25 Dicts/
      +drwxrwxr-x  10 netsw  users    512 Jul  9 01:54 Graphic/
      +drwxrwxr-x   5 netsw  users    512 Jul  9 01:58 Hackers/
      +drwxrwxr-x   8 netsw  users    512 Jul  9 03:19 InfoSys/
      +drwxrwxr-x   3 netsw  users    512 Jul  9 03:21 Math/
      +drwxrwxr-x   3 netsw  users    512 Jul  9 03:24 Misc/
      +drwxrwxr-x   9 netsw  users    512 Aug  1 16:33 Network/
      +drwxrwxr-x   2 netsw  users    512 Jul  9 05:53 Office/
      +drwxrwxr-x   7 netsw  users    512 Jul  9 09:24 SoftEng/
      +drwxrwxr-x   7 netsw  users    512 Jul  9 12:17 System/
      +drwxrwxr-x  12 netsw  users    512 Aug  3 20:15 Typesetting/
      +drwxrwxr-x  10 netsw  users    512 Jul  9 14:08 X11/
      +

      + +In July 1996 I decided to make this 350 MB archive public to the world via a +nice Web interface ( +http://net.sw.engelschall.com/net.sw/). "Nice" means that I wanted to +offer a interface where you can browse directly through the archive hierarchy. +And "nice" means that I didn't wanted to change anything inside this hierarchy +- not even by putting some CGI scripts at the top of it. Why? Because the +above structure should be later accessible via FTP as well, and I didn't +want any Web or CGI stuuf to be there. + +

      +

      Solution: +
      +The solution has two parts: The first is a set of CGI scripts which create all +the pages at all directory levels on-the-fly. I put them under +/e/netsw/.www/ as follows: + +

      +-rw-r--r--   1 netsw  users    1318 Aug  1 18:10 .wwwacl
      +drwxr-xr-x  18 netsw  users     512 Aug  5 15:51 DATA/
      +-rw-rw-rw-   1 netsw  users  372982 Aug  5 16:35 LOGFILE
      +-rw-r--r--   1 netsw  users     659 Aug  4 09:27 TODO
      +-rw-r--r--   1 netsw  users    5697 Aug  1 18:01 netsw-about.html
      +-rwxr-xr-x   1 netsw  users     579 Aug  2 10:33 netsw-access.pl
      +-rwxr-xr-x   1 netsw  users    1532 Aug  1 17:35 netsw-changes.cgi
      +-rwxr-xr-x   1 netsw  users    2866 Aug  5 14:49 netsw-home.cgi
      +drwxr-xr-x   2 netsw  users     512 Jul  8 23:47 netsw-img/
      +-rwxr-xr-x   1 netsw  users   24050 Aug  5 15:49 netsw-lsdir.cgi
      +-rwxr-xr-x   1 netsw  users    1589 Aug  3 18:43 netsw-search.cgi
      +-rwxr-xr-x   1 netsw  users    1885 Aug  1 17:41 netsw-tree.cgi
      +-rw-r--r--   1 netsw  users     234 Jul 30 16:35 netsw-unlimit.lst
      +

      + +The DATA/ subdirectory holds the above directory structure, i.e. the +real net.sw stuff and gets automatically updated via +rdist from time to time. + + The second part of the problem remains: how to link these two structures +together into one smooth-looking URL tree? We want to hide the DATA/ +directory from the user while running the appropriate CGI scripts for the +various URLs. + +Here is the solution: first I put the following into the per-directory +configuration file in the Document Root of the server to rewrite the announced +URL /net.sw/ to the internal path /e/netsw: + +

      +RewriteRule  ^net.sw$       net.sw/        [R]
      +RewriteRule  ^net.sw/(.*)$  e/netsw/$1
      +
      + +

      +The first rule is for requests which miss the trailing slash! The second rule +does the real thing. And then comes the killer configuration which stays in +the per-directory config file /e/netsw/.www/.wwwacl: + +

      +Options       ExecCGI FollowSymLinks Includes MultiViews 
      +
      +RewriteEngine on
      +
      +#  we are reached via /net.sw/ prefix
      +RewriteBase   /net.sw/
      +
      +#  first we rewrite the root dir to 
      +#  the handling cgi script
      +RewriteRule   ^$                       netsw-home.cgi     [L]
      +RewriteRule   ^index\.html$            netsw-home.cgi     [L]
      +
      +#  strip out the subdirs when
      +#  the browser requests us from perdir pages
      +RewriteRule   ^.+/(netsw-[^/]+/.+)$    $1                 [L]
      +
      +#  and now break the rewriting for local files
      +RewriteRule   ^netsw-home\.cgi.*       -                  [L]
      +RewriteRule   ^netsw-changes\.cgi.*    -                  [L]
      +RewriteRule   ^netsw-search\.cgi.*     -                  [L]
      +RewriteRule   ^netsw-tree\.cgi$        -                  [L]
      +RewriteRule   ^netsw-about\.html$      -                  [L]
      +RewriteRule   ^netsw-img/.*$           -                  [L]
      +
      +#  anything else is a subdir which gets handled
      +#  by another cgi script
      +RewriteRule   !^netsw-lsdir\.cgi.*     -                  [C]
      +RewriteRule   (.*)                     netsw-lsdir.cgi/$1
      +
      + +

      +Some hints for interpretation: +

        +
      1. Notice the L (last) flag and no substitution field ('-') in the + forth part +
      2. Notice the ! (not) character and the C (chain) flag + at the first rule in the last part +
      3. Notice the catch-all pattern in the last rule +
      + +
      + +

      +

      NCSA imagemap to Apache mod_imap

      +

      + +

      +
      Description: +
      +When switching from the NCSA webserver to the more modern Apache webserver a +lot of people want a smooth transition. So they want pages which use their old +NCSA imagemap program to work under Apache with the modern +mod_imap. The problem is that there are a lot of +hyperlinks around which reference the imagemap program via +/cgi-bin/imagemap/path/to/page.map. Under Apache this +has to read just /path/to/page.map. + +

      +

      Solution: +
      +We use a global rule to remove the prefix on-the-fly for all requests: + +

      +RewriteEngine  on
      +RewriteRule    ^/cgi-bin/imagemap(.*)  $1  [PT]
      +
      + +

      + +

      +

      Search pages in more than one directory

      +

      + +

      +
      Description: +
      +Sometimes it is neccessary to let the webserver search for pages in more than +one directory. Here MultiViews or other techniques cannot help. + +

      +

      Solution: +
      +We program a explicit ruleset which searches for the files in the directories. + +

      +RewriteEngine on
      +
      +#   first try to find it in custom/...
      +#   ...and if found stop and be happy:
      +RewriteCond         /your/docroot/dir1/%{REQUEST_FILENAME}  -f
      +RewriteRule  ^(.+)  /your/docroot/dir1/$1  [L]
      +
      +#   second try to find it in pub/...
      +#   ...and if found stop and be happy:
      +RewriteCond         /your/docroot/dir2/%{REQUEST_FILENAME}  -f
      +RewriteRule  ^(.+)  /your/docroot/dir2/$1  [L]
      +
      +#   else go on for other Alias or ScriptAlias directives,
      +#   etc.
      +RewriteRule   ^(.+)  -  [PT]
      +
      + +

      + +

      +

      Set Environment Variables According To URL Parts

      +

      + +

      +
      Description: +
      +Perhaps you want to keep status information between requests and use the URL +to encode it. But you don't want to use a CGI wrapper for all pages just to +strip out this information. + +

      +

      Solution: +
      +We use a rewrite rule to strip out the status information and remember it via +an environment variable which can be later dereferenced from within XSSI or +CGI. This way a URL /foo/S=java/bar/ gets translated to +/foo/bar/ and the environment variable named STATUS is set +to the value "java". + +

      +RewriteEngine on
      +RewriteRule   ^(.*)/S=([^/]+)/(.*)    $1/$3 [E=STATUS:$2]
      +
      + +

      + +

      +

      Virtual User Hosts

      +

      + +

      +
      Description: +
      +Assume that you want to provide www.username.host.domain.com +for the homepage of username via just DNS A records to the same machine and +without any virtualhosts on this machine. + +

      +

      Solution: +
      +For HTTP/1.0 requests there is no solution, but for HTTP/1.1 requests which +contain a Host: HTTP header we can use the following ruleset to rewrite +http://www.username.host.com/anypath internally to +/home/username/anypath: + +

      +RewriteEngine on
      +RewriteCond   %{HTTP_HOST}                 ^www\.[^.]+\.host\.com$
      +RewriteRule   ^(.+)                        %{HTTP_HOST}$1          [C]
      +RewriteRule   ^www\.([^.]+)\.host\.com(.*) /home/$1$2
      +
      + +

      + +

      +

      Redirect Homedirs For Foreigners

      +

      + +

      +
      Description: +
      +We want to redirect homedir URLs to another webserver +www.somewhere.com when the requesting user does not stay in the local +domain ourdomain.com. This is sometimes used in virtual host +contexts. + +

      +

      Solution: +
      +Just a rewrite condition: + +

      +RewriteEngine on
      +RewriteCond   %{REMOTE_HOST}  !^.+\.ourdomain\.com$
      +RewriteRule   ^(/~.+)         http://www.somewhere.com/$1 [R,L]
      +
      + +

      + +

      +

      Redirect Failing URLs To Other Webserver

      +

      + +

      +
      Description: +
      +A typical FAQ about URL rewriting is how to redirect failing requests on +webserver A to webserver B. Usually this is done via ErrorDocument +CGI-scripts in Perl, but there is also a mod_rewrite solution. But notice that +this is less performant than using a ErrorDocument CGI-script! + +

      +

      Solution: +
      +The first solution has the best performance but less flexibility and is less +error safe: + +

      +RewriteEngine on
      +RewriteCond   /your/docroot/%{REQUEST_FILENAME} !-f
      +RewriteRule   ^(.+)                             http://webserverB.dom/$1
      +
      + +

      +The problem here is that this will only work for pages inside the +DocumentRoot. While you can add more Conditions (for instance to also handle +homedirs, etc.) there is better variant: + +

      +RewriteEngine on
      +RewriteCond   %{REQUEST_URI} !-U
      +RewriteRule   ^(.+)          http://webserverB.dom/$1
      +
      + +

      +This uses the URL look-ahead feature of mod_rewrite. The result is that this +will work for all types of URLs and is a safe way. But it does a performance +impact on the webserver, because for every request there is one more internal +subrequest. So, if your webserver runs on a powerful CPU, use this one. If it +is a slow machine, use the first approach or better a ErrorDocument +CGI-script. + +

      + +

      +

      Extended Redirection

      +

      + +

      +
      Description: +
      +Sometimes we need more control (concerning the character escaping mechanism) +of URLs on redirects. Usually the Apache kernels URL escape function also +escapes anchors, i.e. URLs like "url#anchor". You cannot use this directly on +redirects with mod_rewrite because the uri_escape() function of Apache would +also escape the hash character. How can we redirect to such a URL? + +

      +

      Solution: +
      +We have to use a kludge by the use of a NPH-CGI script which does the redirect +itself. Because here no escaping is done (NPH=non-parseable headers). First +we introduce a new URL scheme xredirect: by the following per-server +config-line (should be one of the last rewrite rules): + +

      +RewriteRule ^xredirect:(.+) /path/to/nph-xredirect.cgi/$1 \
      +            [T=application/x-httpd-cgi,L]
      +
      + +

      +This forces all URLs prefixed with xredirect: to be piped through the +nph-xredirect.cgi program. And this program just looks like: + +

      +
      +#!/path/to/perl
      +##
      +##  nph-xredirect.cgi -- NPH/CGI script for extended redirects
      +##  Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved. 
      +##
      +
      +$| = 1;
      +$url = $ENV{'PATH_INFO'};
      +
      +print "HTTP/1.0 302 Moved Temporarily\n";
      +print "Server: $ENV{'SERVER_SOFTWARE'}\n";
      +print "Location: $url\n";
      +print "Content-type: text/html\n";
      +print "\n";
      +print "<html>\n";
      +print "<head>\n";
      +print "<title>302 Moved Temporarily (EXTENDED)</title>\n";
      +print "</head>\n";
      +print "<body>\n";
      +print "<h1>Moved Temporarily (EXTENDED)</h1>\n";
      +print "The document has moved <a href=\"$url\">here</a>.<p>\n";
      +print "</body>\n";
      +print "</html>\n";
      +
      +##EOF##
      +
      +
      + +

      +This provides you with the functionality to do redirects to all URL schemes, +i.e. including the one which are not directly accepted by mod_rewrite. For +instance you can now also redirect to news:newsgroup via + +

      +RewriteRule ^anyurl  xredirect:news:newsgroup
      +
      + +

      +Notice: You have not to put [R] or [R,L] to the above rule because the +xredirect: need to be expanded later by our special "pipe through" +rule above. + +

      + +

      +

      Archive Access Multiplexer

      +

      + +

      +
      Description: +
      +Do you know the great CPAN (Comprehensive Perl Archive Network) under http://www.perl.com/CPAN? This does a +redirect to one of several FTP servers around the world which carry a CPAN +mirror and is approximately near the location of the requesting client. +Actually this can be called an FTP access multiplexing service. While CPAN +runs via CGI scripts, how can a similar approach implemented via mod_rewrite? + +

      +

      Solution: +
      +First we notice that from version 3.0.0 mod_rewrite can also use the "ftp:" +scheme on redirects. And second, the location approximation can be done by a +rewritemap over the top-level domain of the client. With a tricky chained +ruleset we can use this top-level domain as a key to our multiplexing map. + +

      +RewriteEngine on
      +RewriteMap    multiplex                txt:/path/to/map.cxan
      +RewriteRule   ^/CxAN/(.*)              %{REMOTE_HOST}::$1                 [C]
      +RewriteRule   ^.+\.([a-zA-Z]+)::(.*)$  ${multiplex:$1|ftp.default.dom}$2  [R,L]
      +
      + +

      +##
      +##  map.cxan -- Multiplexing Map for CxAN
      +##
      +
      +de        ftp://ftp.cxan.de/CxAN/
      +uk        ftp://ftp.cxan.uk/CxAN/
      +com       ftp://ftp.cxan.com/CxAN/
      + :
      +##EOF##
      +
      + +

      + +

      +

      Time-Dependend Rewriting

      +

      + +

      +
      Description: +
      +When tricks like time-dependend content should happen a lot of webmasters +still use CGI scripts which do for instance redirects to specialized pages. +How can it be done via mod_rewrite? + +

      +

      Solution: +
      +There are a lot of variables named TIME_xxx for rewrite conditions. +In conjunction with the special lexicographic comparison patterns <STRING, +>STRING and =STRING we can do time-dependend redirects: + +

      +RewriteEngine on
      +RewriteCond   %{TIME_HOUR}%{TIME_MIN} >0700
      +RewriteCond   %{TIME_HOUR}%{TIME_MIN} <1900
      +RewriteRule   ^foo\.html$             foo.day.html
      +RewriteRule   ^foo\.html$             foo.night.html
      +
      + +

      +This provides the content of foo.day.html under the URL +foo.html from 07:00-19:00 and at the remaining time the contents of +foo.night.html. Just a nice feature for a homepage... + +

      + +

      +

      Backward Compatibility for YYYY to XXXX migration

      +

      + +

      +
      Description: +
      +How can we make URLs backward compatible (still existing virtually) after +migrating document.YYYY to document.XXXX, e.g. after translating a bunch of +.html files to .phtml? + +

      +

      Solution: +
      +We just rewrite the name to its basename and test for existence of the new +extension. If it exists, we take that name, else we rewrite the URL to its +original state. + +

      +#   backward compatibility ruleset for 
      +#   rewriting document.html to document.phtml
      +#   when and only when document.phtml exists
      +#   but no longer document.html
      +RewriteEngine on
      +RewriteBase   /~quux/
      +#   parse out basename, but remember the fact
      +RewriteRule   ^(.*)\.html$              $1      [C,E=WasHTML:yes]
      +#   rewrite to document.phtml if exists
      +RewriteCond   %{REQUEST_FILENAME}.phtml -f
      +RewriteRule   ^(.*)$ $1.phtml                   [S=1]
      +#   else reverse the previous basename cutout
      +RewriteCond   %{ENV:WasHTML}            ^yes$
      +RewriteRule   ^(.*)$ $1.html
      +
      + +

      + +

      Content Handling

      + +

      +

      From Old to New (intern)

      +

      + +

      +
      Description: +
      +Assume we have recently renamed the page bar.html to +foo.html and now want to provide the old URL for backward +compatibility. Actually we want that users of the old URL even not recognize +that the pages was renamed. + +

      +

      Solution: +
      +We rewrite the old URL to the new one internally via the following rule: + +

      +RewriteEngine  on
      +RewriteBase    /~quux/
      +RewriteRule    ^foo\.html$  bar.html
      +
      + +

      + +

      +

      From Old to New (extern)

      +

      + +

      +
      Description: +
      +Assume again that we have recently renamed the page bar.html to +foo.html and now want to provide the old URL for backward +compatibility. But this time we want that the users of the old URL get hinted +to the new one, i.e. their browsers Location field should change, too. + +

      +

      Solution: +
      +We force a HTTP redirect to the new URL which leads to a change of the +browsers and thus the users view: + +

      +RewriteEngine  on
      +RewriteBase    /~quux/
      +RewriteRule    ^foo\.html$  bar.html  [R]
      +
      + +

      + +

      +

      Browser Dependend Content

      +

      + +

      +
      Description: +
      +At least for important top-level pages it is sometimes necesarry to provide +the optimum of browser dependend content, i.e. one has to provide a maximum +version for the latest Netscape variants, a minimum version for the Lynx +browsers and a average feature version for all others. + +

      +

      Solution: +
      +We cannot use content negotiation because the browsers do not provide their +type in that form. Instead we have to act on the HTTP header "User-Agent". +The following condig does the following: If the HTTP header "User-Agent" +begins with "Mozilla/3", the page foo.html is rewritten to +foo.NS.html and and the rewriting stops. If the browser is "Lynx" or +"Mozilla" of version 1 or 2 the URL becomes foo.20.html. All other +browsers receive page foo.32.html. This is done by the following +ruleset: + +

      +RewriteCond %{HTTP_USER_AGENT}  ^Mozilla/3.*
      +RewriteRule ^foo\.html$         foo.NS.html          [L]
      +
      +RewriteCond %{HTTP_USER_AGENT}  ^Lynx/.*         [OR]
      +RewriteCond %{HTTP_USER_AGENT}  ^Mozilla/[12].*
      +RewriteRule ^foo\.html$         foo.20.html          [L]
      +
      +RewriteRule ^foo\.html$         foo.32.html          [L]
      +
      + +

      + +

      +

      Dynamic Mirror

      +

      + +

      +
      Description: +
      +Assume there are nice webpages on remote hosts we want to bring into our +namespace. For FTP servers we would use the mirror program which +actually maintains an explicit up-to-date copy of the remote data on the local +machine. For a webserver we could use the program webcopy which acts +similar via HTTP. But both techniques have one major drawback: The local copy +is always just as up-to-date as often we run the program. It would be much +better if the mirror is not a static one we have to establish explicitly. +Instead we want a dynamic mirror with data which gets updated automatically +when there is need (updated data on the remote host). + +

      +

      Solution: +
      +To provide this feature we map the remote webpage or even the complete remote +webarea to our namespace by the use of the Proxy Throughput feature +(flag [P]): + +

      +RewriteEngine  on
      +RewriteBase    /~quux/
      +RewriteRule    ^hotsheet/(.*)$  http://www.tstimpreso.com/hotsheet/$1  [P]
      +
      + +

      +RewriteEngine  on
      +RewriteBase    /~quux/
      +RewriteRule    ^usa-news\.html$   http://www.quux-corp.com/news/index.html  [P]
      +
      + +

      + +

      +

      Reverse Dynamic Mirror

      +

      + +

      +
      Description: +
      +... + +

      +

      Solution: +
      + +

      +RewriteEngine on
      +RewriteCond   /mirror/of/remotesite/$1           -U 
      +RewriteRule   ^http://www\.remotesite\.com/(.*)$ /mirror/of/remotesite/$1
      +
      + +

      + +

      +

      Retrieve Missing Data from Intranet

      +

      + +

      +
      Description: +
      +This is a tricky way of virtually running a corporates (external) Internet +webserver (www.quux-corp.dom), while actually keeping and maintaining +its data on a (internal) Intranet webserver +(www2.quux-corp.dom) which is protected by a firewall. The +trick is that on the external webserver we retrieve the requested data +on-the-fly from the internal one. + +

      +

      Solution: +
      +First, we have to make sure that our firewall still protects the internal +webserver and that only the external webserver is allowed to retrieve data +from it. For a packet-filtering firewall we could for instance configure a +firewall ruleset like the following: + +

      +ALLOW Host www.quux-corp.dom Port >1024 --> Host www2.quux-corp.dom Port 80  
      +DENY  Host *                 Port *     --> Host www2.quux-corp.dom Port 80
      +
      + +

      +Just adjust it to your actual configuration syntax. Now we can establish the +mod_rewrite rules which request the missing data in the background through the +proxy throughput feature: + +

      +RewriteRule ^/~([^/]+)/?(.*)          /home/$1/.www/$2
      +RewriteCond %{REQUEST_FILENAME}       !-f
      +RewriteCond %{REQUEST_FILENAME}       !-d
      +RewriteRule ^/home/([^/]+)/.www/?(.*) http://www2.quux-corp.dom/~$1/pub/$2 [P]
      +
      + +

      + +

      +

      Load Balancing

      +

      + +

      +
      Description: +
      +Suppose we want to load balance the traffic to www.foo.com over +www[0-5].foo.com (a total of 6 servers). How can this be done? + +

      +

      Solution: +
      +There are a lot of possible solutions for this problem. We will discuss first +a commonly known DNS-based variant and then the special one with mod_rewrite: + +
        +
      1. DNS Round-Robin + +

        +The simplest method for load-balancing is to use the DNS round-robin feature +of BIND. Here you just configure www[0-9].foo.com as usual in your +DNS with A(address) records, e.g. + +

        +www0   IN  A       1.2.3.1
        +www1   IN  A       1.2.3.2
        +www2   IN  A       1.2.3.3
        +www3   IN  A       1.2.3.4
        +www4   IN  A       1.2.3.5
        +www5   IN  A       1.2.3.6
        +
        + +

        +Then you additionally add the following entry: + +

        +www    IN  CNAME   www0.foo.com.
        +       IN  CNAME   www1.foo.com.
        +       IN  CNAME   www2.foo.com.
        +       IN  CNAME   www3.foo.com.
        +       IN  CNAME   www4.foo.com.
        +       IN  CNAME   www5.foo.com.
        +       IN  CNAME   www6.foo.com.
        +
        + +

        +Notice that this seems wrong, but is actually an intended feature of BIND and +can be used in this way. However, now when www.foo.com gets resolved, +BIND gives out www0-www6 - but in a slightly permutated/rotated order +every time. This way the clients are spread over the various servers. + +But notice that this not a perfect load balancing scheme, because DNS resolve +information gets cached by the other nameservers on the net, so once a client +has resolved www.foo.com to a particular wwwN.foo.com, all +subsequent requests also go to this particular name wwwN.foo.com. But +the final result is ok, because the total sum of the requests are really +spread over the various webservers. + +

        +

      2. DNS Load-Balancing + +

        +A sophisticated DNS-based method for load-balancing is to use the program +lbnamed which can be found at http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.html. +It is a Perl 5 program in conjunction with auxilliary tools which provides a +real load-balancing for DNS. + +

        +

      3. Proxy Throughput Round-Robin + +

        +In this variant we use mod_rewrite and its proxy throughput feature. First we +dedicate www0.foo.com to be actually www.foo.com by using a +single + +

        +www    IN  CNAME   www0.foo.com.
        +
        + +

        +entry in the DNS. Then we convert www0.foo.com to a proxy-only +server, i.e. we configure this machine so all arriving URLs are just pushed +through the internal proxy to one of the 5 other servers (www1-www5). +To accomplish this we first establish a ruleset which contacts a load +balancing script lb.pl for all URLs. + +

        +RewriteEngine on
        +RewriteMap    lb      prg:/path/to/lb.pl
        +RewriteRule   ^/(.+)$ ${lb:$1}           [P,L]
        +
        + +

        +Then we write lb.pl: + +

        +#!/path/to/perl
        +##
        +##  lb.pl -- load balancing script
        +##
        +
        +$| = 1;
        +
        +$name   = "www";     # the hostname base
        +$first  = 1;         # the first server (not 0 here, because 0 is myself) 
        +$last   = 5;         # the last server in the round-robin
        +$domain = "foo.dom"; # the domainname
        +
        +$cnt = 0;
        +while (<STDIN>) {
        +    $cnt = (($cnt+1) % ($last+1-$first));
        +    $server = sprintf("%s%d.%s", $name, $cnt+$first, $domain);
        +    print "http://$server/$_";
        +}
        +
        +##EOF##
        +
        + +

        +A last notice: Why is this useful? Seems like www0.foo.com still is +overloaded? The answer is yes, it is overloaded, but with plain proxy +throughput requests, only! All SSI, CGI, ePerl, etc. processing is completely +done on the other machines. This is the essential point. + +

        +

      4. Hardware/TCP Round-Robin + +

        +There is a hardware solution available, too. Cisco has a beast called +LocalDirector which does a load balancing at the TCP/IP level. Actually this +is some sort of a circuit level gateway in front of a webcluster. If you have +enough money and really need a solution with high performance, use this one. + +

      + +
      + +

      +

      Reverse Proxy

      +

      + +

      +
      Description: +
      +... + +

      +

      Solution: +
      + +

      +##
      +##  apache-rproxy.conf -- Apache configuration for Reverse Proxy Usage
      +##
      +
      +#   server type
      +ServerType           standalone
      +Port                 8000
      +MinSpareServers      16
      +StartServers         16
      +MaxSpareServers      16
      +MaxClients           16
      +MaxRequestsPerChild  100
      +
      +#   server operation parameters
      +KeepAlive            on
      +MaxKeepAliveRequests 100
      +KeepAliveTimeout     15
      +Timeout              400
      +IdentityCheck        off
      +HostnameLookups      off
      +
      +#   paths to runtime files
      +PidFile              /path/to/apache-rproxy.pid
      +LockFile             /path/to/apache-rproxy.lock
      +ErrorLog             /path/to/apache-rproxy.elog
      +CustomLog            /path/to/apache-rproxy.dlog "%{%v/%T}t %h -> %{SERVER}e URL: %U"
      +
      +#   unused paths
      +ServerRoot           /tmp
      +DocumentRoot         /tmp
      +CacheRoot            /tmp
      +RewriteLog           /dev/null
      +TransferLog          /dev/null
      +TypesConfig          /dev/null
      +AccessConfig         /dev/null
      +ResourceConfig       /dev/null
      +
      +#   speed up and secure processing
      +<Directory />
      +Options -FollowSymLinks -SymLinksIfOwnerMatch
      +AllowOverwrite None
      +</Directory>
      +
      +#   the status page for monitoring the reverse proxy
      +<Location /rproxy-status>
      +SetHandler server-status
      +</Location>
      +
      +#   enable the URL rewriting engine
      +RewriteEngine        on
      +RewriteLogLevel      0
      +
      +#   define a rewriting map with value-lists where
      +#   mod_rewrite randomly chooses a particular value
      +RewriteMap     server  rnd:/path/to/apache-rproxy.conf-servers
      +
      +#   make sure the status page is handled locally
      +#   and make sure no one uses our proxy except ourself
      +RewriteRule    ^/apache-rproxy-status.*  -  [L]
      +RewriteRule    ^(http|ftp)://.*          -  [F]
      +
      +#   now choose the possible servers for particular URL types
      +RewriteRule    ^/(.*\.(cgi|shtml))$  to://${server:dynamic}/$1  [S=1]
      +RewriteRule    ^/(.*)$               to://${server:static}/$1  
      +
      +#   and delegate the generated URL by passing it 
      +#   through the proxy module
      +RewriteRule    ^to://([^/]+)/(.*)    http://$1/$2   [E=SERVER:$1,P,L]
      +
      +#   and make really sure all other stuff is forbidden 
      +#   when it should survive the above rules...
      +RewriteRule    .*                    -              [F]
      +
      +#   enable the Proxy module without caching
      +ProxyRequests        on
      +NoCache              *
      +
      +#   setup URL reverse mapping for redirect reponses
      +ProxyPassReverse  /  http://www1.foo.dom/
      +ProxyPassReverse  /  http://www2.foo.dom/
      +ProxyPassReverse  /  http://www3.foo.dom/
      +ProxyPassReverse  /  http://www4.foo.dom/
      +ProxyPassReverse  /  http://www5.foo.dom/
      +ProxyPassReverse  /  http://www6.foo.dom/
      +
      + +

      +##
      +##  apache-rproxy.conf-servers -- Apache/mod_rewrite selection table
      +##
      +
      +#   list of backend servers which serve static
      +#   pages (HTML files and Images, etc.)
      +static    www1.foo.dom|www2.foo.dom|www3.foo.dom|www4.foo.dom
      +
      +#   list of backend servers which serve dynamically 
      +#   generated page (CGI programs or mod_perl scripts)
      +dynamic   www5.foo.dom|www6.foo.dom
      +
      + +

      + +

      +

      New MIME-type, New Service

      +

      + +

      +
      Description: +
      +On the net there are a lot of nifty CGI programs. But their usage is usually +boring, so a lot of webmaster don't use them. Even Apache's Action handler +feature for MIME-types is only appropriate when the CGI programs don't need +special URLs (actually PATH_INFO and QUERY_STRINGS) as their input. + +First, let us configure a new file type with extension .scgi +(for secure CGI) which will be processed by the popular cgiwrap +program. The problem here is that for instance we use a Homogeneous URL Layout +(see above) a file inside the user homedirs has the URL +/u/user/foo/bar.scgi. But cgiwrap needs the URL in the form +/~user/foo/bar.scgi/. The following rule solves the problem: + +

      +RewriteRule ^/[uge]/([^/]+)/\.www/(.+)\.scgi(.*) ...
      +... /internal/cgi/user/cgiwrap/~$1/$2.scgi$3  [NS,T=application/x-http-cgi]
      +
      + +

      +Or assume we have some more nifty programs: +wwwlog (which displays the access.log for a URL subtree and +wwwidx (which runs Glimpse on a URL subtree). We have to +provide the URL area to these programs so they know on which area +they have to act on. But usually this ugly, because they are all the +times still requested from that areas, i.e. typically we would run +the swwidx program from within /u/user/foo/ via +hyperlink to + +

      +/internal/cgi/user/swwidx?i=/u/user/foo/
      +

      + +which is ugly. Because we have to hard-code both the location of the +area and the location of the CGI inside the hyperlink. When we have to +reorganise or area, we spend a lot of time changing the various hyperlinks. + +

      +

      Solution: +
      +The solution here is to provide a special new URL format which automatically +leads to the proper CGI invocation. We configure the following: + +

      +RewriteRule   ^/([uge])/([^/]+)(/?.*)/\*  /internal/cgi/user/wwwidx?i=/$1/$2$3/
      +RewriteRule   ^/([uge])/([^/]+)(/?.*):log /internal/cgi/user/wwwlog?f=/$1/$2$3
      +
      + +

      +Now the hyperlink to search at /u/user/foo/ reads only + +

      +href="*"
      +

      + +which internally gets automatically transformed to + +

      +/internal/cgi/user/wwwidx?i=/u/user/foo/
      +

      + +The same approach leads to an invocation for the access log CGI +program when the hyperlink :log gets used. + +

      + +

      +

      From Static to Dynamic

      +

      + +

      +
      Description: +
      +How can we transform a static page foo.html into a dynamic variant +foo.cgi in a seemless way, i.e. without notice by the browser/user. + +

      +

      Solution: +
      +We just rewrite the URL to the CGI-script and force the correct MIME-type so +it gets really run as a CGI-script. This way a request to +/~quux/foo.html internally leads to the invokation of +/~quux/foo.cgi. + +

      +RewriteEngine  on
      +RewriteBase    /~quux/
      +RewriteRule    ^foo\.html$  foo.cgi  [T=application/x-httpd-cgi]
      +
      + +

      + +

      +

      On-the-fly Content-Regeneration

      +

      + +

      +
      Description: +
      +Here comes a really esoteric feature: Dynamically generated but statically +served pages, i.e. pages should be delivered as pur static pages (read from +the filesystem and just passed through), but they have to be generated +dynamically by the webserver if missing. This way you can have CGI-generated +pages which are statically unless one (or a cronjob) removes the static +contents. Then the contents gets refreshed. + +

      +

      Solution: +
      +This is done via the following ruleset: + +

      +RewriteCond %{REQUEST_FILENAME}   !-s
      +RewriteCond ^page\.html$          page.cgi   [T=application/x-httpd-cgi,L]
      +
      + +

      +Here a request to page.html leads to a internal run of a +corresponding page.cgi if page.html is still missing or has +filesize null. The trick here is that page.cgi is a usual CGI script +which (additionally to its STDOUT) writes its output to the file +page.html. Once it was run, the server sends out the data of +page.html. When the webmaster wants to force a refresh the contents, +he just removes page.html (usually done by a cronjob). + +

      + +

      +

      Document With Autorefresh

      +

      + +

      +
      Description: +
      +Wouldn't it be nice while creating a complex webpage if the webbrowser would +automatically refresh the page every time we write a new version from within +our editor? Impossible? + +

      +

      Solution: +
      +No! We just combine the MIME multipart feature, the webserver NPH feature and +the URL manipulation power of mod_rewrite. First, we establish a new URL +feature: Adding just :refresh to any URL causes this to be refreshed +every time it gets updated on the filesystem. + +

      +RewriteRule   ^(/[uge]/[^/]+/?.*):refresh  /internal/cgi/apache/nph-refresh?f=$1
      +
      + +

      +Now when we reference the URL + +

      +/u/foo/bar/page.html:refresh
      +

      + +this leads to the internal invocation of the URL + +

      +/internal/cgi/apache/nph-refresh?f=/u/foo/bar/page.html
      +

      + +The only missing part is the NPH-CGI script. Although one would usually say +"left as an exercise to the reader" ;-) I will provide this, too. + +

      +#!/sw/bin/perl
      +##
      +##  nph-refresh -- NPH/CGI script for auto refreshing pages
      +##  Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved. 
      +##
      +$| = 1;
      +
      +#   split the QUERY_STRING variable
      +@pairs = split(/&/, $ENV{'QUERY_STRING'});
      +foreach $pair (@pairs) {
      +    ($name, $value) = split(/=/, $pair);
      +    $name =~ tr/A-Z/a-z/;
      +    $name = 'QS_' . $name;
      +    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
      +    eval "\$$name = \"$value\"";
      +}
      +$QS_s = 1 if ($QS_s eq '');
      +$QS_n = 3600 if ($QS_n eq '');
      +if ($QS_f eq '') {
      +    print "HTTP/1.0 200 OK\n";
      +    print "Content-type: text/html\n\n";
      +    print "&lt;b&gt;ERROR&lt;/b&gt;: No file given\n";
      +    exit(0);
      +}
      +if (! -f $QS_f) {
      +    print "HTTP/1.0 200 OK\n";
      +    print "Content-type: text/html\n\n";
      +    print "&lt;b&gt;ERROR&lt;/b&gt;: File $QS_f not found\n";
      +    exit(0);
      +}
      +
      +sub print_http_headers_multipart_begin {
      +    print "HTTP/1.0 200 OK\n";
      +    $bound = "ThisRandomString12345";
      +    print "Content-type: multipart/x-mixed-replace;boundary=$bound\n";
      +    &print_http_headers_multipart_next;
      +}
      +
      +sub print_http_headers_multipart_next {
      +    print "\n--$bound\n";
      +}
      +
      +sub print_http_headers_multipart_end {
      +    print "\n--$bound--\n";
      +}
      +
      +sub displayhtml {
      +    local($buffer) = @_;
      +    $len = length($buffer);
      +    print "Content-type: text/html\n";
      +    print "Content-length: $len\n\n";
      +    print $buffer;
      +}
      +
      +sub readfile {
      +    local($file) = @_;
      +    local(*FP, $size, $buffer, $bytes);
      +    ($x, $x, $x, $x, $x, $x, $x, $size) = stat($file);
      +    $size = sprintf("%d", $size);
      +    open(FP, "&lt;$file");
      +    $bytes = sysread(FP, $buffer, $size);
      +    close(FP);
      +    return $buffer;
      +}
      +
      +$buffer = &readfile($QS_f);
      +&print_http_headers_multipart_begin;
      +&displayhtml($buffer);
      +
      +sub mystat {
      +    local($file) = $_[0];
      +    local($time);
      +
      +    ($x, $x, $x, $x, $x, $x, $x, $x, $x, $mtime) = stat($file);
      +    return $mtime;
      +}
      +
      +$mtimeL = &mystat($QS_f);
      +$mtime = $mtime;
      +for ($n = 0; $n &lt; $QS_n; $n++) {
      +    while (1) {
      +        $mtime = &mystat($QS_f);
      +        if ($mtime ne $mtimeL) {
      +            $mtimeL = $mtime;
      +            sleep(2);
      +            $buffer = &readfile($QS_f);
      +            &print_http_headers_multipart_next;
      +            &displayhtml($buffer);
      +            sleep(5);
      +            $mtimeL = &mystat($QS_f);
      +            last;
      +        }
      +        sleep($QS_s);
      +    }
      +}
      +
      +&print_http_headers_multipart_end;
      +
      +exit(0);
      +
      +##EOF##
      +
      + +
      + +

      +

      Mass Virtual Hosting

      +

      + +

      +
      Description: +
      +The <VirtualHost> feature of Apache is nice and works great +when you just have a few dozens virtual hosts. But when you are an ISP and +have hundreds of virtual hosts to provide this feature is not the best choice. + +

      +

      Solution: +
      +To provide this feature we map the remote webpage or even the complete remote +webarea to our namespace by the use of the Proxy Throughput feature +(flag [P]): + +

      +##
      +##  vhost.map 
      +## 
      +www.vhost1.dom:80  /path/to/docroot/vhost1
      +www.vhost2.dom:80  /path/to/docroot/vhost2
      +     :
      +www.vhostN.dom:80  /path/to/docroot/vhostN
      +
      + +

      +##
      +##  httpd.conf
      +##
      +    :
      +#   use the canonical hostname on redirects, etc.
      +UseCanonicalName on
      +
      +    :
      +#   add the virtual host in front of the CLF-format
      +CustomLog  /path/to/access_log  "%{VHOST}e %h %l %u %t \"%r\" %>s %b"
      +    :
      +
      +#   enable the rewriting engine in the main server
      +RewriteEngine on
      +
      +#   define two maps: one for fixing the URL and one which defines
      +#   the available virtual hosts with their corresponding
      +#   DocumentRoot.
      +RewriteMap    lowercase    int:tolower
      +RewriteMap    vhost        txt:/path/to/vhost.map
      +
      +#   Now do the actual virtual host mapping
      +#   via a huge and complicated single rule:
      +#
      +#   1. make sure we don't map for common locations
      +RewriteCond   %{REQUEST_URL}  !^/commonurl1/.*
      +RewriteCond   %{REQUEST_URL}  !^/commonurl2/.*
      +    :
      +RewriteCond   %{REQUEST_URL}  !^/commonurlN/.*
      +#
      +#   2. make sure we have a Host header, because
      +#      currently our approach only supports 
      +#      virtual hosting through this header
      +RewriteCond   %{HTTP_HOST}  !^$
      +#
      +#   3. lowercase the hostname
      +RewriteCond   ${lowercase:%{HTTP_HOST}|NONE}  ^(.+)$
      +#
      +#   4. lookup this hostname in vhost.map and
      +#      remember it only when it is a path 
      +#      (and not "NONE" from above)
      +RewriteCond   ${vhost:%1}  ^(/.*)$
      +#
      +#   5. finally we can map the URL to its docroot location 
      +#      and remember the virtual host for logging puposes
      +RewriteRule   ^/(.*)$   %1/$1  [E=VHOST:${lowercase:%{HTTP_HOST}}]
      +    : 
      +
      + +

      + +

      Access Restriction

      + +

      +

      Blocking of Robots

      +

      + +

      +
      Description: +
      +How can we block a really annoying robot from retrieving pages of a specific +webarea? A /robots.txt file containing entries of the "Robot +Exclusion Protocol" is typically not enough to get rid of such a robot. + +

      +

      Solution: +
      +We use a ruleset which forbids the URLs of the webarea +/~quux/foo/arc/ (perhaps a very deep directory indexed area where the +robot traversal would create big server load). We have to make sure that we +forbid access only to the particular robot, i.e. just forbidding the host +where the robot runs is not enough. This would block users from this host, +too. We accomplish this by also matching the User-Agent HTTP header +information. + +

      +RewriteCond %{HTTP_USER_AGENT}   ^NameOfBadRobot.*      
      +RewriteCond %{REMOTE_ADDR}       ^123\.45\.67\.[8-9]$
      +RewriteRule ^/~quux/foo/arc/.+   -   [F]
      +
      + +

      + +

      +

      Blocked Inline-Images

      +

      + +

      +
      Description: +
      +Assume we have under http://www.quux-corp.de/~quux/ some pages with inlined +GIF graphics. These graphics are nice, so others directly incorporate them via +hyperlinks to their pages. We don't like this practice because it adds useless +traffic to our server. + +

      +

      Solution: +
      +While we cannot 100% protect the images from inclusion, we +can at least restrict the cases where the browser sends +a HTTP Referer header. + +

      +RewriteCond %{HTTP_REFERER} !^$                                  
      +RewriteCond %{HTTP_REFERER} !^http://www.quux-corp.de/~quux/.*$ [NC]
      +RewriteRule .*\.gif$        -                                    [F]
      +
      + +

      +RewriteCond %{HTTP_REFERER}         !^$                                  
      +RewriteCond %{HTTP_REFERER}         !.*/foo-with-gif\.html$
      +RewriteRule ^inlined-in-foo\.gif$   -                        [F]
      +
      + +

      + +

      +

      Host Deny

      +

      + +

      +
      Description: +
      +How can we forbid a list of externally configured hosts from using our server? + +

      +

      Solution: +
      + +For Apache >= 1.3b6: + +

      +RewriteEngine on
      +RewriteMap    hosts-deny  txt:/path/to/hosts.deny
      +RewriteCond   ${hosts-deny:%{REMOTE_HOST}|NOT-FOUND} !=NOT-FOUND [OR]
      +RewriteCond   ${hosts-deny:%{REMOTE_ADDR}|NOT-FOUND} !=NOT-FOUND
      +RewriteRule   ^/.*  -  [F]
      +

      + +For Apache <= 1.3b6: + +

      +RewriteEngine on
      +RewriteMap    hosts-deny  txt:/path/to/hosts.deny
      +RewriteRule   ^/(.*)$ ${hosts-deny:%{REMOTE_HOST}|NOT-FOUND}/$1
      +RewriteRule   !^NOT-FOUND/.* - [F]
      +RewriteRule   ^NOT-FOUND/(.*)$ ${hosts-deny:%{REMOTE_ADDR}|NOT-FOUND}/$1 
      +RewriteRule   !^NOT-FOUND/.* - [F]
      +RewriteRule   ^NOT-FOUND/(.*)$ /$1
      +
      + +

      +##
      +##  hosts.deny 
      +##
      +##  ATTENTION! This is a map, not a list, even when we treat it as such.
      +##             mod_rewrite parses it for key/value pairs, so at least a
      +##             dummy value "-" must be present for each entry.
      +##
      +
      +193.102.180.41 -
      +bsdti1.sdm.de  -
      +192.76.162.40  -
      +
      + +

      + +

      +

      Proxy Deny

      +

      + +

      +
      Description: +
      +How can we forbid a certain host or even a user of a special host from using +the Apache proxy? + +

      +

      Solution: +
      +We first have to make sure mod_rewrite is below(!) mod_proxy in the +Configuration file when compiling the Apache webserver. This way it +gets called _before_ mod_proxy. Then we configure the following for a +host-dependend deny... + +

      +RewriteCond %{REMOTE_HOST} ^badhost\.mydomain\.com$ 
      +RewriteRule !^http://[^/.]\.mydomain.com.*  - [F]
      +
      + +

      ...and this one for a user@host-dependend deny: + +

      +RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST}  ^badguy@badhost\.mydomain\.com$
      +RewriteRule !^http://[^/.]\.mydomain.com.*  - [F]
      +
      + +

      + +

      +

      Special Authentication Variant

      +

      + +

      +
      Description: +
      +Sometimes a very special authentication is needed, for instance a +authentication which checks for a set of explicitly configured users. Only +these should receive access and without explicit prompting (which would occur +when using the Basic Auth via mod_access). + +

      +

      Solution: +
      +We use a list of rewrite conditions to exclude all except our friends: + +

      +RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} !^friend1@client1.quux-corp\.com$ 
      +RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} !^friend2@client2.quux-corp\.com$ 
      +RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} !^friend3@client3.quux-corp\.com$ 
      +RewriteRule ^/~quux/only-for-friends/      -                                 [F]
      +
      + +

      + +

      +

      Referer-based Deflector

      +

      + +

      +
      Description: +
      +How can we program a flexible URL Deflector which acts on the "Referer" HTTP +header and can be configured with as many referring pages as we like? + +

      +

      Solution: +
      +Use the following really tricky ruleset... + +

      +RewriteMap  deflector txt:/path/to/deflector.map
      +
      +RewriteCond %{HTTP_REFERER} !=""
      +RewriteCond ${deflector:%{HTTP_REFERER}} ^-$
      +RewriteRule ^.* %{HTTP_REFERER} [R,L]
      +
      +RewriteCond %{HTTP_REFERER} !=""
      +RewriteCond ${deflector:%{HTTP_REFERER}|NOT-FOUND} !=NOT-FOUND
      +RewriteRule ^.* ${deflector:%{HTTP_REFERER}} [R,L]
      +
      + +

      ... +in conjunction with a corresponding rewrite map: + +

      +##
      +##  deflector.map
      +##
      +
      +http://www.badguys.com/bad/index.html    -
      +http://www.badguys.com/bad/index2.html   -
      +http://www.badguys.com/bad/index3.html   http://somewhere.com/
      +
      + +

      +This automatically redirects the request back to the referring page (when "-" +is used as the value in the map) or to a specific URL (when an URL is +specified in the map as the second argument). + +

      + +

      Other

      + +

      +

      External Rewriting Engine

      +

      + +

      +
      Description: +
      +A FAQ: How can we solve the FOO/BAR/QUUX/etc. problem? There seems no solution +by the use of mod_rewrite... + +

      +

      Solution: +
      +Use an external rewrite map, i.e. a program which acts like a rewrite map. It +is run once on startup of Apache receives the requested URLs on STDIN and has +to put the resulting (usually rewritten) URL on STDOUT (same order!). + +

      +RewriteEngine on
      +RewriteMap    quux-map       prg:/path/to/map.quux.pl
      +RewriteRule   ^/~quux/(.*)$  /~quux/${quux-map:$1}
      +
      + +

      +#!/path/to/perl
      +
      +#   disable buffered I/O which would lead 
      +#   to deadloops for the Apache server
      +$| = 1;
      +
      +#   read URLs one per line from stdin and
      +#   generate substitution URL on stdout
      +while (<>) {
      +    s|^foo/|bar/|;
      +    print $_;
      +}
      +
      + +

      +This is a demonstration-only example and just rewrites all URLs +/~quux/foo/... to /~quux/bar/.... Actually you can program +whatever you like. But notice that while such maps can be used also by +an average user, only the system administrator can define it. + +

      + + +
      + + diff --git a/docs/manual/mod/mod_auth_digest.html b/docs/manual/mod/mod_auth_digest.html new file mode 100644 index 00000000000..35c7f0628e1 --- /dev/null +++ b/docs/manual/mod/mod_auth_digest.html @@ -0,0 +1,416 @@ + + + +Apache module mod_auth_digest + + + + + +

      Module mod_auth_digest

      + +This module is contained in the mod_auth_digest.c file, and is +not compiled in by default. It is only available in Apache 1.3.8 and +later. It provides for user authentication using MD5 Digest +Authentication. + +

      Note this is an updated version of mod_digest. However, it has not been +extensively tested and is therefore marked experimental. If you use this +module, you must make sure to not use mod_digest (because they +share some of the same configuration directives). + + +

      +
    • AuthDigestFile +
    • AuthDigestGroupFile +
    • AuthDigestQop +
    • AuthDigestNonceLifetime +
    • AuthDigestNonceFormat +
    • AuthDigestNcCheck +
    • AuthDigestAlgorithm +
    • AuthDigestDomain +
    • Using Digest Authentication +
    • +
      + + +

      AuthDigestFile

      +Syntax: AuthDigestFile filename
      +Context: directory, .htaccess
      +Override: AuthConfig
      +Status: Base
      +Module: mod_auth_digest
      + +

      The AuthDigestFile directive sets the name of a textual file containing +the list of users and encoded passwords for digest authentication. +Filename is the absolute path to the user file. + +

      The digest file uses a special format. Files in this format can be +created using the "htdigest" utility found in the support/ subdirectory of +the Apache distribution. + +


      + +

      AuthDigestGroupFile

      +Syntax: AuthDigestGroupFile filename
      +Context: directory, .htaccess
      +Override: AuthConfig
      +Status: Base
      +Module: mod_auth_digest
      +Compatibility: Available in Apache 1.3.8 and later + +

      The AuthDigestGroupFile directive sets the name of a textual file +containing the list of groups and their members (user names). +Filename is the absolute path to the group file. + +

      Each line of the group file contains a groupname followed by a colon, +followed by the member usernames separated by spaces. Example: +

      mygroup: bob joe anne
      +Note that searching large text files is very inefficient. + +

      Security: make sure that the AuthGroupFile is stored outside the +document tree of the web-server; do not put it in the directory +that it protects. Otherwise, clients will be able to download the +AuthGroupFile. + +


      + +

      AuthDigestQop

      +Syntax: AuthDigestQop none | 1*{ auth | auth-int }
      +Default: AuthDigestQop auth
      +Context: directory, .htaccess
      +Override: AuthConfig
      +Status: Base
      +Module: mod_auth_digest
      +Compatibility: Available in Apache 1.3.8 and later + +

      The AuthDigestQop directive determines the quality-of-protection to use. +auth will only do authentication (username/password); +auth-int is authentication plus integrity checking (an MD5 hash +of the entity is also computed and checked); none will cause the +module to use the old RFC-2069 digest algorithm (which does not include +integrity checking). Both auth and auth-int may be +specified, in which the case the browser will choose which of these to +use. none should only be used if the browser for some reason +does not like the challenge it receives otherwise. + +

      auth-int is not implemented yet. + +


      + +

      AuthDigestNonceLifetime

      +Syntax: AuthDigestNonceLifetime <time>
      +Default: AuthDigestNonceLifetime 300
      +Context: directory, .htaccess
      +Override: AuthConfig
      +Status: Base
      +Module: mod_auth_digest
      +Compatibility: Available in Apache 1.3.8 and later + +

      The AuthDigestNonceLifetime directive controls how long the server +nonce is valid. When the client contacts the server using an expired +nonce the server will send back a 401 with stale=true. If +<time> is greater than 0 then it specifies the number of +seconds the nonce is valid; this should probably never be set to less +than 10 seconds. If <time> is less than 0 then the nonce +never expires. + + + +


      +

      AuthDigestNonceFormat

      +Syntax: AuthDigestNonceFormat ???
      +Default: AuthDigestNonceFormat ???
      +Context: directory, .htaccess
      +Override: AuthConfig
      +Status: Base
      +Module: mod_auth_digest
      +Compatibility: Available in Apache 1.3.8 and later + +

      Not implemented yet. + + +


      +

      AuthDigestNcCheck

      +Syntax: AuthDigestNcCheck On/Off
      +Default: AuthDigestNcCheck Off
      +Context: server config
      +Override: Not applicable
      +Status: Base
      +Module: mod_auth_digest
      +Compatibility: Available in Apache 1.3.8 and later + +

      Not implemented yet. + + +


      +

      AuthDigestAlgorithm

      +Syntax: AuthDigestAlgorithm MD5 | MD5-sess
      +Default: AuthDigestAlgorithm MD5
      +Context: directory, .htaccess
      +Override: AuthConfig
      +Status: Base
      +Module: mod_auth_digest
      +Compatibility: Available in Apache 1.3.8 and later + +

      The AuthDigestAlgorithm directive selects the algorithm used to calculate +the challenge and response hashes. + +

      MD5-sess is not correctly implemented yet. + + +


      +

      AuthDigestDomain

      +Syntax: AuthDigestDomain URI URI ...
      +Context: directory, .htaccess
      +Override: AuthConfig
      +Status: Base
      +Module: mod_auth_digest
      +Compatibility: Available in Apache 1.3.8 and later + +

      The AuthDigestDomain directive allows you to specify one or more URIs +which are in the same protection space (i.e. use the same realm and +username/password info). The specified URIs are prefixes, i.e. the client +will assume that all URIs "below" these are also protected by the same +username/password. The URIs may be either absolute URIs (i.e. inluding a +scheme, host, port, etc) or relative URIs. + +

      This directive should always be specified and contain at least +the (set of) root URI(s) for this space. Omiting to do so will cause the +client to send the Authorization header for every request sent to +this server. Apart from increasing the size of the request, it may also +have a detrimental effect on performance if "AuthDigestNcCheck" is on. + +

      The URIs specified can also point to different servers, in which case +clients (which understand this) will then share username/password info +across multiple servers without prompting the user each time. + + +


      + +

      Using Digest Authentication

      + +

      Using MD5 Digest authentication is very simple. Simply set up +authentication normally, using "AuthType Digest" and "AuthDigestFile" +instead of the normal "AuthType Basic" and "AuthUserFile"; also, +replace any "AuthGroupFile" with "AuthDigestGroupFile". Then add a +"AuthDigestDomain" directive containing at least the root URI(s) for +this protection space. Example: + +

      +  <Location /private/>
      +  AuthType Digest
      +  AuthName "private area"
      +  AuthDigestDomain /private/ http://mirror.my.dom/private2/
      +  AuthDigestFile /web/auth/.digest_pw
      +  require valid-user
      +  </Location>
      +
      + +

      Note: MD5 authentication provides a more secure +password system than Basic authentication, but only works with supporting +browsers. As of this writing (July 1999), the only major browsers which +support digest authentication are Internet Exploder 5.0 and +Amaya. Therefore, we do not +recommend using this feature on a large Internet site. However, for +personal and intra-net use, where browser users can be controlled, it is +ideal. + + + + + diff --git a/docs/manual/mod/mod_vhost_alias.html b/docs/manual/mod/mod_vhost_alias.html new file mode 100644 index 00000000000..9b86a07143b --- /dev/null +++ b/docs/manual/mod/mod_vhost_alias.html @@ -0,0 +1,311 @@ + + + +Apache module mod_vhost_alias + + + + +

      + [APACHE DOCUMENTATION] +

      + Apache HTTP Server Version 1.3 +

      +
      + +

      Module mod_vhost_alias

      + +

      +This module is contained in the mod_vhost_alias.c file +and is not compiled in by default. It should be mentioned near the +start of the Configuration file so that it doesn't +override the behaviour of other modules that do filename translation, +e.g. mod_userdir and +mod_alias. It provides +support for dynamically configured mass +virtual hosting. +

      + +

      Directory Name Interpolation

      + +

      +All the directives in this module interpolate a string into a +pathname. The interpolated string (henceforth called the "name") may +be either the server name (see the +UseCanonicalName +directive for details on how this is determined) or the IP address of +the virtual host on the server in dotted-quad format. The +interpolation is controlled by specifiers inspired by +printf which have a number of formats: +

      +
      %% +
      insert a % +
      %p +
      insert the port number of the virtual host +
      %N.M +
      insert (part of) the name +
      +

      + +

      +N and M are used to specify substrings of +the name. N selects from the dot-separated components of +the name, and M selects characters within whatever +N has selected. M is optional and defaults +to zero if it isn't present; the dot must be present if and only if +M is present. The interpretation is as follows: +

      +
      0 +
      the whole name +
      1 +
      the first part +
      2 +
      the second part +
      -1 +
      the last part +
      -2 +
      the penultimate part +
      2+ +
      the second and all subsequent parts +
      -2+ +
      the penultimate and all preceding parts +
      1+ and -1+ +
      the same as 0 +
      +If N or M is greater than the number of +parts available a single underscore is interpolated. +

      + +

      Examples

      + +

      +For simple name-based virtual hosts you might use the following +directives in your server configuration file: +

      +	UseCanonicalName	Off
      +	VirtualDocumentRoot	/usr/local/apache/vhosts/%0
      +
      +A request for http://www.example.com/directory/file.html +will be satisfied by the file +/usr/local/apache/vhosts/www.example.com/directory/file.html. +

      + +

      +For a very large number of virtual hosts it is a good idea to arrange +the files to reduce the size of the vhosts directory. To +do this you might use the following in your configuration file: +

      +	UseCanonicalName	Off
      +	VirtualDocumentRoot	/usr/local/apache/vhosts/%3+/%2.1/%2.2/%2.3/%2
      +
      +A request for http://www.example.isp.com/directory/file.html +will be satisfied by the file +/usr/local/apache/isp.com/e/x/a/example/directory/file.html. +A more even spread of files can be acheived by hashing from the end of +the name, for example: +
      +	VirtualDocumentRoot	/u/%3+/%2.-1/%2.-2/%2.-3/%2
      +
      +The example request would come from +/usr/local/apache/vhosts/isp.com/e/l/p/example/directory/file.html. +Alternatively you might use: +
      +	VirtualDocumentRoot	/usr/local/apache/vhosts/%3+/%2.1/%2.2/%2.3/%2.4+
      +
      +The example request would come from +/usr/local/apache/vhosts/isp.com/e/x/a/mple/directory/file.html. +

      + +

      +For IP-based virtual hosting you might use the following in your +configuration file: +

      +	UseCanonicalName DNS
      +	VirtualDocumentRootIP	/usr/local/apache/vhost/%1/%2/%3/%4/docs
      +	VirtualScriptAliasIP	/usr/local/apache/vhost/%1/%2/%3/%4/cgi-bin
      +
      +A request for http://www.example.isp.com/directory/file.html +would be satisfied by the file +/usr/local/apache/10/20/30/40/docs/directory/file.html if +the IP address of www.example.com were 10.20.30.40. +A request for http://www.example.isp.com/cgi-bin/script.pl +would be satisfied by executing the program +/usr/local/apache/10/20/30/40/cgi-bin/script.pl. +

      + +

      +The LogFormat directives +%V and %A are useful in conjunction with +this module. +

      + +
      + +

      Directives

      + +
      + +

      VirtualDocumentRoot directive

      +

      +Syntax: VirtualDocumentRoot interpolated-directory
      +Default: None
      +Context: server config, virtual host
      +Status: Extension
      +Module: mod_vhost_alias
      +Compatibility: VirtualDocumentRoot is only available in 1.3.5 and later.

      +

      +The VirtualDocumentRoot directive allows you to determine +where Apache will find your documents based on the value of the server +name. The result of expanding interpolated-directory is used +as the root of the document tree in a similar manner to the +DocumentRoot +directive's argument. If interpolated-directory is +none then VirtaulDocumentRoot is turned off. +This directive cannot be used in the same context as +VirtualDocumentRootIP. +

      +
      + +

      VirtualDocumentRootIP directive

      +

      +Syntax: VirtualDocumentRootIP interpolated-directory
      +Default: None
      +Context: server config, virtual host
      +Status: Extension
      +Module: mod_vhost_alias
      +Compatibility: VirtualDocumentRootIP is only available in 1.3.5 and later.

      +

      +The VirtualDocumentRootIP directive is like the +VirtualDocumentRoot directive, +except that it uses the IP address of the server end of the connection +instead of the server name. +

      +
      + +

      VirtualScriptAlias directive

      +

      +Syntax: VirtualScriptAlias interpolated-directory
      +Default: None
      +Context: server config, virtual host
      +Status: Extension
      +Module: mod_vhost_alias
      +Compatibility: VirtualScriptAlias is only available in 1.3.5 and later.

      +

      +The VirtualScriptAlias directive allows you to determine +where Apache will find CGI scripts in a similar manner to +VirtualDocumentRoot +does for other documents. It matches requests for URIs starting +/cgi-bin/, much like +ScriptAlias /cgi-bin/ +would. +

      +
      + +

      VirtualScriptAliasIP directive

      +

      +Syntax: VirtualScriptAliasIP interpolated-directory
      +Default: None
      +Context: server config, virtual host
      +Status: Extension
      +Module: mod_vhost_alias
      +Compatibility: VirtualScriptAliasIP is only available in 1.3.5 and later.

      +

      +The VirtualScriptAliasIP directibe is like the +VirtualScriptAlias directive, +except that it uses the IP address of the server end of the connection +instead of the server name. +

      +
      + +

      + Apache HTTP Server Version 1.3 +

      + +Index +Home + + + diff --git a/docs/manual/mpm.html b/docs/manual/mpm.html new file mode 100644 index 00000000000..7521751bfe2 --- /dev/null +++ b/docs/manual/mpm.html @@ -0,0 +1,96 @@ + + + +Available MPM's for Apache 2.0 + + + + +

      Apache 2.0 - Available MPM's

      +

      Dated 15th January 2000

      +

      Apache 2.0 has a new architecture that moves the processing of requests from the code server into a MultiProcessing Module (MPM). By selecting the MPM to use you can alter the way the server behaves. Additionally the introduction of the MPM's has led to platforms developing their own optimised modules. On some platforms there is no choice, whilst on others there are different options, each with differing processing models. This list aims to help you select a suitable MPM for your system.

      +
      +

      Platforms

      + +

      Unix

      + + + + + + + + + + + + + + + + + + + + + +
      MPMDescriptionMaintainer
      preforkThe prefork MPM reproduces the behaviour of Apache 1.3.??
      mpmt_pthreadThis MPM uses a multi-process, multi-threaded model to provide good scability and stability.Manoj?
      dexterThis is Manoj's plaything. It has a number of hybrid features that Manoj has been looking at to improve performance.Manoj
      + +

      Windows

      + + + + + + + + + + + +
      MPMDescriptionMaintainer
      winntThe Windows MPM. This is mainly aimed at Windows NT.Bill Stoddard
      + +

      OS/2

      + + + + + + + + + + + +
      MPMDescriptionMaintainer
      spmt_os2Single process, multiple thread MPM for OS2.Brian Havard
      + +

      BeOS

      + + + + + + + + + + + +
      MPMDescriptionMaintainer
      mpmt_beosMulti-threaded MPM for BeOS. This follows the mpmt_pthread model.David Reid
      + +
      +David Reid, 15th January 2000 + + + diff --git a/docs/manual/mpm.html.en b/docs/manual/mpm.html.en new file mode 100644 index 00000000000..7521751bfe2 --- /dev/null +++ b/docs/manual/mpm.html.en @@ -0,0 +1,96 @@ + + + +Available MPM's for Apache 2.0 + + + + +

      Apache 2.0 - Available MPM's

      +

      Dated 15th January 2000

      +

      Apache 2.0 has a new architecture that moves the processing of requests from the code server into a MultiProcessing Module (MPM). By selecting the MPM to use you can alter the way the server behaves. Additionally the introduction of the MPM's has led to platforms developing their own optimised modules. On some platforms there is no choice, whilst on others there are different options, each with differing processing models. This list aims to help you select a suitable MPM for your system.

      +
      +

      Platforms

      +
      +

      Unix

      + + + + + + + + + + + + + + + + + + + + + +
      MPMDescriptionMaintainer
      preforkThe prefork MPM reproduces the behaviour of Apache 1.3.??
      mpmt_pthreadThis MPM uses a multi-process, multi-threaded model to provide good scability and stability.Manoj?
      dexterThis is Manoj's plaything. It has a number of hybrid features that Manoj has been looking at to improve performance.Manoj
      + +

      Windows

      + + + + + + + + + + + +
      MPMDescriptionMaintainer
      winntThe Windows MPM. This is mainly aimed at Windows NT.Bill Stoddard
      + +

      OS/2

      + + + + + + + + + + + +
      MPMDescriptionMaintainer
      spmt_os2Single process, multiple thread MPM for OS2.Brian Havard
      + +

      BeOS

      + + + + + + + + + + + +
      MPMDescriptionMaintainer
      mpmt_beosMulti-threaded MPM for BeOS. This follows the mpmt_pthread model.David Reid
      + +
      +David Reid, 15th January 2000 + + + diff --git a/docs/manual/new_features_2_0.html b/docs/manual/new_features_2_0.html new file mode 100644 index 00000000000..325696f453c --- /dev/null +++ b/docs/manual/new_features_2_0.html @@ -0,0 +1,50 @@ + + +New features with Apache 2.0 + + + + + +

      Overview of New Features in Apache 2.0

      + +

      Enhancements: Core | + +


      + +

      Core Enhancements:

      + +
      +
      Unix Threading +
      On Unix systems with POSIX threads support, Apache can now run in a + hybrid multiprocess, multithreaded mode. This should improve + scalability. + +
      New Build System +
      The build system has been rewritten from scratch to be based on + autoconf and libtool. This makes Apache's configuration system more + similar to that of other packages. + +
      Multiprotocol Support +
      Apache now has some of the infrastructure in place to support serving + multiple protocols. mod_echo has been written as an example. + +
      Better support for non-Unix platforms +
      Apache 2.0 should be faster and more stable on non-Unix platforms such + as BeOS, OS/2, and Windows. With the introduction of platform-specific + multiprocessing modules and the Apache Portable Runtime, these + platforms can now avoid using POSIX-emulation functions that are + sometimes buggy and always hurt performance. + +
      New API +
      The API for modules has changed significantly for 2.0. Many of the + module-ordering problems from 1.3 should be gone, since 2.0 does much + of this automatically now. Also, new calls have been added that should + allow modules to do more without requiring patching of the core Apache + server. + + + + + diff --git a/docs/manual/new_features_2_0.html.en b/docs/manual/new_features_2_0.html.en new file mode 100644 index 00000000000..325696f453c --- /dev/null +++ b/docs/manual/new_features_2_0.html.en @@ -0,0 +1,50 @@ + + +New features with Apache 2.0 + + + + + +

      Overview of New Features in Apache 2.0

      + +

      Enhancements: Core | + +


      + +

      Core Enhancements:

      + +
      +
      Unix Threading +
      On Unix systems with POSIX threads support, Apache can now run in a + hybrid multiprocess, multithreaded mode. This should improve + scalability. + +
      New Build System +
      The build system has been rewritten from scratch to be based on + autoconf and libtool. This makes Apache's configuration system more + similar to that of other packages. + +
      Multiprotocol Support +
      Apache now has some of the infrastructure in place to support serving + multiple protocols. mod_echo has been written as an example. + +
      Better support for non-Unix platforms +
      Apache 2.0 should be faster and more stable on non-Unix platforms such + as BeOS, OS/2, and Windows. With the introduction of platform-specific + multiprocessing modules and the Apache Portable Runtime, these + platforms can now avoid using POSIX-emulation functions that are + sometimes buggy and always hurt performance. + +
      New API +
      The API for modules has changed significantly for 2.0. Many of the + module-ordering problems from 1.3 should be gone, since 2.0 does much + of this automatically now. Also, new calls have been added that should + allow modules to do more without requiring patching of the core Apache + server. + + + + + diff --git a/docs/manual/platform/netware.html b/docs/manual/platform/netware.html new file mode 100644 index 00000000000..8add778b5f1 --- /dev/null +++ b/docs/manual/platform/netware.html @@ -0,0 +1,278 @@ + + + +Using Apache with Novell NetWare 5 + + + + + + +

      Using Apache With Novell NetWare 5

      + +

      This document explains how to install, configure and run + Apache 1.3 under Novell NetWare 5. Please note that at + this time, NetWare support is entirely experimental, and is + recommended only for experienced users. The Apache Group does not + guarantee that this software will work as documented, or even at + all. If you find any bugs, or wish to contribute in other ways, please + use our bug reporting + page.

      + +

      Warning: Apache on NetWare has not yet been optimized for performance. +Apache still performs best, and is most reliable on Unix platforms. Over +time we will improve NetWare performance. Folks doing comparative reviews +of webserver performance are asked to compare against Apache +on a Unix platform such as Solaris, FreeBSD, or Linux.

      + +

      + +Most of this document assumes that you are installing Apache from a +binary distribution. If you want to compile Apache yourself (possibly +to help with development, or to track down bugs), see the section on +Compiling Apache for NetWare below. + +


      + + + +
      + +

      Requirements

      + +Apache 1.3 is designed to run on NetWare 5.0 and 5.1. +

      + +If running on NetWare 5.0 you must install Service Pack 4. + +

      +

      + +Service pack 4 is available here. + +

      Downloading Apache for NetWare 5

      + +

      Information on the latest version of Apache can be found on the +Apache web server at http://www.apache.org/. This will +list the current release, any more recent alpha or beta-test releases, +together with details of mirror web and anonymous ftp sites.

      + +

      +

      Installing Apache for NetWare

      + +There is no Apache install program for NetWare currently. You will need +to compile apache and copy the files over to the server manually. An +install program will be posted at a later date. +

      +Follow these steps to install Apache on NetWare (assuming you will install to sys:/apache): + +

        +
      • Create a directory called Apache on a NetWare volume +
      • Copy Apache.nlm, Apachec.nlm, htdigest.nlm, and htpasswd.nlm to sys:/apache +
      • Create a directory under sys:/Apache called conf +
      • Copy all the *.conf-dist-nw files to the sys:/apache/conf directory + and rename them all as *.conf files +
      • Copy the mime.types and magic files to sys:/apache/conf directory +
      • Copy all files and subdirectories of \apache-1.3\htdocs to sys:/apache/htdocs and + rename the proper index file (index.html.en) to index.html +
      • Copy all files and subdirectories in \apache-1.3\icons to sys:/apache/icons +
      • Create the directory sys:/apache/logs on the server +
      • Create the directory sys:/apache/cgi-bin on the server +
      • Create the directory sys:/apache/modules and copy all nlm modules built into the + modules directory +
      • Edit the httpd.conf file setting ServerRoot and ServerName to reflect your correct + server settings +
      • Add sys:/apache to the search path. EXAMPLE: search add sys:\apache +
      + +

      Apache may be installed to other volumes besides the default sys volume. + +

      Running Apache for NetWare

      + +To start Apache just type apache at the console. This +will load apache in the OS address space. If you prefer to load +Apache in a protected address space you may specify the address space +with the load statement as follows: +

      +

      +    load address space = apache apache
      +
      +

      +This will load Apache into an address space called apache. Running multiple instances +of Apache concurrently on NetWare is possible by loading each instance into its own +protected address space. + +

      +After starting Apache it will be listening to port 80 (unless you +changed the Port, Listen or +BindAddress directives in the configuration files). +To connect to the server and access the default page, launch a browser +and enter the server's name or address. + +This should respond with a welcome page, and a link to the Apache +manual. If nothing happens or you get an error, look in the +error_log file in the logs directory. + +

      + +Once your basic installation is working, you should configure it +properly by editing the files in the conf directory. + +

      +To unload Apache running in the OS address space just type the +following at the console: +

      +    unload apache
      +
      +If apache is running in a protected address space specify the +address space in the unload statement: +
      +    unload address space = apache apache
      +
      + +

      +When working with Apache it is important to know how it will find the +configuration files. You can specify a configuration file on the command line +in two ways: + +

        +
      • -f specifies a path to a particular configuration file +
      +
          apache -f "vol:/my server/conf/my.conf"
      +
          apache -f test/test.conf
      + +In these cases, the proper ServerRoot should be set in the configuration file. + +

      + +If you don't specify a configuration file name with -f, Apache will +use the file name compiled into the server, usually "conf/httpd.conf". Invoking +Apache with the -V switch will display this value labeled as SERVER_CONFIG_FILE. +Apache will then determine its ServerRoot by trying the following, in this order: + +

        +
      • A ServerRoot directive via a -C switch. +
      • The -d switch on the command line. +
      • Current working directory +
      • The server root compiled into the server. +
      + +

      +The server root compiled into the server is usually "sys:/apache". +invoking apache with the -V switch will display this value +labeled as HTTPD_ROOT. + +

      Configuring Apache for NetWare

      + +Apache is configured by files in the conf +directory. These are the same as files used to configure the Unix +version, but there are a few different directives for Apache on +NetWare. See the Apache documentation for all the +available directives. + +

      + +The main differences in Apache for NetWare are: + +

        +
      • Because Apache for NetWare is multithreaded, it does not use a + separate process for each request, as Apache does with + Unix. Instead there are only threads running: a parent thread, and + a child which handles the requests. Within the child each request is + handled by a separate thread. +

        + + So the "process"-management directives are different: +

        MaxRequestsPerChild + - Like the Unix directive, this controls how many requests a + process will serve before exiting. However, unlike Unix, a + process serves all the requests at once, not just one, so if + this is set, it is recommended that a very high number is + used. The recommended default, MaxRequestsPerChild + 0, does not cause the process to ever exit. +

        ThreadsPerChild - + This directive is new, and tells the server how many threads it + should use. This is the maximum number of connections the server + can handle at once; be sure and set this number high enough for + your site if you get a lot of hits. The recommended default is + ThreadsPerChild 50. +

        ThreadStackSize - + This directive tells the server what size of stack to use for + the individual threads. The recommended default is + ThreadStackSize 65536. +

        +

      • The directives that accept filenames as arguments now must use + NetWare filenames instead of Unix ones. However, because Apache + uses Unix-style names internally, you must use forward slashes, not + backslashes. Volumes can be used; if omitted, the drive with the + Apache executable will be assumed.

        +
      • Apache for NetWare contains the ability to load modules at runtime, + without recompiling the server. If Apache is compiled normally, it + will install a number of optional modules in the + \Apache\modules directory. To activate these, or other + modules, the new LoadModule + directive must be used. For example, to active the status module, + use the following (in addition to the status-activating directives + in access.conf):

        +
        +    LoadModule status_module modules/status
        +
        +

        Information on creating loadable + modules is also available.

        +
      + +

      Compiling Apache for NetWare

      + +

      Compiling Apache requires MetroWerks CodeWarrior 5 to be properly + installed. + +

      First, unpack the Apache distribution into an appropriate + directory. Then go to the src subdirectory of the Apache + distribution and unzip ApacheNW.mcp.gz. You may use a + recent version of WinZip to accomplish this or gzip for Windows. The main + Metrowerks project file for Apache (ApacheNW.mcp) is now ready + to use. Just double click on it from within explorer and it should + automatically launch MetroWerks CodeWarrior. +

      + +

      All major pieces of Apache may be built using the ApacheNW.mcp project + file. This includes modules such as status, info, and proxy. In addition, + the following project files have been provided as well: +

         
      +       /apache-1.3/src/support/htpasswd.mcp.gz
      +       /apache-1.3/src/support/htdigest.mcp.gz
      +
      + +

      +htpasswd.mcp.gz and htdigest.mcp.gz will also need +to be unzipped before they can be used with MetroWerks CodeWarrior. + +

      Once Apache has been compiled, it needs to be installed in its server + root directory. The default is the sys:/Apache + directory.

      +

      +Before running the server you must fill out the conf directory. +Copy the *.conf-dist-nw from the distribution conf directory +and rename *.conf. Edit the ServerRoot entries to your +actual server root (for example "sys:/apache"). Copy over +the conf/magic and conf/mime.types files as well. + + + + + diff --git a/docs/manual/platform/win_compiling.html b/docs/manual/platform/win_compiling.html new file mode 100644 index 00000000000..54970ea86d0 --- /dev/null +++ b/docs/manual/platform/win_compiling.html @@ -0,0 +1,120 @@ + + + +Compiling Apache for Microsoft Windows + + + + + + + + + +Using Apache with Microsoft Windows + + + + + + +

      Compiling Apache for Microsoft Windows

      + +

      There are many important points before you begin compiling Apache. + See Using Apache with Microsoft Windows + before you begin.

      + +

      Compiling Apache requires Microsoft Visual C++ 5.0 (or later) to be + properly installed. It is easiest to compile with the command-line tools + (nmake, etc...). Consult the VC++ manual to determine how to install + them.

      + +

      First, unpack the Apache distribution into an appropriate + directory. Open a command-line prompt, and change to the + src subdirectory of the Apache distribution.

      + +

      The master Apache makefile instructions are contained in the + Makefile.win file. To compile Apache on Windows, simply + use one of the following commands: +

        +
      • nmake /f Makefile.win _apacher (release build) +
      • nmake /f Makefile.win _apached (debug build) +
      + +

      These will both compile Apache. The latter will include debugging + information in the resulting files, making it easier to find bugs and + track down problems.

      + +

      Apache can also be compiled using VC++'s Visual Studio development + environment. Although compiling Apache in this manner is not as + simple, it makes it possible to easily modify the Apache source, or + to compile Apache if the command-line tools are not installed. + Project files (.DSP) are included for each of the + portions of Apache. To build Apache from the these projects files + you will need to build the following projects in this order: + This needs updating for Apache 2.0 +

        +
      1. os\win32\ApacheOS.dsp +
      2. regex\regex.dsp +
      3. ap\ap.dsp +
      4. main\gen_uri_delims.dsp +
      5. main\gen_test_char.dsp +
      6. ApacheCore.dsp +
      7. Apache.dsp +
      + + In addition, the src\os\win32 subdirectory contains + project files for the optional modules (see below).

      + +

      Once Apache has been compiled, it needs to be installed in its server + root directory. The default is the \Apache + directory, on the current hard drive.

      + +

      To install the files into the \Apache directory + automatically, use one the following nmake commands (see above):

      +
        +
      • nmake /f Makefile.win installr INSTDIR=dir + (for release build) +
      • nmake /f Makefile.win installd INSTDIR=dir + (for debug build) +
      + +The dir argument to INSTDIR gives the installation directory; it can +be omitted if Apache is to be installed into \Apache. + +

      This will install the following:

      + +
        +
      • dir\Apache.exe - Apache executable +
      • dir\ApacheCore.dll - Main Apache shared library +
      • dir\modules\ApacheModule*.dll - Optional Apache + modules (7 files) +
      • dir\conf - Empty configuration directory +
      • dir\logs - Empty logging directory +
      + +

      If you do not have nmake, or wish to install in a different directory, + be sure to use a similar naming scheme.

      + +

      +Before running the server you must fill out the conf directory. +Copy the *.conf-dist-win from the distribution conf directory +and rename *.conf. Edit the @@ServerRoot@@ entries to your +actual server root (for example "C:\apache"). Copy over +the conf/magic and conf/mime.types files as well. + + + + diff --git a/emacs-style b/emacs-style new file mode 100644 index 00000000000..a0f16e2209d --- /dev/null +++ b/emacs-style @@ -0,0 +1,9 @@ +(add-hook 'c-mode-hook + (function (lambda () + (c-set-offset 'inclass' ++) + (c-set-offset 'defun-block-intro' ++) + (c-set-offset 'statement-block-intro' ++) + (c-set-offset 'substatement' ++) + (c-set-offset 'brace-list-intro' ++) + ))) +(setq c++-mode-hook c-mode-hook) diff --git a/include/.cvsignore b/include/.cvsignore new file mode 100644 index 00000000000..49eae948bd5 --- /dev/null +++ b/include/.cvsignore @@ -0,0 +1 @@ +ap_config_auto.h diff --git a/include/.indent.pro b/include/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/include/.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/include/ap_config.h b/include/ap_config.h new file mode 100644 index 00000000000..e7219cf892e --- /dev/null +++ b/include/ap_config.h @@ -0,0 +1,1394 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef AP_CONFIG_H +#define AP_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ap_config.h: system-dependant #defines and includes... + * See PORTING for a listing of what they mean + */ + +#include "ap_mmn.h" /* MODULE_MAGIC_NUMBER_ */ + +/* + * Support for platform dependent autogenerated defines + */ +#ifndef WIN32 +#include "ap_config_auto.h" +#else +/* not available under WIN32, so provide important entries manually */ +#undef HAVE_UNISTD_H +#endif + +/* Have to include sys/stat.h before ../os/win32/os.h so we can override +stat() properly */ +#include +#include + +/* So that we can use inline on some critical functions, and use + * GNUC attributes (such as to get -Wall warnings for printf-like + * functions). Only do this in gcc 2.7 or later ... it may work + * on earlier stuff, but why chance it. + * + * We've since discovered that the gcc shipped with NeXT systems + * as "cc" is completely broken. It claims to be __GNUC__ and so + * on, but it doesn't implement half of the things that __GNUC__ + * means. In particular it's missing inline and the __attribute__ + * stuff. So we hack around it. PR#1613. -djg + */ +#if !defined(__GNUC__) || __GNUC__ < 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ < 7) ||\ + defined(NEXT) +#define ap_inline +#define __attribute__(__x) +#define ENUM_BITFIELD(e,n,w) signed int n : w +#else +#define ap_inline __inline__ +#define USE_GNU_INLINE +#define ENUM_BITFIELD(e,n,w) e n : w +#endif + +#ifdef WIN32 +/* include process.h first so we can override spawn[lv]e* properly */ +#include +#include "../os/win32/os.h" +#else +#include "os.h" +#endif + +#if !defined(QNX) && !defined(MPE) && !defined(WIN32) && !defined(TPF) +#include +#endif + +/* Define one of these according to your system. */ +#if defined(MINT) +typedef int rlim_t; +#define JMP_BUF sigjmp_buf +#define NO_LONG_DOUBLE +#define USE_FLOCK_SERIALIZED_ACCEPT +#define _BSD_SOURCE +#define EAGAIN EWOULDBLOCK +int initgroups (char *, int); +char *crypt (const char *pw, const char *salt); +int gethostname (char *name, int namelen); + +#elif defined(MPE) +#include +#define NO_SETSID +#define NO_KILLPG +#define NO_WRITEV +#define HAVE_SHMGET 1 +#define USE_SHMGET_SCOREBOARD +#define SHM_R 0400 /* Read permission */ +#define SHM_W 0200 /* Write permission */ +#define NEED_INITGROUPS +#define NEED_STRCASECMP +#define NEED_STRDUP +#define NEED_STRNCASECMP +extern void GETPRIVMODE(); +extern void GETUSERMODE(); +extern char *inet_ntoa(); +#define NO_SLACK +#define NO_GETTIMEOFDAY +#define S_IEXEC S_IXUSR +#define S_IREAD S_IRUSR +#define S_IWRITE S_IWUSR +#define PF_INET AF_INET + +#elif defined(SUNOS4) +#define HAVE_GMTOFF 1 +#undef NO_KILLPG +#undef NO_SETSID +char *crypt(const char *pw, const char *salt); +char *mktemp(char *template); +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +#include +#define NEED_STRERROR +typedef int rlim_t; +#define memmove(a,b,c) bcopy(b,a,c) +#define NO_LINGCLOSE +#define USE_FLOCK_SERIALIZED_ACCEPT +#define NEED_DIFFTIME +#define HAVE_SYSLOG 1 + +#elif defined(SOLARIS2) +#undef HAVE_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#define bzero(a,b) memset(a,0,b) +#if !defined(USE_SYSVSEM_SERIALIZED_ACCEPT) && \ + !defined(USE_PTHREAD_SERIALIZED_ACCEPT) +#define USE_FCNTL_SERIALIZED_ACCEPT +#endif +#define NEED_UNION_SEMUN +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +int gethostname(char *name, int namelen); +#define HAVE_SYSLOG 1 +#define SYS_SIGLIST _sys_siglist + +#elif defined(IRIX) +#undef HAVE_GMTOFF +/* IRIX has killpg, but it's only in _BSD_COMPAT, so don't use it in case + * there's some weird conflict with non-BSD signals */ +#define NO_KILLPG +#undef NO_SETSID +#if !defined(USE_FLOCK_SERIALIZED_ACCEPT) && \ + !defined(USE_USLOCK_SERIALIZED_ACCEPT) && \ + !defined(USE_SYSVSEM_SERIALIZED_ACCEPT) +#define USE_FCNTL_SERIALIZED_ACCEPT +#endif +#define HAVE_SHMGET 1 +#define USE_SHMGET_SCOREBOARD +#define HAVE_MMAP 1 +#define USE_MMAP_FILES +#define NO_LONG_DOUBLE +#define NO_LINGCLOSE +#define HAVE_SYSLOG 1 + +#elif defined(HIUX) +#undef HAVE_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#ifndef _HIUX_SOURCE +#define _HIUX_SOURCE +#endif +#define HAVE_SHMGET 1 +#define USE_SHMGET_SCOREBOARD +#define SELECT_NEEDS_CAST +#define HAVE_SYSLOG 1 + +#elif defined(HPUX) || defined(HPUX10) +#undef HAVE_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#define USE_FCNTL_SERIALIZED_ACCEPT +#ifndef _HPUX_SOURCE +#define _HPUX_SOURCE +#endif +#define HAVE_SHMGET 1 +#define USE_SHMGET_SCOREBOARD +#define HAVE_SYSLOG 1 +#ifndef HPUX10 +#define SELECT_NEEDS_CAST +typedef int rlim_t; +#endif + +#elif defined(HPUX11) +#ifndef _HPUX_SOURCE +#define _HPUX_SOURCE +#endif +#define HAVE_SHMGET +#define USE_SHMGET_SCOREBOARD +#undef HAVE_GMTOFF +#define USE_FCNTL_SERIALIZED_ACCEPT +/* feeling brave? want to try using POSIX mutexes? */ +/* #define HAVE_MMAP */ +/* #define USE_MMAP_SCOREBOARD */ +/* #define USE_MMAP_FILES */ +/* #define USE_PTHREAD_SERIALIZED_ACCEPT */ +#define NO_KILLPG +#undef NO_SETSID +#define HAVE_SYSLOG + +#elif defined(AIX) +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#ifndef __ps2__ +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +#define HAVE_SYSLOG 1 +#ifndef DEFAULT_GROUP +#define DEFAULT_GROUP "nobody" +#endif +#endif +#ifndef DEFAULT_USER +#define DEFAULT_USER "nobody" +#endif +#ifdef NEED_RLIM_T +typedef int rlim_t; +#endif +#define USE_FCNTL_SERIALIZED_ACCEPT +#ifdef USEBCOPY +#define memmove(a,b,c) bcopy(b,a,c) +#endif +#if AIX >= 42 +#define NET_SIZE_T size_t +#endif + +#elif defined(ULTRIX) +/* we don't want to use sys/resource.h under + Ultrix although this header exists. */ +#undef HAVE_SYS_RESOURCE_H +#define HAVE_GMTOFF 1 +#undef NO_KILLPG +#undef NO_SETSID +#define ULTRIX_BRAIN_DEATH +#define NEED_STRDUP +/* If you have Ultrix 4.3, and are using cc, const is broken */ +#ifndef __ultrix__ /* Hack to check for pre-Ultrix 4.4 cc */ +#define const /* Not implemented */ +#endif + +#elif defined(OSF1) +#define HAVE_GMTOFF 1 +#undef NO_KILLPG +#undef NO_SETSID +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +#define NO_LONG_DOUBLE +#define HAVE_SYSLOG 1 +#define USE_FLOCK_SERIALIZED_ACCEPT +#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT + +#elif defined(PARAGON) +#define HAVE_GMTOFF 1 +#undef NO_KILLPG +#undef NO_SETSID +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +#define NO_LONG_DOUBLE +#define HAVE_SYSLOG 1 +typedef int rlim_t; + +#elif defined(SEQUENT) +#define DEFAULT_USER "nobody" +#define DEFAULT_GROUP "nobody" +#define NO_SHMGET 1 +#define HAVE_MMAP 1 +#define HAVE_SYSLOG 1 +#define USE_MMAP_FILES 1 +#define USE_MMAP_SCOREBOARD 1 +#define USE_FCNTL_SERIALIZED_ACCEPT 1 +#define JMP_BUF sigjmp_buf +#undef NO_SETSID +#if SEQUENT < 40 +typedef int rlim_t; +#define NO_GETTIMEOFDAY +#undef HAVE_SYS_RESOURCE_H /* exists but does not provide *rlimit funcs */ +#include +#endif +#if SEQUENT < 42 +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#endif +#if SEQUENT < 44 +#define NO_KILLPG 1 +#define NET_SIZE_T int +#endif +#if SEQUENT >= 44 +#undef NO_KILLPG +#define NET_SIZE_T size_t +#endif + +#elif defined(NEXT) +typedef unsigned short mode_t; +#define HAVE_GMTOFF 1 +#undef NO_KILLPG +#define NO_SETSID +#define NEED_STRDUP +#define NO_LINGCLOSE +#undef _POSIX_SOURCE +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&(S_IFMT)) == (S_IFDIR)) +#endif +#ifndef S_ISREG +#define S_ISREG(m) (((m)&(S_IFMT)) == (S_IFREG)) +#endif +#ifndef S_IXUSR +#define S_IXUSR 00100 +#endif +#ifndef S_IRGRP +#define S_IRGRP 00040 +#endif +#ifndef S_IXGRP +#define S_IXGRP 00010 +#endif +#ifndef S_IROTH +#define S_IROTH 00004 +#endif +#ifndef S_IXOTH +#define S_IXOTH 00001 +#endif +#ifndef S_IRUSR +#define S_IRUSR S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR S_IWRITE +#endif +#ifndef S_IWGRP +#define S_IWGRP 000020 +#endif +#ifndef S_IWOTH +#define S_IWOTH 000002 +#endif + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +/* PR#2293 fix */ +#define ap_wait_t union wait +#define waitpid(a,b,c) wait4((a) == -1 ? 0 : (a),(union wait *)(b),c,NULL) +#define WEXITSTATUS(status) (int)( WIFEXITED(status) ? ( (status).w_retcode ) : -1) +#define WTERMSIG(status) (int)( (status).w_termsig ) + +typedef int pid_t; +#define USE_LONGJMP +#define NO_USE_SIGACTION +#define HAVE_SYSLOG 1 + +#if defined(__DYNAMIC__) +#define HAVE_DYLD +#define DYLD_CANT_UNLOAD +#endif + +#elif defined(MAC_OS) || defined(MAC_OS_X_SERVER) /* Mac OS (>= 10.0) and Mac OS X Server (<= 5.x) */ +#undef PLATFORM +#ifdef MAC_OS_X_SERVER +#define PLATFORM "Mac OS X Server" +#else +#define PLATFORM "Mac OS" +#endif +#define HAVE_DYLD +#ifdef MAC_OS_X_SERVER +#define DYLD_CANT_UNLOAD +#endif /* MAC_OS_X_SERVER */ +#define HAVE_GMTOFF +#define HAVE_MMAP +#define USE_MMAP_FILES +#define USE_MMAP_SCOREBOARD +#ifdef MAC_OS_X_SERVER +#define MAP_TMPFILE +#endif /* MAC_OS_X_SERVER */ +#define HAVE_RESOURCE +#define HAVE_SNPRINTF +#define JMP_BUF jmp_buf +#define USE_LONGJMP +#define USE_FLOCK_SERIALIZED_ACCEPT +#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT +/* + * If you are using APACI, (you probably should be on Mac OS) these + * values are set at configure time. + */ +#ifndef HTTPD_ROOT +#define HTTPD_ROOT "/Local/Library/WebServer" +#endif +#ifndef DOCUMENT_LOCATION +#define DOCUMENT_LOCATION HTTPD_ROOT "/Documents" +#endif +#ifndef DEFAULT_XFERLOG +#define DEFAULT_XFERLOG "Logs/Access" +#endif +#ifndef DEFAULT_ERRORLOG +#define DEFAULT_ERRORLOG "Logs/Errors" +#endif +#ifndef DEFAULT_PIDLOG +#define DEFAULT_PIDLOG "Logs/Process" +#endif +#ifndef DEFAULT_SCOREBOARD +#define DEFAULT_SCOREBOARD "Logs/Status" +#endif +#ifndef DEFAULT_LOCKFILE +#define DEFAULT_LOCKFILE "Logs/Lock" +#endif +#ifndef SERVER_CONFIG_FILE +#define SERVER_CONFIG_FILE "Configuration/Server" +#endif +#ifndef RESOURCE_CONFIG_FILE +#define RESOURCE_CONFIG_FILE "Configuration/Resources" +#endif +#ifndef TYPES_CONFIG_FILE +#define TYPES_CONFIG_FILE "Configuration/MIME" +#endif +#ifndef ACCESS_CONFIG_FILE +#define ACCESS_CONFIG_FILE "Configuration/Access" +#endif +#ifndef DEFAULT_USER_DIR +#define DEFAULT_USER_DIR "Library/Web Documents" +#endif +#ifndef DEFAULT_USER +#define DEFAULT_USER "www" +#endif +#ifndef DEFAULT_GROUP +#define DEFAULT_GROUP "www" +#endif +#ifndef DEFAULT_PATH +#define DEFAULT_PATH "/bin:/usr/bin:/usr/local/bin" +#endif + +#elif defined(LINUX) + +#if LINUX > 1 +#include + +/* libc4 systems probably still work, it probably doesn't define + * __GNU_LIBRARY__ + * libc5 systems define __GNU_LIBRARY__ == 1, but don't define __GLIBC__ + * glibc 2.x and later systems define __GNU_LIBRARY__ == 6, but list it as + * "deprecated in favour of __GLIBC__"; the value 6 will never be changed. + * glibc 1.x systems (i.e. redhat 4.x on sparc/alpha) should have + * __GLIBC__ < 2 + * all glibc based systems need crypt.h + */ +#if defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1 +#include +#endif + +/* glibc 2.0.0 through 2.0.4 need size_t * here, where 2.0.5 needs socklen_t * + * there's no way to discern between these two libraries. But using int should + * be portable because otherwise these libs would be hopelessly broken with + * reams of existing networking code. We'll use socklen_t * for 2.1.x and + * later. + * + * int works for all the earlier libs, and is picked up by default later. + */ +#if defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 0)) +#define NET_SIZE_T socklen_t +#endif + +#define HAVE_SHMGET 1 +#define USE_SHMGET_SCOREBOARD +#define HAVE_MMAP 1 +#define USE_MMAP_FILES + +/* flock is faster ... but hasn't been tested on 1.x systems */ +/* PR#3531 indicates flock() may not be stable, probably depends on + * kernel version. Go back to using fcntl, but provide a way for + * folks to tweak their Configuration to get flock. + */ +#ifndef USE_FLOCK_SERIALIZED_ACCEPT +#define USE_FCNTL_SERIALIZED_ACCEPT +#endif + +#define SYS_SIGLIST _sys_siglist + +#else +#define USE_FCNTL_SERIALIZED_ACCEPT +#endif + +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#include +#define HAVE_SYSLOG 1 + +/* glibc 2.1 and later finally define rlim_t */ +#if !defined(__GLIBC__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 1) +typedef int rlim_t; +#endif + +#elif defined(SCO) +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_INITGROUPS +#define NO_WRITEV +#include +#define HAVE_SYSLOG 1 +#undef HAVE_SYS_RESOURCE_H + +#elif defined(SCO5) + +#define USE_FCNTL_SERIALIZED_ACCEPT +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +#define SecureWare +#define HAVE_SYSLOG 1 + +/* Although SCO 5 defines these in (note the "s") they don't have + consts. Sigh. */ +extern int strcasecmp(const char *, const char *); +extern int strncasecmp(const char *, const char *, unsigned); + +#elif defined(AUX3) +/* These are to let -Wall compile more cleanly */ +extern int strcasecmp(const char *, const char *); +extern int strncasecmp(const char *, const char *, unsigned); +extern int set42sig(), getopt(), getpeername(), bzero(); +extern int listen(), bind(), socket(), getsockname(); +extern int accept(), gethostname(), connect(), lstat(); +extern int select(), killpg(), shutdown(); +extern int initgroups(), setsockopt(); +extern char *shmat(); +extern int shmctl(); +extern int shmget(); +extern char *sbrk(); +extern char *crypt(); +#include +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_STRDUP +/* fcntl() locking is expensive with NFS */ +#define USE_FLOCK_SERIALIZED_ACCEPT +#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT +#define HAVE_SHMGET 1 +#define USE_SHMGET_SCOREBOARD +/* + * NOTE: If when you run Apache under A/UX and you get a warning + * that httpd couldn't move break, then the below value for + * MOVEBREAK (64megs) is too large for your setup. Try reducing + * to 0x2000000 which is still PLENTY of space. I doubt if + * even on heavy systems sbrk() would be called at all... + */ +#define MOVEBREAK 0x4000000 +#define NO_LINGCLOSE +#define NO_SLACK +#define HAVE_SYSLOG 1 +#undef HAVE_SYS_RESOURCE_H /* exists but does not provide *rlimit funcs */ + +#elif defined(SVR4) +#define NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#ifndef MPRAS +#define NEED_STRCASECMP +#ifndef ENCORE +#define NEED_STRNCASECMP +#endif /* ENCORE */ +#endif /* MPRAS */ +#define bzero(a,b) memset(a,0,b) +/* A lot of SVR4 systems need this */ +#ifndef USE_SYSVSEM_SERIALIZED_ACCEPT +#define USE_FCNTL_SERIALIZED_ACCEPT +#endif +#define HAVE_SYSLOG 1 +#define NET_SIZE_T size_t +#define HAVE_SHMGET 1 +#define USE_SHMGET_SCOREBOARD +#ifdef _OSD_POSIX /* BS2000-POSIX mainframe needs initgroups */ +#define NEED_HASHBANG_EMUL /* execve() doesn't start shell scripts by default */ +#define _KMEMUSER /* Enable SHM_R/SHM_W defines in */ +#undef NEED_STRCASECMP +#undef NEED_STRNCASECMP +#undef bzero +#endif /*_OSD_POSIX*/ + +#elif defined(UW) +#if UW < 700 +#define USE_FCNTL_SERIALIZED_ACCEPT +#define NO_LINGCLOSE +#define NO_KILLPG +#endif +#undef NO_SETSID +#undef NEED_STRDUP +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define bzero(a,b) memset(a,0,b) +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +#define HAVE_SHMGET 1 +#undef USE_SHMGET_SCOREBOARD /* force use of mmap() scoreboard */ +#include +#if UW >= 200 +#define _POSIX_SOURCE +#endif +#define NET_SIZE_T size_t +#define HAVE_SYSLOG 1 + +#elif defined(DGUX) +#define NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#ifdef _IX86_DG +#undef NEED_STRCASECMP +#undef NEED_STRNCASECMP +#else +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#endif +#define bzero(a,b) memset(a,0,b) +/* A lot of SVR4 systems need this */ +#define USE_FCNTL_SERIALIZED_ACCEPT +#define ap_inet_addr inet_network +#define HAVE_SYSLOG 1 + +#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(NETBSD) +#define HAVE_GMTOFF 1 +#undef NO_KILLPG +#undef NO_SETSID +#define HAVE_SYSLOG 1 +#ifndef DEFAULT_USER +#define DEFAULT_USER "nobody" +#endif +#ifndef DEFAULT_GROUP +#define DEFAULT_GROUP "nogroup" +#endif +#define HAVE_SHMGET 1 +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +#define USE_FLOCK_SERIALIZED_ACCEPT +#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT + +#elif defined(UTS21) +#undef HAVE_GMTOFF +#undef NO_KILLPG +#define NO_SETSID +#define NEED_WAITPID +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define HAVE_SYSLOG 1 +#define USE_LONGJMP +#define JMP_BUF jmp_buf +#define NO_USE_SIGACTION +#define NEED_STRERROR +#define NEED_STRSTR +#define NEED_HASHBANG_EMUL +#define NDELAY_PIPE_RETURNS_ZERO +#define NO_DATA NO_ADDRESS +#define ap_wait_t union wait +#define WEXITSTATUS(status) (int)((status).w_retcode) +#define WTERMSIG(status) (int)((status).w_termsig) +#define strftime(buf,bufsize,fmt,tm) ascftime(buf,fmt,tm) +#undef HAVE_SYS_RESOURCE_H /* exists but does not provide *rlimit funcs */ +#include +#include + +#elif defined(APOLLO) +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define HAVE_SYSLOG 1 + +#elif defined(__FreeBSD__) || defined(__bsdi__) +#if defined(__FreeBSD__) +#include +#endif +#define HAVE_GMTOFF 1 +#undef NO_KILLPG +#undef NO_SETSID +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +#ifndef DEFAULT_USER +#define DEFAULT_USER "nobody" +#endif +#ifndef DEFAULT_GROUP +#define DEFAULT_GROUP "nogroup" +#endif +#if defined(__bsdi__) || \ +(defined(__FreeBSD_version) && (__FreeBSD_version < 220000)) +typedef quad_t rlim_t; +#endif +#define USE_FLOCK_SERIALIZED_ACCEPT +#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT +#define HAVE_SYSLOG 1 +#define SYS_SIGLIST sys_siglist + +#elif defined(QNX) +#ifndef crypt +char *crypt(const char *pw, const char *salt); +#endif +#ifndef initgroups +int initgroups(char *, int); +#endif +#ifndef strncasecmp +#define strncasecmp strnicmp +#endif +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_INITGROUPS +#define NEED_SELECT_H +#define NEED_PROCESS_H +#include +#define HAVE_MMAP 1 +#define USE_POSIX_SCOREBOARD +#define USE_FLOCK_SERIALIZED_ACCEPT +#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT +#define HAVE_SYSLOG 1 + +#elif defined(LYNXOS) +#undef HAVE_GMTOFF +#undef USE_MMAP_SCOREBOARD +#undef USE_SHMGET_SCOREBOARD +#undef USE_FCNTL_SERIALIZED_ACCEPT +#undef USE_FLOCK_SERIALIZED_ACCEPT +#define USE_LONGJMP +#undef NO_KILLPG +#undef NO_SETSID +#undef NO_USE_SIGACTION +#undef NO_LINGCLOSE +extern char *crypt(char *pw, char *salt); +typedef int rlim_t; +#define HAVE_SYSLOG 1 + +#elif defined(UXPDS) +#undef NEED_STRCASECMP +#undef NEED_STRNCASECMP +#undef NEED_STRDUP +#undef HAVE_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#define bzero(a,b) memset(a,0,b) +#define USE_FCNTL_SERIALIZED_ACCEPT +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +#define HAVE_SYSLOG 1 + +#elif defined(OS2) +/* Defines required for EMX OS/2 port. */ +#define NO_KILLPG +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define NEED_PROCESS_H +#define NO_SETSID +#define NO_TIMES +#define CASE_BLIND_FILESYSTEM +/* Add some drive name support */ +#define chdir _chdir2 +#include +#define MAXSOCKETS 2048 +#define USE_OS2_SCOREBOARD +#define NO_RELIABLE_PIPED_LOGS +#define USE_OS2SEM_SERIALIZED_ACCEPT +#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT + +#elif defined(__MACHTEN__) +typedef int rlim_t; +#undef NO_KILLPG +#define NO_SETSID +#define HAVE_GMTOFF 1 +#ifndef __MACHTEN_PPC__ +#ifndef __MACHTEN_68K__ +#define __MACHTEN_68K__ +#endif +#define USE_FLOCK_SERIALIZED_ACCEPT +#define NO_USE_SIGACTION +#define JMP_BUF sigjmp_buf +#define USE_LONGJMP +#undef NEED_STRDUP +#else +#define HAVE_SHMGET 1 +#define USE_SHMGET_SCOREBOARD +#define USE_FCNTL_SERIALIZED_ACCEPT +#endif + +/* Convex OS v11 */ +#elif defined(CONVEXOS11) +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define HAVE_MMAP 1 +#define USE_MMAP_SCOREBOARD +#define USE_MMAP_FILES +#define HAVE_SYSLOG 1 + +#define NO_TIMEZONE +#include +#include +typedef int rlim_t; + +#elif defined(ISC) +#include +#define NO_KILLPG +#undef NO_SETSID +#define HAVE_SHMGET 1 +#define USE_SHMGET_SCOREBOARD +#define USE_FCNTL_SERIALIZED_ACCEPT +#define HAVE_SYSLOG 1 + +#elif defined(NEWSOS) +#define HAVE_SHMGET 1 +#define USE_SHMGET_SCOREBOARD +#define USE_LONGJMP +#define NO_SETSID +#define NO_USE_SIGACTION +#define NEED_WAITPID +#define NO_OTHER_CHILD +#define HAVE_SYSLOG 1 +#include +#include +#include +typedef int pid_t; +typedef int rlim_t; +typedef int mode_t; + +#elif defined(RISCIX) +#include +typedef int rlim_t; +#define NO_USE_SIGACTION +#define USE_LONGJMP +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define NEED_STRDUP + +#elif defined(BEOS) +#include + +#define NO_WRITEV +#define NO_KILLPG +#define NEED_INITGROUPS + +#elif defined(_CX_SX) +#define JMP_BUF sigjmp_buf +#include +#include + +#elif defined(WIN32) + +/* All windows stuff is now in os/win32/os.h */ + +#elif defined(TPF) /* IBM Transaction Processing Facility operating system */ + +#include +#include +#include +#include +#define PRIMECRAS 0x010000 +#define JMP_BUF jmp_buf +#define HAVE_SHMGET +#undef HAVE_SYS_RESOURCE_H +#define NEED_INITGROUPS +#define NEED_SIGNAL_INTERRUPT +#include +#ifndef __strings_h +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#endif +#define NEED_STRDUP +#define NO_DBM_REWRITEMAP +#define NO_GETTIMEOFDAY +#define NO_KILLPG +#define NO_LINGCLOSE +#define NO_MMAP +#define NO_OTHER_CHILD +#define NO_RELIABLE_PIPED_LOGS +#define NO_SETSID +#define NO_SLACK +#define NO_TIMES +#define NO_USE_SIGACTION +#define NO_WRITEV +#define USE_LONGJMP +/*#define USE_SHMGET_SCOREBOARD*/ +#define USE_TPF_ACCEPT +#define USE_TPF_CORE_SERIALIZED_ACCEPT +/*#define USE_TPF_DAEMON*/ +#define USE_TPF_SCOREBOARD +#define USE_TPF_SELECT +#undef offsetof +#define offsetof(s_type,field) ((size_t)&(((s_type*)0)->field)) + +#elif defined(__TANDEM) +#define NO_WRITEV +#define NO_KILLPG +#define NEED_INITGROUPS +#define NO_SLACK + +#else +/* Unknown system - Edit these to match */ +#ifdef BSD +#define HAVE_GMTOFF 1 +#else +#undef HAVE_GMTOFF +#endif +/* NO_KILLPG is set on systems that don't have killpg */ +#undef NO_KILLPG +/* NO_SETSID is set on systems that don't have setsid */ +#undef NO_SETSID +/* NEED_STRDUP is set on stupid systems that don't have strdup. */ +#undef NEED_STRDUP +#endif + +/* stuff marked API_EXPORT is part of the API, and intended for use + * by modules + */ +#ifndef API_EXPORT +#define API_EXPORT(type) type +#endif + +/* Stuff marked API_EXPORT_NONSTD is part of the API, and intended for + * use by modules. The difference between API_EXPORT and + * API_EXPORT_NONSTD is that the latter is required for any functions + * which use varargs or are used via indirect function call. This + * is to accomodate the two calling conventions in windows dlls. + */ +#ifndef API_EXPORT_NONSTD +#define API_EXPORT_NONSTD(type) type +#endif + +#ifndef MODULE_VAR_EXPORT +#define MODULE_VAR_EXPORT +#endif +#ifndef API_VAR_EXPORT +#define API_VAR_EXPORT +#endif + +/* modules should not used functions marked CORE_EXPORT + * or CORE_EXPORT_NONSTD */ +#ifndef CORE_EXPORT +#define CORE_EXPORT API_EXPORT +#endif +#ifndef CORE_EXPORT_NONSTD +#define CORE_EXPORT_NONSTD API_EXPORT_NONSTD +#endif + +/* On Mac OS X Server, symbols that conflict with loaded dylibs + * (eg. System framework) need to be declared as private symbols with + * __private_extern__. + * For other systems, make that a no-op. + */ +#if (defined(MAC_OS) || defined(MAC_OS_X_SERVER)) && defined(__DYNAMIC__) +#define ap_private_extern __private_extern__ +#else +#define ap_private_extern +#endif + +/* + * The particular directory style your system supports. If you have dirent.h + * in /usr/include (POSIX) or /usr/include/sys (SYSV), #include + * that file and define DIR_TYPE to be dirent. Otherwise, if you have + * /usr/include/sys/dir.h, define DIR_TYPE to be direct and include that + * file. If you have neither, I'm confused. + */ + +#include +#include + +#if !defined(NEXT) && !defined(WIN32) +#include +#define DIR_TYPE dirent +#elif !defined(WIN32) +#include +#define DIR_TYPE direct +#else +#define DIR_TYPE dirent +#endif + +#include +#include +#include +#ifdef __TANDEM +#include +#endif +#include "ap_ctype.h" +#if !defined(MPE) && !defined(WIN32) && !defined(TPF) && !defined(__TANDEM) +#include +#endif +#ifndef WIN32 +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif /* HAVE_SYS_SELECT_H */ +#ifndef TPF +#include +#endif /* TPF */ +#include +#include +#if !defined(MPE) && !defined(BEOS) && !defined(TPF) +#include /* for inet_ntoa */ +#endif +#include +#include +#include +#include +#define closesocket(s) close(s) +#ifndef O_BINARY +#define O_BINARY (0) +#endif + +#else /* WIN32 */ +#include +#include +#include +#include +#endif /* ndef WIN32 */ +#include +#include /* for ctime */ +#ifdef WIN32 +#define strftime(s,max,format,tm) os_strftime(s,max,format,tm) +#endif +#include +#if defined(TPF) && defined(NSIG) +#undef NSIG +#endif +#include +#if !defined(QNX) && !defined(CONVEXOS11) && !defined(NEXT) && !defined(TPF) +#include +#endif + +#ifdef NEED_PROCESS_H +#include +#endif + +#ifdef WIN32 +#include "../include/hsregex.h" +#elif defined(USE_HSREGEX) +#include "hsregex.h" +#else +#include +#endif + +#ifdef HAVE_SYS_RESOURCE_H +#include +#ifdef SUNOS4 +int getrlimit(int, struct rlimit *); +int setrlimit(int, struct rlimit *); +#endif +#endif +#ifdef USE_MMAP_SCOREBOARD +#if !defined(OS2) && !defined(WIN32) +/* This file is not needed for OS/2 */ +#include +#endif +#endif +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +#define MAP_ANON MAP_ANONYMOUS +#endif + +#if defined(USE_MMAP_FILES) && (defined(NO_MMAP) || !defined(HAVE_MMAP)) +#undef USE_MMAP_FILES +#endif + +#if defined(USE_MMAP_SCOREBOARD) && (defined(NO_MMAP) || !defined(HAVE_MMAP)) +#undef USE_MMAP_SCOREBOARD +#endif + +#if defined(USE_SHMGET_SCOREBOARD) && (defined(NO_SHMGET) || !defined(HAVE_SHMGET)) +#undef USE_SHMGET_SCOREBOARD +#endif + +#ifndef LOGNAME_MAX +#define LOGNAME_MAX 25 +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef ultrix +#define ULTRIX_BRAIN_DEATH +#endif + +#ifndef S_ISLNK +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long) -1) +#endif + +/* + * Replace signal function with sigaction equivalent + */ +#ifndef NO_USE_SIGACTION +typedef void Sigfunc(int); + +#if defined(SIG_IGN) && !defined(SIG_ERR) +#define SIG_ERR ((Sigfunc *)-1) +#endif + +/* + * For some strange reason, QNX defines signal to signal. Eliminate it. + */ +#ifdef signal +#undef signal +#endif +#define signal(s,f) ap_signal(s,f) +Sigfunc *signal(int signo, Sigfunc * func); +#endif + +#include + +#if defined(USE_LONGJMP) +#define ap_longjmp(x, y) longjmp((x), (y)) +#define ap_setjmp(x) setjmp(x) +#ifndef JMP_BUF +#define JMP_BUF jmp_buf +#endif +#else +#define ap_longjmp(x, y) siglongjmp((x), (y)) +#define ap_setjmp(x) sigsetjmp((x), 1) +#ifndef JMP_BUF +#define JMP_BUF sigjmp_buf +#endif +#endif + +/* Majority of os's want to verify FD_SETSIZE */ +#if !defined(WIN32) && !defined(TPF) +#define CHECK_FD_SETSIZE +#endif + +#ifdef USE_TPF_SELECT +#define ap_select(_a, _b, _c, _d, _e) \ + tpf_select(_a, _b, _c, _d, _e) +#elif defined(SELECT_NEEDS_CAST) +#define ap_select(_a, _b, _c, _d, _e) \ + select((_a), (int *)(_b), (int *)(_c), (int *)(_d), (_e)) +#else +#define ap_select(_a, _b, _c, _d, _e) \ + select(_a, _b, _c, _d, _e) +#endif + +#ifdef USE_TPF_ACCEPT +#define ap_accept(_fd, _sa, _ln) tpf_accept(_fd, _sa, _ln) +#else +#define ap_accept(_fd, _sa, _ln) accept(_fd, _sa, _ln) +#endif + +#ifdef NEED_SIGNAL_INTERRUPT +#define ap_check_signals() tpf_process_signals() +#else +#define ap_check_signals() +#endif + +#ifdef ULTRIX_BRAIN_DEATH +#define ap_fdopen(d,m) fdopen((d), (char *)(m)) +#else +#define ap_fdopen(d,m) fdopen((d), (m)) +#endif + +#ifndef ap_inet_addr +#define ap_inet_addr inet_addr +#endif + +#ifdef NO_OTHER_CHILD +#define NO_RELIABLE_PIPED_LOGS +#endif + +/* When the underlying OS doesn't support exec() of scripts which start + * with a HASHBANG (#!) followed by interpreter name and args, define this. + */ +#ifdef NEED_HASHBANG_EMUL +extern int ap_execle(const char *filename, const char *arg,...); +extern int ap_execve(const char *filename, const char *argv[], + const char *envp[]); +/* ap_execle() is a wrapper function around ap_execve(). */ +#define execle ap_execle +#define execve(path,argv,envp) ap_execve(path,argv,envp) +#endif + +/* Finding offsets of elements within structures. + * Taken from the X code... they've sweated portability of this stuff + * so we don't have to. Sigh... + */ + +#if defined(CRAY) || (defined(__arm) && !defined(LINUX)) +#ifdef __STDC__ +#define XtOffset(p_type,field) _Offsetof(p_type,field) +#else +#ifdef CRAY2 +#define XtOffset(p_type,field) \ + (sizeof(int)*((unsigned int)&(((p_type)NULL)->field))) + +#else /* !CRAY2 */ + +#define XtOffset(p_type,field) ((unsigned int)&(((p_type)NULL)->field)) + +#endif /* !CRAY2 */ +#endif /* __STDC__ */ +#else /* ! (CRAY || __arm) */ + +#define XtOffset(p_type,field) \ + ((long) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL))) + +#endif /* !CRAY */ + +#ifdef offsetof +#define XtOffsetOf(s_type,field) offsetof(s_type,field) +#else +#define XtOffsetOf(s_type,field) XtOffset(s_type*,field) +#endif + +/* + * NET_SIZE_T exists because of shortsightedness on the POSIX committee. BSD + * systems used "int *" as the parameter to accept(), getsockname(), + * getpeername() et al. Consequently many unixes took an int * for that + * parameter. The POSIX committee decided that "int" was just too generic and + * had to be replaced with size_t almost everywhere. There's no problem with + * that when you're passing by value. But when you're passing by reference + * this creates a gross source incompatibility with existing programs. On + * 32-bit architectures it creates only a warning. On 64-bit architectures it + * creates broken code -- because "int *" is a pointer to a 64-bit quantity and + * "size_t *" is frequently a pointer to a 32-bit quantity. + * + * Some Unixes adopted "size_t *" for the sake of POSIX compliance. Others + * ignored it because it was such a broken interface. Chaos ensued. POSIX + * finally woke up and decided that it was wrong and created a new type + * socklen_t. The only useful value for socklen_t is int, and that's how + * everyone who has a clue implements it. It is almost always the case that + * NET_SIZE_T should be defined to be an int, unless the system being compiled + * for was created in the window of POSIX madness. + */ +#ifndef NET_SIZE_T +#define NET_SIZE_T int +#endif + +/* Linux defines __WCOREDUMP, but doesn't define WCOREDUMP unless __USE_BSD + * is in use... we'd prefer to just use WCOREDUMP everywhere. + */ +#if defined(__WCOREDUMP) && !defined(WCOREDUMP) +#define WCOREDUMP __WCOREDUMP +#endif + +#ifdef SUNOS_LIB_PROTOTYPES +/* Prototypes needed to get a clean compile with gcc -Wall. + * Believe it or not, these do have to be declared, at least on SunOS, + * because they aren't mentioned in the relevant system headers. + * Sun Quality Software. Gotta love it. This section is not + * currently (13Nov97) used. + */ + +int getopt(int, char **, char *); + +int strcasecmp(const char *, const char *); +int strncasecmp(const char *, const char *, int); +int toupper(int); +int tolower(int); + +int printf(char *,...); +int fprintf(FILE *, char *,...); +int fputs(char *, FILE *); +int fread(char *, int, int, FILE *); +int fwrite(char *, int, int, FILE *); +int fgetc(FILE *); +char *fgets(char *s, int, FILE*); +int fflush(FILE *); +int fclose(FILE *); +int ungetc(int, FILE *); +int _filbuf(FILE *); /* !!! */ +int _flsbuf(unsigned char, FILE *); /* !!! */ +int sscanf(char *, char *,...); +void setbuf(FILE *, char *); +void perror(char *); + +time_t time(time_t *); +int strftime(char *, int, const char *, struct tm *); + +int initgroups(char *, int); +int wait3(int *, int, void *); /* Close enough for us... */ +int lstat(const char *, struct stat *); +int stat(const char *, struct stat *); +int flock(int, int); +#ifndef NO_KILLPG +int killpg(int, int); +#endif +int socket(int, int, int); +int setsockopt(int, int, int, const char *, int); +int listen(int, int); +int bind(int, struct sockaddr *, int); +int connect(int, struct sockaddr *, int); +int accept(int, struct sockaddr *, int *); +int shutdown(int, int); + +int getsockname(int s, struct sockaddr *name, int *namelen); +int getpeername(int s, struct sockaddr *name, int *namelen); +int gethostname(char *name, int namelen); +void syslog(int, char *,...); +char *mktemp(char *); + +long vfprintf(FILE *, const char *, va_list); + +#endif /* SUNOS_LIB_PROTOTYPES */ + +/* The assumption is that when the functions are missing, + * then there's no matching prototype available either. + * Declare what is needed exactly as the replacement routines implement it. + */ +#ifdef NEED_STRDUP +extern char *strdup (const char *str); +#endif +#ifdef NEED_STRCASECMP +extern int strcasecmp (const char *a, const char *b); +#endif +#ifdef NEED_STRNCASECMP +extern int strncasecmp (const char *a, const char *b, int n); +#endif +#ifdef NEED_INITGROUPS +extern int initgroups(const char *name, gid_t basegid); +#endif +#ifdef NEED_WAITPID +extern int waitpid(pid_t pid, int *statusp, int options); +#endif +#ifdef NEED_STRERROR +extern char *strerror (int err); +#endif +#ifdef NEED_DIFFTIME +extern double difftime(time_t time1, time_t time0); +#endif + +#ifndef ap_wait_t +#define ap_wait_t int +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !AP_CONFIG_H */ diff --git a/include/ap_listen.h b/include/ap_listen.h new file mode 100644 index 00000000000..600f6fca28d --- /dev/null +++ b/include/ap_listen.h @@ -0,0 +1,86 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef AP_LISTEN_H +#define AP_LISTEN_H + +typedef struct ap_listen_rec ap_listen_rec; +struct ap_listen_rec { + ap_listen_rec *next; + struct sockaddr_in local_addr; /* local IP address and port */ +/* TODO: replace the fd with APR stuff */ + int fd; +/* more stuff here, like which protocol is bound to the port */ +}; + +ap_listen_rec *ap_listeners; + +void ap_listen_pre_config(void); +int ap_listen_open(pool *pconf, unsigned port); +const char *ap_set_listenbacklog(cmd_parms *cmd, void *dummy, char *arg); +const char *ap_set_listener(cmd_parms *cmd, void *dummy, char *ips); +const char *ap_set_send_buffer_size(cmd_parms *cmd, void *dummy, char *arg); + +#define LISTEN_COMMANDS \ +{ "ListenBacklog", ap_set_listenbacklog, NULL, RSRC_CONF, TAKE1, \ + "Maximum length of the queue of pending connections, as used by listen(2)" }, \ +{ "Listen", ap_set_listener, NULL, RSRC_CONF, TAKE1, \ + "A port number or a numeric IP address and a port number"}, \ +{ "SendBufferSize", ap_set_send_buffer_size, NULL, RSRC_CONF, TAKE1, \ + "Send buffer size in bytes"}, + +#endif diff --git a/include/ap_mmn.h b/include/ap_mmn.h new file mode 100644 index 00000000000..6f3138346d0 --- /dev/null +++ b/include/ap_mmn.h @@ -0,0 +1,256 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_AP_MMN_H +#define APACHE_AP_MMN_H + +/* + * MODULE_MAGIC_NUMBER_MAJOR + * Major API changes that could cause compatibility problems for older modules + * such as structure size changes. No binary compatibility is possible across + * a change in the major version. + * + * MODULE_MAGIC_NUMBER_MINOR + * Minor API changes that do not cause binary compatibility problems. + * Should be reset to 0 when upgrading MODULE_MAGIC_NUMBER_MAJOR. + * + * See the MODULE_MAGIC_AT_LEAST macro below for an example. + */ + +/* + * 19950525 - original value + * 19960512 (1.1b2) - updated, 1.1, version. + * 19960526 (1.1b3) - get_token(), table_unset(), pstrndup() + * functions added + * 19960725 (1.2-dev) - HTTP/1.1 compliance + * (new version of read_client_block) + * 19960806 (1.2-dev) - scan_script_header_err() added + * 19961007 (1.2-dev) - replace read_client_block() with get_client_block() + * 19961125 (1.2b1) - change setup_client_block() to Roy's version + * 19961211 (1.2b3) - rwrite() added + * 19970103 (1.2b5-dev) - header parse API + * 19970427 (1.2b9-dev) - port references made unsigned + * 19970526 (1.2) - correct vhost walk for multiple requests on a single + * connect + * 19970623 (1.3-dev) - NT changes + * 19970628 (1.3-dev) - ap_slack (fd fixes) added + * 19970717 (1.3-dev) - child_init API hook added + * 19970719 (1.3-dev) - discard_request_body() added (to clear the decks + * as needed) + * 19970728 (1.3a2-dev) - child_exit API hook added + * 19970818 (1.3a2-dev) - post read-request phase added + * 19970825 (1.3a2-dev) - r->mtime cell added + * 19970831 (1.3a2-dev) - error logging changed to use aplog_error() + * 19970902 (1.3a2-dev) - MD5 routines and structures renamed to ap_* + * 19970912 (1.3b1-dev) - set_last_modified split into set_last_modified, + * set_etag and meets_conditions + * register_other_child API + * piped_log API + * short_score split into parent and child pieces + * os_is_absolute_path + * 19971026 (1.3b3-dev) - custom config hooks in place + * 19980126 (1.3b4-dev) - ap_cpystrn(), table_addn(), table_setn(), + * table_mergen() + * 19980201 (1.3b4-dev) - construct_url() + * prototype server_rec * -> request_rec * + * add get_server_name() and get_server_port() + * 19980207 (1.3b4-dev) - add dynamic_load_handle to module structure as part + * of the STANDARD_MODULE_STUFF header + * 19980304 (1.3b6-dev) - abstraction of SERVER_BUILT and SERVER_VERSION + * 19980305 (1.3b6-dev) - ap_config.h added for use by external modules + * 19980312 (1.3b6-dev) - parse_uri_components() and its ilk + * remove r->hostlen, add r->unparsed_uri + * set_string_slot_lower() + * clarification: non-RAW_ARGS cmd handlers do not + * need to pstrdup() their arguments + * clarification: request_rec members content_type, + * handler, content_encoding, content_language, + * content_languages MUST all be lowercase strings, + * and MAY NOT be modified in place -- modifications + * require pstrdup(). + * 19980317 (1.3b6-dev) - CORE_EXPORTs for win32 and + * API export basic_http_header, send_header_field, + * set_keepalive, srm_command_loop, check_cmd_context, + * tm2sec + * spacetoplus(), plustospace(), client_to_stdout() + * removed + * 19980324 (1.3b6-dev) - API_EXPORT(index_of_response) + * 19980413 (1.3b6-dev) - The BIG SYMBOL RENAMING: general ap_ prefix + * (see src/include/compat.h for more details) + * ap_vformatter() API, see src/include/ap.h + * 19980507 (1.3b7-dev) - addition of ap_add_version_component() and + * discontinuation of -DSERVER_SUBVERSION support + * 19980519 (1.3b7-dev) - add child_info * to spawn function (as passed to + * ap_spawn_child_err_buff) and to ap_call_exec to make + * children work correctly on Win32. + * 19980527 (1.3b8-dev) - renamed some more functions to ap_ prefix which were + * missed at the big renaming (they are defines): + * is_default_port, default_port and http_method. + * A new communication method for modules was added: + * they can create customized error messages under the + * "error-notes" key in the request_rec->notes table. + * This string will be printed in place of the canned + * error responses, and will be propagated to + * ErrorDocuments or cgi scripts in the + * (REDIRECT_)ERROR_NOTES variable. + * 19980627 (1.3.1-dev) - More renaming that we forgot/bypassed. In particular: + * table_elts --> ap_table_elts + * is_table_empty --> ap_is_table_empty + * 19980708 (1.3.1-dev) - ap_isalnum(), ap_isalpha(), ... "8-bit safe" ctype + * macros and apctype.h added + * 19980713 (1.3.1-dev) - renaming of C header files: + * 1. conf.h -> ap_config.h + * 2. conf_auto.h -> ap_config_auto.h - now merged + * 3. ap_config.h -> ap_config_auto.h - now merged + * 4. compat.h -> ap_compat.h + * 5. apctype.h -> ap_ctype.h + * 19980806 (1.3.2-dev) - add ap_log_rerror() + * - add ap_scan_script_header_err_core() + * - add ap_uuencode() + * - add ap_custom_response() + * 19980811 (1.3.2-dev) - added limit_req_line, limit_req_fieldsize, and + * limit_req_fields to server_rec. + * added limit_req_body to core_dir_config and + * ap_get_limit_req_body() to get its value. + * 19980812 (1.3.2-dev) - split off MODULE_MAGIC_NUMBER + * 19980812.2 - add ap_overlap_tables() + * 19980816 (1.3.2-dev) - change proxy to use tables for headers, change + * struct cache_req to typedef cache_req. + * Delete ap_proxy_get_header(), ap_proxy_add_header(), + * ap_proxy_del_header(). Change interface of + * ap_proxy_send_fb() and ap_proxy_cache_error(). + * Add ap_proxy_send_hdr_line() and ap_proxy_bputs2(). + * 19980825 (1.3.2-dev) - renamed is_HTTP_xxx() macros to ap_is_HTTP_xxx() + * 19980825.1 - mod_proxy only (minor change): modified interface of + * ap_proxy_read_headers() and rdcache() to use a + * request_rec* instead of pool* + * (for implementing better error reporting). + * 19980906 (1.3.2-dev) - added ap_md5_binary() + * 19980917 (1.3.2-dev) - bs2000: changed os_set_authfile() to os_set_account() + * 19981108 (1.3.4-dev) - added ap_method_number_of() + * - changed value of M_INVALID and added WebDAV methods + * 19981108.1 - ap_exists_config_define() is now public (minor bump) + * 19981204 - scoreboard changes -- added generation, changed + * exit_generation to running_generation. Somewhere + * earlier vhostrec was added, but it's only safe to use + * as of this rev. See scoreboard.h for documentation. + * 19981211 - DSO changes -- added ap_single_module_configure() + * -- added ap_single_module_init() + * 19981229 - mod_negotiation overhaul -- added ap_make_etag() + * and added vlist_validator to request_rec. + * 19990101 - renamed macro escape_uri() to ap_escape_uri() + * - added MODULE_MAGIC_COOKIE to identify module structs + * 19990103 (1.3.4-dev) - added ap_array_pstrcat() + * 19990105 (1.3.4-dev) - added ap_os_is_filename_valid() + * 19990106 (1.3.4-dev) - Move MODULE_MAGIC_COOKIE to the end of the + * STANDARD_MODULE_STUFF macro so the version + * numbers and file name remain at invariant offsets + * 19990108 (1.3.4-dev) - status_drops_connection -> ap_status_drops_connection + * scan_script_header -> ap_scan_script_header_err + * - reordered entries in request_rec that were waiting + * for a non-binary-compatible release. + * (1.3.5-dev) + * 19990108.1 - add ap_MD5Encode() for MD5 password handling. + * 19990108.2 - add ap_validate_password() and change ap_MD5Encode() + * to use a stronger algorithm. + * 19990108.4 - add ap_size_list_item(), ap_get_list_item(), and + * ap_find_list_item() + * 19990108.5 - added ap_sub_req_method_uri() and added const to the + * definition of method in request_rec. + * 19990108.6 - SIGPIPE is now ignored by the core server. + * 19990108.7 - ap_isxdigit added + * 19990320 - METHODS and M_INVALID symbol values modified + * 19990320.1 - add ap_vrprintf() + * 19990320.2 - add cmd_parms.context, ap_set_config_vectors, + * export ap_add_file_conf + * 19990320.3 - add ap_regexec() and ap_regerror() + * 19990320.4 - add ap_field_noparam() + * 19990320.5 - add local_ip/host to conn_rec for mass-vhost + * 19990320.6 - add ap_SHA1Final(), ap_SHA1Init(), + * ap_SHA1Update_binary(), ap_SHA1Update(), + * ap_base64encode(), ap_base64encode_binary(), + * ap_base64encode_len(), ap_base64decode(), + * ap_base64decode_binary(), ap_base64decode_len(), + * ap_pbase64decode(), ap_pbase64encode() + */ + +#define MODULE_MAGIC_COOKIE 0x41503133UL /* "AP13" */ + +#ifndef MODULE_MAGIC_NUMBER_MAJOR +#define MODULE_MAGIC_NUMBER_MAJOR 19990320 +#endif +#define MODULE_MAGIC_NUMBER_MINOR 6 /* 0...n */ +#define MODULE_MAGIC_NUMBER MODULE_MAGIC_NUMBER_MAJOR /* backward compat */ + +/* Useful for testing for features. */ +#define MODULE_MAGIC_AT_LEAST(major,minor) \ + ((major) > MODULE_MAGIC_NUMBER_MAJOR \ + || ((major) == MODULE_MAGIC_NUMBER_MAJOR \ + && (minor) >= MODULE_MAGIC_NUMBER_MINOR)) + +/* For example, suppose you wish to use the ap_overlap_tables + function. You can do this: + +#if MODULE_MAGIC_AT_LEAST(19980812,2) + ... use ap_overlap_tables() +#else + ... alternative code which doesn't use ap_overlap_tables() +#endif + +*/ + +#endif /* !APACHE_AP_MMN_H */ diff --git a/include/http_config.h b/include/http_config.h new file mode 100644 index 00000000000..e0c29306f8c --- /dev/null +++ b/include/http_config.h @@ -0,0 +1,414 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_HTTP_CONFIG_H +#define APACHE_HTTP_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The central data structures around here... + */ + +/* Command dispatch structures... */ + +/* Note that for all of these except RAW_ARGS, the config routine is + * passed a freshly allocated string which can be modified or stored + * or whatever... it's only necessary to do pstrdup() stuff with + * RAW_ARGS. + */ +enum cmd_how { + RAW_ARGS, /* cmd_func parses command line itself */ + TAKE1, /* one argument only */ + TAKE2, /* two arguments only */ + ITERATE, /* one argument, occuring multiple times + * (e.g., IndexIgnore) + */ + ITERATE2, /* two arguments, 2nd occurs multiple times + * (e.g., AddIcon) + */ + FLAG, /* One of 'On' or 'Off' */ + NO_ARGS, /* No args at all, e.g. */ + TAKE12, /* one or two arguments */ + TAKE3, /* three arguments only */ + TAKE23, /* two or three arguments */ + TAKE123, /* one, two or three arguments */ + TAKE13 /* one or three arguments */ +}; + +typedef struct command_struct { + const char *name; /* Name of this command */ + const char *(*func) (); /* Function invoked */ + void *cmd_data; /* Extra data, for functions which + * implement multiple commands... + */ + int req_override; /* What overrides need to be allowed to + * enable this command. + */ + enum cmd_how args_how; /* What the command expects as arguments */ + + const char *errmsg; /* 'usage' message, in case of syntax errors */ +} command_rec; + +/* The allowed locations for a configuration directive are the union of + * those indicated by each set bit in the req_override mask. + * + * (req_override & RSRC_CONF) => *.conf outside or + * (req_override & ACCESS_CONF) => *.conf inside or + * (req_override & OR_AUTHCFG) => *.conf inside or + * and .htaccess when AllowOverride AuthConfig + * (req_override & OR_LIMIT) => *.conf inside or + * and .htaccess when AllowOverride Limit + * (req_override & OR_OPTIONS) => *.conf anywhere + * and .htaccess when AllowOverride Options + * (req_override & OR_FILEINFO) => *.conf anywhere + * and .htaccess when AllowOverride FileInfo + * (req_override & OR_INDEXES) => *.conf anywhere + * and .htaccess when AllowOverride Indexes + */ +#define OR_NONE 0 +#define OR_LIMIT 1 +#define OR_OPTIONS 2 +#define OR_FILEINFO 4 +#define OR_AUTHCFG 8 +#define OR_INDEXES 16 +#define OR_UNSET 32 +#define ACCESS_CONF 64 +#define RSRC_CONF 128 +#define OR_ALL (OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG|OR_INDEXES) + +/* This can be returned by a function if they don't wish to handle + * a command. Make it something not likely someone will actually use + * as an error code. + */ + +#define DECLINE_CMD "\a\b" + +/* + * This structure is passed to a command which is being invoked, + * to carry a large variety of miscellaneous data which is all of + * use to *somebody*... + */ + +typedef struct { + void *info; /* Argument to command from cmd_table */ + int override; /* Which allow-override bits are set */ + int limited; /* Which methods are ed */ + + configfile_t *config_file; /* Config file structure from pcfg_openfile() */ + + ap_pool *pool; /* Pool to allocate new storage in */ + struct pool *temp_pool; /* Pool for scratch memory; persists during + * configuration, but wiped before the first + * request is served... + */ + server_rec *server; /* Server_rec being configured for */ + char *path; /* If configuring for a directory, + * pathname of that directory. + * NOPE! That's what it meant previous to the + * existance of , and regex + * matching. Now the only usefulness that can + * be derived from this field is whether a command + * is being called in a server context (path == NULL) + * or being called in a dir context (path != NULL). + */ + const command_rec *cmd; /* configuration command */ + const char *end_token; /* end token required to end a nested section */ + void *context; /* per_dir_config vector passed + * to handle_command */ +} cmd_parms; + +/* This structure records the existence of handlers in a module... */ + +typedef struct { + const char *content_type; /* MUST be all lower case */ + int (*handler) (request_rec *); +} handler_rec; + +/* + * Module structures. Just about everything is dispatched through + * these, directly or indirectly (through the command and handler + * tables). + */ + +typedef struct module_struct { + int version; /* API version, *not* module version; + * check that module is compatible with this + * version of the server. + */ + int minor_version; /* API minor version. Provides API feature + * milestones. Not checked during module init + */ + int module_index; /* Index to this modules structures in + * config vectors. + */ + + const char *name; + void *dynamic_load_handle; + + struct module_struct *next; + + unsigned long magic; /* Magic Cookie to identify a module structure; + * It's mainly important for the DSO facility + * (see also mod_so). + */ + + /* init() occurs after config parsing, but before any children are + * forked. + * Modules should not rely on the order in which create_server_config + * and create_dir_config are called. + */ +#ifdef ULTRIX_BRAIN_DEATH + void (*init) (); + void *(*create_dir_config) (); + void *(*merge_dir_config) (); + void *(*create_server_config) (); + void *(*merge_server_config) (); +#else + void (*init) (server_rec *, pool *); + void *(*create_dir_config) (pool *p, char *dir); + void *(*merge_dir_config) (pool *p, void *base_conf, void *new_conf); + void *(*create_server_config) (pool *p, server_rec *s); + void *(*merge_server_config) (pool *p, void *base_conf, void *new_conf); +#endif + + const command_rec *cmds; + const handler_rec *handlers; + + /* Hooks for getting into the middle of server ops... + + * translate_handler --- translate URI to filename + * access_checker --- check access by host address, etc. All of these + * run; if all decline, that's still OK. + * check_user_id --- get and validate user id from the HTTP request + * auth_checker --- see if the user (from check_user_id) is OK *here*. + * If all of *these* decline, the request is rejected + * (as a SERVER_ERROR, since the module which was + * supposed to handle this was configured wrong). + * type_checker --- Determine MIME type of the requested entity; + * sets content_type, _encoding and _language fields. + * logger --- log a transaction. + * post_read_request --- run right after read_request or internal_redirect, + * and not run during any subrequests. + */ + + int (*translate_handler) (request_rec *); + int (*ap_check_user_id) (request_rec *); + int (*auth_checker) (request_rec *); + int (*access_checker) (request_rec *); + int (*type_checker) (request_rec *); + int (*fixer_upper) (request_rec *); + int (*logger) (request_rec *); + int (*header_parser) (request_rec *); + + /* Regardless of the model the server uses for managing "units of + * execution", i.e. multi-process, multi-threaded, hybrids of those, + * there is the concept of a "heavy weight process". That is, a + * process with its own memory space, file spaces, etc. This method, + * child_init, is called once for each heavy-weight process before + * any requests are served. Note that no provision is made yet for + * initialization per light-weight process (i.e. thread). The + * parameters passed here are the same as those passed to the global + * init method above. + */ +#ifdef ULTRIX_BRAIN_DEATH + void (*child_init) (); + void (*child_exit) (); +#else + void (*child_init) (server_rec *, pool *); + void (*child_exit) (server_rec *, pool *); +#endif + int (*post_read_request) (request_rec *); +} module; + +/* Initializer for the first few module slots, which are only + * really set up once we start running. Note that the first two slots + * provide a version check; this should allow us to deal with changes to + * the API. The major number should reflect changes to the API handler table + * itself or removal of functionality. The minor number should reflect + * additions of functionality to the existing API. (the server can detect + * an old-format module, and either handle it back-compatibly, or at least + * signal an error). See src/include/ap_mmn.h for MMN version history. + */ + +#define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER_MAJOR, \ + MODULE_MAGIC_NUMBER_MINOR, \ + -1, \ + __FILE__, \ + NULL, \ + NULL, \ + MODULE_MAGIC_COOKIE + +/* Generic accessors for other modules to get at their own module-specific + * data + */ + +API_EXPORT(void *) ap_get_module_config(void *conf_vector, module *m); +API_EXPORT(void) ap_set_module_config(void *conf_vector, module *m, void *val); + +#define ap_get_module_config(v,m) \ + (((void **)(v))[(m)->module_index]) +#define ap_set_module_config(v,m,val) \ + ((((void **)(v))[(m)->module_index]) = (val)) + +/* Generic command handling function... */ + +API_EXPORT_NONSTD(const char *) ap_set_string_slot(cmd_parms *, char *, char *); +API_EXPORT_NONSTD(const char *) ap_set_string_slot_lower(cmd_parms *, char *, char *); +API_EXPORT_NONSTD(const char *) ap_set_flag_slot(cmd_parms *, char *, int); +API_EXPORT_NONSTD(const char *) ap_set_file_slot(cmd_parms *, char *, char *); + +/* For modules which need to read config files, open logs, etc. ... + * this returns the fname argument if it begins with '/'; otherwise + * it relativizes it wrt server_root. + */ + +API_EXPORT(char *) ap_server_root_relative(pool *p, char *fname); + +/* Finally, the hook for dynamically loading modules in... */ + +API_EXPORT(void) ap_add_module(module *m); +API_EXPORT(void) ap_remove_module(module *m); +API_EXPORT(void) ap_add_loaded_module(module *mod); +API_EXPORT(void) ap_remove_loaded_module(module *mod); +API_EXPORT(int) ap_add_named_module(const char *name); +API_EXPORT(void) ap_clear_module_list(void); +API_EXPORT(const char *) ap_find_module_name(module *m); +API_EXPORT(module *) ap_find_linked_module(const char *name); + +/* for implementing subconfigs and customized config files */ +API_EXPORT(const char *) ap_srm_command_loop(cmd_parms *parms, void *config); + +#ifdef CORE_PRIVATE + +extern API_VAR_EXPORT module *top_module; + +extern module *ap_prelinked_modules[]; +extern module *ap_preloaded_modules[]; +extern API_VAR_EXPORT module **ap_loaded_modules; + +/* For mod_so.c... */ + +void ap_single_module_configure(pool *p, server_rec *s, module *m); + +/* For http_main.c... */ + +server_rec *ap_read_config(pool *conf_pool, pool *temp_pool, char *config_name); +void ap_init_modules(pool *p, server_rec *s); +void ap_child_init_modules(pool *p, server_rec *s); +void ap_child_exit_modules(pool *p, server_rec *s); +void ap_setup_prelinked_modules(void); +void ap_show_directives(void); +void ap_show_modules(void); + +/* For http_request.c... */ + +void *ap_create_request_config(pool *p); +CORE_EXPORT(void *) ap_create_per_dir_config(pool *p); +void *ap_merge_per_dir_configs(pool *p, void *base, void *new); + +/* For http_core.c... ( command and virtual hosts) */ + +int ap_parse_htaccess(void **result, request_rec *r, int override, + const char *path, const char *access_name); + +CORE_EXPORT(const char *) ap_init_virtual_host(pool *p, const char *hostname, + server_rec *main_server, server_rec **); +void ap_process_resource_config(server_rec *s, char *fname, pool *p, pool *ptemp); + +/* ap_check_cmd_context() definitions: */ +API_EXPORT(const char *) ap_check_cmd_context(cmd_parms *cmd, unsigned forbidden); + +/* ap_check_cmd_context(): Forbidden in: */ +#define NOT_IN_VIRTUALHOST 0x01 /* */ +#define NOT_IN_LIMIT 0x02 /* */ +#define NOT_IN_DIRECTORY 0x04 /* */ +#define NOT_IN_LOCATION 0x08 /* */ +#define NOT_IN_FILES 0x10 /* */ +#define NOT_IN_DIR_LOC_FILE (NOT_IN_DIRECTORY|NOT_IN_LOCATION|NOT_IN_FILES) /* //*/ +#define GLOBAL_ONLY (NOT_IN_VIRTUALHOST|NOT_IN_LIMIT|NOT_IN_DIR_LOC_FILE) + + +/* Module-method dispatchers, also for http_request.c */ + +int ap_translate_name(request_rec *); +int ap_check_access(request_rec *); /* check access on non-auth basis */ +int ap_check_user_id(request_rec *); /* obtain valid username from client auth */ +int ap_check_auth(request_rec *); /* check (validated) user is authorized here */ +int ap_find_types(request_rec *); /* identify MIME type */ +int ap_run_fixups(request_rec *); /* poke around for other metainfo, etc.... */ +int ap_invoke_handler(request_rec *); +int ap_log_transaction(request_rec *r); +int ap_header_parse(request_rec *); +int ap_run_post_read_request(request_rec *); + +/* for mod_perl */ + +CORE_EXPORT(const command_rec *) ap_find_command(const char *name, const command_rec *cmds); +CORE_EXPORT(const command_rec *) ap_find_command_in_modules(const char *cmd_name, module **mod); +CORE_EXPORT(void *) ap_set_config_vectors(cmd_parms *parms, void *config, module *mod); +CORE_EXPORT(const char *) ap_handle_command(cmd_parms *parms, void *config, const char *l); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_HTTP_CONFIG_H */ diff --git a/include/http_core.h b/include/http_core.h new file mode 100644 index 00000000000..459fd248f35 --- /dev/null +++ b/include/http_core.h @@ -0,0 +1,310 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_HTTP_CORE_H +#define APACHE_HTTP_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************** + * + * The most basic server code is encapsulated in a single module + * known as the core, which is just *barely* functional enough to + * serve documents, though not terribly well. + * + * Largely for NCSA back-compatibility reasons, the core needs to + * make pieces of its config structures available to other modules. + * The accessors are declared here, along with the interpretation + * of one of them (allow_options). + */ + +#define OPT_NONE 0 +#define OPT_INDEXES 1 +#define OPT_INCLUDES 2 +#define OPT_SYM_LINKS 4 +#define OPT_EXECCGI 8 +#define OPT_UNSET 16 +#define OPT_INCNOEXEC 32 +#define OPT_SYM_OWNER 64 +#define OPT_MULTI 128 +#define OPT_ALL (OPT_INDEXES|OPT_INCLUDES|OPT_SYM_LINKS|OPT_EXECCGI) + +/* options for get_remote_host() */ +/* REMOTE_HOST returns the hostname, or NULL if the hostname + * lookup fails. It will force a DNS lookup according to the + * HostnameLookups setting. + */ +#define REMOTE_HOST (0) + +/* REMOTE_NAME returns the hostname, or the dotted quad if the + * hostname lookup fails. It will force a DNS lookup according + * to the HostnameLookups setting. + */ +#define REMOTE_NAME (1) + +/* REMOTE_NOLOOKUP is like REMOTE_NAME except that a DNS lookup is + * never forced. + */ +#define REMOTE_NOLOOKUP (2) + +/* REMOTE_DOUBLE_REV will always force a DNS lookup, and also force + * a double reverse lookup, regardless of the HostnameLookups + * setting. The result is the (double reverse checked) hostname, + * or NULL if any of the lookups fail. + */ +#define REMOTE_DOUBLE_REV (3) + +#define SATISFY_ALL 0 +#define SATISFY_ANY 1 +#define SATISFY_NOSPEC 2 + +API_EXPORT(int) ap_allow_options (request_rec *); +API_EXPORT(int) ap_allow_overrides (request_rec *); +API_EXPORT(const char *) ap_default_type (request_rec *); +API_EXPORT(const char *) ap_document_root (request_rec *); /* Don't use this! If your request went + * through a Userdir, or something like + * that, it'll screw you. But it's + * back-compatible... + */ +API_EXPORT(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config, int type); +API_EXPORT(const char *) ap_get_remote_logname(request_rec *r); + +/* Used for constructing self-referencing URLs, and things like SERVER_PORT, + * and SERVER_NAME. + */ +API_EXPORT(char *) ap_construct_url(pool *p, const char *uri, request_rec *r); +API_EXPORT(const char *) ap_get_server_name(request_rec *r); +API_EXPORT(unsigned) ap_get_server_port(const request_rec *r); +API_EXPORT(unsigned long) ap_get_limit_req_body(const request_rec *r); +API_EXPORT(void) ap_custom_response(request_rec *r, int status, char *string); +API_EXPORT(int) ap_exists_config_define(char *name); + +/* Authentication stuff. This is one of the places where compatibility + * with the old config files *really* hurts; they don't discriminate at + * all between different authentication schemes, meaning that we need + * to maintain common state for all of them in the core, and make it + * available to the other modules through interfaces. + */ + +typedef struct { + int method_mask; + char *requirement; +} require_line; + +API_EXPORT(const char *) ap_auth_type (request_rec *); +API_EXPORT(const char *) ap_auth_name (request_rec *); +API_EXPORT(int) ap_satisfies (request_rec *r); +API_EXPORT(const array_header *) ap_requires (request_rec *); + +#ifdef WIN32 +/* + * CGI Script stuff for Win32... + */ +typedef enum { eFileTypeUNKNOWN, eFileTypeBIN, eFileTypeEXE16, eFileTypeEXE32, + eFileTypeSCRIPT } file_type_e; +typedef enum { INTERPRETER_SOURCE_UNSET, INTERPRETER_SOURCE_REGISTRY, + INTERPRETER_SOURCE_SHEBANG } interpreter_source_e; +API_EXPORT(file_type_e) ap_get_win32_interpreter(const request_rec *, char **); +#endif + +#ifdef CORE_PRIVATE + +/* + * Core is also unlike other modules in being implemented in more than + * one file... so, data structures are declared here, even though most of + * the code that cares really is in http_core.c. Also, another accessor. + */ + +char *ap_response_code_string (request_rec *r, int error_index); + +extern API_VAR_EXPORT module core_module; + +/* Per-directory configuration */ + +typedef unsigned char allow_options_t; +typedef unsigned char overrides_t; + +typedef struct { + /* path of the directory/regex/etc. see also d_is_fnmatch below */ + char *d; + /* the number of slashes in d */ + unsigned d_components; + + /* If (opts & OPT_UNSET) then no absolute assignment to options has + * been made. + * invariant: (opts_add & opts_remove) == 0 + * Which said another way means that the last relative (options + or -) + * assignment made to each bit is recorded in exactly one of opts_add + * or opts_remove. + */ + allow_options_t opts; + allow_options_t opts_add; + allow_options_t opts_remove; + overrides_t override; + + /* MIME typing --- the core doesn't do anything at all with this, + * but it does know what to slap on a request for a document which + * goes untyped by other mechanisms before it slips out the door... + */ + + char *ap_default_type; + + /* Authentication stuff. Groan... */ + + int satisfy; + char *ap_auth_type; + char *ap_auth_name; + array_header *ap_requires; + + /* Custom response config. These can contain text or a URL to redirect to. + * if response_code_strings is NULL then there are none in the config, + * if it's not null then it's allocated to sizeof(char*)*RESPONSE_CODES. + * This lets us do quick merges in merge_core_dir_configs(). + */ + + char **response_code_strings; + + /* Hostname resolution etc */ +#define HOSTNAME_LOOKUP_OFF 0 +#define HOSTNAME_LOOKUP_ON 1 +#define HOSTNAME_LOOKUP_DOUBLE 2 +#define HOSTNAME_LOOKUP_UNSET 3 + unsigned int hostname_lookups : 4; + + signed int do_rfc1413 : 2; /* See if client is advertising a username? */ + + signed int content_md5 : 2; /* calculate Content-MD5? */ + +#define USE_CANONICAL_NAME_OFF (0) +#define USE_CANONICAL_NAME_ON (1) +#define USE_CANONICAL_NAME_DNS (2) +#define USE_CANONICAL_NAME_UNSET (3) + unsigned use_canonical_name : 2; + + /* since is_fnmatch(conf->d) was being called so frequently in + * directory_walk() and its relatives, this field was created and + * is set to the result of that call. + */ + unsigned d_is_fnmatch : 1; + + /* System Resource Control */ +#ifdef RLIMIT_CPU + struct rlimit *limit_cpu; +#endif +#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS) + struct rlimit *limit_mem; +#endif +#ifdef RLIMIT_NPROC + struct rlimit *limit_nproc; +#endif + unsigned long limit_req_body; /* limit on bytes in request msg body */ + + /* logging options */ + enum { srv_sig_unset, srv_sig_off, srv_sig_on, + srv_sig_withmail } server_signature; + int loglevel; + + /* Access control */ + array_header *sec; + regex_t *r; + +#ifdef WIN32 + /* Where to find interpreter to run scripts */ + interpreter_source_e script_interpreter_source; +#endif + +} core_dir_config; + +/* Per-server core configuration */ + +typedef struct { + +#ifdef GPROF + char *gprof_dir; +#endif + + /* Name translations --- we want the core to be able to do *something* + * so it's at least a minimally functional web server on its own (and + * can be tested that way). But let's keep it to the bare minimum: + */ + char *ap_document_root; + + /* Access control */ + + char *access_name; + array_header *sec; + array_header *sec_url; +} core_server_config; + +/* for http_config.c */ +void ap_core_reorder_directories(pool *, server_rec *); + +/* for mod_perl */ +CORE_EXPORT(void) ap_add_per_dir_conf (server_rec *s, void *dir_config); +CORE_EXPORT(void) ap_add_per_url_conf (server_rec *s, void *url_config); +CORE_EXPORT(void) ap_add_file_conf(core_dir_config *conf, void *url_config); +CORE_EXPORT_NONSTD(const char *) ap_limit_section (cmd_parms *cmd, void *dummy, const char *arg); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_HTTP_CORE_H */ diff --git a/include/http_log.h b/include/http_log.h new file mode 100644 index 00000000000..d9162bd22fb --- /dev/null +++ b/include/http_log.h @@ -0,0 +1,162 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_HTTP_LOG_H +#define APACHE_HTTP_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_SYSLOG +#include + +#define APLOG_EMERG LOG_EMERG /* system is unusable */ +#define APLOG_ALERT LOG_ALERT /* action must be taken immediately */ +#define APLOG_CRIT LOG_CRIT /* critical conditions */ +#define APLOG_ERR LOG_ERR /* error conditions */ +#define APLOG_WARNING LOG_WARNING /* warning conditions */ +#define APLOG_NOTICE LOG_NOTICE /* normal but significant condition */ +#define APLOG_INFO LOG_INFO /* informational */ +#define APLOG_DEBUG LOG_DEBUG /* debug-level messages */ + +#define APLOG_LEVELMASK LOG_PRIMASK /* mask off the level value */ + +#else + +#define APLOG_EMERG 0 /* system is unusable */ +#define APLOG_ALERT 1 /* action must be taken immediately */ +#define APLOG_CRIT 2 /* critical conditions */ +#define APLOG_ERR 3 /* error conditions */ +#define APLOG_WARNING 4 /* warning conditions */ +#define APLOG_NOTICE 5 /* normal but significant condition */ +#define APLOG_INFO 6 /* informational */ +#define APLOG_DEBUG 7 /* debug-level messages */ + +#define APLOG_LEVELMASK 7 /* mask off the level value */ + +#endif + +#define APLOG_NOERRNO (APLOG_LEVELMASK + 1) +#ifdef WIN32 +/* Set to indicate that error msg should come from Win32's GetLastError(), + * not errno. */ +#define APLOG_WIN32ERROR ((APLOG_LEVELMASK+1) * 2) +#endif + +#ifndef DEFAULT_LOGLEVEL +#define DEFAULT_LOGLEVEL APLOG_WARNING +#endif + +#define APLOG_MARK __FILE__,__LINE__ + +void ap_open_logs (server_rec *, pool *p); + +/* The two primary logging functions, ap_log_error and ap_log_rerror, + * use a printf style format string to build the log message. It is + * VERY IMPORTANT that you not include any raw data from the network, + * such as the request-URI or request header fields, within the format + * string. Doing so makes the server vulnerable to a denial-of-service + * attack and other messy behavior. Instead, use a simple format string + * like "%s", followed by the string containing the untrusted data. + */ +API_EXPORT(void) ap_log_error(const char *file, int line, int level, + const server_rec *s, const char *fmt, ...) + __attribute__((format(printf,5,6))); +API_EXPORT(void) ap_log_rerror(const char *file, int line, int level, + const request_rec *s, const char *fmt, ...) + __attribute__((format(printf,5,6))); +API_EXPORT(void) ap_error_log2stderr (server_rec *); + +void ap_log_pid (pool *p, char *fname); +/* These are for legacy code, new code should use ap_log_error, + * or ap_log_rerror. + */ +API_EXPORT(void) ap_log_error_old(const char *err, server_rec *s); +API_EXPORT(void) ap_log_unixerr(const char *routine, const char *file, + const char *msg, server_rec *s); +API_EXPORT(void) ap_log_printf(const server_rec *s, const char *fmt, ...) + __attribute__((format(printf,2,3))); +API_EXPORT(void) ap_log_reason(const char *reason, const char *fname, + request_rec *r); + +typedef struct piped_log { + pool *p; +#ifndef NO_RELIABLE_PIPED_LOGS + char *program; + int pid; + int fds[2]; +#else + FILE *write_f; +#endif +} piped_log; + +API_EXPORT(piped_log *) ap_open_piped_log (pool *p, const char *program); +API_EXPORT(void) ap_close_piped_log (piped_log *); +#ifndef NO_RELIABLE_PIPED_LOGS +#define ap_piped_log_read_fd(pl) ((pl)->fds[0]) +#define ap_piped_log_write_fd(pl) ((pl)->fds[1]) +#else +#define ap_piped_log_read_fd(pl) (-1) +#define ap_piped_log_write_fd(pl) (fileno((pl)->write_f)) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_HTTP_LOG_H */ diff --git a/include/http_main.h b/include/http_main.h new file mode 100644 index 00000000000..a0d014de785 --- /dev/null +++ b/include/http_main.h @@ -0,0 +1,174 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_HTTP_MAIN_H +#define APACHE_HTTP_MAIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Routines in http_main.c which other code --- in particular modules --- + * may want to call. Right now, that's limited to timeout handling. + * There are two functions which modules can call to trigger a timeout + * (with the per-virtual-server timeout duration); these are hard_timeout + * and soft_timeout. + * + * The difference between the two is what happens when the timeout + * expires (or earlier than that, if the client connection aborts) --- + * a soft_timeout just puts the connection to the client in an + * "aborted" state, which will cause http_protocol.c to stop trying to + * talk to the client, but otherwise allows the code to continue normally. + * hard_timeout(), by contrast, logs the request, and then aborts it + * completely --- longjmp()ing out to the accept() loop in http_main. + * Any resources tied into the request's resource pool will be cleaned up; + * everything that isn't will leak. + * + * soft_timeout() is recommended as a general rule, because it gives your + * code a chance to clean up. However, hard_timeout() may be the most + * convenient way of dealing with timeouts waiting for some external + * resource other than the client, if you can live with the restrictions. + * + * (When a hard timeout is in scope, critical sections can be guarded + * with block_alarms() and unblock_alarms() --- these are declared in + * alloc.c because they are most often used in conjunction with + * routines to allocate something or other, to make sure that the + * cleanup does get registered before any alarm is allowed to happen + * which might require it to be cleaned up; they * are, however, + * implemented in http_main.c). + * + * NOTE! It's not "fair" for a hard_timeout to be in scope through calls + * across modules. Your module code really has no idea what other modules may + * be present in the server, and they may not take too kindly to having a + * longjmp() happen -- it could result in corrupted state. Heck they may not + * even take to kindly to a soft_timeout()... because it can cause EINTR to + * happen on pretty much any syscall, and unless all the libraries and modules + * in use are known to deal well with EINTR it could cause corruption as well. + * But things are likely to do much better with a soft_timeout in scope than a + * hard_timeout. + * + * A module MAY NOT use a hard_timeout() across * sub_req_lookup_xxx() + * functions, or across run_sub_request() functions. A module SHOULD NOT use a + * soft_timeout() in either of these cases, but sometimes there's just no + * choice. + * + * kill_timeout() will disarm either variety of timeout. + * + * reset_timeout() resets the timeout in progress. + */ + +void ap_start_shutdown(void); +void ap_start_restart(int); +API_EXPORT(void) ap_hard_timeout(char *, request_rec *); +void ap_keepalive_timeout(char *, request_rec *); +API_EXPORT(void) ap_soft_timeout(char *, request_rec *); +API_EXPORT(void) ap_kill_timeout(request_rec *); +API_EXPORT(void) ap_reset_timeout(request_rec *); + +API_EXPORT(void) ap_child_terminate(request_rec *r); +API_EXPORT(void) ap_sync_scoreboard_image(void); +int ap_update_child_status(int child_num, int status, request_rec *r); +void ap_time_process_request(int child_num, int status); +unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x); +API_EXPORT(int) ap_check_alarm(void); + +#ifndef NO_OTHER_CHILD +/* + * register an other_child -- a child which the main loop keeps track of + * and knows it is different than the rest of the scoreboard. + * + * pid is the pid of the child. + * + * maintenance is a function that is invoked with a reason, the data + * pointer passed here, and when appropriate a status result from waitpid(). + * + * write_fd is an fd that is probed for writing by select() if it is ever + * unwritable, then maintenance is invoked with reason OC_REASON_UNWRITABLE. + * This is useful for log pipe children, to know when they've blocked. To + * disable this feature, use -1 for write_fd. + */ +API_EXPORT(void) ap_register_other_child(int pid, + void (*maintenance) (int reason, void *data, ap_wait_t status), void *data, + int write_fd); +#define OC_REASON_DEATH 0 /* child has died, caller must call + * unregister still */ +#define OC_REASON_UNWRITABLE 1 /* write_fd is unwritable */ +#define OC_REASON_RESTART 2 /* a restart is occuring, perform + * any necessary cleanup (including + * sending a special signal to child) + */ +#define OC_REASON_UNREGISTER 3 /* unregister has been called, do + * whatever is necessary (including + * kill the child) */ +#define OC_REASON_LOST 4 /* somehow the child exited without + * us knowing ... buggy os? */ + +/* + * unregister an other_child. Note that the data pointer is used here, and + * is assumed to be unique per other_child. This is because the pid and + * write_fd are possibly killed off separately. + */ +API_EXPORT(void) ap_unregister_other_child(void *data); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_HTTP_MAIN_H */ diff --git a/include/http_protocol.h b/include/http_protocol.h new file mode 100644 index 00000000000..3824c1aba06 --- /dev/null +++ b/include/http_protocol.h @@ -0,0 +1,223 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_HTTP_PROTOCOL_H +#define APACHE_HTTP_PROTOCOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Prototypes for routines which either talk directly back to the user, + * or control the ones that eventually do. + */ + +/* Read a request and fill in the fields. */ + +request_rec *ap_read_request(conn_rec *c); + +/* Send a single HTTP header field */ + +API_EXPORT_NONSTD(int) ap_send_header_field(request_rec *r, const char *fieldname, + const char *fieldval); + +/* Send the minimal part of an HTTP response header... but modules should be + * very careful about using this, and should prefer ap_send_http_header(). + * Much of the HTTP/1.1 implementation correctness depends on code in + * ap_send_http_header(). + */ +API_EXPORT(void) ap_basic_http_header(request_rec *r); + +/* Send the Status-Line and header fields for HTTP response */ + +API_EXPORT(void) ap_send_http_header(request_rec *l); + +/* Send the response to special method requests */ + +API_EXPORT(int) ap_send_http_trace(request_rec *r); +int ap_send_http_options(request_rec *r); + +/* Finish up stuff after a request */ + +API_EXPORT(void) ap_finalize_request_protocol(request_rec *r); + +/* Send error back to client... last arg indicates error status in case + * we get an error in the process of trying to deal with an ErrorDocument + * to handle some other error. In that case, we print the default report + * for the first thing that went wrong, and more briefly report on the + * problem with the ErrorDocument. + */ + +void ap_send_error_response(request_rec *r, int recursive_error); + +/* Set last modified header line from the lastmod date of the associated file. + * Also, set content length. + * + * May return an error status, typically USE_LOCAL_COPY (that when the + * permit_cache argument is set to one). + */ + +API_EXPORT(int) ap_set_content_length(request_rec *r, long length); +API_EXPORT(int) ap_set_keepalive(request_rec *r); +API_EXPORT(time_t) ap_rationalize_mtime(request_rec *r, time_t mtime); +API_EXPORT(char *) ap_make_etag(request_rec *r, int force_weak); +API_EXPORT(void) ap_set_etag(request_rec *r); +API_EXPORT(void) ap_set_last_modified(request_rec *r); +API_EXPORT(int) ap_meets_conditions(request_rec *r); + +/* Other ways to send stuff at the client. All of these keep track + * of bytes_sent automatically. This indirection is intended to make + * it a little more painless to slide things like HTTP-NG packetization + * underneath the main body of the code later. In the meantime, it lets + * us centralize a bit of accounting (bytes_sent). + * + * These also return the number of bytes written by the call. + * They should only be called with a timeout registered, for obvious reaasons. + * (Ditto the send_header stuff). + */ + +API_EXPORT(long) ap_send_fd(FILE *f, request_rec *r); +API_EXPORT(long) ap_send_fd_length(FILE *f, request_rec *r, long length); + +API_EXPORT(long) ap_send_fb(BUFF *f, request_rec *r); +API_EXPORT(long) ap_send_fb_length(BUFF *f, request_rec *r, long length); + +API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset, + size_t length); + +/* Hmmm... could macrofy these for now, and maybe forever, though the + * definitions of the macros would get a whole lot hairier. + */ + +API_EXPORT(int) ap_rputc(int c, request_rec *r); +API_EXPORT(int) ap_rputs(const char *str, request_rec *r); +API_EXPORT(int) ap_rwrite(const void *buf, int nbyte, request_rec *r); +API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r,...); +API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list vlist); +API_EXPORT_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt,...) + __attribute__((format(printf,2,3))); +API_EXPORT(int) ap_rflush(request_rec *r); + +/* + * Index used in custom_responses array for a specific error code + * (only use outside protocol.c is in getting them configured). + */ + +API_EXPORT(int) ap_index_of_response(int status); + +/* Reading a block of data from the client connection (e.g., POST arg) */ + +API_EXPORT(int) ap_setup_client_block(request_rec *r, int read_policy); +API_EXPORT(int) ap_should_client_block(request_rec *r); +API_EXPORT(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz); +API_EXPORT(int) ap_discard_request_body(request_rec *r); + +/* Sending a byterange */ + +API_EXPORT(int) ap_set_byterange(request_rec *r); +API_EXPORT(int) ap_each_byterange(request_rec *r, long *offset, long *length); + +/* Support for the Basic authentication protocol. Note that there's + * nothing that prevents these from being in mod_auth.c, except that other + * modules which wanted to provide their own variants on finding users and + * passwords for Basic auth (a fairly common request) would then require + * mod_auth to be loaded or they wouldn't work. + * + * get_basic_auth_pw returns 0 (OK) if it set the 'pw' argument (and assured + * a correct value in r->connection->user); otherwise it returns an error + * code, either SERVER_ERROR if things are really confused, AUTH_REQUIRED + * if no authentication at all seemed to be in use, or DECLINED if there + * was authentication but it wasn't Basic (in which case, the caller should + * presumably decline as well). + * + * note_basic_auth_failure arranges for the right stuff to be scribbled on + * the HTTP return so that the client knows how to authenticate itself the + * next time. As does note_digest_auth_failure for Digest auth. + * + * note_auth_failure does the same thing, but will call the correct one + * based on the authentication type in use. + * + */ + +API_EXPORT(void) ap_note_auth_failure(request_rec *r); +API_EXPORT(void) ap_note_basic_auth_failure(request_rec *r); +API_EXPORT(void) ap_note_digest_auth_failure(request_rec *r); +API_EXPORT(int) ap_get_basic_auth_pw(request_rec *r, const char **pw); + +/* + * Setting up the protocol fields for subsidiary requests... + * Also, a wrapup function to keep the internal accounting straight. + */ + +void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r); +void ap_finalize_sub_req_protocol(request_rec *sub_r); + +/* This is also useful for putting sub_reqs and internal_redirects together */ + +CORE_EXPORT(void) ap_parse_uri(request_rec *r, const char *uri); + +/* Get the method number associated with the given string, assumed to + * contain an HTTP method. Returns M_INVALID if not recognized. + */ +API_EXPORT(int) ap_method_number_of(const char *method); + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_HTTP_PROTOCOL_H */ diff --git a/include/http_request.h b/include/http_request.h new file mode 100644 index 00000000000..4d83c543d6a --- /dev/null +++ b/include/http_request.h @@ -0,0 +1,117 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_HTTP_REQUEST_H +#define APACHE_HTTP_REQUEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* http_request.c is the code which handles the main line of request + * processing, once a request has been read in (finding the right per- + * directory configuration, building it if necessary, and calling all + * the module dispatch functions in the right order). + * + * The pieces here which are public to the modules, allow them to learn + * how the server would handle some other file or URI, or perhaps even + * direct the server to serve that other file instead of the one the + * client requested directly. + * + * There are two ways to do that. The first is the sub_request mechanism, + * which handles looking up files and URIs as adjuncts to some other + * request (e.g., directory entries for multiviews and directory listings); + * the lookup functions stop short of actually running the request, but + * (e.g., for includes), a module may call for the request to be run + * by calling run_sub_req. The space allocated to create sub_reqs can be + * reclaimed by calling destroy_sub_req --- be sure to copy anything you care + * about which was allocated in its pool elsewhere before doing this. + */ + +API_EXPORT(request_rec *) ap_sub_req_lookup_uri(const char *new_file, + const request_rec *r); +API_EXPORT(request_rec *) ap_sub_req_lookup_file(const char *new_file, + const request_rec *r); +API_EXPORT(request_rec *) ap_sub_req_method_uri(const char *method, + const char *new_file, + const request_rec *r); +API_EXPORT(int) ap_run_sub_req(request_rec *r); +API_EXPORT(void) ap_destroy_sub_req(request_rec *r); + +/* + * Then there's the case that you want some other request to be served + * as the top-level request INSTEAD of what the client requested directly. + * If so, call this from a handler, and then immediately return OK. + */ + +API_EXPORT(void) ap_internal_redirect(const char *new_uri, request_rec *); +API_EXPORT(void) ap_internal_redirect_handler(const char *new_uri, request_rec *); +API_EXPORT(int) ap_some_auth_required(request_rec *r); +API_EXPORT(int) ap_is_initial_req(request_rec *r); +API_EXPORT(time_t) ap_update_mtime(request_rec *r, time_t dependency_mtime); + +#ifdef CORE_PRIVATE +/* Function called by main.c to handle first-level request */ +void ap_process_request(request_rec *); +API_EXPORT(void) ap_die(int type, request_rec *r); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_HTTP_REQUEST_H */ diff --git a/include/http_vhost.h b/include/http_vhost.h new file mode 100644 index 00000000000..acdb6e2f426 --- /dev/null +++ b/include/http_vhost.h @@ -0,0 +1,95 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_HTTP_VHOST_H +#define APACHE_HTTP_VHOST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* called before any config is read */ +void ap_init_vhost_config(pool *p); + +/* called after the config has been read */ +void ap_fini_vhost_config(pool *p, server_rec *main_server); + +/* handle addresses in statement */ +const char *ap_parse_vhost_addrs(pool *p, const char *hostname, server_rec *s); + +/* handle NameVirtualHost directive */ +const char *ap_set_name_virtual_host (cmd_parms *cmd, void *dummy, char *arg); + +/* given an ip address only, give our best guess as to what vhost it is */ +void ap_update_vhost_given_ip(conn_rec *conn); + +/* The above is never enough, and this is always called after the headers + * have been read. It may change r->server. + */ +void ap_update_vhost_from_headers(request_rec *r); + +/* return 1 if the host:port matches any of the aliases of r->server + * return 0 otherwise + */ +API_EXPORT(int) ap_matches_request_vhost(request_rec *r, const char *host, + unsigned port); + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_HTTP_VHOST_H */ diff --git a/include/httpd.h b/include/httpd.h new file mode 100644 index 00000000000..7f19d99cc52 --- /dev/null +++ b/include/httpd.h @@ -0,0 +1,1167 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_HTTPD_H +#define APACHE_HTTPD_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * httpd.h: header for simple (ha! not anymore) http daemon + */ + +/* Headers in which EVERYONE has an interest... */ + +#include "ap_config.h" +#include "alloc.h" +#include "buff.h" +#include "ap.h" + +/* ----------------------------- config dir ------------------------------ */ + +/* Define this to be the default server home dir. Most things later in this + * file with a relative pathname will have this added. + */ +#ifndef HTTPD_ROOT +#ifdef OS2 +/* Set default for OS/2 file system */ +#define HTTPD_ROOT "/os2httpd" +#elif defined(WIN32) +/* Set default for Windows file system */ +#define HTTPD_ROOT "/apache" +#elif defined(BEOS) +#define HTTPD_ROOT "/boot/home/apache" +#else +#define HTTPD_ROOT "/usr/local/apache" +#endif +#endif /* HTTPD_ROOT */ + +/* Default location of documents. Can be overridden by the DocumentRoot + * directive. + */ +#ifndef DOCUMENT_LOCATION +#ifdef OS2 +/* Set default for OS/2 file system */ +#define DOCUMENT_LOCATION HTTPD_ROOT "/docs" +#else +#define DOCUMENT_LOCATION HTTPD_ROOT "/htdocs" +#endif +#endif /* DOCUMENT_LOCATION */ + +/* Max. number of dynamically loaded modules */ +#ifndef DYNAMIC_MODULE_LIMIT +#define DYNAMIC_MODULE_LIMIT 64 +#endif + +/* Default administrator's address */ +#define DEFAULT_ADMIN "[no address given]" + +/* The target name of the installed Apache */ +#ifndef TARGET +#define TARGET "httpd" +#endif + +/* + * --------- You shouldn't have to edit anything below this line ---------- + * + * Any modifications to any defaults not defined above should be done in the + * respective config. file. + * + */ + + +/* -- Internal representation for a HTTP protocol number, e.g., HTTP/1.1 -- */ + +#define HTTP_VERSION(major,minor) (1000*(major)+(minor)) +#define HTTP_VERSION_MAJOR(number) ((number)/1000) +#define HTTP_VERSION_MINOR(number) ((number)%1000) + + +/* -------------- Port number for server running standalone --------------- */ + +#define DEFAULT_HTTP_PORT 80 +#define DEFAULT_HTTPS_PORT 443 +#define ap_is_default_port(port,r) ((port) == ap_default_port(r)) +#define ap_http_method(r) "http" +#define ap_default_port(r) DEFAULT_HTTP_PORT + +/* --------- Default user name and group name running standalone ---------- */ +/* --- These may be specified as numbers by placing a # before a number --- */ + +#ifndef DEFAULT_USER +#define DEFAULT_USER "#-1" +#endif +#ifndef DEFAULT_GROUP +#define DEFAULT_GROUP "#-1" +#endif + +/* The name of the log files */ +#ifndef DEFAULT_XFERLOG +#if defined(OS2) || defined(WIN32) +#define DEFAULT_XFERLOG "logs/access.log" +#else +#define DEFAULT_XFERLOG "logs/access_log" +#endif +#endif /* DEFAULT_XFERLOG */ + +#ifndef DEFAULT_ERRORLOG +#if defined(OS2) || defined(WIN32) +#define DEFAULT_ERRORLOG "logs/error.log" +#else +#define DEFAULT_ERRORLOG "logs/error_log" +#endif +#endif /* DEFAULT_ERRORLOG */ + +#ifndef DEFAULT_PIDLOG +#define DEFAULT_PIDLOG "logs/httpd.pid" +#endif +#ifndef DEFAULT_SCOREBOARD +#define DEFAULT_SCOREBOARD "logs/apache_runtime_status" +#endif +#ifndef DEFAULT_LOCKFILE +#define DEFAULT_LOCKFILE "logs/accept.lock" +#endif + +/* Define this to be what your HTML directory content files are called */ +#ifndef DEFAULT_INDEX +#define DEFAULT_INDEX "index.html" +#endif + +/* Define this to 1 if you want fancy indexing, 0 otherwise */ +#ifndef DEFAULT_INDEXING +#define DEFAULT_INDEXING 0 +#endif + +/* Define this to be what type you'd like returned for files with unknown */ +/* suffixes. MUST be all lower case. */ +#ifndef DEFAULT_CONTENT_TYPE +#define DEFAULT_CONTENT_TYPE "text/plain" +#endif + +/* Define this to be what your per-directory security files are called */ +#ifndef DEFAULT_ACCESS_FNAME +#ifdef OS2 +/* Set default for OS/2 file system */ +#define DEFAULT_ACCESS_FNAME "htaccess" +#else +#define DEFAULT_ACCESS_FNAME ".htaccess" +#endif +#endif /* DEFAULT_ACCESS_FNAME */ + +/* The name of the server config file */ +#ifndef SERVER_CONFIG_FILE +#define SERVER_CONFIG_FILE "conf/httpd.conf" +#endif + +/* The name of the document config file */ +#ifndef RESOURCE_CONFIG_FILE +#define RESOURCE_CONFIG_FILE "conf/srm.conf" +#endif + +/* The name of the MIME types file */ +#ifndef TYPES_CONFIG_FILE +#define TYPES_CONFIG_FILE "conf/mime.types" +#endif + +/* The name of the access file */ +#ifndef ACCESS_CONFIG_FILE +#define ACCESS_CONFIG_FILE "conf/access.conf" +#endif + +/* Whether we should enable rfc1413 identity checking */ +#ifndef DEFAULT_RFC1413 +#define DEFAULT_RFC1413 0 +#endif +/* The default directory in user's home dir */ +#ifndef DEFAULT_USER_DIR +#define DEFAULT_USER_DIR "public_html" +#endif + +/* The default path for CGI scripts if none is currently set */ +#ifndef DEFAULT_PATH +#define DEFAULT_PATH "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin" +#endif + +/* The path to the shell interpreter, for parsed docs */ +#ifndef SHELL_PATH +#if defined(OS2) || defined(WIN32) +/* Set default for OS/2 and Windows file system */ +#define SHELL_PATH "CMD.EXE" +#else +#define SHELL_PATH "/bin/sh" +#endif +#endif /* SHELL_PATH */ + +/* The path to the suExec wrapper, can be overridden in Configuration */ +#ifndef SUEXEC_BIN +#define SUEXEC_BIN HTTPD_ROOT "/sbin/suexec" +#endif + +/* The default string lengths */ +#define MAX_STRING_LEN HUGE_STRING_LEN +#define HUGE_STRING_LEN 8192 + +/* The timeout for waiting for messages */ +#ifndef DEFAULT_TIMEOUT +#define DEFAULT_TIMEOUT 300 +#endif + +/* The timeout for waiting for keepalive timeout until next request */ +#ifndef DEFAULT_KEEPALIVE_TIMEOUT +#define DEFAULT_KEEPALIVE_TIMEOUT 15 +#endif + +/* The number of requests to entertain per connection */ +#ifndef DEFAULT_KEEPALIVE +#define DEFAULT_KEEPALIVE 100 +#endif + +/* The size of the server's internal read-write buffers */ +#define IOBUFSIZE 8192 + +/* Number of servers to spawn off by default --- also, if fewer than + * this free when the caretaker checks, it will spawn more. + */ +#ifndef DEFAULT_START_DAEMON +#define DEFAULT_START_DAEMON 5 +#endif + +/* Maximum number of *free* server processes --- more than this, and + * they will die off. + */ + +#ifndef DEFAULT_MAX_FREE_DAEMON +#define DEFAULT_MAX_FREE_DAEMON 10 +#endif + +/* Minimum --- fewer than this, and more will be created */ + +#ifndef DEFAULT_MIN_FREE_DAEMON +#define DEFAULT_MIN_FREE_DAEMON 5 +#endif + +/* Limit on the total --- clients will be locked out if more servers than + * this are needed. It is intended solely to keep the server from crashing + * when things get out of hand. + * + * We keep a hard maximum number of servers, for two reasons --- first off, + * in case something goes seriously wrong, we want to stop the fork bomb + * short of actually crashing the machine we're running on by filling some + * kernel table. Secondly, it keeps the size of the scoreboard file small + * enough that we can read the whole thing without worrying too much about + * the overhead. + */ +#ifndef HARD_SERVER_LIMIT +#ifdef WIN32 +#define HARD_SERVER_LIMIT 1024 +#else +#define HARD_SERVER_LIMIT 256 +#endif +#endif + +/* + * Special Apache error codes. These are basically used + * in http_main.c so we can keep track of various errors. + * + * APEXIT_OK: + * A normal exit + * APEXIT_INIT: + * A fatal error arising during the server's init sequence + * APEXIT_CHILDINIT: + * The child died during it's init sequence + * APEXIT_CHILDFATAL: + * A fatal error, resulting in the whole server aborting. + * If a child exits with this error, the parent process + * considers this a server-wide fatal error and aborts. + * + */ +#define APEXIT_OK 0x0 +#define APEXIT_INIT 0x2 +#define APEXIT_CHILDINIT 0x3 +#define APEXIT_CHILDFATAL 0xf + +/* + * (Unix, OS/2 only) + * Interval, in microseconds, between scoreboard maintenance. During + * each scoreboard maintenance cycle the parent decides if it needs to + * spawn a new child (to meet MinSpareServers requirements), or kill off + * a child (to meet MaxSpareServers requirements). It will only spawn or + * kill one child per cycle. Setting this too low will chew cpu. The + * default is probably sufficient for everyone. But some people may want + * to raise this on servers which aren't dedicated to httpd and where they + * don't like the httpd waking up each second to see what's going on. + */ +#ifndef SCOREBOARD_MAINTENANCE_INTERVAL +#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000 +#endif + +/* Number of requests to try to handle in a single process. If <= 0, + * the children don't die off. That's the default here, since I'm still + * interested in finding and stanching leaks. + */ + +#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD +#define DEFAULT_MAX_REQUESTS_PER_CHILD 0 +#endif + +#ifndef DEFAULT_THREADS_PER_CHILD +#define DEFAULT_THREADS_PER_CHILD 50 +#endif +#ifndef DEFAULT_EXCESS_REQUESTS_PER_CHILD +#define DEFAULT_EXCESS_REQUESTS_PER_CHILD 0 +#endif + +/* The maximum length of the queue of pending connections, as defined + * by listen(2). Under some systems, it should be increased if you + * are experiencing a heavy TCP SYN flood attack. + * + * It defaults to 511 instead of 512 because some systems store it + * as an 8-bit datatype; 512 truncated to 8-bits is 0, while 511 is + * 255 when truncated. + */ + +#ifndef DEFAULT_LISTENBACKLOG +#define DEFAULT_LISTENBACKLOG 511 +#endif + +/* Limits on the size of various request items. These limits primarily + * exist to prevent simple denial-of-service attacks on a server based + * on misuse of the protocol. The recommended values will depend on the + * nature of the server resources -- CGI scripts and database backends + * might require large values, but most servers could get by with much + * smaller limits than we use below. The request message body size can + * be limited by the per-dir config directive LimitRequestBody. + * + * Internal buffer sizes are two bytes more than the DEFAULT_LIMIT_REQUEST_LINE + * and DEFAULT_LIMIT_REQUEST_FIELDSIZE below, which explains the 8190. + * These two limits can be lowered (but not raised) by the server config + * directives LimitRequestLine and LimitRequestFieldsize, respectively. + * + * DEFAULT_LIMIT_REQUEST_FIELDS can be modified or disabled (set = 0) by + * the server config directive LimitRequestFields. + */ +#ifndef DEFAULT_LIMIT_REQUEST_LINE +#define DEFAULT_LIMIT_REQUEST_LINE 8190 +#endif /* default limit on bytes in Request-Line (Method+URI+HTTP-version) */ +#ifndef DEFAULT_LIMIT_REQUEST_FIELDSIZE +#define DEFAULT_LIMIT_REQUEST_FIELDSIZE 8190 +#endif /* default limit on bytes in any one header field */ +#ifndef DEFAULT_LIMIT_REQUEST_FIELDS +#define DEFAULT_LIMIT_REQUEST_FIELDS 100 +#endif /* default limit on number of request header fields */ + +/* + * The below defines the base string of the Server: header. Additional + * tokens can be added via the ap_add_version_component() API call. + * + * The tokens are listed in order of their significance for identifying the + * application. + * + * "Product tokens should be short and to the point -- use of them for + * advertizing or other non-essential information is explicitly forbidden." + * + * Example: "Apache/1.1.0 MrWidget/0.1-alpha" + */ + +#define SERVER_BASEVERSION "Apache/1.3.9" /* SEE COMMENTS ABOVE */ +#define SERVER_VERSION SERVER_BASEVERSION +enum server_token_type { + SrvTk_MIN, /* eg: Apache/1.3.0 */ + SrvTk_OS, /* eg: Apache/1.3.0 (UNIX) */ + SrvTk_FULL /* eg: Apache/1.3.0 (UNIX) PHP/3.0 FooBar/1.2b */ +}; + +API_EXPORT(const char *) ap_get_server_version(void); +API_EXPORT(void) ap_add_version_component(const char *component); +API_EXPORT(const char *) ap_get_server_built(void); + +/* Numeric release version identifier: MMNNFFRBB: major minor fix final beta + * Always increases along the same track as the source branch. + * For example, Apache 1.4.2 would be '10402100', 2.5b7 would be '20500007'. + */ +#define APACHE_RELEASE 10309100 + +#define SERVER_PROTOCOL "HTTP/1.1" +#ifndef SERVER_SUPPORT +#define SERVER_SUPPORT "http://www.apache.org/" +#endif + +#define DECLINED -1 /* Module declines to handle */ +#define DONE -2 /* Module has served the response completely + * - it's safe to die() with no more output + */ +#define OK 0 /* Module has handled this stage. */ + + +/* ----------------------- HTTP Status Codes ------------------------- */ + +/* The size of the static array in http_protocol.c for storing + * all of the potential response status-lines (a sparse table). + * A future version should dynamically generate the table at startup. + */ +#define RESPONSE_CODES 55 + +#define HTTP_CONTINUE 100 +#define HTTP_SWITCHING_PROTOCOLS 101 +#define HTTP_PROCESSING 102 +#define HTTP_OK 200 +#define HTTP_CREATED 201 +#define HTTP_ACCEPTED 202 +#define HTTP_NON_AUTHORITATIVE 203 +#define HTTP_NO_CONTENT 204 +#define HTTP_RESET_CONTENT 205 +#define HTTP_PARTIAL_CONTENT 206 +#define HTTP_MULTI_STATUS 207 +#define HTTP_MULTIPLE_CHOICES 300 +#define HTTP_MOVED_PERMANENTLY 301 +#define HTTP_MOVED_TEMPORARILY 302 +#define HTTP_SEE_OTHER 303 +#define HTTP_NOT_MODIFIED 304 +#define HTTP_USE_PROXY 305 +#define HTTP_TEMPORARY_REDIRECT 307 +#define HTTP_BAD_REQUEST 400 +#define HTTP_UNAUTHORIZED 401 +#define HTTP_PAYMENT_REQUIRED 402 +#define HTTP_FORBIDDEN 403 +#define HTTP_NOT_FOUND 404 +#define HTTP_METHOD_NOT_ALLOWED 405 +#define HTTP_NOT_ACCEPTABLE 406 +#define HTTP_PROXY_AUTHENTICATION_REQUIRED 407 +#define HTTP_REQUEST_TIME_OUT 408 +#define HTTP_CONFLICT 409 +#define HTTP_GONE 410 +#define HTTP_LENGTH_REQUIRED 411 +#define HTTP_PRECONDITION_FAILED 412 +#define HTTP_REQUEST_ENTITY_TOO_LARGE 413 +#define HTTP_REQUEST_URI_TOO_LARGE 414 +#define HTTP_UNSUPPORTED_MEDIA_TYPE 415 +#define HTTP_RANGE_NOT_SATISFIABLE 416 +#define HTTP_EXPECTATION_FAILED 417 +#define HTTP_UNPROCESSABLE_ENTITY 422 +#define HTTP_LOCKED 423 +#define HTTP_FAILED_DEPENDENCY 424 +#define HTTP_INTERNAL_SERVER_ERROR 500 +#define HTTP_NOT_IMPLEMENTED 501 +#define HTTP_BAD_GATEWAY 502 +#define HTTP_SERVICE_UNAVAILABLE 503 +#define HTTP_GATEWAY_TIME_OUT 504 +#define HTTP_VERSION_NOT_SUPPORTED 505 +#define HTTP_VARIANT_ALSO_VARIES 506 +#define HTTP_INSUFFICIENT_STORAGE 507 +#define HTTP_NOT_EXTENDED 510 + +#define DOCUMENT_FOLLOWS HTTP_OK +#define PARTIAL_CONTENT HTTP_PARTIAL_CONTENT +#define MULTIPLE_CHOICES HTTP_MULTIPLE_CHOICES +#define MOVED HTTP_MOVED_PERMANENTLY +#define REDIRECT HTTP_MOVED_TEMPORARILY +#define USE_LOCAL_COPY HTTP_NOT_MODIFIED +#define BAD_REQUEST HTTP_BAD_REQUEST +#define AUTH_REQUIRED HTTP_UNAUTHORIZED +#define FORBIDDEN HTTP_FORBIDDEN +#define NOT_FOUND HTTP_NOT_FOUND +#define METHOD_NOT_ALLOWED HTTP_METHOD_NOT_ALLOWED +#define NOT_ACCEPTABLE HTTP_NOT_ACCEPTABLE +#define LENGTH_REQUIRED HTTP_LENGTH_REQUIRED +#define PRECONDITION_FAILED HTTP_PRECONDITION_FAILED +#define SERVER_ERROR HTTP_INTERNAL_SERVER_ERROR +#define NOT_IMPLEMENTED HTTP_NOT_IMPLEMENTED +#define BAD_GATEWAY HTTP_BAD_GATEWAY +#define VARIANT_ALSO_VARIES HTTP_VARIANT_ALSO_VARIES + +#define ap_is_HTTP_INFO(x) (((x) >= 100)&&((x) < 200)) +#define ap_is_HTTP_SUCCESS(x) (((x) >= 200)&&((x) < 300)) +#define ap_is_HTTP_REDIRECT(x) (((x) >= 300)&&((x) < 400)) +#define ap_is_HTTP_ERROR(x) (((x) >= 400)&&((x) < 600)) +#define ap_is_HTTP_CLIENT_ERROR(x) (((x) >= 400)&&((x) < 500)) +#define ap_is_HTTP_SERVER_ERROR(x) (((x) >= 500)&&((x) < 600)) + +#define ap_status_drops_connection(x) \ + (((x) == HTTP_BAD_REQUEST) || \ + ((x) == HTTP_REQUEST_TIME_OUT) || \ + ((x) == HTTP_LENGTH_REQUIRED) || \ + ((x) == HTTP_REQUEST_ENTITY_TOO_LARGE) || \ + ((x) == HTTP_REQUEST_URI_TOO_LARGE) || \ + ((x) == HTTP_INTERNAL_SERVER_ERROR) || \ + ((x) == HTTP_SERVICE_UNAVAILABLE) || \ + ((x) == HTTP_NOT_IMPLEMENTED)) + +/* Methods recognized (but not necessarily handled) by the server. + * These constants are used in bit shifting masks of size int, so it is + * unsafe to have more methods than bits in an int. HEAD == M_GET. + */ +#define M_GET 0 +#define M_PUT 1 +#define M_POST 2 +#define M_DELETE 3 +#define M_CONNECT 4 +#define M_OPTIONS 5 +#define M_TRACE 6 +#define M_PATCH 7 +#define M_PROPFIND 8 +#define M_PROPPATCH 9 +#define M_MKCOL 10 +#define M_COPY 11 +#define M_MOVE 12 +#define M_LOCK 13 +#define M_UNLOCK 14 +#define M_INVALID 15 + +#define METHODS 16 + +#define CGI_MAGIC_TYPE "application/x-httpd-cgi" +#define INCLUDES_MAGIC_TYPE "text/x-server-parsed-html" +#define INCLUDES_MAGIC_TYPE3 "text/x-server-parsed-html3" +#ifdef CHARSET_EBCDIC +#define ASCIITEXT_MAGIC_TYPE_PREFIX "text/x-ascii-" /* Text files whose content-type starts with this are passed thru unconverted */ +#endif /*CHARSET_EBCDIC*/ +#define MAP_FILE_MAGIC_TYPE "application/x-type-map" +#define ASIS_MAGIC_TYPE "httpd/send-as-is" +#define DIR_MAGIC_TYPE "httpd/unix-directory" +#define STATUS_MAGIC_TYPE "application/x-httpd-status" + +/* + * Define the HTML doctype strings centrally. + */ +#define DOCTYPE_HTML_2_0 "\n" +#define DOCTYPE_HTML_3_2 "\n" +#define DOCTYPE_HTML_4_0S "\n" +#define DOCTYPE_HTML_4_0T "\n" +#define DOCTYPE_HTML_4_0F "\n" + +/* Just in case your linefeed isn't the one the other end is expecting. */ +#ifndef CHARSET_EBCDIC +#define LF 10 +#define CR 13 +#else /* CHARSET_EBCDIC */ +#include "ebcdic.h" +/* OSD_POSIX uses the EBCDIC charset. The transition ASCII->EBCDIC is done in + * the buff package (bread/bputs/bwrite), so everywhere else, we use + * "native EBCDIC" CR and NL characters. These are therefore defined as + * '\r' and '\n'. + * NB: this is not the whole truth - sometimes \015 and \012 are contained + * in literal (EBCDIC!) strings, so these are not converted but passed. + */ +#define CR '\r' +#define LF '\n' +#endif /* CHARSET_EBCDIC */ + +/* Possible values for request_rec.read_body (set by handling module): + * REQUEST_NO_BODY Send 413 error if message has any body + * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length + * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me. + * REQUEST_CHUNKED_PASS Pass the chunks to me without removal. + */ +#define REQUEST_NO_BODY 0 +#define REQUEST_CHUNKED_ERROR 1 +#define REQUEST_CHUNKED_DECHUNK 2 +#define REQUEST_CHUNKED_PASS 3 + +/* Things which may vary per file-lookup WITHIN a request --- + * e.g., state of MIME config. Basically, the name of an object, info + * about the object, and any other info we may ahve which may need to + * change as we go poking around looking for it (e.g., overridden by + * .htaccess files). + * + * Note how the default state of almost all these things is properly + * zero, so that allocating it with pcalloc does the right thing without + * a whole lot of hairy initialization... so long as we are willing to + * make the (fairly) portable assumption that the bit pattern of a NULL + * pointer is, in fact, zero. + */ + +/* This represents the result of calling htaccess; these are cached for + * each request. + */ +struct htaccess_result { + char *dir; /* the directory to which this applies */ + int override; /* the overrides allowed for the .htaccess file */ + void *htaccess; /* the configuration directives */ +/* the next one, or NULL if no more; N.B. never change this */ + const struct htaccess_result *next; +}; + +typedef struct conn_rec conn_rec; +typedef struct server_rec server_rec; +typedef struct request_rec request_rec; +typedef struct listen_rec listen_rec; + +#include "util_uri.h" + +struct request_rec { + + ap_pool *pool; + conn_rec *connection; + server_rec *server; + + request_rec *next; /* If we wind up getting redirected, + * pointer to the request we redirected to. + */ + request_rec *prev; /* If this is an internal redirect, + * pointer to where we redirected *from*. + */ + + request_rec *main; /* If this is a sub_request (see request.h) + * pointer back to the main request. + */ + + /* Info about the request itself... we begin with stuff that only + * protocol.c should ever touch... + */ + + char *the_request; /* First line of request, so we can log it */ + int assbackwards; /* HTTP/0.9, "simple" request */ + int proxyreq; /* A proxy request (calculated during + * post_read_request or translate_name) */ + int header_only; /* HEAD request, as opposed to GET */ + char *protocol; /* Protocol, as given to us, or HTTP/0.9 */ + int proto_num; /* Number version of protocol; 1.1 = 1001 */ + const char *hostname; /* Host, as set by full URI or Host: */ + + time_t request_time; /* When the request started */ + + const char *status_line; /* Status line, if set by script */ + int status; /* In any case */ + + /* Request method, two ways; also, protocol, etc.. Outside of protocol.c, + * look, but don't touch. + */ + + const char *method; /* GET, HEAD, POST, etc. */ + int method_number; /* M_GET, M_POST, etc. */ + + /* + allowed is a bitvector of the allowed methods. + + A handler must ensure that the request method is one that + it is capable of handling. Generally modules should DECLINE + any request methods they do not handle. Prior to aborting the + handler like this the handler should set r->allowed to the list + of methods that it is willing to handle. This bitvector is used + to construct the "Allow:" header required for OPTIONS requests, + and METHOD_NOT_ALLOWED and NOT_IMPLEMENTED status codes. + + Since the default_handler deals with OPTIONS, all modules can + usually decline to deal with OPTIONS. TRACE is always allowed, + modules don't need to set it explicitly. + + Since the default_handler will always handle a GET, a + module which does *not* implement GET should probably return + METHOD_NOT_ALLOWED. Unfortunately this means that a Script GET + handler can't be installed by mod_actions. + */ + int allowed; /* Allowed methods - for 405, OPTIONS, etc */ + + int sent_bodyct; /* byte count in stream is for body */ + long bytes_sent; /* body byte count, for easy access */ + time_t mtime; /* Time the resource was last modified */ + + /* HTTP/1.1 connection-level features */ + + int chunked; /* sending chunked transfer-coding */ + int byterange; /* number of byte ranges */ + char *boundary; /* multipart/byteranges boundary */ + const char *range; /* The Range: header */ + long clength; /* The "real" content length */ + + long remaining; /* bytes left to read */ + long read_length; /* bytes that have been read */ + int read_body; /* how the request body should be read */ + int read_chunked; /* reading chunked transfer-coding */ + unsigned expecting_100; /* is client waiting for a 100 response? */ + + /* MIME header environments, in and out. Also, an array containing + * environment variables to be passed to subprocesses, so people can + * write modules to add to that environment. + * + * The difference between headers_out and err_headers_out is that the + * latter are printed even on error, and persist across internal redirects + * (so the headers printed for ErrorDocument handlers will have them). + * + * The 'notes' table is for notes from one module to another, with no + * other set purpose in mind... + */ + + table *headers_in; + table *headers_out; + table *err_headers_out; + table *subprocess_env; + table *notes; + + /* content_type, handler, content_encoding, content_language, and all + * content_languages MUST be lowercased strings. They may be pointers + * to static strings; they should not be modified in place. + */ + const char *content_type; /* Break these out --- we dispatch on 'em */ + const char *handler; /* What we *really* dispatch on */ + + const char *content_encoding; + const char *content_language; /* for back-compat. only -- do not use */ + array_header *content_languages; /* array of (char*) */ + + char *vlist_validator; /* variant list validator (if negotiated) */ + + int no_cache; + int no_local_copy; + + /* What object is being requested (either directly, or via include + * or content-negotiation mapping). + */ + + char *unparsed_uri; /* the uri without any parsing performed */ + char *uri; /* the path portion of the URI */ + char *filename; + char *path_info; + char *args; /* QUERY_ARGS, if any */ + struct stat finfo; /* ST_MODE set to zero if no such file */ + uri_components parsed_uri; /* components of uri, dismantled */ + + /* Various other config info which may change with .htaccess files + * These are config vectors, with one void* pointer for each module + * (the thing pointed to being the module's business). + */ + + void *per_dir_config; /* Options set in config files, etc. */ + void *request_config; /* Notes on *this* request */ + +/* + * a linked list of the configuration directives in the .htaccess files + * accessed by this request. + * N.B. always add to the head of the list, _never_ to the end. + * that way, a sub request's list can (temporarily) point to a parent's list + */ + const struct htaccess_result *htaccess; + +/* Things placed at the end of the record to avoid breaking binary + * compatibility. It would be nice to remember to reorder the entire + * record to improve 64bit alignment the next time we need to break + * binary compatibility for some other reason. + */ +}; + + +/* Things which are per connection + */ + +struct conn_rec { + + ap_pool *pool; + server_rec *server; + server_rec *base_server; /* Physical vhost this conn come in on */ + void *vhost_lookup_data; /* used by http_vhost.c */ + + /* Information about the connection itself */ + + int child_num; /* The number of the child handling conn_rec */ + BUFF *client; /* Connection to the guy */ + + /* Who is the client? */ + + struct sockaddr_in local_addr; /* local address */ + struct sockaddr_in remote_addr; /* remote address */ + char *remote_ip; /* Client's IP address */ + char *remote_host; /* Client's DNS name, if known. + * NULL if DNS hasn't been checked, + * "" if it has and no address was found. + * N.B. Only access this though + * get_remote_host() */ + char *remote_logname; /* Only ever set if doing rfc1413 lookups. + * N.B. Only access this through + * get_remote_logname() */ + char *user; /* If an authentication check was made, + * this gets set to the user name. We assume + * that there's only one user per connection(!) + */ + char *ap_auth_type; /* Ditto. */ + + unsigned aborted:1; /* Are we still talking? */ + signed int keepalive:2; /* Are we using HTTP Keep-Alive? + * -1 fatal error, 0 undecided, 1 yes */ + unsigned keptalive:1; /* Did we use HTTP Keep-Alive? */ + signed int double_reverse:2;/* have we done double-reverse DNS? + * -1 yes/failure, 0 not yet, 1 yes/success */ + int keepalives; /* How many times have we used it? */ + char *local_ip; /* server IP address */ + char *local_host; /* used for ap_get_server_name when + * UseCanonicalName is set to DNS + * (ignores setting of HostnameLookups) */ +}; + +/* Per-vhost config... */ + +/* The address 255.255.255.255, when used as a virtualhost address, + * will become the "default" server when the ip doesn't match other vhosts. + */ +#define DEFAULT_VHOST_ADDR 0xfffffffful + +typedef struct server_addr_rec server_addr_rec; +struct server_addr_rec { + server_addr_rec *next; + struct in_addr host_addr; /* The bound address, for this server */ + unsigned short host_port; /* The bound port, for this server */ + char *virthost; /* The name given in */ +}; + +struct server_rec { + + server_rec *next; + + /* description of where the definition came from */ + const char *defn_name; + unsigned defn_line_number; + + /* Full locations of server config info */ + + char *srm_confname; + char *access_confname; + + /* Contact information */ + + char *server_admin; + char *server_hostname; + unsigned short port; /* for redirects, etc. */ + + /* Log files --- note that transfer log is now in the modules... */ + + char *error_fname; + FILE *error_log; + int loglevel; + + /* Module-specific configuration for server, and defaults... */ + + int is_virtual; /* true if this is the virtual server */ + void *module_config; /* Config vector containing pointers to + * modules' per-server config structures. + */ + void *lookup_defaults; /* MIME type info, etc., before we start + * checking per-directory info. + */ + /* Transaction handling */ + + server_addr_rec *addrs; + int timeout; /* Timeout, in seconds, before we give up */ + int keep_alive_timeout; /* Seconds we'll wait for another request */ + int keep_alive_max; /* Maximum requests per connection */ + int keep_alive; /* Use persistent connections? */ + int send_buffer_size; /* size of TCP send buffer (in bytes) */ + + char *path; /* Pathname for ServerPath */ + int pathlen; /* Length of path */ + + array_header *names; /* Normal names for ServerAlias servers */ + array_header *wild_names; /* Wildcarded names for ServerAlias servers */ + + uid_t server_uid; /* effective user id when calling exec wrapper */ + gid_t server_gid; /* effective group id when calling exec wrapper */ + + int limit_req_line; /* limit on size of the HTTP request line */ + int limit_req_fieldsize; /* limit on size of any request header field */ + int limit_req_fields; /* limit on number of request header fields */ +}; + +/* These are more like real hosts than virtual hosts */ +struct listen_rec { + listen_rec *next; + struct sockaddr_in local_addr; /* local IP address and port */ + int fd; + int used; /* Only used during restart */ +/* more stuff here, like which protocol is bound to the port */ +}; + +/* Prototypes for utilities... util.c. + */ + +extern void ap_util_init(void); + +/* Time */ +extern API_VAR_EXPORT const char ap_month_snames[12][4]; +extern API_VAR_EXPORT const char ap_day_snames[7][4]; + +API_EXPORT(struct tm *) ap_get_gmtoff(int *tz); +API_EXPORT(char *) ap_get_time(void); +API_EXPORT(char *) ap_field_noparam(pool *p, const char *intype); +API_EXPORT(char *) ap_ht_time(pool *p, time_t t, const char *fmt, int gmt); +API_EXPORT(char *) ap_gm_timestr_822(pool *p, time_t t); + +/* String handling. The *_nc variants allow you to use non-const char **s as + arguments (unfortunately C won't automatically convert a char ** to a const + char **) */ + +API_EXPORT(char *) ap_getword(pool *p, const char **line, char stop); +API_EXPORT(char *) ap_getword_nc(pool *p, char **line, char stop); +API_EXPORT(char *) ap_getword_white(pool *p, const char **line); +API_EXPORT(char *) ap_getword_white_nc(pool *p, char **line); +API_EXPORT(char *) ap_getword_nulls(pool *p, const char **line, char stop); +API_EXPORT(char *) ap_getword_nulls_nc(pool *p, char **line, char stop); +API_EXPORT(char *) ap_getword_conf(pool *p, const char **line); +API_EXPORT(char *) ap_getword_conf_nc(pool *p, char **line); + +API_EXPORT(const char *) ap_size_list_item(const char **field, int *len); +API_EXPORT(char *) ap_get_list_item(pool *p, const char **field); +API_EXPORT(int) ap_find_list_item(pool *p, const char *line, const char *tok); + +API_EXPORT(char *) ap_get_token(pool *p, const char **accept_line, int accept_white); +API_EXPORT(int) ap_find_token(pool *p, const char *line, const char *tok); +API_EXPORT(int) ap_find_last_token(pool *p, const char *line, const char *tok); + +API_EXPORT(int) ap_is_url(const char *u); +API_EXPORT(int) ap_unescape_url(char *url); +API_EXPORT(void) ap_no2slash(char *name); +API_EXPORT(void) ap_getparents(char *name); +API_EXPORT(char *) ap_escape_path_segment(pool *p, const char *s); +API_EXPORT(char *) ap_os_escape_path(pool *p, const char *path, int partial); +#define ap_escape_uri(ppool,path) ap_os_escape_path(ppool,path,1) +API_EXPORT(char *) ap_escape_html(pool *p, const char *s); +API_EXPORT(char *) ap_construct_server(pool *p, const char *hostname, + unsigned port, const request_rec *r); +API_EXPORT(char *) ap_escape_shell_cmd(pool *p, const char *s); + +API_EXPORT(int) ap_count_dirs(const char *path); +API_EXPORT(char *) ap_make_dirstr_prefix(char *d, const char *s, int n); +API_EXPORT(char *) ap_make_dirstr_parent(pool *p, const char *s); +/* deprecated. The previous two routines are preferred. */ +API_EXPORT(char *) ap_make_dirstr(pool *a, const char *s, int n); +API_EXPORT(char *) ap_make_full_path(pool *a, const char *dir, const char *f); + +API_EXPORT(int) ap_is_matchexp(const char *str); +API_EXPORT(int) ap_strcmp_match(const char *str, const char *exp); +API_EXPORT(int) ap_strcasecmp_match(const char *str, const char *exp); +API_EXPORT(char *) ap_pbase64decode(pool *p, const char *bufcoded); +API_EXPORT(char *) ap_pbase64encode(pool *p, char *string); +API_EXPORT(char *) ap_uudecode(pool *p, const char *bufcoded); +API_EXPORT(char *) ap_uuencode(pool *p, char *string); + +#ifdef OS2 +void os2pathname(char *path); +char *ap_double_quotes(pool *p, char *str); +#endif + +API_EXPORT(int) ap_regexec(const regex_t *preg, const char *string, + size_t nmatch, regmatch_t pmatch[], int eflags); +API_EXPORT(size_t) ap_regerror(int errcode, const regex_t *preg, + char *errbuf, size_t errbuf_size); +API_EXPORT(char *) ap_pregsub(pool *p, const char *input, const char *source, + size_t nmatch, regmatch_t pmatch[]); + +API_EXPORT(void) ap_content_type_tolower(char *); +API_EXPORT(void) ap_str_tolower(char *); +API_EXPORT(int) ap_ind(const char *, char); /* Sigh... */ +API_EXPORT(int) ap_rind(const char *, char); + +API_EXPORT(char *) ap_escape_quotes (pool *p, const char *instring); + +/* Common structure for reading of config files / passwd files etc. */ +typedef struct { + int (*getch) (void *param); /* a getc()-like function */ + void *(*getstr) (void *buf, size_t bufsiz, void *param); /* a fgets()-like function */ + int (*close) (void *param); /* a close hander function */ + void *param; /* the argument passed to getch/getstr/close */ + const char *name; /* the filename / description */ + unsigned line_number; /* current line number, starting at 1 */ +} configfile_t; + +/* Open a configfile_t as FILE, return open configfile_t struct pointer */ +API_EXPORT(configfile_t *) ap_pcfg_openfile(pool *p, const char *name); + +/* Allocate a configfile_t handle with user defined functions and params */ +API_EXPORT(configfile_t *) ap_pcfg_open_custom(pool *p, const char *descr, + void *param, + int(*getc_func)(void*), + void *(*gets_func) (void *buf, size_t bufsiz, void *param), + int(*close_func)(void *param)); + +/* Read one line from open configfile_t, strip LF, increase line number */ +API_EXPORT(int) ap_cfg_getline(char *buf, size_t bufsize, configfile_t *cfp); + +/* Read one char from open configfile_t, increase line number upon LF */ +API_EXPORT(int) ap_cfg_getc(configfile_t *cfp); + +/* Detach from open configfile_t, calling the close handler */ +API_EXPORT(int) ap_cfg_closefile(configfile_t *cfp); + +#ifdef NEED_STRERROR +char *strerror(int err); +#endif + +/* Misc system hackery */ + +API_EXPORT(uid_t) ap_uname2id(const char *name); +API_EXPORT(gid_t) ap_gname2id(const char *name); +API_EXPORT(int) ap_is_directory(const char *name); +API_EXPORT(int) ap_can_exec(const struct stat *); +API_EXPORT(void) ap_chdir_file(const char *file); + +#ifndef HAVE_CANONICAL_FILENAME +/* + * We can't define these in os.h because of dependence on pool pointer. + */ +#define ap_os_canonical_filename(p,f) (f) +#define ap_os_case_canonical_filename(p,f) (f) +#define ap_os_systemcase_filename(p,f) (f) +#else +API_EXPORT(char *) ap_os_canonical_filename(pool *p, const char *file); +#ifdef WIN32 +API_EXPORT(char *) ap_os_case_canonical_filename(pool *pPool, const char *szFile); +API_EXPORT(char *) ap_os_systemcase_filename(pool *pPool, const char *szFile); +#else +#define ap_os_case_canonical_filename(p,f) ap_os_canonical_filename(p,f) +#define ap_os_systemcase_filename(p,f) ap_os_canonical_filename(p,f) +#endif +#endif + +#ifdef _OSD_POSIX +extern const char *os_set_account(pool *p, const char *account); +extern int os_init_job_environment(server_rec *s, const char *user_name, int one_process); +#endif /* _OSD_POSIX */ + +char *ap_get_local_host(pool *); +unsigned long ap_get_virthost_addr(char *hostname, unsigned short *port); + +extern API_VAR_EXPORT time_t ap_restart_time; + +/* + * Apache tries to keep all of its long term filehandles (such as log files, + * and sockets) above this number. This is to workaround problems in many + * third party libraries that are compiled with a small FD_SETSIZE. There + * should be no reason to lower this, because it's only advisory. If a file + * can't be allocated above this number then it will remain in the "slack" + * area. + * + * Only the low slack line is used by default. If HIGH_SLACK_LINE is defined + * then an attempt is also made to keep all non-FILE * files above the high + * slack line. This is to work around a Solaris C library limitation, where it + * uses an unsigned char to store the file descriptor. + */ +#ifndef LOW_SLACK_LINE +#define LOW_SLACK_LINE 15 +#endif +/* #define HIGH_SLACK_LINE 255 */ + +/* + * The ap_slack() function takes a fd, and tries to move it above the indicated + * line. It returns an fd which may or may not have moved above the line, and + * never fails. If the high line was requested and it fails it will also try + * the low line. + */ +#ifdef NO_SLACK +#define ap_slack(fd,line) (fd) +#else +int ap_slack(int fd, int line); +#define AP_SLACK_LOW 1 +#define AP_SLACK_HIGH 2 +#endif + +API_EXPORT(char *) ap_escape_quotes(pool *p, const char *instr); + +/* + * Redefine assert() to something more useful for an Apache... + */ +API_EXPORT(void) ap_log_assert(const char *szExp, const char *szFile, int nLine) + __attribute__((noreturn)); +#define ap_assert(exp) ((exp) ? (void)0 : ap_log_assert(#exp,__FILE__,__LINE__)) + +/* The optimized timeout code only works if we're not MULTITHREAD and we're + * also not using a scoreboard file + */ +#if !defined (MULTITHREAD) && \ + (defined (USE_MMAP_SCOREBOARD) || defined (USE_SHMGET_SCOREBOARD)) +#define OPTIMIZE_TIMEOUTS +#endif + +/* A set of flags which indicate places where the server should raise(SIGSTOP). + * This is useful for debugging, because you can then attach to that process + * with gdb and continue. This is important in cases where one_process + * debugging isn't possible. + */ +#define SIGSTOP_DETACH 1 +#define SIGSTOP_MAKE_CHILD 2 +#define SIGSTOP_SPAWN_CHILD 4 +#define SIGSTOP_PIPED_LOG_SPAWN 8 +#define SIGSTOP_CGI_CHILD 16 + +#ifdef DEBUG_SIGSTOP +extern int raise_sigstop_flags; +#define RAISE_SIGSTOP(x) do { \ + if (raise_sigstop_flags & SIGSTOP_##x) raise(SIGSTOP);\ + } while (0) +#else +#define RAISE_SIGSTOP(x) +#endif + +API_EXPORT(extern const char *) ap_psignature(const char *prefix, request_rec *r); + +/* strtoul does not exist on sunos4. */ +#ifdef strtoul +#undef strtoul +#endif +#define strtoul strtoul_is_not_a_portable_function_use_strtol_instead + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_HTTPD_H */ diff --git a/include/pcreposix.h b/include/pcreposix.h new file mode 100644 index 00000000000..47838a8bc56 --- /dev/null +++ b/include/pcreposix.h @@ -0,0 +1,89 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* Copyright (c) 1997-2000 University of Cambridge */ +/* Do not edit this file, this is provided by PCRE */ + +#ifndef _PCREPOSIX_H +#define _PCREPOSIX_H + +/* This is the header for the POSIX wrapper interface to the PCRE Perl- +Compatible Regular Expression library. It defines the things POSIX says should +be there. I hope. */ + +/* Have to include stdlib.h in order to ensure that size_t is defined. */ + +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options defined by POSIX. */ + +#define REG_ICASE 0x01 +#define REG_NEWLINE 0x02 +#define REG_NOTBOL 0x04 +#define REG_NOTEOL 0x08 + +/* These are not used by PCRE, but by defining them we make it easier +to slot PCRE into existing programs that make POSIX calls. */ + +#define REG_EXTENDED 0 +#define REG_NOSUB 0 + +/* Error values. Not all these are relevant or used by the wrapper. */ + +enum { + REG_ASSERT = 1, /* internal error ? */ + REG_BADBR, /* invalid repeat counts in {} */ + REG_BADPAT, /* pattern error */ + REG_BADRPT, /* ? * + invalid */ + REG_EBRACE, /* unbalanced {} */ + REG_EBRACK, /* unbalanced [] */ + REG_ECOLLATE, /* collation error - not relevant */ + REG_ECTYPE, /* bad class */ + REG_EESCAPE, /* bad escape sequence */ + REG_EMPTY, /* empty expression */ + REG_EPAREN, /* unbalanced () */ + REG_ERANGE, /* bad range inside [] */ + REG_ESIZE, /* expression too big */ + REG_ESPACE, /* failed to get memory */ + REG_ESUBREG, /* bad back reference */ + REG_INVARG, /* bad argument */ + REG_NOMATCH /* match failed */ +}; + + +/* The structure representing a compiled regular expression. */ + +typedef struct { + void *re_pcre; + size_t re_nsub; + size_t re_erroffset; +} regex_t; + +/* The structure in which a captured offset is returned. */ + +typedef int regoff_t; + +typedef struct { + regoff_t rm_so; + regoff_t rm_eo; +} regmatch_t; + +/* The functions */ + +extern int regcomp(regex_t *, const char *, int); +extern int regexec(regex_t *, const char *, size_t, regmatch_t *, int); +extern size_t regerror(int, const regex_t *, char *, size_t); +extern void regfree(regex_t *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcreposix.h */ diff --git a/include/util_cfgtree.h b/include/util_cfgtree.h new file mode 100644 index 00000000000..6a4a2f6f27e --- /dev/null +++ b/include/util_cfgtree.h @@ -0,0 +1,70 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +#ifndef AP_CONFTREE_H +#define AP_CONFTREE_H + +typedef struct ap_directive_t { + const char *directive; + const char *args; + int line_num; + struct ap_directive_t *next; + struct ap_directive_t *first_child; + struct ap_directive_t *parent; +} ap_directive_t; + +ap_directive_t *ap_add_node(ap_directive_t **parent, ap_directive_t *current, + ap_directive_t *todadd, int child); + +#endif diff --git a/include/util_md5.h b/include/util_md5.h new file mode 100644 index 00000000000..0a0dc9c9b3c --- /dev/null +++ b/include/util_md5.h @@ -0,0 +1,80 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_UTIL_MD5_H +#define APACHE_UTIL_MD5_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ap_md5.h" + +API_EXPORT(char *) ap_md5(pool *a, const unsigned char *string); +API_EXPORT(char *) ap_md5_binary(pool *a, const unsigned char *buf, int len); +API_EXPORT(char *) ap_md5contextTo64(pool *p, AP_MD5_CTX * context); +#ifdef CHARSET_EBCDIC +API_EXPORT(char *) ap_md5digest(pool *p, FILE *infile, int convert); +#else +API_EXPORT(char *) ap_md5digest(pool *p, FILE *infile); +#endif /* CHARSET_EBCDIC */ + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_UTIL_MD5_H */ diff --git a/include/util_script.h b/include/util_script.h new file mode 100644 index 00000000000..416d72942dc --- /dev/null +++ b/include/util_script.h @@ -0,0 +1,91 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_UTIL_SCRIPT_H +#define APACHE_UTIL_SCRIPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef APACHE_ARG_MAX +#ifdef _POSIX_ARG_MAX +#define APACHE_ARG_MAX _POSIX_ARG_MAX +#else +#define APACHE_ARG_MAX 512 +#endif +#endif + +API_EXPORT(char **) ap_create_environment(pool *p, table *t); +API_EXPORT(int) ap_find_path_info(const char *uri, const char *path_info); +API_EXPORT(void) ap_add_cgi_vars(request_rec *r); +API_EXPORT(void) ap_add_common_vars(request_rec *r); +API_EXPORT(int) ap_scan_script_header_err(request_rec *r, FILE *f, char *buffer); +API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *f, + char *buffer); +API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, + int (*getsfunc) (char *, int, void *), + void *getsfunc_data); +API_EXPORT(void) ap_send_size(size_t size, request_rec *r); +API_EXPORT(int) ap_call_exec(request_rec *r, child_info *pinfo, char *argv0, char **env, + int shellcmd); + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_UTIL_SCRIPT_H */ diff --git a/modules/.cvsignore b/modules/.cvsignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/modules/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/modules/Makefile.in b/modules/Makefile.in new file mode 100644 index 00000000000..da8a82274b5 --- /dev/null +++ b/modules/Makefile.in @@ -0,0 +1,10 @@ + +DEPTH = .. +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +SUBDIRS = mpm $(MODULE_DIRS) + +include $(topsrcdir)/build/rules.mk + diff --git a/modules/README b/modules/README new file mode 100644 index 00000000000..df25f051f25 --- /dev/null +++ b/modules/README @@ -0,0 +1,34 @@ +The directory structure for this level is as follows: + +standard/ + + In this directory are the standard supported modules for + Apache. Not all are compiled by default. + +proxy/ + + This houses the code for the proxy module for Apache. + +experimental/ + + In this directory we've placed some modules which we think + provide some pretty interesting functionality, but which + are still in the early stages of development and could + evolve radically in the future. This code isn't supported + officially. + +extra/ + + This is the directory for third-party modules, such as mod_jserv. + +test/ + + This directory houses modules which test various components + of Apache. You should not compile these into a production + server. + +example/ + + This directory houses example modules, to help module authors + figure their way around the Apache API and module concept. + diff --git a/modules/aaa/.cvsignore b/modules/aaa/.cvsignore new file mode 100644 index 00000000000..c3690626fe3 --- /dev/null +++ b/modules/aaa/.cvsignore @@ -0,0 +1,6 @@ +Makefile +*.lo +*.so +*.dll +*.def +*.exp diff --git a/modules/aaa/.indent.pro b/modules/aaa/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/modules/aaa/.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/aaa/Makefile.in b/modules/aaa/Makefile.in new file mode 100644 index 00000000000..ccd3c197552 --- /dev/null +++ b/modules/aaa/Makefile.in @@ -0,0 +1,8 @@ + +DEPTH = ../.. +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(topsrcdir)/build/special.mk + diff --git a/modules/aaa/config.m4 b/modules/aaa/config.m4 new file mode 100644 index 00000000000..9d069b51c76 --- /dev/null +++ b/modules/aaa/config.m4 @@ -0,0 +1 @@ +APACHE_MODULE(standard) diff --git a/modules/aaa/mod_auth_digest.c b/modules/aaa/mod_auth_digest.c new file mode 100644 index 00000000000..43795221f86 --- /dev/null +++ b/modules/aaa/mod_auth_digest.c @@ -0,0 +1,1919 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_auth_digest: MD5 digest authentication + * + * Originally by Alexei Kosut + * Updated to RFC-2617 by Ronald Tschalär + * based on mod_auth, by Rob McCool and Robert S. Thau + * + * This module an updated version of modules/standard/mod_digest.c + * However, it has not been extensively tested yet, and is therefore + * currently marked experimental. Send problem reports to me + * (ronald@innovation.ch) + * + * Requires either /dev/random (or equivalent) or the truerand library, + * available for instance from + * ftp://research.att.com/dist/mab/librand.shar + * + * Open Issues: + * - qop=auth-int (when streams and trailer support available) + * - nonce-format configurability + * - Proxy-Authorization-Info header is set by this module, but is + * currently ignored by mod_proxy (needs patch to mod_proxy) + * - generating the secret takes a while (~ 8 seconds) if using the + * truerand library + * - shared-mem not completely tested yet. Seems to work ok for me, + * but... (definitely won't work on Windoze) + */ + +/* The section for the Configure script: + * MODULE-DEFINITION-START + * Name: digest_auth_module + * ConfigStart + + RULE_DEV_RANDOM=`./helpers/CutRule DEV_RANDOM $file` + if [ "$RULE_DEV_RANDOM" = "default" ]; then + if [ -r "/dev/random" ]; then + RULE_DEV_RANDOM="/dev/random" + elif [ -r "/dev/urandom" ]; then + RULE_DEV_RANDOM="/dev/urandom" + else + RULE_DEV_RANDOM="truerand" + if helpers/TestCompile func randbyte; then + : + elif helpers/TestCompile lib rand randbyte; then + : + else + echo " (mod_auth_digest) truerand library missing!" + echo "** This will most probably defeat successful compilation." + echo "** See Rule DEV_RANDOM in src/Configuration.tmpl for more information." + fi + fi + fi + if [ "$RULE_DEV_RANDOM" = "truerand" ]; then + echo " using truerand library (-lrand) for the random seed" + LIBS="$LIBS -L/usr/local/lib -lrand" + else + echo " using $RULE_DEV_RANDOM for the random seed" + CFLAGS="$CFLAGS -DDEV_RANDOM=$RULE_DEV_RANDOM" + fi + + * ConfigEnd + * MODULE-DEFINITION-END + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_conf_globals.h" +#include "http_core.h" +#include "http_request.h" +#include "http_log.h" +#include "http_protocol.h" +#include "ap_config.h" +#include "ap_ctype.h" +#include "util_uri.h" +#include "util_md5.h" +#include "ap_sha1.h" +#ifdef HAVE_SHMEM_MM +#include "mm.h" +#endif /* HAVE_SHMEM_MM */ + + +/* struct to hold the configuration info */ + +typedef struct digest_config_struct { + const char *dir_name; + const char *pwfile; + const char *grpfile; + const char *realm; + const char **qop_list; + AP_SHA1_CTX nonce_ctx; + long nonce_lifetime; + const char *nonce_format; + int check_nc; + const char *algorithm; + char *uri_list; + const char *ha1; +} digest_config_rec; + + +#define DFLT_ALGORITHM "MD5" + +#define DFLT_NONCE_LIFE 300L +#define NEXTNONCE_DELTA 30 + + +#define NONCE_TIME_LEN (((sizeof(time_t)+2)/3)*4) +#define NONCE_HASH_LEN 40 +#define NONCE_LEN (NONCE_TIME_LEN + NONCE_HASH_LEN) + +#define SECRET_LEN 20 + + +/* client list definitions */ + +typedef struct hash_entry { + unsigned long key; /* the key for this entry */ + struct hash_entry *next; /* next entry in the bucket */ + unsigned long nonce_count; /* for nonce-count checking */ + char ha1[17]; /* for algorithm=MD5-sess */ + char last_nonce[NONCE_LEN+1]; /* for one-time nonce's */ +} client_entry; + +static struct hash_table { + client_entry **table; + unsigned long tbl_len; + unsigned long num_entries; + unsigned long num_created; + unsigned long num_removed; + unsigned long num_renewed; +} *client_list; + + +/* struct to hold a parsed Authorization header */ + +enum hdr_sts { NO_HEADER, NOT_DIGEST, INVALID, VALID }; + +typedef struct digest_header_struct { + const char *scheme; + const char *realm; + const char *username; + char *nonce; + const char *uri; + const char *digest; + const char *algorithm; + const char *cnonce; + const char *opaque; + unsigned long opaque_num; + const char *message_qop; + const char *nonce_count; + /* the following fields are not (directly) from the header */ + time_t nonce_time; + enum hdr_sts auth_hdr_sts; + uri_components *request_uri; + int needed_auth; + client_entry *client; +} digest_header_rec; + + +/* (mostly) nonce stuff */ + +typedef union time_union { + time_t time; + unsigned char arr[sizeof(time_t)]; +} time_rec; + + +static unsigned char secret[SECRET_LEN]; +static int call_cnt = 0; + + +#ifdef HAVE_SHMEM_MM +/* opaque stuff */ + +static MM *opaque_mm; +static unsigned long *opaque_cntr; + +static MM *client_mm; + +static MM *otn_count_mm; +static time_t *otn_counter; /* one-time-nonce counter */ + +#define SHMEM_SIZE 1000 /* ~ 12 entries */ +#define NUM_BUCKETS 15UL + +#else /* HAVE_SHMEM_MM */ +static void *client_mm = NULL; +#endif /* HAVE_SHMEM_MM */ + +module MODULE_VAR_EXPORT digest_auth_module; + +/* + * initialization code + */ + +#ifdef HAVE_SHMEM_MM +static void cleanup_tables(void *not_used) +{ + fprintf(stderr, "Digest: cleaning up shared memory\n"); + fflush(stderr); + + if (client_mm) { + mm_destroy(client_mm); + client_mm = NULL; + } + + if (opaque_mm) { + mm_destroy(opaque_mm); + opaque_mm = NULL; + } + + if (otn_count_mm) { + mm_destroy(otn_count_mm); + otn_count_mm = NULL; + } +} +#endif /* HAVE_SHMEM_MM */ + +static void initialize_secret(server_rec *s) +{ +#ifdef DEV_RANDOM + FILE *rnd; + size_t got, tot; +#else + extern int randbyte(void); /* from the truerand library */ + unsigned int idx; +#endif + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, + "Digest: generating secret for digest authentication ..."); + +#ifdef DEV_RANDOM +#define XSTR(x) #x +#define STR(x) XSTR(x) + if ((rnd = fopen(STR(DEV_RANDOM), "rb")) == NULL) { + ap_log_error(APLOG_MARK, APLOG_CRIT, s, + "Digest: Couldn't open " STR(DEV_RANDOM)); + exit(EXIT_FAILURE); + } + if (setvbuf(rnd, NULL, _IONBF, 0) != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, s, + "Digest: Error trying to disable buffering for " STR(DEV_RANDOM)); + exit(EXIT_FAILURE); + } + for (tot=0; tottable = (client_entry**) (client_list + 1); + for (idx=0; idxtable[idx] = NULL; + client_list->tbl_len = NUM_BUCKETS; + client_list->num_entries = 0; + + + /* setup opaque */ + + opaque_mm = mm_create(sizeof(*opaque_cntr), tmpnam(NULL)); + if (opaque_mm == NULL) + goto failed; +#ifdef MPE + if (geteuid() == 1) { +#else + if (geteuid() == 0) { +#endif + if (mm_permission(opaque_mm, 0600, ap_user_id, ap_group_id)) + goto failed; + } + opaque_cntr = mm_malloc(opaque_mm, sizeof(*opaque_cntr)); + if (opaque_cntr == NULL) + goto failed; + *opaque_cntr = 1UL; + + + /* setup one-time-nonce counter */ + + otn_count_mm = mm_create(sizeof(*otn_counter), tmpnam(NULL)); + if (otn_count_mm == NULL) + goto failed; +#ifdef MPE + if (geteuid() == 1) { +#else + if (geteuid() == 0) { +#endif + if (mm_permission(otn_count_mm, 0600, ap_user_id, ap_group_id)) + goto failed; + } + otn_counter = mm_malloc(otn_count_mm, sizeof(*otn_counter)); + if (otn_counter == NULL) + goto failed; + *otn_counter = 0; + + + /* success */ + return; + +failed: + if (!client_mm || (client_list && client_list->table && !opaque_mm) + || (opaque_cntr && !otn_count_mm)) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s, + "Digest: failed to create shared memory segments; reason " + "was `%s' - all nonce-count checking, one-time nonces, " + "and MD5-sess algorithm disabled", mm_error()); + else + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s, + "Digest: failed to allocate shared mem; reason was `%s' " + "- all nonce-count checking, one-time nonces, and " + "MD5-sess algorithm disabled", mm_error()); + + cleanup_tables(NULL); +} +#endif /* HAVE_SHMEM_MM */ + +static void initialize_module(server_rec *s, pool *p) +{ + /* keep from doing the init more than once at startup, and delay + * the init until the second round + */ + if (++call_cnt < 2) + return; + + /* only initialize the secret on startup, not on restarts */ + if (call_cnt == 2) + initialize_secret(s); + +#ifdef HAVE_SHMEM_MM + /* Note: this stuff is currently fixed for the lifetime of the server, + * i.e. even across restarts. This means that A) any shmem-size + * configuration changes are ignored, and B) certain optimizations, + * such as only allocating the smallest necessary entry for each + * client, can't be done. However, the alternative is a nightmare: + * we can't call mm_destroy on a graceful restart because there will + * be children using the tables, and we also don't know when the + * last child dies. Therefore we can never clean up the old stuff, + * creating a creeping memory leak. + */ + initialize_tables(s); + /* atexit(cleanup_tables); */ + ap_register_cleanup(p, NULL, cleanup_tables, ap_null_cleanup); +#endif /* HAVE_SHMEM_MM */ +} + + +/* + * configuration code + */ + +static void *create_digest_dir_config(pool *p, char *dir) +{ + digest_config_rec *conf; + + if (dir == NULL) return NULL; + + conf = (digest_config_rec *) ap_pcalloc(p, sizeof(digest_config_rec)); + if (conf) { + conf->qop_list = ap_palloc(p, sizeof(char*)); + conf->qop_list[0] = NULL; + conf->nonce_lifetime = DFLT_NONCE_LIFE; + conf->dir_name = ap_pstrdup(p, dir); + conf->algorithm = DFLT_ALGORITHM; + } + + return conf; +} + +static const char *set_realm(cmd_parms *cmd, void *config, const char *realm) +{ + digest_config_rec *conf = (digest_config_rec *) config; + + /* The core already handles the realm, but it's just too convenient to + * grab it ourselves too and cache some setups. However, we need to + * let the core get at it too, which is why we decline at the end - + * this relies on the fact that http_core is last in the list. + */ + conf->realm = realm; + + /* we precompute the part of the nonce hash that is constant (well, + * the host:port would be too, but that varies for .htaccess files + * and directives outside a virtual host section) + */ + ap_SHA1Init(&conf->nonce_ctx); + ap_SHA1Update_binary(&conf->nonce_ctx, (const unsigned char *) realm, + strlen(realm)); + ap_SHA1Update_binary(&conf->nonce_ctx, secret, sizeof(secret)); + + return DECLINE_CMD; +} + +static const char *set_digest_file(cmd_parms *cmd, void *config, + const char *file) +{ + ((digest_config_rec *) config)->pwfile = file; + return NULL; +} + +static const char *set_group_file(cmd_parms *cmd, void *config, + const char *file) +{ + ((digest_config_rec *) config)->grpfile = file; + return NULL; +} + +static const char *set_qop(cmd_parms *cmd, void *config, const char *op) +{ + digest_config_rec *conf = (digest_config_rec *) config; + const char **tmp; + int cnt; + + if (!strcasecmp(op, "none")) { + if (conf->qop_list[0] == NULL) { + conf->qop_list = ap_palloc(cmd->pool, 2 * sizeof(char*)); + conf->qop_list[1] = NULL; + } + conf->qop_list[0] = "none"; + return NULL; + } + + if (!strcasecmp(op, "auth-int")) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, cmd->server, + "Digest: WARNING: qop `auth-int' currently only works " + "correctly for responses with no entity"); + else if (strcasecmp(op, "auth")) + return ap_pstrcat(cmd->pool, "Unrecognized qop: ", op, NULL); + + for (cnt=0; conf->qop_list[cnt] != NULL; cnt++) + ; + tmp = ap_palloc(cmd->pool, (cnt+2)*sizeof(char*)); + memcpy(tmp, conf->qop_list, cnt*sizeof(char*)); + tmp[cnt] = ap_pstrdup(cmd->pool, op); + tmp[cnt+1] = NULL; + conf->qop_list = tmp; + + return NULL; +} + +static const char *set_nonce_lifetime(cmd_parms *cmd, void *config, + const char *t) +{ + char *endptr; + long lifetime; + + lifetime = strtol(t, &endptr, 10); + if (endptr < (t+strlen(t)) && !ap_isspace(*endptr)) + return ap_pstrcat(cmd->pool, "Invalid time in AuthDigestNonceLifetime: ", t, NULL); + + ((digest_config_rec *) config)->nonce_lifetime = lifetime; + return NULL; +} + +static const char *set_nonce_format(cmd_parms *cmd, void *config, + const char *fmt) +{ + ((digest_config_rec *) config)->nonce_format = fmt; + return "AuthDigestNonceFormat is not implemented (yet)"; +} + +static const char *set_nc_check(cmd_parms *cmd, void *config, int flag) +{ + ((digest_config_rec *) config)->check_nc = flag; + return NULL; +} + +static const char *set_algorithm(cmd_parms *cmd, void *config, const char *alg) +{ + if (!strcasecmp(alg, "MD5-sess")) +#ifdef HAVE_SHMEM_MM + ; +#else /* HAVE_SHMEM_MM */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, cmd->server, + "Digest: WARNING: algorithm `MD5-sess' is currently not " + "correctly implemented"); +#endif /* HAVE_SHMEM_MM */ + else if (strcasecmp(alg, "MD5")) + return ap_pstrcat(cmd->pool, "Invalid algorithm in AuthDigestAlgorithm: ", alg, NULL); + + ((digest_config_rec *) config)->algorithm = alg; + return NULL; +} + +static const char *set_uri_list(cmd_parms *cmd, void *config, const char *uri) +{ + digest_config_rec *c = (digest_config_rec *) config; + if (c->uri_list) { + c->uri_list[strlen(c->uri_list)-1] = '\0'; + c->uri_list = ap_pstrcat(cmd->pool, c->uri_list, " ", uri, "\"", NULL); + } + else + c->uri_list = ap_pstrcat(cmd->pool, ", domain=\"", uri, "\"", NULL); + return NULL; +} + +static const command_rec digest_cmds[] = +{ + {"AuthName", set_realm, NULL, OR_AUTHCFG, TAKE1, + "The authentication realm (e.g. \"Members Only\")"}, + {"AuthDigestFile", set_digest_file, NULL, OR_AUTHCFG, TAKE1, + "The name of the file containing the usernames and password hashes"}, + {"AuthDigestGroupFile", set_group_file, NULL, OR_AUTHCFG, TAKE1, + "The name of the file containing the group names and members"}, + {"AuthDigestQop", set_qop, NULL, OR_AUTHCFG, ITERATE, + "A list of quality-of-protection options"}, + {"AuthDigestNonceLifetime", set_nonce_lifetime, NULL, OR_AUTHCFG, TAKE1, + "Maximum lifetime of the server nonce (seconds)"}, + {"AuthDigestNonceFormat", set_nonce_format, NULL, OR_AUTHCFG, TAKE1, + "The format to use when generating the server nonce"}, + {"AuthDigestNcCheck", set_nc_check, NULL, OR_AUTHCFG, FLAG, + "Whether or not to check the nonce-count sent by the client"}, + {"AuthDigestAlgorithm", set_algorithm, NULL, OR_AUTHCFG, TAKE1, + "The algorithm used for the hash calculation"}, + {"AuthDigestDomain", set_uri_list, NULL, OR_AUTHCFG, ITERATE, + "A list of URI's which belong to the same protection space as the current URI"}, + {NULL} +}; + + +#ifdef HAVE_SHMEM_MM +/* + * client list code + * + * Each client is assigned a number, which is transfered in the opaque + * field of the WWW-Authenticate and Authorization headers. The number + * is just a simple counter which is incremented for each new client. + * Clients can't forge this number because it is hashed up into the + * server nonce, and that is checked. + * + * The clients are kept in a simple hash table, which consists of an + * array of client_entry's, each with a linked list of entries hanging + * off it. The client's number modulo the size of the array gives the + * bucket number. + * + * The clients are garbage collected whenever a new client is allocated + * but there is not enough space left in the shared memory segment. A + * simple semi-LRU is used for this: whenever a client entry is accessed + * it is moved to the beginning of the linked list in its bucket (this + * also makes for faster lookups for current clients). The garbage + * collecter then just removes the oldest entry (i.e. the one at the + * end of the list) in each bucket. + * + * The main advantages of the above scheme are that it's easy to implement + * and it keeps the hash table evenly balanced (i.e. same number of entries + * in each bucket). The major disadvantage is that you may be throwing + * entries out which are in active use. This is not tragic, as these + * clients will just be sent a new client id (opaque field) and nonce + * with a stale=true (i.e. it will just look like the nonce expired, + * thereby forcing an extra round trip). If the shared memory segment + * has enough headroom over the current client set size then this should + * not occur too often. + * + * To help tune the size of the shared memory segment (and see if the + * above algorithm is really sufficient) a set of counters is kept + * indicating the number of clients held, the number of garbage collected + * clients, and the number of erroneously purged clients. These are printed + * out at each garbage collection run. Note that access to the counters is + * not synchronized because they are just indicaters, and whether they are + * off by a few doesn't matter; and for the same reason no attempt is made + * to guarantee the num_renewed is correct in the face of clients spoofing + * the opaque field. + */ + +/* + * Get the client given its client number (the key). Returns the entry, + * or NULL if its not found. + * + * Access to the list itself is synchronized via locks. However, access + * to the entry returned by get_client() is NOT synchronized. This means + * that there are potentially problems if a client uses multiple, + * simultaneous connections to access url's within the same protection + * space. However, these problems are not new: when using multiple + * connections you have no guarantee of the order the requests are + * processed anyway, so you have problems with the nonce-count and + * one-time nonces anyway. + */ +static client_entry *get_client(unsigned long key, const request_rec *r) +{ + int bucket; + client_entry *entry, *prev = NULL; + + + if (!key || !client_mm) return NULL; + + bucket = key % client_list->tbl_len; + entry = client_list->table[bucket]; + + mm_lock(client_mm, MM_LOCK_RD); + + while(entry && key != entry->key) { + prev = entry; + entry = entry->next; + } + + if (entry && prev) { /* move entry to front of list */ + prev->next = entry->next; + entry->next = client_list->table[bucket]; + client_list->table[bucket] = entry; + } + + mm_unlock(client_mm); + + if (entry) + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, + "get_client(): client %lu found", key); + else + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, + "get_client(): client %lu not found", key); + + return entry; +} + + +/* A simple garbage-collecter to remove unused clients. It removes the + * last entry in each bucket and updates the counters. Returns the + * number of removed entries. + */ +static long gc(void) +{ + client_entry *entry, *prev; + unsigned long num_removed = 0, idx; + + /* garbage collect all last entries */ + + for (idx=0; idxtbl_len; idx++) { + entry = client_list->table[idx]; + prev = NULL; + while (entry->next) { /* find last entry */ + prev = entry; + entry = entry->next; + } + if (prev) prev->next = NULL; /* cut list */ + else client_list->table[idx] = NULL; + if (entry) { /* remove entry */ + mm_free(client_mm, entry); + num_removed++; + } + } + + /* update counters and log */ + + client_list->num_entries -= num_removed; + client_list->num_removed += num_removed; + + return num_removed; +} + + +/* + * Add a new client to the list. Returns the entry if successful, NULL + * otherwise. This triggers the garbage collection is memory is low. + */ +static client_entry *add_client(unsigned long key, client_entry *new, + server_rec *s) +{ + int bucket; + client_entry *entry; + + + if (!key || !client_mm) return NULL; + + bucket = key % client_list->tbl_len; + entry = client_list->table[bucket]; + + mm_lock(client_mm, MM_LOCK_RW); + + /* try to allocate a new entry */ + + entry = mm_malloc(client_mm, sizeof(client_entry)); + if (!entry) { + long num_removed = gc(); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, s, + "Digest: gc'd %ld client entries. Total new clients: " + "%ld; Total removed clients: %ld; Total renewed clients: " + "%ld", num_removed, + client_list->num_created - client_list->num_renewed, + client_list->num_removed, client_list->num_renewed); + entry = mm_malloc(client_mm, sizeof(client_entry)); + if (!entry) return NULL; /* give up */ + } + + /* now add the entry */ + + memcpy(entry, new, sizeof(client_entry)); + entry->key = key; + entry->next = client_list->table[bucket]; + client_list->table[bucket] = entry; + client_list->num_created++; + client_list->num_entries++; + + mm_unlock(client_mm); + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, s, + "allocated new client %lu", key); + + return entry; +} +#else /* HAVE_SHMEM_MM */ +static client_entry *get_client(unsigned long key, const request_rec *r) +{ + return NULL; +} +#endif /* HAVE_SHMEM_MM */ + + +/* + * Authorization header parser code + */ + +/* Parse the Authorization header, if it exists */ +static int get_digest_rec(request_rec *r, digest_header_rec *resp) +{ + const char *auth_line = ap_table_get(r->headers_in, + r->proxyreq ? "Proxy-Authorization" + : "Authorization"); + size_t l; + int vk = 0, vv = 0; + char *key, *value; + + + if (!auth_line) { + resp->auth_hdr_sts = NO_HEADER; + return !OK; + } + + resp->scheme = ap_getword_white(r->pool, &auth_line); + if (strcasecmp(resp->scheme, "Digest")) { + resp->auth_hdr_sts = NOT_DIGEST; + return !OK; + } + + l = strlen(auth_line); + + key = ap_palloc(r->pool, l+1); + value = ap_palloc(r->pool, l+1); + + while (auth_line[0] != '\0') { + + /* find key */ + + while (ap_isspace(auth_line[0])) auth_line++; + vk = 0; + while (auth_line[0] != '=' && auth_line[0] != ',' + && auth_line[0] != '\0' && !ap_isspace(auth_line[0])) + key[vk++] = *auth_line++; + key[vk] = '\0'; + while (ap_isspace(auth_line[0])) auth_line++; + + /* find value */ + + if (auth_line[0] == '=') { + auth_line++; + while (ap_isspace(auth_line[0])) auth_line++; + + vv = 0; + if (auth_line[0] == '\"') { /* quoted string */ + auth_line++; + while (auth_line[0] != '\"' && auth_line[0] != '\0') { + if (auth_line[0] == '\\' && auth_line[1] != '\0') + auth_line++; /* escaped char */ + value[vv++] = *auth_line++; + } + if (auth_line[0] != '\0') auth_line++; + } + else { /* token */ + while (auth_line[0] != ',' && auth_line[0] != '\0' + && !ap_isspace(auth_line[0])) + value[vv++] = *auth_line++; + } + value[vv] = '\0'; + } + + while (auth_line[0] != ',' && auth_line[0] != '\0') auth_line++; + if (auth_line[0] != '\0') auth_line++; + + if (!strcasecmp(key, "username")) + resp->username = ap_pstrdup(r->pool, value); + else if (!strcasecmp(key, "realm")) + resp->realm = ap_pstrdup(r->pool, value); + else if (!strcasecmp(key, "nonce")) + resp->nonce = ap_pstrdup(r->pool, value); + else if (!strcasecmp(key, "uri")) + resp->uri = ap_pstrdup(r->pool, value); + else if (!strcasecmp(key, "response")) + resp->digest = ap_pstrdup(r->pool, value); + else if (!strcasecmp(key, "algorithm")) + resp->algorithm = ap_pstrdup(r->pool, value); + else if (!strcasecmp(key, "cnonce")) + resp->cnonce = ap_pstrdup(r->pool, value); + else if (!strcasecmp(key, "opaque")) + resp->opaque = ap_pstrdup(r->pool, value); + else if (!strcasecmp(key, "qop")) + resp->message_qop = ap_pstrdup(r->pool, value); + else if (!strcasecmp(key, "nc")) + resp->nonce_count = ap_pstrdup(r->pool, value); + } + + if (!resp->username || !resp->realm || !resp->nonce || !resp->uri + || !resp->digest) { + resp->auth_hdr_sts = INVALID; + return !OK; + } + + if (resp->opaque) + resp->opaque_num = (unsigned long) strtol(resp->opaque, NULL, 16); + + resp->auth_hdr_sts = VALID; + return OK; +} + + +/* Because the browser may preemptively send auth info, incrementing the + * nonce-count when it does, and because the client does not get notified + * if the URI didn't need authentication after all, we need to be sure to + * update the nonce-count each time we receive an Authorization header no + * matter what the final outcome of the request. Furthermore this is a + * convenient place to get the request-uri (before any subrequests etc + * are initiated) and to initialize the request_config. + * + * Note that this must be called after mod_proxy had its go so that + * r->proxyreq is set correctly. + */ +static int update_nonce_count(request_rec *r) +{ + digest_header_rec *resp; + int res; + + if (!ap_is_initial_req(r)) + return DECLINED; + + resp = ap_pcalloc(r->pool, sizeof(digest_header_rec)); + resp->request_uri = &r->parsed_uri; + resp->needed_auth = 0; + ap_set_module_config(r->request_config, &digest_auth_module, resp); + + res = get_digest_rec(r, resp); + resp->client = get_client(resp->opaque_num, r); + if (res == OK && resp->client) + resp->client->nonce_count++; + + return DECLINED; +} + + +/* + * Nonce generation code + */ + +/* The hash part of the nonce is a SHA-1 hash of the time, realm, opaque, + * and our secret. + */ +static void gen_nonce_hash(char *hash, const char *timestr, const char *opaque, + const server_rec *server, + const digest_config_rec *conf) +{ + const char *hex = "0123456789abcdef"; + unsigned char sha1[SHA_DIGESTSIZE]; + AP_SHA1_CTX ctx; + int idx; + + memcpy(&ctx, &conf->nonce_ctx, sizeof(ctx)); + ap_SHA1Update_binary(&ctx, (const unsigned char *) server->server_hostname, + strlen(server->server_hostname)); + ap_SHA1Update_binary(&ctx, (const unsigned char *) &server->port, + sizeof(server->port)); + ap_SHA1Update_binary(&ctx, (const unsigned char *) timestr, strlen(timestr)); + if (opaque) + ap_SHA1Update_binary(&ctx, (const unsigned char *) opaque, + strlen(opaque)); + ap_SHA1Final(sha1, &ctx); + + for (idx=0; idx> 4]; + *hash++ = hex[sha1[idx] & 0xF]; + } + + *hash++ = '\0'; +} + + +/* The nonce has the format b64(time)+hash . + */ +static const char *gen_nonce(pool *p, time_t now, const char *opaque, + const server_rec *server, + const digest_config_rec *conf) +{ + char *nonce = ap_palloc(p, NONCE_LEN+1); + time_rec t; + + if (conf->nonce_lifetime != 0) + t.time = now; + else +#ifdef HAVE_SHMEM_MM + /* this counter is not synch'd, because it doesn't really matter + * if it counts exactly. + */ + t.time = (*otn_counter)++; +#else /* HAVE_SHMEM_MM */ + t.time = 42; +#endif /* HAVE_SHMEM_MM */ + ap_base64encode_binary(nonce, t.arr, sizeof(t.arr)); + gen_nonce_hash(nonce+NONCE_TIME_LEN, nonce, opaque, server, conf); + + return nonce; +} + + +/* + * Opaque and hash-table management + */ + +#ifdef HAVE_SHMEM_MM +/* + * Generate a new client entry, add it to the list, and return the + * entry. Returns NULL if failed. + */ +static client_entry *gen_client(const request_rec *r) +{ + unsigned long op; + client_entry new = { 0, NULL, 0, "", "" }, *entry; + + if (!opaque_mm) return 0; + + mm_lock(opaque_mm, MM_LOCK_RW); + op = (*opaque_cntr)++; + mm_unlock(opaque_mm); + + if (!(entry = add_client(op, &new, r->server))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "Digest: failed to allocate client entry - ignoring " + "client"); + return NULL; + } + + return entry; +} +#else /* HAVE_SHMEM_MM */ +static client_entry *gen_client(const request_rec *r) { return NULL; } +#endif /* HAVE_SHMEM_MM */ + + + +/* + * MD5-sess code. + * + * If you want to use algorithm=MD5-sess you must write get_userpw_hash() + * yourself (see below). The dummy provided here just returns the hash + * from the auth-file, i.e. it is only useful for testing client + * implementations of MD5-sess . + */ + +/* + * get_userpw_hash() will be called each time a new session needs to be + * generated and is expected to return the equivalent of + * + * ap_md5(r->pool, + * ap_pstrcat(r->pool, username, ":", ap_auth_name(r), ":", passwd)) + * + * You must implement this yourself, and will probably consist of code + * contacting the password server and retrieving the hash from it. + * + * TBD: This function should probably be in a seperate source file so that + * people need not modify mod_auth_digest.c each time they install a new version + * of apache. + */ +static const char *get_userpw_hash(const request_rec *r, + const digest_header_rec *resp, + const digest_config_rec *conf) +{ + /* for now, just get it from pwfile */ + return conf->ha1; +} + + +static const char *get_session(const request_rec *r, + digest_header_rec *resp, + const digest_config_rec *conf) +{ + const char *ha1 = NULL, *urp; + + /* get ha1 from client list */ + if (resp->opaque && resp->client) + ha1 = resp->client->ha1; + + /* generate new session if necessary */ + if (ha1 == NULL || ha1[0] == '\0') { + urp = get_userpw_hash(r, resp, conf); + ha1 = ap_md5(r->pool, + (unsigned char *) ap_pstrcat(r->pool, ha1, ":", resp->nonce, + ":", resp->cnonce, NULL)); + if (!resp->client) + resp->client = gen_client(r); + if (resp->client) + memcpy(resp->client->ha1, ha1, 17); + } + + return ha1; +} + + +static void clear_session(const digest_header_rec *resp) +{ + if (resp->client) + resp->client->ha1[0] = '\0'; +} + + +/* + * Authorization challenge generation code (for WWW-Authenticate) + */ + +static const char *guess_domain(pool *p, const char *uri, const char *filename, + const char *dir) +{ + size_t u_len = strlen(uri), f_len = strlen(filename), d_len = strlen(dir); + const char *u, *f; + + + /* Because of things like mod_alias and mod_rewrite and the fact that + * protection is often on a directory basis (not a location basis) it + * is hard to determine the uri to put in the domain attribute. + * + * What we do is the following: first we see if the directory is + * a prefix for the uri - if this is the case we assume that therefore + * a directive was protecting this uri and we can use it + * for the domain. + */ + if (u_len >= d_len && !memcmp(uri, dir, d_len)) + return dir; + + /* Now we check for , and if we find one we send back a + * dummy uri - this is the only way to specify that the protection + * space only covers a single uri. + */ + if (dir[0] != '/') + /* This doesn't work for Amaya (ok, it's of arguable validity in + * the first place), so just return the file name instead + return "http://0.0.0.0/"; + */ + return dir; + + /* Next we find the largest common common suffix of the request-uri + * and the final file name, ignoring any extensions; this gives us a + * hint as to where any rewriting could've occured (assuming that some + * prefix of the uri is rewritten, not a suffix). + */ + u = uri + u_len - 1; /* strip any extension */ + while (u > uri && *u != '/') u--; + while (*u && *u != '.') u++; + if (*u == '.') u--; + if (*u == '/') u--; + + f = filename + f_len - 1; /* strip any extension */ + while (f > filename && *f != '/') f--; + while (*f && *f != '.') f++; + if (*f == '.') f--; + if (*f == '/') f--; + + while (*f == *u && f > filename && u > uri) u--, f--; + f++; u++; + + while (*f && *f != '/') f++, u++; /* suffix must start with / */ + + /* Now, if the directory reaches into this common suffix then we can + * take the uri with the same reach. + */ + if ((unsigned long) (f-filename) < d_len) { + char *tmp = ap_pstrdup(p, uri); + tmp[(u-uri)+(d_len-(f-filename))] = '\0'; + return tmp; + } + + return ""; /* give up */ +} + + +static const char *ltox(pool *p, unsigned long num) +{ + if (num != 0) + return ap_psprintf(p, "%lx", num); + else + return ""; +} + +static void note_digest_auth_failure(request_rec *r, + const digest_config_rec *conf, + digest_header_rec *resp, int stale) +{ + const char *qop, *opaque, *opaque_param, *domain, *nonce; + int cnt; + + + /* Setup qop */ + + if (conf->qop_list[0] == NULL) + qop = ", qop=\"auth\""; + else if (!strcasecmp(conf->qop_list[0], "none")) + qop = ""; + else { + qop = ap_pstrcat(r->pool, ", qop=\"", conf->qop_list[0], NULL); + for (cnt=1; conf->qop_list[cnt] != NULL; cnt++) + qop = ap_pstrcat(r->pool, qop, ",", conf->qop_list[cnt], NULL); + qop = ap_pstrcat(r->pool, qop, "\"", NULL); + } + + /* MD5-sess stuff */ + + if (!stale && !strcasecmp(conf->algorithm, "MD5-sess")) + clear_session(resp); + + /* Setup opaque */ + + if (resp->opaque == NULL) { + /* new client */ + if ((conf->check_nc || conf->nonce_lifetime == 0 + || !strcasecmp(conf->algorithm, "MD5-sess")) + && (resp->client = gen_client(r)) != NULL) + opaque = ltox(r->pool, resp->client->key); + else + opaque = ""; /* opaque not needed */ + } + else if (resp->client == NULL) { + /* client info was gc'd */ + resp->client = gen_client(r); + if (resp->client != NULL) { + opaque = ltox(r->pool, resp->client->key); + stale = 1; + client_list->num_renewed++; + } + else + opaque = ""; /* ??? */ + } + else { + opaque = resp->opaque; + /* we're generating a new nonce, so reset the nonce-count */ + resp->client->nonce_count = 0; + } + + if (opaque[0]) + opaque_param = ap_pstrcat(r->pool, ", opaque=\"", opaque, "\"", NULL); + else + opaque_param = NULL; + + /* Setup nonce */ + + nonce = gen_nonce(r->pool, r->request_time, opaque, r->server, conf); + if (resp->client && conf->nonce_lifetime == 0) + memcpy(resp->client->last_nonce, nonce, NONCE_LEN+1); + + /* setup domain attribute. We want to send this attribute wherever + * possible so that the client won't send the Authorization header + * unneccessarily (it's usually > 200 bytes!). + */ + + if (conf->uri_list) + domain = conf->uri_list; + else { + /* They didn't specify any domain, so let's guess at it */ + domain = guess_domain(r->pool, resp->request_uri->path, r->filename, + conf->dir_name); + if (domain[0] == '/' && domain[1] == '\0') + domain = ""; /* "/" is the default, so no need to send it */ + else + domain = ap_pstrcat(r->pool, ", domain=\"", domain, "\"", NULL); + } + + ap_table_mergen(r->err_headers_out, + r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate", + ap_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%s\", " + "algorithm=%s%s%s%s%s", + ap_auth_name(r), nonce, conf->algorithm, + opaque_param ? opaque_param : "", + domain ? domain : "", + stale ? ", stale=true" : "", qop)); +} + + +/* + * Authorization header verification code + */ + +static const char *get_hash(request_rec *r, const char *user, + const char *realm, const char *auth_pwfile) +{ + configfile_t *f; + char l[MAX_STRING_LEN]; + const char *rpw; + char *w, *x; + + if (!(f = ap_pcfg_openfile(r->pool, auth_pwfile))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "Digest: Could not open password file: %s", auth_pwfile); + return NULL; + } + while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) { + if ((l[0] == '#') || (!l[0])) + continue; + rpw = l; + w = ap_getword(r->pool, &rpw, ':'); + x = ap_getword(r->pool, &rpw, ':'); + + if (x && w && !strcmp(user, w) && !strcmp(realm, x)) { + ap_cfg_closefile(f); + return ap_pstrdup(r->pool, rpw); + } + } + ap_cfg_closefile(f); + return NULL; +} + +static int check_nc(const request_rec *r, const digest_header_rec *resp, + const digest_config_rec *conf) +{ + if (conf->check_nc && client_mm) { + unsigned long nc; + + const char *snc = resp->nonce_count; + char *endptr; + + nc = strtol(snc, &endptr, 16); + if (endptr < (snc+strlen(snc)) && !ap_isspace(*endptr)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: invalid nc %s received - not a number", snc); + return !OK; + } + + if (!resp->client) + return !OK; + + if (nc != resp->client->nonce_count) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r, + "nonce-count check failed: %lu != %lu", nc, + resp->client->nonce_count); + return !OK; + } + } + + return OK; +} + +static int check_nonce(request_rec *r, digest_header_rec *resp, + const digest_config_rec *conf) +{ + double dt; + time_rec nonce_time; + char tmp, hash[NONCE_HASH_LEN+1]; + + if (strlen(resp->nonce) != NONCE_LEN) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: invalid nonce %s received - length is not %d", + resp->nonce, NONCE_LEN); + note_digest_auth_failure(r, conf, resp, 1); + return AUTH_REQUIRED; + } + + tmp = resp->nonce[NONCE_TIME_LEN]; + resp->nonce[NONCE_TIME_LEN] = '\0'; + ap_base64decode_binary(nonce_time.arr, resp->nonce); + gen_nonce_hash(hash, resp->nonce, resp->opaque, r->server, conf); + resp->nonce[NONCE_TIME_LEN] = tmp; + resp->nonce_time = nonce_time.time; + + if (strcmp(hash, resp->nonce+NONCE_TIME_LEN)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: invalid nonce %s received - hash is not %s", + resp->nonce, hash); + note_digest_auth_failure(r, conf, resp, 1); + return AUTH_REQUIRED; + } + + dt = difftime(r->request_time, nonce_time.time); + if (conf->nonce_lifetime > 0 && dt < 0) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: invalid nonce %s received - user attempted " + "time travel", resp->nonce); + note_digest_auth_failure(r, conf, resp, 1); + return AUTH_REQUIRED; + } + + if (conf->nonce_lifetime > 0) { + if (dt > conf->nonce_lifetime) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r, + "Digest: user %s: nonce expired - sending new nonce", + r->connection->user); + note_digest_auth_failure(r, conf, resp, 1); + return AUTH_REQUIRED; + } + } + else if (conf->nonce_lifetime == 0 && resp->client) { + if (memcmp(resp->client->last_nonce, resp->nonce, NONCE_LEN)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r, + "Digest: user %s: one-time-nonce mismatch - sending " + "new nonce", r->connection->user); + note_digest_auth_failure(r, conf, resp, 1); + return AUTH_REQUIRED; + } + } + /* else (lifetime < 0) => never expires */ + + return OK; +} + +/* The actual MD5 code... whee */ + +static const char *old_digest(const request_rec *r, + const digest_header_rec *resp, const char *ha1) +{ + const char *ha2; + + /* rfc-2069 */ + ha2 = ap_md5(r->pool, (unsigned char *)ap_pstrcat(r->pool, r->method, ":", + resp->uri, NULL)); + return ap_md5(r->pool, + (unsigned char *)ap_pstrcat(r->pool, ha1, ":", resp->nonce, + ":", ha2, NULL)); +} + +static const char *new_digest(const request_rec *r, + digest_header_rec *resp, + const digest_config_rec *conf) +{ + const char *ha1, *ha2, *a2; + + /* draft-ietf-http-authentication-03 */ + if (resp->algorithm && !strcasecmp(resp->algorithm, "MD5-sess")) + ha1 = get_session(r, resp, conf); + else + ha1 = conf->ha1; + + if (resp->message_qop && !strcasecmp(resp->message_qop, "auth-int")) + a2 = ap_pstrcat(r->pool, r->method, ":", resp->uri, ":", + ap_md5(r->pool, (const unsigned char*) ""), NULL); /* TBD */ + else + a2 = ap_pstrcat(r->pool, r->method, ":", resp->uri, NULL); + ha2 = ap_md5(r->pool, (const unsigned char *)a2); + + return ap_md5(r->pool, + (unsigned char *)ap_pstrcat(r->pool, ha1, ":", resp->nonce, + ":", resp->nonce_count, ":", + resp->cnonce, ":", + resp->message_qop, ":", ha2, + NULL)); +} + + +/* These functions return 0 if client is OK, and proper error status + * if not... either AUTH_REQUIRED, if we made a check, and it failed, or + * SERVER_ERROR, if things are so totally confused that we couldn't + * figure out how to tell if the client is authorized or not. + * + * If they return DECLINED, and all other modules also decline, that's + * treated by the server core as a configuration error, logged and + * reported as such. + */ + +/* Determine user ID, and check if the attributes are correct, if it + * really is that user, if the nonce is correct, etc. + */ + +static int authenticate_digest_user(request_rec *r) +{ + digest_config_rec *conf; + digest_header_rec *resp; + request_rec *mainreq; + conn_rec *conn = r->connection; + const char *t; + int res; + + + /* do we require Digest auth for this URI? */ + + if (!(t = ap_auth_type(r)) || strcasecmp(t, "Digest")) + return DECLINED; + + if (!ap_auth_name(r)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: need AuthName: %s", r->uri); + return SERVER_ERROR; + } + + + /* get the client response and mark */ + + mainreq = r; + while (mainreq->main != NULL) mainreq = mainreq->main; + while (mainreq->prev != NULL) mainreq = mainreq->prev; + resp = (digest_header_rec *) ap_get_module_config(mainreq->request_config, + &digest_auth_module); + resp->needed_auth = 1; + + + /* get our conf */ + + conf = (digest_config_rec *) ap_get_module_config(r->per_dir_config, + &digest_auth_module); + + + /* check for existence and syntax of Auth header */ + + if (resp->auth_hdr_sts != VALID) { + if (resp->auth_hdr_sts == NOT_DIGEST) + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: client used wrong authentication scheme " + "`%s': %s", resp->scheme, r->uri); + else if (resp->auth_hdr_sts == INVALID) + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: missing user, realm, nonce, uri, or digest " + "in authorization header: %s", r->uri); + /* else (resp->auth_hdr_sts == NO_HEADER) */ + note_digest_auth_failure(r, conf, resp, 0); + return AUTH_REQUIRED; + } + + r->connection->user = (char *) resp->username; + r->connection->ap_auth_type = (char *) "Digest"; + + + /* check the auth attributes */ + + if (strcmp(resp->uri, resp->request_uri->path)) { + uri_components *r_uri = resp->request_uri, d_uri; + ap_parse_uri_components(r->pool, resp->uri, &d_uri); + + if ((d_uri.hostname && d_uri.hostname[0] != '\0' + && strcasecmp(d_uri.hostname, r->server->server_hostname)) + || (d_uri.port_str && d_uri.port != r->server->port) + || (!d_uri.port_str && r->server->port != 80) + || strcmp(d_uri.path, r_uri->path) + || (d_uri.query != r_uri->query + && (!d_uri.query || !r_uri->query + || strcmp(d_uri.query, r_uri->query))) + ) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: uri mismatch - <%s> does not match " + "request-uri <%s>", resp->uri, + ap_unparse_uri_components(r->pool, r_uri, 0)); + return BAD_REQUEST; + } + } + + if (resp->opaque && resp->opaque_num == 0) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: received invalid opaque - got `%s'", + resp->opaque); + note_digest_auth_failure(r, conf, resp, 0); + return AUTH_REQUIRED; + } + + if (strcmp(resp->realm, conf->realm)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: realm mismatch - got `%s' but expected `%s'", + resp->realm, conf->realm); + note_digest_auth_failure(r, conf, resp, 0); + return AUTH_REQUIRED; + } + + if (resp->algorithm != NULL + && strcasecmp(resp->algorithm, "MD5") + && strcasecmp(resp->algorithm, "MD5-sess")) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: unknown algorithm `%s' received: %s", + resp->algorithm, r->uri); + note_digest_auth_failure(r, conf, resp, 0); + return AUTH_REQUIRED; + } + + if (!conf->pwfile) + return DECLINED; + + if (!(conf->ha1 = get_hash(r, conn->user, conf->realm, conf->pwfile))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: user `%s' in realm `%s' not found: %s", + conn->user, conf->realm, r->uri); + note_digest_auth_failure(r, conf, resp, 0); + return AUTH_REQUIRED; + } + + if (resp->message_qop == NULL) { + /* old (rfc-2069) style digest */ + if (strcmp(resp->digest, old_digest(r, resp, conf->ha1))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: user %s: password mismatch: %s", conn->user, + r->uri); + note_digest_auth_failure(r, conf, resp, 0); + return AUTH_REQUIRED; + } + } + else { + int match = 0, idx; + for (idx=0; conf->qop_list[idx] != NULL; idx++) { + if (!strcasecmp(conf->qop_list[idx], resp->message_qop)) { + match = 1; + break; + } + } + + if (!match + && !(conf->qop_list[0] == NULL + && !strcasecmp(resp->message_qop, "auth"))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: invalid qop `%s' received: %s", + resp->message_qop, r->uri); + note_digest_auth_failure(r, conf, resp, 0); + return AUTH_REQUIRED; + } + + if (strcmp(resp->digest, new_digest(r, resp, conf))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: user %s: password mismatch: %s", conn->user, + r->uri); + note_digest_auth_failure(r, conf, resp, 0); + return AUTH_REQUIRED; + } + } + + if (check_nc(r, resp, conf) != OK) { + note_digest_auth_failure(r, conf, resp, 0); + return AUTH_REQUIRED; + } + + /* Note: this check is done last so that a "stale=true" can be + generated if the nonce is old */ + if ((res = check_nonce(r, resp, conf))) + return res; + + return OK; +} + + +/* + * Checking ID + */ + +static table *groups_for_user(request_rec *r, const char *user, + const char *grpfile) +{ + configfile_t *f; + table *grps = ap_make_table(r->pool, 15); + pool *sp; + char l[MAX_STRING_LEN]; + const char *group_name, *ll, *w; + + if (!(f = ap_pcfg_openfile(r->pool, grpfile))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "Digest: Could not open group file: %s", grpfile); + return NULL; + } + + sp = ap_make_sub_pool(r->pool); + + while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) { + if ((l[0] == '#') || (!l[0])) + continue; + ll = l; + ap_clear_pool(sp); + + group_name = ap_getword(sp, &ll, ':'); + + while (ll[0]) { + w = ap_getword_conf(sp, &ll); + if (!strcmp(w, user)) { + ap_table_setn(grps, ap_pstrdup(r->pool, group_name), "in"); + break; + } + } + } + + ap_cfg_closefile(f); + ap_destroy_pool(sp); + return grps; +} + + +static int digest_check_auth(request_rec *r) +{ + const digest_config_rec *conf = + (digest_config_rec *) ap_get_module_config(r->per_dir_config, + &digest_auth_module); + const char *user = r->connection->user; + int m = r->method_number; + int method_restricted = 0; + register int x; + const char *t, *w; + table *grpstatus; + const array_header *reqs_arr; + require_line *reqs; + + if (!(t = ap_auth_type(r)) || strcasecmp(t, "Digest")) + return DECLINED; + + reqs_arr = ap_requires(r); + /* If there is no "requires" directive, then any user will do. + */ + if (!reqs_arr) + return OK; + reqs = (require_line *) reqs_arr->elts; + + if (conf->grpfile) + grpstatus = groups_for_user(r, user, conf->grpfile); + else + grpstatus = NULL; + + for (x = 0; x < reqs_arr->nelts; x++) { + + if (!(reqs[x].method_mask & (1 << m))) + continue; + + method_restricted = 1; + + t = reqs[x].requirement; + w = ap_getword_white(r->pool, &t); + if (!strcasecmp(w, "valid-user")) + return OK; + else if (!strcasecmp(w, "user")) { + while (t[0]) { + w = ap_getword_conf(r->pool, &t); + if (!strcmp(user, w)) + return OK; + } + } + else if (!strcasecmp(w, "group")) { + if (!grpstatus) + return DECLINED; + + while (t[0]) { + w = ap_getword_conf(r->pool, &t); + if (ap_table_get(grpstatus, w)) + return OK; + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: access to %s failed, reason: unknown require " + "directive \"%s\"", r->uri, reqs[x].requirement); + return DECLINED; + } + } + + if (!method_restricted) + return OK; + + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Digest: access to %s failed, reason: user %s not allowed access", + r->uri, user); + + note_digest_auth_failure(r, conf, + (digest_header_rec *) ap_get_module_config(r->request_config, + &digest_auth_module), + 0); + return AUTH_REQUIRED; +} + + +/* + * Authorization-Info header code + */ + +#ifdef SEND_DIGEST +static const char *hdr(const table *tbl, const char *name) +{ + const char *val = ap_table_get(tbl, name); + if (val) + return val; + else + return ""; +} +#endif + +static int add_auth_info(request_rec *r) +{ + const digest_config_rec *conf = + (digest_config_rec *) ap_get_module_config(r->per_dir_config, + &digest_auth_module); + digest_header_rec *resp = + (digest_header_rec *) ap_get_module_config(r->request_config, + &digest_auth_module); + const char *ai = NULL, *digest = NULL, *nextnonce = ""; + + if (resp == NULL || !resp->needed_auth || conf == NULL) + return OK; + + + /* rfc-2069 digest + */ + if (resp->message_qop == NULL) { + /* old client, so calc rfc-2069 digest */ + +#ifdef SEND_DIGEST + /* most of this totally bogus because the handlers don't set the + * headers until the final handler phase (I wonder why this phase + * is called fixup when there's almost nothing you can fix up...) + * + * Because it's basically impossible to get this right (e.g. the + * Content-length is never set yet when we get here, and we can't + * calc the entity hash) it's best to just leave this #def'd out. + */ + char *entity_info = + ap_md5(r->pool, + (unsigned char *) ap_pstrcat(r->pool, + ap_unparse_uri_components(r->pool, + resp->request_uri, 0), ":", + r->content_type ? r->content_type : ap_default_type(r), ":", + hdr(r->headers_out, "Content-Length"), ":", + r->content_encoding ? r->content_encoding : "", ":", + hdr(r->headers_out, "Last-Modified"), ":", + r->no_cache && !ap_table_get(r->headers_out, "Expires") ? + ap_gm_timestr_822(r->pool, r->request_time) : + hdr(r->headers_out, "Expires"), + NULL)); + digest = + ap_md5(r->pool, + (unsigned char *)ap_pstrcat(r->pool, conf->ha1, ":", + resp->nonce, ":", + r->method, ":", + ap_gm_timestr_822(r->pool, r->request_time), ":", + entity_info, ":", + ap_md5(r->pool, (unsigned char *) ""), /* H(entity) - TBD */ + NULL)); +#endif + } + + + /* setup nextnonce + */ + if (conf->nonce_lifetime > 0) { + /* send nextnonce if current nonce will expire in less than 30 secs */ + if (difftime(r->request_time, resp->nonce_time) > (conf->nonce_lifetime-NEXTNONCE_DELTA)) { + nextnonce = ap_pstrcat(r->pool, ", nextnonce=\"", + gen_nonce(r->pool, r->request_time, + resp->opaque, r->server, conf), + "\"", NULL); + resp->client->nonce_count = 0; + } + } + else if (conf->nonce_lifetime == 0 && resp->client) { + const char *nonce = gen_nonce(r->pool, 0, resp->opaque, r->server, + conf); + nextnonce = ap_pstrcat(r->pool, ", nextnonce=\"", nonce, "\"", NULL); + memcpy(resp->client->last_nonce, nonce, NONCE_LEN+1); + } + /* else nonce never expires, hence no nextnonce */ + + + /* do rfc-2069 digest + */ + if (conf->qop_list[0] && !strcasecmp(conf->qop_list[0], "none") + && resp->message_qop == NULL) { + /* use only RFC-2069 format */ + if (digest) + ai = ap_pstrcat(r->pool, "digest=\"", digest, "\"", nextnonce,NULL); + else + ai = nextnonce; + } + else { + const char *resp_dig, *ha1, *a2, *ha2; + + /* calculate rspauth attribute + */ + if (resp->algorithm && !strcasecmp(resp->algorithm, "MD5-sess")) + ha1 = get_session(r, resp, conf); + else + ha1 = conf->ha1; + + if (resp->message_qop && !strcasecmp(resp->message_qop, "auth-int")) + a2 = ap_pstrcat(r->pool, ":", resp->uri, ":", + ap_md5(r->pool, (const unsigned char *) ""), NULL); /* TBD */ + else + a2 = ap_pstrcat(r->pool, ":", resp->uri, NULL); + ha2 = ap_md5(r->pool, (const unsigned char *)a2); + + resp_dig = ap_md5(r->pool, + (unsigned char *)ap_pstrcat(r->pool, ha1, ":", + resp->nonce, ":", + resp->nonce_count, ":", + resp->cnonce, ":", + resp->message_qop ? + resp->message_qop : "", + ":", ha2, NULL)); + + /* assemble Authentication-Info header + */ + ai = ap_pstrcat(r->pool, + "rspauth=\"", resp_dig, "\"", + nextnonce, + resp->cnonce ? ", cnonce=\"" : "", + resp->cnonce ? ap_escape_quotes(r->pool, resp->cnonce) : + "", + resp->cnonce ? "\"" : "", + resp->nonce_count ? ", nc=" : "", + resp->nonce_count ? resp->nonce_count : "", + resp->message_qop ? ", qop=" : "", + resp->message_qop ? resp->message_qop : "", + digest ? "digest=\"" : "", + digest ? digest : "", + digest ? "\"" : "", + NULL); + } + + if (ai && ai[0]) + ap_table_mergen(r->headers_out, + r->proxyreq ? "Proxy-Authentication-Info" : + "Authentication-Info", + ai); + return OK; +} + + +module MODULE_VAR_EXPORT digest_auth_module = +{ + STANDARD_MODULE_STUFF, + initialize_module, /* initializer */ + create_digest_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + digest_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + authenticate_digest_user, /* check_user_id */ + digest_check_auth, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + add_auth_info, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + update_nonce_count /* post read-request */ +}; + diff --git a/modules/aaa/mod_auth_digest.dsp b/modules/aaa/mod_auth_digest.dsp new file mode 100644 index 00000000000..2f700573f5f --- /dev/null +++ b/modules/aaa/mod_auth_digest.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleAuthDigest" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleAuthDigest - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ApacheModuleAuthDigest.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 "ApacheModuleAuthDigest.mak"\ + CFG="ApacheModuleAuthDigest - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleAuthDigest - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleAuthDigest - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleAuthDigest - 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 ".\ApacheModuleAuthDigestR" +# PROP Intermediate_Dir ".\ApacheModuleAuthDigestR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\include" /I ".\\" /I "..\..\lib\apr\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ApacheCore.lib aprlib.lib kernel32.lib /nologo /subsystem:windows /dll /map /machine:I386 /libpath:"..\..\CoreR" /libpath:"..\..\lib\apr\Release" + +!ELSEIF "$(CFG)" == "ApacheModuleAuthDigest - 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 ".\ApacheModuleAuthDigestD" +# PROP Intermediate_Dir ".\ApacheModuleAuthDigestD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I "..\..\include" /I ".\\" /I "..\..\lib\apr\include" /D "_DEBUG" /D "SHARED_MODULE" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ApacheCore.lib aprlib.lib kernel32.lib /nologo /subsystem:windows /dll /map /debug /machine:I386 /pdbtype:sept /libpath:"..\..\CoreD" /libpath:"..\..\lib\apr\Debug" + +!ENDIF + +# Begin Target + +# Name "ApacheModuleAuthDigest - Win32 Release" +# Name "ApacheModuleAuthDigest - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_auth_digest.c +# End Source File +# End Target +# End Project diff --git a/modules/arch/win32/mod_isapi.c b/modules/arch/win32/mod_isapi.c new file mode 100644 index 00000000000..fa002708ba5 --- /dev/null +++ b/modules/arch/win32/mod_isapi.c @@ -0,0 +1,569 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_isapi.c - Internet Server Application (ISA) module for Apache + * by Alexei Kosut + * + * This module implements Microsoft's ISAPI, allowing Apache (when running + * under Windows) to load Internet Server Applications (ISAPI extensions). + * It implements all of the ISAPI 2.0 specification, except for the + * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI + * extensions that use only synchronous I/O and are compatible with the + * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should + * function as well). + * + * To load, simply place the ISA in a location in the document tree. + * Then add an "AddHandler isapi-isa dll" into your config file. + * You should now be able to load ISAPI DLLs just be reffering to their + * URLs. Make sure the ExecCGI option is active in the directory + * the ISA is in. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_request.h" +#include "http_log.h" +#include "util_script.h" + +/* We use the exact same header file as the original */ +#include + +/* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER, + define this to conform */ +#define RELAX_HEADER_RULE + +module isapi_module; + +/* Our "Connection ID" structure */ + +typedef struct { + LPEXTENSION_CONTROL_BLOCK ecb; + request_rec *r; + int status; +} isapi_cid; + +/* Declare the ISAPI functions */ + +BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName, + LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer); +BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes, + DWORD dwReserved); +BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize); +BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest, + LPVOID lpvBuffer, LPDWORD lpdwSize, + LPDWORD lpdwDataType); + +/* + The optimiser blows it totally here. What happens is that autos are addressed relative to the + stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere + between setting isapi_entry and calling through it. We work around the problem by forcing it to + use frame pointers. +*/ +#pragma optimize("y",off) + +int isapi_handler (request_rec *r) { + LPEXTENSION_CONTROL_BLOCK ecb = + ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK)); + HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO)); + + HINSTANCE isapi_handle; + BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */ + DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */ + BOOL (*isapi_term)(DWORD); /* optional entry point 3 */ + + isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid)); + table *e = r->subprocess_env; + int retval; + + /* Use similar restrictions as CGIs */ + + if (!(ap_allow_options(r) & OPT_EXECCGI)) + return FORBIDDEN; + + if (r->finfo.st_mode == 0) + return NOT_FOUND; + + if (S_ISDIR(r->finfo.st_mode)) + return FORBIDDEN; + + /* Load the module */ + + if (!(isapi_handle = LoadLibraryEx(r->filename, NULL, + LOAD_WITH_ALTERED_SEARCH_PATH))) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "Could not load DLL: %s", r->filename); + return SERVER_ERROR; + } + + if (!(isapi_version = + (void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "DLL could not load GetExtensionVersion(): %s", r->filename); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + if (!(isapi_entry = + (void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "DLL could not load HttpExtensionProc(): %s", r->filename); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension")); + + /* Run GetExtensionVersion() */ + + if ((*isapi_version)(pVer) != TRUE) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "ISAPI GetExtensionVersion() failed: %s", r->filename); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + /* Set up variables */ + ap_add_common_vars(r); + ap_add_cgi_vars(r); + + /* Set up connection ID */ + ecb->ConnID = (HCONN)cid; + cid->ecb = ecb; + cid->r = r; + cid->status = 0; + + ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK); + ecb->dwVersion = MAKELONG(0, 2); + ecb->dwHttpStatusCode = 0; + strcpy(ecb->lpszLogData, ""); + ecb->lpszMethod = r->method; + ecb->lpszQueryString = ap_table_get(e, "QUERY_STRING"); + ecb->lpszPathInfo = ap_table_get(e, "PATH_INFO"); + ecb->lpszPathTranslated = ap_table_get(e, "PATH_TRANSLATED"); + ecb->lpszContentType = ap_table_get(e, "CONTENT_TYPE"); + + /* Set up client input */ + if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) { + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + return retval; + } + + if (ap_should_client_block(r)) { + /* Unlike IIS, which limits this to 48k, we read the whole + * sucker in. I suppose this could be bad for memory if someone + * uploaded the complete works of Shakespeare. Well, WebSite + * does the same thing. + */ + long to_read = atol(ap_table_get(e, "CONTENT_LENGTH")); + long read; + + /* Actually, let's cap it at 48k, until we figure out what + * to do with this... we don't want a Content-Length: 1000000000 + * taking out the machine. + */ + + if (to_read > 49152) { + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read); + + if ((read = ap_get_client_block(r, ecb->lpbData, to_read)) < 0) { + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + /* Although its not to spec, IIS seems to null-terminate + * its lpdData string. So we will too. To make sure + * cbAvailable matches cbTotalBytes, we'll up the latter + * and equalize them. + */ + ecb->cbAvailable = ecb->cbTotalBytes = read + 1; + ecb->lpbData[read] = '\0'; + } + else { + ecb->cbTotalBytes = 0; + ecb->cbAvailable = 0; + ecb->lpbData = NULL; + } + + /* Set up the callbacks */ + + ecb->GetServerVariable = &GetServerVariable; + ecb->WriteClient = &WriteClient; + ecb->ReadClient = &ReadClient; + ecb->ServerSupportFunction = &ServerSupportFunction; + + /* All right... try and load the sucker */ + retval = (*isapi_entry)(ecb); + + /* Set the status (for logging) */ + if (ecb->dwHttpStatusCode) + r->status = ecb->dwHttpStatusCode; + + /* Check for a log message - and log it */ + if (ecb->lpszLogData && strcmp(ecb->lpszLogData, "")) + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "%s: %s", ecb->lpszLogData, r->filename); + + /* All done with the DLL... get rid of it */ + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + + switch(retval) { + case HSE_STATUS_SUCCESS: + case HSE_STATUS_SUCCESS_AND_KEEP_CONN: + /* Ignore the keepalive stuff; Apache handles it just fine without + * the ISA's "advice". + */ + + if (cid->status) /* We have a special status to return */ + return cid->status; + + return OK; + case HSE_STATUS_PENDING: /* We don't support this */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, + "ISAPI asynchronous I/O not supported: %s", r->filename); + case HSE_STATUS_ERROR: + default: + return SERVER_ERROR; + } + +} +#pragma optimize("",on) + +BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName, + LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) { + request_rec *r = ((isapi_cid *)hConn)->r; + table *e = r->subprocess_env; + const char *result; + + /* Mostly, we just grab it from the environment, but there are + * a couple of special cases + */ + + if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) { + /* We don't support NT users, so this is always the same as + * REMOTE_USER + */ + result = ap_table_get(e, "REMOTE_USER"); + } + else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) { + /* Apache doesn't support secure requests inherently, so + * we have no way of knowing. We'll be conservative, and say + * all requests are insecure. + */ + result = "0"; + } + else if (!strcasecmp(lpszVariableName, "URL")) { + result = r->uri; + } + else { + result = ap_table_get(e, lpszVariableName); + } + + if (result) { + if (strlen(result) > *lpdwSizeofBuffer) { + *lpdwSizeofBuffer = strlen(result); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + strncpy(lpvBuffer, result, *lpdwSizeofBuffer); + return TRUE; + } + + /* Didn't find it */ + SetLastError(ERROR_INVALID_INDEX); + return FALSE; +} + +BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes, + DWORD dwReserved) { + request_rec *r = ((isapi_cid *)ConnID)->r; + int writ; /* written, actually, but why shouldn't I make up words? */ + + /* We only support synchronous writing */ + if (dwReserved && dwReserved != HSE_IO_SYNC) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, + "ISAPI asynchronous I/O not supported: %s", r->filename); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) { + SetLastError(ERROR); /* XXX: Find the right error code */ + return FALSE; + } + + *lpwdwBytes = writ; + return TRUE; +} + +BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) { + /* Doesn't need to do anything; we've read all the data already */ + return TRUE; +} + +/* XXX: There is an O(n^2) attack possible here. */ +BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest, + LPVOID lpvBuffer, LPDWORD lpdwSize, + LPDWORD lpdwDataType) { + isapi_cid *cid = (isapi_cid *)hConn; + request_rec *subreq, *r = cid->r; + char *data; + + switch (dwHSERequest) { + case HSE_REQ_SEND_URL_REDIRECT_RESP: + /* Set the status to be returned when the HttpExtensionProc() + * is done. + */ + ap_table_set (r->headers_out, "Location", lpvBuffer); + cid->status = cid->r->status = cid->ecb->dwHttpStatusCode = REDIRECT; + return TRUE; + + case HSE_REQ_SEND_URL: + /* Read any additional input */ + + if (r->remaining > 0) { + char argsbuffer[HUGE_STRING_LEN]; + + while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)); + } + + /* Reset the method to GET */ + r->method = ap_pstrdup(r->pool, "GET"); + r->method_number = M_GET; + + /* Don't let anyone think there's still data */ + ap_table_unset(r->headers_in, "Content-Length"); + + ap_internal_redirect((char *)lpvBuffer, r); + return TRUE; + + case HSE_REQ_SEND_RESPONSE_HEADER: + r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK"); + sscanf(r->status_line, "%d", &r->status); + cid->ecb->dwHttpStatusCode = r->status; + + /* Now fill in the HTTP headers, and the rest of it. Ick. + * lpdwDataType contains a string that has headers (in MIME + * format), a blank like, then (possibly) data. We need + * to parse it. + * + * Easy case first: + */ + if (!lpdwDataType) { + ap_send_http_header(r); + return TRUE; + } + + /* Make a copy - don't disturb the original */ + data = ap_pstrdup(r->pool, (char *)lpdwDataType); + + /* We *should* break before this while loop ends */ + while (*data) { + char *value, *lf = strchr(data, '\n'); + int p; + +#ifdef RELAX_HEADER_RULE + if (lf) + *lf = '\0'; +#else + if (!lf) { /* Huh? Invalid data, I think */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "ISA sent invalid headers: %s", r->filename); + SetLastError(ERROR); /* XXX: Find right error */ + return FALSE; + } + + /* Get rid of \n and \r */ + *lf = '\0'; +#endif + p = strlen(data); + if (p > 0 && data[p-1] == '\r') data[p-1] = '\0'; + + /* End of headers */ + if (*data == '\0') { +#ifdef RELAX_HEADER_RULE + if (lf) +#endif + data = lf + 1; /* Reset data */ + break; + } + + if (!(value = strchr(data, ':'))) { + SetLastError(ERROR); /* XXX: Find right error */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "ISA sent invalid headers", r->filename); + return FALSE; + } + + *value++ = '\0'; + while (*value && ap_isspace(*value)) ++value; + + /* Check all the special-case headers. Similar to what + * ap_scan_script_header_err() does (see that function for + * more detail) + */ + + if (!strcasecmp(data, "Content-Type")) { + char *tmp; + /* Nuke trailing whitespace */ + + char *endp = value + strlen(value) - 1; + while (endp > value && ap_isspace(*endp)) *endp-- = '\0'; + + tmp = ap_pstrdup (r->pool, value); + ap_str_tolower(tmp); + r->content_type = tmp; + } + else if (!strcasecmp(data, "Content-Length")) { + ap_table_set(r->headers_out, data, value); + } + else if (!strcasecmp(data, "Transfer-Encoding")) { + ap_table_set(r->headers_out, data, value); + } + else if (!strcasecmp(data, "Set-Cookie")) { + ap_table_add(r->err_headers_out, data, value); + } + else { + ap_table_merge(r->err_headers_out, data, value); + } + + /* Reset data */ +#ifdef RELAX_HEADER_RULE + if (!lf) { + data += p; + break; + } +#endif + data = lf + 1; + } + + /* All the headers should be set now */ + + ap_send_http_header(r); + + /* Any data left should now be sent directly */ + ap_rputs(data, r); + + return TRUE; + + case HSE_REQ_MAP_URL_TO_PATH: + /* Map a URL to a filename */ + subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer, + *lpdwSize), r); + + GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL); + + /* IIS puts a trailing slash on directories, Apache doesn't */ + + if (S_ISDIR (subreq->finfo.st_mode)) { + int l = strlen((char *)lpvBuffer); + + ((char *)lpvBuffer)[l] = '\\'; + ((char *)lpvBuffer)[l + 1] = '\0'; + } + + return TRUE; + + case HSE_REQ_DONE_WITH_SESSION: + /* Do nothing... since we don't support async I/O, they'll + * return from HttpExtensionProc soon + */ + return TRUE; + + /* We don't support all this async I/O, Microsoft-specific stuff */ + case HSE_REQ_IO_COMPLETION: + case HSE_REQ_TRANSMIT_FILE: + ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, + "ISAPI asynchronous I/O not supported: %s", r->filename); + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } +} + +handler_rec isapi_handlers[] = { +{ "isapi-isa", isapi_handler }, +{ NULL} +}; + +module isapi_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + NULL, /* server config */ + NULL, /* merge server config */ + NULL, /* command table */ + isapi_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/modules/cache/.indent.pro b/modules/cache/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/modules/cache/.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/cache/mod_file_cache.c b/modules/cache/mod_file_cache.c new file mode 100644 index 00000000000..81e269dc4c2 --- /dev/null +++ b/modules/cache/mod_file_cache.c @@ -0,0 +1,559 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * Author: mod_file_cache by Bill Stoddard + * Based on mod_mmap_static by Dean Gaudet + * + * v0.01: initial implementation + */ + +/* + Documentation: + + Some sites have a set of static files that are really busy, and + change infrequently (or even on a regular schedule). Save time + by caching open handles to these files. This module, unlike + mod_mmap_static, caches open file handles, not file content. + On systems (like Windows) with heavy system call overhead and + that have an efficient sendfile implementation, caching file handles + offers several advantages over caching content. First, the file system + can manage the memory, allowing infrequently hit cached files to + be paged out. Second, since caching open handles does not consume + significant resources, it will be possible to enable an AutoLoadCache + feature where static files are dynamically loaded in the cache + as the server runs. On systems that have file change notification, + this module can be enhanced to automatically garbage collect + cached files that change on disk. + + This module should work on Unix systems that have sendfile. Place + cachefile directives into your configuration to direct files to + be cached. + + cachefile /path/to/file1 + cachefile /path/to/file2 + ... + + These files are only cached when the server is restarted, so if you + change the list, or if the files are changed, then you'll need to + restart the server. + + To reiterate that point: if the files are modified *in place* + without restarting the server you may end up serving requests that + are completely bogus. You should update files by unlinking the old + copy and putting a new copy in place. + + There's no such thing as inheriting these files across vhosts or + whatever... place the directives in the main server only. + + Known problems: + + Don't use Alias or RewriteRule to move these files around... unless + you feel like paying for an extra stat() on each request. This is + a deficiency in the Apache API that will hopefully be solved some day. + The file will be served out of the file handle cache, but there will be + an extra stat() that's a waste. +*/ + +#include +#include +#include +#include +#include +#include + +#define CORE_PRIVATE + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_request.h" +#include "http_core.h" +#include "apr_mmap.h" + +module MODULE_VAR_EXPORT file_cache_module; +static ap_pool_t *context; +static int once_through = 0; + +typedef struct { +#if 1 + ap_file_t *file; +#else + ap_mmap_t *mm; +#endif + char *filename; + ap_finfo_t finfo; +} a_file; + +typedef struct { + ap_array_header_t *files; + ap_array_header_t *inode_sorted; +} a_server_config; + + +static void *create_server_config(ap_pool_t *p, server_rec *s) +{ + a_server_config *sconf = ap_palloc(p, sizeof(*sconf)); + + sconf->files = ap_make_array(p, 20, sizeof(a_file)); + sconf->inode_sorted = NULL; + return sconf; +} +#if 0 +static void pre_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp) +{ + context = pconf; +} +#endif +static ap_status_t open_file(ap_file_t **file, char* filename, int flg1, int flg2, + ap_pool_t *context) +{ + ap_status_t rv; +#ifdef WIN32 + /* The Windows file needs to be opened for overlapped i/o, which APR doesn't + * support. + */ + HANDLE hFile; + hFile = CreateFile(filename, /* pointer to name of the file */ + GENERIC_READ, /* access (read-write) mode */ + FILE_SHARE_READ, /* share mode */ + NULL, /* pointer to security attributes */ + OPEN_EXISTING, /* how to create */ + FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, /* file attributes */ + NULL); /* handle to file with attributes to copy */ + if (hFile != INVALID_HANDLE_VALUE) { + rv = ap_put_os_file(file, &hFile, context); + } + else { + rv = GetLastError(); + *file = NULL; + } +#else + rv = ap_open(file, filename, flg1, flg2, context); +#endif + + return rv; +} + +ap_status_t cleanup_mmap(void *sconfv) +{ + a_server_config *sconf = sconfv; + size_t n; + a_file *file; + + n = sconf->files->nelts; + file = (a_file *)sconf->files->elts; + while(n) { +#if 1 + ap_close(file->file); +#else + ap_mmap_delete(file->mm); +#endif + ++file; + --n; + } + return APR_SUCCESS; +} + +static const char *cachefile(cmd_parms *cmd, void *dummy, char *filename) +{ + a_server_config *sconf; + a_file *new_file; + a_file tmp; + ap_file_t *fd = NULL; +#if 0 + caddr_t mm; +#endif + ap_status_t rc; + /* canonicalize the file name */ + /* os_canonical... */ + if (ap_stat(&tmp.finfo, filename, NULL) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server, + "file_cache: unable to stat(%s), skipping", filename); + return NULL; + } + if (tmp.finfo.filetype != APR_REG) { + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server, + "file_cache: %s isn't a regular file, skipping", filename); + return NULL; + } + /* Note: open_file should call ap_open for Unix and CreateFile for Windows. + * The Windows file needs to be opened for async I/O to allow multiple threads + * to serve it up at once. + */ + rc = open_file(&fd, filename, APR_READ, APR_OS_DEFAULT, cmd->pool); //context); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server, + "file_cache: unable to open(%s, O_RDONLY), skipping", filename); + return NULL; + } +#if 1 + tmp.file = fd; +#else + if (ap_mmap_create(&tmp.mm, fd, 0, tmp.finfo.st_size, context) != APR_SUCCESS) { + int save_errno = errno; + ap_close(fd); + errno = save_errno; + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server, + "file_cache: unable to mmap %s, skipping", filename); + return NULL; + } + ap_close(fd); +#endif + tmp.filename = ap_pstrdup(cmd->pool, filename); + sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module); + new_file = ap_push_array(sconf->files); + *new_file = tmp; + if (sconf->files->nelts == 1) { + /* first one, register the cleanup */ + ap_register_cleanup(cmd->pool, sconf, cleanup_mmap, ap_null_cleanup); + } + return NULL; +} + +#ifdef WIN32 +/* Windows doesn't have inodes. This ifdef should be changed to + * something like HAVE_INODES + */ +static int file_compare(const void *av, const void *bv) +{ + const a_file *a = av; + const a_file *b = bv; + + return strcmp(a->filename, b->filename); +} +#else +static int inode_compare(const void *av, const void *bv) +{ + const a_file *a = *(a_file **)av; + const a_file *b = *(a_file **)bv; + long c; + + c = a->finfo.st_ino - b->finfo.st_ino; + if (c == 0) { + return a->finfo.st_dev - b->finfo.st_dev; + } + return c; +} +#endif +static void file_cache_post_config(ap_pool_t *p, ap_pool_t *plog, + ap_pool_t *ptemp, server_rec *s) +{ + a_server_config *sconf; + ap_array_header_t *inodes; + a_file *elts; + int nelts; + int i; + + context = p; + /* sort the elements of the main_server, by filename */ + sconf = ap_get_module_config(s->module_config, &file_cache_module); + elts = (a_file *)sconf->files->elts; + nelts = sconf->files->nelts; + qsort(elts, nelts, sizeof(a_file), file_compare); + + /* build an index by inode as well, speeds up the search in the handler */ +#ifndef WIN32 + inodes = ap_make_array(p, nelts, sizeof(a_file *)); + sconf->inode_sorted = inodes; + for (i = 0; i < nelts; ++i) { + *(a_file **)ap_push_array(inodes) = &elts[i]; + } + qsort(inodes->elts, nelts, sizeof(a_file *), inode_compare); +#endif + /* and make the virtualhosts share the same thing */ + for (s = s->next; s; s = s->next) { + ap_set_module_config(s->module_config, &file_cache_module, sconf); + } +} + +/* If it's one of ours, fill in r->finfo now to avoid extra stat()... this is a + * bit of a kludge, because we really want to run after core_translate runs. + */ +int core_translate_copy(request_rec *r) +{ + void *sconf = r->server->module_config; + core_server_config *conf = ap_get_module_config(sconf, &core_module); + + if (r->proxyreq) { + return HTTP_FORBIDDEN; + } + if ((r->uri[0] != '/') && strcmp(r->uri, "*")) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "Invalid URI in request %s", r->the_request); + return BAD_REQUEST; + } + + if (r->server->path + && !strncmp(r->uri, r->server->path, r->server->pathlen) + && (r->server->path[r->server->pathlen - 1] == '/' + || r->uri[r->server->pathlen] == '/' + || r->uri[r->server->pathlen] == '\0')) { + r->filename = ap_pstrcat(r->pool, conf->ap_document_root, + (r->uri + r->server->pathlen), NULL); + } + else { + /* + * Make sure that we do not mess up the translation by adding two + * /'s in a row. This happens under windows when the document + * root ends with a / + */ + if ((conf->ap_document_root[strlen(conf->ap_document_root)-1] == '/') + && (*(r->uri) == '/')) { + r->filename = ap_pstrcat(r->pool, conf->ap_document_root, r->uri+1, + NULL); + } + else { + r->filename = ap_pstrcat(r->pool, conf->ap_document_root, r->uri, + NULL); + } + + return OK; + } +} +static int file_cache_xlat(request_rec *r) +{ + a_server_config *sconf; + a_file tmp; + a_file *match; + int res; + +#ifdef WIN32 +/* + * This is really broken on Windows. The call to get the core_module config + * in core_translate_copy seg faults because 'core_module' is not exported + * properly and needs a thunk. + * Will be fixed when we get API_VAR_EXPORTS working correctly again + */ + return DECLINED; +#endif + + sconf = ap_get_module_config(r->server->module_config, &file_cache_module); + + /* we only operate when at least one cachefile directive was used */ + if (ap_is_empty_table(sconf->files)) + return DECLINED; + + res = core_translate_copy(r); + if (res == DECLINED || !r->filename) { + return res; + } + if (!r->filename) + return DECLINED; + tmp.filename = r->filename; + match = (a_file *)bsearch(&tmp, sconf->files->elts, sconf->files->nelts, + sizeof(a_file), file_compare); + if (match == NULL) + return DECLINED; + + /* shortcircuit the get_path_info() stat() calls and stuff */ + r->finfo = match->finfo; + return OK; +} + + +static int file_cache_handler(request_rec *r) +{ + a_server_config *sconf; + a_file tmp; + a_file *ptmp; + a_file **pmatch; + a_file *match; + int rangestatus, errstatus; + + /* we don't handle anything but GET */ + if (r->method_number != M_GET) return DECLINED; + + /* file doesn't exist, we won't be dealing with it */ + if (r->finfo.protection == 0) return DECLINED; + + sconf = ap_get_module_config(r->server->module_config, &file_cache_module); +#ifdef WIN32 + tmp.filename = r->filename; +#else + tmp.finfo.st_dev = r->finfo.st_dev; + tmp.finfo.st_ino = r->finfo.st_ino; +#endif + ptmp = &tmp; +#ifdef WIN32 + match = (a_file *)bsearch(ptmp, sconf->files->elts, + sconf->files->nelts, sizeof(a_file), file_compare); + if (match == NULL) { + return DECLINED; + } +#else + pmatch = (a_file **)bsearch(&ptmp, sconf->inode_sorted->elts, + sconf->inode_sorted->nelts, sizeof(a_file *), inode_compare); + if (pmatch == NULL) { + return DECLINED; + } + match = *pmatch; +#endif + + /* note that we would handle GET on this resource */ + r->allowed |= (1 << M_GET); + + /* This handler has no use for a request body (yet), but we still + * need to read and discard it if the client sent one. + */ + if ((errstatus = ap_discard_request_body(r)) != OK) + return errstatus; + + ap_update_mtime(r, match->finfo.mtime); + ap_set_last_modified(r); + ap_set_etag(r); + if (((errstatus = ap_meets_conditions(r)) != OK) + || (errstatus = ap_set_content_length (r, match->finfo.size))) { + return errstatus; + } + + rangestatus = ap_set_byterange(r); + ap_send_http_header(r); + + if (!r->header_only) { + long length = match->finfo.size; + ap_off_t offset = 0; +#if 1 + /* ap_bflush(r->connection->client->); */ + struct iovec iov; + ap_hdtr_t hdtr; + ap_hdtr_t *phdtr = &hdtr; + + /* frob the client buffer */ + iov.iov_base = r->connection->client->outbase; + iov.iov_len = r->connection->client->outcnt; + r->connection->client->outcnt = 0; + + /* initialize the ap_hdtr_t struct */ + phdtr->headers = &iov; + phdtr->numheaders = 1; + phdtr->trailers = NULL; + phdtr->numtrailers = 0; + + if (!rangestatus) { + iol_sendfile(r->connection->client->iol, + match->file, + phdtr, + &offset, + &length, + 0); + } + else { + while (ap_each_byterange(r, &offset, &length)) { + iol_sendfile(r->connection->client->iol, + match->file, + phdtr, + &offset, + &length, + 0); + phdtr = NULL; + } + } +#else + if (!rangestatus) { + ap_send_mmap (match->mm, r, 0, match->finfo.st_size); + } + else { + while (ap_each_byterange(r, &offset, &length)) { + ap_send_mmap(match->mm, r, offset, length); + } + } +#endif + } + + return OK; +} + +static command_rec mmap_cmds[] = +{ + {"cachefile", cachefile, NULL, RSRC_CONF, ITERATE, + "A space seperated list of files to mmap at config time"}, + {NULL} +}; + +static void register_hooks(void) +{ + /* static const char* const aszPre[]={"http_core.c",NULL}; */ + /* ap_hook_pre_config(pre_config,NULL,NULL,HOOK_MIDDLE); */ + ap_hook_post_config(file_cache_post_config, NULL, NULL, HOOK_MIDDLE); + ap_hook_translate_name(file_cache_xlat, NULL, NULL, HOOK_MIDDLE); + /* This trick doesn't work apparently because the translate hooks + are single shot. If the core_hook returns OK, then our hook is + not called. + ap_hook_translate_name(file_cache_xlat, aszPre, NULL, HOOK_MIDDLE); + */ + +}; + +static const handler_rec file_cache_handlers[] = +{ + { "*/*", file_cache_handler }, + { NULL } +}; + +module MODULE_VAR_EXPORT file_cache_module = +{ + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + create_server_config, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + mmap_cmds, /* command handlers */ + file_cache_handlers, /* handlers */ + register_hooks /* register hooks */ +}; diff --git a/modules/cache/mod_file_cache.dsp b/modules/cache/mod_file_cache.dsp new file mode 100644 index 00000000000..f5a10bf4a61 --- /dev/null +++ b/modules/cache/mod_file_cache.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleFileCache" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleFileCache - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ApacheModuleFileCache.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 "ApacheModuleFileCache.mak"\ + CFG="ApacheModuleFileCache - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleFileCache - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleFileCache - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleFileCache - 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 ".\ApacheModuleFileCacheR" +# PROP Intermediate_Dir ".\ApacheModuleFileCacheR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\include" /I ".\\" /I "..\..\lib\apr\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ApacheCore.lib aprlib.lib kernel32.lib /nologo /subsystem:windows /dll /map /machine:I386 /libpath:"..\..\CoreR" /libpath:"..\..\lib\apr\Release" + +!ELSEIF "$(CFG)" == "ApacheModuleFileCache - 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 ".\ApacheModuleFileCacheD" +# PROP Intermediate_Dir ".\ApacheModuleFileCacheD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I "..\..\include" /I ".\\" /I "..\..\lib\apr\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ApacheCore.lib aprlib.lib kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /pdbtype:sept /libpath:"..\..\CoreD" /libpath:"..\..\lib\apr\Debug" + +!ENDIF + +# Begin Target + +# Name "ApacheModuleFileCache - Win32 Release" +# Name "ApacheModuleFileCache - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_file_cache.c +# End Source File +# End Target +# End Project diff --git a/modules/echo/.indent.pro b/modules/echo/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/modules/echo/.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/echo/mod_echo.c b/modules/echo/mod_echo.c new file mode 100644 index 00000000000..210cfa708d4 --- /dev/null +++ b/modules/echo/mod_echo.c @@ -0,0 +1,82 @@ +#include "httpd.h" +#include "http_config.h" +#include "http_connection.h" + +API_VAR_EXPORT module echo_module; + +typedef struct + { + int bEnabled; + } EchoConfig; + +static void *create_echo_server_config(pool *p,server_rec *s) + { + EchoConfig *pConfig=ap_pcalloc(p,sizeof *pConfig); + + pConfig->bEnabled=0; + + return pConfig; + } + +static const char *echo_on(cmd_parms *cmd, void *dummy, char *arg) + { + EchoConfig *pConfig=ap_get_module_config(cmd->server->module_config, + &echo_module); + pConfig->bEnabled=1; + + return NULL; + } + +static int process_echo_connection(conn_rec *c) + { + char buf[1024]; + EchoConfig *pConfig=ap_get_module_config(c->base_server->module_config, + &echo_module); + + if(!pConfig->bEnabled) + return DECLINED; + + for( ; ; ) + { + int w; + int r=ap_bread(c->client,buf,sizeof buf); + if(r <= 0) + break; + w=ap_bwrite(c->client,buf,r); + if(w != r) + break; + ap_bflush(c->client); + } + return OK; + } + +static const command_rec echo_cmds[] = { +{ "ProtocolEcho", echo_on, NULL, RSRC_CONF, RAW_ARGS, + "Run an echo server on this host" }, +{ NULL } +}; + +static void register_hooks() + { + ap_hook_process_connection(process_echo_connection,NULL,NULL,HOOK_MIDDLE); + } + +API_VAR_EXPORT module echo_module = { + STANDARD20_MODULE_STUFF, + NULL, /* pre_command_line */ + NULL, /* pre_config */ + NULL, /* post_config */ + NULL, /* open_logs */ + NULL, /* child_init */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + create_echo_server_config, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + echo_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* pre-run fixups */ + register_hooks /* register hooks */ +}; diff --git a/modules/experimental/.cvsignore b/modules/experimental/.cvsignore new file mode 100644 index 00000000000..8b4c6e3da42 --- /dev/null +++ b/modules/experimental/.cvsignore @@ -0,0 +1,3 @@ +Makefile +*.lo +*.so diff --git a/modules/experimental/.indent.pro b/modules/experimental/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/modules/experimental/.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/experimental/README b/modules/experimental/README new file mode 100644 index 00000000000..77abc097c00 --- /dev/null +++ b/modules/experimental/README @@ -0,0 +1,53 @@ +README for Apache 1.2 Example Module +[April, 1997] + +The files in the src/modules/example directory under the Apache +distribution directory tree are provided as an example to those that +wish to write modules that use the Apache API. + +The main file is mod_example.c, which illustrates all the different +callback mechanisms and call syntaces. By no means does an add-on +module need to include routines for all of the callbacks - quite the +contrary! + +The example module is an actual working module. If you link it into +your server, enable the "example-handler" handler for a location, and then +browse to that location, you will see a display of some of the tracing +the example module did as the various callbacks were made. + +To include the example module in your server, follow the steps below: + + 1. Uncomment the "Module example_module" line near the bottom of + the src/Configuration file. If there isn't one, add it; it + should look like this: + + Module example_module modules/example/mod_example.o + + 2. Run the src/Configure script ("cd src; ./Configure"). This will + build the Makefile for the server itself, and update the + src/modules/Makefile for any additional modules you have + requested from beneath that subdirectory. + + 3. Make the server (run "make" in the src directory). + +To add another module of your own: + + A. mkdir src/modules/mymodule + B. cp src/modules/example/* src/modules/mymodule + C. Modify the files in the new directory + D. Follow steps [1] through [3] above, with appropriate changes. + +To activate the example module, include a block similar to the +following in your srm.conf file: + + + SetHandler example-handler + + +As an alternative, you can put the following into a .htaccess file and +then request the file "test.example" from that location: + + AddHandler example-handler .example + +After reloading/restarting your server, you should be able to browse +to this location and see the brief display mentioned earlier. diff --git a/modules/experimental/mod_example.c b/modules/experimental/mod_example.c new file mode 100644 index 00000000000..68a6b06fe3a --- /dev/null +++ b/modules/experimental/mod_example.c @@ -0,0 +1,1149 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * Apache example module. Provide demonstrations of how modules do things. + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_main.h" +#include "http_protocol.h" +#include "util_script.h" + +#include + +/*--------------------------------------------------------------------------*/ +/* */ +/* Data declarations. */ +/* */ +/* Here are the static cells and structure declarations private to our */ +/* module. */ +/* */ +/*--------------------------------------------------------------------------*/ + +/* + * Sample configuration record. Used for both per-directory and per-server + * configuration data. + * + * It's perfectly reasonable to have two different structures for the two + * different environments. The same command handlers will be called for + * both, though, so the handlers need to be able to tell them apart. One + * possibility is for both structures to start with an int which is zero for + * one and 1 for the other. + * + * Note that while the per-directory and per-server configuration records are + * available to most of the module handlers, they should be treated as + * READ-ONLY by all except the command and merge handlers. Sometimes handlers + * are handed a record that applies to the current location by implication or + * inheritance, and modifying it will change the rules for other locations. + */ +typedef struct excfg { + int cmode; /* Environment to which record applies (directory, + * server, or combination). + */ +#define CONFIG_MODE_SERVER 1 +#define CONFIG_MODE_DIRECTORY 2 +#define CONFIG_MODE_COMBO 3 /* Shouldn't ever happen. */ + int local; /* Boolean: "Example" directive declared here? */ + int congenital; /* Boolean: did we inherit an "Example"? */ + char *trace; /* Pointer to trace string. */ + char *loc; /* Location to which this record applies. */ +} excfg; + +/* + * Let's set up a module-local static cell to point to the accreting callback + * trace. As each API callback is made to us, we'll tack on the particulars + * to whatever we've already recorded. To avoid massive memory bloat as + * directories are walked again and again, we record the routine/environment + * the first time (non-request context only), and ignore subsequent calls for + * the same routine/environment. + */ +static const char *trace = NULL; +static table *static_calls_made = NULL; + +/* + * To avoid leaking memory from pools other than the per-request one, we + * allocate a module-private pool, and then use a sub-pool of that which gets + * freed each time we modify the trace. That way previous layers of trace + * data don't get lost. + */ +static pool *example_pool = NULL; +static pool *example_subpool = NULL; + +/* + * Declare ourselves so the configuration routines can find and know us. + * We'll fill it in at the end of the module. + */ +module example_module; + +/*--------------------------------------------------------------------------*/ +/* */ +/* The following pseudo-prototype declarations illustrate the parameters */ +/* passed to command handlers for the different types of directive */ +/* syntax. If an argument was specified in the directive definition */ +/* (look for "command_rec" below), it's available to the command handler */ +/* via the (void *) info field in the cmd_parms argument passed to the */ +/* handler (cmd->info for the examples below). */ +/* */ +/*--------------------------------------------------------------------------*/ + +/* + * Command handler for a NO_ARGS directive. + * + * static const char *handle_NO_ARGS(cmd_parms *cmd, void *mconfig); + */ + +/* + * Command handler for a RAW_ARGS directive. The "args" argument is the text + * of the commandline following the directive itself. + * + * static const char *handle_RAW_ARGS(cmd_parms *cmd, void *mconfig, + * const char *args); + */ + +/* + * Command handler for a FLAG directive. The single parameter is passed in + * "bool", which is either zero or not for Off or On respectively. + * + * static const char *handle_FLAG(cmd_parms *cmd, void *mconfig, int bool); + */ + +/* + * Command handler for a TAKE1 directive. The single parameter is passed in + * "word1". + * + * static const char *handle_TAKE1(cmd_parms *cmd, void *mconfig, + * char *word1); + */ + +/* + * Command handler for a TAKE2 directive. TAKE2 commands must always have + * exactly two arguments. + * + * static const char *handle_TAKE2(cmd_parms *cmd, void *mconfig, + * char *word1, char *word2); + */ + +/* + * Command handler for a TAKE3 directive. Like TAKE2, these must have exactly + * three arguments, or the parser complains and doesn't bother calling us. + * + * static const char *handle_TAKE3(cmd_parms *cmd, void *mconfig, + * char *word1, char *word2, char *word3); + */ + +/* + * Command handler for a TAKE12 directive. These can take either one or two + * arguments. + * - word2 is a NULL pointer if no second argument was specified. + * + * static const char *handle_TAKE12(cmd_parms *cmd, void *mconfig, + * char *word1, char *word2); + */ + +/* + * Command handler for a TAKE123 directive. A TAKE123 directive can be given, + * as might be expected, one, two, or three arguments. + * - word2 is a NULL pointer if no second argument was specified. + * - word3 is a NULL pointer if no third argument was specified. + * + * static const char *handle_TAKE123(cmd_parms *cmd, void *mconfig, + * char *word1, char *word2, char *word3); + */ + +/* + * Command handler for a TAKE13 directive. Either one or three arguments are + * permitted - no two-parameters-only syntax is allowed. + * - word2 and word3 are NULL pointers if only one argument was specified. + * + * static const char *handle_TAKE13(cmd_parms *cmd, void *mconfig, + * char *word1, char *word2, char *word3); + */ + +/* + * Command handler for a TAKE23 directive. At least two and as many as three + * arguments must be specified. + * - word3 is a NULL pointer if no third argument was specified. + * + * static const char *handle_TAKE23(cmd_parms *cmd, void *mconfig, + * char *word1, char *word2, char *word3); + */ + +/* + * Command handler for a ITERATE directive. + * - Handler is called once for each of n arguments given to the directive. + * - word1 points to each argument in turn. + * + * static const char *handle_ITERATE(cmd_parms *cmd, void *mconfig, + * char *word1); + */ + +/* + * Command handler for a ITERATE2 directive. + * - Handler is called once for each of the second and subsequent arguments + * given to the directive. + * - word1 is the same for each call for a particular directive instance (the + * first argument). + * - word2 points to each of the second and subsequent arguments in turn. + * + * static const char *handle_ITERATE2(cmd_parms *cmd, void *mconfig, + * char *word1, char *word2); + */ + +/*--------------------------------------------------------------------------*/ +/* */ +/* These routines are strictly internal to this module, and support its */ +/* operation. They are not referenced by any external portion of the */ +/* server. */ +/* */ +/*--------------------------------------------------------------------------*/ + +/* + * Locate our directory configuration record for the current request. + */ +static excfg *our_dconfig(request_rec *r) +{ + + return (excfg *) ap_get_module_config(r->per_dir_config, &example_module); +} + +#if 0 +/* + * Locate our server configuration record for the specified server. + */ +static excfg *our_sconfig(server_rec *s) +{ + + return (excfg *) ap_get_module_config(s->module_config, &example_module); +} + +/* + * Likewise for our configuration record for the specified request. + */ +static excfg *our_rconfig(request_rec *r) +{ + + return (excfg *) ap_get_module_config(r->request_config, &example_module); +} +#endif + +/* + * This routine sets up some module-wide cells if they haven't been already. + */ +static void setup_module_cells() +{ + /* + * If we haven't already allocated our module-private pool, do so now. + */ + if (example_pool == NULL) { + example_pool = ap_make_sub_pool(NULL); + }; + /* + * Likewise for the table of routine/environment pairs we visit outside of + * request context. + */ + if (static_calls_made == NULL) { + static_calls_made = ap_make_table(example_pool, 16); + }; +} + +/* + * This routine is used to add a trace of a callback to the list. We're + * passed the server record (if available), the request record (if available), + * a pointer to our private configuration record (if available) for the + * environment to which the callback is supposed to apply, and some text. We + * turn this into a textual representation and add it to the tail of the list. + * The list can be displayed by the example_handler() routine. + * + * If the call occurs within a request context (i.e., we're passed a request + * record), we put the trace into the request pool and attach it to the + * request via the notes mechanism. Otherwise, the trace gets added + * to the static (non-request-specific) list. + * + * Note that the r->notes table is only for storing strings; if you need to + * maintain per-request data of any other type, you need to use another + * mechanism. + */ + +#define TRACE_NOTE "example-trace" + +static void trace_add(server_rec *s, request_rec *r, excfg *mconfig, + const char *note) +{ + + const char *sofar; + char *addon; + char *where; + pool *p; + const char *trace_copy; + + /* + * Make sure our pools and tables are set up - we need 'em. + */ + setup_module_cells(); + /* + * Now, if we're in request-context, we use the request pool. + */ + if (r != NULL) { + p = r->pool; + if ((trace_copy = ap_table_get(r->notes, TRACE_NOTE)) == NULL) { + trace_copy = ""; + } + } + else { + /* + * We're not in request context, so the trace gets attached to our + * module-wide pool. We do the create/destroy every time we're called + * in non-request context; this avoids leaking memory in some of + * the subsequent calls that allocate memory only once (such as the + * key formation below). + * + * Make a new sub-pool and copy any existing trace to it. Point the + * trace cell at the copied value. + */ + p = ap_make_sub_pool(example_pool); + if (trace != NULL) { + trace = ap_pstrdup(p, trace); + } + /* + * Now, if we have a sub-pool from before, nuke it and replace with + * the one we just allocated. + */ + if (example_subpool != NULL) { + ap_destroy_pool(example_subpool); + } + example_subpool = p; + trace_copy = trace; + } + /* + * If we weren't passed a configuration record, we can't figure out to + * what location this call applies. This only happens for co-routines + * that don't operate in a particular directory or server context. If we + * got a valid record, extract the location (directory or server) to which + * it applies. + */ + where = (mconfig != NULL) ? mconfig->loc : "nowhere"; + where = (where != NULL) ? where : ""; + /* + * Now, if we're not in request context, see if we've been called with + * this particular combination before. The table is allocated in the + * module's private pool, which doesn't get destroyed. + */ + if (r == NULL) { + char *key; + + key = ap_pstrcat(p, note, ":", where, NULL); + if (ap_table_get(static_calls_made, key) != NULL) { + /* + * Been here, done this. + */ + return; + } + else { + /* + * First time for this combination of routine and environment - + * log it so we don't do it again. + */ + ap_table_set(static_calls_made, key, "been here"); + } + } + addon = ap_pstrcat(p, "

    • \n", "
      \n", "
      ", + note, "\n", "
      \n", "
      [", + where, "]\n", "
      \n", "
      \n", + "
    • \n", NULL); + sofar = (trace_copy == NULL) ? "" : trace_copy; + trace_copy = ap_pstrcat(p, sofar, addon, NULL); + if (r != NULL) { + ap_table_set(r->notes, TRACE_NOTE, trace_copy); + } + else { + trace = trace_copy; + } + /* + * You *could* change the following if you wanted to see the calling + * sequence reported in the server's error_log, but beware - almost all of + * these co-routines are called for every single request, and the impact + * on the size (and readability) of the error_log is considerable. + */ +#define EXAMPLE_LOG_EACH 0 + if (EXAMPLE_LOG_EACH && (s != NULL)) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, s, "mod_example: %s", note); + } +} + +/*--------------------------------------------------------------------------*/ +/* We prototyped the various syntax for command handlers (routines that */ +/* are called when the configuration parser detects a directive declared */ +/* by our module) earlier. Now we actually declare a "real" routine that */ +/* will be invoked by the parser when our "real" directive is */ +/* encountered. */ +/* */ +/* If a command handler encounters a problem processing the directive, it */ +/* signals this fact by returning a non-NULL pointer to a string */ +/* describing the problem. */ +/* */ +/* The magic return value DECLINE_CMD is used to deal with directives */ +/* that might be declared by multiple modules. If the command handler */ +/* returns NULL, the directive was processed; if it returns DECLINE_CMD, */ +/* the next module (if any) that declares the directive is given a chance */ +/* at it. If it returns any other value, it's treated as the text of an */ +/* error message. */ +/*--------------------------------------------------------------------------*/ +/* + * Command handler for the NO_ARGS "Example" directive. All we do is mark the + * call in the trace log, and flag the applicability of the directive to the + * current location in that location's configuration record. + */ +static const char *cmd_example(cmd_parms *cmd, void *mconfig) +{ + + excfg *cfg = (excfg *) mconfig; + + /* + * "Example Wuz Here" + */ + cfg->local = 1; + trace_add(cmd->server, NULL, cfg, "cmd_example()"); + return NULL; +} + +/*--------------------------------------------------------------------------*/ +/* */ +/* Now we declare our content handlers, which are invoked when the server */ +/* encounters a document which our module is supposed to have a chance to */ +/* see. (See mod_mime's SetHandler and AddHandler directives, and the */ +/* mod_info and mod_status examples, for more details.) */ +/* */ +/* Since content handlers are dumping data directly into the connexion */ +/* (using the r*() routines, such as rputs() and rprintf()) without */ +/* intervention by other parts of the server, they need to make */ +/* sure any accumulated HTTP headers are sent first. This is done by */ +/* calling send_http_header(). Otherwise, no header will be sent at all, */ +/* and the output sent to the client will actually be HTTP-uncompliant. */ +/*--------------------------------------------------------------------------*/ +/* + * Sample content handler. All this does is display the call list that has + * been built up so far. + * + * The return value instructs the caller concerning what happened and what to + * do next: + * OK ("we did our thing") + * DECLINED ("this isn't something with which we want to get involved") + * HTTP_mumble ("an error status should be reported") + */ +static int example_handler(request_rec *r) +{ + + excfg *dcfg; + + dcfg = our_dconfig(r); + trace_add(r->server, r, dcfg, "example_handler()"); + /* + * We're about to start sending content, so we need to force the HTTP + * headers to be sent at this point. Otherwise, no headers will be sent + * at all. We can set any we like first, of course. **NOTE** Here's + * where you set the "Content-type" header, and you do so by putting it in + * r->content_type, *not* r->headers_out("Content-type"). If you don't + * set it, it will be filled in with the server's default type (typically + * "text/plain"). You *must* also ensure that r->content_type is lower + * case. + * + * We also need to start a timer so the server can know if the connexion + * is broken. + */ + r->content_type = "text/html"; + ap_soft_timeout("send example call trace", r); + ap_send_http_header(r); + /* + * If we're only supposed to send header information (HEAD request), we're + * already there. + */ + if (r->header_only) { + ap_kill_timeout(r); + return OK; + } + + /* + * Now send our actual output. Since we tagged this as being + * "text/html", we need to embed any HTML. + */ + ap_rputs(DOCTYPE_HTML_3_2, r); + ap_rputs("\n", r); + ap_rputs(" \n", r); + ap_rputs(" mod_example Module Content-Handler Output\n", r); + ap_rputs(" \n", r); + ap_rputs(" \n", r); + ap_rputs(" \n", r); + ap_rputs("

      mod_example Module Content-Handler Output\n", r); + ap_rputs("

      \n", r); + ap_rputs("

      \n", r); + ap_rprintf(r, " Apache HTTP Server version: \"%s\"\n", + ap_get_server_version()); + ap_rputs("
      \n", r); + ap_rprintf(r, " Server built: \"%s\"\n", ap_get_server_built()); + ap_rputs("

      \n", r);; + ap_rputs("

      \n", r); + ap_rputs(" The format for the callback trace is:\n", r); + ap_rputs("

      \n", r); + ap_rputs("
      \n", r); + ap_rputs("
      n.<routine-name>", r); + ap_rputs("(<routine-data>)\n", r); + ap_rputs("
      \n", r); + ap_rputs("
      [<applies-to>]\n", r); + ap_rputs("
      \n", r); + ap_rputs("
      \n", r); + ap_rputs("

      \n", r); + ap_rputs(" The <routine-data> is supplied by\n", r); + ap_rputs(" the routine when it requests the trace,\n", r); + ap_rputs(" and the <applies-to> is extracted\n", r); + ap_rputs(" from the configuration record at the time of the trace.\n", r); + ap_rputs(" SVR() indicates a server environment\n", r); + ap_rputs(" (blank means the main or default server, otherwise it's\n", r); + ap_rputs(" the name of the VirtualHost); DIR()\n", r); + ap_rputs(" indicates a location in the URL or filesystem\n", r); + ap_rputs(" namespace.\n", r); + ap_rputs("

      \n", r); + ap_rprintf(r, "

      Static callbacks so far:

      \n
        \n%s
      \n", + trace); + ap_rputs("

      Request-specific callbacks so far:

      \n", r); + ap_rprintf(r, "
        \n%s
      \n", ap_table_get(r->notes, TRACE_NOTE)); + ap_rputs("

      Environment for this call:

      \n", r); + ap_rputs("
        \n", r); + ap_rprintf(r, "
      • Applies-to: %s\n
      • \n", dcfg->loc); + ap_rprintf(r, "
      • \"Example\" directive declared here: %s\n
      • \n", + (dcfg->local ? "YES" : "NO")); + ap_rprintf(r, "
      • \"Example\" inherited: %s\n
      • \n", + (dcfg->congenital ? "YES" : "NO")); + ap_rputs("
      \n", r); + ap_rputs(" \n", r); + ap_rputs("\n", r); + /* + * We're all done, so cancel the timeout we set. Since this is probably + * the end of the request we *could* assume this would be done during + * post-processing - but it's possible that another handler might be + * called and inherit our outstanding timer. Not good; to each its own. + */ + ap_kill_timeout(r); + /* + * We did what we wanted to do, so tell the rest of the server we + * succeeded. + */ + return OK; +} + +/*--------------------------------------------------------------------------*/ +/* */ +/* Now let's declare routines for each of the callback phase in order. */ +/* (That's the order in which they're listed in the callback list, *not */ +/* the order in which the server calls them! See the command_rec */ +/* declaration near the bottom of this file.) Note that these may be */ +/* called for situations that don't relate primarily to our function - in */ +/* other words, the fixup handler shouldn't assume that the request has */ +/* to do with "example" stuff. */ +/* */ +/* With the exception of the content handler, all of our routines will be */ +/* called for each request, unless an earlier handler from another module */ +/* aborted the sequence. */ +/* */ +/* Handlers that are declared as "int" can return the following: */ +/* */ +/* OK Handler accepted the request and did its thing with it. */ +/* DECLINED Handler took no action. */ +/* HTTP_mumble Handler looked at request and found it wanting. */ +/* */ +/* What the server does after calling a module handler depends upon the */ +/* handler's return value. In all cases, if the handler returns */ +/* DECLINED, the server will continue to the next module with an handler */ +/* for the current phase. However, if the handler return a non-OK, */ +/* non-DECLINED status, the server aborts the request right there. If */ +/* the handler returns OK, the server's next action is phase-specific; */ +/* see the individual handler comments below for details. */ +/* */ +/*--------------------------------------------------------------------------*/ +/* + * This function is called during server initialisation. Any information + * that needs to be recorded must be in static cells, since there's no + * configuration record. + * + * There is no return value. + */ + +/* + * All our module-initialiser does is add its trace to the log. + */ +static void example_init(server_rec *s, pool *p) +{ + + char *note; + char *sname = s->server_hostname; + + /* + * Set up any module cells that ought to be initialised. + */ + setup_module_cells(); + /* + * The arbitrary text we add to our trace entry indicates for which server + * we're being called. + */ + sname = (sname != NULL) ? sname : ""; + note = ap_pstrcat(p, "example_init(", sname, ")", NULL); + trace_add(s, NULL, NULL, note); +} + +/* + * This function is called during server initialisation when an heavy-weight + * process (such as a child) is being initialised. As with the + * module-initialisation function, any information that needs to be recorded + * must be in static cells, since there's no configuration record. + * + * There is no return value. + */ + +/* + * All our process-initialiser does is add its trace to the log. + */ +static void example_child_init(server_rec *s, pool *p) +{ + + char *note; + char *sname = s->server_hostname; + + /* + * Set up any module cells that ought to be initialised. + */ + setup_module_cells(); + /* + * The arbitrary text we add to our trace entry indicates for which server + * we're being called. + */ + sname = (sname != NULL) ? sname : ""; + note = ap_pstrcat(p, "example_child_init(", sname, ")", NULL); + trace_add(s, NULL, NULL, note); +} + +/* + * This function is called when an heavy-weight process (such as a child) is + * being run down or destroyed. As with the child-initialisation function, + * any information that needs to be recorded must be in static cells, since + * there's no configuration record. + * + * There is no return value. + */ + +/* + * All our process-death routine does is add its trace to the log. + */ +static void example_child_exit(server_rec *s, pool *p) +{ + + char *note; + char *sname = s->server_hostname; + + /* + * The arbitrary text we add to our trace entry indicates for which server + * we're being called. + */ + sname = (sname != NULL) ? sname : ""; + note = ap_pstrcat(p, "example_child_exit(", sname, ")", NULL); + trace_add(s, NULL, NULL, note); +} + +/* + * This function gets called to create up a per-directory configuration + * record. This will be called for the "default" server environment, and for + * each directory for which the parser finds any of our directives applicable. + * If a directory doesn't have any of our directives involved (i.e., they + * aren't in the .htaccess file, or a , , or related + * block), this routine will *not* be called - the configuration for the + * closest ancestor is used. + * + * The return value is a pointer to the created module-specific + * structure. + */ +static void *example_create_dir_config(pool *p, char *dirspec) +{ + + excfg *cfg; + char *dname = dirspec; + + /* + * Allocate the space for our record from the pool supplied. + */ + cfg = (excfg *) ap_pcalloc(p, sizeof(excfg)); + /* + * Now fill in the defaults. If there are any `parent' configuration + * records, they'll get merged as part of a separate callback. + */ + cfg->local = 0; + cfg->congenital = 0; + cfg->cmode = CONFIG_MODE_DIRECTORY; + /* + * Finally, add our trace to the callback list. + */ + dname = (dname != NULL) ? dname : ""; + cfg->loc = ap_pstrcat(p, "DIR(", dname, ")", NULL); + trace_add(NULL, NULL, cfg, "example_create_dir_config()"); + return (void *) cfg; +} + +/* + * This function gets called to merge two per-directory configuration + * records. This is typically done to cope with things like .htaccess files + * or directives for directories that are beneath one for which a + * configuration record was already created. The routine has the + * responsibility of creating a new record and merging the contents of the + * other two into it appropriately. If the module doesn't declare a merge + * routine, the record for the closest ancestor location (that has one) is + * used exclusively. + * + * The routine MUST NOT modify any of its arguments! + * + * The return value is a pointer to the created module-specific structure + * containing the merged values. + */ +static void *example_merge_dir_config(pool *p, void *parent_conf, + void *newloc_conf) +{ + + excfg *merged_config = (excfg *) ap_pcalloc(p, sizeof(excfg)); + excfg *pconf = (excfg *) parent_conf; + excfg *nconf = (excfg *) newloc_conf; + char *note; + + /* + * Some things get copied directly from the more-specific record, rather + * than getting merged. + */ + merged_config->local = nconf->local; + merged_config->loc = ap_pstrdup(p, nconf->loc); + /* + * Others, like the setting of the `congenital' flag, get ORed in. The + * setting of that particular flag, for instance, is TRUE if it was ever + * true anywhere in the upstream configuration. + */ + merged_config->congenital = (pconf->congenital | pconf->local); + /* + * If we're merging records for two different types of environment (server + * and directory), mark the new record appropriately. Otherwise, inherit + * the current value. + */ + merged_config->cmode = + (pconf->cmode == nconf->cmode) ? pconf->cmode : CONFIG_MODE_COMBO; + /* + * Now just record our being called in the trace list. Include the + * locations we were asked to merge. + */ + note = ap_pstrcat(p, "example_merge_dir_config(\"", pconf->loc, "\",\"", + nconf->loc, "\")", NULL); + trace_add(NULL, NULL, merged_config, note); + return (void *) merged_config; +} + +/* + * This function gets called to create a per-server configuration + * record. It will always be called for the "default" server. + * + * The return value is a pointer to the created module-specific + * structure. + */ +static void *example_create_server_config(pool *p, server_rec *s) +{ + + excfg *cfg; + char *sname = s->server_hostname; + + /* + * As with the example_create_dir_config() reoutine, we allocate and fill + * in an empty record. + */ + cfg = (excfg *) ap_pcalloc(p, sizeof(excfg)); + cfg->local = 0; + cfg->congenital = 0; + cfg->cmode = CONFIG_MODE_SERVER; + /* + * Note that we were called in the trace list. + */ + sname = (sname != NULL) ? sname : ""; + cfg->loc = ap_pstrcat(p, "SVR(", sname, ")", NULL); + trace_add(s, NULL, cfg, "example_create_server_config()"); + return (void *) cfg; +} + +/* + * This function gets called to merge two per-server configuration + * records. This is typically done to cope with things like virtual hosts and + * the default server configuration The routine has the responsibility of + * creating a new record and merging the contents of the other two into it + * appropriately. If the module doesn't declare a merge routine, the more + * specific existing record is used exclusively. + * + * The routine MUST NOT modify any of its arguments! + * + * The return value is a pointer to the created module-specific structure + * containing the merged values. + */ +static void *example_merge_server_config(pool *p, void *server1_conf, + void *server2_conf) +{ + + excfg *merged_config = (excfg *) ap_pcalloc(p, sizeof(excfg)); + excfg *s1conf = (excfg *) server1_conf; + excfg *s2conf = (excfg *) server2_conf; + char *note; + + /* + * Our inheritance rules are our own, and part of our module's semantics. + * Basically, just note whence we came. + */ + merged_config->cmode = + (s1conf->cmode == s2conf->cmode) ? s1conf->cmode : CONFIG_MODE_COMBO; + merged_config->local = s2conf->local; + merged_config->congenital = (s1conf->congenital | s1conf->local); + merged_config->loc = ap_pstrdup(p, s2conf->loc); + /* + * Trace our call, including what we were asked to merge. + */ + note = ap_pstrcat(p, "example_merge_server_config(\"", s1conf->loc, "\",\"", + s2conf->loc, "\")", NULL); + trace_add(NULL, NULL, merged_config, note); + return (void *) merged_config; +} + +/* + * This routine is called after the request has been read but before any other + * phases have been processed. This allows us to make decisions based upon + * the input header fields. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no + * further modules are called for this phase. + */ +static int example_post_read_request(request_rec *r) +{ + + excfg *cfg; + + cfg = our_dconfig(r); + /* + * We don't actually *do* anything here, except note the fact that we were + * called. + */ + trace_add(r->server, r, cfg, "example_post_read_request()"); + return DECLINED; +} + +/* + * This routine gives our module an opportunity to translate the URI into an + * actual filename. If we don't do anything special, the server's default + * rules (Alias directives and the like) will continue to be followed. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no + * further modules are called for this phase. + */ +static int example_translate_handler(request_rec *r) +{ + + excfg *cfg; + + cfg = our_dconfig(r); + /* + * We don't actually *do* anything here, except note the fact that we were + * called. + */ + trace_add(r->server, r, cfg, "example_translate_handler()"); + return DECLINED; +} + +/* + * This routine is called to check the authentication information sent with + * the request (such as looking up the user in a database and verifying that + * the [encrypted] password sent matches the one in the database). + * + * The return value is OK, DECLINED, or some HTTP_mumble error (typically + * HTTP_UNAUTHORIZED). If we return OK, no other modules are given a chance + * at the request during this phase. + */ +static int example_check_user_id(request_rec *r) +{ + + excfg *cfg; + + cfg = our_dconfig(r); + /* + * Don't do anything except log the call. + */ + trace_add(r->server, r, cfg, "example_check_user_id()"); + return DECLINED; +} + +/* + * This routine is called to check to see if the resource being requested + * requires authorisation. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no + * other modules are called during this phase. + * + * If *all* modules return DECLINED, the request is aborted with a server + * error. + */ +static int example_auth_checker(request_rec *r) +{ + + excfg *cfg; + + cfg = our_dconfig(r); + /* + * Log the call and return OK, or access will be denied (even though we + * didn't actually do anything). + */ + trace_add(r->server, r, cfg, "example_auth_checker()"); + return DECLINED; +} + +/* + * This routine is called to check for any module-specific restrictions placed + * upon the requested resource. (See the mod_access module for an example.) + * + * The return value is OK, DECLINED, or HTTP_mumble. All modules with an + * handler for this phase are called regardless of whether their predecessors + * return OK or DECLINED. The first one to return any other status, however, + * will abort the sequence (and the request) as usual. + */ +static int example_access_checker(request_rec *r) +{ + + excfg *cfg; + + cfg = our_dconfig(r); + trace_add(r->server, r, cfg, "example_access_checker()"); + return DECLINED; +} + +/* + * This routine is called to determine and/or set the various document type + * information bits, like Content-type (via r->content_type), language, et + * cetera. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no + * further modules are given a chance at the request for this phase. + */ +static int example_type_checker(request_rec *r) +{ + + excfg *cfg; + + cfg = our_dconfig(r); + /* + * Log the call, but don't do anything else - and report truthfully that + * we didn't do anything. + */ + trace_add(r->server, r, cfg, "example_type_checker()"); + return DECLINED; +} + +/* + * This routine is called to perform any module-specific fixing of header + * fields, et cetera. It is invoked just before any content-handler. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, the + * server will still call any remaining modules with an handler for this + * phase. + */ +static int example_fixer_upper(request_rec *r) +{ + + excfg *cfg; + + cfg = our_dconfig(r); + /* + * Log the call and exit. + */ + trace_add(r->server, r, cfg, "example_fixer_upper()"); + return OK; +} + +/* + * This routine is called to perform any module-specific logging activities + * over and above the normal server things. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, any + * remaining modules with an handler for this phase will still be called. + */ +static int example_logger(request_rec *r) +{ + + excfg *cfg; + + cfg = our_dconfig(r); + trace_add(r->server, r, cfg, "example_logger()"); + return DECLINED; +} + +/* + * This routine is called to give the module a chance to look at the request + * headers and take any appropriate specific actions early in the processing + * sequence. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, any + * remaining modules with handlers for this phase will still be called. + */ +static int example_header_parser(request_rec *r) +{ + + excfg *cfg; + + cfg = our_dconfig(r); + trace_add(r->server, r, cfg, "example_header_parser()"); + return DECLINED; +} + +/*--------------------------------------------------------------------------*/ +/* */ +/* All of the routines have been declared now. Here's the list of */ +/* directives specific to our module, and information about where they */ +/* may appear and how the command parser should pass them to us for */ +/* processing. Note that care must be taken to ensure that there are NO */ +/* collisions of directive names between modules. */ +/* */ +/*--------------------------------------------------------------------------*/ +/* + * List of directives specific to our module. + */ +static const command_rec example_cmds[] = +{ + { + "Example", /* directive name */ + cmd_example, /* config action routine */ + NULL, /* argument to include in call */ + OR_OPTIONS, /* where available */ + NO_ARGS, /* arguments */ + "Example directive - no arguments" + /* directive description */ + }, + {NULL} +}; + +/*--------------------------------------------------------------------------*/ +/* */ +/* Now the list of content handlers available from this module. */ +/* */ +/*--------------------------------------------------------------------------*/ +/* + * List of content handlers our module supplies. Each handler is defined by + * two parts: a name by which it can be referenced (such as by + * {Add,Set}Handler), and the actual routine name. The list is terminated by + * a NULL block, since it can be of variable length. + * + * Note that content-handlers are invoked on a most-specific to least-specific + * basis; that is, a handler that is declared for "text/plain" will be + * invoked before one that was declared for "text / *". Note also that + * if a content-handler returns anything except DECLINED, no other + * content-handlers will be called. + */ +static const handler_rec example_handlers[] = +{ + {"example-handler", example_handler}, + {NULL} +}; + +/*--------------------------------------------------------------------------*/ +/* */ +/* Finally, the list of callback routines and data structures that */ +/* provide the hooks into our module from the other parts of the server. */ +/* */ +/*--------------------------------------------------------------------------*/ +/* + * Module definition for configuration. If a particular callback is not + * needed, replace its routine name below with the word NULL. + * + * The number in brackets indicates the order in which the routine is called + * during request processing. Note that not all routines are necessarily + * called (such as if a resource doesn't have access restrictions). + */ +module example_module = +{ + STANDARD_MODULE_STUFF, + example_init, /* module initializer */ + example_create_dir_config, /* per-directory config creator */ + example_merge_dir_config, /* dir config merger */ + example_create_server_config, /* server config creator */ + example_merge_server_config, /* server config merger */ + example_cmds, /* command table */ + example_handlers, /* [7] list of handlers */ + example_translate_handler, /* [2] filename-to-URI translation */ + example_check_user_id, /* [5] check/validate user_id */ + example_auth_checker, /* [6] check user_id is valid *here* */ + example_access_checker, /* [4] check access by host address */ + example_type_checker, /* [7] MIME type checker/setter */ + example_fixer_upper, /* [8] fixups */ + example_logger, /* [10] logger */ +#if MODULE_MAGIC_NUMBER >= 19970103 + example_header_parser, /* [3] header parser */ +#endif +#if MODULE_MAGIC_NUMBER >= 19970719 + example_child_init, /* process initializer */ +#endif +#if MODULE_MAGIC_NUMBER >= 19970728 + example_child_exit, /* process exit/cleanup */ +#endif +#if MODULE_MAGIC_NUMBER >= 19970902 + example_post_read_request /* [1] post read_request handling */ +#endif +}; diff --git a/modules/filters/.indent.pro b/modules/filters/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/modules/filters/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-Tbuf_area +-Tbuff_struct +-Tbuffy +-Tcmd_how +-Tcmd_parms +-Tcommand_rec +-Tcommand_struct +-Tconn_rec +-Tcore_dir_config +-Tcore_server_config +-Tdir_maker_func +-Tevent +-Tglobals_s +-Thandler_func +-Thandler_rec +-Tjoblist_s +-Tlisten_rec +-Tmerger_func +-Tmode_t +-Tmodule +-Tmodule_struct +-Tmutex +-Tn_long +-Tother_child_rec +-Toverrides_t +-Tparent_score +-Tpid_t +-Tpiped_log +-Tpool +-Trequest_rec +-Trequire_line +-Trlim_t +-Tscoreboard +-Tsemaphore +-Tserver_addr_rec +-Tserver_rec +-Tserver_rec_chain +-Tshort_score +-Ttable +-Ttable_entry +-Tthread +-Tu_wide_int +-Tvtime_t +-Twide_int diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c new file mode 100644 index 00000000000..3ee96db83b5 --- /dev/null +++ b/modules/filters/mod_include.c @@ -0,0 +1,2486 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_include.c: Handles the server-parsed HTML documents + * + * Original by Rob McCool; substantial fixups by David Robinson; + * incorporated into the Apache module framework by rst. + * + */ +/* + * sub key may be anything a Perl*Handler can be: + * subroutine name, package name (defaults to package::handler), + * Class->method call or anoymous sub {} + * + * Child accessed + * times.
      + * + * + * + * -Doug MacEachern + */ + +#ifdef USE_PERL_SSI +#include "config.h" +#undef VOIDUSED +#ifdef USE_SFIO +#undef USE_SFIO +#define USE_STDIO +#endif +#include "modules/perl/mod_perl.h" +#else +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#endif + +#define STARTING_SEQUENCE "" +#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]" +#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z" +#define SIZEFMT_BYTES 0 +#define SIZEFMT_KMG 1 +#ifdef CHARSET_EBCDIC +#define RAW_ASCII_CHAR(ch) os_toebcdic[(unsigned char)ch] +#else /*CHARSET_EBCDIC*/ +#define RAW_ASCII_CHAR(ch) (ch) +#endif /*CHARSET_EBCDIC*/ + +module MODULE_VAR_EXPORT includes_module; + +/* just need some arbitrary non-NULL pointer which can't also be a request_rec */ +#define NESTED_INCLUDE_MAGIC (&includes_module) + +/* ------------------------ Environment function -------------------------- */ + +/* XXX: could use ap_table_overlap here */ +static void add_include_vars(request_rec *r, char *timefmt) +{ +#ifndef WIN32 + struct passwd *pw; +#endif /* ndef WIN32 */ + table *e = r->subprocess_env; + char *t; + time_t date = r->request_time; + + ap_table_setn(e, "DATE_LOCAL", ap_ht_time(r->pool, date, timefmt, 0)); + ap_table_setn(e, "DATE_GMT", ap_ht_time(r->pool, date, timefmt, 1)); + ap_table_setn(e, "LAST_MODIFIED", + ap_ht_time(r->pool, r->finfo.st_mtime, timefmt, 0)); + ap_table_setn(e, "DOCUMENT_URI", r->uri); + ap_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info); +#ifndef WIN32 + pw = getpwuid(r->finfo.st_uid); + if (pw) { + ap_table_setn(e, "USER_NAME", ap_pstrdup(r->pool, pw->pw_name)); + } + else { + ap_table_setn(e, "USER_NAME", ap_psprintf(r->pool, "user#%lu", + (unsigned long) r->finfo.st_uid)); + } +#endif /* ndef WIN32 */ + + if ((t = strrchr(r->filename, '/'))) { + ap_table_setn(e, "DOCUMENT_NAME", ++t); + } + else { + ap_table_setn(e, "DOCUMENT_NAME", r->uri); + } + if (r->args) { + char *arg_copy = ap_pstrdup(r->pool, r->args); + + ap_unescape_url(arg_copy); + ap_table_setn(e, "QUERY_STRING_UNESCAPED", + ap_escape_shell_cmd(r->pool, arg_copy)); + } +} + + + +/* --------------------------- Parser functions --------------------------- */ + +#define OUTBUFSIZE 4096 +/* PUT_CHAR and FLUSH_BUF currently only work within the scope of + * find_string(); they are hacks to avoid calling rputc for each and + * every character output. A common set of buffering calls for this + * type of output SHOULD be implemented. + */ +#define PUT_CHAR(c,r) \ + { \ + outbuf[outind++] = c; \ + if (outind == OUTBUFSIZE) { \ + FLUSH_BUF(r) \ + }; \ + } + +/* there SHOULD be some error checking on the return value of + * rwrite, however it is unclear what the API for rwrite returning + * errors is and little can really be done to help the error in + * any case. + */ +#define FLUSH_BUF(r) \ + { \ + ap_rwrite(outbuf, outind, r); \ + outind = 0; \ + } + +/* + * f: file handle being read from + * c: character to read into + * ret: return value to use if input fails + * r: current request_rec + * + * This macro is redefined after find_string() for historical reasons + * to avoid too many code changes. This is one of the many things + * that should be fixed. + */ +#define GET_CHAR(f,c,ret,r) \ + { \ + int i = getc(f); \ + if (i == EOF) { /* either EOF or error -- needs error handling if latter */ \ + if (ferror(f)) { \ + fprintf(stderr, "encountered error in GET_CHAR macro, " \ + "mod_include.\n"); \ + } \ + FLUSH_BUF(r); \ + ap_pfclose(r->pool, f); \ + return ret; \ + } \ + c = (char)i; \ + } + +static int find_string(FILE *in, const char *str, request_rec *r, int printing) +{ + int x, l = strlen(str), p; + char outbuf[OUTBUFSIZE]; + int outind = 0; + char c; + + p = 0; + while (1) { + GET_CHAR(in, c, 1, r); + if (c == str[p]) { + if ((++p) == l) { + FLUSH_BUF(r); + return 0; + } + } + else { + if (printing) { + for (x = 0; x < p; x++) { + PUT_CHAR(str[x], r); + } + PUT_CHAR(c, r); + } + p = 0; + } + } +} + +#undef FLUSH_BUF +#undef PUT_CHAR +#undef GET_CHAR +#define GET_CHAR(f,c,r,p) \ + { \ + int i = getc(f); \ + if (i == EOF) { /* either EOF or error -- needs error handling if latter */ \ + if (ferror(f)) { \ + fprintf(stderr, "encountered error in GET_CHAR macro, " \ + "mod_include.\n"); \ + } \ + ap_pfclose(p, f); \ + return r; \ + } \ + c = (char)i; \ + } + +/* + * decodes a string containing html entities or numeric character references. + * 's' is overwritten with the decoded string. + * If 's' is syntatically incorrect, then the followed fixups will be made: + * unknown entities will be left undecoded; + * references to unused numeric characters will be deleted. + * In particular, � will not be decoded, but will be deleted. + * + * drtr + */ + +/* maximum length of any ISO-LATIN-1 HTML entity name. */ +#define MAXENTLEN (6) + +/* The following is a shrinking transformation, therefore safe. */ + +static void decodehtml(char *s) +{ + int val, i, j; + char *p = s; + const char *ents; + static const char * const entlist[MAXENTLEN + 1] = + { + NULL, /* 0 */ + NULL, /* 1 */ + "lt\074gt\076", /* 2 */ + "amp\046ETH\320eth\360", /* 3 */ + "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\ +iuml\357ouml\366uuml\374yuml\377", /* 4 */ + "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\ +THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\ +ucirc\373thorn\376", /* 5 */ + "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\ +Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\ +Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\ +egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\ +otilde\365oslash\370ugrave\371uacute\372yacute\375" /* 6 */ + }; + + for (; *s != '\0'; s++, p++) { + if (*s != '&') { + *p = *s; + continue; + } + /* find end of entity */ + for (i = 1; s[i] != ';' && s[i] != '\0'; i++) { + continue; + } + + if (s[i] == '\0') { /* treat as normal data */ + *p = *s; + continue; + } + + /* is it numeric ? */ + if (s[1] == '#') { + for (j = 2, val = 0; j < i && ap_isdigit(s[j]); j++) { + val = val * 10 + s[j] - '0'; + } + s += i; + if (j < i || val <= 8 || (val >= 11 && val <= 31) || + (val >= 127 && val <= 160) || val >= 256) { + p--; /* no data to output */ + } + else { + *p = RAW_ASCII_CHAR(val); + } + } + else { + j = i - 1; + if (j > MAXENTLEN || entlist[j] == NULL) { + /* wrong length */ + *p = '&'; + continue; /* skip it */ + } + for (ents = entlist[j]; *ents != '\0'; ents += i) { + if (strncmp(s + 1, ents, j) == 0) { + break; + } + } + + if (*ents == '\0') { + *p = '&'; /* unknown */ + } + else { + *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]); + s += i; + } + } + } + + *p = '\0'; +} + +/* + * extract the next tag name and value. + * if there are no more tags, set the tag name to 'done' + * the tag value is html decoded if dodecode is non-zero + */ + +static char *get_tag(pool *p, FILE *in, char *tag, int tagbuf_len, int dodecode) +{ + char *t = tag, *tag_val, c, term; + + /* makes code below a little less cluttered */ + --tagbuf_len; + + do { /* skip whitespace */ + GET_CHAR(in, c, NULL, p); + } while (ap_isspace(c)); + + /* tags can't start with - */ + if (c == '-') { + GET_CHAR(in, c, NULL, p); + if (c == '-') { + do { + GET_CHAR(in, c, NULL, p); + } while (ap_isspace(c)); + if (c == '>') { + ap_cpystrn(tag, "done", tagbuf_len); + return tag; + } + } + return NULL; /* failed */ + } + + /* find end of tag name */ + while (1) { + if (t - tag == tagbuf_len) { + *t = '\0'; + return NULL; + } + if (c == '=' || ap_isspace(c)) { + break; + } + *(t++) = ap_tolower(c); + GET_CHAR(in, c, NULL, p); + } + + *t++ = '\0'; + tag_val = t; + + while (ap_isspace(c)) { + GET_CHAR(in, c, NULL, p); /* space before = */ + } + if (c != '=') { + ungetc(c, in); + return NULL; + } + + do { + GET_CHAR(in, c, NULL, p); /* space after = */ + } while (ap_isspace(c)); + + /* we should allow a 'name' as a value */ + + if (c != '"' && c != '\'') { + return NULL; + } + term = c; + while (1) { + GET_CHAR(in, c, NULL, p); + if (t - tag == tagbuf_len) { + *t = '\0'; + return NULL; + } +/* Want to accept \" as a valid character within a string. */ + if (c == '\\') { + *(t++) = c; /* Add backslash */ + GET_CHAR(in, c, NULL, p); + if (c == term) { /* Only if */ + *(--t) = c; /* Replace backslash ONLY for terminator */ + } + } + else if (c == term) { + break; + } + *(t++) = c; + } + *t = '\0'; + if (dodecode) { + decodehtml(tag_val); + } + return ap_pstrdup(p, tag_val); +} + +static int get_directive(FILE *in, char *dest, size_t len, pool *p) +{ + char *d = dest; + char c; + + /* make room for nul terminator */ + --len; + + /* skip initial whitespace */ + while (1) { + GET_CHAR(in, c, 1, p); + if (!ap_isspace(c)) { + break; + } + } + /* now get directive */ + while (1) { + if (d - dest == len) { + return 1; + } + *d++ = ap_tolower(c); + GET_CHAR(in, c, 1, p); + if (ap_isspace(c)) { + break; + } + } + *d = '\0'; + return 0; +} + +/* + * Do variable substitution on strings + */ +static void parse_string(request_rec *r, const char *in, char *out, + size_t length, int leave_name) +{ + char ch; + char *next = out; + char *end_out; + + /* leave room for nul terminator */ + end_out = out + length - 1; + + while ((ch = *in++) != '\0') { + switch (ch) { + case '\\': + if (next == end_out) { + /* truncated */ + *next = '\0'; + return; + } + if (*in == '$') { + *next++ = *in++; + } + else { + *next++ = ch; + } + break; + case '$': + { + char var[MAX_STRING_LEN]; + const char *start_of_var_name; + const char *end_of_var_name; /* end of var name + 1 */ + const char *expansion; + const char *val; + size_t l; + + /* guess that the expansion won't happen */ + expansion = in - 1; + if (*in == '{') { + ++in; + start_of_var_name = in; + in = strchr(in, '}'); + if (in == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, + r, "Missing '}' on variable \"%s\"", + expansion); + *next = '\0'; + return; + } + end_of_var_name = in; + ++in; + } + else { + start_of_var_name = in; + while (ap_isalnum(*in) || *in == '_') { + ++in; + } + end_of_var_name = in; + } + /* what a pain, too bad there's no table_getn where you can + * pass a non-nul terminated string */ + l = end_of_var_name - start_of_var_name; + if (l != 0) { + l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l; + memcpy(var, start_of_var_name, l); + var[l] = '\0'; + + val = ap_table_get(r->subprocess_env, var); + if (val) { + expansion = val; + l = strlen(expansion); + } + else if (leave_name) { + l = in - expansion; + } + else { + break; /* no expansion to be done */ + } + } + else { + /* zero-length variable name causes just the $ to be copied */ + l = 1; + } + l = (l > end_out - next) ? (end_out - next) : l; + memcpy(next, expansion, l); + next += l; + break; + } + default: + if (next == end_out) { + /* truncated */ + *next = '\0'; + return; + } + *next++ = ch; + break; + } + } + *next = '\0'; + return; +} + +/* --------------------------- Action handlers ---------------------------- */ + +static int include_cgi(char *s, request_rec *r) +{ + request_rec *rr = ap_sub_req_lookup_uri(s, r); + int rr_status; + + if (rr->status != HTTP_OK) { + return -1; + } + + /* No hardwired path info or query allowed */ + + if ((rr->path_info && rr->path_info[0]) || rr->args) { + return -1; + } + if (rr->finfo.st_mode == 0) { + return -1; + } + + /* 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. + */ + + rr->content_type = CGI_MAGIC_TYPE; + + /* Run it. */ + + rr_status = ap_run_sub_req(rr); + if (ap_is_HTTP_REDIRECT(rr_status)) { + const char *location = ap_table_get(rr->headers_out, "Location"); + location = ap_escape_html(rr->pool, location); + ap_rvputs(r, "", location, "", NULL); + } + + ap_destroy_sub_req(rr); +#ifndef WIN32 + ap_chdir_file(r->filename); +#endif + + return 0; +} + +/* ensure that path is relative, and does not contain ".." elements + * ensentially ensure that it does not match the regex: + * (^/|(^|/)\.\.(/|$)) + * XXX: this needs os abstraction... consider c:..\foo in win32 + */ +static int is_only_below(const char *path) +{ +#ifdef HAVE_DRIVE_LETTERS + if (path[1] == ':') + return 0; +#endif + if (path[0] == '/') { + return 0; + } + if (path[0] == '.' && path[1] == '.' + && (path[2] == '\0' || path[2] == '/')) { + return 0; + } + while (*path) { + if (*path == '/' && path[1] == '.' && path[2] == '.' + && (path[3] == '\0' || path[3] == '/')) { + return 0; + } + ++path; + } + return 1; +} + +static int handle_include(FILE *in, request_rec *r, const char *error, int noexec) +{ + char tag[MAX_STRING_LEN]; + char parsed_string[MAX_STRING_LEN]; + char *tag_val; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + if (!strcmp(tag, "file") || !strcmp(tag, "virtual")) { + request_rec *rr = NULL; + char *error_fmt = NULL; + + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (tag[0] == 'f') { + /* be safe; only files in this directory or below allowed */ + if (!is_only_below(parsed_string)) { + error_fmt = "unable to include file \"%s\" " + "in parsed file %s"; + } + else { + rr = ap_sub_req_lookup_file(parsed_string, r); + } + } + else { + rr = ap_sub_req_lookup_uri(parsed_string, r); + } + + if (!error_fmt && rr->status != HTTP_OK) { + error_fmt = "unable to include \"%s\" in parsed file %s"; + } + + if (!error_fmt && noexec && rr->content_type + && (strncmp(rr->content_type, "text/", 5))) { + error_fmt = "unable to include potential exec \"%s\" " + "in parsed file %s"; + } + if (error_fmt == NULL) { + /* try to avoid recursive includes. We do this by walking + * up the r->main list of subrequests, and at each level + * walking back through any internal redirects. At each + * step, we compare the filenames and the URIs. + * + * The filename comparison catches a recursive include + * with an ever-changing URL, eg. + * + * which, although they would eventually be caught because + * we have a limit on the length of files, etc., can + * recurse for a while. + * + * The URI comparison catches the case where the filename + * is changed while processing the request, so the + * current name is never the same as any previous one. + * This can happen with "DocumentRoot /foo" when you + * request "/" on the server and it includes "/". + * This only applies to modules such as mod_dir that + * (somewhat improperly) mess with r->filename outside + * of a filename translation phase. + */ + int founddupe = 0; + request_rec *p; + for (p = r; p != NULL && !founddupe; p = p->main) { + request_rec *q; + for (q = p; q != NULL; q = q->prev) { + if ( (strcmp(q->filename, rr->filename) == 0) || + (strcmp(q->uri, rr->uri) == 0) ){ + founddupe = 1; + break; + } + } + } + + if (p != NULL) { + error_fmt = "Recursive include of \"%s\" " + "in parsed file %s"; + } + } + + /* see the Kludge in send_parsed_file for why */ + if (rr) + ap_set_module_config(rr->request_config, &includes_module, r); + + if (!error_fmt && ap_run_sub_req(rr)) { + error_fmt = "unable to include \"%s\" in parsed file %s"; + } +#ifndef WIN32 + ap_chdir_file(r->filename); +#endif + if (error_fmt) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, + r, error_fmt, tag_val, r->filename); + ap_rputs(error, r); + } + + /* destroy the sub request if it's not a nested include */ + if (rr != NULL + && ap_get_module_config(rr->request_config, &includes_module) + != NESTED_INCLUDE_MAGIC) { + ap_destroy_sub_req(rr); + } + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "unknown parameter \"%s\" to tag include in %s", + tag, r->filename); + ap_rputs(error, r); + } + } +} + +typedef struct { +#ifdef TPF + TPF_FORK_CHILD t; +#endif + request_rec *r; + char *s; +} include_cmd_arg; + +static int include_cmd_child(void *arg, child_info *pinfo) +{ + request_rec *r = ((include_cmd_arg *) arg)->r; + char *s = ((include_cmd_arg *) arg)->s; + table *env = r->subprocess_env; + int child_pid = 0; +#ifdef DEBUG_INCLUDE_CMD +#ifdef OS2 + /* under OS/2 /dev/tty is referenced as con */ + FILE *dbg = fopen("con", "w"); +#else + FILE *dbg = fopen("/dev/tty", "w"); +#endif +#endif +#ifndef WIN32 + char err_string[MAX_STRING_LEN]; +#endif + +#ifdef DEBUG_INCLUDE_CMD + fprintf(dbg, "Attempting to include command '%s'\n", s); +#endif + + if (r->path_info && r->path_info[0] != '\0') { + request_rec *pa_req; + + ap_table_setn(env, "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); + if (pa_req->filename) { + ap_table_setn(env, "PATH_TRANSLATED", + ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info, + NULL)); + } + } + + if (r->args) { + char *arg_copy = ap_pstrdup(r->pool, r->args); + + ap_table_setn(env, "QUERY_STRING", r->args); + ap_unescape_url(arg_copy); + ap_table_setn(env, "QUERY_STRING_UNESCAPED", + ap_escape_shell_cmd(r->pool, arg_copy)); + } + + ap_error_log2stderr(r->server); + +#ifdef DEBUG_INCLUDE_CMD + fprintf(dbg, "Attempting to exec '%s'\n", s); +#endif +#ifdef TPF + return (0); +#else + ap_cleanup_for_exec(); + /* set shellcmd flag to pass arg to SHELL_PATH */ + child_pid = ap_call_exec(r, pinfo, s, ap_create_environment(r->pool, env), + 1); +#if defined(WIN32) || defined(OS2) + return (child_pid); +#else + /* Oh, drat. We're still here. The log file descriptors are closed, + * so we have to whimper a complaint onto stderr... + */ + +#ifdef DEBUG_INCLUDE_CMD + fprintf(dbg, "Exec failed\n"); +#endif + ap_snprintf(err_string, sizeof(err_string), + "exec of %s failed, reason: %s (errno = %d)\n", + SHELL_PATH, strerror(errno), errno); + write(STDERR_FILENO, err_string, strlen(err_string)); + exit(0); + /* NOT REACHED */ + return (child_pid); +#endif /* WIN32 */ +#endif /* TPF */ +} + +static int include_cmd(char *s, request_rec *r) +{ + include_cmd_arg arg; + BUFF *script_in; + + arg.r = r; + arg.s = s; +#ifdef TPF + arg.t.filename = r->filename; + arg.t.subprocess_env = r->subprocess_env; + arg.t.prog_type = FORK_FILE; +#endif + + if (!ap_bspawn_child(r->pool, include_cmd_child, &arg, + kill_after_timeout, NULL, &script_in, NULL)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "couldn't spawn include command"); + return -1; + } + + ap_send_fb(script_in, r); + ap_bclose(script_in); + return 0; +} + +static int handle_exec(FILE *in, request_rec *r, const char *error) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + char *file = r->filename; + char parsed_string[MAX_STRING_LEN]; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + if (!strcmp(tag, "cmd")) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1); + if (include_cmd(parsed_string, r) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "execution failure for parameter \"%s\" " + "to tag exec in file %s", + tag, r->filename); + ap_rputs(error, r); + } + /* just in case some stooge changed directories */ +#ifndef WIN32 + ap_chdir_file(r->filename); +#endif + } + else if (!strcmp(tag, "cgi")) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (include_cgi(parsed_string, r) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "invalid CGI ref \"%s\" in %s", tag_val, file); + ap_rputs(error, r); + } + /* grumble groan */ +#ifndef WIN32 + ap_chdir_file(r->filename); +#endif + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "unknown parameter \"%s\" to tag exec in %s", + tag, file); + ap_rputs(error, r); + } + } + +} + +static int handle_echo(FILE *in, request_rec *r, const char *error) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + if (!strcmp(tag, "var")) { + const char *val = ap_table_get(r->subprocess_env, tag_val); + + if (val) { + ap_rputs(val, r); + } + else { + ap_rputs("(none)", r); + } + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "unknown parameter \"%s\" to tag echo in %s", + tag, r->filename); + ap_rputs(error, r); + } + } +} + +#ifdef USE_PERL_SSI +static int handle_perl(FILE *in, request_rec *r, const char *error) +{ + char tag[MAX_STRING_LEN]; + char parsed_string[MAX_STRING_LEN]; + char *tag_val; + SV *sub = Nullsv; + AV *av = newAV(); + + if (ap_allow_options(r) & OPT_INCNOEXEC) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "#perl SSI disallowed by IncludesNoExec in %s", + r->filename); + return DECLINED; + } + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + break; + } + if (strnEQ(tag, "sub", 3)) { + sub = newSVpv(tag_val, 0); + } + else if (strnEQ(tag, "arg", 3)) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + av_push(av, newSVpv(parsed_string, 0)); + } + else if (strnEQ(tag, "done", 4)) { + break; + } + } + perl_stdout2client(r); + perl_setup_env(r); + perl_call_handler(sub, r, av); + return OK; +} +#endif + +/* error and tf must point to a string with room for at + * least MAX_STRING_LEN characters + */ +static int handle_config(FILE *in, request_rec *r, char *error, char *tf, + int *sizefmt) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + char parsed_string[MAX_STRING_LEN]; + table *env = r->subprocess_env; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0))) { + return 1; + } + if (!strcmp(tag, "errmsg")) { + parse_string(r, tag_val, error, MAX_STRING_LEN, 0); + } + else if (!strcmp(tag, "timefmt")) { + time_t date = r->request_time; + + parse_string(r, tag_val, tf, MAX_STRING_LEN, 0); + ap_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, tf, 0)); + ap_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, tf, 1)); + ap_table_setn(env, "LAST_MODIFIED", + ap_ht_time(r->pool, r->finfo.st_mtime, tf, 0)); + } + else if (!strcmp(tag, "sizefmt")) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + decodehtml(parsed_string); + if (!strcmp(parsed_string, "bytes")) { + *sizefmt = SIZEFMT_BYTES; + } + else if (!strcmp(parsed_string, "abbrev")) { + *sizefmt = SIZEFMT_KMG; + } + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "unknown parameter \"%s\" to tag config in %s", + tag, r->filename); + ap_rputs(error, r); + } + } +} + + +static int find_file(request_rec *r, const char *directive, const char *tag, + char *tag_val, struct stat *finfo, const char *error) +{ + char *to_send = tag_val; + request_rec *rr = NULL; + int ret=0; + char *error_fmt = NULL; + + if (!strcmp(tag, "file")) { + /* be safe; only files in this directory or below allowed */ + if (!is_only_below(tag_val)) { + error_fmt = "unable to access file \"%s\" " + "in parsed file %s"; + } + else { + ap_getparents(tag_val); /* get rid of any nasties */ + rr = ap_sub_req_lookup_file(tag_val, r); + + if (rr->status == HTTP_OK && rr->finfo.st_mode != 0) { + to_send = rr->filename; + if (stat(to_send, finfo)) { + error_fmt = "unable to get information about \"%s\" " + "in parsed file %s"; + } + } + else { + error_fmt = "unable to lookup information about \"%s\" " + "in parsed file %s"; + } + } + + if (error_fmt) { + ret = -1; + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, error_fmt, to_send, r->filename); + ap_rputs(error, r); + } + + if (rr) ap_destroy_sub_req(rr); + + return ret; + } + else if (!strcmp(tag, "virtual")) { + rr = ap_sub_req_lookup_uri(tag_val, r); + + if (rr->status == HTTP_OK && rr->finfo.st_mode != 0) { + memcpy((char *) finfo, (const char *) &rr->finfo, + sizeof(struct stat)); + ap_destroy_sub_req(rr); + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "unable to get information about \"%s\" " + "in parsed file %s", + tag_val, r->filename); + ap_rputs(error, r); + ap_destroy_sub_req(rr); + return -1; + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "unknown parameter \"%s\" to tag %s in %s", + tag, directive, r->filename); + ap_rputs(error, r); + return -1; + } +} + + +static int handle_fsize(FILE *in, request_rec *r, const char *error, int sizefmt) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + struct stat finfo; + char parsed_string[MAX_STRING_LEN]; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (!find_file(r, "fsize", tag, parsed_string, &finfo, error)) { + if (sizefmt == SIZEFMT_KMG) { + ap_send_size(finfo.st_size, r); + } + else { + int l, x; +#if defined(AP_OFF_T_IS_QUAD) + ap_snprintf(tag, sizeof(tag), "%qd", finfo.st_size); +#else + ap_snprintf(tag, sizeof(tag), "%ld", finfo.st_size); +#endif + l = strlen(tag); /* grrr */ + for (x = 0; x < l; x++) { + if (x && (!((l - x) % 3))) { + ap_rputc(',', r); + } + ap_rputc(tag[x], r); + } + } + } + } + } +} + +static int handle_flastmod(FILE *in, request_rec *r, const char *error, const char *tf) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + struct stat finfo; + char parsed_string[MAX_STRING_LEN]; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) { + ap_rputs(ap_ht_time(r->pool, finfo.st_mtime, tf, 0), r); + } + } + } +} + +static int re_check(request_rec *r, char *string, char *rexp) +{ + regex_t *compiled; + int regex_error; + + compiled = ap_pregcomp(r->pool, rexp, REG_EXTENDED | REG_NOSUB); + if (compiled == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "unable to compile pattern \"%s\"", rexp); + return -1; + } + regex_error = ap_regexec(compiled, string, 0, (regmatch_t *) NULL, 0); + ap_pregfree(r->pool, compiled); + return (!regex_error); +} + +enum token_type { + token_string, + token_and, token_or, token_not, token_eq, token_ne, + token_rbrace, token_lbrace, token_group, + token_ge, token_le, token_gt, token_lt +}; +struct token { + enum token_type type; + char value[MAX_STRING_LEN]; +}; + +/* there is an implicit assumption here that string is at most MAX_STRING_LEN-1 + * characters long... + */ +static const char *get_ptoken(request_rec *r, const char *string, struct token *token) +{ + char ch; + int next = 0; + int qs = 0; + + /* Skip leading white space */ + if (string == (char *) NULL) { + return (char *) NULL; + } + while ((ch = *string++)) { + if (!ap_isspace(ch)) { + break; + } + } + if (ch == '\0') { + return (char *) NULL; + } + + token->type = token_string; /* the default type */ + switch (ch) { + case '(': + token->type = token_lbrace; + return (string); + case ')': + token->type = token_rbrace; + return (string); + case '=': + token->type = token_eq; + return (string); + case '!': + if (*string == '=') { + token->type = token_ne; + return (string + 1); + } + else { + token->type = token_not; + return (string); + } + case '\'': + token->type = token_string; + qs = 1; + break; + case '|': + if (*string == '|') { + token->type = token_or; + return (string + 1); + } + break; + case '&': + if (*string == '&') { + token->type = token_and; + return (string + 1); + } + break; + case '>': + if (*string == '=') { + token->type = token_ge; + return (string + 1); + } + else { + token->type = token_gt; + return (string); + } + case '<': + if (*string == '=') { + token->type = token_le; + return (string + 1); + } + else { + token->type = token_lt; + return (string); + } + default: + token->type = token_string; + break; + } + /* We should only be here if we are in a string */ + if (!qs) { + token->value[next++] = ch; + } + + /* + * Yes I know that goto's are BAD. But, c doesn't allow me to + * exit a loop from a switch statement. Yes, I could use a flag, + * but that is (IMHO) even less readable/maintainable than the goto. + */ + /* + * I used the ++string throughout this section so that string + * ends up pointing to the next token and I can just return it + */ + for (ch = *string; ch != '\0'; ch = *++string) { + if (ch == '\\') { + if ((ch = *++string) == '\0') { + goto TOKEN_DONE; + } + token->value[next++] = ch; + continue; + } + if (!qs) { + if (ap_isspace(ch)) { + goto TOKEN_DONE; + } + switch (ch) { + case '(': + goto TOKEN_DONE; + case ')': + goto TOKEN_DONE; + case '=': + goto TOKEN_DONE; + case '!': + goto TOKEN_DONE; + case '|': + if (*(string + 1) == '|') { + goto TOKEN_DONE; + } + break; + case '&': + if (*(string + 1) == '&') { + goto TOKEN_DONE; + } + break; + case '<': + goto TOKEN_DONE; + case '>': + goto TOKEN_DONE; + } + token->value[next++] = ch; + } + else { + if (ch == '\'') { + qs = 0; + ++string; + goto TOKEN_DONE; + } + token->value[next++] = ch; + } + } + TOKEN_DONE: + /* If qs is still set, I have an unmatched ' */ + if (qs) { + ap_rputs("\nUnmatched '\n", r); + next = 0; + } + token->value[next] = '\0'; + return (string); +} + + +/* + * Hey I still know that goto's are BAD. I don't think that I've ever + * used two in the same project, let alone the same file before. But, + * I absolutely want to make sure that I clean up the memory in all + * cases. And, without rewriting this completely, the easiest way + * is to just branch to the return code which cleans it up. + */ +/* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1 + * characters long... + */ +static int parse_expr(request_rec *r, const char *expr, const char *error) +{ + struct parse_node { + struct parse_node *left, *right, *parent; + struct token token; + int value, done; + } *root, *current, *new; + const char *parse; + char buffer[MAX_STRING_LEN]; + pool *expr_pool; + int retval = 0; + + if ((parse = expr) == (char *) NULL) { + return (0); + } + root = current = (struct parse_node *) NULL; + expr_pool = ap_make_sub_pool(r->pool); + + /* Create Parse Tree */ + while (1) { + new = (struct parse_node *) ap_palloc(expr_pool, + sizeof(struct parse_node)); + new->parent = new->left = new->right = (struct parse_node *) NULL; + new->done = 0; + if ((parse = get_ptoken(r, parse, &new->token)) == (char *) NULL) { + break; + } + switch (new->token.type) { + + case token_string: +#ifdef DEBUG_INCLUDE + ap_rvputs(r, " Token: string (", new->token.value, ")\n", NULL); +#endif + if (current == (struct parse_node *) NULL) { + root = current = new; + break; + } + switch (current->token.type) { + case token_string: + if (current->token.value[0] != '\0') { + strncat(current->token.value, " ", + sizeof(current->token.value) + - strlen(current->token.value) - 1); + } + strncat(current->token.value, new->token.value, + sizeof(current->token.value) + - strlen(current->token.value) - 1); + current->token.value[sizeof(current->token.value) - 1] = '\0'; + break; + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_lbrace: + case token_not: + case token_ge: + case token_gt: + case token_le: + case token_lt: + new->parent = current; + current = current->right = new; + break; + default: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + break; + + case token_and: + case token_or: +#ifdef DEBUG_INCLUDE + ap_rputs(" Token: and/or\n", r); +#endif + if (current == (struct parse_node *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + /* Percolate upwards */ + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_string: + case token_group: + case token_not: + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_ge: + case token_gt: + case token_le: + case token_lt: + current = current->parent; + continue; + case token_lbrace: + break; + default: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + break; + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + current->right = new; + new->parent = current; + } + current = new; + break; + + case token_not: +#ifdef DEBUG_INCLUDE + ap_rputs(" Token: not\n", r); +#endif + if (current == (struct parse_node *) NULL) { + root = current = new; + break; + } + /* Percolate upwards */ + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_not: + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_lbrace: + case token_ge: + case token_gt: + case token_le: + case token_lt: + break; + default: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + break; + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + current->right = new; + new->parent = current; + } + current = new; + break; + + case token_eq: + case token_ne: + case token_ge: + case token_gt: + case token_le: + case token_lt: +#ifdef DEBUG_INCLUDE + ap_rputs(" Token: eq/ne/ge/gt/le/lt\n", r); +#endif + if (current == (struct parse_node *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + /* Percolate upwards */ + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_string: + case token_group: + current = current->parent; + continue; + case token_lbrace: + case token_and: + case token_or: + break; + case token_not: + case token_eq: + case token_ne: + case token_ge: + case token_gt: + case token_le: + case token_lt: + default: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + break; + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + current->right = new; + new->parent = current; + } + current = new; + break; + + case token_rbrace: +#ifdef DEBUG_INCLUDE + ap_rputs(" Token: rbrace\n", r); +#endif + while (current != (struct parse_node *) NULL) { + if (current->token.type == token_lbrace) { + current->token.type = token_group; + break; + } + current = current->parent; + } + if (current == (struct parse_node *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Unmatched ')' in \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + break; + + case token_lbrace: +#ifdef DEBUG_INCLUDE + ap_rputs(" Token: lbrace\n", r); +#endif + if (current == (struct parse_node *) NULL) { + root = current = new; + break; + } + /* Percolate upwards */ + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_not: + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_lbrace: + case token_ge: + case token_gt: + case token_le: + case token_lt: + break; + case token_string: + case token_group: + default: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + break; + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + current->right = new; + new->parent = current; + } + current = new; + break; + default: + break; + } + } + + /* Evaluate Parse Tree */ + current = root; + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_string: +#ifdef DEBUG_INCLUDE + ap_rputs(" Evaluate string\n", r); +#endif + parse_string(r, current->token.value, buffer, sizeof(buffer), 0); + ap_cpystrn(current->token.value, buffer, sizeof(current->token.value)); + current->value = (current->token.value[0] != '\0'); + current->done = 1; + current = current->parent; + break; + + case token_and: + case token_or: +#ifdef DEBUG_INCLUDE + ap_rputs(" Evaluate and/or\n", r); +#endif + if (current->left == (struct parse_node *) NULL || + current->right == (struct parse_node *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + if (!current->left->done) { + switch (current->left->token.type) { + case token_string: + parse_string(r, current->left->token.value, + buffer, sizeof(buffer), 0); + ap_cpystrn(current->left->token.value, buffer, + sizeof(current->left->token.value)); + current->left->value = (current->left->token.value[0] != '\0'); + current->left->done = 1; + break; + default: + current = current->left; + continue; + } + } + if (!current->right->done) { + switch (current->right->token.type) { + case token_string: + parse_string(r, current->right->token.value, + buffer, sizeof(buffer), 0); + ap_cpystrn(current->right->token.value, buffer, + sizeof(current->right->token.value)); + current->right->value = (current->right->token.value[0] != '\0'); + current->right->done = 1; + break; + default: + current = current->right; + continue; + } + } +#ifdef DEBUG_INCLUDE + ap_rvputs(r, " Left: ", current->left->value ? "1" : "0", + "\n", NULL); + ap_rvputs(r, " Right: ", current->right->value ? "1" : "0", + "\n", NULL); +#endif + if (current->token.type == token_and) { + current->value = current->left->value && current->right->value; + } + else { + current->value = current->left->value || current->right->value; + } +#ifdef DEBUG_INCLUDE + ap_rvputs(r, " Returning ", current->value ? "1" : "0", + "\n", NULL); +#endif + current->done = 1; + current = current->parent; + break; + + case token_eq: + case token_ne: +#ifdef DEBUG_INCLUDE + ap_rputs(" Evaluate eq/ne\n", r); +#endif + if ((current->left == (struct parse_node *) NULL) || + (current->right == (struct parse_node *) NULL) || + (current->left->token.type != token_string) || + (current->right->token.type != token_string)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + parse_string(r, current->left->token.value, + buffer, sizeof(buffer), 0); + ap_cpystrn(current->left->token.value, buffer, + sizeof(current->left->token.value)); + parse_string(r, current->right->token.value, + buffer, sizeof(buffer), 0); + ap_cpystrn(current->right->token.value, buffer, + sizeof(current->right->token.value)); + if (current->right->token.value[0] == '/') { + int len; + len = strlen(current->right->token.value); + if (current->right->token.value[len - 1] == '/') { + current->right->token.value[len - 1] = '\0'; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid rexp \"%s\" in file %s", + current->right->token.value, r->filename); + ap_rputs(error, r); + goto RETURN; + } +#ifdef DEBUG_INCLUDE + ap_rvputs(r, " Re Compare (", current->left->token.value, + ") with /", ¤t->right->token.value[1], "/\n", NULL); +#endif + current->value = + re_check(r, current->left->token.value, + ¤t->right->token.value[1]); + } + else { +#ifdef DEBUG_INCLUDE + ap_rvputs(r, " Compare (", current->left->token.value, + ") with (", current->right->token.value, ")\n", NULL); +#endif + current->value = + (strcmp(current->left->token.value, + current->right->token.value) == 0); + } + if (current->token.type == token_ne) { + current->value = !current->value; + } +#ifdef DEBUG_INCLUDE + ap_rvputs(r, " Returning ", current->value ? "1" : "0", + "\n", NULL); +#endif + current->done = 1; + current = current->parent; + break; + case token_ge: + case token_gt: + case token_le: + case token_lt: +#ifdef DEBUG_INCLUDE + ap_rputs(" Evaluate ge/gt/le/lt\n", r); +#endif + if ((current->left == (struct parse_node *) NULL) || + (current->right == (struct parse_node *) NULL) || + (current->left->token.type != token_string) || + (current->right->token.type != token_string)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + } + parse_string(r, current->left->token.value, + buffer, sizeof(buffer), 0); + ap_cpystrn(current->left->token.value, buffer, + sizeof(current->left->token.value)); + parse_string(r, current->right->token.value, + buffer, sizeof(buffer), 0); + ap_cpystrn(current->right->token.value, buffer, + sizeof(current->right->token.value)); +#ifdef DEBUG_INCLUDE + ap_rvputs(r, " Compare (", current->left->token.value, + ") with (", current->right->token.value, ")\n", NULL); +#endif + current->value = + strcmp(current->left->token.value, + current->right->token.value); + if (current->token.type == token_ge) { + current->value = current->value >= 0; + } + else if (current->token.type == token_gt) { + current->value = current->value > 0; + } + else if (current->token.type == token_le) { + current->value = current->value <= 0; + } + else if (current->token.type == token_lt) { + current->value = current->value < 0; + } + else { + current->value = 0; /* Don't return -1 if unknown token */ + } +#ifdef DEBUG_INCLUDE + ap_rvputs(r, " Returning ", current->value ? "1" : "0", + "\n", NULL); +#endif + current->done = 1; + current = current->parent; + break; + + case token_not: + if (current->right != (struct parse_node *) NULL) { + if (!current->right->done) { + current = current->right; + continue; + } + current->value = !current->right->value; + } + else { + current->value = 0; + } +#ifdef DEBUG_INCLUDE + ap_rvputs(r, " Evaluate !: ", current->value ? "1" : "0", + "\n", NULL); +#endif + current->done = 1; + current = current->parent; + break; + + case token_group: + if (current->right != (struct parse_node *) NULL) { + if (!current->right->done) { + current = current->right; + continue; + } + current->value = current->right->value; + } + else { + current->value = 1; + } +#ifdef DEBUG_INCLUDE + ap_rvputs(r, " Evaluate (): ", current->value ? "1" : "0", + "\n", NULL); +#endif + current->done = 1; + current = current->parent; + break; + + case token_lbrace: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Unmatched '(' in \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + + case token_rbrace: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Unmatched ')' in \"%s\" in file %s", + expr, r->filename); + ap_rputs(error, r); + goto RETURN; + + default: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "bad token type"); + ap_rputs(error, r); + goto RETURN; + } + } + + retval = (root == (struct parse_node *) NULL) ? 0 : root->value; + RETURN: + ap_destroy_pool(expr_pool); + return (retval); +} + +static int handle_if(FILE *in, request_rec *r, const char *error, + int *conditional_status, int *printing) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + char *expr; + + expr = NULL; + while (1) { + tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0); + if (*tag == '\0') { + return 1; + } + else if (!strcmp(tag, "done")) { + if (expr == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "missing expr in if statement: %s", + r->filename); + ap_rputs(error, r); + return 1; + } + *printing = *conditional_status = parse_expr(r, expr, error); +#ifdef DEBUG_INCLUDE + ap_rvputs(r, "**** if conditional_status=\"", + *conditional_status ? "1" : "0", "\"\n", NULL); +#endif + return 0; + } + else if (!strcmp(tag, "expr")) { + expr = tag_val; +#ifdef DEBUG_INCLUDE + ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL); +#endif + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "unknown parameter \"%s\" to tag if in %s", + tag, r->filename); + ap_rputs(error, r); + } + } +} + +static int handle_elif(FILE *in, request_rec *r, const char *error, + int *conditional_status, int *printing) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + char *expr; + + expr = NULL; + while (1) { + tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0); + if (*tag == '\0') { + return 1; + } + else if (!strcmp(tag, "done")) { +#ifdef DEBUG_INCLUDE + ap_rvputs(r, "**** elif conditional_status=\"", + *conditional_status ? "1" : "0", "\"\n", NULL); +#endif + if (*conditional_status) { + *printing = 0; + return (0); + } + if (expr == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "missing expr in elif statement: %s", + r->filename); + ap_rputs(error, r); + return 1; + } + *printing = *conditional_status = parse_expr(r, expr, error); +#ifdef DEBUG_INCLUDE + ap_rvputs(r, "**** elif conditional_status=\"", + *conditional_status ? "1" : "0", "\"\n", NULL); +#endif + return 0; + } + else if (!strcmp(tag, "expr")) { + expr = tag_val; +#ifdef DEBUG_INCLUDE + ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL); +#endif + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "unknown parameter \"%s\" to tag if in %s", + tag, r->filename); + ap_rputs(error, r); + } + } +} + +static int handle_else(FILE *in, request_rec *r, const char *error, + int *conditional_status, int *printing) +{ + char tag[MAX_STRING_LEN]; + + if (!get_tag(r->pool, in, tag, sizeof(tag), 1)) { + return 1; + } + else if (!strcmp(tag, "done")) { +#ifdef DEBUG_INCLUDE + ap_rvputs(r, "**** else conditional_status=\"", + *conditional_status ? "1" : "0", "\"\n", NULL); +#endif + *printing = !(*conditional_status); + *conditional_status = 1; + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "else directive does not take tags in %s", + r->filename); + if (*printing) { + ap_rputs(error, r); + } + return -1; + } +} + +static int handle_endif(FILE *in, request_rec *r, const char *error, + int *conditional_status, int *printing) +{ + char tag[MAX_STRING_LEN]; + + if (!get_tag(r->pool, in, tag, sizeof(tag), 1)) { + return 1; + } + else if (!strcmp(tag, "done")) { +#ifdef DEBUG_INCLUDE + ap_rvputs(r, "**** endif conditional_status=\"", + *conditional_status ? "1" : "0", "\"\n", NULL); +#endif + *printing = 1; + *conditional_status = 1; + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "endif directive does not take tags in %s", + r->filename); + ap_rputs(error, r); + return -1; + } +} + +static int handle_set(FILE *in, request_rec *r, const char *error) +{ + char tag[MAX_STRING_LEN]; + char parsed_string[MAX_STRING_LEN]; + char *tag_val; + char *var; + + var = (char *) NULL; + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + else if (!strcmp(tag, "done")) { + return 0; + } + else if (!strcmp(tag, "var")) { + var = tag_val; + } + else if (!strcmp(tag, "value")) { + if (var == (char *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "variable must precede value in set directive in %s", + r->filename); + ap_rputs(error, r); + return -1; + } + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + ap_table_setn(r->subprocess_env, var, ap_pstrdup(r->pool, parsed_string)); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid tag for set directive in %s", r->filename); + ap_rputs(error, r); + return -1; + } + } +} + +static int handle_printenv(FILE *in, request_rec *r, const char *error) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + array_header *arr = ap_table_elts(r->subprocess_env); + table_entry *elts = (table_entry *) arr->elts; + int i; + + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + else if (!strcmp(tag, "done")) { + for (i = 0; i < arr->nelts; ++i) { + ap_rvputs(r, elts[i].key, "=", elts[i].val, "\n", NULL); + } + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "printenv directive does not take tags in %s", + r->filename); + ap_rputs(error, r); + return -1; + } +} + + + +/* -------------------------- The main function --------------------------- */ + +/* This is a stub which parses a file descriptor. */ + +static void send_parsed_content(FILE *f, request_rec *r) +{ + char directive[MAX_STRING_LEN], error[MAX_STRING_LEN]; + char timefmt[MAX_STRING_LEN]; + int noexec = ap_allow_options(r) & OPT_INCNOEXEC; + int ret, sizefmt; + int if_nesting; + int printing; + int conditional_status; + + ap_cpystrn(error, DEFAULT_ERROR_MSG, sizeof(error)); + ap_cpystrn(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt)); + sizefmt = SIZEFMT_KMG; + +/* Turn printing on */ + printing = conditional_status = 1; + if_nesting = 0; + +#ifndef WIN32 + ap_chdir_file(r->filename); +#endif + if (r->args) { /* add QUERY stuff to env cause it ain't yet */ + char *arg_copy = ap_pstrdup(r->pool, r->args); + + ap_table_setn(r->subprocess_env, "QUERY_STRING", r->args); + ap_unescape_url(arg_copy); + ap_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED", + ap_escape_shell_cmd(r->pool, arg_copy)); + } + + while (1) { + if (!find_string(f, STARTING_SEQUENCE, r, printing)) { + if (get_directive(f, directive, sizeof(directive), r->pool)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "mod_include: error reading directive in %s", + r->filename); + ap_rputs(error, r); + return; + } + if (!strcmp(directive, "if")) { + if (!printing) { + if_nesting++; + } + else { + ret = handle_if(f, r, error, &conditional_status, + &printing); + if_nesting = 0; + } + continue; + } + else if (!strcmp(directive, "else")) { + if (!if_nesting) { + ret = handle_else(f, r, error, &conditional_status, + &printing); + } + continue; + } + else if (!strcmp(directive, "elif")) { + if (!if_nesting) { + ret = handle_elif(f, r, error, &conditional_status, + &printing); + } + continue; + } + else if (!strcmp(directive, "endif")) { + if (!if_nesting) { + ret = handle_endif(f, r, error, &conditional_status, + &printing); + } + else { + if_nesting--; + } + continue; + } + if (!printing) { + continue; + } + if (!strcmp(directive, "exec")) { + if (noexec) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "exec used but not allowed in %s", + r->filename); + if (printing) { + ap_rputs(error, r); + } + ret = find_string(f, ENDING_SEQUENCE, r, 0); + } + else { + ret = handle_exec(f, r, error); + } + } + else if (!strcmp(directive, "config")) { + ret = handle_config(f, r, error, timefmt, &sizefmt); + } + else if (!strcmp(directive, "set")) { + ret = handle_set(f, r, error); + } + else if (!strcmp(directive, "include")) { + ret = handle_include(f, r, error, noexec); + } + else if (!strcmp(directive, "echo")) { + ret = handle_echo(f, r, error); + } + else if (!strcmp(directive, "fsize")) { + ret = handle_fsize(f, r, error, sizefmt); + } + else if (!strcmp(directive, "flastmod")) { + ret = handle_flastmod(f, r, error, timefmt); + } + else if (!strcmp(directive, "printenv")) { + ret = handle_printenv(f, r, error); + } +#ifdef USE_PERL_SSI + else if (!strcmp(directive, "perl")) { + ret = handle_perl(f, r, error); + } +#endif + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "unknown directive \"%s\" " + "in parsed doc %s", + directive, r->filename); + if (printing) { + ap_rputs(error, r); + } + ret = find_string(f, ENDING_SEQUENCE, r, 0); + } + if (ret) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "premature EOF in parsed file %s", + r->filename); + return; + } + } + else { + return; + } + } +} + +/***************************************************************** + * + * XBITHACK. Sigh... NB it's configurable per-directory; the compile-time + * option only changes the default. + */ + +module includes_module; +enum xbithack { + xbithack_off, xbithack_on, xbithack_full +}; + +#ifdef XBITHACK +#define DEFAULT_XBITHACK xbithack_full +#else +#define DEFAULT_XBITHACK xbithack_off +#endif + +static void *create_includes_dir_config(pool *p, char *dummy) +{ + enum xbithack *result = (enum xbithack *) ap_palloc(p, sizeof(enum xbithack)); + *result = DEFAULT_XBITHACK; + return result; +} + +static const char *set_xbithack(cmd_parms *cmd, void *xbp, char *arg) +{ + enum xbithack *state = (enum xbithack *) xbp; + + if (!strcasecmp(arg, "off")) { + *state = xbithack_off; + } + else if (!strcasecmp(arg, "on")) { + *state = xbithack_on; + } + else if (!strcasecmp(arg, "full")) { + *state = xbithack_full; + } + else { + return "XBitHack must be set to Off, On, or Full"; + } + + return NULL; +} + +static int send_parsed_file(request_rec *r) +{ + FILE *f; + enum xbithack *state = + (enum xbithack *) ap_get_module_config(r->per_dir_config, &includes_module); + int errstatus; + request_rec *parent; + + if (!(ap_allow_options(r) & OPT_INCLUDES)) { + return DECLINED; + } + r->allowed |= (1 << M_GET); + if (r->method_number != M_GET) { + return DECLINED; + } + if (r->finfo.st_mode == 0) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "File does not exist: %s", + (r->path_info + ? ap_pstrcat(r->pool, r->filename, r->path_info, NULL) + : r->filename)); + return HTTP_NOT_FOUND; + } + + if (!(f = ap_pfopen(r->pool, r->filename, "r"))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "file permissions deny server access: %s", r->filename); + return HTTP_FORBIDDEN; + } + + if ((*state == xbithack_full) +#if !defined(OS2) && !defined(WIN32) + /* OS/2 dosen't support Groups. */ + && (r->finfo.st_mode & S_IXGRP) +#endif + ) { + ap_update_mtime(r, r->finfo.st_mtime); + ap_set_last_modified(r); + } + if ((errstatus = ap_meets_conditions(r)) != OK) { + return errstatus; + } + + ap_send_http_header(r); + + if (r->header_only) { + ap_pfclose(r->pool, f); + return OK; + } + + if ((parent = ap_get_module_config(r->request_config, &includes_module))) { + /* Kludge --- for nested includes, we want to keep the subprocess + * environment of the base document (for compatibility); that means + * torquing our own last_modified date as well so that the + * LAST_MODIFIED variable gets reset to the proper value if the + * nested document resets . + * We also insist that the memory for this subrequest not be + * destroyed, that's dealt with in handle_include(). + */ + r->subprocess_env = parent->subprocess_env; + ap_pool_join(parent->pool, r->pool); + r->finfo.st_mtime = parent->finfo.st_mtime; + } + else { + /* we're not a nested include, so we create an initial + * environment */ + ap_add_common_vars(r); + ap_add_cgi_vars(r); + add_include_vars(r, DEFAULT_TIME_FORMAT); + } + /* XXX: this is bogus, at some point we're going to do a subrequest, + * and when we do it we're going to be subjecting code that doesn't + * expect to be signal-ready to SIGALRM. There is no clean way to + * fix this, except to put alarm support into BUFF. -djg + */ + ap_hard_timeout("send SSI", r); + +#ifdef CHARSET_EBCDIC + /* XXX:@@@ Is the generated/included output ALWAYS in text/ebcdic format? */ + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1); +#endif + + send_parsed_content(f, r); + + if (parent) { + /* signify that the sub request should not be killed */ + ap_set_module_config(r->request_config, &includes_module, + NESTED_INCLUDE_MAGIC); + } + + ap_kill_timeout(r); + return OK; +} + +static int send_shtml_file(request_rec *r) +{ + r->content_type = "text/html"; + return send_parsed_file(r); +} + +static int xbithack_handler(request_rec *r) +{ +#if defined(OS2) || defined(WIN32) + /* OS/2 dosen't currently support the xbithack. This is being worked on. */ + return DECLINED; +#else + enum xbithack *state; + + if (!(r->finfo.st_mode & S_IXUSR)) { + return DECLINED; + } + + state = (enum xbithack *) ap_get_module_config(r->per_dir_config, + &includes_module); + + if (*state == xbithack_off) { + return DECLINED; + } + return send_parsed_file(r); +#endif +} + +static const command_rec includes_cmds[] = +{ + {"XBitHack", set_xbithack, NULL, OR_OPTIONS, TAKE1, "Off, On, or Full"}, + {NULL} +}; + +static const handler_rec includes_handlers[] = +{ + {INCLUDES_MAGIC_TYPE, send_shtml_file}, + {INCLUDES_MAGIC_TYPE3, send_shtml_file}, + {"server-parsed", send_parsed_file}, + {"text/html", xbithack_handler}, + {NULL} +}; + +module MODULE_VAR_EXPORT includes_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_includes_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + includes_cmds, /* command table */ + includes_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/generators/.indent.pro b/modules/generators/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /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/mod_asis.c b/modules/generators/mod_asis.c new file mode 100644 index 00000000000..5e299df9268 --- /dev/null +++ b/modules/generators/mod_asis.c @@ -0,0 +1,145 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#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" + +static int asis_handler(request_rec *r) +{ + FILE *f; + const char *location; + + r->allowed |= (1 << M_GET); + if (r->method_number != M_GET) + return DECLINED; + if (r->finfo.st_mode == 0) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "File does not exist: %s", r->filename); + return NOT_FOUND; + } + + f = ap_pfopen(r->pool, r->filename, "r"); + + if (f == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "file permissions deny server access: %s", r->filename); + return FORBIDDEN; + } + + ap_scan_script_header_err(r, f, NULL); + location = ap_table_get(r->headers_out, "Location"); + + if (location && location[0] == '/' && + ((r->status == HTTP_OK) || ap_is_HTTP_REDIRECT(r->status))) { + + ap_pfclose(r->pool, 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 = ap_pstrdup(r->pool, "GET"); + r->method_number = M_GET; + + ap_internal_redirect_handler(location, r); + return OK; + } + + ap_send_http_header(r); + if (!r->header_only) + ap_send_fd(f, r); + + ap_pfclose(r->pool, f); + return OK; +} + +static const handler_rec asis_handlers[] = +{ + {ASIS_MAGIC_TYPE, asis_handler}, + {"send-as-is", asis_handler}, + {NULL} +}; + +module MODULE_VAR_EXPORT asis_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + 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 table */ + asis_handlers, /* handlers */ + NULL, /* translate_handler */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* pre-run fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/generators/mod_autoindex.c b/modules/generators/mod_autoindex.c new file mode 100644 index 00000000000..9c28dc40864 --- /dev/null +++ b/modules/generators/mod_autoindex.c @@ -0,0 +1,1673 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_autoindex.c: Handles the on-the-fly html index generation + * + * Rob McCool + * 3/23/93 + * + * Adapted to Apache by rst. + */ + +#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 "fnmatch.h" + +module MODULE_VAR_EXPORT autoindex_module; + +/**************************************************************** + * + * Handling configuration directives... + */ + +#define HRULE 1 +#define NO_HRULE 0 +#define FRONT_MATTER 1 +#define END_MATTER 0 + +#define FANCY_INDEXING 1 /* Indexing options */ +#define ICONS_ARE_LINKS 2 +#define SCAN_HTML_TITLES 4 +#define SUPPRESS_LAST_MOD 8 +#define SUPPRESS_SIZE 16 +#define SUPPRESS_DESC 32 +#define SUPPRESS_PREAMBLE 64 +#define SUPPRESS_COLSORT 128 +#define NO_OPTIONS 256 + +#define K_PAD 1 +#define K_NOPAD 0 + +#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 D_ASCENDING 'A' +#define D_DESCENDING 'D' + +/* + * 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 + +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; + int opts; + int incremented_opts; + int decremented_opts; + int name_width; + int name_adjust; + int icon_width; + int icon_height; + char *default_order; + + array_header *icon_list; + array_header *alt_list; + array_header *desc_list; + array_header *ign_list; + array_header *hdr_list; + array_header *rdme_list; + +} 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 + +/* + * Return true if the specified string refers to the parent directory (i.e., + * matches ".." or "../"). Hopefully this one call is significantly less + * expensive than multiple strcmp() calls. + */ +static ap_inline int is_parent(const char *name) +{ + /* + * Now, IFF the first two bytes are dots, and the third byte is either + * EOS (\0) or a slash followed by EOS, we have a match. + */ + if (((name[0] == '.') && (name[1] == '.')) + && ((name[2] == '\0') + || ((name[2] == '/') && (name[3] == '\0')))) { + return 1; + } + return 0; +} + +/* + * 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, char *title) +{ + ap_rvputs(r, DOCTYPE_HTML_3_2, + "\n \n Index of ", title, + "\n \n \n", NULL); +} + +static void push_item(array_header *arr, char *type, char *to, char *path, + char *data) +{ + struct item *p = (struct item *) ap_push_array(arr); + + if (!to) { + to = ""; + } + if (!path) { + path = ""; + } + + p->type = type; + p->data = data ? ap_pstrdup(arr->pool, data) : NULL; + p->apply_path = ap_pstrcat(arr->pool, path, "*", NULL); + + if ((type == BY_PATH) && (!ap_is_matchexp(to))) { + p->apply_to = ap_pstrcat(arr->pool, "*", to, NULL); + } + else if (to) { + p->apply_to = ap_pstrdup(arr->pool, to); + } + else { + p->apply_to = NULL; + } +} + +static const char *add_alt(cmd_parms *cmd, void *d, char *alt, char *to) +{ + if (cmd->info == BY_PATH) { + if (!strcmp(to, "**DIRECTORY**")) { + to = "^^DIRECTORY^^"; + } + } + if (cmd->info == BY_ENCODING) { + ap_str_tolower(to); + } + + 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, char *icon, char *to) +{ + char *iconbak = ap_pstrdup(cmd->pool, icon); + + if (icon[0] == '(') { + char *alt; + char *cl = strchr(iconbak, ')'); + + if (cl == NULL) { + return "missing closing paren"; + } + alt = ap_getword_nc(cmd->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) { + ap_str_tolower(to); + } + + 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, char *desc, char *to) +{ + autoindex_config_rec *dcfg = (autoindex_config_rec *) d; + ai_desc_t *desc_entry; + char *prefix = ""; + + desc_entry = (ai_desc_t *) ap_push_array(dcfg->desc_list); + desc_entry->full_path = (strchr(to, '/') == NULL) ? 0 : 1; + desc_entry->wildcards = (WILDCARDS_REQUIRED + || desc_entry->full_path + || ap_is_fnmatch(to)); + if (desc_entry->wildcards) { + prefix = desc_entry->full_path ? "*/" : "*"; + desc_entry->pattern = ap_pstrcat(dcfg->desc_list->pool, + prefix, to, "*", NULL); + } + else { + desc_entry->pattern = ap_pstrdup(dcfg->desc_list->pool, to); + } + desc_entry->description = ap_pstrdup(dcfg->desc_list->pool, desc); + return NULL; +} + +static const char *add_ignore(cmd_parms *cmd, void *d, char *ext) +{ + push_item(((autoindex_config_rec *) d)->ign_list, 0, ext, cmd->path, NULL); + return NULL; +} + +static const char *add_header(cmd_parms *cmd, void *d, char *name) +{ + push_item(((autoindex_config_rec *) d)->hdr_list, 0, NULL, cmd->path, + name); + return NULL; +} + +static const char *add_readme(cmd_parms *cmd, void *d, char *name) +{ + push_item(((autoindex_config_rec *) d)->rdme_list, 0, NULL, cmd->path, + name); + return NULL; +} + +/* A legacy directive, FancyIndexing is superseded by the IndexOptions + * keyword. But for compatibility.. + */ +static const char *fancy_indexing(cmd_parms *cmd, void *d, int arg) +{ + int curopts; + int newopts; + autoindex_config_rec *cfg; + + cfg = (autoindex_config_rec *) d; + curopts = cfg->opts; + if (curopts & NO_OPTIONS) { + return "FancyIndexing directive conflicts with existing " + "IndexOptions None"; + } + newopts = (arg ? (curopts | FANCY_INDEXING) : (curopts & ~FANCY_INDEXING)); + cfg->opts = newopts; + return NULL; +} + +static const char *add_opts(cmd_parms *cmd, void *d, const char *optstr) +{ + char *w; + int opts; + int opts_add; + int 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; + while (optstr[0]) { + int option = 0; + + w = ap_getword_conf(cmd->pool, &optstr); + if ((*w == '+') || (*w == '-')) { + action = *(w++); + } + else { + action = '\0'; + } + if (!strcasecmp(w, "FancyIndexing")) { + option = FANCY_INDEXING; + } + else if (!strcasecmp(w, "IconsAreLinks")) { + option = ICONS_ARE_LINKS; + } + else if (!strcasecmp(w, "ScanHTMLTitles")) { + option = SCAN_HTML_TITLES; + } + else if (!strcasecmp(w, "SuppressLastModified")) { + option = SUPPRESS_LAST_MOD; + } + else if (!strcasecmp(w, "SuppressSize")) { + option = SUPPRESS_SIZE; + } + else if (!strcasecmp(w, "SuppressDescription")) { + option = SUPPRESS_DESC; + } + else if (!strcasecmp(w, "SuppressHTMLPreamble")) { + option = SUPPRESS_PREAMBLE; + } + else if (!strcasecmp(w, "SuppressColumnSorting")) { + option = SUPPRESS_COLSORT; + } + 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 < 5) { + return "NameWidth value must be greater than 5"; + } + d_cfg->name_width = width; + d_cfg->name_adjust = K_NOADJUST; + } + } + 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, char *direction, + char *key) +{ + char temp[4]; + autoindex_config_rec *d_cfg = (autoindex_config_rec *) m; + + ap_cpystrn(temp, "k=d", sizeof(temp)); + if (!strcasecmp(direction, "Ascending")) { + temp[2] = D_ASCENDING; + } + else if (!strcasecmp(direction, "Descending")) { + temp[2] = D_DESCENDING; + } + else { + return "First keyword must be 'Ascending' or 'Descending'"; + } + + if (!strcasecmp(key, "Name")) { + temp[0] = K_NAME; + } + else if (!strcasecmp(key, "Date")) { + temp[0] = K_LAST_MOD; + } + else if (!strcasecmp(key, "Size")) { + temp[0] = K_SIZE; + } + else if (!strcasecmp(key, "Description")) { + temp[0] = K_DESC; + } + else { + return "Second keyword must be 'Name', 'Date', 'Size', or " + "'Description'"; + } + + if (d_cfg->default_order == NULL) { + d_cfg->default_order = ap_palloc(cmd->pool, 4); + d_cfg->default_order[3] = '\0'; + } + ap_cpystrn(d_cfg->default_order, temp, sizeof(temp)); + return NULL; +} + +#define DIR_CMD_PERMS OR_INDEXES + +static const command_rec autoindex_cmds[] = +{ + {"AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more filenames"}, + {"AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more MIME types"}, + {"AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more content encodings"}, + {"AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more filenames"}, + {"AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more MIME types"}, + {"AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more content encodings"}, + {"IndexOptions", add_opts, NULL, DIR_CMD_PERMS, RAW_ARGS, + "one or more index options"}, + {"IndexOrderDefault", set_default_order, NULL, DIR_CMD_PERMS, TAKE2, + "{Ascending,Descending} {Name,Size,Description,Date}"}, + {"IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, ITERATE, + "one or more file extensions"}, + {"AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "Descriptive text followed by one or more filenames"}, + {"HeaderName", add_header, NULL, DIR_CMD_PERMS, TAKE1, "a filename"}, + {"ReadmeName", add_readme, NULL, DIR_CMD_PERMS, TAKE1, "a filename"}, + {"FancyIndexing", fancy_indexing, NULL, DIR_CMD_PERMS, FLAG, + "Limited to 'on' or 'off' (superseded by IndexOptions FancyIndexing)"}, + {"DefaultIcon", ap_set_string_slot, + (void *) XtOffsetOf(autoindex_config_rec, default_icon), + DIR_CMD_PERMS, TAKE1, "an icon URL"}, + {NULL} +}; + +static void *create_autoindex_config(pool *p, char *dummy) +{ + autoindex_config_rec *new = + (autoindex_config_rec *) ap_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->icon_list = ap_make_array(p, 4, sizeof(struct item)); + new->alt_list = ap_make_array(p, 4, sizeof(struct item)); + new->desc_list = ap_make_array(p, 4, sizeof(ai_desc_t)); + new->ign_list = ap_make_array(p, 4, sizeof(struct item)); + new->hdr_list = ap_make_array(p, 4, sizeof(struct item)); + new->rdme_list = ap_make_array(p, 4, sizeof(struct item)); + new->opts = 0; + new->incremented_opts = 0; + new->decremented_opts = 0; + new->default_order = NULL; + + return (void *) new; +} + +static void *merge_autoindex_configs(pool *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 *) ap_pcalloc(p, sizeof(autoindex_config_rec)); + new->default_icon = add->default_icon ? add->default_icon + : base->default_icon; + 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->alt_list = ap_append_arrays(p, add->alt_list, base->alt_list); + new->ign_list = ap_append_arrays(p, add->ign_list, base->ign_list); + new->hdr_list = ap_append_arrays(p, add->hdr_list, base->hdr_list); + new->desc_list = ap_append_arrays(p, add->desc_list, base->desc_list); + new->icon_list = ap_append_arrays(p, add->icon_list, base->icon_list); + new->rdme_list = ap_append_arrays(p, add->rdme_list, base->rdme_list); + if (add->opts & NO_OPTIONS) { + /* + * If the current directory 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 non-incremental 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 == 0) { + 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 non-incremental 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; + } + + new->default_order = (add->default_order != NULL) + ? add->default_order : base->default_order; + 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; + off_t size; + time_t lm; + struct ent *next; + int ascending; + char key; +}; + +static char *find_item(request_rec *r, array_header *list, int path_only) +{ + const char *content_type = r->content_type; + const char *content_encoding = r->content_encoding; + char *path = r->filename; + + 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; +} + +#define find_icon(d,p,t) find_item(p,d->icon_list,t) +#define find_alt(d,p,t) find_item(p,d->alt_list,t) +#define find_header(d,p) find_item(p,d->hdr_list,0) +#define find_readme(d,p) find_item(p,d->rdme_list,0) + +static char *find_default_icon(autoindex_config_rec *d, char *bogus_name) +{ + request_rec r; + + /* Bleah. I tried to clean up find_item, and it lead to this bit + * of ugliness. Note that the fields initialized are precisely + * those that find_item looks at... + */ + + r.filename = bogus_name; + r.content_type = r.content_encoding = NULL; + + return find_item(&r, d->icon_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 FNM_CASE_BLIND +#else +#define MATCH_FLAGS 0 +#endif + +static char *find_desc(autoindex_config_rec *dcfg, request_rec *r) +{ + int i; + ai_desc_t *list = (ai_desc_t *) dcfg->desc_list->elts; + const char *filename_full = r->filename; + 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 = strrchr(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 = (ap_fnmatch(tuple->pattern, filename, MATCH_FLAGS) == 0); + } + else { + found = (strstr(filename, tuple->pattern) != NULL); + } + if (found) { + return tuple->description; + } + } + return NULL; +} + +static int ignore_entry(autoindex_config_rec *d, char *path) +{ + array_header *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, FILE *f) +{ + char buf[IOBUFSIZE + 1]; + int i, n, c, ch; + + ap_rputs("
      \n", r);
      +    while (!feof(f)) {
      +	do {
      +	    n = fread(buf, sizeof(char), IOBUFSIZE, f);
      +	}
      +	while (n == -1 && ferror(f) && errno == EINTR);
      +	if (n == -1 || n == 0) {
      +	    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, + char *title) +{ + FILE *f; + request_rec *rr = NULL; + int emit_amble = 1; + int emit_H1 = 1; + + /* + * If there's a header 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 ((header_fname != NULL) + && (rr = ap_sub_req_lookup_uri(header_fname, r)) + && (rr->status == HTTP_OK) + && (rr->filename != NULL) + && S_ISREG(rr->finfo.st_mode)) { + /* + * 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 (!strcasecmp(ap_field_noparam(r->pool, rr->content_type), + "text/html")) { + /* Hope everything will work... */ + emit_amble = 0; + emit_H1 = 0; + + if (! suppress_amble) { + emit_preamble(r, title); + } + /* + * 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 ((f = ap_pfopen(r->pool, rr->filename, "r")) != 0) {
      +		    emit_preamble(r, title);
      +		    emit_amble = 0;
      +		    do_emit_plain(r, f);
      +		    ap_pfclose(r->pool, f);
      +		    emit_H1 = 0;
      +		}
      +	    }
      +	}
      +    }
      +
      +    if (emit_amble) {
      +	emit_preamble(r, title);
      +    }
      +    if (emit_H1) {
      +	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) +{ + FILE *f; + 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)) + && (rr->status == HTTP_OK) + && (rr->filename != NULL) + && S_ISREG(rr->finfo.st_mode)) { + /* + * 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 (!strcasecmp(ap_field_noparam(r->pool, rr->content_type), + "text/html")) { + 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 ((f = ap_pfopen(r->pool, rr->filename, "r")) != 0) { + do_emit_plain(r, f); + ap_pfclose(r->pool, 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 = ""; + FILE *thefile = NULL; + int x, y, n, p; + + if (r->status != HTTP_OK) { + return NULL; + } + if ((r->content_type != NULL) + && (!strcasecmp(ap_field_noparam(r->pool, r->content_type), + "text/html") + || !strcmp(r->content_type, INCLUDES_MAGIC_TYPE)) + && !r->content_encoding) { + if (!(thefile = ap_pfopen(r->pool, r->filename, "r"))) { + return NULL; + } + n = fread(titlebuf, sizeof(char), MAX_STRING_LEN - 1, thefile); + if (n <= 0) { + ap_pfclose(r->pool, thefile); + return NULL; + } + titlebuf[n] = '\0'; + for (x = 0, p = 0; titlebuf[x]; x++) { + if (ap_toupper(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] = ' '; + } + } + } + ap_pfclose(r->pool, thefile); + return ap_pstrdup(r->pool, &titlebuf[x]); + } + } + else { + p = 0; + } + } + ap_pfclose(r->pool, thefile); + } + return NULL; +} + +static struct ent *make_autoindex_entry(char *name, int autoindex_opts, + autoindex_config_rec *d, + request_rec *r, char keyid, + char direction) +{ + struct ent *p; + + if ((name[0] == '.') && (!name[1])) { + return (NULL); + } + + if (ignore_entry(d, ap_make_full_path(r->pool, r->filename, name))) { + return (NULL); + } + + p = (struct ent *) ap_pcalloc(r->pool, sizeof(struct ent)); + p->name = ap_pstrdup(r->pool, name); + p->size = -1; + p->icon = NULL; + p->alt = NULL; + p->desc = NULL; + p->lm = -1; + p->key = ap_toupper(keyid); + p->ascending = (ap_toupper(direction) == D_ASCENDING); + + if (autoindex_opts & FANCY_INDEXING) { + request_rec *rr = ap_sub_req_lookup_file(name, r); + + if (rr->finfo.st_mode != 0) { + p->lm = rr->finfo.st_mtime; + if (S_ISDIR(rr->finfo.st_mode)) { + if (!(p->icon = find_icon(d, rr, 1))) { + p->icon = find_default_icon(d, "^^DIRECTORY^^"); + } + if (!(p->alt = find_alt(d, rr, 1))) { + p->alt = "DIR"; + } + p->size = -1; + p->name = ap_pstrcat(r->pool, name, "/", NULL); + } + else { + p->icon = find_icon(d, rr, 0); + p->alt = find_alt(d, rr, 0); + p->size = rr->finfo.st_size; + } + } + + p->desc = find_desc(d, rr); + + if ((!p->desc) && (autoindex_opts & SCAN_HTML_TITLES)) { + p->desc = ap_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, + int autoindex_opts) +{ + int maxsize = 23; + register int x; + + 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, char *anchor, char fname, char curkey, + char curdirection, int nosort) +{ + char qvalue[5]; + int reverse; + + if (!nosort) { + qvalue[0] = '?'; + qvalue[1] = fname; + qvalue[2] = '='; + qvalue[4] = '\0'; + reverse = ((curkey == fname) && (curdirection == D_ASCENDING)); + qvalue[3] = reverse ? D_DESCENDING : D_ASCENDING; + ap_rvputs(r, "<A HREF=\"", qvalue, "\">", anchor, "</A>", NULL); + } + else { + ap_rputs(anchor, r); + } +} + +static void output_directories(struct ent **ar, int n, + autoindex_config_rec *d, request_rec *r, + int autoindex_opts, char keyid, char direction) +{ + int x; + char *name = r->uri; + char *tp; + int static_columns = (autoindex_opts & SUPPRESS_COLSORT); + pool *scratch = ap_make_sub_pool(r->pool); + int name_width; + char *name_scratch; + char *pad_scratch; + + if (name[0] == '\0') { + name = "/"; + } + + name_width = d->name_width; + 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; + } + } + } + name_scratch = ap_palloc(r->pool, name_width + 1); + pad_scratch = ap_palloc(r->pool, name_width + 1); + memset(pad_scratch, ' ', name_width); + pad_scratch[name_width] = '\0'; + + if (autoindex_opts & FANCY_INDEXING) { + ap_rputs("<PRE>", r); + if ((tp = find_default_icon(d, "^^BLANKICON^^"))) { + ap_rvputs(r, "<IMG SRC=\"", ap_escape_html(scratch, tp), + "\" ALT=\" \"", NULL); + if (d->icon_width && d->icon_height) { + ap_rprintf + ( + r, + " HEIGHT=\"%d\" WIDTH=\"%d\"", + d->icon_height, + d->icon_width + ); + } + ap_rputs("> ", r); + } + emit_link(r, "Name", K_NAME, keyid, direction, 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, + static_columns); + ap_rputs(" ", r); + } + if (!(autoindex_opts & SUPPRESS_SIZE)) { + emit_link(r, "Size", K_SIZE, keyid, direction, static_columns); + ap_rputs(" ", r); + } + if (!(autoindex_opts & SUPPRESS_DESC)) { + emit_link(r, "Description", K_DESC, keyid, direction, + static_columns); + } + ap_rputs("\n<HR>\n", r); + } + else { + ap_rputs("<UL>", r); + } + + for (x = 0; x < n; x++) { + char *anchor, *t, *t2; + int nwidth; + + ap_clear_pool(scratch); + + if (is_parent(ar[x]->name)) { + t = ap_make_full_path(scratch, name, "../"); + ap_getparents(t); + if (t[0] == '\0') { + t = "/"; + } + t2 = "Parent Directory"; + anchor = ap_escape_html(scratch, ap_os_escape_path(scratch, t, 0)); + } + else { + t = ar[x]->name; + t2 = t; + anchor = ap_escape_html(scratch, ap_os_escape_path(scratch, t, 0)); + } + + if (autoindex_opts & FANCY_INDEXING) { + 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 && d->icon_height) { + ap_rprintf(r, " HEIGHT=\"%d\" WIDTH=\"%d\"", + d->icon_height, d->icon_width); + } + ap_rputs(">", r); + } + if (autoindex_opts & ICONS_ARE_LINKS) { + ap_rputs("</A>", 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[MAX_STRING_LEN]; + struct tm *ts = localtime(&ar[x]->lm); + strftime(time_str, MAX_STRING_LEN, "%d-%b-%Y %H:%M ", ts); + ap_rputs(time_str, r); + } + else { + /*Length="22-Feb-1998 23:42 " (see 4 lines above) */ + ap_rputs(" ", r); + } + } + if (!(autoindex_opts & SUPPRESS_SIZE)) { + ap_send_size(ar[x]->size, r); + ap_rputs(" ", r); + } + if (!(autoindex_opts & SUPPRESS_DESC)) { + if (ar[x]->desc) { + ap_rputs(terminate_description(d, ar[x]->desc, + autoindex_opts), r); + } + } + } + else { + ap_rvputs(r, "<LI><A HREF=\"", anchor, "\"> ", t2, + "</A>", NULL); + } + ap_rputc('\n', r); + } + if (autoindex_opts & FANCY_INDEXING) { + ap_rputs("</PRE>", r); + } + else { + ap_rputs("</UL>", 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 (is_parent((*e1)->name)) { + return -1; + } + if (is_parent((*e2)->name)) { + return 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: + result = strcmp(c1->desc ? c1->desc : "", c2->desc ? c2->desc : ""); + if (result) { + return result; + } + break; + } + return strcmp(c1->name, c2->name); +} + + +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; + + DIR *d; + struct DIR_TYPE *dstruct; + int num_ent = 0, x; + struct ent *head, *p; + struct ent **ar = NULL; + const char *qstring; + int autoindex_opts = autoindex_conf->opts; + char keyid; + char direction; + + if (!(d = ap_popendir(r->pool, name))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "Can't open directory for index: %s", r->filename); + return HTTP_FORBIDDEN; + } + + r->content_type = "text/html"; + + ap_send_http_header(r); + + if (r->header_only) { + ap_pclosedir(r->pool, d); + return 0; + } + ap_hard_timeout("send directory", r); + + /* Spew HTML preamble */ + + title_endp = title_name + strlen(title_name) - 1; + + while (title_endp > title_name && *title_endp == '/') { + *title_endp-- = '\0'; + } + + emit_head(r, find_header(autoindex_conf, r), + autoindex_opts & SUPPRESS_PREAMBLE, title_name); + + /* + * Figure out what sort of indexing (if any) we're supposed to use. + * + * If no QUERY_STRING was specified or column sorting has been + * explicitly disabled, we use the default specified by the + * IndexOrderDefault directive (if there is one); otherwise, + * we fall back to ascending by name. + */ + qstring = r->args; + if ((autoindex_opts & SUPPRESS_COLSORT) + || ((qstring == NULL) || (*qstring == '\0'))) { + qstring = autoindex_conf->default_order; + } + /* + * If there is no specific ordering defined for this directory, + * default to ascending by filename. + */ + if ((qstring == NULL) || (*qstring == '\0')) { + keyid = K_NAME; + direction = D_ASCENDING; + } + else { + keyid = *qstring; + ap_getword(r->pool, &qstring, '='); + if (qstring != '\0') { + direction = *qstring; + } + else { + direction = D_ASCENDING; + } + } + + /* + * 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; + while ((dstruct = readdir(d))) { + p = make_autoindex_entry(dstruct->d_name, autoindex_opts, + autoindex_conf, r, keyid, direction); + if (p != NULL) { + p->next = head; + head = p; + num_ent++; + } + } + if (num_ent > 0) { + ar = (struct ent **) ap_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); + ap_pclosedir(r->pool, d); + + if (autoindex_opts & FANCY_INDEXING) { + ap_rputs("<HR>\n", r); + } + emit_tail(r, find_readme(autoindex_conf, r), + autoindex_opts & SUPPRESS_PREAMBLE); + + ap_kill_timeout(r); + return 0; +} + +/* The formal handler... */ + +static int handle_autoindex(request_rec *r) +{ + autoindex_config_rec *d; + int allow_opts = ap_allow_options(r); + + d = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config, + &autoindex_module); + + r->allowed |= (1 << M_GET); + if (r->method_number != M_GET) { + return DECLINED; + } + + /* OK, nothing easy. Trot out the heavy artillery... */ + + if (allow_opts & OPT_INDEXES) { + /* 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 = ap_pstrcat(r->pool, r->filename, "/", NULL); + } + return index_directory(r, d); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Directory index forbidden by rule: %s", r->filename); + return HTTP_FORBIDDEN; + } +} + + +static const handler_rec autoindex_handlers[] = +{ + {DIR_MAGIC_TYPE, handle_autoindex}, + {NULL} +}; + +module MODULE_VAR_EXPORT autoindex_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + 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 table */ + autoindex_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/generators/mod_cgi.c b/modules/generators/mod_cgi.c new file mode 100644 index 00000000000..392fb5a1f13 --- /dev/null +++ b/modules/generators/mod_cgi.c @@ -0,0 +1,609 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +/* + * http_script: keeps all script-related ramblings together. + * + * Compliant to CGI/1.1 spec + * + * Adapted by rst from original NCSA code by Rob McCool + * + * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for + * custom error responses, and DOCUMENT_ROOT because we found it useful. + * It also adds SERVER_ADMIN - useful for scripts to know who to mail when + * they fail. + */ + +#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 "http_conf_globals.h" + +module MODULE_VAR_EXPORT cgi_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 = ap_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 { + char *logname; + long logbytes; + int bufbytes; +} cgi_server_conf; + +static void *create_cgi_config(pool *p, server_rec *s) +{ + cgi_server_conf *c = + (cgi_server_conf *) ap_pcalloc(p, sizeof(cgi_server_conf)); + + c->logname = NULL; + c->logbytes = DEFAULT_LOGBYTES; + c->bufbytes = DEFAULT_BUFBYTES; + + return c; +} + +static void *merge_cgi_config(pool *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, char *arg) +{ + server_rec *s = cmd->server; + cgi_server_conf *conf = + (cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module); + + conf->logname = arg; + return NULL; +} + +static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, char *arg) +{ + server_rec *s = cmd->server; + cgi_server_conf *conf = + (cgi_server_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, char *arg) +{ + server_rec *s = cmd->server; + cgi_server_conf *conf = + (cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module); + + conf->bufbytes = atoi(arg); + return NULL; +} + +static const command_rec cgi_cmds[] = +{ + {"ScriptLog", set_scriptlog, NULL, RSRC_CONF, TAKE1, + "the name of a log for script debugging info"}, + {"ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, TAKE1, + "the maximum length (in bytes) of the script debug log"}, + {"ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, TAKE1, + "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, + int show_errno, char *error) +{ + FILE *f; + struct stat finfo; + + ap_log_rerror(APLOG_MARK, show_errno|APLOG_ERR, r, + "%s: %s", error, r->filename); + + if (!conf->logname || + ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0) + && (finfo.st_size > conf->logbytes)) || + ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname), + "a")) == NULL)) { + return ret; + } + + /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ + fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri, + r->args ? "?" : "", r->args ? r->args : "", r->protocol); + /* "%% 500 /usr/local/apache/cgi-bin */ + fprintf(f, "%%%% %d %s\n", ret, r->filename); + + fprintf(f, "%%error\n%s\n", error); + + ap_pfclose(r->pool, f); + return ret; +} + +static int log_script(request_rec *r, cgi_server_conf * conf, int ret, + char *dbuf, const char *sbuf, BUFF *script_in, BUFF *script_err) +{ + array_header *hdrs_arr = ap_table_elts(r->headers_in); + table_entry *hdrs = (table_entry *) hdrs_arr->elts; + char argsbuffer[HUGE_STRING_LEN]; + FILE *f; + int i; + struct stat finfo; + + if (!conf->logname || + ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0) + && (finfo.st_size > conf->logbytes)) || + ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname), + "a")) == NULL)) { + /* Soak up script output */ + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) + continue; +#ifdef WIN32 + /* Soak up stderr and redirect it to the error log. + * Script output to stderr is already directed to the error log + * on Unix, thanks to the magic of fork(). + */ + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r, + "%s", argsbuffer); + } +#else + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) + continue; +#endif + return ret; + } + + /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ + fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri, + r->args ? "?" : "", r->args ? r->args : "", r->protocol); + /* "%% 500 /usr/local/apache/cgi-bin" */ + fprintf(f, "%%%% %d %s\n", ret, r->filename); + + fputs("%request\n", f); + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) + continue; + fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); + } + if ((r->method_number == M_POST || r->method_number == M_PUT) + && *dbuf) { + fprintf(f, "\n%s\n", dbuf); + } + + fputs("%response\n", f); + hdrs_arr = ap_table_elts(r->err_headers_out); + hdrs = (table_entry *) hdrs_arr->elts; + + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) + continue; + fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); + } + + if (sbuf && *sbuf) + fprintf(f, "%s\n", sbuf); + + if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) { + fputs("%stdout\n", f); + fputs(argsbuffer, f); + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) + fputs(argsbuffer, f); + fputs("\n", f); + } + + if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) { + fputs("%stderr\n", f); + fputs(argsbuffer, f); + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) + fputs(argsbuffer, f); + fputs("\n", f); + } + + ap_bclose(script_in); + ap_bclose(script_err); + + ap_pfclose(r->pool, f); + return ret; +} + +/**************************************************************** + * + * Actual CGI handling... + */ + + +struct cgi_child_stuff { +#ifdef TPF + TPF_FORK_CHILD t; +#endif + request_rec *r; + int nph; + int debug; + char *argv0; +}; + +static int cgi_child(void *child_stuff, child_info *pinfo) +{ + struct cgi_child_stuff *cld = (struct cgi_child_stuff *) child_stuff; + request_rec *r = cld->r; + char *argv0 = cld->argv0; + int child_pid; + +#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 + + char **env; + + RAISE_SIGSTOP(CGI_CHILD); +#ifdef DEBUG_CGI + fprintf(dbg, "Attempting to exec %s as %sCGI child (argv0 = %s)\n", + r->filename, cld->nph ? "NPH " : "", argv0); +#endif + + ap_add_cgi_vars(r); + env = ap_create_environment(r->pool, r->subprocess_env); + +#ifdef DEBUG_CGI + fprintf(dbg, "Environment: \n"); + for (i = 0; env[i]; ++i) + fprintf(dbg, "'%s'\n", env[i]); +#endif + +#ifndef WIN32 + ap_chdir_file(r->filename); +#endif + if (!cld->debug) + ap_error_log2stderr(r->server); + + /* Transumute outselves into the script. + * NB only ISINDEX scripts get decoded arguments. + */ + +#ifdef TPF + return (0); +#else + ap_cleanup_for_exec(); + + child_pid = ap_call_exec(r, pinfo, argv0, env, 0); +#if defined(WIN32) || defined(OS2) + return (child_pid); +#else + + /* Uh oh. Still here. Where's the kaboom? There was supposed to be an + * EARTH-shattering kaboom! + * + * Oh, well. Muddle through as best we can... + * + * Note that only stderr is available at this point, so don't pass in + * a server to aplog_error. + */ + + ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed", r->filename); + exit(0); + /* NOT REACHED */ + return (0); +#endif +#endif /* TPF */ +} + +static int cgi_handler(request_rec *r) +{ + int retval, nph, dbpos = 0; + char *argv0, *dbuf = NULL; + BUFF *script_out, *script_in, *script_err; + char argsbuffer[HUGE_STRING_LEN]; + int is_included = !strcmp(r->protocol, "INCLUDED"); + void *sconf = r->server->module_config; + cgi_server_conf *conf = + (cgi_server_conf *) ap_get_module_config(sconf, &cgi_module); + + struct cgi_child_stuff cld; + + if (r->method_number == M_OPTIONS) { + /* 99 out of 100 CGI scripts, this is all they support */ + r->allowed |= (1 << M_GET); + r->allowed |= (1 << M_POST); + return DECLINED; + } + + if ((argv0 = strrchr(r->filename, '/')) != NULL) + argv0++; + else + argv0 = r->filename; + + nph = !(strncmp(argv0, "nph-", 4)); + + if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r)) + return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO, + "Options ExecCGI is off in this directory"); + if (nph && is_included) + return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO, + "attempt to include NPH CGI script"); + +#if defined(OS2) || defined(WIN32) + /* Allow for cgi files without the .EXE extension on them under OS/2 */ + if (r->finfo.st_mode == 0) { + struct stat statbuf; + char *newfile; + + newfile = ap_pstrcat(r->pool, r->filename, ".EXE", NULL); + + if ((stat(newfile, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode))) { + return log_scripterror(r, conf, NOT_FOUND, 0, + "script not found or unable to stat"); + } else { + r->filename = newfile; + } + } +#else + if (r->finfo.st_mode == 0) + return log_scripterror(r, conf, NOT_FOUND, APLOG_NOERRNO, + "script not found or unable to stat"); +#endif + if (S_ISDIR(r->finfo.st_mode)) + return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO, + "attempt to invoke directory as script"); + if (!ap_suexec_enabled) { + if (!ap_can_exec(&r->finfo)) + return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO, + "file permissions deny server execution"); + } + + if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) + return retval; + + ap_add_common_vars(r); + cld.argv0 = argv0; + cld.r = r; + cld.nph = nph; + cld.debug = conf->logname ? 1 : 0; +#ifdef TPF + cld.t.filename = r->filename; + cld.t.subprocess_env = r->subprocess_env; + cld.t.prog_type = FORK_FILE; +#endif /* TPF */ + +#ifdef CHARSET_EBCDIC + /* XXX:@@@ Is the generated/included output ALWAYS in text/ebcdic format? */ + /* Or must we check the Content-Type first? */ + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1); +#endif /*CHARSET_EBCDIC*/ + + /* + * we spawn out of r->main if it's there so that we can avoid + * waiting for free_proc_chain to cleanup in the middle of an + * SSI request -djg + */ + if (!ap_bspawn_child(r->main ? r->main->pool : r->pool, cgi_child, + (void *) &cld, kill_after_timeout, + &script_out, &script_in, &script_err)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "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. + */ + + if (ap_should_client_block(r)) { + int dbsize, len_read; + + if (conf->logname) { + dbuf = ap_pcalloc(r->pool, conf->bufbytes + 1); + dbpos = 0; + } + + ap_hard_timeout("copy script args", r); + + while ((len_read = + ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) { + if (conf->logname) { + if ((dbpos + len_read) > conf->bufbytes) { + dbsize = conf->bufbytes - dbpos; + } + else { + dbsize = len_read; + } + memcpy(dbuf + dbpos, argsbuffer, dbsize); + dbpos += dbsize; + } + ap_reset_timeout(r); + if (ap_bwrite(script_out, argsbuffer, len_read) < len_read) { + /* silly script stopped reading, soak up remaining message */ + while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) { + /* dump it */ + } + break; + } + } + + ap_bflush(script_out); + + ap_kill_timeout(r); + } + + ap_bclose(script_out); + + /* Handle script return... */ + if (script_in && !nph) { + const char *location; + char sbuf[MAX_STRING_LEN]; + int ret; + + if ((ret = ap_scan_script_header_err_buff(r, script_in, sbuf))) { + return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err); + } + +#ifdef CHARSET_EBCDIC + /* Now check the Content-Type to decide if conversion is needed */ + ap_checkconv(r); +#endif /*CHARSET_EBCDIC*/ + + location = ap_table_get(r->headers_out, "Location"); + + if (location && location[0] == '/' && r->status == 200) { + + /* Soak up all the script output */ + ap_hard_timeout("read from script", r); + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) { + continue; + } + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) { + continue; + } + ap_kill_timeout(r); + + + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = ap_pstrdup(r->pool, "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. + */ + ap_table_unset(r->headers_in, "Content-Length"); + + ap_internal_redirect_handler(location, r); + return OK; + } + else if (location && r->status == 200) { + /* XX Note that if a script wants to produce its own Redirect + * body, it now has to explicitly *say* "Status: 302" + */ + return REDIRECT; + } + + ap_send_http_header(r); + if (!r->header_only) { + ap_send_fb(script_in, r); + } + ap_bclose(script_in); + + ap_soft_timeout("soaking script stderr", r); + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) { + continue; + } + ap_kill_timeout(r); + ap_bclose(script_err); + } + + if (script_in && nph) { + ap_send_fb(script_in, r); + } + + return OK; /* NOT r->status, even if it has changed. */ +} + +static const handler_rec cgi_handlers[] = +{ + {CGI_MAGIC_TYPE, cgi_handler}, + {"cgi-script", cgi_handler}, + {NULL} +}; + +module MODULE_VAR_EXPORT cgi_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + 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 table */ + cgi_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/generators/mod_cgid.c b/modules/generators/mod_cgid.c new file mode 100644 index 00000000000..ae90d763d47 --- /dev/null +++ b/modules/generators/mod_cgid.c @@ -0,0 +1,1006 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +/* + * http_script: keeps all script-related ramblings together. + * + * Compliant to cgi/1.1 spec + * + * Adapted by rst from original NCSA code by Rob McCool + * + * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for + * custom error responses, and DOCUMENT_ROOT because we found it useful. + * It also adds SERVER_ADMIN - useful for scripts to know who to mail when + * they fail. + */ + + + +#define CORE_PRIVATE + +#include "apr_lib.h" +#include "apr_general.h" +#include "apr_file_io.h" +#include "apr_portable.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 "http_conf_globals.h" +#include "buff.h" +#include "ap_mpm.h" +#include "iol_socket.h" + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +#ifndef sockaddr_un +struct sockaddr_un { + unsigned short sun_family; + char sun_path[UNIX_PATH_MAX]; +}; +#endif + + +module MODULE_VAR_EXPORT cgid_module; + +static void cgid_init(ap_context_t *p, ap_context_t *plog, ap_context_t *ptemp, server_rec *main_server); +static int once_through = 0; + +static ap_context_t *pcgi; + +/* KLUDGE --- for back-combatibility, we don't have to check Execcgid + * 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 = ap_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 "logs/cgisock" + +typedef struct { + const char *sockname; + char *logname; + long logbytes; + int bufbytes; + BUFF *bin; + BUFF *bout; + BUFF *berror; +} cgid_server_conf; + +/* If a request includes query info in the URL (stuff after "?"), and + * the query info does not contain "=" (indicative of a FORM submission), + * then 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. + * + * XXXX: note that the WIN32 code uses one of the suexec strings + * to pass an interpreter name. Remember this if changing the way they + * are handled in create_argv. + * + */ +static char **create_argv(ap_context_t *p, char *path, char *user, char *group, + char *av0, const char *args) +{ + int x, numwords; + char **av; + char *w; + int idx = 0; + + /* 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 **) ap_palloc(p, (numwords + 5) * sizeof(char *)); + + if (path) { + av[idx++] = path; + } + if (user) { + av[idx++] = user; + } + if (group) { + av[idx++] = group; + } + + av[idx++] = 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; +} + +static int call_exec(request_rec *r, char *argv0, char **env, int shellcmd) +{ + int pid = 0; + int errfileno = STDERR_FILENO; + + /* the fd on r->server->error_log is closed, but we need somewhere to * put the error messages from the log_* functions. So, we use stderr, + * since that is better than allowing errors to go unnoticed. + */ + ap_put_os_file(&r->server->error_log, &errfileno, r->pool); + /* TODO: reimplement suexec */ +#if 0 + if (ap_suexec_enabled + && ((r->server->server_uid != ap_user_id) + || (r->server->server_gid != ap_group_id) + || (!strncmp("/~", r->uri, 2)))) { + + char *execuser, *grpname; + struct passwd *pw; + struct group *gr; + + if (!strncmp("/~", r->uri, 2)) { + gid_t user_gid; + char *username = ap_pstrdup(r->pool, r->uri + 2); + char *pos = strchr(username, '/'); + + if (pos) { + *pos = '\0'; + } + + if ((pw = getpwnam(username)) == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "getpwnam: invalid username %s", username); + return (pid); + } + execuser = ap_pstrcat(r->pool, "~", pw->pw_name, NULL); + user_gid = pw->pw_gid; + + if ((gr = getgrgid(user_gid)) == NULL) { + if ((grpname = ap_palloc(r->pool, 16)) == NULL) { + return (pid); + } + else { + ap_snprintf(grpname, 16, "%ld", (long) user_gid); + } + } + else { + grpname = gr->gr_name; + } + } + else { + if ((pw = getpwuid(r->server->server_uid)) == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "getpwuid: invalid userid %ld", + (long) r->server->server_uid); + return (pid); + } + execuser = ap_pstrdup(r->pool, pw->pw_name); + + if ((gr = getgrgid(r->server->server_gid)) == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "getgrgid: invalid groupid %ld", + (long) r->server->server_gid); + return (pid); + } + grpname = gr->gr_name; + } + + if (shellcmd) { + execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, + NULL, env); + } + + else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { + execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, + NULL, env); + } + + else { + execve(SUEXEC_BIN, + create_argv(r->pool, SUEXEC_BIN, execuser, grpname, + argv0, r->args), + env); + } + } + else { +#endif + if (shellcmd) { + execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env); + } + + else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { + execle(r->filename, argv0, NULL, env); + } + + else { + execve(r->filename, + create_argv(r->pool, NULL, NULL, NULL, argv0, r->args), + env); + } +#if 0 + } +#endif + return (pid); +} + +static void cgid_maint(int reason, void *data, ap_wait_t status) +{ +#ifdef HAS_OTHER_CHILD + int *sd = data; + switch (reason) { + case OC_REASON_DEATH: + case OC_REASON_LOST: + /* stop gap to make sure everything else works. In the end, + * we'll just restart the cgid server. */ + kill(getppid(), SIGWINCH); + break; + case OC_REASON_RESTART: + case OC_REASON_UNREGISTER: + kill(*sd, SIGHUP); + break; + } +#endif +} + +static void get_req(int fd, request_rec *r, char **filename, char **argv0, char ***env) +{ + int i, len, j; + unsigned char *data; + char **environ; + char temp[MAX_STRING_LEN]; + core_dir_config *temp_core; + void **dconf; + + r->server = ap_pcalloc(r->pool, sizeof(server_rec)); + + read(fd, &j, sizeof(int)); + read(fd, &len, sizeof(int)); + data = ap_pcalloc(r->pool, len); + i = read(fd, data, len); + + r->filename = ap_getword(r->pool, (const char **)&data, '\n'); + *argv0 = ap_getword(r->pool, (const char **)&data, '\n'); + + r->uri = ap_getword(r->pool, (const char **)&data, '\n'); + + environ = ap_pcalloc(r->pool, (j + 2) *sizeof(char *)); + i = 0; + for (i = 0; i < j; i++) { + environ[i] = ap_getword(r->pool, (const char **)&data, '\n'); + } + *env = environ; + r->args = ap_getword(r->pool, (const char **)&data, '\n'); + + read(fd, &r->server->server_uid, sizeof(uid_t)); + read(fd, &r->server->server_gid, sizeof(gid_t)); + + read(fd, &i, sizeof(int)); + + /* add 1, so that if i == 0, we still malloc something. */ + dconf = (void **)malloc(sizeof(void *) * i + 1); + + temp_core = (core_dir_config *)malloc(sizeof(core_module)); +#if 0 +#ifdef RLIMIT_CPU + read(fd, &j, sizeof(int)); + if (j) { + temp_core->limit_cpu = (struct rlimit *)malloc (sizeof(struct rlimit)); + read(fd, temp_core->limit_cpu, sizeof(struct rlimit)); + } + else { + temp_core->limit_cpu = NULL; + } +#endif + +#if defined (RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) + read(fd, &j, sizeof(int)); + if (j) { + temp_core->limit_mem = (struct rlimit *)malloc (sizeof(struct rlimit)); + read(fd, temp_core->limit_mem, sizeof(struct rlimit)); + } + else { + temp_core->limit_mem = NULL; + } +#endif + +#ifdef RLIMIT_NPROC + read(fd, &j, sizeof(int)); + if (j) { + temp_core->limit_nproc = (struct rlimit *)malloc (sizeof(struct rlimit)); + read(fd, temp_core->limit_nproc, sizeof(struct rlimit)); + } + else { + temp_core->limit_nproc = NULL; + } +#endif +#endif + dconf[i] = (void *)temp_core; + r->per_dir_config = dconf; +} + + + +static void send_req(int fd, request_rec *r, char *argv0, char **env) +{ + int len; + int rv; + int i = 0; + char *data; + core_dir_config *conf = ap_get_module_config(r->per_dir_config, + &core_module); + + data = ap_pstrcat(r->pool, r->filename, "\n", argv0, "\n", r->uri, "\n", + NULL); + + for (i =0; env[i]; i++) { + continue; + } + + if (write(fd, &i, sizeof(int)) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, + "write to cgi daemon process"); + } + + for (i = 0; env[i]; i++) { + data = ap_pstrcat(r->pool, data, env[i], "\n", NULL); + } + data = ap_pstrcat(r->pool, data, r->args, NULL); + len = strlen(data); + if (write(fd, &len, sizeof(int)) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, + "write to cgi daemon process"); + } + if (write(fd, data, len) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, + "write to cgi daemon process"); + } + if (write(fd, &r->server->server_uid, sizeof(uid_t)) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, + "write to cgi daemon process"); + } + if (write(fd, &r->server->server_gid, sizeof(gid_t)) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, + "write to cgi daemon process"); + } + if (write(fd, &core_module.module_index, sizeof(int)) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, + "write to cgi daemon process"); + } +#if 0 +#ifdef RLIMIT_CPU + if (conf->limit_cpu) { + len = 1; + write(fd, &len, sizeof(int)); + write(fd, conf->limit_cpu, sizeof(struct rlimit)); + } + else { + len = 0; + write(fd, &len, sizeof(int)); + } +#endif + +#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) + if (conf->limit_mem) { + len = 1; + write(fd, &len, sizeof(int)); + write(fd, conf->limit_mem, sizeof(struct rlimit)); + } + else { + len = 0; + write(fd, &len, sizeof(int)); + } +#endif + +#ifdef RLIMIT_NPROC + if (conf->limit_nproc) { + len = 1; + write(fd, &len, sizeof(int)); + write(fd, conf->limit_nproc, sizeof(struct rlimit)); + } + else { + len = 0; + write(fd, &len, sizeof(int)); + } +#endif +#endif +} + +static int cgid_server_child(int sd) +{ + char *argv0; + char *filename; + char **env; + ap_context_t *p; + request_rec *r; + + ap_create_context(&p, pcgi); + r = ap_pcalloc(p, sizeof(request_rec)); + r->pool = p; + dup2(sd, STDIN_FILENO); + dup2(sd, STDOUT_FILENO); + get_req(sd, r, &filename, &argv0, &env); + call_exec(r, argv0, env, 0); + exit(-1); /* We should NEVER get here */ +} + +static int cgid_server(void *data) +{ + struct sockaddr_un unix_addr; + int pid; + int sd, sd2, len; + int errfile; + server_rec *main_server = data; + cgid_server_conf *sconf = (cgid_server_conf *)ap_get_module_config( + main_server->module_config, &cgid_module); + + signal(SIGCHLD, SIG_IGN); + unlink(sconf->sockname); + + if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, + "Couldn't create unix domain socket"); + } + + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + strcpy(unix_addr.sun_path, sconf->sockname); + + if (bind(sd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, + "Couldn't bind unix domain socket"); + } + /* Most implementations silently enforce a value of 5 anyway. + * This way, it'll work the same everywhere. */ + if (listen(sd, 5) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, + "Couldn't listen on unix domain socket"); + } + + while (1) { + sd2 = accept(sd, (struct sockaddr *)&unix_addr, &len); + if (sd2 < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, (server_rec *)data, + "Error accepting on cgid socket."); + continue; + } + + if ((pid = fork()) > 0) { + close(sd2); + } + else if (pid == 0) { + /* setup the STDERR here, because I have all the info + * for it. I'll do the STDIN and STDOUT later, but I can't + * do STDERR as easily. + */ + if (sconf->logname) { + dup2(open(sconf->logname, O_WRONLY), STDERR_FILENO); + } + else { + ap_get_os_file(&errfile, main_server->error_log); + dup2(errfile, STDERR_FILENO); + } + cgid_server_child(sd2); + } + else { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, (server_rec *)data, + "Couldn't fork cgi script"); + } + } + return -1; +} + +static void cgid_init(ap_context_t *p, ap_context_t *plog, ap_context_t *ptemp, server_rec *main_server) +{ + int pid; + int tempfd; + + cgid_server_conf *sconf = (cgid_server_conf *)ap_get_module_config( + main_server->module_config, &cgid_module); + + if (once_through > 0) { + ap_create_context(&pcgi, p); + tempfd = creat(sconf->sockname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + close(tempfd); + + if ((pid = fork()) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, + "Couldn't spawn cgid daemon process"); + } + else if (pid == 0) { + cgid_server(main_server); + exit(-1); + } +#ifdef HAS_OTHER_CHILD + ap_register_other_child(pid, cgid_maint, &pid, -1); +#endif + } + else once_through++; +} + +static void *create_cgid_config(ap_context_t *p, server_rec *s) +{ + cgid_server_conf *c = + (cgid_server_conf *) ap_pcalloc(p, sizeof(cgid_server_conf)); + + c->logname = NULL; + c->logbytes = DEFAULT_LOGBYTES; + c->bufbytes = DEFAULT_BUFBYTES; + c->sockname = ap_server_root_relative(p, DEFAULT_SOCKET); + c->bin = c->bout = c->berror = NULL; + return c; +} + +static void *merge_cgid_config(ap_context_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 const char *set_scriptlog(cmd_parms *cmd, void *dummy, char *arg) +{ + server_rec *s = cmd->server; + cgid_server_conf *conf = + (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module); + + conf->logname = arg; + return NULL; +} + +static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, char *arg) +{ + server_rec *s = cmd->server; + cgid_server_conf *conf = + (cgid_server_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, char *arg) +{ + server_rec *s = cmd->server; + cgid_server_conf *conf = + (cgid_server_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, char *arg) +{ + server_rec *s = cmd->server; + cgid_server_conf *conf = + (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module); + + conf->sockname = ap_server_root_relative(cmd->pool, arg); + return NULL; +} + +static const command_rec cgid_cmds[] = +{ + {"ScriptLog", set_scriptlog, NULL, RSRC_CONF, TAKE1, + "the name of a log for script debugging info"}, + {"ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, TAKE1, + "the maximum length (in bytes) of the script debug log"}, + {"ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, TAKE1, + "the maximum size (in bytes) to record of a POST request"}, + {"Scriptsock", set_script_socket, NULL, RSRC_CONF, TAKE1, + "the name of the socket to use for communication with the cgi daemon."}, + {NULL} +}; + +static int log_scripterror(request_rec *r, cgid_server_conf * conf, int ret, + int show_errno, char *error) +{ + ap_file_t *f; + struct stat finfo; + + ap_log_rerror(APLOG_MARK, show_errno|APLOG_ERR, errno, r, + "%s: %s", error, r->filename); + + if (!conf->logname || + ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0) + && (finfo.st_size > conf->logbytes)) || + (ap_open(&f, ap_server_root_relative(r->pool, conf->logname), + APR_APPEND, APR_OS_DEFAULT, r->pool) != APR_SUCCESS)) { + return ret; + } + + /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */ + ap_fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri, + r->args ? "?" : "", r->args ? r->args : "", r->protocol); + /* "%% 500 /usr/local/apache/cgid-bin */ + ap_fprintf(f, "%%%% %d %s\n", ret, r->filename); + + ap_fprintf(f, "%%error\n%s\n", error); + + ap_close(f); + return ret; +} + +static int log_script(request_rec *r, cgid_server_conf * conf, int ret, + char *dbuf, const char *sbuf, BUFF *script_in, BUFF *script_err) +{ + ap_array_header_t *hdrs_arr = ap_table_elts(r->headers_in); + ap_table_entry_t *hdrs = (ap_table_entry_t *) hdrs_arr->elts; + char argsbuffer[HUGE_STRING_LEN]; + ap_file_t *f; + int i; + struct stat finfo; + + if (!conf->logname || + ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0) + && (finfo.st_size > conf->logbytes)) || + (ap_open(&f, ap_server_root_relative(r->pool, conf->logname), + APR_APPEND, APR_OS_DEFAULT, r->pool) != APR_SUCCESS)) { + /* Soak up script output */ + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) + continue; + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) + continue; + return ret; + } + + /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */ + ap_fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri, + r->args ? "?" : "", r->args ? r->args : "", r->protocol); + /* "%% 500 /usr/local/apache/cgid-bin" */ + ap_fprintf(f, "%%%% %d %s\n", ret, r->filename); + + ap_puts("%request\n", f); + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) + continue; + ap_fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); + } + if ((r->method_number == M_POST || r->method_number == M_PUT) + && *dbuf) { + ap_fprintf(f, "\n%s\n", dbuf); + } + + ap_puts("%response\n", f); + hdrs_arr = ap_table_elts(r->err_headers_out); + hdrs = (ap_table_entry_t *) hdrs_arr->elts; + + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) + continue; + ap_fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); + } + + if (sbuf && *sbuf) + ap_fprintf(f, "%s\n", sbuf); + + if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) { + ap_puts("%stdout\n", f); + ap_puts(argsbuffer, f); + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) + ap_puts(argsbuffer, f); + ap_puts("\n", f); + } + + if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) { + ap_puts("%stderr\n", f); + ap_puts(argsbuffer, f); + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) + ap_puts(argsbuffer, f); + ap_puts("\n", f); + } + + ap_bclose(script_in); + ap_bclose(script_err); + + ap_close(f); + return ret; +} + + + +/**************************************************************** + * + * Actual cgid handling... + */ +static int cgid_handler(request_rec *r) +{ + int retval, nph, dbpos = 0; + char *argv0, *dbuf = NULL; + BUFF *script = NULL; + char argsbuffer[HUGE_STRING_LEN]; + void *sconf = r->server->module_config; + cgid_server_conf *conf = (cgid_server_conf *) ap_get_module_config(sconf, &cgid_module); + int is_included = !strcmp(r->protocol, "INCLUDED"); + int sd; + char **env; + struct sockaddr_un unix_addr; + ap_socket_t *tempsock = NULL; + int nbytes; + ap_iol *iol; + script = ap_bcreate(r->pool, B_RDWR); + + if (r->method_number == M_OPTIONS) { + /* 99 out of 100 cgid scripts, this is all they support */ + r->allowed |= (1 << M_GET); + r->allowed |= (1 << M_POST); + return DECLINED; + } + + if ((argv0 = strrchr(r->filename, '/')) != NULL) + argv0++; + else + argv0 = r->filename; + + nph = !(strncmp(argv0, "nph-", 4)); + + if ((argv0 = strrchr(r->filename, '/')) != NULL) + argv0++; + else + argv0 = r->filename; + + if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r)) + return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO, + "Options ExecCGI is off in this directory"); + if (nph && is_included) + return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO, + "attempt to include NPH CGI script"); + +#if defined(OS2) || defined(WIN32) + /* Allow for cgid files without the .EXE extension on them under OS/2 */ + if (r->finfo.st_mode == 0) { + struct stat statbuf; + char *newfile; + + newfile = ap_pstrcat(r->pool, r->filename, ".EXE", NULL); + + if ((stat(newfile, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode))) { + return log_scripterror(r, conf, NOT_FOUND, 0, + "script not found or unable to stat"); + } else { + r->filename = newfile; + } + } +#else + if (r->finfo.st_mode == 0) + return log_scripterror(r, conf, NOT_FOUND, APLOG_NOERRNO, + "script not found or unable to stat"); +#endif + if (S_ISDIR(r->finfo.st_mode)) + return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO, + "attempt to invoke directory as script"); +/* + if (!ap_suexec_enabled) { + if (!ap_can_exec(&r->finfo)) + return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO, + "file permissions deny server execution"); + } +*/ + ap_add_common_vars(r); + ap_add_cgi_vars(r); + env = ap_create_environment(r->pool, r->subprocess_env); + + if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + return log_scripterror(r, conf, NOT_FOUND, 0, + "unable to create socket to cgi daemon"); + } + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + strcpy(unix_addr.sun_path, conf->sockname); + + if (connect(sd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0) { + return log_scripterror(r, conf, NOT_FOUND, 0, + "unable to connect to cgi daemon"); + } + + send_req(sd, r, argv0, env); + + ap_put_os_sock(&tempsock, &sd, pcgi); + + iol = unix_attach_socket(tempsock); + + ap_bpush_iol(script, iol); + + if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) + return retval; + + if ((argv0 = strrchr(r->filename, '/')) != NULL) + argv0++; + else + argv0 = r->filename; + + /* Transfer any put/post args, CERN style... + * Note that we already ignore SIGPIPE in the core server. + */ + + if (ap_should_client_block(r)) { + int dbsize, len_read; + + if (conf->logname) { + dbuf = ap_pcalloc(r->pool, conf->bufbytes + 1); + dbpos = 0; + } + + + + while ((len_read = + ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) { + if (conf->logname) { + if ((dbpos + len_read) > conf->bufbytes) { + dbsize = conf->bufbytes - dbpos; + } + else { + dbsize = len_read; + } + memcpy(dbuf + dbpos, argsbuffer, dbsize); + dbpos += dbsize; + } + ap_bwrite(script, argsbuffer, len_read, &nbytes); + if (nbytes < len_read) { + /* silly script stopped reading, soak up remaining message */ + while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) { + /* dump it */ + } + break; + } + } + + ap_bflush(script); + + } + + /* Handle script return... */ + if (script && !nph) { + const char *location; + char sbuf[MAX_STRING_LEN]; + int ret; + + if ((ret = ap_scan_script_header_err_buff(r, script, sbuf))) { + return log_script(r, conf, ret, dbuf, sbuf, script, NULL); + } + +#ifdef CHARSET_EBCDIC + /* Now check the Content-Type to decide if conversion is needed */ + ap_checkconv(r); +#endif /*CHARSET_EBCDIC*/ + + location = ap_table_get(r->headers_out, "Location"); + + if (location && location[0] == '/' && r->status == 200) { + + /* Soak up all the script output */ + while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script) > 0) { + continue; + } + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = ap_pstrdup(r->pool, "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. + */ + ap_table_unset(r->headers_in, "Content-Length"); + + ap_internal_redirect_handler(location, r); + return OK; + } + else if (location && r->status == 200) { + /* XX Note that if a script wants to produce its own Redirect + * body, it now has to explicitly *say* "Status: 302" + */ + return REDIRECT; + } + + ap_send_http_header(r); + if (!r->header_only) { + ap_send_fb(script, r); + } + ap_bclose(script); + } + + if (script && nph) { + ap_send_fb(script, r); + } + +ap_destroy_pool(pcgi); + + return OK; /* NOT r->status, even if it has changed. */ +} + +static const handler_rec cgid_handlers[] = +{ + {CGI_MAGIC_TYPE, cgid_handler}, + {"cgi-script", cgid_handler}, + {NULL} +}; + +static void register_hook(void) +{ + ap_hook_post_config(cgid_init, NULL, NULL, HOOK_MIDDLE); +} + +module MODULE_VAR_EXPORT cgid_module = { + STANDARD20_MODULE_STUFF, + NULL, /* 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 */ + cgid_handlers, /* handlers */ + register_hook /* register_handlers */ +}; + diff --git a/modules/generators/mod_info.c b/modules/generators/mod_info.c new file mode 100644 index 00000000000..27995855780 --- /dev/null +++ b/modules/generators/mod_info.c @@ -0,0 +1,695 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +/* + * 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 + * + * Rasmus Lerdorf <rasmus@vex.net>, May 1996 + * + * 05.01.96 Initial Version + * + * Lou Langholtz <ldl@usi.utah.edu>, July 1997 + * + * 07.11.97 Addition of the AddModuleInfo directive + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_main.h" +#include "http_protocol.h" +#include "util_script.h" +#include "http_conf_globals.h" + +typedef struct { + char *name; /* matching module name */ + char *info; /* additional info */ +} info_entry; + +typedef struct { + array_header *more_info; +} info_svr_conf; + +typedef struct info_cfg_lines { + char *cmd; + char *line; + struct info_cfg_lines *next; +} info_cfg_lines; + +module MODULE_VAR_EXPORT info_module; +extern module *top_module; + +static void *create_info_config(pool *p, server_rec *s) +{ + info_svr_conf *conf = (info_svr_conf *) ap_pcalloc(p, sizeof(info_svr_conf)); + + conf->more_info = ap_make_array(p, 20, sizeof(info_entry)); + return conf; +} + +static void *merge_info_config(pool *p, void *basev, void *overridesv) +{ + info_svr_conf *new = (info_svr_conf *) ap_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 = ap_append_arrays(p, overrides->more_info, base->more_info); + return new; +} + +static char *mod_info_html_cmd_string(const char *string, char *buf, size_t buf_len) +{ + const char *s; + char *t; + char *end_buf; + + s = string; + t = buf; + /* keep space for \0 byte */ + end_buf = buf + buf_len - 1; + while ((*s) && (t < end_buf)) { + if (*s == '<') { + strncpy(t, "<", end_buf - t); + t += 4; + } + else if (*s == '>') { + strncpy(t, ">", end_buf - t); + t += 4; + } + else if (*s == '&') { + strncpy(t, "&", end_buf - t); + t += 5; + } + else { + *t++ = *s; + } + s++; + } + /* oops, overflowed... don't overwrite */ + if (t > end_buf) { + *end_buf = '\0'; + } + else { + *t = '\0'; + } + return (buf); +} + +static info_cfg_lines *mod_info_load_config(pool *p, const char *filename, + request_rec *r) +{ + char s[MAX_STRING_LEN]; + configfile_t *fp; + info_cfg_lines *new, *ret, *prev; + const char *t; + + fp = ap_pcfg_openfile(p, filename); + if (!fp) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, + "mod_info: couldn't open config file %s", + filename); + return NULL; + } + ret = NULL; + prev = NULL; + while (!ap_cfg_getline(s, MAX_STRING_LEN, fp)) { + if (*s == '#') { + continue; /* skip comments */ + } + new = ap_palloc(p, sizeof(struct info_cfg_lines)); + new->next = NULL; + if (!ret) { + ret = new; + } + if (prev) { + prev->next = new; + } + t = s; + new->cmd = ap_getword_conf(p, &t); + if (*t) { + new->line = ap_pstrdup(p, t); + } + else { + new->line = NULL; + } + prev = new; + } + ap_cfg_closefile(fp); + return (ret); +} + +static void mod_info_module_cmds(request_rec *r, info_cfg_lines *cfg, + const command_rec *cmds, char *label) +{ + const command_rec *cmd = cmds; + info_cfg_lines *li = cfg, *li_st = NULL, *li_se = NULL; + info_cfg_lines *block_start = NULL; + int lab = 0, nest = 0; + char buf[MAX_STRING_LEN]; + + while (li) { + if (!strncasecmp(li->cmd, "<directory", 10) || + !strncasecmp(li->cmd, "<location", 9) || + !strncasecmp(li->cmd, "<limit", 6) || + !strncasecmp(li->cmd, "<files", 6)) { + if (nest) { + li_se = li; + } + else { + li_st = li; + } + li = li->next; + nest++; + continue; + } + else if (nest && (!strncasecmp(li->cmd, "</limit", 7) || + !strncasecmp(li->cmd, "</location", 10) || + !strncasecmp(li->cmd, "</directory", 11) || + !strncasecmp(li->cmd, "</files", 7))) { + if (block_start) { + if ((nest == 1 && block_start == li_st) || + (nest == 2 && block_start == li_se)) { + ap_rputs("<dd><tt>", r); + if (nest == 2) { + ap_rputs("  ", r); + } + ap_rputs(mod_info_html_cmd_string(li->cmd, buf, sizeof(buf)), r); + ap_rputs(" ", r); + if (li->line) { + ap_rputs(mod_info_html_cmd_string(li->line, buf, sizeof(buf)), r); + } + ap_rputs("</tt>\n", r); + nest--; + if (!nest) { + block_start = NULL; + li_st = NULL; + } + else { + block_start = li_st; + } + li_se = NULL; + } + else { + nest--; + if (!nest) { + li_st = NULL; + } + li_se = NULL; + } + } + else { + nest--; + if (!nest) { + li_st = NULL; + } + li_se = NULL; + } + li = li->next; + continue; + } + cmd = cmds; + while (cmd) { + if (cmd->name) { + if (!strcasecmp(cmd->name, li->cmd)) { + if (!lab) { + ap_rputs("<dt><strong>", r); + ap_rputs(label, r); + ap_rputs("</strong>\n", r); + lab = 1; + } + if (((nest && block_start == NULL) || + (nest == 2 && block_start == li_st)) && + (strncasecmp(li->cmd, "<directory", 10) && + strncasecmp(li->cmd, "<location", 9) && + strncasecmp(li->cmd, "<limit", 6) && + strncasecmp(li->cmd, "</limit", 7) && + strncasecmp(li->cmd, "</location", 10) && + strncasecmp(li->cmd, "</directory", 11) && + strncasecmp(li->cmd, "</files", 7))) { + ap_rputs("<dd><tt>", r); + ap_rputs(mod_info_html_cmd_string(li_st->cmd, buf, sizeof(buf)), r); + ap_rputs(" ", r); + if (li_st->line) { + ap_rputs(mod_info_html_cmd_string(li_st->line, buf, sizeof(buf)), r); + } + ap_rputs("</tt>\n", r); + block_start = li_st; + if (li_se) { + ap_rputs("<dd><tt>  ", r); + ap_rputs(mod_info_html_cmd_string(li_se->cmd, buf, sizeof(buf)), r); + ap_rputs(" ", r); + if (li_se->line) { + ap_rputs(mod_info_html_cmd_string(li_se->line, buf, sizeof(buf)), r); + } + ap_rputs("</tt>\n", r); + block_start = li_se; + } + } + ap_rputs("<dd><tt>", r); + if (nest) { + ap_rputs("  ", r); + } + if (nest == 2) { + ap_rputs("  ", r); + } + ap_rputs(mod_info_html_cmd_string(li->cmd, buf, sizeof(buf)), r); + if (li->line) { + ap_rputs(" <i>", r); + ap_rputs(mod_info_html_cmd_string(li->line, buf, sizeof(buf)), r); + ap_rputs("</i>", r); + } + ap_rputs("</tt>", r); + } + } + else + break; + cmd++; + } + li = li->next; + } +} + +static 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 display_info(request_rec *r) +{ + module *modp = NULL; + char buf[MAX_STRING_LEN], *cfname; + char *more_info; + const command_rec *cmd = NULL; + const handler_rec *hand = NULL; + server_rec *serv = r->server; + int comma = 0; + info_cfg_lines *mod_info_cfg_httpd = NULL; + info_cfg_lines *mod_info_cfg_srm = NULL; + info_cfg_lines *mod_info_cfg_access = NULL; + + r->allowed |= (1 << M_GET); + if (r->method_number != M_GET) + return DECLINED; + + r->content_type = "text/html"; + ap_send_http_header(r); + if (r->header_only) { + return 0; + } + ap_hard_timeout("send server info", r); + + ap_rputs(DOCTYPE_HTML_3_2 + "<html><head><title>Server Information\n", r); + ap_rputs("

      Apache Server Information

      \n", r); + if (!r->args || strcasecmp(r->args, "list")) { + cfname = ap_server_root_relative(r->pool, ap_server_confname); + mod_info_cfg_httpd = mod_info_load_config(r->pool, cfname, r); + cfname = ap_server_root_relative(r->pool, serv->srm_confname); + mod_info_cfg_srm = mod_info_load_config(r->pool, cfname, r); + cfname = ap_server_root_relative(r->pool, serv->access_confname); + mod_info_cfg_access = mod_info_load_config(r->pool, cfname, r); + if (!r->args) { + ap_rputs("Server Settings, ", r); + for (modp = top_module; modp; modp = modp->next) { + ap_rprintf(r, "%s", modp->name, modp->name); + if (modp->next) { + ap_rputs(", ", r); + } + } + ap_rputs("
      ", r); + + } + if (!r->args || !strcasecmp(r->args, "server")) { + ap_rprintf(r, "Server Version: " + "%s
      \n", + ap_get_server_version()); + ap_rprintf(r, "Server Built: " + "%s
      \n", + ap_get_server_built()); + ap_rprintf(r, "API Version: " + "%d:%d
      \n", + MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR); + ap_rprintf(r, "Run Mode: %s
      \n", + (ap_standalone ? "standalone" : "inetd")); + ap_rprintf(r, "User/Group: " + "%s(%d)/%d
      \n", + ap_user_name, (int) ap_user_id, (int) ap_group_id); + ap_rprintf(r, "Hostname/port: " + "%s:%u
      \n", + serv->server_hostname, serv->port); + ap_rprintf(r, "Daemons: " + "start: %d    " + "min idle: %d    " + "max idle: %d    " + "max: %d
      \n", + ap_daemons_to_start, ap_daemons_min_free, + ap_daemons_max_free, ap_daemons_limit); + ap_rprintf(r, "Max Requests: " + "per child: %d    " + "keep alive: %s    " + "max per connection: %d
      \n", + ap_max_requests_per_child, + (serv->keep_alive ? "on" : "off"), + serv->keep_alive_max); + ap_rprintf(r, "Threads: " + "per child: %d   
      \n", + ap_threads_per_child); + ap_rprintf(r, "Excess requests: " + "per child: %d   
      \n", + ap_excess_requests_per_child); + ap_rprintf(r, "Timeouts: " + "connection: %d    " + "keep-alive: %d
      ", + serv->timeout, serv->keep_alive_timeout); + ap_rprintf(r, "Server Root: " + "%s
      \n", ap_server_root); + ap_rprintf(r, "Config File: " + "%s
      \n", ap_server_confname); + ap_rprintf(r, "PID File: " + "%s
      \n", ap_pid_fname); + ap_rprintf(r, "Scoreboard File: " + "%s
      \n", ap_scoreboard_fname); + } + ap_rputs("
      ", r); + for (modp = top_module; modp; modp = modp->next) { + if (!r->args || !strcasecmp(modp->name, r->args)) { + ap_rprintf(r, "
      Module Name: " + "%s\n", + modp->name, modp->name); + ap_rputs("
      Content handlers:", r); + hand = modp->handlers; + if (hand) { + while (hand) { + if (hand->content_type) { + ap_rprintf(r, " %s\n", hand->content_type); + } + else { + break; + } + hand++; + if (hand && hand->content_type) { + ap_rputs(",", r); + } + } + } + else { + ap_rputs(" none", r); + } + ap_rputs("
      Configuration Phase Participation: \n", + r); + if (modp->child_init) { + ap_rputs("Child Init", r); + comma = 1; + } + 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 (modp->child_exit) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Child Exit", r); + comma = 1; + } + if (!comma) + ap_rputs(" none", r); + comma = 0; + ap_rputs("
      Request Phase Participation: \n", + r); + if (modp->post_read_request) { + ap_rputs("Post-Read Request", r); + comma = 1; + } + if (modp->header_parser) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Header Parse", r); + comma = 1; + } + if (modp->translate_handler) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Translate Path", r); + comma = 1; + } + if (modp->access_checker) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Check Access", r); + comma = 1; + } + if (modp->ap_check_user_id) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Verify User ID", r); + comma = 1; + } + if (modp->auth_checker) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Verify User Access", r); + comma = 1; + } + if (modp->type_checker) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Check Type", r); + comma = 1; + } + if (modp->fixer_upper) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Fixups", r); + comma = 1; + } + if (modp->logger) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Logging", r); + comma = 1; + } + if (!comma) + ap_rputs(" none", r); + comma = 0; + ap_rputs("
      Module Directives: ", r); + cmd = modp->cmds; + if (cmd) { + while (cmd) { + if (cmd->name) { + ap_rprintf(r, "
      %s - ", + mod_info_html_cmd_string(cmd->name, + buf, sizeof(buf))); + if (cmd->errmsg) { + ap_rputs(cmd->errmsg, r); + } + ap_rputs("\n", r); + } + else { + break; + } + cmd++; + } + ap_rputs("
      Current Configuration:\n", r); + mod_info_module_cmds(r, mod_info_cfg_httpd, modp->cmds, + "httpd.conf"); + mod_info_module_cmds(r, mod_info_cfg_srm, modp->cmds, + "srm.conf"); + mod_info_module_cmds(r, mod_info_cfg_access, modp->cmds, + "access.conf"); + } + else { + ap_rputs(" none\n", r); + } + more_info = find_more_info(serv, modp->name); + if (more_info) { + ap_rputs("
      Additional Information:\n
      ", + r); + ap_rputs(more_info, 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 { + for (modp = top_module; modp; modp = modp->next) { + ap_rputs(modp->name, r); + if (modp->next) { + ap_rputs("
      ", r); + } + } + } + ap_rputs("
      \n", r); + ap_rputs(ap_psignature("",r), r); + ap_rputs("\n", r); + /* Done, turn off timeout, close file and return */ + ap_kill_timeout(r); + return 0; +} + +static const char *add_module_info(cmd_parms *cmd, void *dummy, char *name, + 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 = ap_push_array(conf->more_info); + + new->name = name; + new->info = info; + return NULL; +} + +static const command_rec info_cmds[] = +{ + {"AddModuleInfo", add_module_info, NULL, RSRC_CONF, TAKE2, + "a module name and additional information on that module"}, + {NULL} +}; + +static const handler_rec info_handlers[] = +{ + {"server-info", display_info}, + {NULL} +}; + +module MODULE_VAR_EXPORT info_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + 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 table */ + info_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/generators/mod_info.dsp b/modules/generators/mod_info.dsp new file mode 100644 index 00000000000..cf856463352 --- /dev/null +++ b/modules/generators/mod_info.dsp @@ -0,0 +1,112 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleInfo" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleInfo - 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 "ApacheModuleInfo.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 "ApacheModuleInfo.mak" CFG="ApacheModuleInfo - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleInfo - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleInfo - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleInfo - 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 ".\ApacheModuleInfoR" +# PROP Intermediate_Dir ".\ApacheModuleInfoR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleInfo - 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 ".\ApacheModuleInfoD" +# PROP Intermediate_Dir ".\ApacheModuleInfoD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleInfo - Win32 Release" +# Name "ApacheModuleInfo - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_info.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/generators/mod_status.c b/modules/generators/mod_status.c new file mode 100644 index 00000000000..88fc0870908 --- /dev/null +++ b/modules/generators/mod_status.c @@ -0,0 +1,773 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* 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.] + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_conf_globals.h" /* for ap_extended_status */ +#include "http_main.h" +#include "util_script.h" +#include +#include "scoreboard.h" +#include "http_log.h" + +#ifdef NEXT +#if (NX_CURRENT_COMPILER_RELEASE == 410) +#ifdef m68k +#define HZ 64 +#else +#define HZ 100 +#endif +#else +#include +#endif +#endif /* NEXT */ + +#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 + +module MODULE_VAR_EXPORT status_module; + +/* + *command-related code. This is here to prevent use of ExtendedStatus + * without status_module included. + */ +static const char *set_extended_status(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + if (!strcasecmp(arg, "off") || !strcmp(arg, "0")) { + ap_extended_status = 0; + } + else { + ap_extended_status = 1; + } + return NULL; +} + +static const command_rec status_module_cmds[] = +{ + { "ExtendedStatus", set_extended_status, NULL, RSRC_CONF, TAKE1, + "\"On\" to enable extended status information, \"Off\" to disable" }, + {NULL} +}; + +/* Format the number of bytes nicely */ +static void format_byte_out(request_rec *r, unsigned long 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, unsigned long 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, time_t tsecs) +{ + long days, hrs, mins, secs; + + secs = tsecs % 60; + tsecs /= 60; + mins = tsecs % 60; + tsecs /= 60; + hrs = tsecs % 24; + days = tsecs / 24; + if (days) + ap_rprintf(r, " %ld day%s", days, days == 1 ? "" : "s"); + if (hrs) + ap_rprintf(r, " %ld hour%s", hrs, hrs == 1 ? "" : "s"); + if (mins) + ap_rprintf(r, " %ld minute%s", mins, mins == 1 ? "" : "s"); + if (secs) + ap_rprintf(r, " %ld 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} +}; + +static char status_flags[SERVER_NUM_STATUS]; + +static int status_handler(request_rec *r) +{ + char *loc; + time_t nowtime = time(NULL); + time_t up_time; + int i, res; + int ready = 0; + int busy = 0; + unsigned long count = 0; + unsigned long lres, bytes; + unsigned long my_lres, my_bytes, conn_bytes; + unsigned short conn_lres; + unsigned long bcount = 0; + unsigned long kbcount = 0; + long req_time; +#ifndef NO_TIMES +#ifdef _SC_CLK_TCK + float tick = sysconf(_SC_CLK_TCK); +#else + float tick = HZ; +#endif +#endif + int short_report = 0; + int no_table_report = 0; + short_score score_record; + parent_score ps_record; + char stat_buffer[HARD_SERVER_LIMIT]; + int pid_buffer[HARD_SERVER_LIMIT]; + clock_t tu, ts, tcu, tcs; + server_rec *vhost; + + tu = ts = tcu = tcs = 0; + + if (!ap_exists_scoreboard_image()) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Server status unavailable in inetd mode"); + return HTTP_INTERNAL_SERVER_ERROR; + } + r->allowed = (1 << M_GET); + if (r->method_number != M_GET) + return DECLINED; + + r->content_type = "text/html"; + + /* + * 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 = strstr(r->args, status_options[i].form_data_str)) != NULL) { + switch (status_options[i].id) { + case STAT_OPT_REFRESH: + if (*(loc + strlen(status_options[i].form_data_str)) == '=') + ap_table_set(r->headers_out, + status_options[i].hdr_out_str, + loc + strlen(status_options[i].hdr_out_str) + 1); + else + ap_table_set(r->headers_out, + status_options[i].hdr_out_str, "1"); + break; + case STAT_OPT_NOTABLE: + no_table_report = 1; + break; + case STAT_OPT_AUTO: + r->content_type = "text/plain"; + short_report = 1; + break; + } + } + i++; + } + } + + ap_send_http_header(r); + + if (r->header_only) + return 0; + + ap_sync_scoreboard_image(); + for (i = 0; i < HARD_SERVER_LIMIT; ++i) { + score_record = ap_scoreboard_image->servers[i]; + ps_record = ap_scoreboard_image->parent[i]; + res = score_record.status; + stat_buffer[i] = status_flags[res]; + pid_buffer[i] = (int) ps_record.pid; + if (res == SERVER_READY) + ready++; + else if (res != SERVER_DEAD) + busy++; + if (ap_extended_status) { + lres = score_record.access_count; + bytes = score_record.bytes_served; + if (lres != 0 || (res != SERVER_READY && res != SERVER_DEAD)) { +#ifndef NO_TIMES + tu += score_record.times.tms_utime; + ts += score_record.times.tms_stime; + tcu += score_record.times.tms_cutime; + tcs += score_record.times.tms_cstime; +#endif /* NO_TIMES */ + count += lres; + bcount += bytes; + if (bcount >= KBYTE) { + kbcount += (bcount >> 10); + bcount = bcount & 0x3ff; + } + } + } + } + + up_time = nowtime - ap_restart_time; + + ap_hard_timeout("send status info", r); + + if (!short_report) { + ap_rputs(DOCTYPE_HTML_3_2 + "\nApache Status\n\n", + r); + ap_rputs("

      Apache Server Status for ", r); + ap_rvputs(r, ap_get_server_name(r), "

      \n\n", NULL); + ap_rvputs(r, "Server Version: ", + ap_get_server_version(), "
      \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_restart_time, DEFAULT_TIME_FORMAT, 0), + "
      \n", NULL); + ap_rprintf(r, "Parent Server Generation: %d
      \n", (int) ap_my_generation); + ap_rputs("Server uptime: ", r); + show_time(r, up_time); + ap_rputs("
      \n", r); + } + + if (ap_extended_status) { + if (short_report) { + ap_rprintf(r, "Total Accesses: %lu\nTotal kBytes: %lu\n", + count, kbcount); + +#ifndef NO_TIMES + /* Allow for OS/2 not having CPU stats */ + if (ts || tu || tcu || tcs) + ap_rprintf(r, "CPULoad: %g\n", + (tu + ts + tcu + tcs) / 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); + + if (up_time > 0) + 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); + } + else { /* !short_report */ + ap_rprintf(r, "Total accesses: %lu - Total Traffic: ", count); + format_kbyte_out(r, kbcount); + +#ifndef NO_TIMES + /* Allow for OS/2 not having CPU stats */ + ap_rputs("
      \n", r); + ap_rprintf(r, "CPU Usage: u%g s%g cu%g cs%g", + tu / tick, ts / tick, tcu / tick, tcs / tick); + + if (ts || tu || tcu || tcs) + ap_rprintf(r, " - %.3g%% CPU load", + (tu + ts + tcu + tcs) / tick / up_time * 100.); +#endif + + ap_rputs("
      \n", r); + + if (up_time > 0) + ap_rprintf(r, "%.3g requests/sec - ", + (float) count / (float) up_time); + + if (up_time > 0) { + format_byte_out(r, KBYTE * (float) kbcount / (float) up_time); + ap_rputs("/second - ", r); + } + + if (count > 0) { + format_byte_out(r, KBYTE * (float) kbcount / (float) count); + ap_rputs("/request", r); + } + + ap_rputs("
      \n", r); + } /* short_report */ + } /* ap_extended_status */ + + if (!short_report) + ap_rprintf(r, "\n%d requests currently being processed, %d idle servers\n" + ,busy, ready); + else + ap_rprintf(r, "BusyServers: %d\nIdleServers: %d\n", busy, ready); + + /* send the scoreboard 'table' out */ + + if (!short_report) + ap_rputs("
      ", r);
      +    else
      +	ap_rputs("Scoreboard: ", r);
      +
      +    for (i = 0; i < HARD_SERVER_LIMIT; ++i) {
      +	ap_rputc(stat_buffer[i], r);
      +	if ((i % STATUS_MAXLINE == (STATUS_MAXLINE - 1)) && !short_report)
      +	    ap_rputs("\n", r);
      +    }
      +
      +    if (short_report)
      +	ap_rputs("\n", r);
      +    else {
      +	ap_rputs("
      \n", r); + ap_rputs("Scoreboard Key:
      \n", r); + ap_rputs("\"_\" Waiting for Connection, \n", r); + ap_rputs("\"S\" Starting up, \n", r); + ap_rputs("\"R\" Reading Request,
      \n", r); + ap_rputs("\"W\" Sending Reply, \n", r); + ap_rputs("\"K\" Keepalive (read), \n", r); + ap_rputs("\"D\" DNS Lookup,
      \n", r); + ap_rputs("\"L\" Logging, \n", r); + ap_rputs("\"G\" Gracefully finishing, \n", r); + ap_rputs("\".\" Open slot with no current process

      \n", r); + ap_rputs("

      \n", r); + if (!ap_extended_status) { + int j = 0; + ap_rputs("PID Key:
      \n", r); + ap_rputs("

      \n", r);
      +	    for (i = 0; i < HARD_SERVER_LIMIT; ++i) {
      +		if (stat_buffer[i] != '.') {
      +		    ap_rprintf(r, "   %d in state: %c ", pid_buffer[i],
      +		     stat_buffer[i]);
      +		    if (++j >= 3) {
      +		    	ap_rputs("\n", r);
      +			j = 0;
      +		    } else
      +		    	ap_rputs(",", r);
      +		}
      +	    }
      +	    ap_rputs("\n", r);
      +	    ap_rputs("
      \n", r); + } + } + + if (ap_extended_status) { + if (!short_report) { + if (no_table_report) + ap_rputs("


      Server Details

      \n\n", r); + else +#ifdef NO_TIMES + /* Allow for OS/2 not having CPU stats */ + ap_rputs("

      \n\n\n\n", r); +#else + ap_rputs("

      \n\n

      SrvPIDAccM\nSSReqConnChildSlotClientVHostRequest
      \n\n", r); +#endif + } + + for (i = 0; i < HARD_SERVER_LIMIT; ++i) { + score_record = ap_scoreboard_image->servers[i]; + ps_record = ap_scoreboard_image->parent[i]; + vhost = score_record.vhostrec; + if (ps_record.generation != ap_my_generation) { + vhost = NULL; + } + +#if defined(NO_GETTIMEOFDAY) +#ifndef NO_TIMES + if (score_record.start_time == (clock_t) 0) +#endif /* NO_TIMES */ + req_time = 0L; +#ifndef NO_TIMES + else { + req_time = score_record.stop_time - score_record.start_time; + req_time = (req_time * 1000) / (int) tick; + } +#endif /* NO_TIMES */ +#else + if (score_record.start_time.tv_sec == 0L && + score_record.start_time.tv_usec == 0L) + req_time = 0L; + else + req_time = + ((score_record.stop_time.tv_sec - score_record.start_time.tv_sec) * 1000) + + ((score_record.stop_time.tv_usec - score_record.start_time.tv_usec) / 1000); +#endif + if (req_time < 0L) + req_time = 0L; + + lres = score_record.access_count; + my_lres = score_record.my_access_count; + conn_lres = score_record.conn_count; + bytes = score_record.bytes_served; + my_bytes = score_record.my_bytes_served; + conn_bytes = score_record.conn_bytes; + if (lres != 0 || (score_record.status != SERVER_READY + && score_record.status != SERVER_DEAD)) { + if (!short_report) { + if (no_table_report) { + if (score_record.status == SERVER_DEAD) + ap_rprintf(r, + "Server %d-%d (-): %d|%lu|%lu [", + i, (int) ps_record.generation, (int) conn_lres, + my_lres, lres); + else + ap_rprintf(r, + "Server %d-%d (%d): %d|%lu|%lu [", + i, (int) ps_record.generation, + (int) ps_record.pid, + (int) conn_lres, my_lres, lres); + + switch (score_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_DEAD: + ap_rputs("Dead", r); + break; + case SERVER_GRACEFUL: + ap_rputs("Graceful", r); + break; + default: + ap_rputs("?STATE?", r); + break; + } +#ifdef NO_TIMES + /* Allow for OS/2 not having CPU stats */ + ap_rprintf(r, "]\n %.0f %ld (", +#else + + ap_rprintf(r, "] u%g s%g cu%g cs%g\n %.0f %ld (", + score_record.times.tms_utime / tick, + score_record.times.tms_stime / tick, + score_record.times.tms_cutime / tick, + score_record.times.tms_cstime / tick, +#endif +#ifdef OPTIMIZE_TIMEOUTS + difftime(nowtime, ps_record.last_rtime), +#else + difftime(nowtime, score_record.last_used), +#endif + (long) req_time); + 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]
      \n\n", + score_record.client, + ap_escape_html(r->pool, score_record.request), + vhost ? vhost->server_hostname : "(unavailable)"); + } + else { /* !no_table_report */ + if (score_record.status == SERVER_DEAD) + ap_rprintf(r, + "\n\n"); + else + ap_rprintf(r, + "\n\n", + score_record.client, + vhost ? vhost->server_hostname : "(unavailable)", + ap_escape_html(r->pool, score_record.request)); + } /* no_table_report */ + } /* !short_report */ + } /* if () */ + } /* for () */ + + if (!(short_report || no_table_report)) { +#ifdef NO_TIMES + ap_rputs("
      SrvPIDAccMCPU\nSSReqConnChildSlotClientVHostRequest
      %d-%d-%d/%lu/%lu", + i, (int) ps_record.generation, + (int) conn_lres, my_lres, lres); + else + ap_rprintf(r, + "
      %d-%d%d%d/%lu/%lu", + i, (int) ps_record.generation, + (int) ps_record.pid, (int) conn_lres, + my_lres, lres); + + switch (score_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_DEAD: + ap_rputs(".", r); + break; + case SERVER_GRACEFUL: + ap_rputs("G", r); + break; + default: + ap_rputs("?", r); + break; + } +#ifdef NO_TIMES + /* Allow for OS/2 not having CPU stats */ + ap_rprintf(r, "\n%.0f%ld", +#else + ap_rprintf(r, "\n%.2f%.0f%ld", + (score_record.times.tms_utime + + score_record.times.tms_stime + + score_record.times.tms_cutime + + score_record.times.tms_cstime) / tick, +#endif +#ifdef OPTIMIZE_TIMEOUTS + difftime(nowtime, ps_record.last_rtime), +#else + difftime(nowtime, score_record.last_used), +#endif + (long) req_time); + ap_rprintf(r, "%-1.1f%-2.2f%-2.2f\n", + (float) conn_bytes / KBYTE, (float) my_bytes / MBYTE, + (float) bytes / MBYTE); + if (score_record.status == SERVER_BUSY_READ) + ap_rprintf(r, + "??..reading..
      %s%s%s
      \n \ +


      \ +\n \ +
      SrvChild Server number - generation\n \ +
      PIDOS process ID\n \ +
      AccNumber of accesses this connection / this child / this slot\n \ +
      MMode of operation\n \ +
      SSSeconds since beginning of most recent request\n \ +
      ReqMilliseconds required to process most recent request\n \ +
      ConnKilobytes transferred this connection\n \ +
      ChildMegabytes transferred this child\n \ +
      SlotTotal megabytes transferred this slot\n \ +
      \n", r); +#else + ap_rputs("
\n \ +
\ +\n \ +
SrvChild Server number - generation\n \ +
PIDOS process ID\n \ +
AccNumber of accesses this connection / this child / this slot\n \ +
MMode of operation\n \ +
CPUCPU usage, number of seconds\n \ +
SSSeconds since beginning of most recent request\n \ +
ReqMilliseconds required to process most recent request\n \ +
ConnKilobytes transferred this connection\n \ +
ChildMegabytes transferred this child\n \ +
SlotTotal megabytes transferred this slot\n \ +
\n", r); +#endif + } + + } else { + + ap_rputs("
To obtain a full report with current status information ", r); + ap_rputs("you need to use the ExtendedStatus On directive. \n", r); + + } + + if (!short_report) { + ap_rputs(ap_psignature("
\n",r), r); + ap_rputs("\n", r); + } + + ap_kill_timeout(r); + return 0; +} + + +static void status_init(server_rec *s, pool *p) +{ + 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_GRACEFUL] = 'G'; +} + +static const handler_rec status_handlers[] = +{ + {STATUS_MAGIC_TYPE, status_handler}, + {"server-status", status_handler}, + {NULL} +}; + +module MODULE_VAR_EXPORT status_module = +{ + STANDARD_MODULE_STUFF, + status_init, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + status_module_cmds, /* command table */ + status_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/generators/mod_status.dsp b/modules/generators/mod_status.dsp new file mode 100644 index 00000000000..8bc9271959b --- /dev/null +++ b/modules/generators/mod_status.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleStatus" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleStatus - 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 "ApacheModuleStatus.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 "ApacheModuleStatus.mak"\ + CFG="ApacheModuleStatus - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleStatus - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleStatus - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleStatus - 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 ".\ApacheModuleStatusR" +# PROP Intermediate_Dir ".\ApacheModuleStatusR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleStatus - 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 ".\ApacheModuleStatusD" +# PROP Intermediate_Dir ".\ApacheModuleStatusD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleStatus - Win32 Release" +# Name "ApacheModuleStatus - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_status.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/http/.indent.pro b/modules/http/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/modules/http/.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/http/http_core.c b/modules/http/http_core.c new file mode 100644 index 00000000000..e02d32ec5f4 --- /dev/null +++ b/modules/http/http_core.c @@ -0,0 +1,3214 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" /* For index_of_response(). Grump. */ +#include "http_request.h" +#include "http_conf_globals.h" +#include "http_vhost.h" +#include "http_main.h" /* For the default_handler below... */ +#include "http_log.h" +#include "rfc1413.h" +#include "util_md5.h" +#include "scoreboard.h" +#include "fnmatch.h" + +#ifdef USE_MMAP_FILES +#include + +/* mmap support for static files based on ideas from John Heidemann's + * patch against 1.0.5. See + * . + */ + +/* Files have to be at least this big before they're mmap()d. This is to deal + * with systems where the expense of doing an mmap() and an munmap() outweighs + * the benefit for small files. It shouldn't be set lower than 1. + */ +#ifndef MMAP_THRESHOLD +#ifdef SUNOS4 +#define MMAP_THRESHOLD (8*1024) +#else +#define MMAP_THRESHOLD 1 +#endif +#endif +#endif +#ifndef MMAP_LIMIT +#define MMAP_LIMIT (4*1024*1024) +#endif + +/* Server core module... This module provides support for really basic + * server operations, including options and commands which control the + * operation of other modules. Consider this the bureaucracy module. + * + * The core module also defines handlers, etc., do handle just enough + * to allow a server with the core module ONLY to actually serve documents + * (though it slaps DefaultType on all of 'em); this was useful in testing, + * but may not be worth preserving. + * + * This file could almost be mod_core.c, except for the stuff which affects + * the http_conf_globals. + */ + +static void *create_core_dir_config(pool *a, char *dir) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_pcalloc(a, sizeof(core_dir_config)); + if (!dir || dir[strlen(dir) - 1] == '/') { + conf->d = dir; + } + else if (strncmp(dir, "proxy:", 6) == 0) { + conf->d = ap_pstrdup(a, dir); + } + else { + conf->d = ap_pstrcat(a, dir, "/", NULL); + } + conf->d_is_fnmatch = conf->d ? (ap_is_fnmatch(conf->d) != 0) : 0; + conf->d_components = conf->d ? ap_count_dirs(conf->d) : 0; + + conf->opts = dir ? OPT_UNSET : OPT_UNSET|OPT_ALL; + conf->opts_add = conf->opts_remove = OPT_NONE; + conf->override = dir ? OR_UNSET : OR_UNSET|OR_ALL; + + conf->content_md5 = 2; + + conf->use_canonical_name = USE_CANONICAL_NAME_UNSET; + + conf->hostname_lookups = HOSTNAME_LOOKUP_UNSET; + conf->do_rfc1413 = DEFAULT_RFC1413 | 2; /* set bit 1 to indicate default */ + conf->satisfy = SATISFY_NOSPEC; + +#ifdef RLIMIT_CPU + conf->limit_cpu = NULL; +#endif +#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) + conf->limit_mem = NULL; +#endif +#ifdef RLIMIT_NPROC + conf->limit_nproc = NULL; +#endif + + conf->limit_req_body = 0; + conf->sec = ap_make_array(a, 2, sizeof(void *)); +#ifdef WIN32 + conf->script_interpreter_source = INTERPRETER_SOURCE_UNSET; +#endif + + conf->server_signature = srv_sig_unset; + + return (void *)conf; +} + +static void *merge_core_dir_configs(pool *a, void *basev, void *newv) +{ + core_dir_config *base = (core_dir_config *)basev; + core_dir_config *new = (core_dir_config *)newv; + core_dir_config *conf; + int i; + + conf = (core_dir_config *)ap_palloc(a, sizeof(core_dir_config)); + memcpy((char *)conf, (const char *)base, sizeof(core_dir_config)); + if (base->response_code_strings) { + conf->response_code_strings = + ap_palloc(a, sizeof(*conf->response_code_strings) + * RESPONSE_CODES); + memcpy(conf->response_code_strings, base->response_code_strings, + sizeof(*conf->response_code_strings) * RESPONSE_CODES); + } + + conf->d = new->d; + conf->d_is_fnmatch = new->d_is_fnmatch; + conf->d_components = new->d_components; + conf->r = new->r; + + if (new->opts & OPT_UNSET) { + /* there was no explicit setting of new->opts, so we merge + * preserve the invariant (opts_add & opts_remove) == 0 + */ + conf->opts_add = (conf->opts_add & ~new->opts_remove) | new->opts_add; + conf->opts_remove = (conf->opts_remove & ~new->opts_add) + | new->opts_remove; + conf->opts = (conf->opts & ~conf->opts_remove) | conf->opts_add; + if ((base->opts & OPT_INCNOEXEC) && (new->opts & OPT_INCLUDES)) { + conf->opts = (conf->opts & ~OPT_INCNOEXEC) | OPT_INCLUDES; + } + } + else { + /* otherwise we just copy, because an explicit opts setting + * overrides all earlier +/- modifiers + */ + conf->opts = new->opts; + conf->opts_add = new->opts_add; + conf->opts_remove = new->opts_remove; + } + + if (!(new->override & OR_UNSET)) { + conf->override = new->override; + } + if (new->ap_default_type) { + conf->ap_default_type = new->ap_default_type; + } + + if (new->ap_auth_type) { + conf->ap_auth_type = new->ap_auth_type; + } + if (new->ap_auth_name) { + conf->ap_auth_name = new->ap_auth_name; + } + if (new->ap_requires) { + conf->ap_requires = new->ap_requires; + } + + if (new->response_code_strings) { + if (conf->response_code_strings == NULL) { + conf->response_code_strings = ap_palloc(a, + sizeof(*conf->response_code_strings) * RESPONSE_CODES); + memcpy(conf->response_code_strings, new->response_code_strings, + sizeof(*conf->response_code_strings) * RESPONSE_CODES); + } + else { + for (i = 0; i < RESPONSE_CODES; ++i) { + if (new->response_code_strings[i] != NULL) { + conf->response_code_strings[i] + = new->response_code_strings[i]; + } + } + } + } + if (new->hostname_lookups != HOSTNAME_LOOKUP_UNSET) { + conf->hostname_lookups = new->hostname_lookups; + } + if ((new->do_rfc1413 & 2) == 0) { + conf->do_rfc1413 = new->do_rfc1413; + } + if ((new->content_md5 & 2) == 0) { + conf->content_md5 = new->content_md5; + } + if (new->use_canonical_name != USE_CANONICAL_NAME_UNSET) { + conf->use_canonical_name = new->use_canonical_name; + } + +#ifdef RLIMIT_CPU + if (new->limit_cpu) { + conf->limit_cpu = new->limit_cpu; + } +#endif +#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) + if (new->limit_mem) { + conf->limit_mem = new->limit_mem; + } +#endif +#ifdef RLIMIT_NPROC + if (new->limit_nproc) { + conf->limit_nproc = new->limit_nproc; + } +#endif + + if (new->limit_req_body) { + conf->limit_req_body = new->limit_req_body; + } + conf->sec = ap_append_arrays(a, base->sec, new->sec); + + if (new->satisfy != SATISFY_NOSPEC) { + conf->satisfy = new->satisfy; + } + +#ifdef WIN32 + if (new->script_interpreter_source != INTERPRETER_SOURCE_UNSET) { + conf->script_interpreter_source = new->script_interpreter_source; + } +#endif + + if (new->server_signature != srv_sig_unset) { + conf->server_signature = new->server_signature; + } + + return (void*)conf; +} + +static void *create_core_server_config(pool *a, server_rec *s) +{ + core_server_config *conf; + int is_virtual = s->is_virtual; + + conf = (core_server_config *)ap_pcalloc(a, sizeof(core_server_config)); +#ifdef GPROF + conf->gprof_dir = NULL; +#endif + conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME; + conf->ap_document_root = is_virtual ? NULL : DOCUMENT_LOCATION; + conf->sec = ap_make_array(a, 40, sizeof(void *)); + conf->sec_url = ap_make_array(a, 40, sizeof(void *)); + + return (void *)conf; +} + +static void *merge_core_server_configs(pool *p, void *basev, void *virtv) +{ + core_server_config *base = (core_server_config *)basev; + core_server_config *virt = (core_server_config *)virtv; + core_server_config *conf; + + conf = (core_server_config *)ap_pcalloc(p, sizeof(core_server_config)); + *conf = *virt; + if (!conf->access_name) { + conf->access_name = base->access_name; + } + if (!conf->ap_document_root) { + conf->ap_document_root = base->ap_document_root; + } + conf->sec = ap_append_arrays(p, base->sec, virt->sec); + conf->sec_url = ap_append_arrays(p, base->sec_url, virt->sec_url); + + return conf; +} + +/* Add per-directory configuration entry (for section); + * these are part of the core server config. + */ + +CORE_EXPORT(void) ap_add_per_dir_conf(server_rec *s, void *dir_config) +{ + core_server_config *sconf = ap_get_module_config(s->module_config, + &core_module); + void **new_space = (void **)ap_push_array(sconf->sec); + + *new_space = dir_config; +} + +CORE_EXPORT(void) ap_add_per_url_conf(server_rec *s, void *url_config) +{ + core_server_config *sconf = ap_get_module_config(s->module_config, + &core_module); + void **new_space = (void **)ap_push_array(sconf->sec_url); + + *new_space = url_config; +} + +CORE_EXPORT(void) ap_add_file_conf(core_dir_config *conf, void *url_config) +{ + void **new_space = (void **)ap_push_array(conf->sec); + + *new_space = url_config; +} + +/* core_reorder_directories reorders the directory sections such that the + * 1-component sections come first, then the 2-component, and so on, finally + * followed by the "special" sections. A section is "special" if it's a regex, + * or if it doesn't start with / -- consider proxy: matching. All movements + * are in-order to preserve the ordering of the sections from the config files. + * See directory_walk(). + */ + +#ifdef HAVE_DRIVE_LETTERS +#define IS_SPECIAL(entry_core) \ + ((entry_core)->r != NULL \ + || ((entry_core)->d[0] != '/' && (entry_core)->d[1] != ':')) +#else +#define IS_SPECIAL(entry_core) \ + ((entry_core)->r != NULL || (entry_core)->d[0] != '/') +#endif + +/* We need to do a stable sort, qsort isn't stable. So to make it stable + * we'll be maintaining the original index into the list, and using it + * as the minor key during sorting. The major key is the number of + * components (where a "special" section has infinite components). + */ +struct reorder_sort_rec { + void *elt; + int orig_index; +}; + +static int reorder_sorter(const void *va, const void *vb) +{ + const struct reorder_sort_rec *a = va; + const struct reorder_sort_rec *b = vb; + core_dir_config *core_a; + core_dir_config *core_b; + + core_a = (core_dir_config *)ap_get_module_config(a->elt, &core_module); + core_b = (core_dir_config *)ap_get_module_config(b->elt, &core_module); + if (IS_SPECIAL(core_a)) { + if (!IS_SPECIAL(core_b)) { + return 1; + } + } + else if (IS_SPECIAL(core_b)) { + return -1; + } + else { + /* we know they're both not special */ + if (core_a->d_components < core_b->d_components) { + return -1; + } + else if (core_a->d_components > core_b->d_components) { + return 1; + } + } + /* Either they're both special, or they're both not special and have the + * same number of components. In any event, we now have to compare + * the minor key. */ + return a->orig_index - b->orig_index; +} + +void ap_core_reorder_directories(pool *p, server_rec *s) +{ + core_server_config *sconf; + array_header *sec; + struct reorder_sort_rec *sortbin; + int nelts; + void **elts; + int i; + pool *tmp; + + sconf = ap_get_module_config(s->module_config, &core_module); + sec = sconf->sec; + nelts = sec->nelts; + elts = (void **)sec->elts; + + /* we have to allocate tmp space to do a stable sort */ + tmp = ap_make_sub_pool(p); + sortbin = ap_palloc(tmp, sec->nelts * sizeof(*sortbin)); + for (i = 0; i < nelts; ++i) { + sortbin[i].orig_index = i; + sortbin[i].elt = elts[i]; + } + + qsort(sortbin, nelts, sizeof(*sortbin), reorder_sorter); + + /* and now copy back to the original array */ + for (i = 0; i < nelts; ++i) { + elts[i] = sortbin[i].elt; + } + + ap_destroy_pool(tmp); +} + +/***************************************************************** + * + * There are some elements of the core config structures in which + * other modules have a legitimate interest (this is ugly, but necessary + * to preserve NCSA back-compatibility). So, we have a bunch of accessors + * here... + */ + +API_EXPORT(int) ap_allow_options(request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); + + return conf->opts; +} + +API_EXPORT(int) ap_allow_overrides(request_rec *r) +{ + core_dir_config *conf; + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + return conf->override; +} + +API_EXPORT(const char *) ap_auth_type(request_rec *r) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + return conf->ap_auth_type; +} + +API_EXPORT(const char *) ap_auth_name(request_rec *r) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + return conf->ap_auth_name; +} + +API_EXPORT(const char *) ap_default_type(request_rec *r) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + return conf->ap_default_type + ? conf->ap_default_type + : DEFAULT_CONTENT_TYPE; +} + +API_EXPORT(const char *) ap_document_root(request_rec *r) /* Don't use this! */ +{ + core_server_config *conf; + + conf = (core_server_config *)ap_get_module_config(r->server->module_config, + &core_module); + return conf->ap_document_root; +} + +API_EXPORT(const array_header *) ap_requires(request_rec *r) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + return conf->ap_requires; +} + +API_EXPORT(int) ap_satisfies(request_rec *r) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + return conf->satisfy; +} + +/* Should probably just get rid of this... the only code that cares is + * part of the core anyway (and in fact, it isn't publicised to other + * modules). + */ + +char *ap_response_code_string(request_rec *r, int error_index) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + if (conf->response_code_strings == NULL) { + return NULL; + } + return conf->response_code_strings[error_index]; +} + + +/* Code from Harald Hanche-Olsen */ +static ap_inline void do_double_reverse (conn_rec *conn) +{ + struct hostent *hptr; + + if (conn->double_reverse) { + /* already done */ + return; + } + if (conn->remote_host == NULL || conn->remote_host[0] == '\0') { + /* single reverse failed, so don't bother */ + conn->double_reverse = -1; + return; + } + hptr = gethostbyname(conn->remote_host); + if (hptr) { + char **haddr; + + for (haddr = hptr->h_addr_list; *haddr; haddr++) { + if (((struct in_addr *)(*haddr))->s_addr + == conn->remote_addr.sin_addr.s_addr) { + conn->double_reverse = 1; + return; + } + } + } + conn->double_reverse = -1; +} + +API_EXPORT(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config, + int type) +{ + struct in_addr *iaddr; + struct hostent *hptr; + int hostname_lookups; + int old_stat = SERVER_DEAD; /* we shouldn't ever be in this state */ + + /* If we haven't checked the host name, and we want to */ + if (dir_config) { + hostname_lookups = + ((core_dir_config *)ap_get_module_config(dir_config, &core_module)) + ->hostname_lookups; + if (hostname_lookups == HOSTNAME_LOOKUP_UNSET) { + hostname_lookups = HOSTNAME_LOOKUP_OFF; + } + } + else { + /* the default */ + hostname_lookups = HOSTNAME_LOOKUP_OFF; + } + + if (type != REMOTE_NOLOOKUP + && conn->remote_host == NULL + && (type == REMOTE_DOUBLE_REV + || hostname_lookups != HOSTNAME_LOOKUP_OFF)) { + old_stat = ap_update_child_status(conn->child_num, SERVER_BUSY_DNS, + (request_rec*)NULL); + iaddr = &(conn->remote_addr.sin_addr); + hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), AF_INET); + if (hptr != NULL) { + conn->remote_host = ap_pstrdup(conn->pool, (void *)hptr->h_name); + ap_str_tolower(conn->remote_host); + + if (hostname_lookups == HOSTNAME_LOOKUP_DOUBLE) { + do_double_reverse(conn); + if (conn->double_reverse != 1) { + conn->remote_host = NULL; + } + } + } + /* if failed, set it to the NULL string to indicate error */ + if (conn->remote_host == NULL) { + conn->remote_host = ""; + } + } + if (type == REMOTE_DOUBLE_REV) { + do_double_reverse(conn); + if (conn->double_reverse == -1) { + return NULL; + } + } + if (old_stat != SERVER_DEAD) { + (void)ap_update_child_status(conn->child_num, old_stat, + (request_rec*)NULL); + } + +/* + * Return the desired information; either the remote DNS name, if found, + * or either NULL (if the hostname was requested) or the IP address + * (if any identifier was requested). + */ + if (conn->remote_host != NULL && conn->remote_host[0] != '\0') { + return conn->remote_host; + } + else { + if (type == REMOTE_HOST || type == REMOTE_DOUBLE_REV) { + return NULL; + } + else { + return conn->remote_ip; + } + } +} + +API_EXPORT(const char *) ap_get_remote_logname(request_rec *r) +{ + core_dir_config *dir_conf; + + if (r->connection->remote_logname != NULL) { + return r->connection->remote_logname; + } + +/* If we haven't checked the identity, and we want to */ + dir_conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + if (dir_conf->do_rfc1413 & 1) { + return ap_rfc1413(r->connection, r->server); + } + else { + return NULL; + } +} + +/* There are two options regarding what the "name" of a server is. The + * "canonical" name as defined by ServerName and Port, or the "client's + * name" as supplied by a possible Host: header or full URI. We never + * trust the port passed in the client's headers, we always use the + * port of the actual socket. + * + * The DNS option to UseCanonicalName causes this routine to do a + * reverse lookup on the local IP address of the connectiona and use + * that for the ServerName. This makes its value more reliable while + * at the same time allowing Demon's magic virtual hosting to work. + * The assumption is that DNS lookups are sufficiently quick... + * -- fanf 1998-10-03 + */ +API_EXPORT(const char *) ap_get_server_name(request_rec *r) +{ + conn_rec *conn = r->connection; + core_dir_config *d; + + d = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + if (d->use_canonical_name == USE_CANONICAL_NAME_OFF) { + return r->hostname ? r->hostname : r->server->server_hostname; + } + if (d->use_canonical_name == USE_CANONICAL_NAME_DNS) { + if (conn->local_host == NULL) { + struct in_addr *iaddr; + struct hostent *hptr; + int old_stat; + old_stat = ap_update_child_status(conn->child_num, + SERVER_BUSY_DNS, r); + iaddr = &(conn->local_addr.sin_addr); + hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), + AF_INET); + if (hptr != NULL) { + conn->local_host = ap_pstrdup(conn->pool, + (void *)hptr->h_name); + ap_str_tolower(conn->local_host); + } + else { + conn->local_host = ap_pstrdup(conn->pool, + r->server->server_hostname); + } + (void) ap_update_child_status(conn->child_num, old_stat, r); + } + return conn->local_host; + } + /* default */ + return r->server->server_hostname; +} + +API_EXPORT(unsigned) ap_get_server_port(const request_rec *r) +{ + unsigned port; + core_dir_config *d = + (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); + + port = r->server->port ? r->server->port : ap_default_port(r); + + if (d->use_canonical_name == USE_CANONICAL_NAME_OFF + || d->use_canonical_name == USE_CANONICAL_NAME_DNS) { + return r->hostname ? ntohs(r->connection->local_addr.sin_port) + : port; + } + /* default */ + return port; +} + +API_EXPORT(char *) ap_construct_url(pool *p, const char *uri, + request_rec *r) +{ + unsigned port = ap_get_server_port(r); + const char *host = ap_get_server_name(r); + + if (ap_is_default_port(port, r)) { + return ap_pstrcat(p, ap_http_method(r), "://", host, uri, NULL); + } + return ap_psprintf(p, "%s://%s:%u%s", ap_http_method(r), host, port, uri); +} + +API_EXPORT(unsigned long) ap_get_limit_req_body(const request_rec *r) +{ + core_dir_config *d = + (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); + + return d->limit_req_body; +} + +#ifdef WIN32 +static char* get_interpreter_from_win32_registry(pool *p, const char* ext) +{ + char extension_path[] = "SOFTWARE\\Classes\\"; + char executable_path[] = "\\SHELL\\OPEN\\COMMAND"; + + HKEY hkeyOpen; + DWORD type; + int size; + int result; + char *keyName; + char *buffer; + char *s; + + if (!ext) + return NULL; + /* + * Future optimization: + * When the registry is successfully searched, store the interpreter + * string in a table to make subsequent look-ups faster + */ + + /* Open the key associated with the script extension */ + keyName = ap_pstrcat(p, extension_path, ext, NULL); + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, + &hkeyOpen); + + if (result != ERROR_SUCCESS) + return NULL; + + /* Read to NULL buffer to find value size */ + size = 0; + result = RegQueryValueEx(hkeyOpen, "", NULL, &type, NULL, &size); + + if (result == ERROR_SUCCESS) { + buffer = ap_palloc(p, size); + result = RegQueryValueEx(hkeyOpen, "", NULL, &type, buffer, &size); + } + + RegCloseKey(hkeyOpen); + + if (result != ERROR_SUCCESS) + return NULL; + + /* Open the key associated with the interpreter path */ + keyName = ap_pstrcat(p, extension_path, buffer, executable_path, NULL); + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, + &hkeyOpen); + + if (result != ERROR_SUCCESS) + return NULL; + + /* Read to NULL buffer to find value size */ + size = 0; + result = RegQueryValueEx(hkeyOpen, "", 0, &type, NULL, &size); + + if (result == ERROR_SUCCESS) { + buffer = ap_palloc(p, size); + result = RegQueryValueEx(hkeyOpen, "", 0, &type, buffer, &size); + } + + RegCloseKey(hkeyOpen); + + if (result != ERROR_SUCCESS) + return NULL; + + /* + * The canonical way shell command entries are entered in the Win32 + * registry is as follows: + * shell [options] "%1" + * where + * shell - full path name to interpreter or shell to run. + * E.g., c:\usr\local\ntreskit\perl\bin\perl.exe + * options - optional switches + * E.g., \C + * "%1" - Place holder for file to run the shell against. + * Typically quoted. + * + * If we find a %1 or a quoted %1, lop it off. + */ + if (buffer && *buffer) { + if ((s = strstr(buffer, "\"%1"))) + *s = '\0'; + else if ((s = strstr(buffer, "%1"))) + *s = '\0'; + } + + return buffer; +} + +API_EXPORT (file_type_e) ap_get_win32_interpreter(const request_rec *r, + char** interpreter ) +{ + HANDLE hFile; + DWORD nBytesRead; + BOOLEAN bResult; + char buffer[1024]; + core_dir_config *d; + int i; + file_type_e fileType = eFileTypeUNKNOWN; + char *ext = NULL; + char *exename = NULL; + + d = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + /* Find the file extension */ + exename = strrchr(r->filename, '/'); + if (!exename) { + exename = strrchr(r->filename, '\\'); + } + if (!exename) { + exename = r->filename; + } + else { + exename++; + } + ext = strrchr(exename, '.'); + + if (ext && (!strcasecmp(ext,".bat") || !strcasecmp(ext,".cmd"))) { + return eFileTypeEXE32; + } + + /* If the file has an extension and it is not .com and not .exe and + * we've been instructed to search the registry, then do it! + */ + if (ext && strcasecmp(ext,".exe") && strcasecmp(ext,".com") && + d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY) { + /* Check the registry */ + *interpreter = get_interpreter_from_win32_registry(r->pool, ext); + if (*interpreter) + return eFileTypeSCRIPT; + else { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r->server, + "ScriptInterpreterSource config directive set to \"registry\".\n\t" + "Registry was searched but interpreter not found. Trying the shebang line."); + } + } + + /* Need to peek into the file figure out what it really is... */ + hFile = CreateFile(r->filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return eFileTypeUNKNOWN; + } + bResult = ReadFile(hFile, (void*) &buffer, sizeof(buffer) - 1, + &nBytesRead, NULL); + if (!bResult || (nBytesRead == 0)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "ReadFile(%s) failed", r->filename); + CloseHandle(hFile); + return eFileTypeUNKNOWN; + } + CloseHandle(hFile); + buffer[nBytesRead] = '\0'; + + /* Script or executable, that is the question... */ + if ((buffer[0] == '#') && (buffer[1] == '!')) { + /* Assuming file is a script since it starts with a shebang */ + fileType = eFileTypeSCRIPT; + for (i = 2; i < sizeof(buffer); i++) { + if ((buffer[i] == '\r') + || (buffer[i] == '\n')) { + break; + } + } + buffer[i] = '\0'; + for (i = 2; buffer[i] == ' ' ; ++i) + ; + *interpreter = ap_pstrdup(r->pool, buffer + i ); + } + else { + /* Not a script, is it an executable? */ + IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)buffer; + if ((nBytesRead >= sizeof(IMAGE_DOS_HEADER)) && (hdr->e_magic == IMAGE_DOS_SIGNATURE)) { + if (hdr->e_lfarlc < 0x40) + fileType = eFileTypeEXE16; + else + fileType = eFileTypeEXE32; + } + else + fileType = eFileTypeUNKNOWN; + } + + return fileType; +} +#endif + +/***************************************************************** + * + * Commands... this module handles almost all of the NCSA httpd.conf + * commands, but most of the old srm.conf is in the the modules. + */ + +static const char end_directory_section[] = ""; +static const char end_directorymatch_section[] = ""; +static const char end_location_section[] = ""; +static const char end_locationmatch_section[] = ""; +static const char end_files_section[] = ""; +static const char end_filesmatch_section[] = ""; +static const char end_virtualhost_section[] = ""; +static const char end_ifmodule_section[] = ""; +static const char end_ifdefine_section[] = ""; + + +API_EXPORT(const char *) ap_check_cmd_context(cmd_parms *cmd, + unsigned forbidden) +{ + const char *gt = (cmd->cmd->name[0] == '<' + && cmd->cmd->name[strlen(cmd->cmd->name)-1] != '>') + ? ">" : ""; + + if ((forbidden & NOT_IN_VIRTUALHOST) && cmd->server->is_virtual) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, gt, + " cannot occur within section", NULL); + } + + if ((forbidden & NOT_IN_LIMIT) && cmd->limited != -1) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, gt, + " cannot occur within section", NULL); + } + + if ((forbidden & NOT_IN_DIR_LOC_FILE) == NOT_IN_DIR_LOC_FILE + && cmd->path != NULL) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, gt, + " cannot occur within " + "section", NULL); + } + + if (((forbidden & NOT_IN_DIRECTORY) + && (cmd->end_token == end_directory_section + || cmd->end_token == end_directorymatch_section)) + || ((forbidden & NOT_IN_LOCATION) + && (cmd->end_token == end_location_section + || cmd->end_token == end_locationmatch_section)) + || ((forbidden & NOT_IN_FILES) + && (cmd->end_token == end_files_section + || cmd->end_token == end_filesmatch_section))) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, gt, + " cannot occur within <", cmd->end_token+2, + " section", NULL); + } + + return NULL; +} + +static const char *set_access_name(cmd_parms *cmd, void *dummy, char *arg) +{ + void *sconf = cmd->server->module_config; + core_server_config *conf = ap_get_module_config(sconf, &core_module); + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + conf->access_name = ap_pstrdup(cmd->pool, arg); + return NULL; +} + +#ifdef GPROF +static const char *set_gprof_dir(cmd_parms *cmd, void *dummy, char *arg) +{ + void *sconf = cmd->server->module_config; + core_server_config *conf = ap_get_module_config(sconf, &core_module); + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + conf->gprof_dir = ap_pstrdup(cmd->pool, arg); + return NULL; +} +#endif /*GPROF*/ + +static const char *set_document_root(cmd_parms *cmd, void *dummy, char *arg) +{ + void *sconf = cmd->server->module_config; + core_server_config *conf = ap_get_module_config(sconf, &core_module); + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + arg = ap_os_canonical_filename(cmd->pool, arg); + if (ap_configtestonly && ap_docrootcheck && !ap_is_directory(arg)) { + if (cmd->server->is_virtual) { + fprintf(stderr, "Warning: DocumentRoot [%s] does not exist\n", + arg); + } + else { + return "DocumentRoot must be a directory"; + } + } + + conf->ap_document_root = arg; + return NULL; +} + +API_EXPORT(void) ap_custom_response(request_rec *r, int status, char *string) +{ + core_dir_config *conf = + ap_get_module_config(r->per_dir_config, &core_module); + int idx; + + if(conf->response_code_strings == NULL) { + conf->response_code_strings = + ap_pcalloc(r->pool, + sizeof(*conf->response_code_strings) * + RESPONSE_CODES); + } + + idx = ap_index_of_response(status); + + conf->response_code_strings[idx] = + ((ap_is_url(string) || (*string == '/')) && (*string != '"')) ? + ap_pstrdup(r->pool, string) : ap_pstrcat(r->pool, "\"", string, NULL); +} + +static const char *set_error_document(cmd_parms *cmd, core_dir_config *conf, + char *line) +{ + int error_number, index_number, idx500; + char *w; + + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + /* 1st parameter should be a 3 digit number, which we recognize; + * convert it into an array index + */ + + w = ap_getword_conf_nc(cmd->pool, &line); + error_number = atoi(w); + + idx500 = ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR); + + if (error_number == HTTP_INTERNAL_SERVER_ERROR) { + index_number = idx500; + } + else if ((index_number = ap_index_of_response(error_number)) == idx500) { + return ap_pstrcat(cmd->pool, "Unsupported HTTP response code ", + w, NULL); + } + + /* The entry should be ignored if it is a full URL for a 401 error */ + + if (error_number == 401 && + line[0] != '/' && line[0] != '"') { /* Ignore it... */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, cmd->server, + "cannot use a full URL in a 401 ErrorDocument " + "directive --- ignoring!"); + } + else { /* Store it... */ + if (conf->response_code_strings == NULL) { + conf->response_code_strings = + ap_pcalloc(cmd->pool, + sizeof(*conf->response_code_strings) * RESPONSE_CODES); + } + conf->response_code_strings[index_number] = ap_pstrdup(cmd->pool, line); + } + + return NULL; +} + +/* access.conf commands... + * + * The *only* thing that can appear in access.conf at top level is a + * section. NB we need to have a way to cut the srm_command_loop + * invoked by dirsection (i.e., ) short when is seen. + * We do that by returning an error, which dirsection itself recognizes and + * discards as harmless. Cheesy, but it works. + */ + +static const char *set_override(cmd_parms *cmd, core_dir_config *d, + const char *l) +{ + char *w; + + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + d->override = OR_NONE; + while (l[0]) { + w = ap_getword_conf(cmd->pool, &l); + if (!strcasecmp(w, "Limit")) { + d->override |= OR_LIMIT; + } + else if (!strcasecmp(w, "Options")) { + d->override |= OR_OPTIONS; + } + else if (!strcasecmp(w, "FileInfo")) { + d->override |= OR_FILEINFO; + } + else if (!strcasecmp(w, "AuthConfig")) { + d->override |= OR_AUTHCFG; + } + else if (!strcasecmp(w, "Indexes")) { + d->override |= OR_INDEXES; + } + else if (!strcasecmp(w, "None")) { + d->override = OR_NONE; + } + else if (!strcasecmp(w, "All")) { + d->override = OR_ALL; + } + else { + return ap_pstrcat(cmd->pool, "Illegal override option ", w, NULL); + } + d->override &= ~OR_UNSET; + } + + return NULL; +} + +static const char *set_options(cmd_parms *cmd, core_dir_config *d, + const char *l) +{ + allow_options_t opt; + int first = 1; + char action; + + while (l[0]) { + char *w = ap_getword_conf(cmd->pool, &l); + action = '\0'; + + if (*w == '+' || *w == '-') { + action = *(w++); + } + else if (first) { + d->opts = OPT_NONE; + first = 0; + } + + if (!strcasecmp(w, "Indexes")) { + opt = OPT_INDEXES; + } + else if (!strcasecmp(w, "Includes")) { + opt = OPT_INCLUDES; + } + else if (!strcasecmp(w, "IncludesNOEXEC")) { + opt = (OPT_INCLUDES | OPT_INCNOEXEC); + } + else if (!strcasecmp(w, "FollowSymLinks")) { + opt = OPT_SYM_LINKS; + } + else if (!strcasecmp(w, "SymLinksIfOwnerMatch")) { + opt = OPT_SYM_OWNER; + } + else if (!strcasecmp(w, "execCGI")) { + opt = OPT_EXECCGI; + } + else if (!strcasecmp(w, "MultiViews")) { + opt = OPT_MULTI; + } + else if (!strcasecmp(w, "RunScripts")) { /* AI backcompat. Yuck */ + opt = OPT_MULTI|OPT_EXECCGI; + } + else if (!strcasecmp(w, "None")) { + opt = OPT_NONE; + } + else if (!strcasecmp(w, "All")) { + opt = OPT_ALL; + } + else { + return ap_pstrcat(cmd->pool, "Illegal option ", w, NULL); + } + + /* we ensure the invariant (d->opts_add & d->opts_remove) == 0 */ + if (action == '-') { + d->opts_remove |= opt; + d->opts_add &= ~opt; + d->opts &= ~opt; + } + else if (action == '+') { + d->opts_add |= opt; + d->opts_remove &= ~opt; + d->opts |= opt; + } + else { + d->opts |= opt; + } + } + + return NULL; +} + +static const char *satisfy(cmd_parms *cmd, core_dir_config *c, char *arg) +{ + if (!strcasecmp(arg, "all")) { + c->satisfy = SATISFY_ALL; + } + else if (!strcasecmp(arg, "any")) { + c->satisfy = SATISFY_ANY; + } + else { + return "Satisfy either 'any' or 'all'."; + } + return NULL; +} + +static const char *require(cmd_parms *cmd, core_dir_config *c, char *arg) +{ + require_line *r; + + if (!c->ap_requires) { + c->ap_requires = ap_make_array(cmd->pool, 2, sizeof(require_line)); + } + r = (require_line *)ap_push_array(c->ap_requires); + r->requirement = ap_pstrdup(cmd->pool, arg); + r->method_mask = cmd->limited; + return NULL; +} + +CORE_EXPORT_NONSTD(const char *) ap_limit_section(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *limited_methods = ap_getword(cmd->pool, &arg, '>'); + void *tog = cmd->cmd->cmd_data; + int limited = 0; + + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + /* XXX: NB: Currently, we have no way of checking + * whether or sections are closed properly. + * (If we would add a srm_command_loop() here we might...) + */ + + while (limited_methods[0]) { + char *method = ap_getword_conf(cmd->pool, &limited_methods); + int methnum = ap_method_number_of(method); + + if (methnum == M_TRACE && !tog) { + return "TRACE cannot be controlled by "; + } + else if (methnum == M_INVALID) { + return ap_pstrcat(cmd->pool, "unknown method \"", method, + "\" in " : ">", NULL); + } + else { + limited |= (1 << methnum); + } + } + + /* Killing two features with one function, + * if (tog == NULL) , else + */ + cmd->limited = tog ? ~limited : limited; + return NULL; +} + +static const char *endlimit_section(cmd_parms *cmd, void *dummy, void *dummy2) +{ + void *tog = cmd->cmd->cmd_data; + + if (cmd->limited == -1) { + return tog ? " unexpected" : " unexpected"; + } + + cmd->limited = -1; + return NULL; +} + +/* + * When a section is not closed properly when end-of-file is reached, + * then an error message should be printed: + */ +static const char *missing_endsection(cmd_parms *cmd, int nest) +{ + if (nest < 2) { + return ap_psprintf(cmd->pool, "Missing %s directive at end-of-file", + cmd->end_token); + } + return ap_psprintf(cmd->pool, "%d missing %s directives at end-of-file", + nest, cmd->end_token); +} + +/* We use this in and , to ensure that + * people don't get bitten by wrong-cased regex matches + */ + +#ifdef WIN32 +#define USE_ICASE REG_ICASE +#else +#define USE_ICASE 0 +#endif + +static const char *end_nested_section(cmd_parms *cmd, void *dummy) +{ + if (cmd->end_token == NULL) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, + " without matching <", cmd->cmd->name + 2, + " section", NULL); + } + /* + * This '!=' may look weird on a string comparison, but it's correct -- + * it's been set up so that checking for two pointers to the same datum + * is valid here. And faster. + */ + if (cmd->cmd->name != cmd->end_token) { + return ap_pstrcat(cmd->pool, "Expected ", cmd->end_token, " but saw ", + cmd->cmd->name, NULL); + } + return cmd->end_token; +} + +/* + * Report a missing-'>' syntax error. + */ +static char *unclosed_directive(cmd_parms *cmd) +{ + return ap_pstrcat(cmd->pool, cmd->cmd->name, + "> directive missing closing '>'", NULL); +} + +static const char *dirsection(cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *errmsg; + char *endp = strrchr(arg, '>'); + int old_overrides = cmd->override; + char *old_path = cmd->path; + core_dir_config *conf; + void *new_dir_conf = ap_create_per_dir_config(cmd->pool); + regex_t *r = NULL; + const char *old_end_token; + const command_rec *thiscmd = cmd->cmd; + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (endp == NULL) { + return unclosed_directive(cmd); + } + + *endp = '\0'; + + cmd->path = ap_getword_conf(cmd->pool, &arg); + cmd->override = OR_ALL|ACCESS_CONF; + + if (thiscmd->cmd_data) { /* */ + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE); + } + else if (!strcmp(cmd->path, "~")) { + cmd->path = ap_getword_conf(cmd->pool, &arg); + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE); + } + else { + /* Ensure that the pathname is canonical */ + cmd->path = ap_os_canonical_filename(cmd->pool, cmd->path); + } + + old_end_token = cmd->end_token; + cmd->end_token = thiscmd->cmd_data ? end_directorymatch_section : end_directory_section; + errmsg = ap_srm_command_loop(cmd, new_dir_conf); + if (errmsg == NULL) { + errmsg = missing_endsection(cmd, 1); + } + cmd->end_token = old_end_token; + if (errmsg != (thiscmd->cmd_data + ? end_directorymatch_section + : end_directory_section)) { + return errmsg; + } + + conf = (core_dir_config *)ap_get_module_config(new_dir_conf, &core_module); + conf->r = r; + + ap_add_per_dir_conf(cmd->server, new_dir_conf); + + if (*arg != '\0') { + return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name, + "> arguments not (yet) supported.", NULL); + } + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +static const char *urlsection(cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *errmsg; + char *endp = strrchr(arg, '>'); + int old_overrides = cmd->override; + char *old_path = cmd->path; + core_dir_config *conf; + regex_t *r = NULL; + const char *old_end_token; + const command_rec *thiscmd = cmd->cmd; + + void *new_url_conf = ap_create_per_dir_config(cmd->pool); + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (endp == NULL) { + return unclosed_directive(cmd); + } + + *endp = '\0'; + + cmd->path = ap_getword_conf(cmd->pool, &arg); + cmd->override = OR_ALL|ACCESS_CONF; + + if (thiscmd->cmd_data) { /* */ + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED); + } + else if (!strcmp(cmd->path, "~")) { + cmd->path = ap_getword_conf(cmd->pool, &arg); + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED); + } + + old_end_token = cmd->end_token; + cmd->end_token = thiscmd->cmd_data ? end_locationmatch_section + : end_location_section; + errmsg = ap_srm_command_loop(cmd, new_url_conf); + if (errmsg == NULL) { + errmsg = missing_endsection(cmd, 1); + } + cmd->end_token = old_end_token; + if (errmsg != (thiscmd->cmd_data + ? end_locationmatch_section + : end_location_section)) { + return errmsg; + } + + conf = (core_dir_config *)ap_get_module_config(new_url_conf, &core_module); + conf->d = ap_pstrdup(cmd->pool, cmd->path); /* No mangling, please */ + conf->d_is_fnmatch = ap_is_fnmatch(conf->d) != 0; + conf->r = r; + + ap_add_per_url_conf(cmd->server, new_url_conf); + + if (*arg != '\0') { + return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name, + "> arguments not (yet) supported.", NULL); + } + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +static const char *filesection(cmd_parms *cmd, core_dir_config *c, + const char *arg) +{ + const char *errmsg; + char *endp = strrchr(arg, '>'); + int old_overrides = cmd->override; + char *old_path = cmd->path; + core_dir_config *conf; + regex_t *r = NULL; + const char *old_end_token; + const command_rec *thiscmd = cmd->cmd; + + void *new_file_conf = ap_create_per_dir_config(cmd->pool); + + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_LOCATION); + if (err != NULL) { + return err; + } + + if (endp == NULL) { + return unclosed_directive(cmd); + } + + *endp = '\0'; + + cmd->path = ap_getword_conf(cmd->pool, &arg); + /* Only if not an .htaccess file */ + if (!old_path) { + cmd->override = OR_ALL|ACCESS_CONF; + } + + if (thiscmd->cmd_data) { /* */ + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE); + } + else if (!strcmp(cmd->path, "~")) { + cmd->path = ap_getword_conf(cmd->pool, &arg); + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE); + } + else { + /* Ensure that the pathname is canonical */ + cmd->path = ap_os_canonical_filename(cmd->pool, cmd->path); + } + + old_end_token = cmd->end_token; + cmd->end_token = thiscmd->cmd_data ? end_filesmatch_section : end_files_section; + errmsg = ap_srm_command_loop(cmd, new_file_conf); + if (errmsg == NULL) { + errmsg = missing_endsection(cmd, 1); + } + cmd->end_token = old_end_token; + if (errmsg != (thiscmd->cmd_data + ? end_filesmatch_section + : end_files_section)) { + return errmsg; + } + + conf = (core_dir_config *)ap_get_module_config(new_file_conf, + &core_module); + conf->d = cmd->path; + conf->d_is_fnmatch = ap_is_fnmatch(conf->d) != 0; + conf->r = r; + + ap_add_file_conf(c, new_file_conf); + + if (*arg != '\0') { + return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name, + "> arguments not (yet) supported.", NULL); + } + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +/* XXX: NB: Currently, we have no way of checking + * whether sections are closed properly. + * Extra (redundant, unpaired) directives are + * simply silently ignored. + */ +static const char *end_ifmod(cmd_parms *cmd, void *dummy) +{ + return NULL; +} + +static const char *start_ifmod(cmd_parms *cmd, void *dummy, char *arg) +{ + char *endp = strrchr(arg, '>'); + char l[MAX_STRING_LEN]; + int not = (arg[0] == '!'); + module *found; + int nest = 1; + + if (endp == NULL) { + return unclosed_directive(cmd); + } + + *endp = '\0'; + + if (not) { + arg++; + } + + found = ap_find_linked_module(arg); + + if ((!not && found) || (not && !found)) { + return NULL; + } + + while (nest && !(ap_cfg_getline(l, MAX_STRING_LEN, cmd->config_file))) { + if (!strncasecmp(l, "")) { + nest--; + } + } + + if (nest) { + cmd->end_token = end_ifmodule_section; + return missing_endsection(cmd, nest); + } + return NULL; +} + +API_EXPORT(int) ap_exists_config_define(char *name) +{ + char **defines; + int i; + + defines = (char **)ap_server_config_defines->elts; + for (i = 0; i < ap_server_config_defines->nelts; i++) { + if (strcmp(defines[i], name) == 0) { + return 1; + } + } + return 0; +} + +static const char *end_ifdefine(cmd_parms *cmd, void *dummy) +{ + return NULL; +} + +static const char *start_ifdefine(cmd_parms *cmd, void *dummy, char *arg) +{ + char *endp; + char l[MAX_STRING_LEN]; + int defined; + int not = 0; + int nest = 1; + + endp = strrchr(arg, '>'); + if (endp == NULL) { + return unclosed_directive(cmd); + } + + *endp = '\0'; + + if (arg[0] == '!') { + not = 1; + arg++; + } + + defined = ap_exists_config_define(arg); + + if ((!not && defined) || (not && !defined)) { + return NULL; + } + + while (nest && !(ap_cfg_getline(l, MAX_STRING_LEN, cmd->config_file))) { + if (!strncasecmp(l, "")) { + nest--; + } + } + if (nest) { + cmd->end_token = end_ifdefine_section; + return missing_endsection(cmd, nest); + } + return NULL; +} + +/* httpd.conf commands... beginning with the business */ + +static const char *virtualhost_section(cmd_parms *cmd, void *dummy, char *arg) +{ + server_rec *main_server = cmd->server, *s; + const char *errmsg; + char *endp = strrchr(arg, '>'); + pool *p = cmd->pool, *ptemp = cmd->temp_pool; + const char *old_end_token; + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (endp == NULL) { + return unclosed_directive(cmd); + } + + *endp = '\0'; + + /* FIXME: There's another feature waiting to happen here -- since you + can now put multiple addresses/names on a single + you might want to use it to group common definitions and then + define other "subhosts" with their individual differences. But + personally I'd rather just do it with a macro preprocessor. -djg */ + if (main_server->is_virtual) { + return " doesn't nest!"; + } + + errmsg = ap_init_virtual_host(p, arg, main_server, &s); + if (errmsg) { + return errmsg; + } + + s->next = main_server->next; + main_server->next = s; + + s->defn_name = cmd->config_file->name; + s->defn_line_number = cmd->config_file->line_number; + + old_end_token = cmd->end_token; + cmd->end_token = end_virtualhost_section; + cmd->server = s; + errmsg = ap_srm_command_loop(cmd, s->lookup_defaults); + cmd->server = main_server; + if (errmsg == NULL) { + errmsg = missing_endsection(cmd, 1); + } + cmd->end_token = old_end_token; + + if (s->srm_confname) { + ap_process_resource_config(s, s->srm_confname, p, ptemp); + } + + if (s->access_confname) { + ap_process_resource_config(s, s->access_confname, p, ptemp); + } + + if (errmsg == end_virtualhost_section) { + return NULL; + } + return errmsg; +} + +static const char *set_server_alias(cmd_parms *cmd, void *dummy, + const char *arg) +{ + if (!cmd->server->names) { + return "ServerAlias only used in "; + } + while (*arg) { + char **item, *name = ap_getword_conf(cmd->pool, &arg); + if (ap_is_matchexp(name)) { + item = (char **)ap_push_array(cmd->server->wild_names); + } + else { + item = (char **)ap_push_array(cmd->server->names); + } + *item = name; + } + return NULL; +} + +static const char *add_module_command(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (!ap_add_named_module(arg)) { + return ap_pstrcat(cmd->pool, "Cannot add module via name '", arg, + "': not in list of loaded modules", NULL); + } + return NULL; +} + +static const char *clear_module_list_command(cmd_parms *cmd, void *dummy) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_clear_module_list(); + return NULL; +} + +static const char *set_server_string_slot(cmd_parms *cmd, void *dummy, + char *arg) +{ + /* This one's pretty generic... */ + + int offset = (int)(long)cmd->info; + char *struct_ptr = (char *)cmd->server; + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + *(char **)(struct_ptr + offset) = arg; + return NULL; +} + +static const char *server_type(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (!strcasecmp(arg, "inetd")) { + ap_standalone = 0; + } + else if (!strcasecmp(arg, "standalone")) { + ap_standalone = 1; + } + else { + return "ServerType must be either 'inetd' or 'standalone'"; + } + + return NULL; +} + +static const char *server_port(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + int port; + + if (err != NULL) { + return err; + } + port = atoi(arg); + if (port <= 0 || port >= 65536) { /* 65536 == 1<<16 */ + return ap_pstrcat(cmd->temp_pool, "The port number \"", arg, + "\" is outside the appropriate range " + "(i.e., 1..65535).", NULL); + } + cmd->server->port = port; + return NULL; +} + +static const char *set_signature_flag(cmd_parms *cmd, core_dir_config *d, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (strcasecmp(arg, "On") == 0) { + d->server_signature = srv_sig_on; + } + else if (strcasecmp(arg, "Off") == 0) { + d->server_signature = srv_sig_off; + } + else if (strcasecmp(arg, "EMail") == 0) { + d->server_signature = srv_sig_withmail; + } + else { + return "ServerSignature: use one of: off | on | email"; + } + return NULL; +} + +static const char *set_send_buffer_size(cmd_parms *cmd, void *dummy, char *arg) +{ + int s = atoi(arg); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (s < 512 && s != 0) { + return "SendBufferSize must be >= 512 bytes, or 0 for system default."; + } + cmd->server->send_buffer_size = s; + return NULL; +} + +static const char *set_user(cmd_parms *cmd, void *dummy, char *arg) +{ +#ifdef WIN32 + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, cmd->server, + "User directive has no affect on Win32"); + cmd->server->server_uid = ap_user_id = 1; +#else + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (!cmd->server->is_virtual) { + ap_user_name = arg; + cmd->server->server_uid = ap_user_id = ap_uname2id(arg); + } + else { + if (ap_suexec_enabled) { + cmd->server->server_uid = ap_uname2id(arg); + } + else { + cmd->server->server_uid = ap_user_id; + fprintf(stderr, + "Warning: User directive in " + "requires SUEXEC wrapper.\n"); + } + } +#if !defined (BIG_SECURITY_HOLE) && !defined (OS2) + if (cmd->server->server_uid == 0) { + fprintf(stderr, + "Error:\tApache has not been designed to serve pages while\n" + "\trunning as root. There are known race conditions that\n" + "\twill allow any local user to read any file on the system.\n" + "\tIf you still desire to serve pages as root then\n" + "\tadd -DBIG_SECURITY_HOLE to the EXTRA_CFLAGS line in your\n" + "\tsrc/Configuration file and rebuild the server. It is\n" + "\tstrongly suggested that you instead modify the User\n" + "\tdirective in your httpd.conf file to list a non-root\n" + "\tuser.\n"); + exit (1); + } +#endif +#endif /* WIN32 */ + + return NULL; +} + +static const char *set_group(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (!cmd->server->is_virtual) { + cmd->server->server_gid = ap_group_id = ap_gname2id(arg); + } + else { + if (ap_suexec_enabled) { + cmd->server->server_gid = ap_gname2id(arg); + } + else { + cmd->server->server_gid = ap_group_id; + fprintf(stderr, + "Warning: Group directive in requires " + "SUEXEC wrapper.\n"); + } + } + + return NULL; +} + +static const char *set_server_root(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + arg = ap_os_canonical_filename(cmd->pool, arg); + + if (!ap_is_directory(arg)) { + return "ServerRoot must be a valid directory"; + } + ap_cpystrn(ap_server_root, arg, + sizeof(ap_server_root)); + return NULL; +} + +static const char *set_timeout(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + cmd->server->timeout = atoi(arg); + return NULL; +} + +static const char *set_keep_alive_timeout(cmd_parms *cmd, void *dummy, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + cmd->server->keep_alive_timeout = atoi(arg); + return NULL; +} + +static const char *set_keep_alive(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + /* We've changed it to On/Off, but used to use numbers + * so we accept anything but "Off" or "0" as "On" + */ + if (!strcasecmp(arg, "off") || !strcmp(arg, "0")) { + cmd->server->keep_alive = 0; + } + else { + cmd->server->keep_alive = 1; + } + return NULL; +} + +static const char *set_keep_alive_max(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + cmd->server->keep_alive_max = atoi(arg); + return NULL; +} + +static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (cmd->server->is_virtual) { + return "PidFile directive not allowed in "; + } + ap_pid_fname = arg; + return NULL; +} + +static const char *set_scoreboard(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_scoreboard_fname = arg; + return NULL; +} + +static const char *set_lockfile(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_lock_fname = arg; + return NULL; +} + +static const char *set_idcheck(cmd_parms *cmd, core_dir_config *d, int arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + d->do_rfc1413 = arg != 0; + return NULL; +} + +static const char *set_hostname_lookups(cmd_parms *cmd, core_dir_config *d, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (!strcasecmp(arg, "on")) { + d->hostname_lookups = HOSTNAME_LOOKUP_ON; + } + else if (!strcasecmp(arg, "off")) { + d->hostname_lookups = HOSTNAME_LOOKUP_OFF; + } + else if (!strcasecmp(arg, "double")) { + d->hostname_lookups = HOSTNAME_LOOKUP_DOUBLE; + } + else { + return "parameter must be 'on', 'off', or 'double'"; + } + return NULL; +} + +static const char *set_serverpath(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + cmd->server->path = arg; + cmd->server->pathlen = strlen(arg); + return NULL; +} + +static const char *set_content_md5(cmd_parms *cmd, core_dir_config *d, int arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + d->content_md5 = arg != 0; + return NULL; +} + +static const char *set_use_canonical_name(cmd_parms *cmd, core_dir_config *d, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (strcasecmp(arg, "on") == 0) { + d->use_canonical_name = USE_CANONICAL_NAME_ON; + } + else if (strcasecmp(arg, "off") == 0) { + d->use_canonical_name = USE_CANONICAL_NAME_OFF; + } + else if (strcasecmp(arg, "dns") == 0) { + d->use_canonical_name = USE_CANONICAL_NAME_DNS; + } + else { + return "parameter must be 'on', 'off', or 'dns'"; + } + return NULL; +} + +static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, char *arg) +{ +#ifdef WIN32 + fprintf(stderr, "WARNING: StartServers has no effect on Win32\n"); +#else + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_to_start = atoi(arg); +#endif + return NULL; +} + +static const char *set_min_free_servers(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_min_free = atoi(arg); + if (ap_daemons_min_free <= 0) { + fprintf(stderr, "WARNING: detected MinSpareServers set to non-positive.\n"); + fprintf(stderr, "Resetting to 1 to avoid almost certain Apache failure.\n"); + fprintf(stderr, "Please read the documentation.\n"); + ap_daemons_min_free = 1; + } + + return NULL; +} + +static const char *set_max_free_servers(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_max_free = atoi(arg); + return NULL; +} + +static const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_limit = atoi(arg); + if (ap_daemons_limit > HARD_SERVER_LIMIT) { + fprintf(stderr, "WARNING: MaxClients of %d exceeds compile time limit " + "of %d servers,\n", ap_daemons_limit, HARD_SERVER_LIMIT); + fprintf(stderr, " lowering MaxClients to %d. To increase, please " + "see the\n", HARD_SERVER_LIMIT); + fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n"); + ap_daemons_limit = HARD_SERVER_LIMIT; + } + else if (ap_daemons_limit < 1) { + fprintf(stderr, "WARNING: Require MaxClients > 0, setting to 1\n"); + ap_daemons_limit = 1; + } + return NULL; +} + +static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_max_requests_per_child = atoi(arg); + return NULL; +} + +static const char *set_threads(cmd_parms *cmd, void *dummy, char *arg) { + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_threads_per_child = atoi(arg); + if (ap_threads_per_child > HARD_SERVER_LIMIT) { + fprintf(stderr, "WARNING: ThreadsPerChild of %d exceeds compile time limit " + "of %d threads,\n", ap_threads_per_child, HARD_SERVER_LIMIT); + fprintf(stderr, " lowering ThreadsPerChild to %d. To increase, please " + "see the\n", HARD_SERVER_LIMIT); + fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n"); + ap_threads_per_child = HARD_SERVER_LIMIT; + } + else if (ap_threads_per_child < 1) { + fprintf(stderr, "WARNING: Require ThreadsPerChild > 0, setting to 1\n"); + ap_threads_per_child = 1; + } + + return NULL; +} + +static const char *set_excess_requests(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_excess_requests_per_child = atoi(arg); + return NULL; +} + + +#if defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS) +static void set_rlimit(cmd_parms *cmd, struct rlimit **plimit, const char *arg, + const char * arg2, int type) +{ + char *str; + struct rlimit *limit; + /* If your platform doesn't define rlim_t then typedef it in ap_config.h */ + rlim_t cur = 0; + rlim_t max = 0; + + *plimit = (struct rlimit *)ap_pcalloc(cmd->pool, sizeof(**plimit)); + limit = *plimit; + if ((getrlimit(type, limit)) != 0) { + *plimit = NULL; + ap_log_error(APLOG_MARK, APLOG_ERR, cmd->server, + "%s: getrlimit failed", cmd->cmd->name); + return; + } + + if ((str = ap_getword_conf(cmd->pool, &arg))) { + if (!strcasecmp(str, "max")) { + cur = limit->rlim_max; + } + else { + cur = atol(str); + } + } + else { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server, + "Invalid parameters for %s", cmd->cmd->name); + return; + } + + if (arg2 && (str = ap_getword_conf(cmd->pool, &arg2))) { + max = atol(str); + } + + /* if we aren't running as root, cannot increase max */ + if (geteuid()) { + limit->rlim_cur = cur; + if (max) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server, + "Must be uid 0 to raise maximum %s", cmd->cmd->name); + } + } + else { + if (cur) { + limit->rlim_cur = cur; + } + if (max) { + limit->rlim_max = max; + } + } +} +#endif + +#if !defined (RLIMIT_CPU) || !(defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)) || !defined (RLIMIT_NPROC) +static const char *no_set_limit(cmd_parms *cmd, core_dir_config *conf, + char *arg, char *arg2) +{ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server, + "%s not supported on this platform", cmd->cmd->name); + return NULL; +} +#endif + +#ifdef RLIMIT_CPU +static const char *set_limit_cpu(cmd_parms *cmd, core_dir_config *conf, + char *arg, char *arg2) +{ + set_rlimit(cmd, &conf->limit_cpu, arg, arg2, RLIMIT_CPU); + return NULL; +} +#endif + +#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS) +static const char *set_limit_mem(cmd_parms *cmd, core_dir_config *conf, + char *arg, char * arg2) +{ +#if defined(RLIMIT_AS) + set_rlimit(cmd, &conf->limit_mem, arg, arg2 ,RLIMIT_AS); +#elif defined(RLIMIT_DATA) + set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_DATA); +#elif defined(RLIMIT_VMEM) + set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_VMEM); +#endif + return NULL; +} +#endif + +#ifdef RLIMIT_NPROC +static const char *set_limit_nproc(cmd_parms *cmd, core_dir_config *conf, + char *arg, char * arg2) +{ + set_rlimit(cmd, &conf->limit_nproc, arg, arg2, RLIMIT_NPROC); + return NULL; +} +#endif + +static const char *set_bind_address(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_bind_address.s_addr = ap_get_virthost_addr(arg, NULL); + return NULL; +} + +static const char *set_listener(cmd_parms *cmd, void *dummy, char *ips) +{ + listen_rec *new; + char *ports; + unsigned short port; + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ports = strchr(ips, ':'); + if (ports != NULL) { + if (ports == ips) { + return "Missing IP address"; + } + else if (ports[1] == '\0') { + return "Address must end in :"; + } + *(ports++) = '\0'; + } + else { + ports = ips; + } + + new=ap_pcalloc(cmd->pool, sizeof(listen_rec)); + new->local_addr.sin_family = AF_INET; + if (ports == ips) { /* no address */ + new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + } + else { + new->local_addr.sin_addr.s_addr = ap_get_virthost_addr(ips, NULL); + } + port = atoi(ports); + if (!port) { + return "Port must be numeric"; + } + new->local_addr.sin_port = htons(port); + new->fd = -1; + new->used = 0; + new->next = ap_listeners; + ap_listeners = new; + return NULL; +} + +static const char *set_listenbacklog(cmd_parms *cmd, void *dummy, char *arg) +{ + int b; + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + b = atoi(arg); + if (b < 1) { + return "ListenBacklog must be > 0"; + } + ap_listenbacklog = b; + return NULL; +} + +static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg) +{ + struct stat finfo; + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + arg = ap_server_root_relative(cmd->pool, arg); + if ((stat(arg, &finfo) == -1) || !S_ISDIR(finfo.st_mode)) { + return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", arg, + " does not exist or is not a directory", NULL); + } + ap_cpystrn(ap_coredump_dir, arg, sizeof(ap_coredump_dir)); + return NULL; +} + +static const char *include_config (cmd_parms *cmd, void *dummy, char *name) +{ + name = ap_server_root_relative(cmd->pool, name); + + ap_process_resource_config(cmd->server, name, cmd->pool, cmd->temp_pool); + + return NULL; +} + +static const char *set_loglevel(cmd_parms *cmd, void *dummy, const char *arg) +{ + char *str; + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if ((str = ap_getword_conf(cmd->pool, &arg))) { + if (!strcasecmp(str, "emerg")) { + cmd->server->loglevel = APLOG_EMERG; + } + else if (!strcasecmp(str, "alert")) { + cmd->server->loglevel = APLOG_ALERT; + } + else if (!strcasecmp(str, "crit")) { + cmd->server->loglevel = APLOG_CRIT; + } + else if (!strcasecmp(str, "error")) { + cmd->server->loglevel = APLOG_ERR; + } + else if (!strcasecmp(str, "warn")) { + cmd->server->loglevel = APLOG_WARNING; + } + else if (!strcasecmp(str, "notice")) { + cmd->server->loglevel = APLOG_NOTICE; + } + else if (!strcasecmp(str, "info")) { + cmd->server->loglevel = APLOG_INFO; + } + else if (!strcasecmp(str, "debug")) { + cmd->server->loglevel = APLOG_DEBUG; + } + else { + return "LogLevel requires level keyword: one of " + "emerg/alert/crit/error/warn/notice/info/debug"; + } + } + else { + return "LogLevel requires level keyword"; + } + + return NULL; +} + +API_EXPORT(const char *) ap_psignature(const char *prefix, request_rec *r) +{ + char sport[20]; + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + if ((conf->server_signature == srv_sig_off) + || (conf->server_signature == srv_sig_unset)) { + return ""; + } + + ap_snprintf(sport, sizeof sport, "%u", (unsigned) ap_get_server_port(r)); + + if (conf->server_signature == srv_sig_withmail) { + return ap_pstrcat(r->pool, prefix, "
" SERVER_BASEVERSION + " Server at server->server_admin, "\">", + ap_get_server_name(r), " Port ", sport, + "
\n", NULL); + } + return ap_pstrcat(r->pool, prefix, "
" SERVER_BASEVERSION + " Server at ", ap_get_server_name(r), " Port ", sport, + "
\n", NULL); +} + +/* + * Load an authorisation realm into our location configuration, applying the + * usual rules that apply to realms. + */ +static const char *set_authname(cmd_parms *cmd, void *mconfig, char *word1) +{ + core_dir_config *aconfig = (core_dir_config *)mconfig; + + aconfig->ap_auth_name = ap_escape_quotes(cmd->pool, word1); + return NULL; +} + +#ifdef _OSD_POSIX /* BS2000 Logon Passwd file */ +static const char *set_bs2000_account(cmd_parms *cmd, void *dummy, char *name) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + return os_set_account(cmd->pool, name); +} +#endif /*_OSD_POSIX*/ + +/* + * Handle a request to include the server's OS platform in the Server + * response header field (the ServerTokens directive). Unfortunately + * this requires a new global in order to communicate the setting back to + * http_main so it can insert the information in the right place in the + * string. + */ + +static const char *set_serv_tokens(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (!strcasecmp(arg, "OS")) { + ap_server_tokens = SrvTk_OS; + } + else if (!strcasecmp(arg, "Min") || !strcasecmp(arg, "Minimal")) { + ap_server_tokens = SrvTk_MIN; + } + else { + ap_server_tokens = SrvTk_FULL; + } + return NULL; +} + +static const char *set_limit_req_line(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + int lim; + + if (err != NULL) { + return err; + } + lim = atoi(arg); + if (lim < 0) { + return ap_pstrcat(cmd->temp_pool, "LimitRequestLine \"", arg, + "\" must be a non-negative integer", NULL); + } + if (lim > DEFAULT_LIMIT_REQUEST_LINE) { + return ap_psprintf(cmd->temp_pool, "LimitRequestLine \"%s\" " + "must not exceed the precompiled maximum of %d", + arg, DEFAULT_LIMIT_REQUEST_LINE); + } + cmd->server->limit_req_line = lim; + return NULL; +} + +static const char *set_limit_req_fieldsize(cmd_parms *cmd, void *dummy, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + int lim; + + if (err != NULL) { + return err; + } + lim = atoi(arg); + if (lim < 0) { + return ap_pstrcat(cmd->temp_pool, "LimitRequestFieldsize \"", arg, + "\" must be a non-negative integer (0 = no limit)", + NULL); + } + if (lim > DEFAULT_LIMIT_REQUEST_FIELDSIZE) { + return ap_psprintf(cmd->temp_pool, "LimitRequestFieldsize \"%s\" " + "must not exceed the precompiled maximum of %d", + arg, DEFAULT_LIMIT_REQUEST_FIELDSIZE); + } + cmd->server->limit_req_fieldsize = lim; + return NULL; +} + +static const char *set_limit_req_fields(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + int lim; + + if (err != NULL) { + return err; + } + lim = atoi(arg); + if (lim < 0) { + return ap_pstrcat(cmd->temp_pool, "LimitRequestFields \"", arg, + "\" must be a non-negative integer (0 = no limit)", + NULL); + } + cmd->server->limit_req_fields = lim; + return NULL; +} + +static const char *set_limit_req_body(cmd_parms *cmd, core_dir_config *conf, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + /* WTF: If strtoul is not portable, then write a replacement. + * Instead we have an idiotic define in httpd.h that prevents + * it from being used even when it is available. Sheesh. + */ + conf->limit_req_body = (unsigned long)strtol(arg, (char **)NULL, 10); + return NULL; +} + +#ifdef WIN32 +static const char *set_interpreter_source(cmd_parms *cmd, core_dir_config *d, + char *arg) +{ + if (!strcasecmp(arg, "registry")) { + d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY; + } else if (!strcasecmp(arg, "script")) { + d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG; + } else { + d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG; + } + return NULL; +} +#endif + +/* Note --- ErrorDocument will now work from .htaccess files. + * The AllowOverride of Fileinfo allows webmasters to turn it off + */ + +static const command_rec core_cmds[] = { + +/* Old access config file commands */ + +{ "" }, +{ "" }, +{ "" }, +{ "" }, +{ "", endlimit_section, NULL, OR_ALL, NO_ARGS, + "Marks end of " }, +{ "", endlimit_section, (void*)1, OR_ALL, NO_ARGS, + "Marks end of " }, +{ "" }, +{ "" }, +{ "" }, +{ "" }, +{ "" }, +{ "AuthType", ap_set_string_slot, + (void*)XtOffsetOf(core_dir_config, ap_auth_type), OR_AUTHCFG, TAKE1, + "An HTTP authorization type (e.g., \"Basic\")" }, +{ "AuthName", set_authname, NULL, OR_AUTHCFG, TAKE1, + "The authentication realm (e.g. \"Members Only\")" }, +{ "Require", require, NULL, OR_AUTHCFG, RAW_ARGS, + "Selects which authenticated users or groups may access a protected space" }, +{ "Satisfy", satisfy, NULL, OR_AUTHCFG, TAKE1, + "access policy if both allow and require used ('all' or 'any')" }, +#ifdef GPROF +{ "GprofDir", set_gprof_dir, NULL, RSRC_CONF, TAKE1, + "Directory to plop gmon.out files" }, +#endif + +/* Old resource config file commands */ + +{ "AccessFileName", set_access_name, NULL, RSRC_CONF, RAW_ARGS, + "Name(s) of per-directory config files (default: .htaccess)" }, +{ "DocumentRoot", set_document_root, NULL, RSRC_CONF, TAKE1, + "Root directory of the document tree" }, +{ "ErrorDocument", set_error_document, NULL, OR_FILEINFO, RAW_ARGS, + "Change responses for HTTP errors" }, +{ "AllowOverride", set_override, NULL, ACCESS_CONF, RAW_ARGS, + "Controls what groups of directives can be configured by per-directory " + "config files" }, +{ "Options", set_options, NULL, OR_OPTIONS, RAW_ARGS, + "Set a number of attributes for a given directory" }, +{ "DefaultType", ap_set_string_slot, + (void*)XtOffsetOf (core_dir_config, ap_default_type), + OR_FILEINFO, TAKE1, "the default MIME type for untypable files" }, + +/* Old server config file commands */ + +{ "ServerType", server_type, NULL, RSRC_CONF, TAKE1, + "'inetd' or 'standalone'"}, +{ "Port", server_port, NULL, RSRC_CONF, TAKE1, "A TCP port number"}, +{ "HostnameLookups", set_hostname_lookups, NULL, ACCESS_CONF|RSRC_CONF, TAKE1, + "\"on\" to enable, \"off\" to disable reverse DNS lookups, or \"double\" to " + "enable double-reverse DNS lookups" }, +{ "User", set_user, NULL, RSRC_CONF, TAKE1, + "Effective user id for this server"}, +{ "Group", set_group, NULL, RSRC_CONF, TAKE1, + "Effective group id for this server"}, +{ "ServerAdmin", set_server_string_slot, + (void *)XtOffsetOf (server_rec, server_admin), RSRC_CONF, TAKE1, + "The email address of the server administrator" }, +{ "ServerName", set_server_string_slot, + (void *)XtOffsetOf (server_rec, server_hostname), RSRC_CONF, TAKE1, + "The hostname of the server" }, +{ "ServerSignature", set_signature_flag, NULL, OR_ALL, TAKE1, + "En-/disable server signature (on|off|email)" }, +{ "ServerRoot", set_server_root, NULL, RSRC_CONF, TAKE1, + "Common directory of server-related files (logs, confs, etc.)" }, +{ "ErrorLog", set_server_string_slot, + (void *)XtOffsetOf (server_rec, error_fname), RSRC_CONF, TAKE1, + "The filename of the error log" }, +{ "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1, + "A file for logging the server process ID"}, +{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1, + "A file for Apache to maintain runtime process management information"}, +{ "LockFile", set_lockfile, NULL, RSRC_CONF, TAKE1, + "The lockfile used when Apache needs to lock the accept() call"}, +{ "AccessConfig", set_server_string_slot, + (void *)XtOffsetOf (server_rec, access_confname), RSRC_CONF, TAKE1, + "The filename of the access config file" }, +{ "ResourceConfig", set_server_string_slot, + (void *)XtOffsetOf (server_rec, srm_confname), RSRC_CONF, TAKE1, + "The filename of the resource config file" }, +{ "ServerAlias", set_server_alias, NULL, RSRC_CONF, RAW_ARGS, + "A name or names alternately used to access the server" }, +{ "ServerPath", set_serverpath, NULL, RSRC_CONF, TAKE1, + "The pathname the server can be reached at" }, +{ "Timeout", set_timeout, NULL, RSRC_CONF, TAKE1, "Timeout duration (sec)" }, +{ "KeepAliveTimeout", set_keep_alive_timeout, NULL, RSRC_CONF, TAKE1, + "Keep-Alive timeout duration (sec)"}, +{ "MaxKeepAliveRequests", set_keep_alive_max, NULL, RSRC_CONF, TAKE1, + "Maximum number of Keep-Alive requests per connection, or 0 for infinite" }, +{ "KeepAlive", set_keep_alive, NULL, RSRC_CONF, TAKE1, + "Whether persistent connections should be On or Off" }, +{ "IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF, FLAG, + "Enable identd (RFC 1413) user lookups - SLOW" }, +{ "ContentDigest", set_content_md5, NULL, OR_OPTIONS, + FLAG, "whether or not to send a Content-MD5 header with each request" }, +{ "UseCanonicalName", set_use_canonical_name, NULL, + RSRC_CONF, TAKE1, + "How to work out the ServerName : Port when constructing URLs" }, +{ "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1, + "Number of child processes launched at server startup" }, +{ "MinSpareServers", set_min_free_servers, NULL, RSRC_CONF, TAKE1, + "Minimum number of idle children, to handle request spikes" }, +{ "MaxSpareServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, + "Maximum number of idle children" }, +{ "MaxServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, + "Deprecated equivalent to MaxSpareServers" }, +{ "ServersSafetyLimit", set_server_limit, NULL, RSRC_CONF, TAKE1, + "Deprecated equivalent to MaxClients" }, +{ "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1, + "Maximum number of children alive at the same time" }, +{ "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1, + "Maximum number of requests a particular child serves before dying." }, +{ "RLimitCPU", +#ifdef RLIMIT_CPU + set_limit_cpu, (void*)XtOffsetOf(core_dir_config, limit_cpu), +#else + no_set_limit, NULL, +#endif + OR_ALL, TAKE12, "Soft/hard limits for max CPU usage in seconds" }, +{ "RLimitMEM", +#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined (RLIMIT_AS) + set_limit_mem, (void*)XtOffsetOf(core_dir_config, limit_mem), +#else + no_set_limit, NULL, +#endif + OR_ALL, TAKE12, "Soft/hard limits for max memory usage per process" }, +{ "RLimitNPROC", +#ifdef RLIMIT_NPROC + set_limit_nproc, (void*)XtOffsetOf(core_dir_config, limit_nproc), +#else + no_set_limit, NULL, +#endif + OR_ALL, TAKE12, "soft/hard limits for max number of processes per uid" }, +{ "BindAddress", set_bind_address, NULL, RSRC_CONF, TAKE1, + "'*', a numeric IP address, or the name of a host with a unique IP address"}, +{ "Listen", set_listener, NULL, RSRC_CONF, TAKE1, + "A port number or a numeric IP address and a port number"}, +{ "SendBufferSize", set_send_buffer_size, NULL, RSRC_CONF, TAKE1, + "Send buffer size in bytes"}, +{ "AddModule", add_module_command, NULL, RSRC_CONF, ITERATE, + "The name of a module" }, +{ "ClearModuleList", clear_module_list_command, NULL, RSRC_CONF, NO_ARGS, + NULL }, +{ "ThreadsPerChild", set_threads, NULL, RSRC_CONF, TAKE1, + "Number of threads a child creates" }, +{ "ExcessRequestsPerChild", set_excess_requests, NULL, RSRC_CONF, TAKE1, + "Maximum number of requests a particular child serves after it is ready " + "to die." }, +{ "ListenBacklog", set_listenbacklog, NULL, RSRC_CONF, TAKE1, + "Maximum length of the queue of pending connections, as used by listen(2)" }, +{ "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1, + "The location of the directory Apache changes to before dumping core" }, +{ "Include", include_config, NULL, (RSRC_CONF | ACCESS_CONF), TAKE1, + "Name of the config file to be included" }, +{ "LogLevel", set_loglevel, NULL, RSRC_CONF, TAKE1, + "Level of verbosity in error logging" }, +{ "NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF, TAKE1, + "A numeric IP address:port, or the name of a host" }, +#ifdef _OSD_POSIX +{ "BS2000Account", set_bs2000_account, NULL, RSRC_CONF, TAKE1, + "Name of server User's bs2000 logon account name" }, +#endif +#ifdef WIN32 +{ "ScriptInterpreterSource", set_interpreter_source, NULL, OR_FILEINFO, TAKE1, + "Where to find interpreter to run Win32 scripts (Registry or script shebang line)" }, +#endif +{ "ServerTokens", set_serv_tokens, NULL, RSRC_CONF, TAKE1, + "Determine tokens displayed in the Server: header - Min(imal), OS or Full" }, +{ "LimitRequestLine", set_limit_req_line, NULL, RSRC_CONF, TAKE1, + "Limit on maximum size of an HTTP request line"}, +{ "LimitRequestFieldsize", set_limit_req_fieldsize, NULL, RSRC_CONF, TAKE1, + "Limit on maximum size of an HTTP request header field"}, +{ "LimitRequestFields", set_limit_req_fields, NULL, RSRC_CONF, TAKE1, + "Limit (0 = unlimited) on max number of header fields in a request message"}, +{ "LimitRequestBody", set_limit_req_body, + (void*)XtOffsetOf(core_dir_config, limit_req_body), + OR_ALL, TAKE1, + "Limit (in bytes) on maximum size of request message body" }, +{ NULL } +}; + +/***************************************************************** + * + * Core handlers for various phases of server operation... + */ + +static int core_translate(request_rec *r) +{ + void *sconf = r->server->module_config; + core_server_config *conf = ap_get_module_config(sconf, &core_module); + + if (r->proxyreq) { + return HTTP_FORBIDDEN; + } + if ((r->uri[0] != '/') && strcmp(r->uri, "*")) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid URI in request %s", r->the_request); + return BAD_REQUEST; + } + + if (r->server->path + && !strncmp(r->uri, r->server->path, r->server->pathlen) + && (r->server->path[r->server->pathlen - 1] == '/' + || r->uri[r->server->pathlen] == '/' + || r->uri[r->server->pathlen] == '\0')) { + r->filename = ap_pstrcat(r->pool, conf->ap_document_root, + (r->uri + r->server->pathlen), NULL); + } + else { + /* + * Make sure that we do not mess up the translation by adding two + * /'s in a row. This happens under windows when the document + * root ends with a / + */ + if ((conf->ap_document_root[strlen(conf->ap_document_root)-1] == '/') + && (*(r->uri) == '/')) { + r->filename = ap_pstrcat(r->pool, conf->ap_document_root, r->uri+1, + NULL); + } + else { + r->filename = ap_pstrcat(r->pool, conf->ap_document_root, r->uri, + NULL); + } + } + + return OK; +} + +static int do_nothing(request_rec *r) { return OK; } + +#ifdef USE_MMAP_FILES +struct mmap_rec { + void *mm; + size_t length; +}; + +static void mmap_cleanup(void *mmv) +{ + struct mmap_rec *mmd = mmv; + + if (munmap(mmd->mm, mmd->length) == -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, NULL, + "Failed to munmap memory of length %ld at 0x%lx", + (long) mmd->length, (long) mmd->mm); + } +} +#endif + +/* + * Default handler for MIME types without other handlers. Only GET + * and OPTIONS at this point... anyone who wants to write a generic + * handler for PUT or POST is free to do so, but it seems unwise to provide + * any defaults yet... So, for now, we assume that this will always be + * the last handler called and return 405 or 501. + */ + +static int default_handler(request_rec *r) +{ + core_dir_config *d = + (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); + int rangestatus, errstatus; + FILE *f; +#ifdef USE_MMAP_FILES + caddr_t mm; +#endif +#ifdef CHARSET_EBCDIC + int convert_flag; +#endif + + /* This handler has no use for a request body (yet), but we still + * need to read and discard it if the client sent one. + */ + if ((errstatus = ap_discard_request_body(r)) != OK) { + return errstatus; + } + + r->allowed |= (1 << M_GET) | (1 << M_OPTIONS); + + if (r->method_number == M_INVALID) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid method in request %s", r->the_request); + return NOT_IMPLEMENTED; + } + if (r->method_number == M_OPTIONS) { + return ap_send_http_options(r); + } + if (r->method_number == M_PUT) { + return METHOD_NOT_ALLOWED; + } + + if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) { + char *emsg; + + emsg = "File does not exist: "; + if (r->path_info == NULL) { + emsg = ap_pstrcat(r->pool, emsg, r->filename, NULL); + } + else { + emsg = ap_pstrcat(r->pool, emsg, r->filename, r->path_info, NULL); + } + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, "%s", emsg); + return HTTP_NOT_FOUND; + } + if (r->method_number != M_GET) { + return METHOD_NOT_ALLOWED; + } + +#if defined(OS2) || defined(WIN32) + /* Need binary mode for OS/2 */ + f = ap_pfopen(r->pool, r->filename, "rb"); +#else + f = ap_pfopen(r->pool, r->filename, "r"); +#endif + + if (f == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "file permissions deny server access: %s", r->filename); + return FORBIDDEN; + } + + ap_update_mtime(r, r->finfo.st_mtime); + ap_set_last_modified(r); + ap_set_etag(r); + ap_table_setn(r->headers_out, "Accept-Ranges", "bytes"); + if (((errstatus = ap_meets_conditions(r)) != OK) + || (errstatus = ap_set_content_length(r, r->finfo.st_size))) { + return errstatus; + } + +#ifdef USE_MMAP_FILES + ap_block_alarms(); + if ((r->finfo.st_size >= MMAP_THRESHOLD) + && (r->finfo.st_size < MMAP_LIMIT) + && (!r->header_only || (d->content_md5 & 1))) { + /* we need to protect ourselves in case we die while we've got the + * file mmapped */ + mm = mmap(NULL, r->finfo.st_size, PROT_READ, MAP_PRIVATE, + fileno(f), 0); + if (mm == (caddr_t)-1) { + ap_log_rerror(APLOG_MARK, APLOG_CRIT, r, + "default_handler: mmap failed: %s", r->filename); + } + } + else { + mm = (caddr_t)-1; + } + + if (mm == (caddr_t)-1) { + ap_unblock_alarms(); +#endif + +#ifdef CHARSET_EBCDIC + /* To make serving of "raw ASCII text" files easy (they serve faster + * since they don't have to be converted from EBCDIC), a new + * "magic" type prefix was invented: text/x-ascii-{plain,html,...} + * If we detect one of these content types here, we simply correct + * the type to the real text/{plain,html,...} type. Otherwise, we + * set a flag that translation is required later on. + */ + convert_flag = ap_checkconv(r); + if (d->content_md5 & 1) { + ap_table_setn(r->headers_out, "Content-MD5", + ap_md5digest(r->pool, f, convert_flag)); + } +#else + if (d->content_md5 & 1) { + ap_table_setn(r->headers_out, "Content-MD5", + ap_md5digest(r->pool, f)); + } +#endif /* CHARSET_EBCDIC */ + + rangestatus = ap_set_byterange(r); + + ap_send_http_header(r); + + if (!r->header_only) { + if (!rangestatus) { + ap_send_fd(f, r); + } + else { + long offset, length; + while (ap_each_byterange(r, &offset, &length)) { + /* + * Non zero returns are more portable than checking + * for a return of -1. + */ + if (fseek(f, offset, SEEK_SET)) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "Failed to fseek for byterange (%ld, %ld)", + offset, length); + } + else { + ap_send_fd_length(f, r, length); + } + } + } + } + +#ifdef USE_MMAP_FILES + } + else { + struct mmap_rec *mmd; + + mmd = ap_palloc(r->pool, sizeof(*mmd)); + mmd->mm = mm; + mmd->length = r->finfo.st_size; + ap_register_cleanup(r->pool, (void *)mmd, mmap_cleanup, mmap_cleanup); + ap_unblock_alarms(); + + if (d->content_md5 & 1) { + AP_MD5_CTX context; + + ap_MD5Init(&context); + ap_MD5Update(&context, (void *)mm, (unsigned int)r->finfo.st_size); + ap_table_setn(r->headers_out, "Content-MD5", + ap_md5contextTo64(r->pool, &context)); + } + + rangestatus = ap_set_byterange(r); + ap_send_http_header(r); + + if (!r->header_only) { + if (!rangestatus) { + ap_send_mmap(mm, r, 0, r->finfo.st_size); + } + else { + long offset, length; + while (ap_each_byterange(r, &offset, &length)) { + ap_send_mmap(mm, r, offset, length); + } + } + } + } +#endif + + ap_pfclose(r->pool, f); + return OK; +} + +static const handler_rec core_handlers[] = { +{ "*/*", default_handler }, +{ "default-handler", default_handler }, +{ NULL, NULL } +}; + +API_VAR_EXPORT module core_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_core_dir_config, /* create per-directory config structure */ + merge_core_dir_configs, /* merge per-directory config structures */ + create_core_server_config, /* create per-server config structure */ + merge_core_server_configs, /* merge per-server config structures */ + core_cmds, /* command table */ + core_handlers, /* handlers */ + core_translate, /* translate_handler */ + NULL, /* check_user_id */ + NULL, /* check auth */ + do_nothing, /* check access */ + do_nothing, /* type_checker */ + NULL, /* pre-run fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post_read_request */ +}; diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c new file mode 100644 index 00000000000..31779312c65 --- /dev/null +++ b/modules/http/http_protocol.c @@ -0,0 +1,2763 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_protocol.c --- routines which directly communicate with the client. + * + * Code originally by Rob McCool; much redone by Robert S. Thau + * and the Apache Group. + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_request.h" +#include "http_vhost.h" +#include "http_log.h" /* For errors detected in basic auth common + * support code... */ +#include "util_date.h" /* For parseHTTPdate and BAD_DATE */ +#include +#include "http_conf_globals.h" + +#define SET_BYTES_SENT(r) \ + do { if (r->sent_bodyct) \ + ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \ + } while (0) + + +static int parse_byterange(char *range, long clength, long *start, long *end) +{ + char *dash = strchr(range, '-'); + + if (!dash) + return 0; + + if ((dash == range)) { + /* In the form "-5" */ + *start = clength - atol(dash + 1); + *end = clength - 1; + } + else { + *dash = '\0'; + dash++; + *start = atol(range); + if (*dash) + *end = atol(dash); + else /* "5-" */ + *end = clength - 1; + } + + if (*start < 0) + *start = 0; + + if (*end >= clength) + *end = clength - 1; + + if (*start > *end) + return 0; + + return (*start > 0 || *end < clength - 1); +} + +static int internal_byterange(int, long *, request_rec *, const char **, long *, + long *); + +API_EXPORT(int) ap_set_byterange(request_rec *r) +{ + const char *range, *if_range, *match; + long range_start, range_end; + + if (!r->clength || r->assbackwards) + return 0; + + /* Check for Range request-header (HTTP/1.1) or Request-Range for + * backwards-compatibility with second-draft Luotonen/Franks + * byte-ranges (e.g. Netscape Navigator 2-3). + * + * We support this form, with Request-Range, and (farther down) we + * send multipart/x-byteranges instead of multipart/byteranges for + * Request-Range based requests to work around a bug in Netscape + * Navigator 2-3 and MSIE 3. + */ + + if (!(range = ap_table_get(r->headers_in, "Range"))) + range = ap_table_get(r->headers_in, "Request-Range"); + + if (!range || strncasecmp(range, "bytes=", 6)) { + return 0; + } + + /* Check the If-Range header for Etag or Date. + * Note that this check will return false (as required) if either + * of the two etags are weak. + */ + if ((if_range = ap_table_get(r->headers_in, "If-Range"))) { + if (if_range[0] == '"') { + if (!(match = ap_table_get(r->headers_out, "Etag")) || + (strcmp(if_range, match) != 0)) + return 0; + } + else if (!(match = ap_table_get(r->headers_out, "Last-Modified")) || + (strcmp(if_range, match) != 0)) + return 0; + } + + if (!strchr(range, ',')) { + /* A single range */ + if (!parse_byterange(ap_pstrdup(r->pool, range + 6), r->clength, + &range_start, &range_end)) + return 0; + + r->byterange = 1; + + ap_table_setn(r->headers_out, "Content-Range", + ap_psprintf(r->pool, "bytes %ld-%ld/%ld", + range_start, range_end, r->clength)); + ap_table_setn(r->headers_out, "Content-Length", + ap_psprintf(r->pool, "%ld", range_end - range_start + 1)); + } + else { + /* a multiple range */ + const char *r_range = ap_pstrdup(r->pool, range + 6); + long tlength = 0; + + r->byterange = 2; + r->boundary = ap_psprintf(r->pool, "%lx%lx", + r->request_time, (long) getpid()); + while (internal_byterange(0, &tlength, r, &r_range, NULL, NULL)); + ap_table_setn(r->headers_out, "Content-Length", + ap_psprintf(r->pool, "%ld", tlength)); + } + + r->status = PARTIAL_CONTENT; + r->range = range + 6; + + return 1; +} + +API_EXPORT(int) ap_each_byterange(request_rec *r, long *offset, long *length) +{ + return internal_byterange(1, NULL, r, &r->range, offset, length); +} + +/* If this function is called with realreq=1, it will spit out + * the correct headers for a byterange chunk, and set offset and + * length to the positions they should be. + * + * If it is called with realreq=0, it will add to tlength the length + * it *would* have used with realreq=1. + * + * Either case will return 1 if it should be called again, and 0 + * when done. + */ +static int internal_byterange(int realreq, long *tlength, request_rec *r, + const char **r_range, long *offset, long *length) +{ + long range_start, range_end; + char *range; + + if (!**r_range) { + if (r->byterange > 1) { + if (realreq) + ap_rvputs(r, "\015\012--", r->boundary, "--\015\012", NULL); + else + *tlength += 4 + strlen(r->boundary) + 4; + } + return 0; + } + + range = ap_getword(r->pool, r_range, ','); + if (!parse_byterange(range, r->clength, &range_start, &range_end)) + /* Skip this one */ + return internal_byterange(realreq, tlength, r, r_range, offset, + length); + + if (r->byterange > 1) { + const char *ct = r->content_type ? r->content_type : ap_default_type(r); + char ts[MAX_STRING_LEN]; + + ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end, + r->clength); + if (realreq) + ap_rvputs(r, "\015\012--", r->boundary, "\015\012Content-type: ", + ct, "\015\012Content-range: bytes ", ts, "\015\012\015\012", + NULL); + else + *tlength += 4 + strlen(r->boundary) + 16 + strlen(ct) + 23 + + strlen(ts) + 4; + } + + if (realreq) { + *offset = range_start; + *length = range_end - range_start + 1; + } + else { + *tlength += range_end - range_start + 1; + } + return 1; +} + +API_EXPORT(int) ap_set_content_length(request_rec *r, long clength) +{ + r->clength = clength; + ap_table_setn(r->headers_out, "Content-Length", ap_psprintf(r->pool, "%ld", clength)); + return 0; +} + +API_EXPORT(int) ap_set_keepalive(request_rec *r) +{ + int ka_sent = 0; + int wimpy = ap_find_token(r->pool, + ap_table_get(r->headers_out, "Connection"), "close"); + const char *conn = ap_table_get(r->headers_in, "Connection"); + + /* The following convoluted conditional determines whether or not + * the current connection should remain persistent after this response + * (a.k.a. HTTP Keep-Alive) and whether or not the output message + * body should use the HTTP/1.1 chunked transfer-coding. In English, + * + * IF we have not marked this connection as errored; + * and the response body has a defined length due to the status code + * being 304 or 204, the request method being HEAD, already + * having defined Content-Length or Transfer-Encoding: chunked, or + * the request version being HTTP/1.1 and thus capable of being set + * as chunked [we know the (r->chunked = 1) side-effect is ugly]; + * and the server configuration enables keep-alive; + * and the server configuration has a reasonable inter-request timeout; + * and there is no maximum # requests or the max hasn't been reached; + * and the response status does not require a close; + * and the response generator has not already indicated close; + * and the client did not request non-persistence (Connection: close); + * and we haven't been configured to ignore the buggy twit + * or they're a buggy twit coming through a HTTP/1.1 proxy + * and the client is requesting an HTTP/1.0-style keep-alive + * or the client claims to be HTTP/1.1 compliant (perhaps a proxy); + * THEN we can be persistent, which requires more headers be output. + * + * Note that the condition evaluation order is extremely important. + */ + if ((r->connection->keepalive != -1) && + ((r->status == HTTP_NOT_MODIFIED) || + (r->status == HTTP_NO_CONTENT) || + r->header_only || + ap_table_get(r->headers_out, "Content-Length") || + ap_find_last_token(r->pool, + ap_table_get(r->headers_out, "Transfer-Encoding"), + "chunked") || + ((r->proto_num >= HTTP_VERSION(1,1)) && + (r->chunked = 1))) && /* THIS CODE IS CORRECT, see comment above. */ + r->server->keep_alive && + (r->server->keep_alive_timeout > 0) && + ((r->server->keep_alive_max == 0) || + (r->server->keep_alive_max > r->connection->keepalives)) && + !ap_status_drops_connection(r->status) && + !wimpy && + !ap_find_token(r->pool, conn, "close") && + (!ap_table_get(r->subprocess_env, "nokeepalive") || + ap_table_get(r->headers_in, "Via")) && + ((ka_sent = ap_find_token(r->pool, conn, "keep-alive")) || + (r->proto_num >= HTTP_VERSION(1,1))) + ) { + int left = r->server->keep_alive_max - r->connection->keepalives; + + r->connection->keepalive = 1; + r->connection->keepalives++; + + /* If they sent a Keep-Alive token, send one back */ + if (ka_sent) { + if (r->server->keep_alive_max) + ap_table_setn(r->headers_out, "Keep-Alive", + ap_psprintf(r->pool, "timeout=%d, max=%d", + r->server->keep_alive_timeout, left)); + else + ap_table_setn(r->headers_out, "Keep-Alive", + ap_psprintf(r->pool, "timeout=%d", + r->server->keep_alive_timeout)); + ap_table_mergen(r->headers_out, "Connection", "Keep-Alive"); + } + + return 1; + } + + /* Otherwise, we need to indicate that we will be closing this + * connection immediately after the current response. + * + * We only really need to send "close" to HTTP/1.1 clients, but we + * always send it anyway, because a broken proxy may identify itself + * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag + * to a HTTP/1.1 client. Better safe than sorry. + */ + if (!wimpy) + ap_table_mergen(r->headers_out, "Connection", "close"); + + r->connection->keepalive = 0; + + return 0; +} + +/* + * Return the latest rational time from a request/mtime (modification time) + * pair. We return the mtime unless it's in the future, in which case we + * return the current time. We use the request time as a reference in order + * to limit the number of calls to time(). We don't check for futurosity + * unless the mtime is at least as new as the reference. + */ +API_EXPORT(time_t) ap_rationalize_mtime(request_rec *r, time_t mtime) +{ + time_t now; + + /* For all static responses, it's almost certain that the file was + * last modified before the beginning of the request. So there's + * no reason to call time(NULL) again. But if the response has been + * created on demand, then it might be newer than the time the request + * started. In this event we really have to call time(NULL) again + * so that we can give the clients the most accurate Last-Modified. If we + * were given a time in the future, we return the current time - the + * Last-Modified can't be in the future. + */ + now = (mtime < r->request_time) ? r->request_time : time(NULL); + return (mtime > now) ? now : mtime; +} + +API_EXPORT(int) ap_meets_conditions(request_rec *r) +{ + const char *etag = ap_table_get(r->headers_out, "ETag"); + const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch; + time_t mtime; + + /* Check for conditional requests --- note that we only want to do + * this if we are successful so far and we are not processing a + * subrequest or an ErrorDocument. + * + * The order of the checks is important, since ETag checks are supposed + * to be more accurate than checks relative to the modification time. + * However, not all documents are guaranteed to *have* ETags, and some + * might have Last-Modified values w/o ETags, so this gets a little + * complicated. + */ + + if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) { + return OK; + } + + mtime = (r->mtime != 0) ? r->mtime : time(NULL); + + /* If an If-Match request-header field was given + * AND the field value is not "*" (meaning match anything) + * AND if our strong ETag does not match any entity tag in that field, + * respond with a status of 412 (Precondition Failed). + */ + if ((if_match = ap_table_get(r->headers_in, "If-Match")) != NULL) { + if (if_match[0] != '*' && + (etag == NULL || etag[0] == 'W' || + !ap_find_list_item(r->pool, if_match, etag))) { + return HTTP_PRECONDITION_FAILED; + } + } + else { + /* Else if a valid If-Unmodified-Since request-header field was given + * AND the requested resource has been modified since the time + * specified in this field, then the server MUST + * respond with a status of 412 (Precondition Failed). + */ + if_unmodified = ap_table_get(r->headers_in, "If-Unmodified-Since"); + if (if_unmodified != NULL) { + time_t ius = ap_parseHTTPdate(if_unmodified); + + if ((ius != BAD_DATE) && (mtime > ius)) { + return HTTP_PRECONDITION_FAILED; + } + } + } + + /* If an If-None-Match request-header field was given + * AND the field value is "*" (meaning match anything) + * OR our ETag matches any of the entity tags in that field, fail. + * + * If the request method was GET or HEAD, failure means the server + * SHOULD respond with a 304 (Not Modified) response. + * For all other request methods, failure means the server MUST + * respond with a status of 412 (Precondition Failed). + * + * GET or HEAD allow weak etag comparison, all other methods require + * strong comparison. We can only use weak if it's not a range request. + */ + if_nonematch = ap_table_get(r->headers_in, "If-None-Match"); + if (if_nonematch != NULL) { + if (r->method_number == M_GET) { + if (if_nonematch[0] == '*') + return HTTP_NOT_MODIFIED; + if (etag != NULL) { + if (ap_table_get(r->headers_in, "Range")) { + if (etag[0] != 'W' && + ap_find_list_item(r->pool, if_nonematch, etag)) { + return HTTP_NOT_MODIFIED; + } + } + else if (strstr(if_nonematch, etag)) { + return HTTP_NOT_MODIFIED; + } + } + } + else if (if_nonematch[0] == '*' || + (etag != NULL && + ap_find_list_item(r->pool, if_nonematch, etag))) { + return HTTP_PRECONDITION_FAILED; + } + } + /* Else if a valid If-Modified-Since request-header field was given + * AND it is a GET or HEAD request + * AND the requested resource has not been modified since the time + * specified in this field, then the server MUST + * respond with a status of 304 (Not Modified). + * A date later than the server's current request time is invalid. + */ + else if ((r->method_number == M_GET) + && ((if_modified_since = + ap_table_get(r->headers_in, "If-Modified-Since")) != NULL)) { + time_t ims = ap_parseHTTPdate(if_modified_since); + + if ((ims >= mtime) && (ims <= r->request_time)) { + return HTTP_NOT_MODIFIED; + } + } + return OK; +} + +/* + * Construct an entity tag (ETag) from resource information. If it's a real + * file, build in some of the file characteristics. If the modification time + * is newer than (request-time minus 1 second), mark the ETag as weak - it + * could be modified again in as short an interval. We rationalize the + * modification time we're given to keep it from being in the future. + */ +API_EXPORT(char *) ap_make_etag(request_rec *r, int force_weak) +{ + char *etag; + char *weak; + + /* + * Make an ETag header out of various pieces of information. We use + * the last-modified date and, if we have a real file, the + * length and inode number - note that this doesn't have to match + * the content-length (i.e. includes), it just has to be unique + * for the file. + * + * If the request was made within a second of the last-modified date, + * we send a weak tag instead of a strong one, since it could + * be modified again later in the second, and the validation + * would be incorrect. + */ + + weak = ((r->request_time - r->mtime > 1) && !force_weak) ? "" : "W/"; + + if (r->finfo.st_mode != 0) { + etag = ap_psprintf(r->pool, + "%s\"%lx-%lx-%lx\"", weak, + (unsigned long) r->finfo.st_ino, + (unsigned long) r->finfo.st_size, + (unsigned long) r->mtime); + } + else { + etag = ap_psprintf(r->pool, "%s\"%lx\"", weak, + (unsigned long) r->mtime); + } + + return etag; +} + +API_EXPORT(void) ap_set_etag(request_rec *r) +{ + char *etag; + char *variant_etag, *vlv; + int vlv_weak; + + if (!r->vlist_validator) { + etag = ap_make_etag(r, 0); + } + else { + /* If we have a variant list validator (vlv) due to the + * response being negotiated, then we create a structured + * entity tag which merges the variant etag with the variant + * list validator (vlv). This merging makes revalidation + * somewhat safer, ensures that caches which can deal with + * Vary will (eventually) be updated if the set of variants is + * changed, and is also a protocol requirement for transparent + * content negotiation. + */ + + /* if the variant list validator is weak, we make the whole + * structured etag weak. If we would not, then clients could + * have problems merging range responses if we have different + * variants with the same non-globally-unique strong etag. + */ + + vlv = r->vlist_validator; + vlv_weak = (vlv[0] == 'W'); + + variant_etag = ap_make_etag(r, vlv_weak); + + /* merge variant_etag and vlv into a structured etag */ + + variant_etag[strlen(variant_etag) - 1] = '\0'; + if (vlv_weak) + vlv += 3; + else + vlv++; + etag = ap_pstrcat(r->pool, variant_etag, ";", vlv, NULL); + } + + ap_table_setn(r->headers_out, "ETag", etag); +} + +/* + * This function sets the Last-Modified output header field to the value + * of the mtime field in the request structure - rationalized to keep it from + * being in the future. + */ +API_EXPORT(void) ap_set_last_modified(request_rec *r) +{ + time_t mod_time = ap_rationalize_mtime(r, r->mtime); + + ap_table_setn(r->headers_out, "Last-Modified", + ap_gm_timestr_822(r->pool, mod_time)); +} + +/* Get the method number associated with the given string, assumed to + * contain an HTTP method. Returns M_INVALID if not recognized. + * + * This is the first step toward placing method names in a configurable + * list. Hopefully it (and other routines) can eventually be moved to + * something like a mod_http_methods.c, complete with config stuff. + */ +API_EXPORT(int) ap_method_number_of(const char *method) +{ + switch (*method) { + case 'H': + if (strcmp(method, "HEAD") == 0) + return M_GET; /* see header_only in request_rec */ + break; + case 'G': + if (strcmp(method, "GET") == 0) + return M_GET; + break; + case 'P': + if (strcmp(method, "POST") == 0) + return M_POST; + if (strcmp(method, "PUT") == 0) + return M_PUT; + if (strcmp(method, "PATCH") == 0) + return M_PATCH; + if (strcmp(method, "PROPFIND") == 0) + return M_PROPFIND; + if (strcmp(method, "PROPPATCH") == 0) + return M_PROPPATCH; + break; + case 'D': + if (strcmp(method, "DELETE") == 0) + return M_DELETE; + break; + case 'C': + if (strcmp(method, "CONNECT") == 0) + return M_CONNECT; + if (strcmp(method, "COPY") == 0) + return M_COPY; + break; + case 'M': + if (strcmp(method, "MKCOL") == 0) + return M_MKCOL; + if (strcmp(method, "MOVE") == 0) + return M_MOVE; + break; + case 'O': + if (strcmp(method, "OPTIONS") == 0) + return M_OPTIONS; + break; + case 'T': + if (strcmp(method, "TRACE") == 0) + return M_TRACE; + break; + case 'L': + if (strcmp(method, "LOCK") == 0) + return M_LOCK; + break; + case 'U': + if (strcmp(method, "UNLOCK") == 0) + return M_UNLOCK; + break; + } + return M_INVALID; +} + +/* Get a line of protocol input, including any continuation lines + * caused by MIME folding (or broken clients) if fold != 0, and place it + * in the buffer s, of size n bytes, without the ending newline. + * + * Returns -1 on error, or the length of s. + * + * Note: Because bgets uses 1 char for newline and 1 char for NUL, + * the most we can get is (n - 2) actual characters if it + * was ended by a newline, or (n - 1) characters if the line + * length exceeded (n - 1). So, if the result == (n - 1), + * then the actual input line exceeded the buffer length, + * and it would be a good idea for the caller to puke 400 or 414. + */ +static int getline(char *s, int n, BUFF *in, int fold) +{ + char *pos, next; + int retval; + int total = 0; + + pos = s; + + do { + retval = ap_bgets(pos, n, in); /* retval == -1 if error, 0 if EOF */ + + if (retval <= 0) + return ((retval < 0) && (total == 0)) ? -1 : total; + + /* retval is the number of characters read, not including NUL */ + + n -= retval; /* Keep track of how much of s is full */ + pos += (retval - 1); /* and where s ends */ + total += retval; /* and how long s has become */ + + if (*pos == '\n') { /* Did we get a full line of input? */ + /* + * Trim any extra trailing spaces or tabs except for the first + * space or tab at the beginning of a blank string. This makes + * it much easier to check field values for exact matches, and + * saves memory as well. Terminate string at end of line. + */ + while (pos > (s + 1) && (*(pos - 1) == ' ' || *(pos - 1) == '\t')) { + --pos; /* trim extra trailing spaces or tabs */ + --total; /* but not one at the beginning of line */ + ++n; + } + *pos = '\0'; + --total; + ++n; + } + else + return total; /* if not, input line exceeded buffer size */ + + /* Continue appending if line folding is desired and + * the last line was not empty and we have room in the buffer and + * the next line begins with a continuation character. + */ + } while (fold && (retval != 1) && (n > 1) + && (ap_blookc(&next, in) == 1) + && ((next == ' ') || (next == '\t'))); + + return total; +} + +/* parse_uri: break apart the uri + * Side Effects: + * - sets r->args to rest after '?' (or NULL if no '?') + * - sets r->uri to request uri (without r->args part) + * - sets r->hostname (if not set already) from request (scheme://host:port) + */ +CORE_EXPORT(void) ap_parse_uri(request_rec *r, const char *uri) +{ + int status = HTTP_OK; + + r->unparsed_uri = ap_pstrdup(r->pool, uri); + + if (r->method_number == M_CONNECT) { + status = ap_parse_hostinfo_components(r->pool, uri, &r->parsed_uri); + } else { + /* Simple syntax Errors in URLs are trapped by parse_uri_components(). */ + status = ap_parse_uri_components(r->pool, uri, &r->parsed_uri); + } + + if (ap_is_HTTP_SUCCESS(status)) { + /* if it has a scheme we may need to do absoluteURI vhost stuff */ + if (r->parsed_uri.scheme + && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))) { + r->hostname = r->parsed_uri.hostname; + } else if (r->method_number == M_CONNECT) { + r->hostname = r->parsed_uri.hostname; + } + r->args = r->parsed_uri.query; + r->uri = r->parsed_uri.path ? r->parsed_uri.path + : ap_pstrdup(r->pool, "/"); +#if defined(OS2) || defined(WIN32) + /* Handle path translations for OS/2 and plug security hole. + * This will prevent "http://www.wherever.com/..\..\/" from + * returning a directory for the root drive. + */ + { + char *x; + + for (x = r->uri; (x = strchr(x, '\\')) != NULL; ) + *x = '/'; + } +#endif /* OS2 || WIN32 */ + } + else { + r->args = NULL; + r->hostname = NULL; + r->status = status; /* set error status */ + r->uri = ap_pstrdup(r->pool, uri); + } +} + +static int read_request_line(request_rec *r) +{ + char l[DEFAULT_LIMIT_REQUEST_LINE + 2]; /* getline's two extra for \n\0 */ + const char *ll = l; + const char *uri; + conn_rec *conn = r->connection; + int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */ + int len; + + /* Read past empty lines until we get a real request line, + * a read error, the connection closes (EOF), or we timeout. + * + * We skip empty lines because browsers have to tack a CRLF on to the end + * of POSTs to support old CERN webservers. But note that we may not + * have flushed any previous response completely to the client yet. + * We delay the flush as long as possible so that we can improve + * performance for clients that are pipelining requests. If a request + * is pipelined then we won't block during the (implicit) read() below. + * If the requests aren't pipelined, then the client is still waiting + * for the final buffer flush from us, and we will block in the implicit + * read(). B_SAFEREAD ensures that the BUFF layer flushes if it will + * have to block during a read. + */ + ap_bsetflag(conn->client, B_SAFEREAD, 1); + while ((len = getline(l, sizeof(l), conn->client, 0)) <= 0) { + if ((len < 0) || ap_bgetflag(conn->client, B_EOF)) { + ap_bsetflag(conn->client, B_SAFEREAD, 0); + /* this is a hack to make sure that request time is set, + * it's not perfect, but it's better than nothing + */ + r->request_time = time(0); + return 0; + } + } + /* we've probably got something to do, ignore graceful restart requests */ +#ifdef SIGUSR1 + signal(SIGUSR1, SIG_IGN); +#endif + + ap_bsetflag(conn->client, B_SAFEREAD, 0); + + r->request_time = time(NULL); + r->the_request = ap_pstrdup(r->pool, l); + r->method = ap_getword_white(r->pool, &ll); + uri = ap_getword_white(r->pool, &ll); + + /* Provide quick information about the request method as soon as known */ + + r->method_number = ap_method_number_of(r->method); + if (r->method_number == M_GET && r->method[0] == 'H') { + r->header_only = 1; + } + + ap_parse_uri(r, uri); + + /* getline returns (size of max buffer - 1) if it fills up the + * buffer before finding the end-of-line. This is only going to + * happen if it exceeds the configured limit for a request-line. + */ + if (len > r->server->limit_req_line) { + r->status = HTTP_REQUEST_URI_TOO_LARGE; + r->proto_num = HTTP_VERSION(1,0); + r->protocol = ap_pstrdup(r->pool, "HTTP/1.0"); + return 0; + } + + r->assbackwards = (ll[0] == '\0'); + r->protocol = ap_pstrdup(r->pool, ll[0] ? ll : "HTTP/0.9"); + + if (2 == sscanf(r->protocol, "HTTP/%u.%u", &major, &minor) + && minor < HTTP_VERSION(1,0)) /* don't allow HTTP/0.1000 */ + r->proto_num = HTTP_VERSION(major, minor); + else + r->proto_num = HTTP_VERSION(1,0); + + return 1; +} + +static void get_mime_headers(request_rec *r) +{ + char field[DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2]; /* getline's two extra */ + conn_rec *c = r->connection; + char *value; + char *copy; + int len; + unsigned int fields_read = 0; + table *tmp_headers; + + /* We'll use ap_overlap_tables later to merge these into r->headers_in. */ + tmp_headers = ap_make_table(r->pool, 50); + + /* + * Read header lines until we get the empty separator line, a read error, + * the connection closes (EOF), reach the server limit, or we timeout. + */ + while ((len = getline(field, sizeof(field), c->client, 1)) > 0) { + + if (r->server->limit_req_fields && + (++fields_read > r->server->limit_req_fields)) { + r->status = HTTP_BAD_REQUEST; + ap_table_setn(r->notes, "error-notes", + "The number of request header fields exceeds " + "this server's limit.

\n"); + return; + } + /* getline returns (size of max buffer - 1) if it fills up the + * buffer before finding the end-of-line. This is only going to + * happen if it exceeds the configured limit for a field size. + */ + if (len > r->server->limit_req_fieldsize) { + r->status = HTTP_BAD_REQUEST; + ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool, + "Size of a request header field exceeds server limit.

\n" + "

\n", field, "
\n", NULL)); + return; + } + copy = ap_palloc(r->pool, len + 1); + memcpy(copy, field, len + 1); + + if (!(value = strchr(copy, ':'))) { /* Find the colon separator */ + r->status = HTTP_BAD_REQUEST; /* or abort the bad request */ + ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool, + "Request header field is missing colon separator.

\n" + "

\n", copy, "
\n", NULL)); + return; + } + + *value = '\0'; + ++value; + while (*value == ' ' || *value == '\t') + ++value; /* Skip to start of value */ + + ap_table_addn(tmp_headers, copy, value); + } + + ap_overlap_tables(r->headers_in, tmp_headers, AP_OVERLAP_TABLES_MERGE); +} + +request_rec *ap_read_request(conn_rec *conn) +{ + request_rec *r; + pool *p; + const char *expect; + int access_status; + + p = ap_make_sub_pool(conn->pool); + r = ap_pcalloc(p, sizeof(request_rec)); + r->pool = p; + r->connection = conn; + conn->server = conn->base_server; + r->server = conn->server; + + conn->keptalive = conn->keepalive == 1; + conn->keepalive = 0; + + conn->user = NULL; + conn->ap_auth_type = NULL; + + r->headers_in = ap_make_table(r->pool, 50); + r->subprocess_env = ap_make_table(r->pool, 50); + r->headers_out = ap_make_table(r->pool, 12); + r->err_headers_out = ap_make_table(r->pool, 5); + r->notes = ap_make_table(r->pool, 5); + + r->request_config = ap_create_request_config(r->pool); + r->per_dir_config = r->server->lookup_defaults; + + r->sent_bodyct = 0; /* bytect isn't for body */ + + r->read_length = 0; + r->read_body = REQUEST_NO_BODY; + + r->status = HTTP_REQUEST_TIME_OUT; /* Until we get a request */ + r->the_request = NULL; + +#ifdef CHARSET_EBCDIC + ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1); +#endif + + /* Get the request... */ + + ap_keepalive_timeout("read request line", r); + if (!read_request_line(r)) { + ap_kill_timeout(r); + if (r->status == HTTP_REQUEST_URI_TOO_LARGE) { + + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "request failed: URI too long"); + ap_send_error_response(r, 0); + ap_log_transaction(r); + return r; + } + return NULL; + } + if (!r->assbackwards) { + ap_hard_timeout("read request headers", r); + get_mime_headers(r); + ap_kill_timeout(r); + if (r->status != HTTP_REQUEST_TIME_OUT) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "request failed: error reading the headers"); + ap_send_error_response(r, 0); + ap_log_transaction(r); + return r; + } + } + else { + ap_kill_timeout(r); + + if (r->header_only) { + /* + * Client asked for headers only with HTTP/0.9, which doesn't send + * headers! Have to dink things just to make sure the error message + * comes through... + */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "client sent invalid HTTP/0.9 request: HEAD %s", + r->uri); + r->header_only = 0; + r->status = HTTP_BAD_REQUEST; + ap_send_error_response(r, 0); + ap_log_transaction(r); + return r; + } + } + + r->status = HTTP_OK; /* Until further notice. */ + + /* update what we think the virtual host is based on the headers we've + * now read + */ + ap_update_vhost_from_headers(r); + + /* we may have switched to another server */ + r->per_dir_config = r->server->lookup_defaults; + + conn->keptalive = 0; /* We now have a request to play with */ + + if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1,1))) || + ((r->proto_num == HTTP_VERSION(1,1)) && + !ap_table_get(r->headers_in, "Host"))) { + /* + * Client sent us an HTTP/1.1 or later request without telling us the + * hostname, either with a full URL or a Host: header. We therefore + * need to (as per the 1.1 spec) send an error. As a special case, + * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain + * a Host: header, and the server MUST respond with 400 if it doesn't. + */ + r->status = HTTP_BAD_REQUEST; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "client sent HTTP/1.1 request without hostname " + "(see RFC2068 section 9, and 14.23): %s", r->uri); + ap_send_error_response(r, 0); + ap_log_transaction(r); + return r; + } + if (((expect = ap_table_get(r->headers_in, "Expect")) != NULL) && + (expect[0] != '\0')) { + /* + * The Expect header field was added to HTTP/1.1 after RFC 2068 + * as a means to signal when a 100 response is desired and, + * unfortunately, to signal a poor man's mandatory extension that + * the server must understand or return 417 Expectation Failed. + */ + if (strcasecmp(expect, "100-continue") == 0) { + r->expecting_100 = 1; + } + else { + r->status = HTTP_EXPECTATION_FAILED; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r, + "client sent an unrecognized expectation value of " + "Expect: %s", expect); + ap_send_error_response(r, 0); + (void) ap_discard_request_body(r); + ap_log_transaction(r); + return r; + } + } + + if ((access_status = ap_run_post_read_request(r))) { + ap_die(access_status, r); + ap_log_transaction(r); + return NULL; + } + + return r; +} + +/* + * A couple of other functions which initialize some of the fields of + * a request structure, as appropriate for adjuncts of one kind or another + * to a request in progress. Best here, rather than elsewhere, since + * *someone* has to set the protocol-specific fields... + */ + +void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r) +{ + rnew->the_request = r->the_request; /* Keep original request-line */ + + rnew->assbackwards = 1; /* Don't send headers from this. */ + rnew->no_local_copy = 1; /* Don't try to send USE_LOCAL_COPY for a + * fragment. */ + rnew->method = "GET"; + rnew->method_number = M_GET; + rnew->protocol = "INCLUDED"; + + rnew->status = HTTP_OK; + + rnew->headers_in = r->headers_in; + rnew->subprocess_env = ap_copy_table(rnew->pool, r->subprocess_env); + rnew->headers_out = ap_make_table(rnew->pool, 5); + rnew->err_headers_out = ap_make_table(rnew->pool, 5); + rnew->notes = ap_make_table(rnew->pool, 5); + + rnew->expecting_100 = r->expecting_100; + rnew->read_length = r->read_length; + rnew->read_body = REQUEST_NO_BODY; + + rnew->main = (request_rec *) r; +} + +void ap_finalize_sub_req_protocol(request_rec *sub) +{ + SET_BYTES_SENT(sub->main); +} + +/* + * Support for the Basic authentication protocol, and a bit for Digest. + */ + +API_EXPORT(void) ap_note_auth_failure(request_rec *r) +{ + if (!strcasecmp(ap_auth_type(r), "Basic")) + ap_note_basic_auth_failure(r); + else if (!strcasecmp(ap_auth_type(r), "Digest")) + ap_note_digest_auth_failure(r); +} + +API_EXPORT(void) ap_note_basic_auth_failure(request_rec *r) +{ + if (strcasecmp(ap_auth_type(r), "Basic")) + ap_note_auth_failure(r); + else + ap_table_setn(r->err_headers_out, + r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate", + ap_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"", + NULL)); +} + +API_EXPORT(void) ap_note_digest_auth_failure(request_rec *r) +{ + ap_table_setn(r->err_headers_out, + r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate", + ap_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%lu\"", + ap_auth_name(r), r->request_time)); +} + +API_EXPORT(int) ap_get_basic_auth_pw(request_rec *r, const char **pw) +{ + const char *auth_line = ap_table_get(r->headers_in, + r->proxyreq ? "Proxy-Authorization" + : "Authorization"); + const char *t; + + if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic")) + return DECLINED; + + if (!ap_auth_name(r)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, + r, "need AuthName: %s", r->uri); + return SERVER_ERROR; + } + + if (!auth_line) { + ap_note_basic_auth_failure(r); + return AUTH_REQUIRED; + } + + if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) { + /* Client tried to authenticate using wrong auth scheme */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "client used wrong authentication scheme: %s", r->uri); + ap_note_basic_auth_failure(r); + return AUTH_REQUIRED; + } + + /* CHARSET_EBCDIC Issue's here ?!? Compare with 32/9 instead + * as we are operating on an octed stream ? + */ + while (*auth_line== ' ' || *auth_line== '\t') + auth_line++; + + t = ap_pbase64decode(r->pool, auth_line); + /* Note that this allocation has to be made from r->connection->pool + * because it has the lifetime of the connection. The other allocations + * are temporary and can be tossed away any time. + */ + r->connection->user = ap_getword_nulls (r->connection->pool, &t, ':'); + r->connection->ap_auth_type = "Basic"; + + *pw = t; + + return OK; +} + +/* New Apache routine to map status codes into array indicies + * e.g. 100 -> 0, 101 -> 1, 200 -> 2 ... + * The number of status lines must equal the value of RESPONSE_CODES (httpd.h) + * and must be listed in order. + */ + +#ifdef UTS21 +/* The second const triggers an assembler bug on UTS 2.1. + * Another workaround is to move some code out of this file into another, + * but this is easier. Dave Dykstra, 3/31/99 + */ +static const char * status_lines[RESPONSE_CODES] = +#else +static const char * const status_lines[RESPONSE_CODES] = +#endif +{ + "100 Continue", + "101 Switching Protocols", + "102 Processing", +#define LEVEL_200 3 + "200 OK", + "201 Created", + "202 Accepted", + "203 Non-Authoritative Information", + "204 No Content", + "205 Reset Content", + "206 Partial Content", + "207 Multi-Status", +#define LEVEL_300 11 + "300 Multiple Choices", + "301 Moved Permanently", + "302 Found", + "303 See Other", + "304 Not Modified", + "305 Use Proxy", + "306 unused", + "307 Temporary Redirect", +#define LEVEL_400 19 + "400 Bad Request", + "401 Authorization Required", + "402 Payment Required", + "403 Forbidden", + "404 Not Found", + "405 Method Not Allowed", + "406 Not Acceptable", + "407 Proxy Authentication Required", + "408 Request Time-out", + "409 Conflict", + "410 Gone", + "411 Length Required", + "412 Precondition Failed", + "413 Request Entity Too Large", + "414 Request-URI Too Large", + "415 Unsupported Media Type", + "416 Requested Range Not Satisfiable", + "417 Expectation Failed", + "418 unused", + "419 unused", + "420 unused", + "421 unused", + "422 Unprocessable Entity", + "423 Locked", + "424 Failed Dependency", +#define LEVEL_500 44 + "500 Internal Server Error", + "501 Method Not Implemented", + "502 Bad Gateway", + "503 Service Temporarily Unavailable", + "504 Gateway Time-out", + "505 HTTP Version Not Supported", + "506 Variant Also Negotiates", + "507 Insufficient Storage", + "508 unused", + "509 unused", + "510 Not Extended" +}; + +/* The index is found by its offset from the x00 code of each level. + * Although this is fast, it will need to be replaced if some nutcase + * decides to define a high-numbered code before the lower numbers. + * If that sad event occurs, replace the code below with a linear search + * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1]; + */ +API_EXPORT(int) ap_index_of_response(int status) +{ + static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400, + LEVEL_500, RESPONSE_CODES}; + int i, pos; + + if (status < 100) /* Below 100 is illegal for HTTP status */ + return LEVEL_500; + + for (i = 0; i < 5; i++) { + status -= 100; + if (status < 100) { + pos = (status + shortcut[i]); + if (pos < shortcut[i + 1]) + return pos; + else + return LEVEL_500; /* status unknown (falls in gap) */ + } + } + return LEVEL_500; /* 600 or above is also illegal */ +} + +/* Send a single HTTP header field to the client. Note that this function + * is used in calls to table_do(), so their interfaces are co-dependent. + * In other words, don't change this one without checking table_do in alloc.c. + * It returns true unless there was a write error of some kind. + */ +API_EXPORT_NONSTD(int) ap_send_header_field(request_rec *r, + const char *fieldname, const char *fieldval) +{ + return (0 < ap_rvputs(r, fieldname, ": ", fieldval, "\015\012", NULL)); +} + +API_EXPORT(void) ap_basic_http_header(request_rec *r) +{ + char *protocol; +#ifdef CHARSET_EBCDIC + int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII); +#endif /*CHARSET_EBCDIC*/ + + if (r->assbackwards) + return; + + if (!r->status_line) + r->status_line = status_lines[ap_index_of_response(r->status)]; + + /* mod_proxy is only HTTP/1.0, so avoid sending HTTP/1.1 error response; + * kluge around broken browsers when indicated by force-response-1.0 + */ + if (r->proxyreq + || (r->proto_num == HTTP_VERSION(1,0) + && ap_table_get(r->subprocess_env, "force-response-1.0"))) { + + protocol = "HTTP/1.0"; + r->connection->keepalive = -1; + } + else + protocol = SERVER_PROTOCOL; + +#ifdef CHARSET_EBCDIC + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1); +#endif /*CHARSET_EBCDIC*/ + + /* Output the HTTP/1.x Status-Line and the Date and Server fields */ + + ap_rvputs(r, protocol, " ", r->status_line, "\015\012", NULL); + + ap_send_header_field(r, "Date", ap_gm_timestr_822(r->pool, r->request_time)); + ap_send_header_field(r, "Server", ap_get_server_version()); + + ap_table_unset(r->headers_out, "Date"); /* Avoid bogosity */ + ap_table_unset(r->headers_out, "Server"); +#ifdef CHARSET_EBCDIC + if (!convert) + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert); +#endif /*CHARSET_EBCDIC*/ +} + +/* Navigator versions 2.x, 3.x and 4.0 betas up to and including 4.0b2 + * have a header parsing bug. If the terminating \r\n occur starting + * at offset 256, 257 or 258 of output then it will not properly parse + * the headers. Curiously it doesn't exhibit this problem at 512, 513. + * We are guessing that this is because their initial read of a new request + * uses a 256 byte buffer, and subsequent reads use a larger buffer. + * So the problem might exist at different offsets as well. + * + * This should also work on keepalive connections assuming they use the + * same small buffer for the first read of each new request. + * + * At any rate, we check the bytes written so far and, if we are about to + * tickle the bug, we instead insert a bogus padding header. Since the bug + * manifests as a broken image in Navigator, users blame the server. :( + * It is more expensive to check the User-Agent than it is to just add the + * bytes, so we haven't used the BrowserMatch feature here. + */ +static void terminate_header(BUFF *client) +{ + long int bs; + + ap_bgetopt(client, BO_BYTECT, &bs); + if (bs >= 255 && bs <= 257) + ap_bputs("X-Pad: avoid browser bug\015\012", client); + + ap_bputs("\015\012", client); /* Send the terminating empty line */ +} + +/* Build the Allow field-value from the request handler method mask. + * Note that we always allow TRACE, since it is handled below. + */ +static char *make_allow(request_rec *r) +{ + return 2 + ap_pstrcat(r->pool, + (r->allowed & (1 << M_GET)) ? ", GET, HEAD" : "", + (r->allowed & (1 << M_POST)) ? ", POST" : "", + (r->allowed & (1 << M_PUT)) ? ", PUT" : "", + (r->allowed & (1 << M_DELETE)) ? ", DELETE" : "", + (r->allowed & (1 << M_CONNECT)) ? ", CONNECT" : "", + (r->allowed & (1 << M_OPTIONS)) ? ", OPTIONS" : "", + (r->allowed & (1 << M_PATCH)) ? ", PATCH" : "", + (r->allowed & (1 << M_PROPFIND)) ? ", PROPFIND" : "", + (r->allowed & (1 << M_PROPPATCH)) ? ", PROPPATCH" : "", + (r->allowed & (1 << M_MKCOL)) ? ", MKCOL" : "", + (r->allowed & (1 << M_COPY)) ? ", COPY" : "", + (r->allowed & (1 << M_MOVE)) ? ", MOVE" : "", + (r->allowed & (1 << M_LOCK)) ? ", LOCK" : "", + (r->allowed & (1 << M_UNLOCK)) ? ", UNLOCK" : "", + ", TRACE", + NULL); +} + +API_EXPORT(int) ap_send_http_trace(request_rec *r) +{ + int rv; + + /* Get the original request */ + while (r->prev) + r = r->prev; + + if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY))) + return rv; + + ap_hard_timeout("send TRACE", r); + + r->content_type = "message/http"; + ap_send_http_header(r); + + /* Now we recreate the request, and echo it back */ + + ap_rvputs(r, r->the_request, "\015\012", NULL); + + ap_table_do((int (*) (void *, const char *, const char *)) + ap_send_header_field, (void *) r, r->headers_in, NULL); + ap_rputs("\015\012", r); + + ap_kill_timeout(r); + return OK; +} + +int ap_send_http_options(request_rec *r) +{ + const long int zero = 0L; + + if (r->assbackwards) + return DECLINED; + + ap_hard_timeout("send OPTIONS", r); + + ap_basic_http_header(r); + + ap_table_setn(r->headers_out, "Content-Length", "0"); + ap_table_setn(r->headers_out, "Allow", make_allow(r)); + ap_set_keepalive(r); + + ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field, + (void *) r, r->headers_out, NULL); + + terminate_header(r->connection->client); + + ap_kill_timeout(r); + ap_bsetopt(r->connection->client, BO_BYTECT, &zero); + + return OK; +} + +/* + * Here we try to be compatible with clients that want multipart/x-byteranges + * instead of multipart/byteranges (also see above), as per HTTP/1.1. We + * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication + * that the browser supports an older protocol. We also check User-Agent + * for Microsoft Internet Explorer 3, which needs this as well. + */ +static int use_range_x(request_rec *r) +{ + const char *ua; + return (ap_table_get(r->headers_in, "Request-Range") || + ((ua = ap_table_get(r->headers_in, "User-Agent")) + && strstr(ua, "MSIE 3"))); +} + +/* This routine is called by ap_table_do and merges all instances of + * the passed field values into a single array that will be further + * processed by some later routine. Originally intended to help split + * and recombine multiple Vary fields, though it is generic to any field + * consisting of comma/space-separated tokens. + */ +static int uniq_field_values(void *d, const char *key, const char *val) +{ + array_header *values; + char *start; + char *e; + char **strpp; + int i; + + values = (array_header *)d; + + e = ap_pstrdup(values->pool, val); + + do { + /* Find a non-empty fieldname */ + + while (*e == ',' || ap_isspace(*e)) { + ++e; + } + if (*e == '\0') { + break; + } + start = e; + while (*e != '\0' && *e != ',' && !ap_isspace(*e)) { + ++e; + } + if (*e != '\0') { + *e++ = '\0'; + } + + /* Now add it to values if it isn't already represented. + * Could be replaced by a ap_array_strcasecmp() if we had one. + */ + for (i = 0, strpp = (char **) values->elts; i < values->nelts; + ++i, ++strpp) { + if (*strpp && strcasecmp(*strpp, start) == 0) { + break; + } + } + if (i == values->nelts) { /* if not found */ + *(char **)ap_push_array(values) = start; + } + } while (*e != '\0'); + + return 1; +} + +/* + * Since some clients choke violently on multiple Vary fields, or + * Vary fields with duplicate tokens, combine any multiples and remove + * any duplicates. + */ +static void fixup_vary(request_rec *r) +{ + array_header *varies; + + varies = ap_make_array(r->pool, 5, sizeof(char *)); + + /* Extract all Vary fields from the headers_out, separate each into + * its comma-separated fieldname values, and then add them to varies + * if not already present in the array. + */ + ap_table_do((int (*)(void *, const char *, const char *))uniq_field_values, + (void *) varies, r->headers_out, "Vary", NULL); + + /* If we found any, replace old Vary fields with unique-ified value */ + + if (varies->nelts > 0) { + ap_table_setn(r->headers_out, "Vary", + ap_array_pstrcat(r->pool, varies, ',')); + } +} + +API_EXPORT(void) ap_send_http_header(request_rec *r) +{ + int i; + const long int zero = 0L; +#ifdef CHARSET_EBCDIC + int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII); +#endif /*CHARSET_EBCDIC*/ + + if (r->assbackwards) { + if (!r->main) + ap_bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; + return; + } + + /* + * Now that we are ready to send a response, we need to combine the two + * header field tables into a single table. If we don't do this, our + * later attempts to set or unset a given fieldname might be bypassed. + */ + if (!ap_is_empty_table(r->err_headers_out)) + r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out, + r->headers_out); + + /* + * Remove the 'Vary' header field if the client can't handle it. + * Since this will have nasty effects on HTTP/1.1 caches, force + * the response into HTTP/1.0 mode. + */ + if (ap_table_get(r->subprocess_env, "force-no-vary") != NULL) { + ap_table_unset(r->headers_out, "Vary"); + r->proto_num = HTTP_VERSION(1,0); + ap_table_set(r->subprocess_env, "force-response-1.0", "1"); + } + else { + fixup_vary(r); + } + + ap_hard_timeout("send headers", r); + + ap_basic_http_header(r); + +#ifdef CHARSET_EBCDIC + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1); +#endif /*CHARSET_EBCDIC*/ + + ap_set_keepalive(r); + + if (r->chunked) { + ap_table_mergen(r->headers_out, "Transfer-Encoding", "chunked"); + ap_table_unset(r->headers_out, "Content-Length"); + } + + if (r->byterange > 1) + ap_table_setn(r->headers_out, "Content-Type", + ap_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/", + "byteranges; boundary=", r->boundary, NULL)); + else if (r->content_type) + ap_table_setn(r->headers_out, "Content-Type", r->content_type); + else + ap_table_setn(r->headers_out, "Content-Type", ap_default_type(r)); + + if (r->content_encoding) + ap_table_setn(r->headers_out, "Content-Encoding", r->content_encoding); + + if (r->content_languages && r->content_languages->nelts) { + for (i = 0; i < r->content_languages->nelts; ++i) { + ap_table_mergen(r->headers_out, "Content-Language", + ((char **) (r->content_languages->elts))[i]); + } + } + else if (r->content_language) + ap_table_setn(r->headers_out, "Content-Language", r->content_language); + + /* + * Control cachability for non-cachable responses if not already set by + * some other part of the server configuration. + */ + if (r->no_cache && !ap_table_get(r->headers_out, "Expires")) + ap_table_addn(r->headers_out, "Expires", + ap_gm_timestr_822(r->pool, r->request_time)); + + /* Send the entire table of header fields, terminated by an empty line. */ + + ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field, + (void *) r, r->headers_out, NULL); + + terminate_header(r->connection->client); + + ap_kill_timeout(r); + + ap_bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; /* Whatever follows is real body stuff... */ + + /* Set buffer flags for the body */ + if (r->chunked) + ap_bsetflag(r->connection->client, B_CHUNK, 1); +#ifdef CHARSET_EBCDIC + if (!convert) + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert); +#endif /*CHARSET_EBCDIC*/ +} + +/* finalize_request_protocol is called at completion of sending the + * response. It's sole purpose is to send the terminating protocol + * information for any wrappers around the response message body + * (i.e., transfer encodings). It should have been named finalize_response. + */ +API_EXPORT(void) ap_finalize_request_protocol(request_rec *r) +{ + if (r->chunked && !r->connection->aborted) { + /* + * Turn off chunked encoding --- we can only do this once. + */ + r->chunked = 0; + ap_bsetflag(r->connection->client, B_CHUNK, 0); + + ap_soft_timeout("send ending chunk", r); + ap_rputs("0\015\012", r); + /* If we had footer "headers", we'd send them now */ + ap_rputs("\015\012", r); + ap_kill_timeout(r); + } +} + +/* Here we deal with getting the request message body from the client. + * Whether or not the request contains a body is signaled by the presence + * of a non-zero Content-Length or by a Transfer-Encoding: chunked. + * + * Note that this is more complicated than it was in Apache 1.1 and prior + * versions, because chunked support means that the module does less. + * + * The proper procedure is this: + * + * 1. Call setup_client_block() near the beginning of the request + * handler. This will set up all the necessary properties, and will + * return either OK, or an error code. If the latter, the module should + * return that error code. The second parameter selects the policy to + * apply if the request message indicates a body, and how a chunked + * transfer-coding should be interpreted. Choose one of + * + * REQUEST_NO_BODY Send 413 error if message has any body + * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length + * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me. + * REQUEST_CHUNKED_PASS Pass the chunks to me without removal. + * + * In order to use the last two options, the caller MUST provide a buffer + * large enough to hold a chunk-size line, including any extensions. + * + * 2. When you are ready to read a body (if any), call should_client_block(). + * This will tell the module whether or not to read input. If it is 0, + * the module should assume that there is no message body to read. + * This step also sends a 100 Continue response to HTTP/1.1 clients, + * so should not be called until the module is *definitely* ready to + * read content. (otherwise, the point of the 100 response is defeated). + * Never call this function more than once. + * + * 3. Finally, call get_client_block in a loop. Pass it a buffer and its size. + * It will put data into the buffer (not necessarily a full buffer), and + * return the length of the input block. When it is done reading, it will + * return 0 if EOF, or -1 if there was an error. + * If an error occurs on input, we force an end to keepalive. + */ + +API_EXPORT(int) ap_setup_client_block(request_rec *r, int read_policy) +{ + const char *tenc = ap_table_get(r->headers_in, "Transfer-Encoding"); + const char *lenp = ap_table_get(r->headers_in, "Content-Length"); + unsigned long max_body; + + r->read_body = read_policy; + r->read_chunked = 0; + r->remaining = 0; + + if (tenc) { + if (strcasecmp(tenc, "chunked")) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Unknown Transfer-Encoding %s", tenc); + return HTTP_NOT_IMPLEMENTED; + } + if (r->read_body == REQUEST_CHUNKED_ERROR) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "chunked Transfer-Encoding forbidden: %s", r->uri); + return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED; + } + + r->read_chunked = 1; + } + else if (lenp) { + const char *pos = lenp; + + while (ap_isdigit(*pos) || ap_isspace(*pos)) + ++pos; + if (*pos != '\0') { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid Content-Length %s", lenp); + return HTTP_BAD_REQUEST; + } + + r->remaining = atol(lenp); + } + + if ((r->read_body == REQUEST_NO_BODY) && + (r->read_chunked || (r->remaining > 0))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "%s with body is not allowed for %s", r->method, r->uri); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + max_body = ap_get_limit_req_body(r); + if (max_body && (r->remaining > max_body)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Request content-length of %s is larger than the configured " + "limit of %lu", lenp, max_body); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + return OK; +} + +API_EXPORT(int) ap_should_client_block(request_rec *r) +{ + /* First check if we have already read the request body */ + + if (r->read_length || (!r->read_chunked && (r->remaining <= 0))) + return 0; + + if (r->expecting_100 && r->proto_num >= HTTP_VERSION(1,1)) { + /* sending 100 Continue interim response */ + ap_rvputs(r, SERVER_PROTOCOL, " ", status_lines[0], "\015\012\015\012", + NULL); + ap_rflush(r); + } + + return 1; +} + +static long get_chunk_size(char *b) +{ + long chunksize = 0; + + while (ap_isxdigit(*b)) { + int xvalue = 0; + + if (*b >= '0' && *b <= '9') + xvalue = *b - '0'; + else if (*b >= 'A' && *b <= 'F') + xvalue = *b - 'A' + 0xa; + else if (*b >= 'a' && *b <= 'f') + xvalue = *b - 'a' + 0xa; + + chunksize = (chunksize << 4) | xvalue; + ++b; + } + + return chunksize; +} + +/* get_client_block is called in a loop to get the request message body. + * This is quite simple if the client includes a content-length + * (the normal case), but gets messy if the body is chunked. Note that + * r->remaining is used to maintain state across calls and that + * r->read_length is the total number of bytes given to the caller + * across all invocations. It is messy because we have to be careful not + * to read past the data provided by the client, since these reads block. + * Returns 0 on End-of-body, -1 on error or premature chunk end. + * + * Reading the chunked encoding requires a buffer size large enough to + * hold a chunk-size line, including any extensions. For now, we'll leave + * that to the caller, at least until we can come up with a better solution. + */ +API_EXPORT(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz) +{ + int c; + long len_read, len_to_read; + long chunk_start = 0; + unsigned long max_body; + + if (!r->read_chunked) { /* Content-length read */ + len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining; + len_read = ap_bread(r->connection->client, buffer, len_to_read); + if (len_read <= 0) { + if (len_read < 0) + r->connection->keepalive = -1; + return len_read; + } + r->read_length += len_read; + r->remaining -= len_read; + return len_read; + } + + /* + * Handle chunked reading Note: we are careful to shorten the input + * bufsiz so that there will always be enough space for us to add a CRLF + * (if necessary). + */ + if (r->read_body == REQUEST_CHUNKED_PASS) + bufsiz -= 2; + if (bufsiz <= 0) + return -1; /* Cannot read chunked with a small buffer */ + + /* Check to see if we have already read too much request data. + * For efficiency reasons, we only check this at the top of each + * caller read pass, since the limit exists just to stop infinite + * length requests and nobody cares if it goes over by one buffer. + */ + max_body = ap_get_limit_req_body(r); + if (max_body && (r->read_length > max_body)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Chunked request body is larger than the configured limit of %lu", + max_body); + r->connection->keepalive = -1; + return -1; + } + + if (r->remaining == 0) { /* Start of new chunk */ + + chunk_start = getline(buffer, bufsiz, r->connection->client, 0); + if ((chunk_start <= 0) || (chunk_start >= (bufsiz - 1)) + || !ap_isxdigit(*buffer)) { + r->connection->keepalive = -1; + return -1; + } + + len_to_read = get_chunk_size(buffer); + + if (len_to_read == 0) { /* Last chunk indicated, get footers */ + if (r->read_body == REQUEST_CHUNKED_DECHUNK) { + get_mime_headers(r); + ap_snprintf(buffer, bufsiz, "%ld", r->read_length); + ap_table_unset(r->headers_in, "Transfer-Encoding"); + ap_table_setn(r->headers_in, "Content-Length", + ap_pstrdup(r->pool, buffer)); + return 0; + } + r->remaining = -1; /* Indicate footers in-progress */ + } + else { + r->remaining = len_to_read; + } + if (r->read_body == REQUEST_CHUNKED_PASS) { + buffer[chunk_start++] = CR; /* Restore chunk-size line end */ + buffer[chunk_start++] = LF; + buffer += chunk_start; /* and pass line on to caller */ + bufsiz -= chunk_start; + } + else { + /* REQUEST_CHUNKED_DECHUNK -- do not include the length of the + * header in the return value + */ + chunk_start = 0; + } + } + /* When REQUEST_CHUNKED_PASS, we are */ + if (r->remaining == -1) { /* reading footers until empty line */ + len_read = chunk_start; + + while ((bufsiz > 1) && ((len_read = + getline(buffer, bufsiz, r->connection->client, 1)) > 0)) { + + if (len_read != (bufsiz - 1)) { + buffer[len_read++] = CR; /* Restore footer line end */ + buffer[len_read++] = LF; + } + chunk_start += len_read; + buffer += len_read; + bufsiz -= len_read; + } + if (len_read < 0) { + r->connection->keepalive = -1; + return -1; + } + + if (len_read == 0) { /* Indicates an empty line */ + buffer[0] = CR; + buffer[1] = LF; + chunk_start += 2; + r->remaining = -2; + } + r->read_length += chunk_start; + return chunk_start; + } + /* When REQUEST_CHUNKED_PASS, we */ + if (r->remaining == -2) { /* finished footers when last called */ + r->remaining = 0; /* so now we must signal EOF */ + return 0; + } + + /* Otherwise, we are in the midst of reading a chunk of data */ + + len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining; + + len_read = ap_bread(r->connection->client, buffer, len_to_read); + if (len_read <= 0) { + r->connection->keepalive = -1; + return -1; + } + + r->remaining -= len_read; + + if (r->remaining == 0) { /* End of chunk, get trailing CRLF */ + if ((c = ap_bgetc(r->connection->client)) == CR) { + c = ap_bgetc(r->connection->client); + } + if (c != LF) { + r->connection->keepalive = -1; + return -1; + } + if (r->read_body == REQUEST_CHUNKED_PASS) { + buffer[len_read++] = CR; + buffer[len_read++] = LF; + } + } + r->read_length += (chunk_start + len_read); + + return (chunk_start + len_read); +} + +/* In HTTP/1.1, any method can have a body. However, most GET handlers + * wouldn't know what to do with a request body if they received one. + * This helper routine tests for and reads any message body in the request, + * simply discarding whatever it receives. We need to do this because + * failing to read the request body would cause it to be interpreted + * as the next request on a persistent connection. + * + * Since we return an error status if the request is malformed, this + * routine should be called at the beginning of a no-body handler, e.g., + * + * if ((retval = ap_discard_request_body(r)) != OK) + * return retval; + */ +API_EXPORT(int) ap_discard_request_body(request_rec *r) +{ + int rv; + + if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_PASS))) + return rv; + + /* In order to avoid sending 100 Continue when we already know the + * final response status, and yet not kill the connection if there is + * no request body to be read, we need to duplicate the test from + * ap_should_client_block() here negated rather than call it directly. + */ + if ((r->read_length == 0) && (r->read_chunked || (r->remaining > 0))) { + char dumpbuf[HUGE_STRING_LEN]; + + if (r->expecting_100) { + r->connection->keepalive = -1; + return OK; + } + ap_hard_timeout("reading request body", r); + while ((rv = ap_get_client_block(r, dumpbuf, HUGE_STRING_LEN)) > 0) + continue; + ap_kill_timeout(r); + + if (rv < 0) + return HTTP_BAD_REQUEST; + } + return OK; +} + +/* + * Send the body of a response to the client. + */ +API_EXPORT(long) ap_send_fd(FILE *f, request_rec *r) +{ + return ap_send_fd_length(f, r, -1); +} + +API_EXPORT(long) ap_send_fd_length(FILE *f, request_rec *r, long length) +{ + char buf[IOBUFSIZE]; + long total_bytes_sent = 0; + register int n, w, o, len; + + if (length == 0) + return 0; + + ap_soft_timeout("send body", r); + + while (!r->connection->aborted) { + if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length) + len = length - total_bytes_sent; + else + len = IOBUFSIZE; + + while ((n = fread(buf, sizeof(char), len, f)) < 1 + && ferror(f) && errno == EINTR && !r->connection->aborted) + continue; + + if (n < 1) { + break; + } + o = 0; + + while (n && !r->connection->aborted) { + w = ap_bwrite(r->connection->client, &buf[o], n); + if (w > 0) { + ap_reset_timeout(r); /* reset timeout after successful write */ + total_bytes_sent += w; + n -= w; + o += w; + } + else if (w < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before send body completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + break; + } + } + } + + ap_kill_timeout(r); + SET_BYTES_SENT(r); + return total_bytes_sent; +} + +/* + * Send the body of a response to the client. + */ +API_EXPORT(long) ap_send_fb(BUFF *fb, request_rec *r) +{ + return ap_send_fb_length(fb, r, -1); +} + +API_EXPORT(long) ap_send_fb_length(BUFF *fb, request_rec *r, long length) +{ + char buf[IOBUFSIZE]; + long total_bytes_sent = 0; + register int n, w, o, len, fd; + fd_set fds; + + if (length == 0) + return 0; + + /* Make fb unbuffered and non-blocking */ + ap_bsetflag(fb, B_RD, 0); +#ifndef TPF + ap_bnonblock(fb, B_RD); +#endif + fd = ap_bfileno(fb, B_RD); +#ifdef CHECK_FD_SETSIZE + if (fd >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "send body: filedescriptor (%u) larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", fd, FD_SETSIZE); + return 0; + } +#endif + + ap_soft_timeout("send body", r); + + FD_ZERO(&fds); + while (!r->connection->aborted) { +#ifdef NDELAY_PIPE_RETURNS_ZERO + /* Contributed by dwd@bell-labs.com for UTS 2.1.2, where the fcntl */ + /* O_NDELAY flag causes read to return 0 when there's nothing */ + /* available when reading from a pipe. That makes it tricky */ + /* to detect end-of-file :-(. This stupid bug is even documented */ + /* in the read(2) man page where it says that everything but */ + /* pipes return -1 and EAGAIN. That makes it a feature, right? */ + int afterselect = 0; +#endif + if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length) + len = length - total_bytes_sent; + else + len = IOBUFSIZE; + + do { + n = ap_bread(fb, buf, len); +#ifdef NDELAY_PIPE_RETURNS_ZERO + if ((n > 0) || (n == 0 && afterselect)) + break; +#else + if (n >= 0) + break; +#endif + if (r->connection->aborted) + break; + if (n < 0 && errno != EAGAIN) + break; + + /* we need to block, so flush the output first */ + if (ap_bflush(r->connection->client) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before send body completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + break; + } + FD_SET(fd, &fds); + /* + * we don't care what select says, we might as well loop back + * around and try another read + */ + ap_select(fd + 1, &fds, NULL, NULL, NULL); +#ifdef NDELAY_PIPE_RETURNS_ZERO + afterselect = 1; +#endif + } while (!r->connection->aborted); + + if (n < 1 || r->connection->aborted) { + break; + } + o = 0; + + while (n && !r->connection->aborted) { + w = ap_bwrite(r->connection->client, &buf[o], n); + if (w > 0) { + ap_reset_timeout(r); /* reset timeout after successful write */ + total_bytes_sent += w; + n -= w; + o += w; + } + else if (w < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before send body completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + break; + } + } + } + + ap_kill_timeout(r); + SET_BYTES_SENT(r); + return total_bytes_sent; +} + + + +/* The code writes MMAP_SEGMENT_SIZE bytes at a time. This is due to Apache's + * timeout model, which is a timeout per-write rather than a time for the + * entire transaction to complete. Essentially this should be small enough + * so that in one Timeout period, your slowest clients should be reasonably + * able to receive this many bytes. + * + * To take advantage of zero-copy TCP under Solaris 2.6 this should be a + * multiple of 16k. (And you need a SunATM2.0 network card.) + */ +#ifndef MMAP_SEGMENT_SIZE +#define MMAP_SEGMENT_SIZE 32768 +#endif + +/* send data from an in-memory buffer */ +API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset, + size_t length) +{ + size_t total_bytes_sent = 0; + int n, w; + + if (length == 0) + return 0; + + ap_soft_timeout("send mmap", r); + + length += offset; + while (!r->connection->aborted && offset < length) { + if (length - offset > MMAP_SEGMENT_SIZE) { + n = MMAP_SEGMENT_SIZE; + } + else { + n = length - offset; + } + + while (n && !r->connection->aborted) { + w = ap_bwrite(r->connection->client, (char *) mm + offset, n); + if (w > 0) { + ap_reset_timeout(r); /* reset timeout after successful write */ + total_bytes_sent += w; + n -= w; + offset += w; + } + else if (w < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before send mmap completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + break; + } + } + } + + ap_kill_timeout(r); + SET_BYTES_SENT(r); + return total_bytes_sent; +} + +API_EXPORT(int) ap_rputc(int c, request_rec *r) +{ + if (r->connection->aborted) + return EOF; + + if (ap_bputc(c, r->connection->client) < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rputc completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return EOF; + } + SET_BYTES_SENT(r); + return c; +} + +API_EXPORT(int) ap_rputs(const char *str, request_rec *r) +{ + int rcode; + + if (r->connection->aborted) + return EOF; + + rcode = ap_bputs(str, r->connection->client); + if (rcode < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rputs completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return EOF; + } + SET_BYTES_SENT(r); + return rcode; +} + +API_EXPORT(int) ap_rwrite(const void *buf, int nbyte, request_rec *r) +{ + int n; + + if (r->connection->aborted) + return -1; + + n = ap_bwrite(r->connection->client, buf, nbyte); + if (n < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rwrite completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return -1; + } + SET_BYTES_SENT(r); + return n; +} + +API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list ap) +{ + int n; + + if (r->connection->aborted) + return -1; + + n = ap_vbprintf(r->connection->client, fmt, ap); + + if (n < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before vrprintf completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return -1; + } + SET_BYTES_SENT(r); + return n; +} + +API_EXPORT(int) ap_rprintf(request_rec *r, const char *fmt,...) +{ + va_list vlist; + int n; + + if (r->connection->aborted) + return -1; + + va_start(vlist, fmt); + n = ap_vbprintf(r->connection->client, fmt, vlist); + va_end(vlist); + + if (n < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rprintf completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return -1; + } + SET_BYTES_SENT(r); + return n; +} + +API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r,...) +{ + va_list args; + int i, j, k; + const char *x; + BUFF *fb = r->connection->client; + + if (r->connection->aborted) + return EOF; + + va_start(args, r); + for (k = 0;;) { + x = va_arg(args, const char *); + if (x == NULL) + break; + j = strlen(x); + i = ap_bwrite(fb, x, j); + if (i != j) { + va_end(args); + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rvputs completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return EOF; + } + k += i; + } + va_end(args); + + SET_BYTES_SENT(r); + return k; +} + +API_EXPORT(int) ap_rflush(request_rec *r) +{ + if (ap_bflush(r->connection->client) < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rflush completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return EOF; + } + return 0; +} + +/* We should have named this send_canned_response, since it is used for any + * response that can be generated by the server from the request record. + * This includes all 204 (no content), 3xx (redirect), 4xx (client error), + * and 5xx (server error) messages that have not been redirected to another + * handler via the ErrorDocument feature. + */ +void ap_send_error_response(request_rec *r, int recursive_error) +{ + int status = r->status; + int idx = ap_index_of_response(status); + char *custom_response; + const char *location = ap_table_get(r->headers_out, "Location"); + + /* + * It's possible that the Location field might be in r->err_headers_out + * instead of r->headers_out; use the latter if possible, else the + * former. + */ + if (location == NULL) { + location = ap_table_get(r->err_headers_out, "Location"); + } + /* We need to special-case the handling of 204 and 304 responses, + * since they have specific HTTP requirements and do not include a + * message body. Note that being assbackwards here is not an option. + */ + if (status == HTTP_NOT_MODIFIED) { + if (!ap_is_empty_table(r->err_headers_out)) + r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out, + r->headers_out); + ap_hard_timeout("send 304", r); + + ap_basic_http_header(r); + ap_set_keepalive(r); + + ap_table_do((int (*)(void *, const char *, const char *)) ap_send_header_field, + (void *) r, r->headers_out, + "Connection", + "Keep-Alive", + "ETag", + "Content-Location", + "Expires", + "Cache-Control", + "Vary", + "Warning", + "WWW-Authenticate", + "Proxy-Authenticate", + NULL); + + terminate_header(r->connection->client); + + ap_kill_timeout(r); + return; + } + + if (status == HTTP_NO_CONTENT) { + ap_send_http_header(r); + ap_finalize_request_protocol(r); + return; + } + + if (!r->assbackwards) { + table *tmp = r->headers_out; + + /* For all HTTP/1.x responses for which we generate the message, + * we need to avoid inheriting the "normal status" header fields + * that may have been set by the request handler before the + * error or redirect, except for Location on external redirects. + */ + r->headers_out = r->err_headers_out; + r->err_headers_out = tmp; + ap_clear_table(r->err_headers_out); + + if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) { + if ((location != NULL) && *location) { + ap_table_setn(r->headers_out, "Location", location); + } + else { + location = ""; /* avoids coredump when printing, below */ + } + } + + r->content_language = NULL; + r->content_languages = NULL; + r->content_encoding = NULL; + r->clength = 0; + r->content_type = "text/html"; + + if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED)) + ap_table_setn(r->headers_out, "Allow", make_allow(r)); + + ap_send_http_header(r); + + if (r->header_only) { + ap_finalize_request_protocol(r); + ap_rflush(r); + return; + } + } + + ap_hard_timeout("send error body", r); + + if ((custom_response = ap_response_code_string(r, idx))) { + /* + * We have a custom response output. This should only be + * a text-string to write back. But if the ErrorDocument + * was a local redirect and the requested resource failed + * for any reason, the custom_response will still hold the + * redirect URL. We don't really want to output this URL + * as a text message, so first check the custom response + * string to ensure that it is a text-string (using the + * same test used in ap_die(), i.e. does it start with a "). + * If it doesn't, we've got a recursive error, so find + * the original error and output that as well. + */ + if (custom_response[0] == '\"') { + ap_rputs(custom_response + 1, r); + ap_kill_timeout(r); + ap_finalize_request_protocol(r); + ap_rflush(r); + return; + } + /* + * Redirect failed, so get back the original error + */ + while (r->prev && (r->prev->status != HTTP_OK)) + r = r->prev; + } + { + const char *title = status_lines[idx]; + const char *h1; + const char *error_notes; + + /* Accept a status_line set by a module, but only if it begins + * with the 3 digit status code + */ + if (r->status_line != NULL + && strlen(r->status_line) > 4 /* long enough */ + && ap_isdigit(r->status_line[0]) + && ap_isdigit(r->status_line[1]) + && ap_isdigit(r->status_line[2]) + && ap_isspace(r->status_line[3]) + && ap_isalnum(r->status_line[4])) { + title = r->status_line; + } + + /* folks decided they didn't want the error code in the H1 text */ + h1 = &title[4]; + + ap_rvputs(r, + DOCTYPE_HTML_2_0 + "\n", title, + "\n\n

", h1, "

\n", + NULL); + + switch (status) { + case HTTP_MOVED_PERMANENTLY: + case HTTP_MOVED_TEMPORARILY: + case HTTP_TEMPORARY_REDIRECT: + ap_rvputs(r, "The document has moved pool, location), "\">here.

\n", + NULL); + break; + case HTTP_SEE_OTHER: + ap_rvputs(r, "The answer to your request is located pool, location), "\">here.

\n", + NULL); + break; + case HTTP_USE_PROXY: + ap_rvputs(r, "This resource is only accessible " + "through the proxy\n", + ap_escape_html(r->pool, location), + "
\nYou will need to ", + "configure your client to use that proxy.

\n", NULL); + break; + case HTTP_PROXY_AUTHENTICATION_REQUIRED: + case AUTH_REQUIRED: + ap_rputs("This server could not verify that you\n" + "are authorized to access the document\n" + "requested. Either you supplied the wrong\n" + "credentials (e.g., bad password), or your\n" + "browser doesn't understand how to supply\n" + "the credentials required.

\n", r); + break; + case BAD_REQUEST: + ap_rputs("Your browser sent a request that " + "this server could not understand.

\n", r); + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + break; + case HTTP_FORBIDDEN: + ap_rvputs(r, "You don't have permission to access ", + ap_escape_html(r->pool, r->uri), + "\non this server.

\n", NULL); + break; + case NOT_FOUND: + ap_rvputs(r, "The requested URL ", + ap_escape_html(r->pool, r->uri), + " was not found on this server.

\n", NULL); + break; + case METHOD_NOT_ALLOWED: + ap_rvputs(r, "The requested method ", r->method, + " is not allowed " + "for the URL ", ap_escape_html(r->pool, r->uri), + ".

\n", NULL); + break; + case NOT_ACCEPTABLE: + ap_rvputs(r, + "An appropriate representation of the " + "requested resource ", + ap_escape_html(r->pool, r->uri), + " could not be found on this server.

\n", NULL); + /* fall through */ + case MULTIPLE_CHOICES: + { + const char *list; + if ((list = ap_table_get(r->notes, "variant-list"))) + ap_rputs(list, r); + } + break; + case LENGTH_REQUIRED: + ap_rvputs(r, "A request of the requested method ", r->method, + " requires a valid Content-length.

\n", NULL); + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + break; + case PRECONDITION_FAILED: + ap_rvputs(r, "The precondition on the request for the URL ", + ap_escape_html(r->pool, r->uri), + " evaluated to false.

\n", NULL); + break; + case HTTP_NOT_IMPLEMENTED: + ap_rvputs(r, ap_escape_html(r->pool, r->method), " to ", + ap_escape_html(r->pool, r->uri), + " not supported.

\n", NULL); + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + break; + case BAD_GATEWAY: + ap_rputs("The proxy server received an invalid\015\012" + "response from an upstream server.

\015\012", r); + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + break; + case VARIANT_ALSO_VARIES: + ap_rvputs(r, "A variant for the requested resource\n

\n",
+		      ap_escape_html(r->pool, r->uri),
+		      "\n
\nis itself a negotiable resource. " + "This indicates a configuration error.

\n", NULL); + break; + case HTTP_REQUEST_TIME_OUT: + ap_rputs("I'm tired of waiting for your request.\n", r); + break; + case HTTP_GONE: + ap_rvputs(r, "The requested resource
", + ap_escape_html(r->pool, r->uri), + "
\nis no longer available on this server ", + "and there is no forwarding address.\n", + "Please remove all references to this resource.\n", + NULL); + break; + case HTTP_REQUEST_ENTITY_TOO_LARGE: + ap_rvputs(r, "The requested resource
", + ap_escape_html(r->pool, r->uri), "
\n", + "does not allow request data with ", r->method, + " requests, or the amount of data provided in\n", + "the request exceeds the capacity limit.\n", NULL); + break; + case HTTP_REQUEST_URI_TOO_LARGE: + ap_rputs("The requested URL's length exceeds the capacity\n" + "limit for this server.

\n", r); + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + break; + case HTTP_UNSUPPORTED_MEDIA_TYPE: + ap_rputs("The supplied request data is not in a format\n" + "acceptable for processing by this resource.\n", r); + break; + case HTTP_RANGE_NOT_SATISFIABLE: + ap_rputs("None of the range-specifier values in the Range\n" + "request-header field overlap the current extent\n" + "of the selected resource.\n", r); + break; + case HTTP_EXPECTATION_FAILED: + ap_rvputs(r, "The expectation given in the Expect request-header" + "\nfield could not be met by this server.

\n" + "The client sent

\n    Expect: ",
+	              ap_table_get(r->headers_in, "Expect"), "\n
\n" + "but we only allow the 100-continue expectation.\n", + NULL); + break; + case HTTP_UNPROCESSABLE_ENTITY: + ap_rputs("The server understands the media type of the\n" + "request entity, but was unable to process the\n" + "contained instructions.\n", r); + break; + case HTTP_LOCKED: + ap_rputs("The requested resource is currently locked.\n" + "The lock must be released or proper identification\n" + "given before the method can be applied.\n", r); + break; + case HTTP_FAILED_DEPENDENCY: + ap_rputs("The method could not be performed on the resource\n" + "because the requested action depended on another\n" + "action and that other action failed.\n", r); + break; + case HTTP_INSUFFICIENT_STORAGE: + ap_rputs("The method could not be performed on the resource\n" + "because the server is unable to store the\n" + "representation needed to successfully complete the\n" + "request. There is insufficient free space left in\n" + "your storage allocation.\n", r); + break; + case HTTP_SERVICE_UNAVAILABLE: + ap_rputs("The server is temporarily unable to service your\n" + "request due to maintenance downtime or capacity\n" + "problems. Please try again later.\n", r); + break; + case HTTP_GATEWAY_TIME_OUT: + ap_rputs("The proxy server did not receive a timely response\n" + "from the upstream server.\n", r); + break; + case HTTP_NOT_EXTENDED: + ap_rputs("A mandatory extension policy in the request is not\n" + "accepted by the server for this resource.\n", r); + break; + default: /* HTTP_INTERNAL_SERVER_ERROR */ + /* + * This comparison to expose error-notes could be modified to + * use a configuration directive and export based on that + * directive. For now "*" is used to designate an error-notes + * that is totally safe for any user to see (ie lacks paths, + * database passwords, etc.) + */ + if (((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) + && (h1 = ap_table_get(r->notes, "verbose-error-to")) != NULL + && (strcmp(h1, "*") == 0)) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + else { + ap_rvputs(r, "The server encountered an internal error or\n" + "misconfiguration and was unable to complete\n" + "your request.

\n" + "Please contact the server administrator,\n ", + ap_escape_html(r->pool, r->server->server_admin), + " and inform them of the time the error occurred,\n" + "and anything you might have done that may have\n" + "caused the error.

\n" + "More information about this error may be available\n" + "in the server error log.

\n", NULL); + } + /* + * It would be nice to give the user the information they need to + * fix the problem directly since many users don't have access to + * the error_log (think University sites) even though they can easily + * get this error by misconfiguring an htaccess file. However, the + * error notes tend to include the real file pathname in this case, + * which some people consider to be a breach of privacy. Until we + * can figure out a way to remove the pathname, leave this commented. + * + * if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + * ap_rvputs(r, error_notes, "

\n", NULL); + * } + */ + break; + } + + if (recursive_error) { + ap_rvputs(r, "

Additionally, a ", + status_lines[ap_index_of_response(recursive_error)], + "\nerror was encountered while trying to use an " + "ErrorDocument to handle the request.\n", NULL); + } + ap_rputs(ap_psignature("


\n", r), r); + ap_rputs("\n", r); + } + ap_kill_timeout(r); + ap_finalize_request_protocol(r); + ap_rflush(r); +} diff --git a/modules/http/http_request.c b/modules/http/http_request.c new file mode 100644 index 00000000000..bc4d45f60f8 --- /dev/null +++ b/modules/http/http_request.c @@ -0,0 +1,1374 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_request.c: functions to get and process requests + * + * Rob McCool 3/21/93 + * + * Thoroughly revamped by rst for Apache. NB this file reads + * best from the bottom up. + * + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_conf_globals.h" /* for ap_extended_status */ +#include "http_log.h" +#include "http_main.h" +#include "scoreboard.h" +#include "fnmatch.h" + +/***************************************************************** + * + * Getting and checking directory configuration. Also checks the + * FollowSymlinks and FollowSymOwner stuff, since this is really the + * only place that can happen (barring a new mid_dir_walk callout). + * + * We can't do it as an access_checker module function which gets + * called with the final per_dir_config, since we could have a directory + * with FollowSymLinks disabled, which contains a symlink to another + * with a .htaccess file which turns FollowSymLinks back on --- and + * access in such a case must be denied. So, whatever it is that + * checks FollowSymLinks needs to know the state of the options as + * they change, all the way down. + */ + +/* + * We don't want people able to serve up pipes, or unix sockets, or other + * scary things. Note that symlink tests are performed later. + */ +static int check_safe_file(request_rec *r) +{ + if (r->finfo.st_mode == 0 /* doesn't exist */ + || S_ISDIR(r->finfo.st_mode) + || S_ISREG(r->finfo.st_mode) + || S_ISLNK(r->finfo.st_mode)) { + return OK; + } + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "object is not a file, directory or symlink: %s", + r->filename); + return HTTP_FORBIDDEN; +} + + +static int check_symlinks(char *d, int opts) +{ +#if defined(OS2) || defined(WIN32) + /* OS/2 doesn't have symlinks */ + return OK; +#else + struct stat lfi, fi; + char *lastp; + int res; + + if (opts & OPT_SYM_LINKS) + return OK; + + /* + * Strip trailing '/', if any, off what we're checking; trailing slashes + * make some systems follow symlinks to directories even in lstat(). + * After we've done the lstat, put it back. Also, don't bother checking + * '/' at all... + * + * Note that we don't have to worry about multiple slashes here because of + * no2slash() below... + */ + + lastp = d + strlen(d) - 1; + if (lastp == d) + return OK; /* Root directory, '/' */ + + if (*lastp == '/') + *lastp = '\0'; + else + lastp = NULL; + + res = lstat(d, &lfi); + + if (lastp) + *lastp = '/'; + + /* + * Note that we don't reject accesses to nonexistent files (multiviews or + * the like may cons up a way to run the transaction anyway)... + */ + + if (!(res >= 0) || !S_ISLNK(lfi.st_mode)) + return OK; + + /* OK, it's a symlink. May still be OK with OPT_SYM_OWNER */ + + if (!(opts & OPT_SYM_OWNER)) + return HTTP_FORBIDDEN; + + if (stat(d, &fi) < 0) + return HTTP_FORBIDDEN; + + return (fi.st_uid == lfi.st_uid) ? OK : HTTP_FORBIDDEN; + +#endif +} + +/* Dealing with the file system to get PATH_INFO + */ +static int get_path_info(request_rec *r) +{ + char *cp; + char *path = r->filename; + char *end = &path[strlen(path)]; + char *last_cp = NULL; + int rv; +#ifdef HAVE_DRIVE_LETTERS + char bStripSlash=1; +#endif + + if (r->finfo.st_mode) { + /* assume path_info already set */ + return OK; + } + +#ifdef HAVE_DRIVE_LETTERS + /* If the directory is x:\, then we don't want to strip + * the trailing slash since x: is not a valid directory. + */ + if (strlen(path) == 3 && path[1] == ':' && path[2] == '/') + bStripSlash = 0; + + + /* If UNC name == //machine/share/, do not + * advance over the trailing slash. Any other + * UNC name is OK to strip the slash. + */ + cp = end; + if (strlen(path) > 2 && path[0] == '/' && path[1] == '/' && + path[2] != '/' && cp[-1] == '/') { + char *p; + int iCount=0; + p = path; + while (p = strchr(p,'/')) { + p++; + iCount++; + } + + if (iCount == 4) + bStripSlash = 0; + } + + if (bStripSlash) +#endif + /* Advance over trailing slashes ... NOT part of filename + * if file is not a UNC name (Win32 only). + */ + for (cp = end; cp > path && cp[-1] == '/'; --cp) + continue; + + + while (cp > path) { + + /* See if the pathname ending here exists... */ + + *cp = '\0'; + + /* We must not stat() filenames that may cause os-specific system + * problems, such as "/file/aux" on DOS-abused filesystems. + * So pretend that they do not exist by returning an ENOENT error. + * This will force us to drop that part of the path and keep + * looking back for a "real" file that exists, while still allowing + * the "invalid" path parts within the PATH_INFO. + */ + if (!ap_os_is_filename_valid(path)) { + errno = ENOENT; + rv = -1; + } + else { + errno = 0; + rv = stat(path, &r->finfo); + } + + if (cp != end) + *cp = '/'; + + if (!rv) { + + /* + * Aha! Found something. If it was a directory, we will search + * contents of that directory for a multi_match, so the PATH_INFO + * argument starts with the component after that. + */ + + if (S_ISDIR(r->finfo.st_mode) && last_cp) { + r->finfo.st_mode = 0; /* No such file... */ + cp = last_cp; + } + + r->path_info = ap_pstrdup(r->pool, cp); + *cp = '\0'; + return OK; + } + /* must set this to zero, some stat()s may have corrupted it + * even if they returned an error. + */ + r->finfo.st_mode = 0; +#if defined(ENOENT) && defined(ENOTDIR) + if (errno == ENOENT || errno == ENOTDIR) { + last_cp = cp; + + while (--cp > path && *cp != '/') + continue; + + while (cp > path && cp[-1] == '/') + --cp; + } + else { +#if defined(EACCES) + if (errno != EACCES) +#endif + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "access to %s failed", r->uri); + return HTTP_FORBIDDEN; + } +#else +#error ENOENT || ENOTDIR not defined; please see the +#error comments at this line in the source for a workaround. + /* + * If ENOENT || ENOTDIR is not defined in one of the your OS's + * include files, Apache does not know how to check to see why the + * stat() of the index file failed; there are cases where it can fail + * even though the file exists. This means that it is possible for + * someone to get a directory listing of a directory even though + * there is an index (eg. index.html) file in it. If you do not have + * a problem with this, delete the above #error lines and start the + * compile again. If you need to do this, please submit a bug report + * from http://www.apache.org/bug_report.html letting us know that + * you needed to do this. Please be sure to include the operating + * system you are using. + */ + last_cp = cp; + + while (--cp > path && *cp != '/') + continue; + + while (cp > path && cp[-1] == '/') + --cp; +#endif /* ENOENT && ENOTDIR */ + } + return OK; +} + +static int directory_walk(request_rec *r) +{ + core_server_config *sconf = ap_get_module_config(r->server->module_config, + &core_module); + void *per_dir_defaults = r->server->lookup_defaults; + void **sec = (void **) sconf->sec->elts; + int num_sec = sconf->sec->nelts; + char *test_filename; + char *test_dirname; + int res; + unsigned i, num_dirs, iStart; + int j, test_filename_len; + + /* + * Are we dealing with a file? If not, we can (hopefuly) safely assume we + * have a handler that doesn't require one, but for safety's sake, and so + * we have something find_types() can get something out of, fake one. But + * don't run through the directory entries. + */ + + if (r->filename == NULL) { + r->filename = ap_pstrdup(r->pool, r->uri); + r->finfo.st_mode = 0; /* Not really a file... */ + r->per_dir_config = per_dir_defaults; + + return OK; + } + + /* + * Go down the directory hierarchy. Where we have to check for symlinks, + * do so. Where a .htaccess file has permission to override anything, + * try to find one. If either of these things fails, we could poke + * around, see why, and adjust the lookup_rec accordingly --- this might + * save us a call to get_path_info (with the attendant stat()s); however, + * for the moment, that's not worth the trouble. + * + * Fake filenames (i.e. proxy:) only match Directory sections. + */ + + if (!ap_os_is_path_absolute(r->filename)) + { + void *this_conf, *entry_config; + core_dir_config *entry_core; + char *entry_dir; + + for (j = 0; j < num_sec; ++j) { + + entry_config = sec[j]; + + entry_core = (core_dir_config *) + ap_get_module_config(entry_config, &core_module); + entry_dir = entry_core->d; + + this_conf = NULL; + if (entry_core->r) { + if (!ap_regexec(entry_core->r, r->filename, 0, NULL, 0)) + this_conf = entry_config; + } + else if (entry_core->d_is_fnmatch) { + if (!ap_fnmatch(entry_dir, r->filename, 0)) + this_conf = entry_config; + } + else if (!strncmp(r->filename, entry_dir, strlen(entry_dir))) + this_conf = entry_config; + + if (this_conf) + per_dir_defaults = ap_merge_per_dir_configs(r->pool, + per_dir_defaults, + this_conf); + } + + r->per_dir_config = per_dir_defaults; + + return OK; + } + + r->filename = ap_os_case_canonical_filename(r->pool, r->filename); + + res = get_path_info(r); + if (res != OK) { + return res; + } + + r->filename = ap_os_canonical_filename(r->pool, r->filename); + + test_filename = ap_pstrdup(r->pool, r->filename); + + ap_no2slash(test_filename); + num_dirs = ap_count_dirs(test_filename); + + if (!ap_os_is_filename_valid(r->filename)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Filename is not valid: %s", r->filename); + return HTTP_FORBIDDEN; + } + + if ((res = check_safe_file(r))) { + return res; + } + + test_filename_len = strlen(test_filename); + if (test_filename[test_filename_len - 1] == '/') + --num_dirs; + + if (S_ISDIR(r->finfo.st_mode)) + ++num_dirs; + + /* + * We will use test_dirname as scratch space while we build directory + * names during the walk. Profiling shows directory_walk to be a busy + * function so we try to avoid allocating lots of extra memory here. + * We need 2 extra bytes, one for trailing \0 and one because + * make_dirstr_prefix will add potentially one extra /. + */ + test_dirname = ap_palloc(r->pool, test_filename_len + 2); + + iStart = 1; +#ifdef WIN32 + /* If the name is a UNC name, then do not walk through the + * machine and share name (e.g. \\machine\share\) + */ + if (num_dirs > 3 && test_filename[0] == '/' && test_filename[1] == '/') + iStart = 4; +#endif + + /* j keeps track of which section we're on, see core_reorder_directories */ + j = 0; + for (i = iStart; i <= num_dirs; ++i) { + int overrides_here; + core_dir_config *core_dir = (core_dir_config *) + ap_get_module_config(per_dir_defaults, &core_module); + + /* + * XXX: this could be made faster by only copying the next component + * rather than copying the entire thing all over. + */ + ap_make_dirstr_prefix(test_dirname, test_filename, i); + + /* + * Do symlink checks first, because they are done with the + * permissions appropriate to the *parent* directory... + */ + + if ((res = check_symlinks(test_dirname, core_dir->opts))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Symbolic link not allowed: %s", test_dirname); + return res; + } + + /* + * Begin *this* level by looking for matching sections + * from access.conf. + */ + + for (; j < num_sec; ++j) { + void *entry_config = sec[j]; + core_dir_config *entry_core; + char *entry_dir; + void *this_conf; + + entry_core = (core_dir_config *) + ap_get_module_config(entry_config, &core_module); + entry_dir = entry_core->d; + + if (entry_core->r + || !ap_os_is_path_absolute(entry_dir) + || entry_core->d_components > i) + break; + + this_conf = NULL; + if (entry_core->d_is_fnmatch) { + if (!ap_fnmatch(entry_dir, test_dirname, FNM_PATHNAME)) { + this_conf = entry_config; + } + } + else if (!strcmp(test_dirname, entry_dir)) + this_conf = entry_config; + + if (this_conf) { + per_dir_defaults = ap_merge_per_dir_configs(r->pool, + per_dir_defaults, + this_conf); + core_dir = (core_dir_config *) + ap_get_module_config(per_dir_defaults, &core_module); + } + } + overrides_here = core_dir->override; + + /* If .htaccess files are enabled, check for one. */ + + if (overrides_here) { + void *htaccess_conf = NULL; + + res = ap_parse_htaccess(&htaccess_conf, r, overrides_here, + ap_pstrdup(r->pool, test_dirname), + sconf->access_name); + if (res) + return res; + + if (htaccess_conf) { + per_dir_defaults = ap_merge_per_dir_configs(r->pool, + per_dir_defaults, + htaccess_conf); + r->per_dir_config = per_dir_defaults; + } + } + } + + /* + * There's two types of IS_SPECIAL sections (see http_core.c), and we've + * already handled the proxy:-style stuff. Now we'll deal with the + * regexes. + */ + for (; j < num_sec; ++j) { + void *entry_config = sec[j]; + core_dir_config *entry_core; + + entry_core = (core_dir_config *) + ap_get_module_config(entry_config, &core_module); + + if (entry_core->r) { + if (!ap_regexec(entry_core->r, test_dirname, 0, NULL, REG_NOTEOL)) { + per_dir_defaults = + ap_merge_per_dir_configs(r->pool, per_dir_defaults, + entry_config); + } + } + } + r->per_dir_config = per_dir_defaults; + + /* + * Symlink permissions are determined by the parent. If the request is + * for a directory then applying the symlink test here would use the + * permissions of the directory as opposed to its parent. Consider a + * symlink pointing to a dir with a .htaccess disallowing symlinks. If + * you access /symlink (or /symlink/) you would get a 403 without this + * S_ISDIR test. But if you accessed /symlink/index.html, for example, + * you would *not* get the 403. + */ + if (!S_ISDIR(r->finfo.st_mode) + && (res = check_symlinks(r->filename, ap_allow_options(r)))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Symbolic link not allowed: %s", r->filename); + return res; + } + return OK; /* Can only "fail" if access denied by the + * symlink goop. */ +} + +static int location_walk(request_rec *r) +{ + core_server_config *sconf = ap_get_module_config(r->server->module_config, + &core_module); + void *per_dir_defaults = r->per_dir_config; + void **url = (void **) sconf->sec_url->elts; + int len, num_url = sconf->sec_url->nelts; + char *test_location; + void *this_conf, *entry_config; + core_dir_config *entry_core; + char *entry_url; + int j; + + if (!num_url) { + return OK; + } + + /* Location and LocationMatch differ on their behaviour w.r.t. multiple + * slashes. Location matches multiple slashes with a single slash, + * LocationMatch doesn't. An exception, for backwards brokenness is + * absoluteURIs... in which case neither match multiple slashes. + */ + if (r->uri[0] != '/') { + test_location = r->uri; + } + else { + test_location = ap_pstrdup(r->pool, r->uri); + ap_no2slash(test_location); + } + + /* Go through the location entries, and check for matches. */ + + /* we apply the directive sections in some order; + * should really try them with the most general first. + */ + for (j = 0; j < num_url; ++j) { + + entry_config = url[j]; + + entry_core = (core_dir_config *) + ap_get_module_config(entry_config, &core_module); + entry_url = entry_core->d; + + len = strlen(entry_url); + + this_conf = NULL; + + if (entry_core->r) { + if (!ap_regexec(entry_core->r, r->uri, 0, NULL, 0)) + this_conf = entry_config; + } + else if (entry_core->d_is_fnmatch) { + if (!ap_fnmatch(entry_url, test_location, FNM_PATHNAME)) { + this_conf = entry_config; + } + } + else if (!strncmp(test_location, entry_url, len) && + (entry_url[len - 1] == '/' || + test_location[len] == '/' || test_location[len] == '\0')) + this_conf = entry_config; + + if (this_conf) + per_dir_defaults = ap_merge_per_dir_configs(r->pool, + per_dir_defaults, this_conf); + } + r->per_dir_config = per_dir_defaults; + + return OK; +} + +static int file_walk(request_rec *r) +{ + core_dir_config *conf = ap_get_module_config(r->per_dir_config, &core_module); + void *per_dir_defaults = r->per_dir_config; + void **file = (void **) conf->sec->elts; + int num_files = conf->sec->nelts; + char *test_file; + + /* get the basename */ + test_file = strrchr(r->filename, '/'); + if (test_file == NULL) { + test_file = r->filename; + } + else { + ++test_file; + } + + /* Go through the file entries, and check for matches. */ + + if (num_files) { + void *this_conf, *entry_config; + core_dir_config *entry_core; + char *entry_file; + int j; + + /* we apply the directive sections in some order; + * should really try them with the most general first. + */ + for (j = 0; j < num_files; ++j) { + + entry_config = file[j]; + + entry_core = (core_dir_config *) + ap_get_module_config(entry_config, &core_module); + entry_file = entry_core->d; + + this_conf = NULL; + + if (entry_core->r) { + if (!ap_regexec(entry_core->r, test_file, 0, NULL, 0)) + this_conf = entry_config; + } + else if (entry_core->d_is_fnmatch) { + if (!ap_fnmatch(entry_file, test_file, FNM_PATHNAME)) { + this_conf = entry_config; + } + } + else if (!strcmp(test_file, entry_file)) { + this_conf = entry_config; + } + + if (this_conf) + per_dir_defaults = ap_merge_per_dir_configs(r->pool, + per_dir_defaults, + this_conf); + } + r->per_dir_config = per_dir_defaults; + } + return OK; +} + +/***************************************************************** + * + * The sub_request mechanism. + * + * Fns to look up a relative URI from, e.g., a map file or SSI document. + * These do all access checks, etc., but don't actually run the transaction + * ... use run_sub_req below for that. Also, be sure to use destroy_sub_req + * as appropriate if you're likely to be creating more than a few of these. + * (An early Apache version didn't destroy the sub_reqs used in directory + * indexing. The result, when indexing a directory with 800-odd files in + * it, was massively excessive storage allocation). + * + * Note more manipulation of protocol-specific vars in the request + * structure... + */ + +static request_rec *make_sub_request(const request_rec *r) +{ + pool *rrp = ap_make_sub_pool(r->pool); + request_rec *rr = ap_pcalloc(rrp, sizeof(request_rec)); + + rr->pool = rrp; + return rr; +} + +API_EXPORT(request_rec *) ap_sub_req_method_uri(const char *method, + const char *new_file, + const request_rec *r) +{ + request_rec *rnew; + int res; + char *udir; + + rnew = make_sub_request(r); + rnew->hostname = r->hostname; + rnew->request_time = r->request_time; + rnew->connection = r->connection; + rnew->server = r->server; + rnew->request_config = ap_create_request_config(rnew->pool); + rnew->htaccess = r->htaccess; + rnew->per_dir_config = r->server->lookup_defaults; + + ap_set_sub_req_protocol(rnew, r); + + /* would be nicer to pass "method" to ap_set_sub_req_protocol */ + rnew->method = method; + rnew->method_number = ap_method_number_of(method); + + if (new_file[0] == '/') + ap_parse_uri(rnew, new_file); + else { + udir = ap_make_dirstr_parent(rnew->pool, r->uri); + udir = ap_escape_uri(rnew->pool, udir); /* re-escape it */ + ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_file)); + } + + res = ap_unescape_url(rnew->uri); + if (res) { + rnew->status = res; + return rnew; + } + + ap_getparents(rnew->uri); + + if ((res = location_walk(rnew))) { + rnew->status = res; + return rnew; + } + + res = ap_translate_name(rnew); + if (res) { + rnew->status = res; + return rnew; + } + + /* + * We could be clever at this point, and avoid calling directory_walk, + * etc. However, we'd need to test that the old and new filenames contain + * the same directory components, so it would require duplicating the + * start of translate_name. Instead we rely on the cache of .htaccess + * results. + * + * NB: directory_walk() clears the per_dir_config, so we don't inherit + * from location_walk() above + */ + + if ((res = directory_walk(rnew)) + || (res = file_walk(rnew)) + || (res = location_walk(rnew)) + || ((ap_satisfies(rnew) == SATISFY_ALL + || ap_satisfies(rnew) == SATISFY_NOSPEC) + ? ((res = ap_check_access(rnew)) + || (ap_some_auth_required(rnew) + && ((res = ap_check_user_id(rnew)) + || (res = ap_check_auth(rnew))))) + : ((res = ap_check_access(rnew)) + && (!ap_some_auth_required(rnew) + || ((res = ap_check_user_id(rnew)) + || (res = ap_check_auth(rnew))))) + ) + || (res = ap_find_types(rnew)) + || (res = ap_run_fixups(rnew)) + ) { + rnew->status = res; + } + return rnew; +} + +API_EXPORT(request_rec *) ap_sub_req_lookup_uri(const char *new_file, + const request_rec *r) +{ + return ap_sub_req_method_uri("GET", new_file, r); +} + +API_EXPORT(request_rec *) ap_sub_req_lookup_file(const char *new_file, + const request_rec *r) +{ + request_rec *rnew; + int res; + char *fdir; + + rnew = make_sub_request(r); + rnew->hostname = r->hostname; + rnew->request_time = r->request_time; + rnew->connection = r->connection; + rnew->server = r->server; + rnew->request_config = ap_create_request_config(rnew->pool); + rnew->htaccess = r->htaccess; + + ap_set_sub_req_protocol(rnew, r); + fdir = ap_make_dirstr_parent(rnew->pool, r->filename); + + /* + * Check for a special case... if there are no '/' characters in new_file + * at all, then we are looking at a relative lookup in the same + * directory. That means we won't have to redo directory_walk, and we may + * not even have to redo access checks. + */ + + if (strchr(new_file, '/') == NULL) { + char *udir = ap_make_dirstr_parent(rnew->pool, r->uri); + + rnew->uri = ap_make_full_path(rnew->pool, udir, new_file); + rnew->filename = ap_make_full_path(rnew->pool, fdir, new_file); + ap_parse_uri(rnew, rnew->uri); /* fill in parsed_uri values */ + if (stat(rnew->filename, &rnew->finfo) < 0) { + rnew->finfo.st_mode = 0; + } + + if ((res = check_safe_file(rnew))) { + rnew->status = res; + return rnew; + } + + rnew->per_dir_config = r->per_dir_config; + + /* + * no matter what, if it's a subdirectory, we need to re-run + * directory_walk + */ + if (S_ISDIR(rnew->finfo.st_mode)) { + res = directory_walk(rnew); + if (!res) { + res = file_walk(rnew); + } + } + else { + if ((res = check_symlinks(rnew->filename, ap_allow_options(rnew)))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, rnew, + "Symbolic link not allowed: %s", rnew->filename); + rnew->status = res; + return rnew; + } + /* + * do a file_walk, if it doesn't change the per_dir_config then + * we know that we don't have to redo all the access checks + */ + if ((res = file_walk(rnew))) { + rnew->status = res; + return rnew; + } + if (rnew->per_dir_config == r->per_dir_config) { + if ((res = ap_find_types(rnew)) || (res = ap_run_fixups(rnew))) { + rnew->status = res; + } + return rnew; + } + } + } + else { + /* XXX: @@@: What should be done with the parsed_uri values? */ + ap_parse_uri(rnew, new_file); /* fill in parsed_uri values */ + /* + * XXX: this should be set properly like it is in the same-dir case + * but it's actually sometimes to impossible to do it... because the + * file may not have a uri associated with it -djg + */ + rnew->uri = "INTERNALLY GENERATED file-relative req"; + rnew->filename = ((ap_os_is_path_absolute(new_file)) ? + ap_pstrdup(rnew->pool, new_file) : + ap_make_full_path(rnew->pool, fdir, new_file)); + rnew->per_dir_config = r->server->lookup_defaults; + res = directory_walk(rnew); + if (!res) { + res = file_walk(rnew); + } + } + + if (res + || ((ap_satisfies(rnew) == SATISFY_ALL + || ap_satisfies(rnew) == SATISFY_NOSPEC) + ? ((res = ap_check_access(rnew)) + || (ap_some_auth_required(rnew) + && ((res = ap_check_user_id(rnew)) + || (res = ap_check_auth(rnew))))) + : ((res = ap_check_access(rnew)) + && (!ap_some_auth_required(rnew) + || ((res = ap_check_user_id(rnew)) + || (res = ap_check_auth(rnew))))) + ) + || (res = ap_find_types(rnew)) + || (res = ap_run_fixups(rnew)) + ) { + rnew->status = res; + } + return rnew; +} + +API_EXPORT(int) ap_run_sub_req(request_rec *r) +{ +#ifndef CHARSET_EBCDIC + int retval = ap_invoke_handler(r); +#else /*CHARSET_EBCDIC*/ + /* Save the EBCDIC conversion setting of the caller across subrequests */ + int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII); + int retval = ap_invoke_handler(r); + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert); +#endif /*CHARSET_EBCDIC*/ + ap_finalize_sub_req_protocol(r); + return retval; +} + +API_EXPORT(void) ap_destroy_sub_req(request_rec *r) +{ + /* Reclaim the space */ + ap_destroy_pool(r->pool); +} + +/***************************************************************** + * + * Mainline request processing... + */ + +API_EXPORT(void) ap_die(int type, request_rec *r) +{ + int error_index = ap_index_of_response(type); + char *custom_response = ap_response_code_string(r, error_index); + int recursive_error = 0; + + if (type == DONE) { + ap_finalize_request_protocol(r); + return; + } + + /* + * The following takes care of Apache redirects to custom response URLs + * Note that if we are already dealing with the response to some other + * error condition, we just report on the original error, and give up on + * any attempt to handle the other thing "intelligently"... + */ + + if (r->status != HTTP_OK) { + recursive_error = type; + + while (r->prev && (r->prev->status != HTTP_OK)) + r = r->prev; /* Get back to original error */ + + type = r->status; + custom_response = NULL; /* Do NOT retry the custom thing! */ + } + + r->status = type; + + /* + * This test is done here so that none of the auth modules needs to know + * about proxy authentication. They treat it like normal auth, and then + * we tweak the status. + */ + if (r->status == AUTH_REQUIRED && r->proxyreq) { + r->status = HTTP_PROXY_AUTHENTICATION_REQUIRED; + } + + /* + * If we want to keep the connection, be sure that the request body + * (if any) has been read. + */ + if ((r->status != HTTP_NOT_MODIFIED) && (r->status != HTTP_NO_CONTENT) + && !ap_status_drops_connection(r->status) + && r->connection && (r->connection->keepalive != -1)) { + + (void) ap_discard_request_body(r); + } + + /* + * Two types of custom redirects --- plain text, and URLs. Plain text has + * a leading '"', so the URL code, here, is triggered on its absence + */ + + if (custom_response && custom_response[0] != '"') { + + if (ap_is_url(custom_response)) { + /* + * The URL isn't local, so lets drop through the rest of this + * apache code, and continue with the usual REDIRECT handler. + * But note that the client will ultimately see the wrong + * status... + */ + r->status = REDIRECT; + ap_table_setn(r->headers_out, "Location", custom_response); + } + else if (custom_response[0] == '/') { + const char *error_notes; + r->no_local_copy = 1; /* Do NOT send USE_LOCAL_COPY for + * error documents! */ + /* + * This redirect needs to be a GET no matter what the original + * method was. + */ + ap_table_setn(r->subprocess_env, "REQUEST_METHOD", r->method); + + /* + * Provide a special method for modules to communicate + * more informative (than the plain canned) messages to us. + * Propagate them to ErrorDocuments via the ERROR_NOTES variable: + */ + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_table_setn(r->subprocess_env, "ERROR_NOTES", error_notes); + } + r->method = ap_pstrdup(r->pool, "GET"); + r->method_number = M_GET; + ap_internal_redirect(custom_response, r); + return; + } + else { + /* + * Dumb user has given us a bad url to redirect to --- fake up + * dying with a recursive server error... + */ + recursive_error = SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid error redirection directive: %s", + custom_response); + } + } + ap_send_error_response(r, recursive_error); +} + +static void decl_die(int status, char *phase, request_rec *r) +{ + if (status == DECLINED) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, r, + "configuration error: couldn't %s: %s", phase, r->uri); + ap_die(SERVER_ERROR, r); + } + else + ap_die(status, r); +} + +API_EXPORT(int) ap_some_auth_required(request_rec *r) +{ + /* Is there a require line configured for the type of *this* req? */ + + const array_header *reqs_arr = ap_requires(r); + require_line *reqs; + int i; + + if (!reqs_arr) + return 0; + + reqs = (require_line *) reqs_arr->elts; + + for (i = 0; i < reqs_arr->nelts; ++i) + if (reqs[i].method_mask & (1 << r->method_number)) + return 1; + + return 0; +} + +static void process_request_internal(request_rec *r) +{ + int access_status; + + /* Ignore embedded %2F's in path for proxy requests */ + if (!r->proxyreq && r->parsed_uri.path) { + access_status = ap_unescape_url(r->parsed_uri.path); + if (access_status) { + ap_die(access_status, r); + return; + } + } + + ap_getparents(r->uri); /* OK --- shrinking transformations... */ + + if ((access_status = location_walk(r))) { + ap_die(access_status, r); + return; + } + + if ((access_status = ap_translate_name(r))) { + decl_die(access_status, "translate", r); + return; + } + + if (!r->proxyreq) { + /* + * We don't want TRACE to run through the normal handler set, we + * handle it specially. + */ + if (r->method_number == M_TRACE) { + if ((access_status = ap_send_http_trace(r))) + ap_die(access_status, r); + else + ap_finalize_request_protocol(r); + return; + } + } + + if (r->proto_num > HTTP_VERSION(1,0) && ap_table_get(r->subprocess_env, "downgrade-1.0")) { + r->proto_num = HTTP_VERSION(1,0); + } + + /* + * NB: directory_walk() clears the per_dir_config, so we don't inherit + * from location_walk() above + */ + + if ((access_status = directory_walk(r))) { + ap_die(access_status, r); + return; + } + + if ((access_status = file_walk(r))) { + ap_die(access_status, r); + return; + } + + if ((access_status = location_walk(r))) { + ap_die(access_status, r); + return; + } + + if ((access_status = ap_header_parse(r))) { + ap_die(access_status, r); + return; + } + + switch (ap_satisfies(r)) { + case SATISFY_ALL: + case SATISFY_NOSPEC: + if ((access_status = ap_check_access(r)) != 0) { + decl_die(access_status, "check access", r); + return; + } + if (ap_some_auth_required(r)) { + if (((access_status = ap_check_user_id(r)) != 0) || !ap_auth_type(r)) { + decl_die(access_status, ap_auth_type(r) + ? "check user. No user file?" + : "perform authentication. AuthType not set!", r); + return; + } + if (((access_status = ap_check_auth(r)) != 0) || !ap_auth_type(r)) { + decl_die(access_status, ap_auth_type(r) + ? "check access. No groups file?" + : "perform authentication. AuthType not set!", r); + return; + } + } + break; + case SATISFY_ANY: + if (((access_status = ap_check_access(r)) != 0) || !ap_auth_type(r)) { + if (!ap_some_auth_required(r)) { + decl_die(access_status, ap_auth_type(r) + ? "check access" + : "perform authentication. AuthType not set!", r); + return; + } + if (((access_status = ap_check_user_id(r)) != 0) || !ap_auth_type(r)) { + decl_die(access_status, ap_auth_type(r) + ? "check user. No user file?" + : "perform authentication. AuthType not set!", r); + return; + } + if (((access_status = ap_check_auth(r)) != 0) || !ap_auth_type(r)) { + decl_die(access_status, ap_auth_type(r) + ? "check access. No groups file?" + : "perform authentication. AuthType not set!", r); + return; + } + } + break; + } + + if (! (r->proxyreq + && r->parsed_uri.scheme != NULL + && strcmp(r->parsed_uri.scheme, "http") == 0) ) { + if ((access_status = ap_find_types(r)) != 0) { + decl_die(access_status, "find types", r); + return; + } + } + + if ((access_status = ap_run_fixups(r)) != 0) { + ap_die(access_status, r); + return; + } + + if ((access_status = ap_invoke_handler(r)) != 0) { + ap_die(access_status, r); + return; + } + + /* Take care of little things that need to happen when we're done */ + ap_finalize_request_protocol(r); +} + +void ap_process_request(request_rec *r) +{ + int old_stat; + + if (ap_extended_status) + ap_time_process_request(r->connection->child_num, START_PREQUEST); + + process_request_internal(r); + + old_stat = ap_update_child_status(r->connection->child_num, + SERVER_BUSY_LOG, r); + + /* + * We want to flush the last packet if this isn't a pipelining connection + * *before* we start into logging. Suppose that the logging causes a DNS + * lookup to occur, which may have a high latency. If we hold off on + * this packet, then it'll appear like the link is stalled when really + * it's the application that's stalled. + */ + ap_bhalfduplex(r->connection->client); + ap_log_transaction(r); + + (void) ap_update_child_status(r->connection->child_num, old_stat, r); + if (ap_extended_status) + ap_time_process_request(r->connection->child_num, STOP_PREQUEST); +} + +static table *rename_original_env(pool *p, table *t) +{ + array_header *env_arr = ap_table_elts(t); + table_entry *elts = (table_entry *) env_arr->elts; + table *new = ap_make_table(p, env_arr->nalloc); + int i; + + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) + continue; + ap_table_setn(new, ap_pstrcat(p, "REDIRECT_", elts[i].key, NULL), + elts[i].val); + } + + return new; +} + +static request_rec *internal_internal_redirect(const char *new_uri, request_rec *r) +{ + int access_status; + request_rec *new = (request_rec *) ap_pcalloc(r->pool, sizeof(request_rec)); + + new->connection = r->connection; + new->server = r->server; + new->pool = r->pool; + + /* + * A whole lot of this really ought to be shared with http_protocol.c... + * another missing cleanup. It's particularly inappropriate to be + * setting header_only, etc., here. + */ + + new->method = r->method; + new->method_number = r->method_number; + ap_parse_uri(new, new_uri); + new->request_config = ap_create_request_config(r->pool); + new->per_dir_config = r->server->lookup_defaults; + + new->prev = r; + r->next = new; + + /* Inherit the rest of the protocol info... */ + + new->the_request = r->the_request; + + new->allowed = r->allowed; + + new->status = r->status; + new->assbackwards = r->assbackwards; + new->header_only = r->header_only; + new->protocol = r->protocol; + new->proto_num = r->proto_num; + new->hostname = r->hostname; + new->request_time = r->request_time; + new->main = r->main; + + new->headers_in = r->headers_in; + new->headers_out = ap_make_table(r->pool, 12); + new->err_headers_out = r->err_headers_out; + new->subprocess_env = rename_original_env(r->pool, r->subprocess_env); + new->notes = ap_make_table(r->pool, 5); + + new->htaccess = r->htaccess; + new->no_cache = r->no_cache; + new->expecting_100 = r->expecting_100; + new->no_local_copy = r->no_local_copy; + new->read_length = r->read_length; /* We can only read it once */ + new->vlist_validator = r->vlist_validator; + + ap_table_setn(new->subprocess_env, "REDIRECT_STATUS", + ap_psprintf(r->pool, "%d", r->status)); + + /* + * XXX: hmm. This is because mod_setenvif and mod_unique_id really need + * to do their thing on internal redirects as well. Perhaps this is a + * misnamed function. + */ + if ((access_status = ap_run_post_read_request(new))) { + ap_die(access_status, new); + return NULL; + } + + return new; +} + +API_EXPORT(void) ap_internal_redirect(const char *new_uri, request_rec *r) +{ + request_rec *new = internal_internal_redirect(new_uri, r); + process_request_internal(new); +} + +/* This function is designed for things like actions or CGI scripts, when + * using AddHandler, and you want to preserve the content type across + * an internal redirect. + */ +API_EXPORT(void) ap_internal_redirect_handler(const char *new_uri, request_rec *r) +{ + request_rec *new = internal_internal_redirect(new_uri, r); + if (r->handler) + new->content_type = r->content_type; + process_request_internal(new); +} + +/* + * Is it the initial main request, which we only get *once* per HTTP request? + */ +API_EXPORT(int) ap_is_initial_req(request_rec *r) +{ + return + (r->main == NULL) /* otherwise, this is a sub-request */ + && + (r->prev == NULL); /* otherwise, this is an internal redirect */ +} + +/* + * Function to set the r->mtime field to the specified value if it's later + * than what's already there. + */ +API_EXPORT(time_t) ap_update_mtime(request_rec *r, time_t dependency_mtime) +{ + if (r->mtime < dependency_mtime) { + r->mtime = dependency_mtime; + } + return r->mtime; +} diff --git a/modules/http/mod_mime.c b/modules/http/mod_mime.c new file mode 100644 index 00000000000..f27356bfdd3 --- /dev/null +++ b/modules/http/mod_mime.c @@ -0,0 +1,402 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_mime.c: Sends/gets MIME headers for requests + * + * Rob McCool + * + */ + +#define MIME_PRIVATE + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" + +typedef struct handlers_info { + char *name; +} handlers_info; + +typedef struct { + table *forced_types; /* Additional AddTyped stuff */ + table *encoding_types; /* Added with AddEncoding... */ + table *language_types; /* Added with AddLanguage... */ + table *handlers; /* Added with AddHandler... */ + array_header *handlers_remove; /* List of handlers to remove */ + + char *type; /* Type forced with ForceType */ + char *handler; /* Handler forced with SetHandler */ + char *default_language; /* Language if no AddLanguage ext found */ +} mime_dir_config; + +module MODULE_VAR_EXPORT mime_module; + +static void *create_mime_dir_config(pool *p, char *dummy) +{ + mime_dir_config *new = + (mime_dir_config *) ap_palloc(p, sizeof(mime_dir_config)); + + new->forced_types = ap_make_table(p, 4); + new->encoding_types = ap_make_table(p, 4); + new->language_types = ap_make_table(p, 4); + new->handlers = ap_make_table(p, 4); + new->handlers_remove = ap_make_array(p, 4, sizeof(handlers_info)); + + new->type = NULL; + new->handler = NULL; + new->default_language = NULL; + + return new; +} + +static void *merge_mime_dir_configs(pool *p, void *basev, void *addv) +{ + mime_dir_config *base = (mime_dir_config *) basev; + mime_dir_config *add = (mime_dir_config *) addv; + mime_dir_config *new = + (mime_dir_config *) ap_palloc(p, sizeof(mime_dir_config)); + int i; + handlers_info *hand; + + hand = (handlers_info *) add->handlers_remove->elts; + for (i = 0; i < add->handlers_remove->nelts; i++) { + ap_table_unset(base->handlers, hand[i].name); + } + + new->forced_types = ap_overlay_tables(p, add->forced_types, + base->forced_types); + new->encoding_types = ap_overlay_tables(p, add->encoding_types, + base->encoding_types); + new->language_types = ap_overlay_tables(p, add->language_types, + base->language_types); + new->handlers = ap_overlay_tables(p, add->handlers, + base->handlers); + + new->type = add->type ? add->type : base->type; + new->handler = add->handler ? add->handler : base->handler; + new->default_language = add->default_language ? + add->default_language : base->default_language; + + return new; +} + +static const char *add_type(cmd_parms *cmd, mime_dir_config * m, char *ct, + char *ext) +{ + if (*ext == '.') + ++ext; + ap_str_tolower(ct); + ap_table_setn(m->forced_types, ext, ct); + return NULL; +} + +static const char *add_encoding(cmd_parms *cmd, mime_dir_config * m, char *enc, + char *ext) +{ + if (*ext == '.') + ++ext; + ap_str_tolower(enc); + ap_table_setn(m->encoding_types, ext, enc); + return NULL; +} + +static const char *add_language(cmd_parms *cmd, mime_dir_config * m, char *lang, + char *ext) +{ + if (*ext == '.') + ++ext; + ap_str_tolower(lang); + ap_table_setn(m->language_types, ext, lang); + return NULL; +} + +static const char *add_handler(cmd_parms *cmd, mime_dir_config * m, char *hdlr, + char *ext) +{ + if (*ext == '.') + ++ext; + ap_str_tolower(hdlr); + ap_table_setn(m->handlers, ext, hdlr); + return NULL; +} + +/* + * Note handler names that should be un-added for this location. This + * will keep the association from being inherited, as well, but not + * from being re-added at a subordinate level. + */ +static const char *remove_handler(cmd_parms *cmd, void *m, char *ext) +{ + mime_dir_config *mcfg = (mime_dir_config *) m; + handlers_info *hand; + + if (*ext == '.') { + ++ext; + } + hand = (handlers_info *) ap_push_array(mcfg->handlers_remove); + hand->name = ap_pstrdup(cmd->pool, ext); + return NULL; +} + +/* The sole bit of server configuration that the MIME module has is + * the name of its config file, so... + */ + +static const char *set_types_config(cmd_parms *cmd, void *dummy, char *arg) +{ + ap_set_module_config(cmd->server->module_config, &mime_module, arg); + return NULL; +} + +static const command_rec mime_cmds[] = +{ + {"AddType", add_type, NULL, OR_FILEINFO, ITERATE2, + "a mime type followed by one or more file extensions"}, + {"AddEncoding", add_encoding, NULL, OR_FILEINFO, ITERATE2, + "an encoding (e.g., gzip), followed by one or more file extensions"}, + {"AddLanguage", add_language, NULL, OR_FILEINFO, ITERATE2, + "a language (e.g., fr), followed by one or more file extensions"}, + {"AddHandler", add_handler, NULL, OR_FILEINFO, ITERATE2, + "a handler name followed by one or more file extensions"}, + {"ForceType", ap_set_string_slot_lower, + (void *)XtOffsetOf(mime_dir_config, type), OR_FILEINFO, TAKE1, + "a media type"}, + {"RemoveHandler", remove_handler, NULL, OR_FILEINFO, ITERATE, + "one or more file extensions"}, + {"SetHandler", ap_set_string_slot_lower, + (void *)XtOffsetOf(mime_dir_config, handler), OR_FILEINFO, TAKE1, + "a handler name"}, + {"TypesConfig", set_types_config, NULL, RSRC_CONF, TAKE1, + "the MIME types config file"}, + {"DefaultLanguage", ap_set_string_slot, + (void*)XtOffsetOf(mime_dir_config, default_language), OR_FILEINFO, TAKE1, + "language to use for documents with no other language file extension" }, + {NULL} +}; + +/* Hash table --- only one of these per daemon; virtual hosts can + * get private versions through AddType... + */ + +#define MIME_HASHSIZE (32) +#define hash(i) (ap_tolower(i) % MIME_HASHSIZE) + +static table *hash_buckets[MIME_HASHSIZE]; + +static void init_mime(server_rec *s, pool *p) +{ + configfile_t *f; + char l[MAX_STRING_LEN]; + int x; + char *types_confname = ap_get_module_config(s->module_config, &mime_module); + + if (!types_confname) + types_confname = TYPES_CONFIG_FILE; + + types_confname = ap_server_root_relative(p, types_confname); + + if (!(f = ap_pcfg_openfile(p, types_confname))) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "could not open mime types log file %s.", types_confname); + exit(1); + } + + for (x = 0; x < MIME_HASHSIZE; x++) + hash_buckets[x] = ap_make_table(p, 10); + + while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) { + const char *ll = l, *ct; + + if (l[0] == '#') + continue; + ct = ap_getword_conf(p, &ll); + + while (ll[0]) { + char *ext = ap_getword_conf(p, &ll); + ap_str_tolower(ext); /* ??? */ + ap_table_setn(hash_buckets[hash(ext[0])], ext, ct); + } + } + ap_cfg_closefile(f); +} + +static int find_ct(request_rec *r) +{ + const char *fn = strrchr(r->filename, '/'); + mime_dir_config *conf = + (mime_dir_config *) ap_get_module_config(r->per_dir_config, &mime_module); + char *ext; + const char *orighandler = r->handler; + const char *type; + + if (S_ISDIR(r->finfo.st_mode)) { + r->content_type = DIR_MAGIC_TYPE; + return OK; + } + + /* TM -- FIXME + * if r->filename does not contain a '/', the following passes a null + * pointer to getword, causing a SEGV .. + */ + + if (fn == NULL) + fn = r->filename; + + /* Parse filename extensions, which can be in any order */ + while ((ext = ap_getword(r->pool, &fn, '.')) && *ext) { + int found = 0; + + /* Check for Content-Type */ + if ((type = ap_table_get(conf->forced_types, ext)) + || (type = ap_table_get(hash_buckets[hash(*ext)], ext))) { + r->content_type = type; + found = 1; + } + + /* Check for Content-Language */ + if ((type = ap_table_get(conf->language_types, ext))) { + const char **new; + + r->content_language = type; /* back compat. only */ + if (!r->content_languages) + r->content_languages = ap_make_array(r->pool, 2, sizeof(char *)); + new = (const char **) ap_push_array(r->content_languages); + *new = type; + found = 1; + } + + /* Check for Content-Encoding */ + if ((type = ap_table_get(conf->encoding_types, ext))) { + if (!r->content_encoding) + r->content_encoding = type; + else + r->content_encoding = ap_pstrcat(r->pool, r->content_encoding, + ", ", type, NULL); + found = 1; + } + + /* Check for a special handler, but not for proxy request */ + if ((type = ap_table_get(conf->handlers, ext)) && !r->proxyreq) { + r->handler = type; + found = 1; + } + + /* This is to deal with cases such as foo.gif.bak, which we want + * to not have a type. So if we find an unknown extension, we + * zap the type/language/encoding and reset the handler + */ + + if (!found) { + r->content_type = NULL; + r->content_language = NULL; + r->content_languages = NULL; + r->content_encoding = NULL; + r->handler = orighandler; + } + + } + + /* Set default language, if none was specified by the extensions + * and we have a DefaultLanguage setting in force + */ + + if (!r->content_languages && conf->default_language) { + const char **new; + + r->content_language = conf->default_language; /* back compat. only */ + if (!r->content_languages) + r->content_languages = ap_make_array(r->pool, 2, sizeof(char *)); + new = (const char **) ap_push_array(r->content_languages); + *new = conf->default_language; + } + + /* Check for overrides with ForceType/SetHandler */ + + if (conf->type && strcmp(conf->type, "none")) + r->content_type = conf->type; + if (conf->handler && strcmp(conf->handler, "none")) + r->handler = conf->handler; + + if (!r->content_type) + return DECLINED; + + return OK; +} + +module MODULE_VAR_EXPORT mime_module = +{ + STANDARD_MODULE_STUFF, + init_mime, /* initializer */ + create_mime_dir_config, /* dir config creator */ + merge_mime_dir_configs, /* dir config merger */ + NULL, /* server config */ + NULL, /* merge server config */ + mime_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + find_ct, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/loggers/.indent.pro b/modules/loggers/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/modules/loggers/.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/loggers/mod_log_config.c b/modules/loggers/mod_log_config.c new file mode 100644 index 00000000000..fcdebcc4f95 --- /dev/null +++ b/modules/loggers/mod_log_config.c @@ -0,0 +1,1137 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * Modified by djm@va.pubnix.com: + * If no TransferLog is given explicitly, decline to log. + * + * This is module implements the TransferLog directive (same as the + * common log module), and additional directives, LogFormat and CustomLog. + * + * + * Syntax: + * + * TransferLog fn Logs transfers to fn in standard log format, unless + * a custom format is set with LogFormat + * LogFormat format Set a log format from TransferLog files + * CustomLog fn format + * Log to file fn with format given by the format + * argument + * + * CookieLog fn For backwards compatability with old Cookie + * logging module - now deprecated. + * + * There can be any number of TransferLog and CustomLog + * commands. Each request will be logged to _ALL_ the + * named files, in the appropriate format. + * + * If no TransferLog or CustomLog directive appears in a VirtualHost, + * the request will be logged to the log file(s) defined outside + * the virtual host section. If a TransferLog or CustomLog directive + * appears in the VirtualHost section, the log files defined outside + * the VirtualHost will _not_ be used. This makes this module compatable + * with the CLF and config log modules, where the use of TransferLog + * inside the VirtualHost section overrides its use outside. + * + * Examples: + * + * TransferLog logs/access_log + * + * LogFormat "... custom format ..." + * TransferLog log/virtual_only + * CustomLog log/virtual_useragents "%t %{user-agent}i" + * + * + * This will log using CLF to access_log any requests handled by the + * main server, while any requests to the virtual host will be logged + * with the "... custom format..." to virtual_only _AND_ using + * the custom user-agent log to virtual_useragents. + * + * Note that the NCSA referer and user-agent logs are easily added with + * CustomLog: + * CustomLog logs/referer "%{referer}i -> %U" + * CustomLog logs/agent "%{user-agent}i" + * + * RefererIgnore functionality can be obtained with conditional + * logging (SetEnvIf and CustomLog ... env=!VAR). + * + * But using this method allows much easier modification of the + * log format, e.g. to log hosts along with UA: + * CustomLog logs/referer "%{referer}i %U %h" + * + * The argument to LogFormat and CustomLog is a string, which can include + * literal characters copied into the log files, and '%' directives as + * follows: + * + * %...b: bytes sent, excluding HTTP headers. + * %...{FOOBAR}e: The contents of the environment variable FOOBAR + * %...f: filename + * %...h: remote host + * %...a: remote IP-address + * %...A: local IP-address + * %...{Foobar}i: The contents of Foobar: header line(s) in the request + * sent to the client. + * %...l: remote logname (from identd, if supplied) + * %...{Foobar}n: The contents of note "Foobar" from another module. + * %...{Foobar}o: The contents of Foobar: header line(s) in the reply. + * %...p: the port the request was served to + * %...P: the process ID of the child that serviced the request. + * %...r: first line of request + * %...s: status. For requests that got internally redirected, this + * is status of the *original* request --- %...>s for the last. + * %...t: time, in common log format time format + * %...{format}t: The time, in the form given by format, which should + * be in strftime(3) format. + * %...T: the time taken to serve the request, in seconds. + * %...u: remote user (from auth; may be bogus if return status (%s) is 401) + * %...U: the URL path requested. + * %...v: the configured name of the server (i.e. which virtual host?) + * %...V: the server name according to the UseCanonicalName setting + * + * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can + * indicate conditions for inclusion of the item (which will cause it + * to be replaced with '-' if the condition is not met). Note that + * there is no escaping performed on the strings from %r, %...i and + * %...o; some with long memories may remember that I thought this was + * a bad idea, once upon a time, and I'm still not comfortable with + * it, but it is difficult to see how to "do the right thing" with all + * of '%..i', unless we URL-escape everything and break with CLF. + * + * The forms of condition are a list of HTTP status codes, which may + * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs + * User-agent: on 400 errors and 501 errors (Bad Request, Not + * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all + * requests which did *not* return some sort of normal status. + * + * The default LogFormat reproduces CLF; see below. + * + * The way this is supposed to work with virtual hosts is as follows: + * a virtual host can have its own LogFormat, or its own TransferLog. + * If it doesn't have its own LogFormat, it inherits from the main + * server. If it doesn't have its own TransferLog, it writes to the + * same descriptor (meaning the same process for "| ..."). + * + * --- rst */ + +#define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b" + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" /* For REMOTE_NAME */ +#include "http_log.h" +#include + +module MODULE_VAR_EXPORT config_log_module; + +static int xfer_flags = (O_WRONLY | O_APPEND | O_CREAT); +#if defined(OS2) || defined(WIN32) +/* OS/2 dosen't support users and groups */ +static mode_t xfer_mode = (S_IREAD | S_IWRITE); +#else +static mode_t xfer_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +#endif + +/* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is + * guaranteed to be atomic when writing a pipe. And PIPE_BUF >= 512 + * is guaranteed. So we'll just guess 512 in the event the system + * doesn't have this. Now, for file writes there is actually no limit, + * the entire write is atomic. Whether all systems implement this + * correctly is another question entirely ... so we'll just use PIPE_BUF + * because it's probably a good guess as to what is implemented correctly + * everywhere. + */ +#ifdef PIPE_BUF +#define LOG_BUFSIZE PIPE_BUF +#else +#define LOG_BUFSIZE (512) +#endif + +/* + * multi_log_state is our per-(virtual)-server configuration. We store + * an array of the logs we are going to use, each of type config_log_state. + * If a default log format is given by LogFormat, store in default_format + * (backward compat. with mod_log_config). We also store for each virtual + * server a pointer to the logs specified for the main server, so that if this + * vhost has no logs defined, we can use the main server's logs instead. + * + * So, for the main server, config_logs contains a list of the log files + * and server_config_logs in empty. For a vhost, server_config_logs + * points to the same array as config_logs in the main server, and + * config_logs points to the array of logs defined inside this vhost, + * which might be empty. + */ + +typedef struct { + char *default_format_string; + array_header *default_format; + array_header *config_logs; + array_header *server_config_logs; + table *formats; +} multi_log_state; + +/* + * config_log_state holds the status of a single log file. fname might + * be NULL, which means this module does no logging for this + * request. format might be NULL, in which case the default_format + * from the multi_log_state should be used, or if that is NULL as + * well, use the CLF. log_fd is -1 before the log file is opened and + * set to a valid fd after it is opened. + */ + +typedef struct { + char *fname; + char *format_string; + array_header *format; + int log_fd; + char *condition_var; +#ifdef BUFFERED_LOGS + int outcnt; + char outbuf[LOG_BUFSIZE]; +#endif +} config_log_state; + +/* + * Format items... + * Note that many of these could have ap_sprintfs replaced with static buffers. + */ + +typedef const char *(*item_key_func) (request_rec *, char *); + +typedef struct { + item_key_func func; + char *arg; + int condition_sense; + int want_orig; + array_header *conditions; +} log_format_item; + +static char *format_integer(pool *p, int i) +{ + return ap_psprintf(p, "%d", i); +} + +static char *pfmt(pool *p, int i) +{ + if (i <= 0) { + return "-"; + } + else { + return format_integer(p, i); + } +} + +static const char *constant_item(request_rec *dummy, char *stuff) +{ + return stuff; +} + +static const char *log_remote_host(request_rec *r, char *a) +{ + return ap_get_remote_host(r->connection, r->per_dir_config, + REMOTE_NAME); +} + +static const char *log_remote_address(request_rec *r, char *a) +{ + return r->connection->remote_ip; +} + +static const char *log_local_address(request_rec *r, char *a) +{ + return r->connection->local_ip; +} + +static const char *log_remote_logname(request_rec *r, char *a) +{ + return ap_get_remote_logname(r); +} + +static const char *log_remote_user(request_rec *r, char *a) +{ + char *rvalue = r->connection->user; + + if (rvalue == NULL) { + rvalue = "-"; + } + else if (strlen(rvalue) == 0) { + rvalue = "\"\""; + } + return rvalue; +} + +static const char *log_request_line(request_rec *r, char *a) +{ + /* NOTE: If the original request contained a password, we + * re-write the request line here to contain XXXXXX instead: + * (note the truncation before the protocol string for HTTP/0.9 requests) + * (note also that r->the_request contains the unmodified request) + */ + return (r->parsed_uri.password) ? ap_pstrcat(r->pool, r->method, " ", + ap_unparse_uri_components(r->pool, &r->parsed_uri, 0), + r->assbackwards ? NULL : " ", r->protocol, NULL) + : r->the_request; +} + +static const char *log_request_file(request_rec *r, char *a) +{ + return r->filename; +} +static const char *log_request_uri(request_rec *r, char *a) +{ + return r->uri; +} +static const char *log_status(request_rec *r, char *a) +{ + return pfmt(r->pool, r->status); +} + +static const char *log_bytes_sent(request_rec *r, char *a) +{ + if (!r->sent_bodyct) { + return "-"; + } + else { + long int bs; + ap_bgetopt(r->connection->client, BO_BYTECT, &bs); + return ap_psprintf(r->pool, "%ld", bs); + } +} + +static const char *log_header_in(request_rec *r, char *a) +{ + return ap_table_get(r->headers_in, a); +} + +static const char *log_header_out(request_rec *r, char *a) +{ + const char *cp = ap_table_get(r->headers_out, a); + if (!strcasecmp(a, "Content-type") && r->content_type) { + cp = r->content_type; + } + if (cp) { + return cp; + } + return ap_table_get(r->err_headers_out, a); +} + +static const char *log_note(request_rec *r, char *a) +{ + return ap_table_get(r->notes, a); +} +static const char *log_env_var(request_rec *r, char *a) +{ + return ap_table_get(r->subprocess_env, a); +} + +static const char *log_request_time(request_rec *r, char *a) +{ + int timz; + struct tm *t; + char tstr[MAX_STRING_LEN]; + + t = ap_get_gmtoff(&timz); + + if (a && *a) { /* Custom format */ + strftime(tstr, MAX_STRING_LEN, a, t); + } + else { /* CLF format */ + char sign = (timz < 0 ? '-' : '+'); + + if (timz < 0) { + timz = -timz; + } + ap_snprintf(tstr, sizeof(tstr), "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]", + t->tm_mday, ap_month_snames[t->tm_mon], t->tm_year+1900, + t->tm_hour, t->tm_min, t->tm_sec, + sign, timz / 60, timz % 60); + } + + return ap_pstrdup(r->pool, tstr); +} + +static const char *log_request_duration(request_rec *r, char *a) +{ + return ap_psprintf(r->pool, "%ld", time(NULL) - r->request_time); +} + +/* These next two routines use the canonical name:port so that log + * parsers don't need to duplicate all the vhost parsing crud. + */ +static const char *log_virtual_host(request_rec *r, char *a) +{ + return r->server->server_hostname; +} + +static const char *log_server_port(request_rec *r, char *a) +{ + return ap_psprintf(r->pool, "%u", + r->server->port ? r->server->port : ap_default_port(r)); +} + +/* This respects the setting of UseCanonicalName so that + * the dynamic mass virtual hosting trick works better. + */ +static const char *log_server_name(request_rec *r, char *a) +{ + return ap_get_server_name(r); +} + +static const char *log_child_pid(request_rec *r, char *a) +{ + return ap_psprintf(r->pool, "%ld", (long) getpid()); +} + +/***************************************************************** + * + * Parsing the log format string + */ + +static struct log_item_list { + char ch; + item_key_func func; + int want_orig_default; +} log_item_keys[] = { + + { + 'h', log_remote_host, 0 + }, + { + 'a', log_remote_address, 0 + }, + { + 'A', log_local_address, 0 + }, + { + 'l', log_remote_logname, 0 + }, + { + 'u', log_remote_user, 0 + }, + { + 't', log_request_time, 0 + }, + { + 'T', log_request_duration, 1 + }, + { + 'r', log_request_line, 1 + }, + { + 'f', log_request_file, 0 + }, + { + 'U', log_request_uri, 1 + }, + { + 's', log_status, 1 + }, + { + 'b', log_bytes_sent, 0 + }, + { + 'i', log_header_in, 0 + }, + { + 'o', log_header_out, 0 + }, + { + 'n', log_note, 0 + }, + { + 'e', log_env_var, 0 + }, + { + 'V', log_server_name, 0 + }, + { + 'v', log_virtual_host, 0 + }, + { + 'p', log_server_port, 0 + }, + { + 'P', log_child_pid, 0 + }, + { + '\0' + } +}; + +static struct log_item_list *find_log_func(char k) +{ + int i; + + for (i = 0; log_item_keys[i].ch; ++i) + if (k == log_item_keys[i].ch) { + return &log_item_keys[i]; + } + + return NULL; +} + +static char *parse_log_misc_string(pool *p, log_format_item *it, + const char **sa) +{ + const char *s; + char *d; + + it->func = constant_item; + it->conditions = NULL; + + s = *sa; + while (*s && *s != '%') { + s++; + } + /* + * This might allocate a few chars extra if there's a backslash + * escape in the format string. + */ + it->arg = ap_palloc(p, s - *sa + 1); + + d = it->arg; + s = *sa; + while (*s && *s != '%') { + if (*s != '\\') { + *d++ = *s++; + } + else { + s++; + switch (*s) { + case '\\': + *d++ = '\\'; + s++; + break; + case 'n': + *d++ = '\n'; + s++; + break; + case 't': + *d++ = '\t'; + s++; + break; + default: + /* copy verbatim */ + *d++ = '\\'; + /* + * Allow the loop to deal with this *s in the normal + * fashion so that it handles end of string etc. + * properly. + */ + break; + } + } + } + *d = '\0'; + + *sa = s; + return NULL; +} + +static char *parse_log_item(pool *p, log_format_item *it, const char **sa) +{ + const char *s = *sa; + + if (*s != '%') { + return parse_log_misc_string(p, it, sa); + } + + ++s; + it->condition_sense = 0; + it->conditions = NULL; + it->want_orig = -1; + it->arg = ""; /* For safety's sake... */ + + while (*s) { + int i; + struct log_item_list *l; + + switch (*s) { + case '!': + ++s; + it->condition_sense = !it->condition_sense; + break; + + case '<': + ++s; + it->want_orig = 1; + break; + + case '>': + ++s; + it->want_orig = 0; + break; + + case ',': + ++s; + break; + + case '{': + ++s; + it->arg = ap_getword(p, &s, '}'); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + i = *s - '0'; + while (ap_isdigit(*++s)) { + i = i * 10 + (*s) - '0'; + } + if (!it->conditions) { + it->conditions = ap_make_array(p, 4, sizeof(int)); + } + *(int *) ap_push_array(it->conditions) = i; + break; + + default: + l = find_log_func(*s++); + if (!l) { + char dummy[2]; + + dummy[0] = s[-1]; + dummy[1] = '\0'; + return ap_pstrcat(p, "Unrecognized LogFormat directive %", + dummy, NULL); + } + it->func = l->func; + if (it->want_orig == -1) { + it->want_orig = l->want_orig_default; + } + *sa = s; + return NULL; + } + } + + return "Ran off end of LogFormat parsing args to some directive"; +} + +static array_header *parse_log_string(pool *p, const char *s, const char **err) +{ + array_header *a = ap_make_array(p, 30, sizeof(log_format_item)); + char *res; + + while (*s) { + if ((res = parse_log_item(p, (log_format_item *) ap_push_array(a), &s))) { + *err = res; + return NULL; + } + } + + s = "\n"; + parse_log_item(p, (log_format_item *) ap_push_array(a), &s); + return a; +} + +/***************************************************************** + * + * Actually logging. + */ + +static const char *process_item(request_rec *r, request_rec *orig, + log_format_item *item) +{ + const char *cp; + + /* First, see if we need to process this thing at all... */ + + if (item->conditions && item->conditions->nelts != 0) { + int i; + int *conds = (int *) item->conditions->elts; + int in_list = 0; + + for (i = 0; i < item->conditions->nelts; ++i) { + if (r->status == conds[i]) { + in_list = 1; + break; + } + } + + if ((item->condition_sense && in_list) + || (!item->condition_sense && !in_list)) { + return "-"; + } + } + + /* We do. Do it... */ + + cp = (*item->func) (item->want_orig ? orig : r, item->arg); + return cp ? cp : "-"; +} + +#ifdef BUFFERED_LOGS +static void flush_log(config_log_state *cls) +{ + if (cls->outcnt && cls->log_fd != -1) { + write(cls->log_fd, cls->outbuf, cls->outcnt); + cls->outcnt = 0; + } +} +#endif + +static int config_log_transaction(request_rec *r, config_log_state *cls, + array_header *default_format) +{ + log_format_item *items; + char *str, *s; + const char **strs; + int *strl; + request_rec *orig; + int i; + int len = 0; + array_header *format; + char *envar; + + if (cls->fname == NULL) { + return DECLINED; + } + + /* + * See if we've got any conditional envariable-controlled logging decisions + * to make. + */ + if (cls->condition_var != NULL) { + envar = cls->condition_var; + if (*envar != '!') { + if (ap_table_get(r->subprocess_env, envar) == NULL) { + return DECLINED; + } + } + else { + if (ap_table_get(r->subprocess_env, &envar[1]) != NULL) { + return DECLINED; + } + } + } + + format = cls->format ? cls->format : default_format; + + strs = ap_palloc(r->pool, sizeof(char *) * (format->nelts)); + strl = ap_palloc(r->pool, sizeof(int) * (format->nelts)); + items = (log_format_item *) format->elts; + + orig = r; + while (orig->prev) { + orig = orig->prev; + } + while (r->next) { + r = r->next; + } + + for (i = 0; i < format->nelts; ++i) { + strs[i] = process_item(r, orig, &items[i]); + } + + for (i = 0; i < format->nelts; ++i) { + len += strl[i] = strlen(strs[i]); + } + +#ifdef BUFFERED_LOGS + if (len + cls->outcnt > LOG_BUFSIZE) { + flush_log(cls); + } + if (len >= LOG_BUFSIZE) { + str = ap_palloc(r->pool, len + 1); + for (i = 0, s = str; i < format->nelts; ++i) { + memcpy(s, strs[i], strl[i]); + s += strl[i]; + } + write(cls->log_fd, str, len); + } + else { + for (i = 0, s = &cls->outbuf[cls->outcnt]; i < format->nelts; ++i) { + memcpy(s, strs[i], strl[i]); + s += strl[i]; + } + cls->outcnt += len; + } +#else + str = ap_palloc(r->pool, len + 1); + + for (i = 0, s = str; i < format->nelts; ++i) { + memcpy(s, strs[i], strl[i]); + s += strl[i]; + } + + write(cls->log_fd, str, len); +#endif + + return OK; +} + +static int multi_log_transaction(request_rec *r) +{ + multi_log_state *mls = ap_get_module_config(r->server->module_config, + &config_log_module); + config_log_state *clsarray; + int i; + + /* + * Log this transaction.. + */ + if (mls->config_logs->nelts) { + clsarray = (config_log_state *) mls->config_logs->elts; + for (i = 0; i < mls->config_logs->nelts; ++i) { + config_log_state *cls = &clsarray[i]; + + config_log_transaction(r, cls, mls->default_format); + } + } + else if (mls->server_config_logs) { + clsarray = (config_log_state *) mls->server_config_logs->elts; + for (i = 0; i < mls->server_config_logs->nelts; ++i) { + config_log_state *cls = &clsarray[i]; + + config_log_transaction(r, cls, mls->default_format); + } + } + + return OK; +} + +/***************************************************************** + * + * Module glue... + */ + +static void *make_config_log_state(pool *p, server_rec *s) +{ + multi_log_state *mls; + + mls = (multi_log_state *) ap_palloc(p, sizeof(multi_log_state)); + mls->config_logs = ap_make_array(p, 1, sizeof(config_log_state)); + mls->default_format_string = NULL; + mls->default_format = NULL; + mls->server_config_logs = NULL; + mls->formats = ap_make_table(p, 4); + ap_table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT); + + return mls; +} + +/* + * Use the merger to simply add a pointer from the vhost log state + * to the log of logs specified for the non-vhost configuration. Make sure + * vhosts inherit any globally-defined format names. + */ + +static void *merge_config_log_state(pool *p, void *basev, void *addv) +{ + multi_log_state *base = (multi_log_state *) basev; + multi_log_state *add = (multi_log_state *) addv; + + add->server_config_logs = base->config_logs; + if (!add->default_format) { + add->default_format_string = base->default_format_string; + add->default_format = base->default_format; + } + add->formats = ap_overlay_tables(p, base->formats, add->formats); + + return add; +} + +/* + * Set the default logfile format, or define a nickname for a format string. + */ +static const char *log_format(cmd_parms *cmd, void *dummy, char *fmt, + char *name) +{ + const char *err_string = NULL; + multi_log_state *mls = ap_get_module_config(cmd->server->module_config, + &config_log_module); + + /* + * If we were given two arguments, the second is a name to be given to the + * format. This syntax just defines the nickname - it doesn't actually + * make the format the default. + */ + if (name != NULL) { + parse_log_string(cmd->pool, fmt, &err_string); + if (err_string == NULL) { + ap_table_setn(mls->formats, name, fmt); + } + } + else { + mls->default_format_string = fmt; + mls->default_format = parse_log_string(cmd->pool, fmt, &err_string); + } + return err_string; +} + + +static const char *add_custom_log(cmd_parms *cmd, void *dummy, char *fn, + char *fmt, char *envclause) +{ + const char *err_string = NULL; + multi_log_state *mls = ap_get_module_config(cmd->server->module_config, + &config_log_module); + config_log_state *cls; + + cls = (config_log_state *) ap_push_array(mls->config_logs); + cls->condition_var = NULL; + if (envclause != NULL) { + if (strncasecmp(envclause, "env=", 4) != 0) { + return "error in condition clause"; + } + if ((envclause[4] == '\0') + || ((envclause[4] == '!') && (envclause[5] == '\0'))) { + return "missing environment variable name"; + } + cls->condition_var = ap_pstrdup(cmd->pool, &envclause[4]); + } + + cls->fname = fn; + cls->format_string = fmt; + if (fmt == NULL) { + cls->format = NULL; + } + else { + cls->format = parse_log_string(cmd->pool, fmt, &err_string); + } + cls->log_fd = -1; + + return err_string; +} + +static const char *set_transfer_log(cmd_parms *cmd, void *dummy, char *fn) +{ + return add_custom_log(cmd, dummy, fn, NULL, NULL); +} + +static const char *set_cookie_log(cmd_parms *cmd, void *dummy, char *fn) +{ + return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t", NULL); +} + +static const command_rec config_log_cmds[] = +{ + {"CustomLog", add_custom_log, NULL, RSRC_CONF, TAKE23, + "a file name, a custom log format string or format name, " + "and an optional \"env=\" clause (see docs)"}, + {"TransferLog", set_transfer_log, NULL, RSRC_CONF, TAKE1, + "the filename of the access log"}, + {"LogFormat", log_format, NULL, RSRC_CONF, TAKE12, + "a log format string (see docs) and an optional format name"}, + {"CookieLog", set_cookie_log, NULL, RSRC_CONF, TAKE1, + "the filename of the cookie log"}, + {NULL} +}; + +static config_log_state *open_config_log(server_rec *s, pool *p, + config_log_state *cls, + array_header *default_format) +{ + if (cls->log_fd > 0) { + return cls; /* virtual config shared w/main server */ + } + + if (cls->fname == NULL) { + return cls; /* Leave it NULL to decline. */ + } + + if (*cls->fname == '|') { + piped_log *pl; + + pl = ap_open_piped_log(p, cls->fname + 1); + if (pl == NULL) { + exit(1); + } + cls->log_fd = ap_piped_log_write_fd(pl); + } + else { + char *fname = ap_server_root_relative(p, cls->fname); + if ((cls->log_fd = ap_popenf(p, fname, xfer_flags, xfer_mode)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "could not open transfer log file %s.", fname); + exit(1); + } + } +#ifdef BUFFERED_LOGS + cls->outcnt = 0; +#endif + + return cls; +} + +static config_log_state *open_multi_logs(server_rec *s, pool *p) +{ + int i; + multi_log_state *mls = ap_get_module_config(s->module_config, + &config_log_module); + config_log_state *clsarray; + const char *dummy; + const char *format; + + if (mls->default_format_string) { + format = ap_table_get(mls->formats, mls->default_format_string); + if (format) { + mls->default_format = parse_log_string(p, format, &dummy); + } + } + + if (!mls->default_format) { + mls->default_format = parse_log_string(p, DEFAULT_LOG_FORMAT, &dummy); + } + + if (mls->config_logs->nelts) { + clsarray = (config_log_state *) mls->config_logs->elts; + for (i = 0; i < mls->config_logs->nelts; ++i) { + config_log_state *cls = &clsarray[i]; + + if (cls->format_string) { + format = ap_table_get(mls->formats, cls->format_string); + if (format) { + cls->format = parse_log_string(p, format, &dummy); + } + } + + cls = open_config_log(s, p, cls, mls->default_format); + } + } + else if (mls->server_config_logs) { + clsarray = (config_log_state *) mls->server_config_logs->elts; + for (i = 0; i < mls->server_config_logs->nelts; ++i) { + config_log_state *cls = &clsarray[i]; + + if (cls->format_string) { + format = ap_table_get(mls->formats, cls->format_string); + if (format) { + cls->format = parse_log_string(p, format, &dummy); + } + } + + cls = open_config_log(s, p, cls, mls->default_format); + } + } + + return NULL; +} + +static void init_config_log(server_rec *s, pool *p) +{ + /* First, do "physical" server, which gets default log fd and format + * for the virtual servers, if they don't override... + */ + + open_multi_logs(s, p); + + /* Then, virtual servers */ + + for (s = s->next; s; s = s->next) { + open_multi_logs(s, p); + } +} + +#ifdef BUFFERED_LOGS +static void flush_all_logs(server_rec *s, pool *p) +{ + multi_log_state *mls; + array_header *log_list; + config_log_state *clsarray; + int i; + + for (; s; s = s->next) { + mls = ap_get_module_config(s->module_config, &config_log_module); + log_list = NULL; + if (mls->config_logs->nelts) { + log_list = mls->config_logs; + } + else if (mls->server_config_logs) { + log_list = mls->server_config_logs; + } + if (log_list) { + clsarray = (config_log_state *) log_list->elts; + for (i = 0; i < log_list->nelts; ++i) { + flush_log(&clsarray[i]); + } + } + } +} +#endif + +module MODULE_VAR_EXPORT config_log_module = +{ + STANDARD_MODULE_STUFF, + init_config_log, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + make_config_log_state, /* server config */ + merge_config_log_state, /* merge server config */ + config_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + multi_log_transaction, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ +#ifdef BUFFERED_LOGS + flush_all_logs, /* child_exit */ +#else + NULL, +#endif + NULL /* post read-request */ +}; diff --git a/modules/mappers/.indent.pro b/modules/mappers/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/modules/mappers/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-Tbuf_area +-Tbuff_struct +-Tbuffy +-Tcmd_how +-Tcmd_parms +-Tcommand_rec +-Tcommand_struct +-Tconn_rec +-Tcore_dir_config +-Tcore_server_config +-Tdir_maker_func +-Tevent +-Tglobals_s +-Thandler_func +-Thandler_rec +-Tjoblist_s +-Tlisten_rec +-Tmerger_func +-Tmode_t +-Tmodule +-Tmodule_struct +-Tmutex +-Tn_long +-Tother_child_rec +-Toverrides_t +-Tparent_score +-Tpid_t +-Tpiped_log +-Tpool +-Trequest_rec +-Trequire_line +-Trlim_t +-Tscoreboard +-Tsemaphore +-Tserver_addr_rec +-Tserver_rec +-Tserver_rec_chain +-Tshort_score +-Ttable +-Ttable_entry +-Tthread +-Tu_wide_int +-Tvtime_t +-Twide_int diff --git a/modules/mappers/mod_actions.c b/modules/mappers/mod_actions.c new file mode 100644 index 00000000000..5906ee4c43a --- /dev/null +++ b/modules/mappers/mod_actions.c @@ -0,0 +1,232 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_actions.c: executes scripts based on MIME type or HTTP method + * + * by Alexei Kosut; based on mod_cgi.c, mod_mime.c and mod_includes.c, + * adapted by rst from original NCSA code by Rob McCool + * + * Usage instructions: + * + * Action mime/type /cgi-bin/script + * + * will activate /cgi-bin/script when a file of content type mime/type is + * requested. It sends the URL and file path of the requested document using + * the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. + * + * Script PUT /cgi-bin/script + * + * will activate /cgi-bin/script when a request is received with the + * HTTP method "PUT". The available method names are defined in httpd.h. + * If the method is GET, the script will only be activated if the requested + * URI includes query information (stuff after a ?-mark). + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#include "util_script.h" + +typedef struct { + table *action_types; /* Added with Action... */ + char *scripted[METHODS]; /* Added with Script... */ +} action_dir_config; + +module action_module; + +static void *create_action_dir_config(pool *p, char *dummy) +{ + action_dir_config *new = + (action_dir_config *) ap_palloc(p, sizeof(action_dir_config)); + + new->action_types = ap_make_table(p, 4); + memset(new->scripted, 0, sizeof(new->scripted)); + + return new; +} + +static void *merge_action_dir_configs(pool *p, void *basev, void *addv) +{ + action_dir_config *base = (action_dir_config *) basev; + action_dir_config *add = (action_dir_config *) addv; + action_dir_config *new = (action_dir_config *) ap_palloc(p, + sizeof(action_dir_config)); + int i; + + new->action_types = ap_overlay_tables(p, add->action_types, + base->action_types); + + for (i = 0; i < METHODS; ++i) { + new->scripted[i] = add->scripted[i] ? add->scripted[i] + : base->scripted[i]; + } + return new; +} + +static const char *add_action(cmd_parms *cmd, action_dir_config * m, char *type, + char *script) +{ + ap_table_setn(m->action_types, type, script); + return NULL; +} + +static const char *set_script(cmd_parms *cmd, action_dir_config * m, + char *method, char *script) +{ + int methnum; + + methnum = ap_method_number_of(method); + if (methnum == M_TRACE) + return "TRACE not allowed for Script"; + else if (methnum == M_INVALID) + return "Unknown method type for Script"; + else + m->scripted[methnum] = script; + + return NULL; +} + +static const command_rec action_cmds[] = +{ + {"Action", add_action, NULL, OR_FILEINFO, TAKE2, + "a media type followed by a script name"}, + {"Script", set_script, NULL, ACCESS_CONF | RSRC_CONF, TAKE2, + "a method followed by a script name"}, + {NULL} +}; + +static int action_handler(request_rec *r) +{ + action_dir_config *conf = (action_dir_config *) + ap_get_module_config(r->per_dir_config, &action_module); + const char *t, *action = r->handler ? r->handler : r->content_type; + const char *script; + int i; + + /* Set allowed stuff */ + for (i = 0; i < METHODS; ++i) { + if (conf->scripted[i]) + r->allowed |= (1 << i); + } + + /* First, check for the method-handling scripts */ + if (r->method_number == M_GET) { + if (r->args) + script = conf->scripted[M_GET]; + else + script = NULL; + } + else { + script = conf->scripted[r->method_number]; + } + + /* Check for looping, which can happen if the CGI script isn't */ + if (script && r->prev && r->prev->prev) + return DECLINED; + + /* Second, check for actions (which override the method scripts) */ + if ((t = ap_table_get(conf->action_types, + action ? action : ap_default_type(r)))) { + script = t; + if (r->finfo.st_mode == 0) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "File does not exist: %s", r->filename); + return NOT_FOUND; + } + } + + if (script == NULL) + return DECLINED; + + ap_internal_redirect_handler(ap_pstrcat(r->pool, script, ap_escape_uri(r->pool, + r->uri), r->args ? "?" : NULL, r->args, NULL), r); + return OK; +} + +static const handler_rec action_handlers[] = +{ + {"*/*", action_handler}, + {NULL} +}; + +module action_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_action_dir_config, /* dir config creater */ + merge_action_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + action_cmds, /* command table */ + action_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/mappers/mod_alias.c b/modules/mappers/mod_alias.c new file mode 100644 index 00000000000..f92d982d233 --- /dev/null +++ b/modules/mappers/mod_alias.c @@ -0,0 +1,418 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_alias.c: Stuff for dealing with directory aliases + * + * Original by Rob McCool, rewritten in succession by David Robinson + * and rst. + * + */ + +#include "httpd.h" +#include "http_config.h" + +typedef struct { + char *real; + char *fake; + char *handler; + regex_t *regexp; + int redir_status; /* 301, 302, 303, 410, etc */ +} alias_entry; + +typedef struct { + array_header *aliases; + array_header *redirects; +} alias_server_conf; + +typedef struct { + array_header *redirects; +} alias_dir_conf; + +module MODULE_VAR_EXPORT alias_module; + +static void *create_alias_config(pool *p, server_rec *s) +{ + alias_server_conf *a = + (alias_server_conf *) ap_pcalloc(p, sizeof(alias_server_conf)); + + a->aliases = ap_make_array(p, 20, sizeof(alias_entry)); + a->redirects = ap_make_array(p, 20, sizeof(alias_entry)); + return a; +} + +static void *create_alias_dir_config(pool *p, char *d) +{ + alias_dir_conf *a = + (alias_dir_conf *) ap_pcalloc(p, sizeof(alias_dir_conf)); + a->redirects = ap_make_array(p, 2, sizeof(alias_entry)); + return a; +} + +static void *merge_alias_config(pool *p, void *basev, void *overridesv) +{ + alias_server_conf *a = + (alias_server_conf *) ap_pcalloc(p, sizeof(alias_server_conf)); + alias_server_conf *base = (alias_server_conf *) basev, *overrides = (alias_server_conf *) overridesv; + + a->aliases = ap_append_arrays(p, overrides->aliases, base->aliases); + a->redirects = ap_append_arrays(p, overrides->redirects, base->redirects); + return a; +} + +static void *merge_alias_dir_config(pool *p, void *basev, void *overridesv) +{ + alias_dir_conf *a = + (alias_dir_conf *) ap_pcalloc(p, sizeof(alias_dir_conf)); + alias_dir_conf *base = (alias_dir_conf *) basev, *overrides = (alias_dir_conf *) overridesv; + a->redirects = ap_append_arrays(p, overrides->redirects, base->redirects); + return a; +} + +static const char *add_alias_internal(cmd_parms *cmd, void *dummy, char *f, char *r, + int use_regex) +{ + server_rec *s = cmd->server; + alias_server_conf *conf = + (alias_server_conf *) ap_get_module_config(s->module_config, &alias_module); + alias_entry *new = ap_push_array(conf->aliases); + + /* XX r can NOT be relative to DocumentRoot here... compat bug. */ + + if (use_regex) { + new->regexp = ap_pregcomp(cmd->pool, f, REG_EXTENDED); + if (new->regexp == NULL) + return "Regular expression could not be compiled."; + } + + new->fake = f; + new->real = r; + new->handler = cmd->info; + + return NULL; +} + +static const char *add_alias(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + return add_alias_internal(cmd, dummy, f, r, 0); +} + +static const char *add_alias_regex(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + return add_alias_internal(cmd, dummy, f, r, 1); +} + +static const char *add_redirect_internal(cmd_parms *cmd, alias_dir_conf * dirconf, + char *arg1, char *arg2, char *arg3, + int use_regex) +{ + alias_entry *new; + server_rec *s = cmd->server; + alias_server_conf *serverconf = + (alias_server_conf *) ap_get_module_config(s->module_config, &alias_module); + int status = (int) (long) cmd->info; + regex_t *r = NULL; + char *f = arg2; + char *url = arg3; + + if (!strcasecmp(arg1, "gone")) + status = HTTP_GONE; + else if (!strcasecmp(arg1, "permanent")) + status = HTTP_MOVED_PERMANENTLY; + else if (!strcasecmp(arg1, "temp")) + status = HTTP_MOVED_TEMPORARILY; + else if (!strcasecmp(arg1, "seeother")) + status = HTTP_SEE_OTHER; + else if (ap_isdigit(*arg1)) + status = atoi(arg1); + else { + f = arg1; + url = arg2; + } + + if (use_regex) { + r = ap_pregcomp(cmd->pool, f, REG_EXTENDED); + if (r == NULL) + return "Regular expression could not be compiled."; + } + + if (ap_is_HTTP_REDIRECT(status)) { + if (!url) + return "URL to redirect to is missing"; + if (!use_regex && !ap_is_url(url)) + return "Redirect to non-URL"; + } + else { + if (url) + return "Redirect URL not valid for this status"; + } + + if (cmd->path) + new = ap_push_array(dirconf->redirects); + else + new = ap_push_array(serverconf->redirects); + + new->fake = f; + new->real = url; + new->regexp = r; + new->redir_status = status; + return NULL; +} + +static const char *add_redirect(cmd_parms *cmd, alias_dir_conf * dirconf, char *arg1, + char *arg2, char *arg3) +{ + return add_redirect_internal(cmd, dirconf, arg1, arg2, arg3, 0); +} + +static const char *add_redirect_regex(cmd_parms *cmd, alias_dir_conf * dirconf, + char *arg1, char *arg2, char *arg3) +{ + return add_redirect_internal(cmd, dirconf, arg1, arg2, arg3, 1); +} + +static const command_rec alias_cmds[] = +{ + {"Alias", add_alias, NULL, RSRC_CONF, TAKE2, + "a fakename and a realname"}, + {"ScriptAlias", add_alias, "cgi-script", RSRC_CONF, TAKE2, + "a fakename and a realname"}, + {"Redirect", add_redirect, (void *) HTTP_MOVED_TEMPORARILY, + OR_FILEINFO, TAKE23, + "an optional status, then document to be redirected and destination URL"}, + {"AliasMatch", add_alias_regex, NULL, RSRC_CONF, TAKE2, + "a regular expression and a filename"}, + {"ScriptAliasMatch", add_alias_regex, "cgi-script", RSRC_CONF, TAKE2, + "a regular expression and a filename"}, + {"RedirectMatch", add_redirect_regex, (void *) HTTP_MOVED_TEMPORARILY, + OR_FILEINFO, TAKE23, + "an optional status, then a regular expression and destination URL"}, + {"RedirectTemp", add_redirect, (void *) HTTP_MOVED_TEMPORARILY, + OR_FILEINFO, TAKE2, + "a document to be redirected, then the destination URL"}, + {"RedirectPermanent", add_redirect, (void *) HTTP_MOVED_PERMANENTLY, + OR_FILEINFO, TAKE2, + "a document to be redirected, then the destination URL"}, + {NULL} +}; + +static int alias_matches(const char *uri, const char *alias_fakename) +{ + const char *end_fakename = alias_fakename + strlen(alias_fakename); + const char *aliasp = alias_fakename, *urip = uri; + + while (aliasp < end_fakename) { + if (*aliasp == '/') { + /* any number of '/' in the alias matches any number in + * the supplied URI, but there must be at least one... + */ + if (*urip != '/') + return 0; + + while (*aliasp == '/') + ++aliasp; + while (*urip == '/') + ++urip; + } + else { + /* Other characters are compared literally */ + if (*urip++ != *aliasp++) + return 0; + } + } + + /* Check last alias path component matched all the way */ + + if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') + return 0; + + /* Return number of characters from URI which matched (may be + * greater than length of alias, since we may have matched + * doubled slashes) + */ + + return urip - uri; +} + +static char *try_alias_list(request_rec *r, array_header *aliases, int doesc, int *status) +{ + alias_entry *entries = (alias_entry *) aliases->elts; + regmatch_t regm[10]; + char *found = NULL; + int i; + + for (i = 0; i < aliases->nelts; ++i) { + alias_entry *p = &entries[i]; + int l; + + if (p->regexp) { + if (!ap_regexec(p->regexp, r->uri, p->regexp->re_nsub + 1, regm, 0)) { + if (p->real) { + found = ap_pregsub(r->pool, p->real, r->uri, + p->regexp->re_nsub + 1, regm); + if (found && doesc) { + found = ap_escape_uri(r->pool, found); + } + } + else { + /* need something non-null */ + found = ap_pstrdup(r->pool, ""); + } + } + } + else { + l = alias_matches(r->uri, p->fake); + + if (l > 0) { + if (doesc) { + char *escurl; + escurl = ap_os_escape_path(r->pool, r->uri + l, 1); + + found = ap_pstrcat(r->pool, p->real, escurl, NULL); + } + else + found = ap_pstrcat(r->pool, p->real, r->uri + l, NULL); + } + } + + if (found) { + if (p->handler) { /* Set handler, and leave a note for mod_cgi */ + r->handler = p->handler; + ap_table_setn(r->notes, "alias-forced-type", r->handler); + } + + *status = p->redir_status; + + return found; + } + + } + + return NULL; +} + +static int translate_alias_redir(request_rec *r) +{ + void *sconf = r->server->module_config; + alias_server_conf *serverconf = + (alias_server_conf *) ap_get_module_config(sconf, &alias_module); + char *ret; + int status; + + if (r->uri[0] != '/' && r->uri[0] != '\0') + return DECLINED; + + if ((ret = try_alias_list(r, serverconf->redirects, 1, &status)) != NULL) { + if (ap_is_HTTP_REDIRECT(status)) { + /* include QUERY_STRING if any */ + if (r->args) { + ret = ap_pstrcat(r->pool, ret, "?", r->args, NULL); + } + ap_table_setn(r->headers_out, "Location", ret); + } + return status; + } + + if ((ret = try_alias_list(r, serverconf->aliases, 0, &status)) != NULL) { + r->filename = ret; + return OK; + } + + return DECLINED; +} + +static int fixup_redir(request_rec *r) +{ + void *dconf = r->per_dir_config; + alias_dir_conf *dirconf = + (alias_dir_conf *) ap_get_module_config(dconf, &alias_module); + char *ret; + int status; + + /* It may have changed since last time, so try again */ + + if ((ret = try_alias_list(r, dirconf->redirects, 1, &status)) != NULL) { + if (ap_is_HTTP_REDIRECT(status)) + ap_table_setn(r->headers_out, "Location", ret); + return status; + } + + return DECLINED; +} + +module MODULE_VAR_EXPORT alias_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_alias_dir_config, /* dir config creater */ + merge_alias_dir_config, /* dir merger --- default is to override */ + create_alias_config, /* server config */ + merge_alias_config, /* merge server configs */ + alias_cmds, /* command table */ + NULL, /* handlers */ + translate_alias_redir, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + fixup_redir, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/mappers/mod_dir.c b/modules/mappers/mod_dir.c new file mode 100644 index 00000000000..a32b390a88b --- /dev/null +++ b/modules/mappers/mod_dir.c @@ -0,0 +1,246 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_dir.c: handle default index files, and trailing-/ redirects + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" + +module MODULE_VAR_EXPORT dir_module; + +typedef struct dir_config_struct { + array_header *index_names; +} dir_config_rec; + +#define DIR_CMD_PERMS OR_INDEXES + +static const char *add_index(cmd_parms *cmd, void *dummy, char *arg) +{ + dir_config_rec *d = dummy; + + if (!d->index_names) { + d->index_names = ap_make_array(cmd->pool, 2, sizeof(char *)); + } + *(char **)ap_push_array(d->index_names) = arg; + return NULL; +} + +static const command_rec dir_cmds[] = +{ + {"DirectoryIndex", add_index, NULL, + DIR_CMD_PERMS, ITERATE, + "a list of file names"}, + {NULL} +}; + +static void *create_dir_config(pool *p, char *dummy) +{ + dir_config_rec *new = + (dir_config_rec *) ap_pcalloc(p, sizeof(dir_config_rec)); + + new->index_names = NULL; + return (void *) new; +} + +static void *merge_dir_configs(pool *p, void *basev, void *addv) +{ + dir_config_rec *new = (dir_config_rec *) ap_pcalloc(p, sizeof(dir_config_rec)); + dir_config_rec *base = (dir_config_rec *) basev; + dir_config_rec *add = (dir_config_rec *) addv; + + new->index_names = add->index_names ? add->index_names : base->index_names; + return new; +} + +static int handle_dir(request_rec *r) +{ + dir_config_rec *d = + (dir_config_rec *) ap_get_module_config(r->per_dir_config, + &dir_module); + char *dummy_ptr[1]; + char **names_ptr; + int num_names; + int error_notfound = 0; + + if (r->uri[0] == '\0' || r->uri[strlen(r->uri) - 1] != '/') { + char *ifile; + if (r->args != NULL) + ifile = ap_pstrcat(r->pool, ap_escape_uri(r->pool, r->uri), + "/", "?", r->args, NULL); + else + ifile = ap_pstrcat(r->pool, ap_escape_uri(r->pool, r->uri), + "/", NULL); + + ap_table_setn(r->headers_out, "Location", + ap_construct_url(r->pool, ifile, r)); + return HTTP_MOVED_PERMANENTLY; + } + + /* 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 = ap_pstrcat(r->pool, r->filename, "/", NULL); + } + + if (d->index_names) { + names_ptr = (char **)d->index_names->elts; + num_names = d->index_names->nelts; + } + else { + dummy_ptr[0] = DEFAULT_INDEX; + names_ptr = dummy_ptr; + num_names = 1; + } + + for (; num_names; ++names_ptr, --num_names) { + char *name_ptr = *names_ptr; + request_rec *rr = ap_sub_req_lookup_uri(name_ptr, r); + + if (rr->status == HTTP_OK && S_ISREG(rr->finfo.st_mode)) { + char *new_uri = ap_escape_uri(r->pool, rr->uri); + + if (rr->args != NULL) + new_uri = ap_pstrcat(r->pool, new_uri, "?", rr->args, NULL); + else if (r->args != NULL) + new_uri = ap_pstrcat(r->pool, new_uri, "?", r->args, NULL); + + ap_destroy_sub_req(rr); + ap_internal_redirect(new_uri, r); + return OK; + } + + /* If the request returned a redirect, propagate it to the client */ + + if (ap_is_HTTP_REDIRECT(rr->status) || + (rr->status == HTTP_NOT_ACCEPTABLE && num_names == 1)) { + + ap_pool_join(r->pool, rr->pool); + error_notfound = rr->status; + r->notes = ap_overlay_tables(r->pool, r->notes, rr->notes); + r->headers_out = ap_overlay_tables(r->pool, r->headers_out, + rr->headers_out); + r->err_headers_out = ap_overlay_tables(r->pool, r->err_headers_out, + rr->err_headers_out); + return error_notfound; + } + + /* If the request returned something other than 404 (or 200), + * it means the module encountered some sort of problem. To be + * secure, we should return the error, rather than create + * along a (possibly unsafe) directory index. + * + * So we store the error, and if none of the listed files + * exist, we return the last error response we got, instead + * of a directory listing. + */ + if (rr->status && rr->status != HTTP_NOT_FOUND && rr->status != HTTP_OK) + error_notfound = rr->status; + + ap_destroy_sub_req(rr); + } + + if (error_notfound) + return error_notfound; + + if (r->method_number != M_GET) + return DECLINED; + + /* nothing for us to do, pass on through */ + + return DECLINED; +} + + +static const handler_rec dir_handlers[] = +{ + {DIR_MAGIC_TYPE, handle_dir}, + {NULL} +}; + +module MODULE_VAR_EXPORT dir_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_dir_config, /* dir config creater */ + merge_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + dir_cmds, /* command table */ + dir_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/mappers/mod_imap.c b/modules/mappers/mod_imap.c new file mode 100644 index 00000000000..c5152e71e40 --- /dev/null +++ b/modules/mappers/mod_imap.c @@ -0,0 +1,919 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * This imagemap module started as a port of the original imagemap.c + * written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu). + * This version includes the mapping algorithms found in version 1.3 + * of imagemap.c. + * + * Contributors to this code include: + * + * Kevin Hughes, kevinh@pulua.hcc.hawaii.edu + * + * Eric Haines, erich@eye.com + * "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com + * + * Randy Terbush, randy@zyzzyva.com + * port to Apache module format, "base_uri" and support for relative URLs + * + * James H. Cloos, Jr., cloos@jhcloos.com + * Added point datatype, using code in NCSA's version 1.8 imagemap.c + * program, as distributed with version 1.4.1 of their server. + * The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu + * + * Nathan Kurz, nate@tripod.com + * Rewrite/reorganization. New handling of default, base and relative URLs. + * New Configuration directives: + * ImapMenu {none, formatted, semiformatted, unformatted} + * ImapDefault {error, nocontent, referer, menu, URL} + * ImapBase {map, referer, URL} + * Support for creating non-graphical menu added. (backwards compatible): + * Old: directive URL [x,y ...] + * New: directive URL "Menu text" [x,y ...] + * or: directive URL x,y ... "Menu text" + * Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca. + * + * Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified + */ + +#include "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" + +#define IMAP_MAGIC_TYPE "application/x-httpd-imap" +#define MAXVERTS 100 +#define X 0 +#define Y 1 + +#define IMAP_MENU_DEFAULT "formatted" +#define IMAP_DEFAULT_DEFAULT "nocontent" +#define IMAP_BASE_DEFAULT "map" + +#ifdef SUNOS4 +double strtod(); /* SunOS needed this */ +#endif + +module MODULE_VAR_EXPORT imap_module; + +typedef struct { + char *imap_menu; + char *imap_default; + char *imap_base; +} imap_conf_rec; + +static void *create_imap_dir_config(pool *p, char *dummy) +{ + imap_conf_rec *icr = + (imap_conf_rec *) ap_palloc(p, sizeof(imap_conf_rec)); + + icr->imap_menu = NULL; + icr->imap_default = NULL; + icr->imap_base = NULL; + + return icr; +} + +static void *merge_imap_dir_configs(pool *p, void *basev, void *addv) +{ + imap_conf_rec *new = (imap_conf_rec *) ap_pcalloc(p, sizeof(imap_conf_rec)); + imap_conf_rec *base = (imap_conf_rec *) basev; + imap_conf_rec *add = (imap_conf_rec *) addv; + + new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu; + new->imap_default = add->imap_default ? add->imap_default + : base->imap_default; + new->imap_base = add->imap_base ? add->imap_base : base->imap_base; + + return new; +} + + +static const command_rec imap_cmds[] = +{ + {"ImapMenu", ap_set_string_slot, + (void *) XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES, TAKE1, + "the type of menu generated: none, formatted, semiformatted, unformatted"}, + {"ImapDefault", ap_set_string_slot, + (void *) XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES, TAKE1, + "the action taken if no match: error, nocontent, referer, menu, URL"}, + {"ImapBase", ap_set_string_slot, + (void *) XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES, TAKE1, + "the base for all URL's: map, referer, URL (or start of)"}, + {NULL} +}; + +static int pointinrect(const double point[2], double coords[MAXVERTS][2]) +{ + double max[2], min[2]; + if (coords[0][X] > coords[1][X]) { + max[0] = coords[0][X]; + min[0] = coords[1][X]; + } + else { + max[0] = coords[1][X]; + min[0] = coords[0][X]; + } + + if (coords[0][Y] > coords[1][Y]) { + max[1] = coords[0][Y]; + min[1] = coords[1][Y]; + } + else { + max[1] = coords[1][Y]; + min[1] = coords[0][Y]; + } + + return ((point[X] >= min[0] && point[X] <= max[0]) && + (point[Y] >= min[1] && point[Y] <= max[1])); +} + +static int pointincircle(const double point[2], double coords[MAXVERTS][2]) +{ + double radius1, radius2; + + radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y])) + + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X])); + + radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y])) + + ((coords[0][X] - point[X]) * (coords[0][X] - point[X])); + + return (radius2 <= radius1); +} + +#define fmin(a,b) (((a)>(b))?(b):(a)) +#define fmax(a,b) (((a)>(b))?(a):(b)) + +static int pointinpoly(const double point[2], double pgon[MAXVERTS][2]) +{ + int i, numverts, crossings = 0; + double x = point[X], y = point[Y]; + + for (numverts = 0; pgon[numverts][X] != -1 && numverts < MAXVERTS; + numverts++) { + /* just counting the vertexes */ + } + + for (i = 0; i < numverts; i++) { + double x1=pgon[i][X]; + double y1=pgon[i][Y]; + double x2=pgon[(i + 1) % numverts][X]; + double y2=pgon[(i + 1) % numverts][Y]; + double d=(y - y1) * (x2 - x1) - (x - x1) * (y2 - y1); + + if ((y1 >= y) != (y2 >= y)) { + crossings +=y2 - y1 >= 0 ? d >= 0 : d <= 0; + } + if (!d && fmin(x1,x2) <= x && x <= fmax(x1,x2) + && fmin(y1,y2) <= y && y <= fmax(y1,y2)) { + return 1; + } + } + return crossings & 0x01; +} + + +static int is_closer(const double point[2], double coords[MAXVERTS][2], + double *closest) +{ + double dist_squared = ((point[X] - coords[0][X]) + * (point[X] - coords[0][X])) + + ((point[Y] - coords[0][Y]) + * (point[Y] - coords[0][Y])); + + if (point[X] < 0 || point[Y] < 0) { + return (0); /* don't mess around with negative coordinates */ + } + + if (*closest < 0 || dist_squared < *closest) { + *closest = dist_squared; + return (1); /* if this is the first point or is the closest yet + set 'closest' equal to this distance^2 */ + } + + return (0); /* if it's not the first or closest */ + +} + +static double get_x_coord(const char *args) +{ + char *endptr; /* we want it non-null */ + double x_coord = -1; /* -1 is returned if no coordinate is given */ + + if (args == NULL) { + return (-1); /* in case we aren't passed anything */ + } + + while (*args && !ap_isdigit(*args) && *args != ',') { + args++; /* jump to the first digit, but not past + a comma or end */ + } + + x_coord = strtod(args, &endptr); + + if (endptr > args) { /* if a conversion was made */ + return (x_coord); + } + + return (-1); /* else if no conversion was made, + or if no args was given */ +} + +static double get_y_coord(const char *args) +{ + char *endptr; /* we want it non-null */ + char *start_of_y = NULL; + double y_coord = -1; /* -1 is returned on error */ + + if (args == NULL) { + return (-1); /* in case we aren't passed anything */ + } + + start_of_y = strchr(args, ','); /* the comma */ + + if (start_of_y) { + + start_of_y++; /* start looking at the character after + the comma */ + + while (*start_of_y && !ap_isdigit(*start_of_y)) { + start_of_y++; /* jump to the first digit, but not + past the end */ + } + + y_coord = strtod(start_of_y, &endptr); + + if (endptr > start_of_y) { + return (y_coord); + } + } + + return (-1); /* if no conversion was made, or + no comma was found in args */ +} + + +/* See if string has a "quoted part", and if so set *quoted_part to + * the first character of the quoted part, then hammer a \0 onto the + * trailing quote, and set *string to point at the first character + * past the second quote. + * + * Otherwise set *quoted_part to NULL, and leave *string alone. + */ +static void read_quoted(char **string, char **quoted_part) +{ + char *strp = *string; + + /* assume there's no quoted part */ + *quoted_part = NULL; + + while (ap_isspace(*strp)) { + strp++; /* go along string until non-whitespace */ + } + + if (*strp == '"') { /* if that character is a double quote */ + strp++; /* step over it */ + *quoted_part = strp; /* note where the quoted part begins */ + + while (*strp && *strp != '"') { + ++strp; /* skip the quoted portion */ + } + + *strp = '\0'; /* end the string with a NUL */ + + strp++; /* step over the last double quote */ + *string = strp; + } +} + +/* + * returns the mapped URL or NULL. + */ +static char *imap_url(request_rec *r, const char *base, const char *value) +{ +/* translates a value into a URL. */ + int slen, clen; + char *string_pos = NULL; + const char *string_pos_const = NULL; + char *directory = NULL; + const char *referer = NULL; + char *my_base; + + if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) { + return ap_construct_url(r->pool, r->uri, r); + } + + if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) { + return ap_pstrdup(r->pool, value); /* these are handled elsewhere, + so just copy them */ + } + + if (!strcasecmp(value, "referer")) { + referer = ap_table_get(r->headers_in, "Referer"); + if (referer && *referer) { + return ap_pstrdup(r->pool, referer); + } + else { + /* XXX: This used to do *value = '\0'; ... which is totally bogus + * because it hammers the passed in value, which can be a string + * constant, or part of a config, or whatever. Total garbage. + * This works around that without changing the rest of this + * code much + */ + value = ""; /* if 'referer' but no referring page, + null the value */ + } + } + + string_pos_const = value; + while (ap_isalpha(*string_pos_const)) { + string_pos_const++; /* go along the URL from the map + until a non-letter */ + } + if (*string_pos_const == ':') { + /* if letters and then a colon (like http:) */ + /* it's an absolute URL, so use it! */ + return ap_pstrdup(r->pool, value); + } + + if (!base || !*base) { + if (value && *value) { + return ap_pstrdup(r->pool, value); /* no base: use what is given */ + } + /* no base, no value: pick a simple default */ + return ap_construct_url(r->pool, "/", r); + } + + /* must be a relative URL to be combined with base */ + if (strchr(base, '/') == NULL && (!strncmp(value, "../", 3) + || !strcmp(value, ".."))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "invalid base directive in map file: %s", r->uri); + return NULL; + } + my_base = ap_pstrdup(r->pool, base); + string_pos = my_base; + while (*string_pos) { + if (*string_pos == '/' && *(string_pos + 1) == '/') { + string_pos += 2; /* if there are two slashes, jump over them */ + continue; + } + if (*string_pos == '/') { /* the first single slash */ + if (value[0] == '/') { + *string_pos = '\0'; + } /* if the URL from the map starts from root, + end the base URL string at the first single + slash */ + else { + directory = string_pos; /* save the start of + the directory portion */ + + string_pos = strrchr(string_pos, '/'); /* now reuse + string_pos */ + string_pos++; /* step over that last slash */ + *string_pos = '\0'; + } /* but if the map url is relative, leave the + slash on the base (if there is one) */ + break; + } + string_pos++; /* until we get to the end of my_base without + finding a slash by itself */ + } + + while (!strncmp(value, "../", 3) || !strcmp(value, "..")) { + + if (directory && (slen = strlen(directory))) { + + /* for each '..', knock a directory off the end + by ending the string right at the last slash. + But only consider the directory portion: don't eat + into the server name. And only try if a directory + portion was found */ + + clen = slen - 1; + + while ((slen - clen) == 1) { + + if ((string_pos = strrchr(directory, '/'))) { + *string_pos = '\0'; + } + clen = strlen(directory); + if (clen == 0) { + break; + } + } + + value += 2; /* jump over the '..' that we found in the + value */ + } + else if (directory) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "invalid directory name in map file: %s", r->uri); + return NULL; + } + + if (!strncmp(value, "/../", 4) || !strcmp(value, "/..")) { + value++; /* step over the '/' if there are more '..' + to do. This way, we leave the starting + '/' on value after the last '..', but get + rid of it otherwise */ + } + + } /* by this point, value does not start + with '..' */ + + if (value && *value) { + return ap_pstrcat(r->pool, my_base, value, NULL); + } + return my_base; +} + +static int imap_reply(request_rec *r, char *redirect) +{ + if (!strcasecmp(redirect, "error")) { + return SERVER_ERROR; /* they actually requested an error! */ + } + if (!strcasecmp(redirect, "nocontent")) { + return HTTP_NO_CONTENT; /* tell the client to keep the page it has */ + } + if (redirect && *redirect) { + ap_table_setn(r->headers_out, "Location", redirect); + return REDIRECT; /* must be a URL, so redirect to it */ + } + return SERVER_ERROR; +} + +static void menu_header(request_rec *r, char *menu) +{ + r->content_type = "text/html"; + ap_send_http_header(r); + ap_hard_timeout("send menu", r); /* killed in menu_footer */ + + ap_rvputs(r, DOCTYPE_HTML_3_2, "\nMenu for ", r->uri, + "\n\n", NULL); + + if (!strcasecmp(menu, "formatted")) { + ap_rvputs(r, "

Menu for ", r->uri, "

\n
\n\n", NULL); + } + + return; +} + +static void menu_blank(request_rec *r, char *menu) +{ + if (!strcasecmp(menu, "formatted")) { + ap_rputs("\n", r); + } + if (!strcasecmp(menu, "semiformatted")) { + ap_rputs("
\n", r); + } + if (!strcasecmp(menu, "unformatted")) { + ap_rputs("\n", r); + } + return; +} + +static void menu_comment(request_rec *r, char *menu, char *comment) +{ + if (!strcasecmp(menu, "formatted")) { + ap_rputs("\n", r); /* print just a newline if 'formatted' */ + } + if (!strcasecmp(menu, "semiformatted") && *comment) { + ap_rvputs(r, comment, "\n", NULL); + } + if (!strcasecmp(menu, "unformatted") && *comment) { + ap_rvputs(r, comment, "\n", NULL); + } + return; /* comments are ignored in the + 'formatted' form */ +} + +static void menu_default(request_rec *r, char *menu, char *href, char *text) +{ + if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) { + return; /* don't print such lines, these aren't + really href's */ + } + if (!strcasecmp(menu, "formatted")) { + ap_rvputs(r, "
(Default) ", text,
+               "
\n", NULL); + } + if (!strcasecmp(menu, "semiformatted")) { + ap_rvputs(r, "
(Default) ", text,
+               "
\n", NULL); + } + if (!strcasecmp(menu, "unformatted")) { + ap_rvputs(r, "", text, "", NULL); + } + return; +} + +static void menu_directive(request_rec *r, char *menu, char *href, char *text) +{ + if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) { + return; /* don't print such lines, as this isn't + really an href */ + } + if (!strcasecmp(menu, "formatted")) { + ap_rvputs(r, "
          ", text,
+               "
\n", NULL); + } + if (!strcasecmp(menu, "semiformatted")) { + ap_rvputs(r, "
          ", text,
+               "
\n", NULL); + } + if (!strcasecmp(menu, "unformatted")) { + ap_rvputs(r, "", text, "", NULL); + } + return; +} + +static void menu_footer(request_rec *r) +{ + ap_rputs("\n\n\n\n", r); /* finish the menu */ + ap_kill_timeout(r); +} + +static int imap_handler(request_rec *r) +{ + char input[MAX_STRING_LEN]; + char *directive; + char *value; + char *href_text; + char *base; + char *redirect; + char *mapdflt; + char *closest = NULL; + double closest_yet = -1; + + double testpoint[2]; + double pointarray[MAXVERTS + 1][2]; + int vertex; + + char *string_pos; + int showmenu = 0; + + imap_conf_rec *icr = ap_get_module_config(r->per_dir_config, &imap_module); + + char *imap_menu = icr->imap_menu ? icr->imap_menu : IMAP_MENU_DEFAULT; + char *imap_default = icr->imap_default + ? icr->imap_default : IMAP_DEFAULT_DEFAULT; + char *imap_base = icr->imap_base ? icr->imap_base : IMAP_BASE_DEFAULT; + + configfile_t *imap; + + if (r->method_number != M_GET) { + return DECLINED; + } + + imap = ap_pcfg_openfile(r->pool, r->filename); + + if (!imap) { + return NOT_FOUND; + } + + base = imap_url(r, NULL, imap_base); /* set base according + to default */ + if (!base) { + return HTTP_INTERNAL_SERVER_ERROR; + } + mapdflt = imap_url(r, NULL, imap_default); /* and default to + global default */ + if (!mapdflt) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + testpoint[X] = get_x_coord(r->args); + testpoint[Y] = get_y_coord(r->args); + + if ((testpoint[X] == -1 || testpoint[Y] == -1) || + (testpoint[X] == 0 && testpoint[Y] == 0)) { + /* if either is -1 or if both are zero (new Lynx) */ + /* we don't have valid coordinates */ + testpoint[X] = -1; + testpoint[Y] = -1; + if (strncasecmp(imap_menu, "none", 2)) { + showmenu = 1; /* show the menu _unless_ ImapMenu is + 'none' or 'no' */ + } + } + + if (showmenu) { /* send start of imagemap menu if + we're going to */ + menu_header(r, imap_menu); + } + + while (!ap_cfg_getline(input, sizeof(input), imap)) { + if (!input[0]) { + if (showmenu) { + menu_blank(r, imap_menu); + } + continue; + } + + if (input[0] == '#') { + if (showmenu) { + menu_comment(r, imap_menu, input + 1); + } + continue; + } /* blank lines and comments are ignored + if we aren't printing a menu */ + + /* find the first two space delimited fields, recall that + * ap_cfg_getline has removed leading/trailing whitespace. + * + * note that we're tokenizing as we go... if we were to use the + * ap_getword() class of functions we would end up allocating extra + * memory for every line of the map file + */ + string_pos = input; + if (!*string_pos) { /* need at least two fields */ + goto need_2_fields; + } + + directive = string_pos; + while (*string_pos && !ap_isspace(*string_pos)) { /* past directive */ + ++string_pos; + } + if (!*string_pos) { /* need at least two fields */ + goto need_2_fields; + } + *string_pos++ = '\0'; + + if (!*string_pos) { /* need at least two fields */ + goto need_2_fields; + } + while(*string_pos && ap_isspace(*string_pos)) { /* past whitespace */ + ++string_pos; + } + + value = string_pos; + while (*string_pos && !ap_isspace(*string_pos)) { /* past value */ + ++string_pos; + } + if (ap_isspace(*string_pos)) { + *string_pos++ = '\0'; + } + else { + /* end of input, don't advance past it */ + *string_pos = '\0'; + } + + if (!strncasecmp(directive, "base", 4)) { /* base, base_uri */ + base = imap_url(r, NULL, value); + if (!base) { + goto menu_bail; + } + continue; /* base is never printed to a menu */ + } + + read_quoted(&string_pos, &href_text); + + if (!strcasecmp(directive, "default")) { /* default */ + mapdflt = imap_url(r, NULL, value); + if (!mapdflt) { + goto menu_bail; + } + if (showmenu) { /* print the default if there's a menu */ + redirect = imap_url(r, base, mapdflt); + if (!redirect) { + goto menu_bail; + } + menu_default(r, imap_menu, redirect, + href_text ? href_text : mapdflt); + } + continue; + } + + vertex = 0; + while (vertex < MAXVERTS && + sscanf(string_pos, "%lf%*[, ]%lf", + &pointarray[vertex][X], &pointarray[vertex][Y]) == 2) { + /* Now skip what we just read... we can't use ANSIism %n */ + while (ap_isspace(*string_pos)) { /* past whitespace */ + string_pos++; + } + while (ap_isdigit(*string_pos)) { /* and the 1st number */ + string_pos++; + } + string_pos++; /* skip the ',' */ + while (ap_isspace(*string_pos)) { /* past any more whitespace */ + string_pos++; + } + while (ap_isdigit(*string_pos)) { /* 2nd number */ + string_pos++; + } + vertex++; + } /* so long as there are more vertices to + read, and we have room, read them in. + We start where we left off of the last + sscanf, not at the beginning. */ + + pointarray[vertex][X] = -1; /* signals the end of vertices */ + + if (showmenu) { + if (!href_text) { + read_quoted(&string_pos, &href_text); /* href text could + be here instead */ + } + redirect = imap_url(r, base, value); + if (!redirect) { + goto menu_bail; + } + menu_directive(r, imap_menu, redirect, + href_text ? href_text : value); + continue; + } + /* note that we don't make it past here if we are making a menu */ + + if (testpoint[X] == -1 || pointarray[0][X] == -1) { + continue; /* don't try the following tests if testpoints + are invalid, or if there are no + coordinates */ + } + + if (!strcasecmp(directive, "poly")) { /* poly */ + + if (pointinpoly(testpoint, pointarray)) { + ap_cfg_closefile(imap); + redirect = imap_url(r, base, value); + if (!redirect) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return (imap_reply(r, redirect)); + } + continue; + } + + if (!strcasecmp(directive, "circle")) { /* circle */ + + if (pointincircle(testpoint, pointarray)) { + ap_cfg_closefile(imap); + redirect = imap_url(r, base, value); + if (!redirect) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return (imap_reply(r, redirect)); + } + continue; + } + + if (!strcasecmp(directive, "rect")) { /* rect */ + + if (pointinrect(testpoint, pointarray)) { + ap_cfg_closefile(imap); + redirect = imap_url(r, base, value); + if (!redirect) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return (imap_reply(r, redirect)); + } + continue; + } + + if (!strcasecmp(directive, "point")) { /* point */ + + if (is_closer(testpoint, pointarray, &closest_yet)) { + closest = ap_pstrdup(r->pool, value); + } + + continue; + } /* move on to next line whether it's + closest or not */ + + } /* nothing matched, so we get another line! */ + + ap_cfg_closefile(imap); /* we are done with the map file; close it */ + + if (showmenu) { + menu_footer(r); /* finish the menu and we are done */ + return OK; + } + + if (closest) { /* if a 'point' directive has been seen */ + redirect = imap_url(r, base, closest); + if (!redirect) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return (imap_reply(r, redirect)); + } + + if (mapdflt) { /* a default should be defined, even if + only 'nocontent' */ + redirect = imap_url(r, base, mapdflt); + if (!redirect) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return (imap_reply(r, redirect)); + } + + return HTTP_INTERNAL_SERVER_ERROR; /* If we make it this far, + we failed. They lose! */ + +need_2_fields: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "map file %s, line %d syntax error: requires at " + "least two fields", r->uri, imap->line_number); + /* fall through */ +menu_bail: + ap_cfg_closefile(imap); + if (showmenu) { + /* There's not much else we can do ... we've already sent the headers + * to the client. + */ + ap_rputs("\n\n[an internal server error occured]\n", r); + menu_footer(r); + return OK; + } + return HTTP_INTERNAL_SERVER_ERROR; +} + + +static const handler_rec imap_handlers[] = +{ + {IMAP_MAGIC_TYPE, imap_handler}, + {"imap-file", imap_handler}, + {NULL} +}; + +module MODULE_VAR_EXPORT imap_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_imap_dir_config, /* dir config creater */ + merge_imap_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + imap_cmds, /* command table */ + imap_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/mappers/mod_negotiation.c b/modules/mappers/mod_negotiation.c new file mode 100644 index 00000000000..5f7411260c2 --- /dev/null +++ b/modules/mappers/mod_negotiation.c @@ -0,0 +1,2748 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_negotiation.c: keeps track of MIME types the client is willing to + * accept, and contains code to handle type arbitration. + * + * rst + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_core.h" +#include "http_log.h" +#include "util_script.h" + +/* Commands --- configuring document caching on a per (virtual?) + * server basis... + */ + +typedef struct { + array_header *language_priority; +} neg_dir_config; + +module MODULE_VAR_EXPORT negotiation_module; + +static void *create_neg_dir_config(pool *p, char *dummy) +{ + neg_dir_config *new = (neg_dir_config *) ap_palloc(p, sizeof(neg_dir_config)); + + new->language_priority = ap_make_array(p, 4, sizeof(char *)); + return new; +} + +static void *merge_neg_dir_configs(pool *p, void *basev, void *addv) +{ + neg_dir_config *base = (neg_dir_config *) basev; + neg_dir_config *add = (neg_dir_config *) addv; + neg_dir_config *new = (neg_dir_config *) ap_palloc(p, sizeof(neg_dir_config)); + + /* give priority to the config in the subdirectory */ + new->language_priority = ap_append_arrays(p, add->language_priority, + base->language_priority); + return new; +} + +static const char *set_language_priority(cmd_parms *cmd, void *n, char *lang) +{ + array_header *arr = ((neg_dir_config *) n)->language_priority; + char **langp = (char **) ap_push_array(arr); + + *langp = lang; + return NULL; +} + +static const char *cache_negotiated_docs(cmd_parms *cmd, void *dummy, + char *dummy2) +{ + void *server_conf = cmd->server->module_config; + + ap_set_module_config(server_conf, &negotiation_module, "Cache"); + return NULL; +} + +static int do_cache_negotiated_docs(server_rec *s) +{ + return (ap_get_module_config(s->module_config, &negotiation_module) != NULL); +} + +static const command_rec negotiation_cmds[] = +{ + {"CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF, NO_ARGS, + "no arguments (either present or absent)"}, + {"LanguagePriority", set_language_priority, NULL, OR_FILEINFO, ITERATE, + "space-delimited list of MIME language abbreviations"}, + {NULL} +}; + +/* + * Record of available info on a media type specified by the client + * (we also use 'em for encodings and languages) + */ + +typedef struct accept_rec { + char *name; /* MUST be lowercase */ + float quality; + float level; + char *charset; /* for content-type only */ +} accept_rec; + +/* + * Record of available info on a particular variant + * + * Note that a few of these fields are updated by the actual negotiation + * code. These are: + * + * level_matched --- initialized to zero. Set to the value of level + * if the client actually accepts this media type at that + * level (and *not* if it got in on a wildcard). See level_cmp + * below. + * mime_stars -- initialized to zero. Set to the number of stars + * present in the best matching Accept header element. + * 1 for star/star, 2 for type/star and 3 for + * type/subtype. + * + * definite -- initialized to 1. Set to 0 if there is a match which + * makes the variant non-definite according to the rules + * in rfc2296. + */ + +typedef struct var_rec { + request_rec *sub_req; /* May be NULL (is, for map files) */ + char *mime_type; /* MUST be lowercase */ + char *file_name; + const char *content_encoding; + array_header *content_languages; /* list of languages for this variant */ + char *content_charset; + char *description; + + /* The next five items give the quality values for the dimensions + * of negotiation for this variant. They are obtained from the + * appropriate header lines, except for source_quality, which + * is obtained from the variant itself (the 'qs' parameter value + * from the variant's mime-type). Apart from source_quality, + * these values are set when we find the quality for each variant + * (see best_match()). source_quality is set from the 'qs' parameter + * of the variant description or mime type: see set_mime_fields(). + */ + float lang_quality; /* quality of this variant's language */ + float encoding_quality; /* ditto encoding */ + float charset_quality; /* ditto charset */ + float mime_type_quality; /* ditto media type */ + float source_quality; /* source quality for this variant */ + + /* Now some special values */ + float level; /* Auxiliary to content-type... */ + float bytes; /* content length, if known */ + int lang_index; /* pre HTTP/1.1 language priority stuff */ + int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */ + + /* Above are all written-once properties of the variant. The + * three fields below are changed during negotiation: + */ + + float level_matched; + int mime_stars; + int definite; +} var_rec; + +/* Something to carry around the state of negotiation (and to keep + * all of this thread-safe)... + */ + +typedef struct { + pool *pool; + request_rec *r; + char *dir_name; + int accept_q; /* 1 if an Accept item has a q= param */ + float default_lang_quality; /* fiddle lang q for variants with no lang */ + + /* the array pointers below are NULL if the corresponding accept + * headers are not present + */ + array_header *accepts; /* accept_recs */ + array_header *accept_encodings; /* accept_recs */ + array_header *accept_charsets; /* accept_recs */ + array_header *accept_langs; /* accept_recs */ + + array_header *avail_vars; /* available variants */ + + int count_multiviews_variants; /* number of variants found on disk */ + + int is_transparent; /* 1 if this resource is trans. negotiable */ + + int dont_fiddle_headers; /* 1 if we may not fiddle with accept hdrs */ + int ua_supports_trans; /* 1 if ua supports trans negotiation */ + int send_alternates; /* 1 if we want to send an Alternates header */ + int may_choose; /* 1 if we may choose a variant for the client */ + int use_rvsa; /* 1 if we must use RVSA/1.0 negotiation algo */ +} negotiation_state; + +/* A few functions to manipulate var_recs. + * Cleaning out the fields... + */ + +static void clean_var_rec(var_rec *mime_info) +{ + mime_info->sub_req = NULL; + mime_info->mime_type = ""; + mime_info->file_name = ""; + mime_info->content_encoding = NULL; + mime_info->content_languages = NULL; + mime_info->content_charset = ""; + mime_info->description = ""; + + mime_info->is_pseudo_html = 0; + mime_info->level = 0.0f; + mime_info->level_matched = 0.0f; + mime_info->bytes = 0.0f; + mime_info->lang_index = -1; + mime_info->mime_stars = 0; + mime_info->definite = 1; + + mime_info->charset_quality = 1.0f; + mime_info->encoding_quality = 1.0f; + mime_info->lang_quality = 1.0f; + mime_info->mime_type_quality = 1.0f; + mime_info->source_quality = 0.0f; +} + +/* Initializing the relevant fields of a variant record from the + * accept_info read out of its content-type, one way or another. + */ + +static void set_mime_fields(var_rec *var, accept_rec *mime_info) +{ + var->mime_type = mime_info->name; + var->source_quality = mime_info->quality; + var->level = mime_info->level; + var->content_charset = mime_info->charset; + + var->is_pseudo_html = (!strcmp(var->mime_type, "text/html") + || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE) + || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE3)); +} + +/* Create a variant list validator in r using info from vlistr. */ + +static void set_vlist_validator(request_rec *r, request_rec *vlistr) +{ + /* Calculating the variant list validator is similar to + * calculating an etag for the source of the variant list + * information, so we use ap_make_etag(). Note that this + * validator can be 'weak' in extreme case. + */ + + ap_update_mtime (vlistr, vlistr->finfo.st_mtime); + r->vlist_validator = ap_make_etag(vlistr, 0); + + /* ap_set_etag will later take r->vlist_validator into account + * when creating the etag header + */ +} + + +/***************************************************************** + * + * Parsing (lists of) media types and their parameters, as seen in + * HTTPD header lines and elsewhere. + */ + +/* + * Get a single mime type entry --- one media type and parameters; + * enter the values we recognize into the argument accept_rec + */ + +static const char *get_entry(pool *p, accept_rec *result, + const char *accept_line) +{ + result->quality = 1.0f; + result->level = 0.0f; + result->charset = ""; + + /* + * Note that this handles what I gather is the "old format", + * + * Accept: text/html text/plain moo/zot + * + * without any compatibility kludges --- if the token after the + * MIME type begins with a semicolon, we know we're looking at parms, + * otherwise, we know we aren't. (So why all the pissing and moaning + * in the CERN server code? I must be missing something). + */ + + result->name = ap_get_token(p, &accept_line, 0); + ap_str_tolower(result->name); /* You want case-insensitive, + * you'll *get* case-insensitive. + */ + + /* KLUDGE!!! Default HTML to level 2.0 unless the browser + * *explicitly* says something else. + */ + + if (!strcmp(result->name, "text/html") && (result->level == 0.0)) { + result->level = 2.0f; + } + else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE)) { + result->level = 2.0f; + } + else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE3)) { + result->level = 3.0f; + } + + while (*accept_line == ';') { + /* Parameters ... */ + + char *parm; + char *cp; + char *end; + + ++accept_line; + parm = ap_get_token(p, &accept_line, 1); + + /* Look for 'var = value' --- and make sure the var is in lcase. */ + + for (cp = parm; (*cp && !ap_isspace(*cp) && *cp != '='); ++cp) { + *cp = ap_tolower(*cp); + } + + if (!*cp) { + continue; /* No '='; just ignore it. */ + } + + *cp++ = '\0'; /* Delimit var */ + while (*cp && (ap_isspace(*cp) || *cp == '=')) { + ++cp; + } + + if (*cp == '"') { + ++cp; + for (end = cp; + (*end && *end != '\n' && *end != '\r' && *end != '\"'); + end++); + } + else { + for (end = cp; (*end && !ap_isspace(*end)); end++); + } + if (*end) { + *end = '\0'; /* strip ending quote or return */ + } + ap_str_tolower(cp); + + if (parm[0] == 'q' + && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) { + result->quality = atof(cp); + } + else if (parm[0] == 'l' && !strcmp(&parm[1], "evel")) { + result->level = atof(cp); + } + else if (!strcmp(parm, "charset")) { + result->charset = cp; + } + } + + if (*accept_line == ',') { + ++accept_line; + } + + return accept_line; +} + +/***************************************************************** + * + * Dealing with header lines ... + * + * Accept, Accept-Charset, Accept-Language and Accept-Encoding + * are handled by do_header_line() - they all have the same + * basic structure of a list of items of the format + * name; q=N; charset=TEXT + * + * where charset is only valid in Accept. + */ + +static array_header *do_header_line(pool *p, const char *accept_line) +{ + array_header *accept_recs; + + if (!accept_line) { + return NULL; + } + + accept_recs = ap_make_array(p, 40, sizeof(accept_rec)); + + while (*accept_line) { + accept_rec *new = (accept_rec *) ap_push_array(accept_recs); + accept_line = get_entry(p, new, accept_line); + } + + return accept_recs; +} + +/* Given the text of the Content-Languages: line from the var map file, + * return an array containing the languages of this variant + */ + +static array_header *do_languages_line(pool *p, const char **lang_line) +{ + array_header *lang_recs = ap_make_array(p, 2, sizeof(char *)); + + if (!lang_line) { + return lang_recs; + } + + while (**lang_line) { + char **new = (char **) ap_push_array(lang_recs); + *new = ap_get_token(p, lang_line, 0); + ap_str_tolower(*new); + if (**lang_line == ',' || **lang_line == ';') { + ++(*lang_line); + } + } + + return lang_recs; +} + +/***************************************************************** + * + * Handling header lines from clients... + */ + +static negotiation_state *parse_accept_headers(request_rec *r) +{ + negotiation_state *new = + (negotiation_state *) ap_pcalloc(r->pool, sizeof(negotiation_state)); + accept_rec *elts; + table *hdrs = r->headers_in; + int i; + + new->pool = r->pool; + new->r = r; + new->dir_name = ap_make_dirstr_parent(r->pool, r->filename); + + new->accepts = do_header_line(r->pool, ap_table_get(hdrs, "Accept")); + + /* calculate new->accept_q value */ + if (new->accepts) { + elts = (accept_rec *) new->accepts->elts; + + for (i = 0; i < new->accepts->nelts; ++i) { + if (elts[i].quality < 1.0) { + new->accept_q = 1; + } + } + } + + new->accept_encodings = + do_header_line(r->pool, ap_table_get(hdrs, "Accept-Encoding")); + new->accept_langs = + do_header_line(r->pool, ap_table_get(hdrs, "Accept-Language")); + new->accept_charsets = + do_header_line(r->pool, ap_table_get(hdrs, "Accept-Charset")); + + new->avail_vars = ap_make_array(r->pool, 40, sizeof(var_rec)); + + return new; +} + + +static void parse_negotiate_header(request_rec *r, negotiation_state *neg) +{ + const char *negotiate = ap_table_get(r->headers_in, "Negotiate"); + char *tok; + + /* First, default to no TCN, no Alternates, and the original Apache + * negotiation algorithm with fiddles for broken browser configs. + * + * To save network bandwidth, we do not configure to send an + * Alternates header to the user agent by default. User + * agents that want an Alternates header for agent-driven + * negotiation will have to request it by sending an + * appropriate Negotiate header. + */ + neg->ua_supports_trans = 0; + neg->send_alternates = 0; + neg->may_choose = 1; + neg->use_rvsa = 0; + neg->dont_fiddle_headers = 0; + + if (!negotiate) + return; + + if (strcmp(negotiate, "trans") == 0) { + /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they + * do not support transparent content negotiation, so for Lynx we + * ignore the negotiate header when its contents are exactly "trans". + * If future versions of Lynx ever need to say 'negotiate: trans', + * they can send the equivalent 'negotiate: trans, trans' instead + * to avoid triggering the workaround below. + */ + const char *ua = ap_table_get(r->headers_in, "User-Agent"); + + if (ua && (strncmp(ua, "Lynx", 4) == 0)) + return; + } + + neg->may_choose = 0; /* An empty Negotiate would require 300 response */ + + while ((tok = ap_get_list_item(neg->pool, &negotiate)) != NULL) { + + if (strcmp(tok, "trans") == 0 || + strcmp(tok, "vlist") == 0 || + strcmp(tok, "guess-small") == 0 || + ap_isdigit(tok[0]) || + strcmp(tok, "*") == 0) { + + /* The user agent supports transparent negotiation */ + neg->ua_supports_trans = 1; + + /* Send-alternates could be configurable, but note + * that it must be 1 if we have 'vlist' in the + * negotiate header. + */ + neg->send_alternates = 1; + + if (strcmp(tok, "1.0") == 0) { + /* we may use the RVSA/1.0 algorithm, configure for it */ + neg->may_choose = 1; + neg->use_rvsa = 1; + neg->dont_fiddle_headers = 1; + } + else if (tok[0] == '*') { + /* we may use any variant selection algorithm, configure + * to use the Apache algorithm + */ + neg->may_choose = 1; + + /* We disable header fiddles on the assumption that a + * client sending Negotiate knows how to send correct + * headers which don't need fiddling. + */ + neg->dont_fiddle_headers = 1; + } + } + } + +#ifdef NEG_DEBUG + fprintf(stderr, "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d " + "send_alternates=%d, may_choose=%d\n", + neg->dont_fiddle_headers, neg->use_rvsa, + neg->ua_supports_trans, neg->send_alternates, neg->may_choose); +#endif + +} + +/* Sometimes clients will give us no Accept info at all; this routine sets + * up the standard default for that case, and also arranges for us to be + * willing to run a CGI script if we find one. (In fact, we set up to + * dramatically prefer CGI scripts in cases where that's appropriate, + * e.g., POST or when URI includes query args or extra path info). + */ +static void maybe_add_default_accepts(negotiation_state *neg, + int prefer_scripts) +{ + accept_rec *new_accept; + + if (!neg->accepts) { + neg->accepts = ap_make_array(neg->pool, 4, sizeof(accept_rec)); + + new_accept = (accept_rec *) ap_push_array(neg->accepts); + + new_accept->name = "*/*"; + new_accept->quality = 1.0f; + new_accept->level = 0.0f; + } + + new_accept = (accept_rec *) ap_push_array(neg->accepts); + + new_accept->name = CGI_MAGIC_TYPE; + if (neg->use_rvsa) { + new_accept->quality = 0; + } + else { + new_accept->quality = prefer_scripts ? 2.0f : 0.001f; + } + new_accept->level = 0.0f; +} + +/***************************************************************** + * + * Parsing type-map files, in Roy's meta/http format augmented with + * #-comments. + */ + +/* Reading RFC822-style header lines, ignoring #-comments and + * handling continuations. + */ + +enum header_state { + header_eof, header_seen, header_sep +}; + +static enum header_state get_header_line(char *buffer, int len, FILE *map) +{ + char *buf_end = buffer + len; + char *cp; + int c; + + /* Get a noncommented line */ + + do { + if (fgets(buffer, MAX_STRING_LEN, map) == NULL) { + return header_eof; + } + } while (buffer[0] == '#'); + + /* If blank, just return it --- this ends information on this variant */ + + for (cp = buffer; (*cp && ap_isspace(*cp)); ++cp) { + continue; + } + + if (*cp == '\0') { + return header_sep; + } + + /* If non-blank, go looking for header lines, but note that we still + * have to treat comments specially... + */ + + cp += strlen(cp); + + while ((c = getc(map)) != EOF) { + if (c == '#') { + /* Comment line */ + while ((c = getc(map)) != EOF && c != '\n') { + continue; + } + } + else if (ap_isspace(c)) { + /* Leading whitespace. POSSIBLE continuation line + * Also, possibly blank --- if so, we ungetc() the final newline + * so that we will pick up the blank line the next time 'round. + */ + + while (c != EOF && c != '\n' && ap_isspace(c)) { + c = getc(map); + } + + ungetc(c, map); + + if (c == '\n') { + return header_seen; /* Blank line */ + } + + /* Continuation */ + + while (cp < buf_end - 2 && (c = getc(map)) != EOF && c != '\n') { + *cp++ = c; + } + + *cp++ = '\n'; + *cp = '\0'; + } + else { + + /* Line beginning with something other than whitespace */ + + ungetc(c, map); + return header_seen; + } + } + + return header_seen; +} + +/* Stripping out RFC822 comments */ + +static void strip_paren_comments(char *hdr) +{ + /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */ + /* Nope, it isn't correct. Fails to handle backslash escape as well. */ + + while (*hdr) { + if (*hdr == '"') { + hdr = strchr(hdr, '"'); + if (hdr == NULL) { + return; + } + ++hdr; + } + else if (*hdr == '(') { + while (*hdr && *hdr != ')') { + *hdr++ = ' '; + } + + if (*hdr) { + *hdr++ = ' '; + } + } + else { + ++hdr; + } + } +} + +/* Getting to a header body from the header */ + +static char *lcase_header_name_return_body(char *header, request_rec *r) +{ + char *cp = header; + + for ( ; *cp && *cp != ':' ; ++cp) { + *cp = ap_tolower(*cp); + } + + if (!*cp) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Syntax error in type map --- no ':': %s", r->filename); + return NULL; + } + + do { + ++cp; + } while (*cp && ap_isspace(*cp)); + + if (!*cp) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Syntax error in type map --- no header body: %s", + r->filename); + return NULL; + } + + return cp; +} + +static int read_type_map(negotiation_state *neg, request_rec *rr) +{ + request_rec *r = neg->r; + FILE *map; + char buffer[MAX_STRING_LEN]; + enum header_state hstate; + struct var_rec mime_info; + int has_content; + + /* We are not using multiviews */ + neg->count_multiviews_variants = 0; + + map = ap_pfopen(neg->pool, rr->filename, "r"); + if (map == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "cannot access type map file: %s", rr->filename); + return HTTP_FORBIDDEN; + } + + clean_var_rec(&mime_info); + has_content = 0; + + do { + hstate = get_header_line(buffer, MAX_STRING_LEN, map); + + if (hstate == header_seen) { + char *body1 = lcase_header_name_return_body(buffer, neg->r); + const char *body; + + if (body1 == NULL) { + return SERVER_ERROR; + } + + strip_paren_comments(body1); + body = body1; + + if (!strncmp(buffer, "uri:", 4)) { + mime_info.file_name = ap_get_token(neg->pool, &body, 0); + } + else if (!strncmp(buffer, "content-type:", 13)) { + struct accept_rec accept_info; + + get_entry(neg->pool, &accept_info, body); + set_mime_fields(&mime_info, &accept_info); + has_content = 1; + } + else if (!strncmp(buffer, "content-length:", 15)) { + mime_info.bytes = atof(body); + has_content = 1; + } + else if (!strncmp(buffer, "content-language:", 17)) { + mime_info.content_languages = do_languages_line(neg->pool, + &body); + has_content = 1; + } + else if (!strncmp(buffer, "content-encoding:", 17)) { + mime_info.content_encoding = ap_get_token(neg->pool, &body, 0); + has_content = 1; + } + else if (!strncmp(buffer, "description:", 12)) { + char *desc = ap_pstrdup(neg->pool, body); + char *cp; + + for (cp = desc; *cp; ++cp) { + if (*cp=='\n') *cp=' '; + } + if (cp>desc) *(cp-1)=0; + mime_info.description = desc; + } + } + else { + if (*mime_info.file_name && has_content) { + void *new_var = ap_push_array(neg->avail_vars); + + memcpy(new_var, (void *) &mime_info, sizeof(var_rec)); + } + + clean_var_rec(&mime_info); + has_content = 0; + } + } while (hstate != header_eof); + + ap_pfclose(neg->pool, map); + + set_vlist_validator(r, rr); + + return OK; +} + + +/* Sort function used by read_types_multi. */ +static int variantsortf(var_rec *a, var_rec *b) { + + /* First key is the source quality, sort in descending order. */ + + /* XXX: note that we currently implement no method of setting the + * source quality for multiviews variants, so we are always comparing + * 1.0 to 1.0 for now + */ + if (a->source_quality < b->source_quality) + return 1; + if (a->source_quality > b->source_quality) + return -1; + + /* Second key is the variant name */ + return strcmp(a->file_name, b->file_name); +} + +/***************************************************************** + * + * Same as read_type_map, except we use a filtered directory listing + * as the map... + */ + +static int read_types_multi(negotiation_state *neg) +{ + request_rec *r = neg->r; + + char *filp; + int prefix_len; + DIR *dirp; + struct DIR_TYPE *dir_entry; + struct var_rec mime_info; + struct accept_rec accept_info; + void *new_var; + + clean_var_rec(&mime_info); + + if (!(filp = strrchr(r->filename, '/'))) { + return DECLINED; /* Weird... */ + } + + if (strncmp(r->filename, "proxy:", 6) == 0) { + return DECLINED; + } + + ++filp; + prefix_len = strlen(filp); + + dirp = ap_popendir(neg->pool, neg->dir_name); + + if (dirp == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "cannot read directory for multi: %s", neg->dir_name); + return HTTP_FORBIDDEN; + } + + while ((dir_entry = readdir(dirp))) { + request_rec *sub_req; + + /* Do we have a match? */ + + if (strncmp(dir_entry->d_name, filp, prefix_len)) { + continue; + } + if (dir_entry->d_name[prefix_len] != '.') { + continue; + } + + /* Yep. See if it's something which we have access to, and + * which has a known type and encoding (as opposed to something + * which we'll be slapping default_type on later). + */ + + sub_req = ap_sub_req_lookup_file(dir_entry->d_name, r); + + /* If it has a handler, we'll pretend it's a CGI script, + * since that's a good indication of the sort of thing it + * might be doing. + */ + if (sub_req->handler && !sub_req->content_type) { + sub_req->content_type = CGI_MAGIC_TYPE; + } + + if (sub_req->status != HTTP_OK || !sub_req->content_type) { + ap_destroy_sub_req(sub_req); + continue; + } + + /* If it's a map file, we use that instead of the map + * we're building... + */ + + if (((sub_req->content_type) && + !strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) || + ((sub_req->handler) && + !strcmp(sub_req->handler, "type-map"))) { + + ap_pclosedir(neg->pool, dirp); + neg->avail_vars->nelts = 0; + if (sub_req->status != HTTP_OK) { + return sub_req->status; + } + return read_type_map(neg, sub_req); + } + + /* Have reasonable variant --- gather notes. */ + + mime_info.sub_req = sub_req; + mime_info.file_name = ap_pstrdup(neg->pool, dir_entry->d_name); + if (sub_req->content_encoding) { + mime_info.content_encoding = sub_req->content_encoding; + } + if (sub_req->content_languages) { + mime_info.content_languages = sub_req->content_languages; + } + + get_entry(neg->pool, &accept_info, sub_req->content_type); + set_mime_fields(&mime_info, &accept_info); + + new_var = ap_push_array(neg->avail_vars); + memcpy(new_var, (void *) &mime_info, sizeof(var_rec)); + + neg->count_multiviews_variants++; + + clean_var_rec(&mime_info); + } + + ap_pclosedir(neg->pool, dirp); + + set_vlist_validator(r, r); + + /* Sort the variants into a canonical order. The negotiation + * result sometimes depends on the order of the variants. By + * sorting the variants into a canonical order, rather than using + * the order in which readdir() happens to return them, we ensure + * that the negotiation result will be consistent over filesystem + * backup/restores and over all mirror sites. + */ + + qsort((void *) neg->avail_vars->elts, neg->avail_vars->nelts, + sizeof(var_rec), (int (*)(const void *, const void *)) variantsortf); + + return OK; +} + + +/***************************************************************** + * And now for the code you've been waiting for... actually + * finding a match to the client's requirements. + */ + +/* Matching MIME types ... the star/star and foo/star commenting conventions + * are implemented here. (You know what I mean by star/star, but just + * try mentioning those three characters in a C comment). Using strcmp() + * is legit, because everything has already been smashed to lowercase. + * + * Note also that if we get an exact match on the media type, we update + * level_matched for use in level_cmp below... + * + * We also give a value for mime_stars, which is used later. It should + * be 1 for star/star, 2 for type/star and 3 for type/subtype. + */ + +static int mime_match(accept_rec *accept_r, var_rec *avail) +{ + char *accept_type = accept_r->name; + char *avail_type = avail->mime_type; + int len = strlen(accept_type); + + if (accept_type[0] == '*') { /* Anything matches star/star */ + if (avail->mime_stars < 1) { + avail->mime_stars = 1; + } + return 1; + } + else if ((accept_type[len - 1] == '*') && + !strncmp(accept_type, avail_type, len - 2)) { + if (avail->mime_stars < 2) { + avail->mime_stars = 2; + } + return 1; + } + else if (!strcmp(accept_type, avail_type) + || (!strcmp(accept_type, "text/html") + && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE) + || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) { + if (accept_r->level >= avail->level) { + avail->level_matched = avail->level; + avail->mime_stars = 3; + return 1; + } + } + + return OK; +} + +/* This code implements a piece of the tie-breaking algorithm between + * variants of equal quality. This piece is the treatment of variants + * of the same base media type, but different levels. What we want to + * return is the variant at the highest level that the client explicitly + * claimed to accept. + * + * If all the variants available are at a higher level than that, or if + * the client didn't say anything specific about this media type at all + * and these variants just got in on a wildcard, we prefer the lowest + * level, on grounds that that's the one that the client is least likely + * to choke on. + * + * (This is all motivated by treatment of levels in HTML --- we only + * want to give level 3 to browsers that explicitly ask for it; browsers + * that don't, including HTTP/0.9 browsers that only get the implicit + * "Accept: * / *" [space added to avoid confusing cpp --- no, that + * syntax doesn't really work] should get HTML2 if available). + * + * (Note that this code only comes into play when we are choosing among + * variants of equal quality, where the draft standard gives us a fair + * bit of leeway about what to do. It ain't specified by the standard; + * rather, it is a choice made by this server about what to do in cases + * where the standard does not specify a unique course of action). + */ + +static int level_cmp(var_rec *var1, var_rec *var2) +{ + /* Levels are only comparable between matching media types */ + + if (var1->is_pseudo_html && !var2->is_pseudo_html) { + return 0; + } + + if (!var1->is_pseudo_html && strcmp(var1->mime_type, var2->mime_type)) { + return 0; + } + /* The result of the above if statements is that, if we get to + * here, both variants have the same mime_type or both are + * pseudo-html. + */ + + /* Take highest level that matched, if either did match. */ + + if (var1->level_matched > var2->level_matched) { + return 1; + } + if (var1->level_matched < var2->level_matched) { + return -1; + } + + /* Neither matched. Take lowest level, if there's a difference. */ + + if (var1->level < var2->level) { + return 1; + } + if (var1->level > var2->level) { + return -1; + } + + /* Tied */ + + return 0; +} + +/* Finding languages. The main entry point is set_language_quality() + * which is called for each variant. It sets two elements in the + * variant record: + * language_quality - the 'q' value of the 'best' matching language + * from Accept-Language: header (HTTP/1.1) + * lang_index - Pre HTTP/1.1 language priority, using + * position of language on the Accept-Language: + * header, if present, else LanguagePriority + * directive order. + * + * When we do the variant checking for best variant, we use language + * quality first, and if a tie, language_index next (this only applies + * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0 + * algorithm, lang_index is never used. + * + * set_language_quality() calls find_lang_index() and find_default_index() + * to set lang_index. + */ + +static int find_lang_index(array_header *accept_langs, char *lang) +{ + accept_rec *accs; + int i; + + if (!lang || !accept_langs) { + return -1; + } + + accs = (accept_rec *) accept_langs->elts; + + for (i = 0; i < accept_langs->nelts; ++i) { + if (!strncmp(lang, accs[i].name, strlen(accs[i].name))) { + return i; + } + } + + return -1; +} + +/* This function returns the priority of a given language + * according to LanguagePriority. It is used in case of a tie + * between several languages. + */ + +static int find_default_index(neg_dir_config *conf, char *lang) +{ + array_header *arr; + int nelts; + char **elts; + int i; + + if (!lang) { + return -1; + } + + arr = conf->language_priority; + nelts = arr->nelts; + elts = (char **) arr->elts; + + for (i = 0; i < nelts; ++i) { + if (!strcasecmp(elts[i], lang)) { + return i; + } + } + + return -1; +} + +/* set_default_lang_quality() sets the quality we apply to variants + * which have no language assigned to them. If none of the variants + * have a language, we are not negotiating on language, so all are + * acceptable, and we set the default q value to 1.0. However if + * some of the variants have languages, we set this default to 0.001. + * The value of this default will be applied to all variants with + * no explicit language -- which will have the effect of making them + * acceptable, but only if no variants with an explicit language + * are acceptable. The default q value set here is assigned to variants + * with no language type in set_language_quality(). + * + * Note that if using the RVSA/1.0 algorithm, we don't use this + * fiddle. + */ + +static void set_default_lang_quality(negotiation_state *neg) +{ + var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; + int j; + + if (!neg->dont_fiddle_headers) { + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + if (variant->content_languages && + variant->content_languages->nelts) { + neg->default_lang_quality = 0.001f; + return; + } + } + } + + neg->default_lang_quality = 1.0f; +} + +/* Set the language_quality value in the variant record. Also + * assigns lang_index for back-compat. + * + * To find the language_quality value, we look for the 'q' value + * of the 'best' matching language on the Accept-Language + * header. The 'best' match is the language on Accept-Language + * header which matches the language of this variant either fully, + * or as far as the prefix marker (-). If two or more languages + * match, use the longest string from the Accept-Language header + * (see HTTP/1.1 [14.4]) + * + * When a variant has multiple languages, we find the 'best' + * match for each variant language tag as above, then select the + * one with the highest q value. Because both the accept-header + * and variant can have multiple languages, we now have a hairy + * loop-within-a-loop here. + * + * If the variant has no language and we have no Accept-Language + * items, leave the quality at 1.0 and return. + * + * If the variant has no language, we use the default as set by + * set_default_lang_quality() (1.0 if we are not negotiating on + * language, 0.001 if we are). + * + * Following the setting of the language quality, we drop through to + * set the old 'lang_index'. This is set based on either the order + * of the languages on the Accept-Language header, or the + * order on the LanguagePriority directive. This is only used + * in the negotiation if the language qualities tie. + */ + +static void set_language_quality(negotiation_state *neg, var_rec *variant) +{ + char *firstlang; + int idx; + + if (!variant->content_languages || !variant->content_languages->nelts) { + /* This variant has no content-language, so use the default + * quality factor for variants with no content-language + * (previously set by set_default_lang_quality()). + * Leave the factor alone (it remains at 1.0) when we may not fiddle + * with the headers. + */ + if (!neg->dont_fiddle_headers) { + variant->lang_quality = neg->default_lang_quality; + } + if (!neg->accept_langs) { + return; /* no accept-language header */ + } + + } + else { + /* Variant has one (or more) languages. Look for the best + * match. We do this by going through each language on the + * variant description looking for a match on the + * Accept-Language header. The best match is the longest + * matching language on the header. The final result is the + * best q value from all the languages on the variant + * description. + */ + + if (!neg->accept_langs) { + /* no accept-language header makes the variant indefinite */ + variant->definite = 0; + } + else { /* There is an accept-language with 0 or more items */ + accept_rec *accs = (accept_rec *) neg->accept_langs->elts; + accept_rec *best = NULL, *star = NULL; + accept_rec *bestthistag; + char *lang, *p; + float fiddle_q = 0.0f; + int any_match_on_star = 0; + int i, j, alen, longest_lang_range_len; + + for (j = 0; j < variant->content_languages->nelts; ++j) { + p = NULL; + bestthistag = NULL; + longest_lang_range_len = 0; + alen = 0; + + /* lang is the variant's language-tag, which is the one + * we are allowed to use the prefix of in HTTP/1.1 + */ + lang = ((char **) (variant->content_languages->elts))[j]; + + /* now find the best (i.e. longest) matching + * Accept-Language header language. We put the best match + * for this tag in bestthistag. We cannot update the + * overall best (based on q value) because the best match + * for this tag is the longest language item on the accept + * header, not necessarily the highest q. + */ + for (i = 0; i < neg->accept_langs->nelts; ++i) { + if (!strcmp(accs[i].name, "*")) { + if (!star) { + star = &accs[i]; + } + continue; + } + /* Find language. We match if either the variant + * language tag exactly matches the language range + * from the accept header, or a prefix of the variant + * language tag up to a '-' character matches the + * whole of the language range in the Accept-Language + * header. Note that HTTP/1.x allows any number of + * '-' characters in a tag or range, currently only + * tags with zero or one '-' characters are defined + * for general use (see rfc1766). + * + * We only use language range in the Accept-Language + * header the best match for the variant language tag + * if it is longer than the previous best match. + */ + + alen = strlen(accs[i].name); + + if ((strlen(lang) >= alen) && + !strncmp(lang, accs[i].name, alen) && + ((lang[alen] == 0) || (lang[alen] == '-')) ) { + + if (alen > longest_lang_range_len) { + longest_lang_range_len = alen; + bestthistag = &accs[i]; + } + } + + if (!bestthistag && !neg->dont_fiddle_headers) { + /* The next bit is a fiddle. Some browsers might + * be configured to send more specific language + * ranges than desirable. For example, an + * Accept-Language of en-US should never match + * variants with languages en or en-GB. But US + * English speakers might pick en-US as their + * language choice. So this fiddle checks if the + * language range has a prefix, and if so, it + * matches variants which match that prefix with a + * priority of 0.001. So a request for en-US would + * match variants of types en and en-GB, but at + * much lower priority than matches of en-US + * directly, or of any other language listed on + * the Accept-Language header. Note that this + * fiddle does not handle multi-level prefixes. + */ + if ((p = strchr(accs[i].name, '-'))) { + int plen = p - accs[i].name; + + if (!strncmp(lang, accs[i].name, plen)) { + fiddle_q = 0.001f; + } + } + } + } + /* Finished looking at Accept-Language headers, the best + * (longest) match is in bestthistag, or NULL if no match + */ + if (!best || + (bestthistag && bestthistag->quality > best->quality)) { + best = bestthistag; + } + + /* See if the tag matches on a * in the Accept-Language + * header. If so, record this fact for later use + */ + if (!bestthistag && star) { + any_match_on_star = 1; + } + } + + /* If one of the language tags of the variant matched on *, we + * need to see if its q is better than that of any non-* match + * on any other tag of the variant. If so the * match takes + * precedence and the overall match is not definite. + */ + if ( any_match_on_star && + ((best && star->quality > best->quality) || + (!best)) ) { + best = star; + variant->definite = 0; + } + + variant->lang_quality = best ? best->quality : fiddle_q; + } + } + + /* Now set the old lang_index field. Since this is old + * stuff anyway, don't bother with handling multiple languages + * per variant, just use the first one assigned to it + */ + idx = 0; + if (variant->content_languages && variant->content_languages->nelts) { + firstlang = ((char **) variant->content_languages->elts)[0]; + } + else { + firstlang = ""; + } + if (!neg->accept_langs) { /* Client doesn't care */ + idx = find_default_index((neg_dir_config *) ap_get_module_config( + neg->r->per_dir_config, &negotiation_module), + firstlang); + } + else { /* Client has Accept-Language */ + idx = find_lang_index(neg->accept_langs, firstlang); + } + variant->lang_index = idx; + + return; +} + +/* Determining the content length --- if the map didn't tell us, + * we have to do a stat() and remember for next time. + * + * Grump. For Apache, even the first stat here may well be + * redundant (for multiviews) with a stat() done by the sub_req + * machinery. At some point, that ought to be fixed. + */ + +static float find_content_length(negotiation_state *neg, var_rec *variant) +{ + struct stat statb; + + if (variant->bytes == 0) { + char *fullname = ap_make_full_path(neg->pool, neg->dir_name, + variant->file_name); + + if (stat(fullname, &statb) >= 0) { + /* Note, precision may be lost */ + variant->bytes = (float) statb.st_size; + } + } + + return variant->bytes; +} + +/* For a given variant, find the best matching Accept: header + * and assign the Accept: header's quality value to the + * mime_type_quality field of the variant, for later use in + * determining the best matching variant. + */ + +static void set_accept_quality(negotiation_state *neg, var_rec *variant) +{ + int i; + accept_rec *accept_recs; + float q = 0.0f; + int q_definite = 1; + + /* if no Accept: header, leave quality alone (will + * remain at the default value of 1) + * + * XXX: This if is currently never true because of the effect of + * maybe_add_default_accepts(). + */ + if (!neg->accepts) { + if (variant->mime_type && *variant->mime_type) + variant->definite = 0; + return; + } + + accept_recs = (accept_rec *) neg->accepts->elts; + + /* + * Go through each of the ranges on the Accept: header, + * looking for the 'best' match with this variant's + * content-type. We use the best match's quality + * value (from the Accept: header) for this variant's + * mime_type_quality field. + * + * The best match is determined like this: + * type/type is better than type/ * is better than * / * + * if match is type/type, use the level mime param if available + */ + for (i = 0; i < neg->accepts->nelts; ++i) { + + accept_rec *type = &accept_recs[i]; + int prev_mime_stars; + + prev_mime_stars = variant->mime_stars; + + if (!mime_match(type, variant)) { + continue; /* didn't match the content type at all */ + } + else { + /* did match - see if there were less or more stars than + * in previous match + */ + if (prev_mime_stars == variant->mime_stars) { + continue; /* more stars => not as good a match */ + } + } + + /* If we are allowed to mess with the q-values + * and have no explicit q= parameters in the accept header, + * make wildcards very low, so we have a low chance + * of ending up with them if there's something better. + */ + + if (!neg->dont_fiddle_headers && !neg->accept_q && + variant->mime_stars == 1) { + q = 0.01f; + } + else if (!neg->dont_fiddle_headers && !neg->accept_q && + variant->mime_stars == 2) { + q = 0.02f; + } + else { + q = type->quality; + } + + q_definite = (variant->mime_stars == 3); + } + variant->mime_type_quality = q; + variant->definite = variant->definite && q_definite; + +} + +/* For a given variant, find the 'q' value of the charset given + * on the Accept-Charset line. If no charsets are listed, + * assume value of '1'. + */ +static void set_charset_quality(negotiation_state *neg, var_rec *variant) +{ + int i; + accept_rec *accept_recs; + char *charset = variant->content_charset; + accept_rec *star = NULL; + + /* if no Accept-Charset: header, leave quality alone (will + * remain at the default value of 1) + */ + if (!neg->accept_charsets) { + if (charset && *charset) + variant->definite = 0; + return; + } + + accept_recs = (accept_rec *) neg->accept_charsets->elts; + + if (charset == NULL || !*charset) { + /* Charset of variant not known */ + + /* if not a text / * type, leave quality alone */ + if (!(!strncmp(variant->mime_type, "text/", 5) + || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE) + || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE3) + )) + return; + + /* Don't go guessing if we are in strict header mode, + * e.g. when running the rvsa, as any guess won't be reflected + * in the variant list or content-location headers. + */ + if (neg->dont_fiddle_headers) + return; + + charset = "iso-8859-1"; /* The default charset for HTTP text types */ + } + + /* + * Go through each of the items on the Accept-Charset header, + * looking for a match with this variant's charset. If none + * match, charset is unacceptable, so set quality to 0. + */ + for (i = 0; i < neg->accept_charsets->nelts; ++i) { + + accept_rec *type = &accept_recs[i]; + + if (!strcmp(type->name, charset)) { + variant->charset_quality = type->quality; + return; + } + else if (strcmp(type->name, "*") == 0) { + star = type; + } + } + /* No explicit match */ + if (star) { + variant->charset_quality = star->quality; + variant->definite = 0; + return; + } + /* If this variant is in charset iso-8859-1, the default is 1.0 */ + if (strcmp(charset, "iso-8859-1") == 0) { + variant->charset_quality = 1.0f; + } + else { + variant->charset_quality = 0.0f; + } +} + + +/* is_identity_encoding is included for back-compat, but does anyone + * use 7bit, 8bin or binary in their var files?? + */ + +static int is_identity_encoding(const char *enc) +{ + return (!enc || !enc[0] || !strcmp(enc, "7bit") || !strcmp(enc, "8bit") + || !strcmp(enc, "binary")); +} + +/* + * set_encoding_quality determines whether the encoding for a particular + * variant is acceptable for the user-agent. + * + * The rules for encoding are that if the user-agent does not supply + * any Accept-Encoding header, then all encodings are allowed but a + * variant with no encoding should be preferred. + * If there is an empty Accept-Encoding header, then no encodings are + * acceptable. If there is a non-empty Accept-Encoding header, then + * any of the listed encodings are acceptable, as well as no encoding + * unless the "identity" encoding is specifically excluded. + */ +static void set_encoding_quality(negotiation_state *neg, var_rec *variant) +{ + accept_rec *accept_recs; + const char *enc = variant->content_encoding; + accept_rec *star = NULL; + float value_if_not_found = 0.0f; + int i; + + if (!neg->accept_encodings) { + /* We had no Accept-Encoding header, assume that all + * encodings are acceptable with a low quality, + * but we prefer no encoding if available. + */ + if (!enc || is_identity_encoding(enc)) + variant->encoding_quality = 1.0f; + else + variant->encoding_quality = 0.5f; + + return; + } + + if (!enc || is_identity_encoding(enc)) { + enc = "identity"; + value_if_not_found = 0.0001f; + } + + accept_recs = (accept_rec *) neg->accept_encodings->elts; + + /* Go through each of the encodings on the Accept-Encoding: header, + * looking for a match with our encoding. x- prefixes are ignored. + */ + if (enc[0] == 'x' && enc[1] == '-') { + enc += 2; + } + for (i = 0; i < neg->accept_encodings->nelts; ++i) { + + char *name = accept_recs[i].name; + + if (name[0] == 'x' && name[1] == '-') { + name += 2; + } + + if (!strcmp(name, enc)) { + variant->encoding_quality = accept_recs[i].quality; + return; + } + + if (strcmp(name, "*") == 0) { + star = &accept_recs[i]; + } + + } + /* No explicit match */ + if (star) { + variant->encoding_quality = star->quality; + return; + } + + /* Encoding not found on Accept-Encoding: header, so it is + * _not_ acceptable unless it is the identity (no encoding) + */ + variant->encoding_quality = value_if_not_found; +} + +/************************************************************* + * Possible results of the variant selection algorithm + */ +enum algorithm_results { + alg_choice = 1, /* choose variant */ + alg_list /* list variants */ +}; + +/* Below is the 'best_match' function. It returns an int, which has + * one of the two values alg_choice or alg_list, which give the result + * of the variant selection algorithm. alg_list means that no best + * variant was found by the algorithm, alg_choice means that a best + * variant was found and should be returned. The list/choice + * terminology comes from TCN (rfc2295), but is used in a more generic + * way here. The best variant is returned in *pbest. best_match has + * two possible algorithms for determining the best variant: the + * RVSA/1.0 algorithm (from RFC2296), and the standard Apache + * algorithm. These are split out into separate functions + * (is_variant_better_rvsa() and is_variant_better()). Selection of + * one is through the neg->use_rvsa flag. + * + * The call to best_match also creates full information, including + * language, charset, etc quality for _every_ variant. This is needed + * for generating a correct Vary header, and can be used for the + * Alternates header, the human-readable list responses and 406 errors. + */ + +/* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm + * v1.0) from rfc2296. This is the algorithm that goes together with + * transparent content negotiation (TCN). + */ +static int is_variant_better_rvsa(negotiation_state *neg, var_rec *variant, + var_rec *best, float *p_bestq) +{ + float bestq = *p_bestq, q; + + /* TCN does not cover negotiation on content-encoding. For now, + * we ignore the encoding unless it was explicitly excluded. + */ + if (variant->encoding_quality == 0.0f) + return 0; + + q = variant->mime_type_quality * + variant->source_quality * + variant->charset_quality * + variant->lang_quality; + + /* RFC 2296 calls for the result to be rounded to 5 decimal places, + * but we don't do that because it serves no useful purpose other + * than to ensure that a remote algorithm operates on the same + * precision as ours. That is silly, since what we obviously want + * is for the algorithm to operate on the best available precision + * regardless of who runs it. Since the above calculation may + * result in significant variance at 1e-12, rounding would be bogus. + */ + +#ifdef NEG_DEBUG + fprintf(stderr, "Variant: file=%s type=%s lang=%s sourceq=%1.3f " + "mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f " + "q=%1.5f definite=%d\n", + (variant->file_name ? variant->file_name : ""), + (variant->mime_type ? variant->mime_type : ""), + (variant->content_languages + ? ap_array_pstrcat(neg->pool, variant->content_languages, ',') + : ""), + variant->source_quality, + variant->mime_type_quality, + variant->lang_quality, + variant->charset_quality, + variant->encoding_quality, + q, + variant->definite); +#endif + + if (q <= 0.0f) { + return 0; + } + if (q > bestq) { + *p_bestq = q; + return 1; + } + if (q == bestq) { + /* If the best variant's encoding is of lesser quality than + * this variant, then we prefer this variant + */ + if (variant->encoding_quality > best->encoding_quality) { + *p_bestq = q; + return 1; + } + } + return 0; +} + +/* Negotiation algorithm as used by previous versions of Apache + * (just about). + */ + +static int is_variant_better(negotiation_state *neg, var_rec *variant, + var_rec *best, float *p_bestq) +{ + float bestq = *p_bestq, q; + int levcmp; + + /* For non-transparent negotiation, server can choose how + * to handle the negotiation. We'll use the following in + * order: content-type, language, content-type level, charset, + * content encoding, content length. + * + * For each check, we have three possible outcomes: + * This variant is worse than current best: return 0 + * This variant is better than the current best: + * assign this variant's q to *p_bestq, and return 1 + * This variant is just as desirable as the current best: + * drop through to the next test. + * + * This code is written in this long-winded way to allow future + * customisation, either by the addition of additional + * checks, or to allow the order of the checks to be determined + * by configuration options (e.g. we might prefer to check + * language quality _before_ content type). + */ + + /* First though, eliminate this variant if it is not + * acceptable by type, charset, encoding or language. + */ + +#ifdef NEG_DEBUG + fprintf(stderr, "Variant: file=%s type=%s lang=%s sourceq=%1.3f " + "mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f \n", + (variant->file_name ? variant->file_name : ""), + (variant->mime_type ? variant->mime_type : ""), + (variant->content_languages + ? ap_array_pstrcat(neg->pool, variant->content_languages, ',') + : ""), + variant->source_quality, + variant->mime_type_quality, + variant->lang_quality, + variant->lang_index, + variant->charset_quality, + variant->encoding_quality); +#endif + + if (variant->encoding_quality == 0.0f || + variant->lang_quality == 0.0f || + variant->source_quality == 0.0f || + variant->charset_quality == 0.0f || + variant->mime_type_quality == 0.0f) { + return 0; /* don't consider unacceptables */ + } + + q = variant->mime_type_quality * variant->source_quality; + if (q == 0.0 || q < bestq) { + return 0; + } + if (q > bestq || !best) { + *p_bestq = q; + return 1; + } + + /* language */ + if (variant->lang_quality < best->lang_quality) { + return 0; + } + if (variant->lang_quality > best->lang_quality) { + *p_bestq = q; + return 1; + } + + /* if language qualities were equal, try the LanguagePriority stuff */ + if (best->lang_index != -1 && + (variant->lang_index == -1 || variant->lang_index > best->lang_index)) { + return 0; + } + if (variant->lang_index != -1 && + (best->lang_index == -1 || variant->lang_index < best->lang_index)) { + *p_bestq = q; + return 1; + } + + /* content-type level (sometimes used with text/html, though we + * support it on other types too) + */ + levcmp = level_cmp(variant, best); + if (levcmp == -1) { + return 0; + } + if (levcmp == 1) { + *p_bestq = q; + return 1; + } + + /* charset */ + if (variant->charset_quality < best->charset_quality) { + return 0; + } + /* If the best variant's charset is ISO-8859-1 and this variant has + * the same charset quality, then we prefer this variant + */ + + if (variant->charset_quality > best->charset_quality || + ((variant->content_charset != NULL && + *variant->content_charset != '\0' && + strcmp(variant->content_charset, "iso-8859-1") != 0) && + (best->content_charset == NULL || + *best->content_charset == '\0' || + strcmp(best->content_charset, "iso-8859-1") == 0))) { + *p_bestq = q; + return 1; + } + + /* Prefer the highest value for encoding_quality. + */ + if (variant->encoding_quality < best->encoding_quality) { + return 0; + } + if (variant->encoding_quality > best->encoding_quality) { + *p_bestq = q; + return 1; + } + + /* content length if all else equal */ + if (find_content_length(neg, variant) >= find_content_length(neg, best)) { + return 0; + } + + /* ok, to get here means every thing turned out equal, except + * we have a shorter content length, so use this variant + */ + *p_bestq = q; + return 1; +} + +static int best_match(negotiation_state *neg, var_rec **pbest) +{ + int j; + var_rec *best = NULL; + float bestq = 0.0f; + enum algorithm_results algorithm_result; + + var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; + + set_default_lang_quality(neg); + + /* + * Find the 'best' variant + */ + + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + + /* Find all the relevant 'quality' values from the + * Accept... headers, and store in the variant. This also + * prepares for sending an Alternates header etc so we need to + * do it even if we do not actually plan to find a best + * variant. + */ + set_accept_quality(neg, variant); + set_language_quality(neg, variant); + set_encoding_quality(neg, variant); + set_charset_quality(neg, variant); + + /* Only do variant selection if we may actually choose a + * variant for the client + */ + if (neg->may_choose) { + + /* Now find out if this variant is better than the current + * best, either using the RVSA/1.0 algorithm, or Apache's + * internal server-driven algorithm. Presumably other + * server-driven algorithms are possible, and could be + * implemented here. + */ + + if (neg->use_rvsa) { + if (is_variant_better_rvsa(neg, variant, best, &bestq)) { + best = variant; + } + } + else { + if (is_variant_better(neg, variant, best, &bestq)) { + best = variant; + } + } + } + } + + /* We now either have a best variant, or no best variant */ + + if (neg->use_rvsa) { + /* calculate result for RVSA/1.0 algorithm: + * only a choice response if the best variant has q>0 + * and is definite + */ + algorithm_result = (best && best->definite) && (bestq > 0) ? + alg_choice : alg_list; + } + else { + /* calculate result for Apache negotiation algorithm */ + algorithm_result = bestq > 0 ? alg_choice : alg_list; + } + + /* Returning a choice response with a non-neighboring variant is a + * protocol security error in TCN (see rfc2295). We do *not* + * verify here that the variant and URI are neighbors, even though + * we may return alg_choice. We depend on the environment (the + * caller) to only declare the resource transparently negotiable if + * all variants are neighbors. + */ + *pbest = best; + return algorithm_result; +} + +/* Sets response headers for a negotiated response. + * neg->is_transparent determines whether a transparently negotiated + * response or a plain `server driven negotiation' response is + * created. Applicable headers are Alternates, Vary, and TCN. + * + * The Vary header we create is sometimes longer than is required for + * the correct caching of negotiated results by HTTP/1.1 caches. For + * example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if + * the Accept: header assigns a 0 quality to .ps, then the results of + * the two server-side negotiation algorithms we currently implement + * will never depend on Accept-Language so we could return `Vary: + * negotiate, accept' instead of the longer 'Vary: negotiate, accept, + * accept-language' which the code below will return. A routine for + * computing the exact minimal Vary header would be a huge pain to code + * and maintain though, especially because we need to take all possible + * twiddles in the server-side negotiation algorithms into account. + */ +static void set_neg_headers(request_rec *r, negotiation_state *neg, + int alg_result) +{ + table *hdrs; + var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; + const char *sample_type = NULL; + const char *sample_language = NULL; + const char *sample_encoding = NULL; + const char *sample_charset = NULL; + char *lang; + char *qstr; + char *lenstr; + long len; + array_header *arr; + int max_vlist_array = (neg->avail_vars->nelts * 21); + int first_variant = 1; + int vary_by_type = 0; + int vary_by_language = 0; + int vary_by_charset = 0; + int vary_by_encoding = 0; + int j; + + /* In order to avoid O(n^2) memory copies in building Alternates, + * we preallocate a table with the maximum substrings possible, + * fill it with the variant list, and then concatenate the entire array. + * Note that if you change the number of substrings pushed, you also + * need to change the calculation of max_vlist_array above. + */ + if (neg->send_alternates && neg->avail_vars->nelts) + arr = ap_make_array(r->pool, max_vlist_array, sizeof(char *)); + else + arr = NULL; + + /* Put headers into err_headers_out, since send_http_header() + * outputs both headers_out and err_headers_out. + */ + hdrs = r->err_headers_out; + + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + + if (variant->content_languages && variant->content_languages->nelts) { + lang = ap_array_pstrcat(r->pool, variant->content_languages, ','); + } + else { + lang = NULL; + } + + /* Calculate Vary by looking for any difference between variants */ + + if (first_variant) { + sample_type = variant->mime_type; + sample_charset = variant->content_charset; + sample_language = lang; + sample_encoding = variant->content_encoding; + } + else { + if (!vary_by_type && + strcmp(sample_type ? sample_type : "", + variant->mime_type ? variant->mime_type : "")) { + vary_by_type = 1; + } + if (!vary_by_charset && + strcmp(sample_charset ? sample_charset : "", + variant->content_charset ? + variant->content_charset : "")) { + vary_by_charset = 1; + } + if (!vary_by_language && + strcmp(sample_language ? sample_language : "", + lang ? lang : "")) { + vary_by_language = 1; + } + if (!vary_by_encoding && + strcmp(sample_encoding ? sample_encoding : "", + variant->content_encoding ? + variant->content_encoding : "")) { + vary_by_encoding = 1; + } + } + first_variant = 0; + + if (!neg->send_alternates) + continue; + + /* Generate the string components for this Alternates entry */ + + *((const char **) ap_push_array(arr)) = "{\""; + *((const char **) ap_push_array(arr)) = variant->file_name; + *((const char **) ap_push_array(arr)) = "\" "; + + qstr = (char *) ap_palloc(r->pool, 6); + ap_snprintf(qstr, 6, "%1.3f", variant->source_quality); + + /* Strip trailing zeros (saves those valuable network bytes) */ + if (qstr[4] == '0') { + qstr[4] = '\0'; + if (qstr[3] == '0') { + qstr[3] = '\0'; + if (qstr[2] == '0') { + qstr[1] = '\0'; + } + } + } + *((const char **) ap_push_array(arr)) = qstr; + + if (variant->mime_type && *variant->mime_type) { + *((const char **) ap_push_array(arr)) = " {type "; + *((const char **) ap_push_array(arr)) = variant->mime_type; + *((const char **) ap_push_array(arr)) = "}"; + } + if (variant->content_charset && *variant->content_charset) { + *((const char **) ap_push_array(arr)) = " {charset "; + *((const char **) ap_push_array(arr)) = variant->content_charset; + *((const char **) ap_push_array(arr)) = "}"; + } + if (lang) { + *((const char **) ap_push_array(arr)) = " {language "; + *((const char **) ap_push_array(arr)) = lang; + *((const char **) ap_push_array(arr)) = "}"; + } + if (variant->content_encoding && *variant->content_encoding) { + /* Strictly speaking, this is non-standard, but so is TCN */ + + *((const char **) ap_push_array(arr)) = " {encoding "; + *((const char **) ap_push_array(arr)) = variant->content_encoding; + *((const char **) ap_push_array(arr)) = "}"; + } + + /* Note that the Alternates specification (in rfc2295) does + * not require that we include {length x}, so we could omit it + * if determining the length is too expensive. We currently + * always include it though. 22 bytes is enough for 2^64. + * + * If the variant is a CGI script, find_content_length would + * return the length of the script, not the output it + * produces, so we check for the presence of a handler and if + * there is one we don't add a length. + * + * XXX: TODO: This check does not detect a CGI script if we + * get the variant from a type map. This needs to be fixed + * (without breaking things if the type map specifies a + * content-length, which currently leads to the correct result). + */ + if (!(variant->sub_req && variant->sub_req->handler) + && (len = find_content_length(neg, variant)) != 0) { + + lenstr = (char *) ap_palloc(r->pool, 22); + ap_snprintf(lenstr, 22, "%ld", len); + *((const char **) ap_push_array(arr)) = " {length "; + *((const char **) ap_push_array(arr)) = lenstr; + *((const char **) ap_push_array(arr)) = "}"; + } + + *((const char **) ap_push_array(arr)) = "}"; + *((const char **) ap_push_array(arr)) = ", "; /* trimmed below */ + } + + if (neg->send_alternates && neg->avail_vars->nelts) { + arr->nelts--; /* remove last comma */ + ap_table_mergen(hdrs, "Alternates", + ap_array_pstrcat(r->pool, arr, '\0')); + } + + if (neg->is_transparent || vary_by_type || vary_by_language || + vary_by_language || vary_by_charset || vary_by_encoding) { + + ap_table_mergen(hdrs, "Vary", 2 + ap_pstrcat(r->pool, + neg->is_transparent ? ", negotiate" : "", + vary_by_type ? ", accept" : "", + vary_by_language ? ", accept-language" : "", + vary_by_charset ? ", accept-charset" : "", + vary_by_encoding ? ", accept-encoding" : "", NULL)); + } + + if (neg->is_transparent) { /* Create TCN response header */ + ap_table_setn(hdrs, "TCN", + alg_result == alg_list ? "list" : "choice"); + } +} + +/********************************************************************** + * + * Return an HTML list of variants. This is output as part of the + * choice response or 406 status body. + */ + +static char *make_variant_list(request_rec *r, negotiation_state *neg) +{ + array_header *arr; + int i; + int max_vlist_array = (neg->avail_vars->nelts * 15) + 2; + + /* In order to avoid O(n^2) memory copies in building the list, + * we preallocate a table with the maximum substrings possible, + * fill it with the variant list, and then concatenate the entire array. + */ + arr = ap_make_array(r->pool, max_vlist_array, sizeof(char *)); + + *((const char **) ap_push_array(arr)) = "Available variants:\n
    \n"; + + for (i = 0; i < neg->avail_vars->nelts; ++i) { + var_rec *variant = &((var_rec *) neg->avail_vars->elts)[i]; + char *filename = variant->file_name ? variant->file_name : ""; + array_header *languages = variant->content_languages; + char *description = variant->description ? variant->description : ""; + + /* The format isn't very neat, and it would be nice to make + * the tags human readable (eg replace 'language en' with 'English'). + * Note that if you change the number of substrings pushed, you also + * need to change the calculation of max_vlist_array above. + */ + *((const char **) ap_push_array(arr)) = "
  • "; + *((const char **) ap_push_array(arr)) = filename; + *((const char **) ap_push_array(arr)) = " "; + *((const char **) ap_push_array(arr)) = description; + + if (variant->mime_type && *variant->mime_type) { + *((const char **) ap_push_array(arr)) = ", type "; + *((const char **) ap_push_array(arr)) = variant->mime_type; + } + if (languages && languages->nelts) { + *((const char **) ap_push_array(arr)) = ", language "; + *((const char **) ap_push_array(arr)) = ap_array_pstrcat(r->pool, + languages, ','); + } + if (variant->content_charset && *variant->content_charset) { + *((const char **) ap_push_array(arr)) = ", charset "; + *((const char **) ap_push_array(arr)) = variant->content_charset; + } + if (variant->content_encoding) { + *((const char **) ap_push_array(arr)) = ", encoding "; + *((const char **) ap_push_array(arr)) = variant->content_encoding; + } + *((const char **) ap_push_array(arr)) = "\n"; + } + *((const char **) ap_push_array(arr)) = "
\n"; + + return ap_array_pstrcat(r->pool, arr, '\0'); +} + +static void store_variant_list(request_rec *r, negotiation_state *neg) +{ + if (r->main == NULL) { + ap_table_setn(r->notes, "variant-list", make_variant_list(r, neg)); + } + else { + ap_table_setn(r->main->notes, "variant-list", + make_variant_list(r->main, neg)); + } +} + +/* Called if we got a "Choice" response from the variant selection algorithm. + * It checks the result of the chosen variant to see if it + * is itself negotiated (if so, return error VARIANT_ALSO_VARIES). + * Otherwise, add the appropriate headers to the current response. + */ + +static int setup_choice_response(request_rec *r, negotiation_state *neg, + var_rec *variant) +{ + request_rec *sub_req; + const char *sub_vary; + + if (!variant->sub_req) { + int status; + + sub_req = ap_sub_req_lookup_file(variant->file_name, r); + status = sub_req->status; + + if (status != HTTP_OK && + !ap_table_get(sub_req->err_headers_out, "TCN")) { + ap_destroy_sub_req(sub_req); + return status; + } + variant->sub_req = sub_req; + } + else { + sub_req = variant->sub_req; + } + + /* The variant selection algorithm told us to return a "Choice" + * response. This is the normal variant response, with + * some extra headers. First, ensure that the chosen + * variant did or will not itself engage in transparent negotiation. + * If not, set the appropriate headers, and fall through to + * the normal variant handling + */ + + /* This catches the error that a transparent type map selects a + * transparent multiviews resource as the best variant. + * + * XXX: We do not signal an error if a transparent type map + * selects a _non_transparent multiviews resource as the best + * variant, because we can generate a legal negotiation response + * in this case. In this case, the vlist_validator of the + * nontransparent subrequest will be lost however. This could + * lead to cases in which a change in the set of variants or the + * negotiation algorithm of the nontransparent resource is never + * propagated up to a HTTP/1.1 cache which interprets Vary. To be + * completely on the safe side we should return VARIANT_ALSO_VARIES + * for this type of recursive negotiation too. + */ + if (neg->is_transparent && + ap_table_get(sub_req->err_headers_out, "TCN")) { + return VARIANT_ALSO_VARIES; + } + + /* This catches the error that a transparent type map recursively + * selects, as the best variant, another type map which itself + * causes transparent negotiation to be done. + * + * XXX: Actually, we catch this error by catching all cases of + * type map recursion. There are some borderline recursive type + * map arrangements which would not produce transparent + * negotiation protocol errors or lack of cache propagation + * problems, but such arrangements are very hard to detect at this + * point in the control flow, so we do not bother to single them + * out. + * + * Recursive type maps imply a recursive arrangement of negotiated + * resources which is visible to outside clients, and this is not + * supported by the transparent negotiation caching protocols, so + * if we are to have generic support for recursive type maps, we + * have to create some configuration setting which makes all type + * maps non-transparent when recursion is enabled. Also, if we + * want recursive type map support which ensures propagation of + * type map changes into HTTP/1.1 caches that handle Vary, we + * would have to extend the current mechanism for generating + * variant list validators. + */ + if (sub_req->handler && strcmp(sub_req->handler, "type-map") == 0) { + return VARIANT_ALSO_VARIES; + } + + /* This adds an appropriate Variant-Vary header if the subrequest + * is a multiviews resource. + * + * XXX: TODO: Note that this does _not_ handle any Vary header + * returned by a CGI if sub_req is a CGI script, because we don't + * see that Vary header yet at this point in the control flow. + * This won't cause any cache consistency problems _unless_ the + * CGI script also returns a Cache-Control header marking the + * response as cachable. This needs to be fixed, also there are + * problems if a CGI returns an Etag header which also need to be + * fixed. + */ + if ((sub_vary = ap_table_get(sub_req->err_headers_out, "Vary")) != NULL) { + ap_table_setn(r->err_headers_out, "Variant-Vary", sub_vary); + + /* Move the subreq Vary header into the main request to + * prevent having two Vary headers in the response, which + * would be legal but strange. + */ + ap_table_setn(r->err_headers_out, "Vary", sub_vary); + ap_table_unset(sub_req->err_headers_out, "Vary"); + } + + ap_table_setn(r->err_headers_out, "Content-Location", + ap_pstrdup(r->pool, variant->file_name)); + + set_neg_headers(r, neg, alg_choice); /* add Alternates and Vary */ + + /* Still to do by caller: add Expires */ + + return 0; +} + +/**************************************************************** + * + * Executive... + */ + +static int do_negotiation(request_rec *r, negotiation_state *neg, + var_rec **bestp, int prefer_scripts) +{ + var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; + int alg_result; /* result of variant selection algorithm */ + int res; + int j; + + /* Decide if resource is transparently negotiable */ + + /* GET or HEAD? (HEAD has same method number as GET) */ + if (r->method_number == M_GET) { + + /* maybe this should be configurable, see also the comment + * about recursive type maps in setup_choice_response() + */ + neg->is_transparent = 1; + + /* We can't be transparent if we are a map file in the middle + * of the request URI. + */ + if (r->path_info && *r->path_info) + neg->is_transparent = 0; + + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + + /* We can't be transparent, because of internal + * assumptions in best_match(), if there is a + * non-neighboring variant. We can have a non-neighboring + * variant when processing a type map. + */ + if (strchr(variant->file_name, '/')) + neg->is_transparent = 0; + } + } + + if (neg->is_transparent) { + parse_negotiate_header(r, neg); + } + else { /* configure negotiation on non-transparent resource */ + neg->may_choose = 1; + } + + maybe_add_default_accepts(neg, prefer_scripts); + + alg_result = best_match(neg, bestp); + + /* alg_result is one of + * alg_choice: a best variant is chosen + * alg_list: no best variant is chosen + */ + + if (alg_result == alg_list) { + /* send a list response or NOT_ACCEPTABLE error response */ + + neg->send_alternates = 1; /* always include Alternates header */ + set_neg_headers(r, neg, alg_result); + store_variant_list(r, neg); + + if (neg->is_transparent && neg->ua_supports_trans) { + /* XXX todo: expires? cachability? */ + + /* Some HTTP/1.0 clients are known to choke when they get + * a 300 (multiple choices) response without a Location + * header. However the 300 code response we are are about + * to generate will only reach 1.0 clients which support + * transparent negotiation, and they should be OK. The + * response should never reach older 1.0 clients, even if + * we have CacheNegotiatedDocs enabled, because no 1.0 + * proxy cache (we know of) will cache and return 300 + * responses (they certainly won't if they conform to the + * HTTP/1.0 specification). + */ + return MULTIPLE_CHOICES; + } + + if (!*bestp) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "no acceptable variant: %s", r->filename); + return NOT_ACCEPTABLE; + } + } + + /* Variant selection chose a variant */ + + /* XXX todo: merge the two cases in the if statement below */ + if (neg->is_transparent) { + + if ((res = setup_choice_response(r, neg, *bestp)) != 0) { + return res; /* return if error */ + } + } + else { + set_neg_headers(r, neg, alg_result); + } + + /* Make sure caching works - Vary should handle HTTP/1.1, but for + * HTTP/1.0, we can't allow caching at all. + */ + + /* XXX: Note that we only set r->no_cache to 1, which causes + * Expires: to be added, when responding to a HTTP/1.0 + * client. If we return the response to a 1.1 client, we do not + * add Expires , because doing so would degrade 1.1 cache + * performance by preventing re-use of the response without prior + * revalidation. On the other hand, if the 1.1 client is a proxy + * which was itself contacted by a 1.0 client, or a proxy cache + * which can be contacted later by 1.0 clients, then we currently + * rely on this 1.1 proxy to add the Expires: when it + * forwards the response. + * + * XXX: TODO: Find out if the 1.1 spec requires proxies and + * tunnels to add Expires: when forwarding the response to + * 1.0 clients. I (kh) recall it is rather vague on this point. + * Testing actual 1.1 proxy implementations would also be nice. If + * Expires: is not added by proxies then we need to always + * include Expires: ourselves to ensure correct caching, but + * this would degrade HTTP/1.1 cache efficiency unless we also add + * Cache-Control: max-age=N, which we currently don't. + * + * Roy: No, we are not going to screw over HTTP future just to + * ensure that people who can't be bothered to upgrade their + * clients will always receive perfect server-side negotiation. + * Hell, those clients are sending bogus accept headers anyway. + * + * Manual setting of cache-control/expires always overrides this + * automated kluge, on purpose. + */ + + if ((!do_cache_negotiated_docs(r->server) + && (r->proto_num < HTTP_VERSION(1,1))) + && neg->count_multiviews_variants != 1) { + r->no_cache = 1; + } + + return OK; +} + +static int handle_map_file(request_rec *r) +{ + negotiation_state *neg = parse_accept_headers(r); + var_rec *best; + int res; + + char *udir; + + if ((res = read_type_map(neg, r))) { + return res; + } + + res = do_negotiation(r, neg, &best, 0); + if (res != 0) return res; + + if (r->path_info && *r->path_info) { + r->uri[ap_find_path_info(r->uri, r->path_info)] = '\0'; + } + udir = ap_make_dirstr_parent(r->pool, r->uri); + udir = ap_escape_uri(r->pool, udir); + ap_internal_redirect(ap_pstrcat(r->pool, udir, best->file_name, + r->path_info, NULL), r); + return OK; +} + +static int handle_multi(request_rec *r) +{ + negotiation_state *neg; + var_rec *best, *avail_recs; + request_rec *sub_req; + int res; + int j; + + if (r->finfo.st_mode != 0 || !(ap_allow_options(r) & OPT_MULTI)) { + return DECLINED; + } + + neg = parse_accept_headers(r); + + if ((res = read_types_multi(neg))) { + return_from_multi: + /* free all allocated memory from subrequests */ + avail_recs = (var_rec *) neg->avail_vars->elts; + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + if (variant->sub_req) { + ap_destroy_sub_req(variant->sub_req); + } + } + return res; + } + if (neg->avail_vars->nelts == 0) { + return DECLINED; + } + + res = do_negotiation(r, neg, &best, + (r->method_number != M_GET) || r->args || + (r->path_info && *r->path_info)); + if (res != 0) + goto return_from_multi; + + if (!(sub_req = best->sub_req)) { + /* We got this out of a map file, so we don't actually have + * a sub_req structure yet. Get one now. + */ + + sub_req = ap_sub_req_lookup_file(best->file_name, r); + if (sub_req->status != HTTP_OK) { + res = sub_req->status; + ap_destroy_sub_req(sub_req); + goto return_from_multi; + } + } + + /* BLECH --- don't multi-resolve non-ordinary files */ + + if (!S_ISREG(sub_req->finfo.st_mode)) { + res = NOT_FOUND; + goto return_from_multi; + } + + /* Otherwise, use it. */ + + /* now do a "fast redirect" ... promote the sub_req into the main req */ + /* We need to tell POOL_DEBUG that we're guaranteeing that sub_req->pool + * will exist as long as r->pool. Otherwise we run into troubles because + * some values in this request will be allocated in r->pool, and others in + * sub_req->pool. + */ + ap_pool_join(r->pool, sub_req->pool); + r->mtime = 0; /* reset etag info for subrequest */ + r->filename = sub_req->filename; + r->handler = sub_req->handler; + r->content_type = sub_req->content_type; + r->content_encoding = sub_req->content_encoding; + r->content_languages = sub_req->content_languages; + r->content_language = sub_req->content_language; + r->finfo = sub_req->finfo; + r->per_dir_config = sub_req->per_dir_config; + /* copy output headers from subrequest, but leave negotiation headers */ + r->notes = ap_overlay_tables(r->pool, sub_req->notes, r->notes); + r->headers_out = ap_overlay_tables(r->pool, sub_req->headers_out, + r->headers_out); + r->err_headers_out = ap_overlay_tables(r->pool, sub_req->err_headers_out, + r->err_headers_out); + r->subprocess_env = ap_overlay_tables(r->pool, sub_req->subprocess_env, + r->subprocess_env); + avail_recs = (var_rec *) neg->avail_vars->elts; + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + if (variant != best && variant->sub_req) { + ap_destroy_sub_req(variant->sub_req); + } + } + return OK; +} + +/********************************************************************** + * There is a problem with content-encoding, as some clients send and + * expect an x- token (e.g. x-gzip) while others expect the plain token + * (i.e. gzip). To try and deal with this as best as possible we do + * the following: if the client sent an Accept-Encoding header and it + * contains a plain token corresponding to the content encoding of the + * response, then set content encoding using the plain token. Else if + * the A-E header contains the x- token use the x- token in the C-E + * header. Else don't do anything. + * + * Note that if no A-E header was sent, or it does not contain a token + * compatible with the final content encoding, then the token in the + * C-E header will be whatever was specified in the AddEncoding + * directive. + */ +static int fix_encoding(request_rec *r) +{ + const char *enc = r->content_encoding; + char *x_enc = NULL; + array_header *accept_encodings; + accept_rec *accept_recs; + int i; + + if (!enc || !*enc) { + return DECLINED; + } + + if (enc[0] == 'x' && enc[1] == '-') { + enc += 2; + } + + if ((accept_encodings = do_header_line(r->pool, + ap_table_get(r->headers_in, "Accept-Encoding"))) == NULL) { + return DECLINED; + } + + accept_recs = (accept_rec *) accept_encodings->elts; + + for (i = 0; i < accept_encodings->nelts; ++i) { + char *name = accept_recs[i].name; + + if (!strcmp(name, enc)) { + r->content_encoding = name; + return OK; + } + + if (name[0] == 'x' && name[1] == '-' && !strcmp(name+2, enc)) { + x_enc = name; + } + } + + if (x_enc) { + r->content_encoding = x_enc; + return OK; + } + + return DECLINED; +} + +static const handler_rec negotiation_handlers[] = +{ + {MAP_FILE_MAGIC_TYPE, handle_map_file}, + {"type-map", handle_map_file}, + {NULL} +}; + +module MODULE_VAR_EXPORT negotiation_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_neg_dir_config, /* dir config creator */ + merge_neg_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + negotiation_cmds, /* command table */ + negotiation_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + handle_multi, /* type_checker */ + fix_encoding, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c new file mode 100644 index 00000000000..c44de0dafab --- /dev/null +++ b/modules/mappers/mod_rewrite.c @@ -0,0 +1,4263 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* _ _ _ +** _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___ +** | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \ +** | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/ +** |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___| +** |_____| +** +** URL Rewriting Module +** +** This module uses a rule-based rewriting engine (based on a +** regular-expression parser) to rewrite requested URLs on the fly. +** +** It supports an unlimited number of additional rule conditions (which can +** operate on a lot of variables, even on HTTP headers) for granular +** matching and even external database lookups (either via plain text +** tables, DBM hash files or even external processes) for advanced URL +** substitution. +** +** It operates on the full URLs (including the PATH_INFO part) both in +** per-server context (httpd.conf) and per-dir context (.htaccess) and even +** can generate QUERY_STRING parts on result. The rewriting result finally +** can lead to internal subprocessing, external request redirection or even +** to internal proxy throughput. +** +** This module was originally written in April 1996 and +** gifted exclusively to the The Apache Group in July 1997 by +** +** Ralf S. Engelschall +** rse@engelschall.com +** www.engelschall.com +*/ + + +#include "mod_rewrite.h" + +#ifndef NO_WRITEV +#include +#include +#endif + +/* +** +-------------------------------------------------------+ +** | | +** | static module configuration +** | | +** +-------------------------------------------------------+ +*/ + + +/* +** Our interface to the Apache server kernel: +** +** o Runtime logic of a request is as following: +** while(request or subrequest) +** foreach(stage #0...#9) +** foreach(module) (**) +** try to run hook +** +** o the order of modules at (**) is the inverted order as +** given in the "Configuration" file, i.e. the last module +** specified is the first one called for each hook! +** The core module is always the last! +** +** o there are two different types of result checking and +** continue processing: +** for hook #0,#1,#4,#5,#6,#8: +** hook run loop stops on first modules which gives +** back a result != DECLINED, i.e. it usually returns OK +** which says "OK, module has handled this _stage_" and for #1 +** this have not to mean "Ok, the filename is now valid". +** for hook #2,#3,#7,#9: +** all hooks are run, independend of result +** +** o at the last stage, the core module always +** - says "BAD_REQUEST" if r->filename does not begin with "/" +** - prefix URL with document_root or replaced server_root +** with document_root and sets r->filename +** - always return a "OK" independed if the file really exists +** or not! +*/ + + /* The section for the Configure script: + * MODULE-DEFINITION-START + * Name: rewrite_module + * ConfigStart + . ./helpers/find-dbm-lib + if [ "x$found_dbm" = "x1" ]; then + echo " enabling DBM support for mod_rewrite" + else + echo " disabling DBM support for mod_rewrite" + echo " (perhaps you need to add -ldbm, -lndbm or -lgdbm to EXTRA_LIBS)" + CFLAGS="$CFLAGS -DNO_DBM_REWRITEMAP" + fi + * ConfigEnd + * MODULE-DEFINITION-END + */ + + /* the table of commands we provide */ +static const command_rec command_table[] = { + { "RewriteEngine", cmd_rewriteengine, NULL, OR_FILEINFO, FLAG, + "On or Off to enable or disable (default) the whole rewriting engine" }, + { "RewriteOptions", cmd_rewriteoptions, NULL, OR_FILEINFO, ITERATE, + "List of option strings to set" }, + { "RewriteBase", cmd_rewritebase, NULL, OR_FILEINFO, TAKE1, + "the base URL of the per-directory context" }, + { "RewriteCond", cmd_rewritecond, NULL, OR_FILEINFO, RAW_ARGS, + "an input string and a to be applied regexp-pattern" }, + { "RewriteRule", cmd_rewriterule, NULL, OR_FILEINFO, RAW_ARGS, + "an URL-applied regexp-pattern and a substitution URL" }, + { "RewriteMap", cmd_rewritemap, NULL, RSRC_CONF, TAKE2, + "a mapname and a filename" }, + { "RewriteLock", cmd_rewritelock, NULL, RSRC_CONF, TAKE1, + "the filename of a lockfile used for inter-process synchronization"}, + { "RewriteLog", cmd_rewritelog, NULL, RSRC_CONF, TAKE1, + "the filename of the rewriting logfile" }, + { "RewriteLogLevel", cmd_rewriteloglevel, NULL, RSRC_CONF, TAKE1, + "the level of the rewriting logfile verbosity " + "(0=none, 1=std, .., 9=max)" }, + { NULL } +}; + + /* the table of content handlers we provide */ +static const handler_rec handler_table[] = { + { "redirect-handler", handler_redirect }, + { NULL } +}; + + /* the main config structure */ +module MODULE_VAR_EXPORT rewrite_module = { + STANDARD_MODULE_STUFF, + init_module, /* module initializer */ + config_perdir_create, /* create per-dir config structures */ + config_perdir_merge, /* merge per-dir config structures */ + config_server_create, /* create per-server config structures */ + config_server_merge, /* merge per-server config structures */ + command_table, /* table of config file commands */ + handler_table, /* [#8] MIME-typed-dispatched handlers */ + hook_uri2file, /* [#1] URI to filename translation */ + NULL, /* [#4] validate user id from request */ + NULL, /* [#5] check if the user is ok _here_ */ + NULL, /* [#3] check access by host address */ + hook_mimetype, /* [#6] determine MIME type */ + hook_fixup, /* [#7] pre-run fixups */ + NULL, /* [#9] log a transaction */ + NULL, /* [#2] header parser */ + init_child, /* child_init */ + NULL, /* child_exit */ + NULL /* [#0] post read-request */ +}; + + /* the cache */ +static cache *cachep; + + /* whether proxy module is available or not */ +static int proxy_available; + +static char *lockname; +static int lockfd = -1; + +/* +** +-------------------------------------------------------+ +** | | +** | configuration directive handling +** | | +** +-------------------------------------------------------+ +*/ + +/* +** +** per-server configuration structure handling +** +*/ + +static void *config_server_create(pool *p, server_rec *s) +{ + rewrite_server_conf *a; + + a = (rewrite_server_conf *)ap_pcalloc(p, sizeof(rewrite_server_conf)); + + a->state = ENGINE_DISABLED; + a->options = OPTION_NONE; + a->rewritelogfile = NULL; + a->rewritelogfp = -1; + a->rewriteloglevel = 0; + a->rewritemaps = ap_make_array(p, 2, sizeof(rewritemap_entry)); + a->rewriteconds = ap_make_array(p, 2, sizeof(rewritecond_entry)); + a->rewriterules = ap_make_array(p, 2, sizeof(rewriterule_entry)); + a->server = s; + + return (void *)a; +} + +static void *config_server_merge(pool *p, void *basev, void *overridesv) +{ + rewrite_server_conf *a, *base, *overrides; + + a = (rewrite_server_conf *)ap_pcalloc(p, sizeof(rewrite_server_conf)); + base = (rewrite_server_conf *)basev; + overrides = (rewrite_server_conf *)overridesv; + + a->state = overrides->state; + a->options = overrides->options; + a->server = overrides->server; + + if (a->options & OPTION_INHERIT) { + /* + * local directives override + * and anything else is inherited + */ + a->rewriteloglevel = overrides->rewriteloglevel != 0 + ? overrides->rewriteloglevel + : base->rewriteloglevel; + a->rewritelogfile = overrides->rewritelogfile != NULL + ? overrides->rewritelogfile + : base->rewritelogfile; + a->rewritelogfp = overrides->rewritelogfp != -1 + ? overrides->rewritelogfp + : base->rewritelogfp; + a->rewritemaps = ap_append_arrays(p, overrides->rewritemaps, + base->rewritemaps); + a->rewriteconds = ap_append_arrays(p, overrides->rewriteconds, + base->rewriteconds); + a->rewriterules = ap_append_arrays(p, overrides->rewriterules, + base->rewriterules); + } + else { + /* + * local directives override + * and anything else gets defaults + */ + a->rewriteloglevel = overrides->rewriteloglevel; + a->rewritelogfile = overrides->rewritelogfile; + a->rewritelogfp = overrides->rewritelogfp; + a->rewritemaps = overrides->rewritemaps; + a->rewriteconds = overrides->rewriteconds; + a->rewriterules = overrides->rewriterules; + } + + return (void *)a; +} + + +/* +** +** per-directory configuration structure handling +** +*/ + +static void *config_perdir_create(pool *p, char *path) +{ + rewrite_perdir_conf *a; + + a = (rewrite_perdir_conf *)ap_pcalloc(p, sizeof(rewrite_perdir_conf)); + + a->state = ENGINE_DISABLED; + a->options = OPTION_NONE; + a->baseurl = NULL; + a->rewriteconds = ap_make_array(p, 2, sizeof(rewritecond_entry)); + a->rewriterules = ap_make_array(p, 2, sizeof(rewriterule_entry)); + + if (path == NULL) { + a->directory = NULL; + } + else { + /* make sure it has a trailing slash */ + if (path[strlen(path)-1] == '/') { + a->directory = ap_pstrdup(p, path); + } + else { + a->directory = ap_pstrcat(p, path, "/", NULL); + } + } + + return (void *)a; +} + +static void *config_perdir_merge(pool *p, void *basev, void *overridesv) +{ + rewrite_perdir_conf *a, *base, *overrides; + + a = (rewrite_perdir_conf *)ap_pcalloc(p, + sizeof(rewrite_perdir_conf)); + base = (rewrite_perdir_conf *)basev; + overrides = (rewrite_perdir_conf *)overridesv; + + a->state = overrides->state; + a->options = overrides->options; + a->directory = overrides->directory; + a->baseurl = overrides->baseurl; + + if (a->options & OPTION_INHERIT) { + a->rewriteconds = ap_append_arrays(p, overrides->rewriteconds, + base->rewriteconds); + a->rewriterules = ap_append_arrays(p, overrides->rewriterules, + base->rewriterules); + } + else { + a->rewriteconds = overrides->rewriteconds; + a->rewriterules = overrides->rewriterules; + } + + return (void *)a; +} + + +/* +** +** the configuration commands +** +*/ + +static const char *cmd_rewriteengine(cmd_parms *cmd, + rewrite_perdir_conf *dconf, int flag) +{ + rewrite_server_conf *sconf; + + sconf = + (rewrite_server_conf *)ap_get_module_config(cmd->server->module_config, + &rewrite_module); + + if (cmd->path == NULL) { /* is server command */ + sconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED); + } + else /* is per-directory command */ { + dconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED); + } + + return NULL; +} + +static const char *cmd_rewriteoptions(cmd_parms *cmd, + rewrite_perdir_conf *dconf, char *option) +{ + rewrite_server_conf *sconf; + const char *err; + + sconf = (rewrite_server_conf *) + ap_get_module_config(cmd->server->module_config, &rewrite_module); + + if (cmd->path == NULL) { /* is server command */ + err = cmd_rewriteoptions_setoption(cmd->pool, + &(sconf->options), option); + } + else { /* is per-directory command */ + err = cmd_rewriteoptions_setoption(cmd->pool, + &(dconf->options), option); + } + + return err; +} + +static const char *cmd_rewriteoptions_setoption(pool *p, int *options, + char *name) +{ + if (strcasecmp(name, "inherit") == 0) { + *options |= OPTION_INHERIT; + } + else { + return ap_pstrcat(p, "RewriteOptions: unknown option '", + name, "'\n", NULL); + } + return NULL; +} + +static const char *cmd_rewritelog(cmd_parms *cmd, void *dconf, char *a1) +{ + rewrite_server_conf *sconf; + + sconf = (rewrite_server_conf *) + ap_get_module_config(cmd->server->module_config, &rewrite_module); + + sconf->rewritelogfile = a1; + + return NULL; +} + +static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, char *a1) +{ + rewrite_server_conf *sconf; + + sconf = (rewrite_server_conf *) + ap_get_module_config(cmd->server->module_config, &rewrite_module); + + sconf->rewriteloglevel = atoi(a1); + + return NULL; +} + +static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, char *a1, + char *a2) +{ + rewrite_server_conf *sconf; + rewritemap_entry *new; + struct stat st; + + sconf = (rewrite_server_conf *) + ap_get_module_config(cmd->server->module_config, &rewrite_module); + + new = ap_push_array(sconf->rewritemaps); + + new->name = a1; + new->func = NULL; + if (strncmp(a2, "txt:", 4) == 0) { + new->type = MAPTYPE_TXT; + new->datafile = a2+4; + new->checkfile = a2+4; + } + else if (strncmp(a2, "rnd:", 4) == 0) { + new->type = MAPTYPE_RND; + new->datafile = a2+4; + new->checkfile = a2+4; + } + else if (strncmp(a2, "dbm:", 4) == 0) { +#ifndef NO_DBM_REWRITEMAP + new->type = MAPTYPE_DBM; + new->datafile = a2+4; + new->checkfile = ap_pstrcat(cmd->pool, a2+4, NDBM_FILE_SUFFIX, NULL); +#else + return ap_pstrdup(cmd->pool, "RewriteMap: cannot use NDBM mapfile, " + "because no NDBM support is compiled in"); +#endif + } + else if (strncmp(a2, "prg:", 4) == 0) { + new->type = MAPTYPE_PRG; + new->datafile = a2+4; + new->checkfile = a2+4; + } + else if (strncmp(a2, "int:", 4) == 0) { + new->type = MAPTYPE_INT; + new->datafile = NULL; + new->checkfile = NULL; + if (strcmp(a2+4, "tolower") == 0) { + new->func = rewrite_mapfunc_tolower; + } + else if (strcmp(a2+4, "toupper") == 0) { + new->func = rewrite_mapfunc_toupper; + } + else if (strcmp(a2+4, "escape") == 0) { + new->func = rewrite_mapfunc_escape; + } + else if (strcmp(a2+4, "unescape") == 0) { + new->func = rewrite_mapfunc_unescape; + } + else if (sconf->state == ENGINE_ENABLED) { + return ap_pstrcat(cmd->pool, "RewriteMap: internal map not found:", + a2+4, NULL); + } + } + else { + new->type = MAPTYPE_TXT; + new->datafile = a2; + new->checkfile = a2; + } + new->fpin = -1; + new->fpout = -1; + + if (new->checkfile && (sconf->state == ENGINE_ENABLED) + && (stat(new->checkfile, &st) == -1)) { + return ap_pstrcat(cmd->pool, + "RewriteMap: map file or program not found:", + new->checkfile, NULL); + } + + return NULL; +} + +static const char *cmd_rewritelock(cmd_parms *cmd, void *dconf, char *a1) +{ + const char *error; + + if ((error = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL) + return error; + + lockname = a1; + + return NULL; +} + +static const char *cmd_rewritebase(cmd_parms *cmd, rewrite_perdir_conf *dconf, + char *a1) +{ + if (cmd->path == NULL || dconf == NULL) { + return "RewriteBase: only valid in per-directory config files"; + } + if (a1[0] == '\0') { + return "RewriteBase: empty URL not allowed"; + } + if (a1[0] != '/') { + return "RewriteBase: argument is not a valid URL"; + } + + dconf->baseurl = a1; + + return NULL; +} + +static const char *cmd_rewritecond(cmd_parms *cmd, rewrite_perdir_conf *dconf, + char *str) +{ + rewrite_server_conf *sconf; + rewritecond_entry *new; + regex_t *regexp; + char *a1; + char *a2; + char *a3; + char *cp; + const char *err; + int rc; + + sconf = (rewrite_server_conf *) + ap_get_module_config(cmd->server->module_config, &rewrite_module); + + /* make a new entry in the internal temporary rewrite rule list */ + if (cmd->path == NULL) { /* is server command */ + new = ap_push_array(sconf->rewriteconds); + } + else { /* is per-directory command */ + new = ap_push_array(dconf->rewriteconds); + } + + /* parse the argument line ourself */ + if (parseargline(str, &a1, &a2, &a3)) { + return ap_pstrcat(cmd->pool, "RewriteCond: bad argument line '", str, + "'\n", NULL); + } + + /* arg1: the input string */ + new->input = ap_pstrdup(cmd->pool, a1); + + /* arg3: optional flags field + (this have to be first parsed, because we need to + know if the regex should be compiled with ICASE!) */ + new->flags = CONDFLAG_NONE; + if (a3 != NULL) { + if ((err = cmd_rewritecond_parseflagfield(cmd->pool, new, + a3)) != NULL) { + return err; + } + } + + /* arg2: the pattern + try to compile the regexp to test if is ok */ + cp = a2; + if (cp[0] == '!') { + new->flags |= CONDFLAG_NOTMATCH; + cp++; + } + + /* now be careful: Under the POSIX regex library + we can compile the pattern for case-insensitive matching, + under the old V8 library we have to do it self via a hack */ + if (new->flags & CONDFLAG_NOCASE) { + rc = ((regexp = ap_pregcomp(cmd->pool, cp, REG_EXTENDED|REG_ICASE)) + == NULL); + } + else { + rc = ((regexp = ap_pregcomp(cmd->pool, cp, REG_EXTENDED)) == NULL); + } + if (rc) { + return ap_pstrcat(cmd->pool, + "RewriteCond: cannot compile regular expression '", + a2, "'\n", NULL); + } + + new->pattern = ap_pstrdup(cmd->pool, cp); + new->regexp = regexp; + + return NULL; +} + +static const char *cmd_rewritecond_parseflagfield(pool *p, + rewritecond_entry *cfg, + char *str) +{ + char *cp; + char *cp1; + char *cp2; + char *cp3; + char *key; + char *val; + const char *err; + + if (str[0] != '[' || str[strlen(str)-1] != ']') { + return "RewriteCond: bad flag delimiters"; + } + + cp = str+1; + str[strlen(str)-1] = ','; /* for simpler parsing */ + for ( ; *cp != '\0'; ) { + /* skip whitespaces */ + for ( ; (*cp == ' ' || *cp == '\t') && *cp != '\0'; cp++) + ; + if (*cp == '\0') { + break; + } + cp1 = cp; + if ((cp2 = strchr(cp, ',')) != NULL) { + cp = cp2+1; + for ( ; (*(cp2-1) == ' ' || *(cp2-1) == '\t'); cp2--) + ; + *cp2 = '\0'; + if ((cp3 = strchr(cp1, '=')) != NULL) { + *cp3 = '\0'; + key = cp1; + val = cp3+1; + } + else { + key = cp1; + val = ""; + } + if ((err = cmd_rewritecond_setflag(p, cfg, key, val)) != NULL) { + return err; + } + } + else { + break; + } + } + + return NULL; +} + +static const char *cmd_rewritecond_setflag(pool *p, rewritecond_entry *cfg, + char *key, char *val) +{ + if ( strcasecmp(key, "nocase") == 0 + || strcasecmp(key, "NC") == 0 ) { + cfg->flags |= CONDFLAG_NOCASE; + } + else if ( strcasecmp(key, "ornext") == 0 + || strcasecmp(key, "OR") == 0 ) { + cfg->flags |= CONDFLAG_ORNEXT; + } + else { + return ap_pstrcat(p, "RewriteCond: unknown flag '", key, "'\n", NULL); + } + return NULL; +} + +static const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf, + char *str) +{ + rewrite_server_conf *sconf; + rewriterule_entry *new; + regex_t *regexp; + char *a1; + char *a2; + char *a3; + char *cp; + const char *err; + int mode; + + sconf = (rewrite_server_conf *) + ap_get_module_config(cmd->server->module_config, &rewrite_module); + + /* make a new entry in the internal rewrite rule list */ + if (cmd->path == NULL) { /* is server command */ + new = ap_push_array(sconf->rewriterules); + } + else { /* is per-directory command */ + new = ap_push_array(dconf->rewriterules); + } + + /* parse the argument line ourself */ + if (parseargline(str, &a1, &a2, &a3)) { + return ap_pstrcat(cmd->pool, "RewriteRule: bad argument line '", str, + "'\n", NULL); + } + + /* arg3: optional flags field */ + new->forced_mimetype = NULL; + new->forced_responsecode = HTTP_MOVED_TEMPORARILY; + new->flags = RULEFLAG_NONE; + new->env[0] = NULL; + new->skip = 0; + if (a3 != NULL) { + if ((err = cmd_rewriterule_parseflagfield(cmd->pool, new, + a3)) != NULL) { + return err; + } + } + + /* arg1: the pattern + * try to compile the regexp to test if is ok + */ + cp = a1; + if (cp[0] == '!') { + new->flags |= RULEFLAG_NOTMATCH; + cp++; + } + mode = REG_EXTENDED; + if (new->flags & RULEFLAG_NOCASE) { + mode |= REG_ICASE; + } + if ((regexp = ap_pregcomp(cmd->pool, cp, mode)) == NULL) { + return ap_pstrcat(cmd->pool, + "RewriteRule: cannot compile regular expression '", + a1, "'\n", NULL); + } + new->pattern = ap_pstrdup(cmd->pool, cp); + new->regexp = regexp; + + /* arg2: the output string + * replace the $ by \ which is needed by the currently + * used Regular Expression library + */ + new->output = ap_pstrdup(cmd->pool, a2); + + /* now, if the server or per-dir config holds an + * array of RewriteCond entries, we take it for us + * and clear the array + */ + if (cmd->path == NULL) { /* is server command */ + new->rewriteconds = sconf->rewriteconds; + sconf->rewriteconds = ap_make_array(cmd->pool, 2, + sizeof(rewritecond_entry)); + } + else { /* is per-directory command */ + new->rewriteconds = dconf->rewriteconds; + dconf->rewriteconds = ap_make_array(cmd->pool, 2, + sizeof(rewritecond_entry)); + } + + return NULL; +} + +static const char *cmd_rewriterule_parseflagfield(pool *p, + rewriterule_entry *cfg, + char *str) +{ + char *cp; + char *cp1; + char *cp2; + char *cp3; + char *key; + char *val; + const char *err; + + if (str[0] != '[' || str[strlen(str)-1] != ']') { + return "RewriteRule: bad flag delimiters"; + } + + cp = str+1; + str[strlen(str)-1] = ','; /* for simpler parsing */ + for ( ; *cp != '\0'; ) { + /* skip whitespaces */ + for ( ; (*cp == ' ' || *cp == '\t') && *cp != '\0'; cp++) + ; + if (*cp == '\0') { + break; + } + cp1 = cp; + if ((cp2 = strchr(cp, ',')) != NULL) { + cp = cp2+1; + for ( ; (*(cp2-1) == ' ' || *(cp2-1) == '\t'); cp2--) + ; + *cp2 = '\0'; + if ((cp3 = strchr(cp1, '=')) != NULL) { + *cp3 = '\0'; + key = cp1; + val = cp3+1; + } + else { + key = cp1; + val = ""; + } + if ((err = cmd_rewriterule_setflag(p, cfg, key, val)) != NULL) { + return err; + } + } + else { + break; + } + } + + return NULL; +} + +static const char *cmd_rewriterule_setflag(pool *p, rewriterule_entry *cfg, + char *key, char *val) +{ + int status = 0; + int i; + + if ( strcasecmp(key, "redirect") == 0 + || strcasecmp(key, "R") == 0 ) { + cfg->flags |= RULEFLAG_FORCEREDIRECT; + if (strlen(val) > 0) { + if (strcasecmp(val, "permanent") == 0) { + status = HTTP_MOVED_PERMANENTLY; + } + else if (strcasecmp(val, "temp") == 0) { + status = HTTP_MOVED_TEMPORARILY; + } + else if (strcasecmp(val, "seeother") == 0) { + status = HTTP_SEE_OTHER; + } + else if (ap_isdigit(*val)) { + status = atoi(val); + } + if (!ap_is_HTTP_REDIRECT(status)) { + return "RewriteRule: invalid HTTP response code " + "for flag 'R'"; + } + cfg->forced_responsecode = status; + } + } + else if ( strcasecmp(key, "last") == 0 + || strcasecmp(key, "L") == 0 ) { + cfg->flags |= RULEFLAG_LASTRULE; + } + else if ( strcasecmp(key, "next") == 0 + || strcasecmp(key, "N") == 0 ) { + cfg->flags |= RULEFLAG_NEWROUND; + } + else if ( strcasecmp(key, "chain") == 0 + || strcasecmp(key, "C") == 0 ) { + cfg->flags |= RULEFLAG_CHAIN; + } + else if ( strcasecmp(key, "type") == 0 + || strcasecmp(key, "T") == 0 ) { + cfg->forced_mimetype = ap_pstrdup(p, val); + ap_str_tolower(cfg->forced_mimetype); + } + else if ( strcasecmp(key, "env") == 0 + || strcasecmp(key, "E") == 0 ) { + for (i = 0; (cfg->env[i] != NULL) && (i < MAX_ENV_FLAGS); i++) + ; + if (i < MAX_ENV_FLAGS) { + cfg->env[i] = ap_pstrdup(p, val); + cfg->env[i+1] = NULL; + } + else { + return "RewriteRule: too many environment flags 'E'"; + } + } + else if ( strcasecmp(key, "nosubreq") == 0 + || strcasecmp(key, "NS") == 0 ) { + cfg->flags |= RULEFLAG_IGNOREONSUBREQ; + } + else if ( strcasecmp(key, "proxy") == 0 + || strcasecmp(key, "P") == 0 ) { + cfg->flags |= RULEFLAG_PROXY; + } + else if ( strcasecmp(key, "passthrough") == 0 + || strcasecmp(key, "PT") == 0 ) { + cfg->flags |= RULEFLAG_PASSTHROUGH; + } + else if ( strcasecmp(key, "skip") == 0 + || strcasecmp(key, "S") == 0 ) { + cfg->skip = atoi(val); + } + else if ( strcasecmp(key, "forbidden") == 0 + || strcasecmp(key, "F") == 0 ) { + cfg->flags |= RULEFLAG_FORBIDDEN; + } + else if ( strcasecmp(key, "gone") == 0 + || strcasecmp(key, "G") == 0 ) { + cfg->flags |= RULEFLAG_GONE; + } + else if ( strcasecmp(key, "qsappend") == 0 + || strcasecmp(key, "QSA") == 0 ) { + cfg->flags |= RULEFLAG_QSAPPEND; + } + else if ( strcasecmp(key, "nocase") == 0 + || strcasecmp(key, "NC") == 0 ) { + cfg->flags |= RULEFLAG_NOCASE; + } + else { + return ap_pstrcat(p, "RewriteRule: unknown flag '", key, "'\n", NULL); + } + return NULL; +} + + +/* +** +** Global Module Initialization +** [called from read_config() after all +** config commands were already called] +** +*/ + +static void init_module(server_rec *s, pool *p) +{ + /* check if proxy module is available */ + proxy_available = (ap_find_linked_module("mod_proxy.c") != NULL); + + /* create the rewriting lockfile in the parent */ + rewritelock_create(s, p); + ap_register_cleanup(p, (void *)s, rewritelock_remove, ap_null_cleanup); + + /* step through the servers and + * - open each rewriting logfile + * - open the RewriteMap prg:xxx programs + */ + for (; s; s = s->next) { + open_rewritelog(s, p); + run_rewritemap_programs(s, p); + } +} + + +/* +** +** Per-Child Module Initialization +** [called after a child process is spawned] +** +*/ + +static void init_child(server_rec *s, pool *p) +{ + /* open the rewriting lockfile */ + rewritelock_open(s, p); + + /* create the lookup cache */ + cachep = init_cache(p); +} + + +/* +** +-------------------------------------------------------+ +** | | +** | runtime hooks +** | | +** +-------------------------------------------------------+ +*/ + +/* +** +** URI-to-filename hook +** +** [used for the rewriting engine triggered by +** the per-server 'RewriteRule' directives] +** +*/ + +static int hook_uri2file(request_rec *r) +{ + void *sconf; + rewrite_server_conf *conf; + const char *var; + const char *thisserver; + char *thisport; + const char *thisurl; + char buf[512]; + char docroot[512]; + char *cp, *cp2; + const char *ccp; + struct stat finfo; + unsigned int port; + int n; + int l; + + /* + * retrieve the config structures + */ + sconf = r->server->module_config; + conf = (rewrite_server_conf *)ap_get_module_config(sconf, + &rewrite_module); + + /* + * only do something under runtime if the engine is really enabled, + * else return immediately! + */ + if (conf->state == ENGINE_DISABLED) { + return DECLINED; + } + + /* + * check for the ugly API case of a virtual host section where no + * mod_rewrite directives exists. In this situation we became no chance + * by the API to setup our default per-server config so we have to + * on-the-fly assume we have the default config. But because the default + * config has a disabled rewriting engine we are lucky because can + * just stop operating now. + */ + if (conf->server != r->server) { + return DECLINED; + } + + /* + * add the SCRIPT_URL variable to the env. this is a bit complicated + * due to the fact that apache uses subrequests and internal redirects + */ + + if (r->main == NULL) { + var = ap_pstrcat(r->pool, "REDIRECT_", ENVVAR_SCRIPT_URL, NULL); + var = ap_table_get(r->subprocess_env, var); + if (var == NULL) { + ap_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, r->uri); + } + else { + ap_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var); + } + } + else { + var = ap_table_get(r->main->subprocess_env, ENVVAR_SCRIPT_URL); + ap_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var); + } + + /* + * create the SCRIPT_URI variable for the env + */ + + /* add the canonical URI of this URL */ + thisserver = ap_get_server_name(r); + port = ap_get_server_port(r); + if (ap_is_default_port(port, r)) { + thisport = ""; + } + else { + ap_snprintf(buf, sizeof(buf), ":%u", port); + thisport = buf; + } + thisurl = ap_table_get(r->subprocess_env, ENVVAR_SCRIPT_URL); + + /* set the variable */ + var = ap_pstrcat(r->pool, ap_http_method(r), "://", thisserver, thisport, + thisurl, NULL); + ap_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URI, var); + + /* if filename was not initially set, + * we start with the requested URI + */ + if (r->filename == NULL) { + r->filename = ap_pstrdup(r->pool, r->uri); + rewritelog(r, 2, "init rewrite engine with requested uri %s", + r->filename); + } + + /* + * now apply the rules ... + */ + if (apply_rewrite_list(r, conf->rewriterules, NULL)) { + + if (strlen(r->filename) > 6 && + strncmp(r->filename, "proxy:", 6) == 0) { + /* it should be go on as an internal proxy request */ + + /* check if the proxy module is enabled, so + * we can actually use it! + */ + if (!proxy_available) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "attempt to make remote request from mod_rewrite " + "without proxy enabled: %s", r->filename); + return FORBIDDEN; + } + + /* make sure the QUERY_STRING and + * PATH_INFO parts get incorporated + */ + if (r->path_info != NULL) { + r->filename = ap_pstrcat(r->pool, r->filename, + r->path_info, NULL); + } + if (r->args != NULL && + r->uri == r->unparsed_uri) { + /* see proxy_http:proxy_http_canon() */ + r->filename = ap_pstrcat(r->pool, r->filename, + "?", r->args, NULL); + } + + /* now make sure the request gets handled by the proxy handler */ + r->proxyreq = 1; + r->handler = "proxy-server"; + + rewritelog(r, 1, "go-ahead with proxy request %s [OK]", + r->filename); + return OK; + } + else if ( (strlen(r->filename) > 7 && + strncasecmp(r->filename, "http://", 7) == 0) + || (strlen(r->filename) > 8 && + strncasecmp(r->filename, "https://", 8) == 0) + || (strlen(r->filename) > 9 && + strncasecmp(r->filename, "gopher://", 9) == 0) + || (strlen(r->filename) > 6 && + strncasecmp(r->filename, "ftp://", 6) == 0) ) { + /* it was finally rewritten to a remote URL */ + + /* skip 'scheme:' */ + for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++) + ; + /* skip '://' */ + cp += 3; + /* skip host part */ + for ( ; *cp != '/' && *cp != '\0'; cp++) + ; + if (*cp != '\0') { + rewritelog(r, 1, "escaping %s for redirect", r->filename); + cp2 = ap_escape_uri(r->pool, cp); + *cp = '\0'; + r->filename = ap_pstrcat(r->pool, r->filename, cp2, NULL); + } + + /* append the QUERY_STRING part */ + if (r->args != NULL) { + r->filename = ap_pstrcat(r->pool, r->filename, + "?", r->args, NULL); + } + + /* determine HTTP redirect response code */ + if (ap_is_HTTP_REDIRECT(r->status)) { + n = r->status; + r->status = HTTP_OK; /* make Apache kernel happy */ + } + else { + n = REDIRECT; + } + + /* now do the redirection */ + ap_table_setn(r->headers_out, "Location", r->filename); + rewritelog(r, 1, "redirect to %s [REDIRECT/%d]", r->filename, n); + return n; + } + else if (strlen(r->filename) > 10 && + strncmp(r->filename, "forbidden:", 10) == 0) { + /* This URLs is forced to be forbidden for the requester */ + return FORBIDDEN; + } + else if (strlen(r->filename) > 5 && + strncmp(r->filename, "gone:", 5) == 0) { + /* This URLs is forced to be gone */ + return HTTP_GONE; + } + else if (strlen(r->filename) > 12 && + strncmp(r->filename, "passthrough:", 12) == 0) { + /* + * Hack because of underpowered API: passing the current + * rewritten filename through to other URL-to-filename handlers + * just as it were the requested URL. This is to enable + * post-processing by mod_alias, etc. which always act on + * r->uri! The difference here is: We do not try to + * add the document root + */ + r->uri = ap_pstrdup(r->pool, r->filename+12); + return DECLINED; + } + else { + /* it was finally rewritten to a local path */ + + /* expand "/~user" prefix */ +#ifndef WIN32 + r->filename = expand_tildepaths(r, r->filename); +#endif + rewritelog(r, 2, "local path result: %s", r->filename); + + /* the filename has to start with a slash! */ + if (r->filename[0] != '/') { + return BAD_REQUEST; + } + + /* if there is no valid prefix, we have + * to emulate the translator from the core and + * prefix the filename with document_root + * + * NOTICE: + * We cannot leave out the prefix_stat because + * - when we always prefix with document_root + * then no absolute path can be created, e.g. via + * emulating a ScriptAlias directive, etc. + * - when we always NOT prefix with document_root + * then the files under document_root have to + * be references directly and document_root + * gets never used and will be a dummy parameter - + * this is also bad + * + * BUT: + * Under real Unix systems this is no problem, + * because we only do stat() on the first directory + * and this gets cached by the kernel for along time! + */ + n = prefix_stat(r->filename, &finfo); + if (n == 0) { + if ((ccp = ap_document_root(r)) != NULL) { + l = ap_cpystrn(docroot, ccp, sizeof(docroot)) - docroot; + + /* always NOT have a trailing slash */ + if (docroot[l-1] == '/') { + docroot[l-1] = '\0'; + } + if (r->server->path + && !strncmp(r->filename, r->server->path, + r->server->pathlen)) { + r->filename = ap_pstrcat(r->pool, docroot, + (r->filename + + r->server->pathlen), NULL); + } + else { + r->filename = ap_pstrcat(r->pool, docroot, + r->filename, NULL); + } + rewritelog(r, 2, "prefixed with document_root to %s", + r->filename); + } + } + + rewritelog(r, 1, "go-ahead with %s [OK]", r->filename); + return OK; + } + } + else { + rewritelog(r, 1, "pass through %s", r->filename); + return DECLINED; + } +} + + +/* +** +** MIME-type hook +** +** [used to support the forced-MIME-type feature] +** +*/ + +static int hook_mimetype(request_rec *r) +{ + const char *t; + + /* now check if we have to force a MIME-type */ + t = ap_table_get(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR); + if (t == NULL) { + return DECLINED; + } + else { + rewritelog(r, 1, "force filename %s to have MIME-type '%s'", + r->filename, t); + r->content_type = t; + return OK; + } +} + + +/* +** +** Fixup hook +** +** [used for the rewriting engine triggered by +** the per-directory 'RewriteRule' directives] +** +*/ + +static int hook_fixup(request_rec *r) +{ + rewrite_perdir_conf *dconf; + char *cp; + char *cp2; + const char *ccp; + char *prefix; + int l; + int n; + char *ofilename; + + dconf = (rewrite_perdir_conf *)ap_get_module_config(r->per_dir_config, + &rewrite_module); + + /* if there is no per-dir config we return immediately */ + if (dconf == NULL) { + return DECLINED; + } + + /* we shouldn't do anything in subrequests */ + if (r->main != NULL) { + return DECLINED; + } + + /* if there are no real (i.e. no RewriteRule directives!) + per-dir config of us, we return also immediately */ + if (dconf->directory == NULL) { + return DECLINED; + } + + /* + * only do something under runtime if the engine is really enabled, + * for this directory, else return immediately! + */ + if (!(ap_allow_options(r) & (OPT_SYM_LINKS | OPT_SYM_OWNER))) { + /* FollowSymLinks is mandatory! */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Options FollowSymLinks or SymLinksIfOwnerMatch is off " + "which implies that RewriteRule directive is forbidden: " + "%s", r->filename); + return FORBIDDEN; + } + else { + /* FollowSymLinks is given, but the user can + * still turn off the rewriting engine + */ + if (dconf->state == ENGINE_DISABLED) { + return DECLINED; + } + } + + /* + * remember the current filename before rewriting for later check + * to prevent deadlooping because of internal redirects + * on final URL/filename which can be equal to the inital one. + */ + ofilename = r->filename; + + /* + * now apply the rules ... + */ + if (apply_rewrite_list(r, dconf->rewriterules, dconf->directory)) { + + if (strlen(r->filename) > 6 && + strncmp(r->filename, "proxy:", 6) == 0) { + /* it should go on as an internal proxy request */ + + /* make sure the QUERY_STRING and + * PATH_INFO parts get incorporated + * (r->path_info was already appended by the + * rewriting engine because of the per-dir context!) + */ + if (r->args != NULL + && r->uri == r->unparsed_uri) { + /* see proxy_http:proxy_http_canon() */ + r->filename = ap_pstrcat(r->pool, r->filename, + "?", r->args, NULL); + } + + /* now make sure the request gets handled by the proxy handler */ + r->proxyreq = 1; + r->handler = "proxy-server"; + + rewritelog(r, 1, "[per-dir %s] go-ahead with proxy request " + "%s [OK]", dconf->directory, r->filename); + return OK; + } + else if ( (strlen(r->filename) > 7 && + strncmp(r->filename, "http://", 7) == 0) + || (strlen(r->filename) > 8 && + strncmp(r->filename, "https://", 8) == 0) + || (strlen(r->filename) > 9 && + strncmp(r->filename, "gopher://", 9) == 0) + || (strlen(r->filename) > 6 && + strncmp(r->filename, "ftp://", 6) == 0) ) { + /* it was finally rewritten to a remote URL */ + + /* because we are in a per-dir context + * first try to replace the directory with its base-URL + * if there is a base-URL available + */ + if (dconf->baseurl != NULL) { + /* skip 'scheme:' */ + for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++) + ; + /* skip '://' */ + cp += 3; + if ((cp = strchr(cp, '/')) != NULL) { + rewritelog(r, 2, + "[per-dir %s] trying to replace " + "prefix %s with %s", + dconf->directory, dconf->directory, + dconf->baseurl); + cp2 = subst_prefix_path(r, cp, dconf->directory, + dconf->baseurl); + if (strcmp(cp2, cp) != 0) { + *cp = '\0'; + r->filename = ap_pstrcat(r->pool, r->filename, + cp2, NULL); + } + } + } + + /* now prepare the redirect... */ + + /* skip 'scheme:' */ + for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++) + ; + /* skip '://' */ + cp += 3; + /* skip host part */ + for ( ; *cp != '/' && *cp != '\0'; cp++) + ; + if (*cp != '\0') { + rewritelog(r, 1, "[per-dir %s] escaping %s for redirect", + dconf->directory, r->filename); + cp2 = ap_escape_uri(r->pool, cp); + *cp = '\0'; + r->filename = ap_pstrcat(r->pool, r->filename, cp2, NULL); + } + + /* append the QUERY_STRING part */ + if (r->args != NULL) { + r->filename = ap_pstrcat(r->pool, r->filename, + "?", r->args, NULL); + } + + /* determine HTTP redirect response code */ + if (ap_is_HTTP_REDIRECT(r->status)) { + n = r->status; + r->status = HTTP_OK; /* make Apache kernel happy */ + } + else { + n = REDIRECT; + } + + /* now do the redirection */ + ap_table_setn(r->headers_out, "Location", r->filename); + rewritelog(r, 1, "[per-dir %s] redirect to %s [REDIRECT/%d]", + dconf->directory, r->filename, n); + return n; + } + else if (strlen(r->filename) > 10 && + strncmp(r->filename, "forbidden:", 10) == 0) { + /* This URL is forced to be forbidden for the requester */ + return FORBIDDEN; + } + else if (strlen(r->filename) > 5 && + strncmp(r->filename, "gone:", 5) == 0) { + /* This URL is forced to be gone */ + return HTTP_GONE; + } + else { + /* it was finally rewritten to a local path */ + + /* if someone used the PASSTHROUGH flag in per-dir + * context we just ignore it. It is only useful + * in per-server context + */ + if (strlen(r->filename) > 12 && + strncmp(r->filename, "passthrough:", 12) == 0) { + r->filename = ap_pstrdup(r->pool, r->filename+12); + } + + /* the filename has to start with a slash! */ + if (r->filename[0] != '/') { + return BAD_REQUEST; + } + + /* Check for deadlooping: + * At this point we KNOW that at least one rewriting + * rule was applied, but when the resulting URL is + * the same as the initial URL, we are not allowed to + * use the following internal redirection stuff because + * this would lead to a deadloop. + */ + if (strcmp(r->filename, ofilename) == 0) { + rewritelog(r, 1, "[per-dir %s] initial URL equal rewritten " + "URL: %s [IGNORING REWRITE]", + dconf->directory, r->filename); + return OK; + } + + /* if there is a valid base-URL then substitute + * the per-dir prefix with this base-URL if the + * current filename still is inside this per-dir + * context. If not then treat the result as a + * plain URL + */ + if (dconf->baseurl != NULL) { + rewritelog(r, 2, + "[per-dir %s] trying to replace prefix %s with %s", + dconf->directory, dconf->directory, dconf->baseurl); + r->filename = subst_prefix_path(r, r->filename, + dconf->directory, + dconf->baseurl); + } + else { + /* if no explicit base-URL exists we assume + * that the directory prefix is also a valid URL + * for this webserver and only try to remove the + * document_root if it is prefix + */ + if ((ccp = ap_document_root(r)) != NULL) { + prefix = ap_pstrdup(r->pool, ccp); + /* always NOT have a trailing slash */ + l = strlen(prefix); + if (prefix[l-1] == '/') { + prefix[l-1] = '\0'; + l--; + } + if (strncmp(r->filename, prefix, l) == 0) { + rewritelog(r, 2, + "[per-dir %s] strip document_root " + "prefix: %s -> %s", + dconf->directory, r->filename, + r->filename+l); + r->filename = ap_pstrdup(r->pool, r->filename+l); + } + } + } + + /* now initiate the internal redirect */ + rewritelog(r, 1, "[per-dir %s] internal redirect with %s " + "[INTERNAL REDIRECT]", dconf->directory, r->filename); + r->filename = ap_pstrcat(r->pool, "redirect:", r->filename, NULL); + r->handler = "redirect-handler"; + return OK; + } + } + else { + rewritelog(r, 1, "[per-dir %s] pass through %s", + dconf->directory, r->filename); + return DECLINED; + } +} + + +/* +** +** Content-Handlers +** +** [used for redirect support] +** +*/ + +static int handler_redirect(request_rec *r) +{ + /* just make sure that we are really meant! */ + if (strncmp(r->filename, "redirect:", 9) != 0) { + return DECLINED; + } + + /* now do the internal redirect */ + ap_internal_redirect(ap_pstrcat(r->pool, r->filename+9, + r->args ? "?" : NULL, r->args, NULL), r); + + /* and return gracefully */ + return OK; +} + + +/* +** +-------------------------------------------------------+ +** | | +** | the rewriting engine +** | | +** +-------------------------------------------------------+ +*/ + +/* + * Apply a complete rule set, + * i.e. a list of rewrite rules + */ +static int apply_rewrite_list(request_rec *r, array_header *rewriterules, + char *perdir) +{ + rewriterule_entry *entries; + rewriterule_entry *p; + int i; + int changed; + int rc; + int s; + + /* + * Iterate over all existing rules + */ + entries = (rewriterule_entry *)rewriterules->elts; + changed = 0; + loop: + for (i = 0; i < rewriterules->nelts; i++) { + p = &entries[i]; + + /* + * Ignore this rule on subrequests if we are explicitly + * asked to do so or this is a proxy-throughput or a + * forced redirect rule. + */ + if (r->main != NULL && + (p->flags & RULEFLAG_IGNOREONSUBREQ || + p->flags & RULEFLAG_PROXY || + p->flags & RULEFLAG_FORCEREDIRECT )) { + continue; + } + + /* + * Apply the current rule. + */ + rc = apply_rewrite_rule(r, p, perdir); + if (rc) { + /* + * Indicate a change if this was not a match-only rule. + */ + if (rc != 2) { + changed = 1; + } + + /* + * Pass-Through Feature (`RewriteRule .. .. [PT]'): + * Because the Apache 1.x API is very limited we + * need this hack to pass the rewritten URL to other + * modules like mod_alias, mod_userdir, etc. + */ + if (p->flags & RULEFLAG_PASSTHROUGH) { + rewritelog(r, 2, "forcing '%s' to get passed through " + "to next API URI-to-filename handler", r->filename); + r->filename = ap_pstrcat(r->pool, "passthrough:", + r->filename, NULL); + changed = 1; + break; + } + + /* + * Rule has the "forbidden" flag set which means that + * we stop processing and indicate this to the caller. + */ + if (p->flags & RULEFLAG_FORBIDDEN) { + rewritelog(r, 2, "forcing '%s' to be forbidden", r->filename); + r->filename = ap_pstrcat(r->pool, "forbidden:", + r->filename, NULL); + changed = 1; + break; + } + + /* + * Rule has the "gone" flag set which means that + * we stop processing and indicate this to the caller. + */ + if (p->flags & RULEFLAG_GONE) { + rewritelog(r, 2, "forcing '%s' to be gone", r->filename); + r->filename = ap_pstrcat(r->pool, "gone:", r->filename, NULL); + changed = 1; + break; + } + + /* + * Stop processing also on proxy pass-through and + * last-rule and new-round flags. + */ + if (p->flags & RULEFLAG_PROXY) { + break; + } + if (p->flags & RULEFLAG_LASTRULE) { + break; + } + + /* + * On "new-round" flag we just start from the top of + * the rewriting ruleset again. + */ + if (p->flags & RULEFLAG_NEWROUND) { + goto loop; + } + + /* + * If we are forced to skip N next rules, do it now. + */ + if (p->skip > 0) { + s = p->skip; + while ( i < rewriterules->nelts + && s > 0) { + i++; + p = &entries[i]; + s--; + } + } + } + else { + /* + * If current rule is chained with next rule(s), + * skip all this next rule(s) + */ + while ( i < rewriterules->nelts + && p->flags & RULEFLAG_CHAIN) { + i++; + p = &entries[i]; + } + } + } + return changed; +} + +/* + * Apply a single(!) rewrite rule + */ +static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p, + char *perdir) +{ + char *uri; + char *output; + const char *vary; + char newuri[MAX_STRING_LEN]; + char env[MAX_STRING_LEN]; + regex_t *regexp; + regmatch_t regmatch[MAX_NMATCH]; + backrefinfo *briRR = NULL; + backrefinfo *briRC = NULL; + int prefixstrip; + int failed; + array_header *rewriteconds; + rewritecond_entry *conds; + rewritecond_entry *c; + int i; + int rc; + + /* + * Initialisation + */ + uri = r->filename; + regexp = p->regexp; + output = p->output; + + /* + * Add (perhaps splitted away) PATH_INFO postfix to URL to + * make sure we really match against the complete URL. + */ + if (perdir != NULL && r->path_info != NULL && r->path_info[0] != '\0') { + rewritelog(r, 3, "[per-dir %s] add path-info postfix: %s -> %s%s", + perdir, uri, uri, r->path_info); + uri = ap_pstrcat(r->pool, uri, r->path_info, NULL); + } + + /* + * On per-directory context (.htaccess) strip the location + * prefix from the URL to make sure patterns apply only to + * the local part. Additionally indicate this special + * threatment in the logfile. + */ + prefixstrip = 0; + if (perdir != NULL) { + if ( strlen(uri) >= strlen(perdir) + && strncmp(uri, perdir, strlen(perdir)) == 0) { + rewritelog(r, 3, "[per-dir %s] strip per-dir prefix: %s -> %s", + perdir, uri, uri+strlen(perdir)); + uri = uri+strlen(perdir); + prefixstrip = 1; + } + } + + /* + * Try to match the URI against the RewriteRule pattern + * and exit immeddiately if it didn't apply. + */ + if (perdir == NULL) { + rewritelog(r, 3, "applying pattern '%s' to uri '%s'", + p->pattern, uri); + } + else { + rewritelog(r, 3, "[per-dir %s] applying pattern '%s' to uri '%s'", + perdir, p->pattern, uri); + } + rc = (ap_regexec(regexp, uri, regexp->re_nsub+1, regmatch, 0) == 0); + if (! (( rc && !(p->flags & RULEFLAG_NOTMATCH)) || + (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) ) { + return 0; + } + + /* + * Else create the RewriteRule `regsubinfo' structure which + * holds the substitution information. + */ + briRR = (backrefinfo *)ap_palloc(r->pool, sizeof(backrefinfo)); + if (!rc && (p->flags & RULEFLAG_NOTMATCH)) { + /* empty info on negative patterns */ + briRR->source = ""; + briRR->nsub = 0; + } + else { + briRR->source = ap_pstrdup(r->pool, uri); + briRR->nsub = regexp->re_nsub; + memcpy((void *)(briRR->regmatch), (void *)(regmatch), + sizeof(regmatch)); + } + + /* + * Initiallally create the RewriteCond backrefinfo with + * empty backrefinfo, i.e. not subst parts + * (this one is adjusted inside apply_rewrite_cond() later!!) + */ + briRC = (backrefinfo *)ap_pcalloc(r->pool, sizeof(backrefinfo)); + briRC->source = ""; + briRC->nsub = 0; + + /* + * Ok, we already know the pattern has matched, but we now + * additionally have to check for all existing preconditions + * (RewriteCond) which have to be also true. We do this at + * this very late stage to avoid unnessesary checks which + * would slow down the rewriting engine!! + */ + rewriteconds = p->rewriteconds; + conds = (rewritecond_entry *)rewriteconds->elts; + failed = 0; + for (i = 0; i < rewriteconds->nelts; i++) { + c = &conds[i]; + rc = apply_rewrite_cond(r, c, perdir, briRR, briRC); + if (c->flags & CONDFLAG_ORNEXT) { + /* + * The "OR" case + */ + if (rc == 0) { + /* One condition is false, but another can be + * still true, so we have to continue... + */ + ap_table_unset(r->notes, VARY_KEY_THIS); + continue; + } + else { + /* One true condition is enough in "or" case, so + * skip the other conditions which are "ornext" + * chained + */ + while ( i < rewriteconds->nelts + && c->flags & CONDFLAG_ORNEXT) { + i++; + c = &conds[i]; + } + continue; + } + } + else { + /* + * The "AND" case, i.e. no "or" flag, + * so a single failure means total failure. + */ + if (rc == 0) { + failed = 1; + break; + } + } + vary = ap_table_get(r->notes, VARY_KEY_THIS); + if (vary != NULL) { + ap_table_merge(r->notes, VARY_KEY, vary); + ap_table_unset(r->notes, VARY_KEY_THIS); + } + } + /* if any condition fails the complete rule fails */ + if (failed) { + ap_table_unset(r->notes, VARY_KEY); + ap_table_unset(r->notes, VARY_KEY_THIS); + return 0; + } + + /* + * Regardless of what we do next, we've found a match. Check to see + * if any of the request header fields were involved, and add them + * to the Vary field of the response. + */ + if ((vary = ap_table_get(r->notes, VARY_KEY)) != NULL) { + ap_table_merge(r->headers_out, "Vary", vary); + ap_table_unset(r->notes, VARY_KEY); + } + + /* + * If this is a pure matching rule (`RewriteRule -') + * we stop processing and return immediately. The only thing + * we have not to forget are the environment variables + * (`RewriteRule - [E=...]') + */ + if (strcmp(output, "-") == 0) { + for (i = 0; p->env[i] != NULL; i++) { + /* 1. take the string */ + ap_cpystrn(env, p->env[i], sizeof(env)); + /* 2. expand $N (i.e. backrefs to RewriteRule pattern) */ + expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$'); + /* 3. expand %N (i.e. backrefs to latest RewriteCond pattern) */ + expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%'); + /* 4. expand %{...} (i.e. variables) */ + expand_variables_inbuffer(r, env, sizeof(env)); + /* 5. expand ${...} (RewriteMap lookups) */ + expand_map_lookups(r, env, sizeof(env)); + /* and add the variable to Apache's structures */ + add_env_variable(r, env); + } + if (p->forced_mimetype != NULL) { + if (perdir == NULL) { + /* In the per-server context we can force the MIME-type + * the correct way by notifying our MIME-type hook handler + * to do the job when the MIME-type API stage is reached. + */ + rewritelog(r, 2, "remember %s to have MIME-type '%s'", + r->filename, p->forced_mimetype); + ap_table_setn(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR, + p->forced_mimetype); + } + else { + /* In per-directory context we operate in the Fixup API hook + * which is after the MIME-type hook, so our MIME-type handler + * has no chance to set r->content_type. And because we are + * in the situation where no substitution takes place no + * sub-request will happen (which could solve the + * restriction). As a workaround we do it ourself now + * immediately although this is not strictly API-conforming. + * But it's the only chance we have... + */ + rewritelog(r, 1, "[per-dir %s] force %s to have MIME-type " + "'%s'", perdir, r->filename, p->forced_mimetype); + r->content_type = p->forced_mimetype; + } + } + return 2; + } + + /* + * Ok, now we finally know all patterns have matched and + * that there is something to replace, so we create the + * substitution URL string in `newuri'. + */ + /* 1. take the output string */ + ap_cpystrn(newuri, output, sizeof(newuri)); + /* 2. expand $N (i.e. backrefs to RewriteRule pattern) */ + expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR, '$'); + /* 3. expand %N (i.e. backrefs to latest RewriteCond pattern) */ + expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC, '%'); + /* 4. expand %{...} (i.e. variables) */ + expand_variables_inbuffer(r, newuri, sizeof(newuri)); + /* 5. expand ${...} (RewriteMap lookups) */ + expand_map_lookups(r, newuri, sizeof(newuri)); + /* and log the result... */ + if (perdir == NULL) { + rewritelog(r, 2, "rewrite %s -> %s", uri, newuri); + } + else { + rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, uri, newuri); + } + + /* + * Additionally do expansion for the environment variable + * strings (`RewriteRule .. .. [E=]'). + */ + for (i = 0; p->env[i] != NULL; i++) { + /* 1. take the string */ + ap_cpystrn(env, p->env[i], sizeof(env)); + /* 2. expand $N (i.e. backrefs to RewriteRule pattern) */ + expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$'); + /* 3. expand %N (i.e. backrefs to latest RewriteCond pattern) */ + expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%'); + /* 4. expand %{...} (i.e. variables) */ + expand_variables_inbuffer(r, env, sizeof(env)); + /* 5. expand ${...} (RewriteMap lookups) */ + expand_map_lookups(r, env, sizeof(env)); + /* and add the variable to Apache's structures */ + add_env_variable(r, env); + } + + /* + * Now replace API's knowledge of the current URI: + * Replace r->filename with the new URI string and split out + * an on-the-fly generated QUERY_STRING part into r->args + */ + r->filename = ap_pstrdup(r->pool, newuri); + splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND); + + /* + * Again add the previously stripped per-directory location + * prefix if the new URI is not a new one for this + * location, i.e. if it's not starting with either a slash + * or a fully qualified URL scheme. + */ + i = strlen(r->filename); + if ( prefixstrip + && !( r->filename[0] == '/' + || ( (i > 7 && strncasecmp(r->filename, "http://", 7) == 0) + || (i > 8 && strncasecmp(r->filename, "https://", 8) == 0) + || (i > 9 && strncasecmp(r->filename, "gopher://", 9) == 0) + || (i > 6 && strncasecmp(r->filename, "ftp://", 6) == 0)))) { + rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s", + perdir, r->filename, perdir, r->filename); + r->filename = ap_pstrcat(r->pool, perdir, r->filename, NULL); + } + + /* + * If this rule is forced for proxy throughput + * (`RewriteRule ... ... [P]') then emulate mod_proxy's + * URL-to-filename handler to be sure mod_proxy is triggered + * for this URL later in the Apache API. But make sure it is + * a fully-qualified URL. (If not it is qualified with + * ourself). + */ + if (p->flags & RULEFLAG_PROXY) { + fully_qualify_uri(r); + if (perdir == NULL) { + rewritelog(r, 2, "forcing proxy-throughput with %s", r->filename); + } + else { + rewritelog(r, 2, "[per-dir %s] forcing proxy-throughput with %s", + perdir, r->filename); + } + r->filename = ap_pstrcat(r->pool, "proxy:", r->filename, NULL); + return 1; + } + + /* + * If this rule is explicitly forced for HTTP redirection + * (`RewriteRule .. .. [R]') then force an external HTTP + * redirect. But make sure it is a fully-qualified URL. (If + * not it is qualified with ourself). + */ + if (p->flags & RULEFLAG_FORCEREDIRECT) { + fully_qualify_uri(r); + if (perdir == NULL) { + rewritelog(r, 2, + "explicitly forcing redirect with %s", r->filename); + } + else { + rewritelog(r, 2, + "[per-dir %s] explicitly forcing redirect with %s", + perdir, r->filename); + } + r->status = p->forced_responsecode; + return 1; + } + + /* + * Special Rewriting Feature: Self-Reduction + * We reduce the URL by stripping a possible + * http[s]://[:] prefix, i.e. a prefix which + * corresponds to ourself. This is to simplify rewrite maps + * and to avoid recursion, etc. When this prefix is not a + * coincidence then the user has to use [R] explicitly (see + * above). + */ + reduce_uri(r); + + /* + * If this rule is still implicitly forced for HTTP + * redirection (`RewriteRule .. ://...') then + * directly force an external HTTP redirect. + */ + i = strlen(r->filename); + if ( (i > 7 && strncasecmp(r->filename, "http://", 7) == 0) + || (i > 8 && strncasecmp(r->filename, "https://", 8) == 0) + || (i > 9 && strncasecmp(r->filename, "gopher://", 9) == 0) + || (i > 6 && strncasecmp(r->filename, "ftp://", 6) == 0)) { + if (perdir == NULL) { + rewritelog(r, 2, + "implicitly forcing redirect (rc=%d) with %s", + p->forced_responsecode, r->filename); + } + else { + rewritelog(r, 2, "[per-dir %s] implicitly forcing redirect " + "(rc=%d) with %s", perdir, p->forced_responsecode, + r->filename); + } + r->status = p->forced_responsecode; + return 1; + } + + /* + * Now we are sure it is not a fully qualified URL. But + * there is still one special case left: A local rewrite in + * per-directory context, i.e. a substitution URL which does + * not start with a slash. Here we add again the initially + * stripped per-directory prefix. + */ + if (prefixstrip && r->filename[0] != '/') { + rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s", + perdir, r->filename, perdir, r->filename); + r->filename = ap_pstrcat(r->pool, perdir, r->filename, NULL); + } + + /* + * Finally we had to remember if a MIME-type should be + * forced for this URL (`RewriteRule .. .. [T=]') + * Later in the API processing phase this is forced by our + * MIME API-hook function. This time its no problem even for + * the per-directory context (where the MIME-type hook was + * already processed) because a sub-request happens ;-) + */ + if (p->forced_mimetype != NULL) { + ap_table_setn(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR, + p->forced_mimetype); + if (perdir == NULL) { + rewritelog(r, 2, "remember %s to have MIME-type '%s'", + r->filename, p->forced_mimetype); + } + else { + rewritelog(r, 2, + "[per-dir %s] remember %s to have MIME-type '%s'", + perdir, r->filename, p->forced_mimetype); + } + } + + /* + * Puuhhhhhhhh... WHAT COMPLICATED STUFF ;_) + * But now we're done for this particular rule. + */ + return 1; +} + +static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p, + char *perdir, backrefinfo *briRR, + backrefinfo *briRC) +{ + char input[MAX_STRING_LEN]; + struct stat sb; + request_rec *rsub; + regmatch_t regmatch[MAX_NMATCH]; + int rc; + + /* + * Construct the string we match against + */ + + /* 1. take the string */ + ap_cpystrn(input, p->input, sizeof(input)); + /* 2. expand $N (i.e. backrefs to RewriteRule pattern) */ + expand_backref_inbuffer(r->pool, input, sizeof(input), briRR, '$'); + /* 3. expand %N (i.e. backrefs to latest RewriteCond pattern) */ + expand_backref_inbuffer(r->pool, input, sizeof(input), briRC, '%'); + /* 4. expand %{...} (i.e. variables) */ + expand_variables_inbuffer(r, input, sizeof(input)); + /* 5. expand ${...} (RewriteMap lookups) */ + expand_map_lookups(r, input, sizeof(input)); + + /* + * Apply the patterns + */ + + rc = 0; + if (strcmp(p->pattern, "-f") == 0) { + if (stat(input, &sb) == 0) { + if (S_ISREG(sb.st_mode)) { + rc = 1; + } + } + } + else if (strcmp(p->pattern, "-s") == 0) { + if (stat(input, &sb) == 0) { + if (S_ISREG(sb.st_mode) && sb.st_size > 0) { + rc = 1; + } + } + } + else if (strcmp(p->pattern, "-l") == 0) { +#if !defined(OS2) && !defined(WIN32) + if (lstat(input, &sb) == 0) { + if (S_ISLNK(sb.st_mode)) { + rc = 1; + } + } +#endif + } + else if (strcmp(p->pattern, "-d") == 0) { + if (stat(input, &sb) == 0) { + if (S_ISDIR(sb.st_mode)) { + rc = 1; + } + } + } + else if (strcmp(p->pattern, "-U") == 0) { + /* avoid infinite subrequest recursion */ + if (strlen(input) > 0 /* nonempty path, and */ + && ( r->main == NULL /* - either not in a subrequest */ + || ( r->main->uri != NULL /* - or in a subrequest... */ + && r->uri != NULL /* ...and URIs aren't NULL... */ + /* ...and sub/main URIs differ */ + && strcmp(r->main->uri, r->uri) != 0) ) ) { + + /* run a URI-based subrequest */ + rsub = ap_sub_req_lookup_uri(input, r); + + /* URI exists for any result up to 3xx, redirects allowed */ + if (rsub->status < 400) + rc = 1; + + /* log it */ + rewritelog(r, 5, "RewriteCond URI (-U) check: " + "path=%s -> status=%d", input, rsub->status); + + /* cleanup by destroying the subrequest */ + ap_destroy_sub_req(rsub); + } + } + else if (strcmp(p->pattern, "-F") == 0) { + /* avoid infinite subrequest recursion */ + if (strlen(input) > 0 /* nonempty path, and */ + && ( r->main == NULL /* - either not in a subrequest */ + || ( r->main->uri != NULL /* - or in a subrequest... */ + && r->uri != NULL /* ...and URIs aren't NULL... */ + /* ...and sub/main URIs differ */ + && strcmp(r->main->uri, r->uri) != 0) ) ) { + + /* process a file-based subrequest: + * this differs from -U in that no path translation is done. + */ + rsub = ap_sub_req_lookup_file(input, r); + + /* file exists for any result up to 2xx, no redirects */ + if (rsub->status < 300 && + /* double-check that file exists since default result is 200 */ + stat(rsub->filename, &sb) == 0) { + rc = 1; + } + + /* log it */ + rewritelog(r, 5, "RewriteCond file (-F) check: path=%s " + "-> file=%s status=%d", input, rsub->filename, + rsub->status); + + /* cleanup by destroying the subrequest */ + ap_destroy_sub_req(rsub); + } + } + else if (strlen(p->pattern) > 1 && *(p->pattern) == '>') { + rc = (compare_lexicography(input, p->pattern+1) == 1 ? 1 : 0); + } + else if (strlen(p->pattern) > 1 && *(p->pattern) == '<') { + rc = (compare_lexicography(input, p->pattern+1) == -1 ? 1 : 0); + } + else if (strlen(p->pattern) > 1 && *(p->pattern) == '=') { + if (strcmp(p->pattern+1, "\"\"") == 0) { + rc = (*input == '\0'); + } + else { + rc = (strcmp(input, p->pattern+1) == 0 ? 1 : 0); + } + } + else { + /* it is really a regexp pattern, so apply it */ + rc = (ap_regexec(p->regexp, input, + p->regexp->re_nsub+1, regmatch,0) == 0); + + /* if it isn't a negated pattern and really matched + we update the passed-through regex subst info structure */ + if (rc && !(p->flags & CONDFLAG_NOTMATCH)) { + briRC->source = ap_pstrdup(r->pool, input); + briRC->nsub = p->regexp->re_nsub; + memcpy((void *)(briRC->regmatch), (void *)(regmatch), + sizeof(regmatch)); + } + } + + /* if this is a non-matching regexp, just negate the result */ + if (p->flags & CONDFLAG_NOTMATCH) { + rc = !rc; + } + + rewritelog(r, 4, "RewriteCond: input='%s' pattern='%s%s' => %s", + input, (p->flags & CONDFLAG_NOTMATCH ? "!" : ""), + p->pattern, rc ? "matched" : "not-matched"); + + /* end just return the result */ + return rc; +} + + +/* +** +-------------------------------------------------------+ +** | | +** | URL transformation functions +** | | +** +-------------------------------------------------------+ +*/ + +/* +** +** split out a QUERY_STRING part from +** the current URI string +** +*/ + +static void splitout_queryargs(request_rec *r, int qsappend) +{ + char *q; + char *olduri; + + q = strchr(r->filename, '?'); + if (q != NULL) { + olduri = ap_pstrdup(r->pool, r->filename); + *q++ = '\0'; + if (qsappend) { + r->args = ap_pstrcat(r->pool, q, "&", r->args, NULL); + } + else { + r->args = ap_pstrdup(r->pool, q); + } + if (strlen(r->args) == 0) { + r->args = NULL; + rewritelog(r, 3, "split uri=%s -> uri=%s, args=", olduri, + r->filename); + } + else { + if (r->args[strlen(r->args)-1] == '&') { + r->args[strlen(r->args)-1] = '\0'; + } + rewritelog(r, 3, "split uri=%s -> uri=%s, args=%s", olduri, + r->filename, r->args); + } + } + return; +} + + +/* +** +** strip 'http[s]://ourhost/' from URI +** +*/ + +static void reduce_uri(request_rec *r) +{ + char *cp; + unsigned short port; + char *portp; + char *hostp; + char *url; + char c; + char host[LONG_STRING_LEN]; + char buf[MAX_STRING_LEN]; + char *olduri; + int l; + + cp = ap_http_method(r); + l = strlen(cp); + if ( strlen(r->filename) > l+3 + && strncasecmp(r->filename, cp, l) == 0 + && r->filename[l] == ':' + && r->filename[l+1] == '/' + && r->filename[l+2] == '/' ) { + /* there was really a rewrite to a remote path */ + + olduri = ap_pstrdup(r->pool, r->filename); /* save for logging */ + + /* cut the hostname and port out of the URI */ + ap_cpystrn(buf, r->filename+(l+3), sizeof(buf)); + hostp = buf; + for (cp = hostp; *cp != '\0' && *cp != '/' && *cp != ':'; cp++) + ; + if (*cp == ':') { + /* set host */ + *cp++ = '\0'; + ap_cpystrn(host, hostp, sizeof(host)); + /* set port */ + portp = cp; + for (; *cp != '\0' && *cp != '/'; cp++) + ; + c = *cp; + *cp = '\0'; + port = atoi(portp); + *cp = c; + /* set remaining url */ + url = cp; + } + else if (*cp == '/') { + /* set host */ + *cp = '\0'; + ap_cpystrn(host, hostp, sizeof(host)); + *cp = '/'; + /* set port */ + port = ap_default_port(r); + /* set remaining url */ + url = cp; + } + else { + /* set host */ + ap_cpystrn(host, hostp, sizeof(host)); + /* set port */ + port = ap_default_port(r); + /* set remaining url */ + url = "/"; + } + + /* now check whether we could reduce it to a local path... */ + if (ap_matches_request_vhost(r, host, port)) { + /* this is our host, so only the URL remains */ + r->filename = ap_pstrdup(r->pool, url); + rewritelog(r, 3, "reduce %s -> %s", olduri, r->filename); + } + } + return; +} + + +/* +** +** add 'http[s]://ourhost[:ourport]/' to URI +** if URI is still not fully qualified +** +*/ + +static void fully_qualify_uri(request_rec *r) +{ + int i; + char buf[32]; + const char *thisserver; + char *thisport; + int port; + + i = strlen(r->filename); + if (!( (i > 7 && strncasecmp(r->filename, "http://", 7) == 0) + || (i > 8 && strncasecmp(r->filename, "https://", 8) == 0) + || (i > 9 && strncasecmp(r->filename, "gopher://", 9) == 0) + || (i > 6 && strncasecmp(r->filename, "ftp://", 6) == 0))) { + + thisserver = ap_get_server_name(r); + port = ap_get_server_port(r); + if (ap_is_default_port(port,r)) { + thisport = ""; + } + else { + ap_snprintf(buf, sizeof(buf), ":%u", port); + thisport = buf; + } + + if (r->filename[0] == '/') { + r->filename = ap_psprintf(r->pool, "%s://%s%s%s", + ap_http_method(r), thisserver, + thisport, r->filename); + } + else { + r->filename = ap_psprintf(r->pool, "%s://%s%s/%s", + ap_http_method(r), thisserver, + thisport, r->filename); + } + } + return; +} + + +/* +** +** Expand the %0-%9 or $0-$9 regex backreferences +** +*/ + +static void expand_backref_inbuffer(pool *p, char *buf, int nbuf, + backrefinfo *bri, char c) +{ + int i; + + if (bri->nsub < 1) { + return; + } + + if (c != '$') { + /* safe existing $N backrefs and replace N with $N backrefs */ + for (i = 0; buf[i] != '\0' && i < nbuf; i++) { + if (buf[i] == '$' && (buf[i+1] >= '0' && buf[i+1] <= '9')) { + buf[i++] = '\001'; + } + else if (buf[i] == c && (buf[i+1] >= '0' && buf[i+1] <= '9')) { + buf[i++] = '$'; + } + } + } + + /* now apply the pregsub() function */ + ap_cpystrn(buf, ap_pregsub(p, buf, bri->source, + bri->nsub+1, bri->regmatch), nbuf); + + if (c != '$') { + /* restore the original $N backrefs */ + for (i = 0; buf[i] != '\0' && i < nbuf; i++) { + if (buf[i] == '\001' && (buf[i+1] >= '0' && buf[i+1] <= '9')) { + buf[i++] = '$'; + } + } + } +} + + +/* +** +** Expand tilde-paths (/~user) through +** Unix /etc/passwd database information +** +*/ +#ifndef WIN32 +static char *expand_tildepaths(request_rec *r, char *uri) +{ + char user[LONG_STRING_LEN]; + struct passwd *pw; + char *newuri; + int i, j; + + newuri = uri; + if (uri != NULL && strlen(uri) > 2 && uri[0] == '/' && uri[1] == '~') { + /* cut out the username */ + for (j = 0, i = 2; j < sizeof(user)-1 + && uri[i] != '\0' + && uri[i] != '/' ; ) { + user[j++] = uri[i++]; + } + user[j] = '\0'; + + /* lookup username in systems passwd file */ + if ((pw = getpwnam(user)) != NULL) { + /* ok, user was found, so expand the ~user string */ + if (uri[i] != '\0') { + /* ~user/anything... has to be expanded */ + if (pw->pw_dir[strlen(pw->pw_dir)-1] == '/') { + pw->pw_dir[strlen(pw->pw_dir)-1] = '\0'; + } + newuri = ap_pstrcat(r->pool, pw->pw_dir, uri+i, NULL); + } + else { + /* only ~user has to be expanded */ + newuri = ap_pstrdup(r->pool, pw->pw_dir); + } + } + } + return newuri; +} +#endif + +/* +** +** mapfile expansion support +** i.e. expansion of MAP lookup directives +** ${:} in RewriteRule rhs +** +*/ + +#define limit_length(n) (n > LONG_STRING_LEN-1 ? LONG_STRING_LEN-1 : n) + +static void expand_map_lookups(request_rec *r, char *uri, int uri_len) +{ + char newuri[MAX_STRING_LEN]; + char *cpI; + char *cpIE; + char *cpO; + char *cpT; + char *cpT2; + char mapname[LONG_STRING_LEN]; + char mapkey[LONG_STRING_LEN]; + char defaultvalue[LONG_STRING_LEN]; + int n; + + cpI = uri; + cpIE = cpI+strlen(cpI); + cpO = newuri; + while (cpI < cpIE) { + if (cpI+6 < cpIE && strncmp(cpI, "${", 2) == 0) { + /* missing delimiter -> take it as plain text */ + if ( strchr(cpI+2, ':') == NULL + || strchr(cpI+2, '}') == NULL) { + memcpy(cpO, cpI, 2); + cpO += 2; + cpI += 2; + continue; + } + cpI += 2; + + cpT = strchr(cpI, ':'); + n = cpT-cpI; + memcpy(mapname, cpI, limit_length(n)); + mapname[limit_length(n)] = '\0'; + cpI += n+1; + + cpT2 = strchr(cpI, '|'); + cpT = strchr(cpI, '}'); + if (cpT2 != NULL && cpT2 < cpT) { + n = cpT2-cpI; + memcpy(mapkey, cpI, limit_length(n)); + mapkey[limit_length(n)] = '\0'; + cpI += n+1; + + n = cpT-cpI; + memcpy(defaultvalue, cpI, limit_length(n)); + defaultvalue[limit_length(n)] = '\0'; + cpI += n+1; + } + else { + n = cpT-cpI; + memcpy(mapkey, cpI, limit_length(n)); + mapkey[limit_length(n)] = '\0'; + cpI += n+1; + + defaultvalue[0] = '\0'; + } + + cpT = lookup_map(r, mapname, mapkey); + if (cpT != NULL) { + n = strlen(cpT); + if (cpO + n >= newuri + sizeof(newuri)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, + r, "insufficient space in " + "expand_map_lookups, aborting"); + return; + } + memcpy(cpO, cpT, n); + cpO += n; + } + else { + n = strlen(defaultvalue); + if (cpO + n >= newuri + sizeof(newuri)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, + r, "insufficient space in " + "expand_map_lookups, aborting"); + return; + } + memcpy(cpO, defaultvalue, n); + cpO += n; + } + } + else { + cpT = strstr(cpI, "${"); + if (cpT == NULL) + cpT = cpI+strlen(cpI); + n = cpT-cpI; + if (cpO + n >= newuri + sizeof(newuri)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, + r, "insufficient space in " + "expand_map_lookups, aborting"); + return; + } + memcpy(cpO, cpI, n); + cpO += n; + cpI += n; + } + } + *cpO = '\0'; + ap_cpystrn(uri, newuri, uri_len); + return; +} + +#undef limit_length + + + +/* +** +-------------------------------------------------------+ +** | | +** | DBM hashfile support +** | | +** +-------------------------------------------------------+ +*/ + + +static char *lookup_map(request_rec *r, char *name, char *key) +{ + void *sconf; + rewrite_server_conf *conf; + array_header *rewritemaps; + rewritemap_entry *entries; + rewritemap_entry *s; + char *value; + struct stat st; + int i; + + /* get map configuration */ + sconf = r->server->module_config; + conf = (rewrite_server_conf *)ap_get_module_config(sconf, + &rewrite_module); + rewritemaps = conf->rewritemaps; + + entries = (rewritemap_entry *)rewritemaps->elts; + for (i = 0; i < rewritemaps->nelts; i++) { + s = &entries[i]; + if (strcmp(s->name, name) == 0) { + if (s->type == MAPTYPE_TXT) { + if (stat(s->checkfile, &st) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "mod_rewrite: can't access text RewriteMap " + "file %s", s->checkfile); + rewritelog(r, 1, "can't open RewriteMap file, " + "see error log"); + return NULL; + } + value = get_cache_string(cachep, s->name, CACHEMODE_TS, + st.st_mtime, key); + if (value == NULL) { + rewritelog(r, 6, "cache lookup FAILED, forcing new " + "map lookup"); + if ((value = + lookup_map_txtfile(r, s->datafile, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s key=%s[txt] " + "-> val=%s", s->name, key, value); + set_cache_string(cachep, s->name, CACHEMODE_TS, + st.st_mtime, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s[txt] " + "key=%s", s->name, key); + set_cache_string(cachep, s->name, CACHEMODE_TS, + st.st_mtime, key, ""); + return NULL; + } + } + else { + rewritelog(r, 5, "cache lookup OK: map=%s[txt] key=%s " + "-> val=%s", s->name, key, value); + return value[0] != '\0' ? value : NULL; + } + } + else if (s->type == MAPTYPE_DBM) { +#ifndef NO_DBM_REWRITEMAP + if (stat(s->checkfile, &st) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "mod_rewrite: can't access DBM RewriteMap " + "file %s", s->checkfile); + rewritelog(r, 1, "can't open DBM RewriteMap file, " + "see error log"); + return NULL; + } + value = get_cache_string(cachep, s->name, CACHEMODE_TS, + st.st_mtime, key); + if (value == NULL) { + rewritelog(r, 6, + "cache lookup FAILED, forcing new map lookup"); + if ((value = + lookup_map_dbmfile(r, s->datafile, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s[dbm] key=%s " + "-> val=%s", s->name, key, value); + set_cache_string(cachep, s->name, CACHEMODE_TS, + st.st_mtime, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s[dbm] " + "key=%s", s->name, key); + set_cache_string(cachep, s->name, CACHEMODE_TS, + st.st_mtime, key, ""); + return NULL; + } + } + else { + rewritelog(r, 5, "cache lookup OK: map=%s[dbm] key=%s " + "-> val=%s", s->name, key, value); + return value[0] != '\0' ? value : NULL; + } +#else + return NULL; +#endif + } + else if (s->type == MAPTYPE_PRG) { + if ((value = + lookup_map_program(r, s->fpin, s->fpout, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s key=%s -> val=%s", + s->name, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s key=%s", + s->name, key); + } + } + else if (s->type == MAPTYPE_INT) { + if ((value = lookup_map_internal(r, s->func, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s key=%s -> val=%s", + s->name, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s key=%s", + s->name, key); + } + } + else if (s->type == MAPTYPE_RND) { + if (stat(s->checkfile, &st) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "mod_rewrite: can't access text RewriteMap " + "file %s", s->checkfile); + rewritelog(r, 1, "can't open RewriteMap file, " + "see error log"); + return NULL; + } + value = get_cache_string(cachep, s->name, CACHEMODE_TS, + st.st_mtime, key); + if (value == NULL) { + rewritelog(r, 6, "cache lookup FAILED, forcing new " + "map lookup"); + if ((value = + lookup_map_txtfile(r, s->datafile, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s key=%s[txt] " + "-> val=%s", s->name, key, value); + set_cache_string(cachep, s->name, CACHEMODE_TS, + st.st_mtime, key, value); + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s[txt] " + "key=%s", s->name, key); + set_cache_string(cachep, s->name, CACHEMODE_TS, + st.st_mtime, key, ""); + return NULL; + } + } + else { + rewritelog(r, 5, "cache lookup OK: map=%s[txt] key=%s " + "-> val=%s", s->name, key, value); + } + if (value[0] != '\0') { + value = select_random_value_part(r, value); + rewritelog(r, 5, "randomly choosen the subvalue `%s'", value); + } + else { + value = NULL; + } + return value; + } + } + } + return NULL; +} + +static char *lookup_map_txtfile(request_rec *r, char *file, char *key) +{ + FILE *fp = NULL; + char line[1024]; + char *value = NULL; + char *cpT; + size_t skip; + char *curkey; + char *curval; + + if ((fp = ap_pfopen(r->pool, file, "r")) == NULL) { + return NULL; + } + + while (fgets(line, sizeof(line), fp) != NULL) { + if (line[0] == '#') + continue; /* ignore comments */ + cpT = line; + curkey = cpT; + skip = strcspn(cpT," \t\r\n"); + if (skip == 0) + continue; /* ignore lines that start with a space, tab, CR, or LF */ + cpT += skip; + *cpT = '\0'; + if (strcmp(curkey, key) != 0) + continue; /* key does not match... */ + + /* found a matching key; now extract and return the value */ + ++cpT; + skip = strspn(cpT, " \t\r\n"); + cpT += skip; + curval = cpT; + skip = strcspn(cpT, " \t\r\n"); + if (skip == 0) + continue; /* no value... */ + cpT += skip; + *cpT = '\0'; + value = ap_pstrdup(r->pool, curval); + break; + } + ap_pfclose(r->pool, fp); + return value; +} + +#ifndef NO_DBM_REWRITEMAP +static char *lookup_map_dbmfile(request_rec *r, char *file, char *key) +{ + DBM *dbmfp = NULL; + datum dbmkey; + datum dbmval; + char *value = NULL; + char buf[MAX_STRING_LEN]; + + dbmkey.dptr = key; + dbmkey.dsize = strlen(key); + if ((dbmfp = dbm_open(file, O_RDONLY, 0666)) != NULL) { + dbmval = dbm_fetch(dbmfp, dbmkey); + if (dbmval.dptr != NULL) { + memcpy(buf, dbmval.dptr, + dbmval.dsize < sizeof(buf)-1 ? + dbmval.dsize : sizeof(buf)-1 ); + buf[dbmval.dsize] = '\0'; + value = ap_pstrdup(r->pool, buf); + } + dbm_close(dbmfp); + } + return value; +} +#endif + +static char *lookup_map_program(request_rec *r, int fpin, int fpout, char *key) +{ + char buf[LONG_STRING_LEN]; + char c; + int i; +#ifndef NO_WRITEV + struct iovec iov[2]; +#endif + + /* when `RewriteEngine off' was used in the per-server + * context then the rewritemap-programs were not spawned. + * In this case using such a map (usually in per-dir context) + * is useless because it is not available. + */ + if (fpin == -1 || fpout == -1) { + return NULL; + } + + /* take the lock */ + rewritelock_alloc(r); + + /* write out the request key */ +#ifdef NO_WRITEV + write(fpin, key, strlen(key)); + write(fpin, "\n", 1); +#else + iov[0].iov_base = key; + iov[0].iov_len = strlen(key); + iov[1].iov_base = "\n"; + iov[1].iov_len = 1; + writev(fpin, iov, 2); +#endif + + /* read in the response value */ + i = 0; + while (read(fpout, &c, 1) == 1 && (i < LONG_STRING_LEN-1)) { + if (c == '\n') { + break; + } + buf[i++] = c; + } + buf[i] = '\0'; + + /* give the lock back */ + rewritelock_free(r); + + if (strcasecmp(buf, "NULL") == 0) { + return NULL; + } + else { + return ap_pstrdup(r->pool, buf); + } +} + +static char *lookup_map_internal(request_rec *r, + char *(*func)(request_rec *, char *), + char *key) +{ + /* currently we just let the function convert + the key to a corresponding value */ + return func(r, key); +} + +static char *rewrite_mapfunc_toupper(request_rec *r, char *key) +{ + char *value, *cp; + + for (cp = value = ap_pstrdup(r->pool, key); cp != NULL && *cp != '\0'; + cp++) { + *cp = ap_toupper(*cp); + } + return value; +} + +static char *rewrite_mapfunc_tolower(request_rec *r, char *key) +{ + char *value, *cp; + + for (cp = value = ap_pstrdup(r->pool, key); cp != NULL && *cp != '\0'; + cp++) { + *cp = ap_tolower(*cp); + } + return value; +} + +static char *rewrite_mapfunc_escape(request_rec *r, char *key) +{ + char *value; + + value = ap_escape_uri(r->pool, key); + return value; +} + +static char *rewrite_mapfunc_unescape(request_rec *r, char *key) +{ + char *value; + + value = ap_pstrdup(r->pool, key); + ap_unescape_url(value); + return value; +} + +static int rewrite_rand_init_done = 0; + +static void rewrite_rand_init(void) +{ + if (!rewrite_rand_init_done) { + srand((unsigned)(getpid())); + rewrite_rand_init_done = 1; + } + return; +} + +static int rewrite_rand(int l, int h) +{ + int i; + char buf[50]; + + rewrite_rand_init(); + ap_snprintf(buf, sizeof(buf), "%.0f", + (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l))); + i = atoi(buf)+1; + if (i < l) i = l; + if (i > h) i = h; + return i; +} + +static char *select_random_value_part(request_rec *r, char *value) +{ + char *buf; + int n, i, k; + + /* count number of distinct values */ + for (n = 1, i = 0; value[i] != '\0'; i++) { + if (value[i] == '|') { + n++; + } + } + + /* when only one value we have no option to choose */ + if (n == 1) { + return value; + } + + /* else randomly select one */ + k = rewrite_rand(1, n); + + /* and grep it out */ + for (n = 1, i = 0; value[i] != '\0'; i++) { + if (n == k) { + break; + } + if (value[i] == '|') { + n++; + } + } + buf = ap_pstrdup(r->pool, &value[i]); + for (i = 0; buf[i] != '\0' && buf[i] != '|'; i++) + ; + buf[i] = '\0'; + return buf; +} + + +/* +** +-------------------------------------------------------+ +** | | +** | rewriting logfile support +** | | +** +-------------------------------------------------------+ +*/ + + +static void open_rewritelog(server_rec *s, pool *p) +{ + rewrite_server_conf *conf; + char *fname; + piped_log *pl; + int rewritelog_flags = ( O_WRONLY|O_APPEND|O_CREAT ); +#ifdef WIN32 + mode_t rewritelog_mode = ( _S_IREAD|_S_IWRITE ); +#else + mode_t rewritelog_mode = ( S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ); +#endif + + conf = ap_get_module_config(s->module_config, &rewrite_module); + + if (conf->rewritelogfile == NULL) { + return; + } + if (*(conf->rewritelogfile) == '\0') { + return; + } + if (conf->rewritelogfp > 0) { + return; /* virtual log shared w/ main server */ + } + + fname = ap_server_root_relative(p, conf->rewritelogfile); + + if (*conf->rewritelogfile == '|') { + if ((pl = ap_open_piped_log(p, conf->rewritelogfile+1)) == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "mod_rewrite: could not open reliable pipe " + "to RewriteLog filter %s", conf->rewritelogfile+1); + exit(1); + } + conf->rewritelogfp = ap_piped_log_write_fd(pl); + } + else if (*conf->rewritelogfile != '\0') { + if ((conf->rewritelogfp = ap_popenf(p, fname, rewritelog_flags, + rewritelog_mode)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + + "mod_rewrite: could not open RewriteLog " + "file %s", fname); + exit(1); + } + } + return; +} + +static void rewritelog(request_rec *r, int level, const char *text, ...) +{ + rewrite_server_conf *conf; + conn_rec *conn; + char *str1; + char str2[512]; + char str3[1024]; + char type[20]; + char redir[20]; + va_list ap; + int i; + request_rec *req; + char *ruser; + const char *rhost; + + va_start(ap, text); + conf = ap_get_module_config(r->server->module_config, &rewrite_module); + conn = r->connection; + + if (conf->rewritelogfp < 0) { + return; + } + if (conf->rewritelogfile == NULL) { + return; + } + if (*(conf->rewritelogfile) == '\0') { + return; + } + + if (level > conf->rewriteloglevel) { + return; + } + + if (conn->user == NULL) { + ruser = "-"; + } + else if (strlen(conn->user) != 0) { + ruser = conn->user; + } + else { + ruser = "\"\""; + } + + rhost = ap_get_remote_host(conn, r->server->module_config, + REMOTE_NOLOOKUP); + if (rhost == NULL) { + rhost = "UNKNOWN-HOST"; + } + + str1 = ap_pstrcat(r->pool, rhost, " ", + (conn->remote_logname != NULL ? + conn->remote_logname : "-"), " ", + ruser, NULL); + ap_vsnprintf(str2, sizeof(str2), text, ap); + + if (r->main == NULL) { + strcpy(type, "initial"); + } + else { + strcpy(type, "subreq"); + } + + for (i = 0, req = r; req->prev != NULL; req = req->prev) { + i++; + } + if (i == 0) { + redir[0] = '\0'; + } + else { + ap_snprintf(redir, sizeof(redir), "/redir#%d", i); + } + + ap_snprintf(str3, sizeof(str3), + "%s %s [%s/sid#%lx][rid#%lx/%s%s] (%d) %s\n", str1, + current_logtime(r), ap_get_server_name(r), + (unsigned long)(r->server), (unsigned long)r, + type, redir, level, str2); + + fd_lock(r, conf->rewritelogfp); + write(conf->rewritelogfp, str3, strlen(str3)); + fd_unlock(r, conf->rewritelogfp); + + va_end(ap); + return; +} + +static char *current_logtime(request_rec *r) +{ + int timz; + struct tm *t; + char tstr[80]; + char sign; + + t = ap_get_gmtoff(&timz); + sign = (timz < 0 ? '-' : '+'); + if (timz < 0) { + timz = -timz; + } + + strftime(tstr, 80, "[%d/%b/%Y:%H:%M:%S ", t); + ap_snprintf(tstr + strlen(tstr), 80-strlen(tstr), "%c%.2d%.2d]", + sign, timz/60, timz%60); + return ap_pstrdup(r->pool, tstr); +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | rewriting lockfile support +** | | +** +-------------------------------------------------------+ +*/ + +#ifdef WIN32 +#define REWRITELOCK_MODE ( _S_IREAD|_S_IWRITE ) +#else +#define REWRITELOCK_MODE ( S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ) +#endif + +static void rewritelock_create(server_rec *s, pool *p) +{ + rewrite_server_conf *conf; + + conf = ap_get_module_config(s->module_config, &rewrite_module); + + /* only operate if a lockfile is used */ + if (lockname == NULL || *(lockname) == '\0') { + return; + } + + /* fixup the path, especially for rewritelock_remove() */ + lockname = ap_server_root_relative(p, lockname); + + /* create the lockfile */ + unlink(lockname); + if ((lockfd = ap_popenf(p, lockname, O_WRONLY|O_CREAT, + REWRITELOCK_MODE)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "mod_rewrite: Parent could not create RewriteLock " + "file %s", lockname); + exit(1); + } +#if !defined(OS2) && !defined(WIN32) + /* make sure the childs have access to this file */ + if (geteuid() == 0 /* is superuser */) + chown(lockname, ap_user_id, -1 /* no gid change */); +#endif + + return; +} + +static void rewritelock_open(server_rec *s, pool *p) +{ + rewrite_server_conf *conf; + + conf = ap_get_module_config(s->module_config, &rewrite_module); + + /* only operate if a lockfile is used */ + if (lockname == NULL || *(lockname) == '\0') { + return; + } + + /* open the lockfile (once per child) to get a unique fd */ + if ((lockfd = ap_popenf(p, lockname, O_WRONLY, + REWRITELOCK_MODE)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "mod_rewrite: Child could not open RewriteLock " + "file %s", lockname); + exit(1); + } + return; +} + +static void rewritelock_remove(void *data) +{ + /* only operate if a lockfile is used */ + if (lockname == NULL || *(lockname) == '\0') { + return; + } + + /* remove the lockfile */ + unlink(lockname); + lockname = NULL; + lockfd = -1; +} + +static void rewritelock_alloc(request_rec *r) +{ + if (lockfd != -1) { + fd_lock(r, lockfd); + } + return; +} + +static void rewritelock_free(request_rec *r) +{ + if (lockfd != -1) { + fd_unlock(r, lockfd); + } + return; +} + + +/* +** +-------------------------------------------------------+ +** | | +** | program map support +** | | +** +-------------------------------------------------------+ +*/ + +static void run_rewritemap_programs(server_rec *s, pool *p) +{ + rewrite_server_conf *conf; + FILE *fpin; + FILE *fpout; + FILE *fperr; + array_header *rewritemaps; + rewritemap_entry *entries; + rewritemap_entry *map; + int i; + int rc; + + conf = ap_get_module_config(s->module_config, &rewrite_module); + + /* If the engine isn't turned on, + * don't even try to do anything. + */ + if (conf->state == ENGINE_DISABLED) { + return; + } + + rewritemaps = conf->rewritemaps; + entries = (rewritemap_entry *)rewritemaps->elts; + for (i = 0; i < rewritemaps->nelts; i++) { + map = &entries[i]; + if (map->type != MAPTYPE_PRG) { + continue; + } + if (map->datafile == NULL + || *(map->datafile) == '\0' + || map->fpin != -1 + || map->fpout != -1 ) { + continue; + } + fpin = NULL; + fpout = NULL; + rc = ap_spawn_child(p, rewritemap_program_child, + (void *)map->datafile, kill_after_timeout, + &fpin, &fpout, &fperr); + if (rc == 0 || fpin == NULL || fpout == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "mod_rewrite: could not fork child for " + "RewriteMap process"); + exit(1); + } + map->fpin = fileno(fpin); + map->fpout = fileno(fpout); + map->fperr = fileno(fperr); + } + return; +} + +/* child process code */ +static int rewritemap_program_child(void *cmd, child_info *pinfo) +{ + int child_pid = 1; + + /* + * Prepare for exec + */ + ap_cleanup_for_exec(); +#ifdef SIGHUP + signal(SIGHUP, SIG_IGN); +#endif + + /* + * Exec() the child program + */ +#if defined(WIN32) + /* MS Windows */ + { + char pCommand[MAX_STRING_LEN]; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ap_snprintf(pCommand, sizeof(pCommand), "%s /C %s", SHELL_PATH, cmd); + + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = pinfo->hPipeInputRead; + si.hStdOutput = pinfo->hPipeOutputWrite; + si.hStdError = pinfo->hPipeErrorWrite; + + if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, 0, + environ, NULL, &si, &pi)) { + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + child_pid = pi.dwProcessId; + } + } +#elif defined(OS2) + /* IBM OS/2 */ + execl(SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); +#else + /* Standard Unix */ + execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + return(child_pid); +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | environment variable support +** | | +** +-------------------------------------------------------+ +*/ + + +static void expand_variables_inbuffer(request_rec *r, char *buf, int buf_len) +{ + char *newbuf; + newbuf = expand_variables(r, buf); + if (strcmp(newbuf, buf) != 0) { + ap_cpystrn(buf, newbuf, buf_len); + } + return; +} + +static char *expand_variables(request_rec *r, char *str) +{ + char output[MAX_STRING_LEN]; + char input[MAX_STRING_LEN]; + char *cp; + char *cp2; + char *cp3; + int expanded; + char *outp; + char *endp; + + ap_cpystrn(input, str, sizeof(input)); + output[0] = '\0'; + outp = output; + endp = output + sizeof(output); + expanded = 0; + for (cp = input; cp < input+MAX_STRING_LEN; ) { + if ((cp2 = strstr(cp, "%{")) != NULL) { + if ((cp3 = strstr(cp2, "}")) != NULL) { + *cp2 = '\0'; + outp = ap_cpystrn(outp, cp, endp - outp); + + cp2 += 2; + *cp3 = '\0'; + outp = ap_cpystrn(outp, lookup_variable(r, cp2), endp - outp); + + cp = cp3+1; + expanded = 1; + continue; + } + } + outp = ap_cpystrn(outp, cp, endp - outp); + break; + } + return expanded ? ap_pstrdup(r->pool, output) : str; +} + +static char *lookup_variable(request_rec *r, char *var) +{ + const char *result; + char resultbuf[LONG_STRING_LEN]; + time_t tc; + struct tm *tm; + request_rec *rsub; +#ifndef WIN32 + struct passwd *pw; + struct group *gr; + struct stat finfo; +#endif + + result = NULL; + + /* HTTP headers */ + if (strcasecmp(var, "HTTP_USER_AGENT") == 0) { + result = lookup_header(r, "User-Agent"); + } + else if (strcasecmp(var, "HTTP_REFERER") == 0) { + result = lookup_header(r, "Referer"); + } + else if (strcasecmp(var, "HTTP_COOKIE") == 0) { + result = lookup_header(r, "Cookie"); + } + else if (strcasecmp(var, "HTTP_FORWARDED") == 0) { + result = lookup_header(r, "Forwarded"); + } + else if (strcasecmp(var, "HTTP_HOST") == 0) { + result = lookup_header(r, "Host"); + } + else if (strcasecmp(var, "HTTP_PROXY_CONNECTION") == 0) { + result = lookup_header(r, "Proxy-Connection"); + } + else if (strcasecmp(var, "HTTP_ACCEPT") == 0) { + result = lookup_header(r, "Accept"); + } + /* all other headers from which we are still not know about */ + else if (strlen(var) > 5 && strncasecmp(var, "HTTP:", 5) == 0) { + result = lookup_header(r, var+5); + } + + /* connection stuff */ + else if (strcasecmp(var, "REMOTE_ADDR") == 0) { + result = r->connection->remote_ip; + } + else if (strcasecmp(var, "REMOTE_HOST") == 0) { + result = (char *)ap_get_remote_host(r->connection, + r->per_dir_config, REMOTE_NAME); + } + else if (strcasecmp(var, "REMOTE_USER") == 0) { + result = r->connection->user; + } + else if (strcasecmp(var, "REMOTE_IDENT") == 0) { + result = (char *)ap_get_remote_logname(r); + } + + /* request stuff */ + else if (strcasecmp(var, "THE_REQUEST") == 0) { /* non-standard */ + result = r->the_request; + } + else if (strcasecmp(var, "REQUEST_METHOD") == 0) { + result = r->method; + } + else if (strcasecmp(var, "REQUEST_URI") == 0) { /* non-standard */ + result = r->uri; + } + else if (strcasecmp(var, "SCRIPT_FILENAME") == 0 || + strcasecmp(var, "REQUEST_FILENAME") == 0 ) { + result = r->filename; + } + else if (strcasecmp(var, "PATH_INFO") == 0) { + result = r->path_info; + } + else if (strcasecmp(var, "QUERY_STRING") == 0) { + result = r->args; + } + else if (strcasecmp(var, "AUTH_TYPE") == 0) { + result = r->connection->ap_auth_type; + } + else if (strcasecmp(var, "IS_SUBREQ") == 0) { /* non-standard */ + result = (r->main != NULL ? "true" : "false"); + } + + /* internal server stuff */ + else if (strcasecmp(var, "DOCUMENT_ROOT") == 0) { + result = ap_document_root(r); + } + else if (strcasecmp(var, "SERVER_ADMIN") == 0) { + result = r->server->server_admin; + } + else if (strcasecmp(var, "SERVER_NAME") == 0) { + result = ap_get_server_name(r); + } + else if (strcasecmp(var, "SERVER_ADDR") == 0) { /* non-standard */ + result = r->connection->local_ip; + } + else if (strcasecmp(var, "SERVER_PORT") == 0) { + ap_snprintf(resultbuf, sizeof(resultbuf), "%u", ap_get_server_port(r)); + result = resultbuf; + } + else if (strcasecmp(var, "SERVER_PROTOCOL") == 0) { + result = r->protocol; + } + else if (strcasecmp(var, "SERVER_SOFTWARE") == 0) { + result = ap_get_server_version(); + } + else if (strcasecmp(var, "API_VERSION") == 0) { /* non-standard */ + ap_snprintf(resultbuf, sizeof(resultbuf), "%d:%d", + MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR); + result = resultbuf; + } + + /* underlaying Unix system stuff */ + else if (strcasecmp(var, "TIME_YEAR") == 0) { + tc = time(NULL); + tm = localtime(&tc); + ap_snprintf(resultbuf, sizeof(resultbuf), "%02d%02d", + (tm->tm_year / 100) + 19, tm->tm_year % 100); + result = resultbuf; + } +#define MKTIMESTR(format, tmfield) \ + tc = time(NULL); \ + tm = localtime(&tc); \ + ap_snprintf(resultbuf, sizeof(resultbuf), format, tm->tmfield); \ + result = resultbuf; + else if (strcasecmp(var, "TIME_MON") == 0) { + MKTIMESTR("%02d", tm_mon+1) + } + else if (strcasecmp(var, "TIME_DAY") == 0) { + MKTIMESTR("%02d", tm_mday) + } + else if (strcasecmp(var, "TIME_HOUR") == 0) { + MKTIMESTR("%02d", tm_hour) + } + else if (strcasecmp(var, "TIME_MIN") == 0) { + MKTIMESTR("%02d", tm_min) + } + else if (strcasecmp(var, "TIME_SEC") == 0) { + MKTIMESTR("%02d", tm_sec) + } + else if (strcasecmp(var, "TIME_WDAY") == 0) { + MKTIMESTR("%d", tm_wday) + } + else if (strcasecmp(var, "TIME") == 0) { + tc = time(NULL); + tm = localtime(&tc); + ap_snprintf(resultbuf, sizeof(resultbuf), + "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19, + (tm->tm_year % 100), tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + result = resultbuf; + rewritelog(r, 1, "RESULT='%s'", result); + } + + /* all other env-variables from the parent Apache process */ + else if (strlen(var) > 4 && strncasecmp(var, "ENV:", 4) == 0) { + /* first try the internal Apache notes structure */ + result = ap_table_get(r->notes, var+4); + /* second try the internal Apache env structure */ + if (result == NULL) { + result = ap_table_get(r->subprocess_env, var+4); + } + /* third try the external OS env */ + if (result == NULL) { + result = getenv(var+4); + } + } + +#define LOOKAHEAD(subrecfunc) \ + if ( \ + /* filename is safe to use */ \ + r->filename != NULL \ + /* - and we're either not in a subrequest */ \ + && ( r->main == NULL \ + /* - or in a subrequest where paths are non-NULL... */ \ + || ( r->main->uri != NULL && r->uri != NULL \ + /* ...and sub and main paths differ */ \ + && strcmp(r->main->uri, r->uri) != 0))) { \ + /* process a file-based subrequest */ \ + rsub = subrecfunc(r->filename, r); \ + /* now recursively lookup the variable in the sub_req */ \ + result = lookup_variable(rsub, var+5); \ + /* copy it up to our scope before we destroy sub_req's pool */ \ + result = ap_pstrdup(r->pool, result); \ + /* cleanup by destroying the subrequest */ \ + ap_destroy_sub_req(rsub); \ + /* log it */ \ + rewritelog(r, 5, "lookahead: path=%s var=%s -> val=%s", \ + r->filename, var+5, result); \ + /* return ourself to prevent re-pstrdup */ \ + return (char *)result; \ + } + + /* look-ahead for parameter through URI-based sub-request */ + else if (strlen(var) > 5 && strncasecmp(var, "LA-U:", 5) == 0) { + LOOKAHEAD(ap_sub_req_lookup_uri) + } + /* look-ahead for parameter through file-based sub-request */ + else if (strlen(var) > 5 && strncasecmp(var, "LA-F:", 5) == 0) { + LOOKAHEAD(ap_sub_req_lookup_file) + } + +#ifndef WIN32 + /* Win32 has a rather different view of file ownerships. + For now, just forget it */ + + /* file stuff */ + else if (strcasecmp(var, "SCRIPT_USER") == 0) { + result = ""; + if (r->finfo.st_mode != 0) { + if ((pw = getpwuid(r->finfo.st_uid)) != NULL) { + result = pw->pw_name; + } + } + else { + if (stat(r->filename, &finfo) == 0) { + if ((pw = getpwuid(finfo.st_uid)) != NULL) { + result = pw->pw_name; + } + } + } + } + else if (strcasecmp(var, "SCRIPT_GROUP") == 0) { + result = ""; + if (r->finfo.st_mode != 0) { + if ((gr = getgrgid(r->finfo.st_gid)) != NULL) { + result = gr->gr_name; + } + } + else { + if (stat(r->filename, &finfo) == 0) { + if ((gr = getgrgid(finfo.st_gid)) != NULL) { + result = gr->gr_name; + } + } + } + } +#endif /* ndef WIN32 */ + + if (result == NULL) { + return ap_pstrdup(r->pool, ""); + } + else { + return ap_pstrdup(r->pool, result); + } +} + +static char *lookup_header(request_rec *r, const char *name) +{ + array_header *hdrs_arr; + table_entry *hdrs; + int i; + + hdrs_arr = ap_table_elts(r->headers_in); + hdrs = (table_entry *)hdrs_arr->elts; + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (hdrs[i].key == NULL) { + continue; + } + if (strcasecmp(hdrs[i].key, name) == 0) { + ap_table_merge(r->notes, VARY_KEY_THIS, name); + return hdrs[i].val; + } + } + return NULL; +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | caching support +** | | +** +-------------------------------------------------------+ +*/ + + +static cache *init_cache(pool *p) +{ + cache *c; + + c = (cache *)ap_palloc(p, sizeof(cache)); + c->pool = ap_make_sub_pool(p); + c->lists = ap_make_array(c->pool, 2, sizeof(cachelist)); + return c; +} + +static void set_cache_string(cache *c, char *res, int mode, time_t t, + char *key, char *value) +{ + cacheentry ce; + + ce.time = t; + ce.key = key; + ce.value = value; + store_cache_string(c, res, &ce); + return; +} + +static char *get_cache_string(cache *c, char *res, int mode, + time_t t, char *key) +{ + cacheentry *ce; + + ce = retrieve_cache_string(c, res, key); + if (ce == NULL) { + return NULL; + } + if (mode & CACHEMODE_TS) { + if (t != ce->time) { + return NULL; + } + } + else if (mode & CACHEMODE_TTL) { + if (t > ce->time) { + return NULL; + } + } + return ap_pstrdup(c->pool, ce->value); +} + +static int cache_tlb_hash(char *key) +{ + unsigned long n; + char *p; + + n = 0; + for (p=key; *p != '\0'; ++p) { + n = n * 53711 + 134561 + (unsigned)(*p & 0xff); + } + + return n % CACHE_TLB_ROWS; +} + +static cacheentry *cache_tlb_lookup(cachetlbentry *tlb, cacheentry *elt, + char *key) +{ + int ix = cache_tlb_hash(key); + int i; + int j; + + for (i=0; i < CACHE_TLB_COLS; ++i) { + j = tlb[ix].t[i]; + if (j < 0) + return NULL; + if (strcmp(elt[j].key, key) == 0) + return &elt[j]; + } + return NULL; +} + +static void cache_tlb_replace(cachetlbentry *tlb, cacheentry *elt, + cacheentry *e) +{ + int ix = cache_tlb_hash(e->key); + int i; + + tlb = &tlb[ix]; + + for (i=1; i < CACHE_TLB_COLS; ++i) + tlb->t[i] = tlb->t[i-1]; + + tlb->t[0] = e - elt; +} + +static void store_cache_string(cache *c, char *res, cacheentry *ce) +{ + int i; + int j; + cachelist *l; + cacheentry *e; + cachetlbentry *t; + int found_list; + + found_list = 0; + /* first try to edit an existing entry */ + for (i = 0; i < c->lists->nelts; i++) { + l = &(((cachelist *)c->lists->elts)[i]); + if (strcmp(l->resource, res) == 0) { + found_list = 1; + + e = cache_tlb_lookup((cachetlbentry *)l->tlb->elts, + (cacheentry *)l->entries->elts, ce->key); + if (e != NULL) { + e->time = ce->time; + e->value = ap_pstrdup(c->pool, ce->value); + return; + } + + for (j = 0; j < l->entries->nelts; j++) { + e = &(((cacheentry *)l->entries->elts)[j]); + if (strcmp(e->key, ce->key) == 0) { + e->time = ce->time; + e->value = ap_pstrdup(c->pool, ce->value); + cache_tlb_replace((cachetlbentry *)l->tlb->elts, + (cacheentry *)l->entries->elts, e); + return; + } + } + } + } + + /* create a needed new list */ + if (!found_list) { + l = ap_push_array(c->lists); + l->resource = ap_pstrdup(c->pool, res); + l->entries = ap_make_array(c->pool, 2, sizeof(cacheentry)); + l->tlb = ap_make_array(c->pool, CACHE_TLB_ROWS, + sizeof(cachetlbentry)); + for (i=0; itlb->elts)[i]; + for (j=0; jt[j] = -1; + } + } + + /* create the new entry */ + for (i = 0; i < c->lists->nelts; i++) { + l = &(((cachelist *)c->lists->elts)[i]); + if (strcmp(l->resource, res) == 0) { + e = ap_push_array(l->entries); + e->time = ce->time; + e->key = ap_pstrdup(c->pool, ce->key); + e->value = ap_pstrdup(c->pool, ce->value); + cache_tlb_replace((cachetlbentry *)l->tlb->elts, + (cacheentry *)l->entries->elts, e); + return; + } + } + + /* not reached, but when it is no problem... */ + return; +} + +static cacheentry *retrieve_cache_string(cache *c, char *res, char *key) +{ + int i; + int j; + cachelist *l; + cacheentry *e; + + for (i = 0; i < c->lists->nelts; i++) { + l = &(((cachelist *)c->lists->elts)[i]); + if (strcmp(l->resource, res) == 0) { + + e = cache_tlb_lookup((cachetlbentry *)l->tlb->elts, + (cacheentry *)l->entries->elts, key); + if (e != NULL) + return e; + + for (j = 0; j < l->entries->nelts; j++) { + e = &(((cacheentry *)l->entries->elts)[j]); + if (strcmp(e->key, key) == 0) { + return e; + } + } + } + } + return NULL; +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | misc functions +** | | +** +-------------------------------------------------------+ +*/ + +static char *subst_prefix_path(request_rec *r, char *input, char *match, + char *subst) +{ + char matchbuf[LONG_STRING_LEN]; + char substbuf[LONG_STRING_LEN]; + char *output; + int l; + + output = input; + + /* first create a match string which always has a trailing slash */ + l = ap_cpystrn(matchbuf, match, sizeof(matchbuf)) - matchbuf; + if (matchbuf[l-1] != '/') { + matchbuf[l] = '/'; + matchbuf[l+1] = '\0'; + l++; + } + /* now compare the prefix */ + if (strncmp(input, matchbuf, l) == 0) { + rewritelog(r, 5, "strip matching prefix: %s -> %s", output, output+l); + output = ap_pstrdup(r->pool, output+l); + + /* and now add the base-URL as replacement prefix */ + l = ap_cpystrn(substbuf, subst, sizeof(substbuf)) - substbuf; + if (substbuf[l-1] != '/') { + substbuf[l] = '/'; + substbuf[l+1] = '\0'; + l++; + } + if (output[0] == '/') { + rewritelog(r, 4, "add subst prefix: %s -> %s%s", + output, substbuf, output+1); + output = ap_pstrcat(r->pool, substbuf, output+1, NULL); + } + else { + rewritelog(r, 4, "add subst prefix: %s -> %s%s", + output, substbuf, output); + output = ap_pstrcat(r->pool, substbuf, output, NULL); + } + } + return output; +} + + +/* +** +** own command line parser which don't have the '\\' problem +** +*/ + +static int parseargline(char *str, char **a1, char **a2, char **a3) +{ + char *cp; + int isquoted; + +#define SKIP_WHITESPACE(cp) \ + for ( ; *cp == ' ' || *cp == '\t'; ) { \ + cp++; \ + }; + +#define CHECK_QUOTATION(cp,isquoted) \ + isquoted = 0; \ + if (*cp == '"') { \ + isquoted = 1; \ + cp++; \ + } + +#define DETERMINE_NEXTSTRING(cp,isquoted) \ + for ( ; *cp != '\0'; cp++) { \ + if ( (isquoted && (*cp == ' ' || *cp == '\t')) \ + || (*cp == '\\' && (*(cp+1) == ' ' || *(cp+1) == '\t'))) { \ + cp++; \ + continue; \ + } \ + if ( (!isquoted && (*cp == ' ' || *cp == '\t')) \ + || (isquoted && *cp == '"') ) { \ + break; \ + } \ + } + + cp = str; + SKIP_WHITESPACE(cp); + + /* determine first argument */ + CHECK_QUOTATION(cp, isquoted); + *a1 = cp; + DETERMINE_NEXTSTRING(cp, isquoted); + if (*cp == '\0') { + return 1; + } + *cp++ = '\0'; + + SKIP_WHITESPACE(cp); + + /* determine second argument */ + CHECK_QUOTATION(cp, isquoted); + *a2 = cp; + DETERMINE_NEXTSTRING(cp, isquoted); + if (*cp == '\0') { + *cp++ = '\0'; + *a3 = NULL; + return 0; + } + *cp++ = '\0'; + + SKIP_WHITESPACE(cp); + + /* again check if there are only two arguments */ + if (*cp == '\0') { + *cp++ = '\0'; + *a3 = NULL; + return 0; + } + + /* determine second argument */ + CHECK_QUOTATION(cp, isquoted); + *a3 = cp; + DETERMINE_NEXTSTRING(cp, isquoted); + *cp++ = '\0'; + + return 0; +} + + +static void add_env_variable(request_rec *r, char *s) +{ + char var[MAX_STRING_LEN]; + char val[MAX_STRING_LEN]; + char *cp; + int n; + + if ((cp = strchr(s, ':')) != NULL) { + n = ((cp-s) > MAX_STRING_LEN-1 ? MAX_STRING_LEN-1 : (cp-s)); + memcpy(var, s, n); + var[n] = '\0'; + ap_cpystrn(val, cp+1, sizeof(val)); + ap_table_set(r->subprocess_env, var, val); + rewritelog(r, 5, "setting env variable '%s' to '%s'", var, val); + } +} + + + +/* +** +** stat() for only the prefix of a path +** +*/ + +static int prefix_stat(const char *path, struct stat *sb) +{ + char curpath[LONG_STRING_LEN]; + char *cp; + + ap_cpystrn(curpath, path, sizeof(curpath)); + if (curpath[0] != '/') { + return 0; + } + if ((cp = strchr(curpath+1, '/')) != NULL) { + *cp = '\0'; + } + if (stat(curpath, sb) == 0) { + return 1; + } + else { + return 0; + } +} + + +/* +** +** File locking +** +*/ + +#ifdef USE_FCNTL +static struct flock lock_it; +static struct flock unlock_it; +#endif + +static void fd_lock(request_rec *r, int fd) +{ + int rc; + +#ifdef USE_FCNTL + lock_it.l_whence = SEEK_SET; /* from current point */ + lock_it.l_start = 0; /* -"- */ + lock_it.l_len = 0; /* until end of file */ + lock_it.l_type = F_WRLCK; /* set exclusive/write lock */ + lock_it.l_pid = 0; /* pid not actually interesting */ + + while ( ((rc = fcntl(fd, F_SETLKW, &lock_it)) < 0) + && (errno == EINTR) ) { + continue; + } +#endif +#ifdef USE_FLOCK + while ( ((rc = flock(fd, LOCK_EX)) < 0) + && (errno == EINTR) ) { + continue; + } +#endif +#ifdef USE_LOCKING + /* Lock the first byte, always, assume we want to append + and seek to the end afterwards */ + lseek(fd, 0, SEEK_SET); + rc = _locking(fd, _LK_LOCK, 1); + lseek(fd, 0, SEEK_END); +#endif + + if (rc < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "mod_rewrite: failed to lock file descriptor"); + exit(1); + } + return; +} + +static void fd_unlock(request_rec *r, int fd) +{ + int rc; + +#ifdef USE_FCNTL + unlock_it.l_whence = SEEK_SET; /* from current point */ + unlock_it.l_start = 0; /* -"- */ + unlock_it.l_len = 0; /* until end of file */ + unlock_it.l_type = F_UNLCK; /* unlock */ + unlock_it.l_pid = 0; /* pid not actually interesting */ + + rc = fcntl(fd, F_SETLKW, &unlock_it); +#endif +#ifdef USE_FLOCK + rc = flock(fd, LOCK_UN); +#endif +#ifdef USE_LOCKING + lseek(fd, 0, SEEK_SET); + rc = _locking(fd, _LK_UNLCK, 1); + lseek(fd, 0, SEEK_END); +#endif + + if (rc < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "mod_rewrite: failed to unlock file descriptor"); + exit(1); + } +} + +/* +** +** Lexicographic Compare +** +*/ + +static int compare_lexicography(char *cpNum1, char *cpNum2) +{ + int i; + int n1, n2; + + n1 = strlen(cpNum1); + n2 = strlen(cpNum2); + if (n1 > n2) { + return 1; + } + if (n1 < n2) { + return -1; + } + for (i = 0; i < n1; i++) { + if (cpNum1[i] > cpNum2[i]) { + return 1; + } + if (cpNum1[i] < cpNum2[i]) { + return -1; + } + } + return 0; +} + + +/*EOF*/ diff --git a/modules/mappers/mod_rewrite.dsp b/modules/mappers/mod_rewrite.dsp new file mode 100644 index 00000000000..4a0ffd1bf68 --- /dev/null +++ b/modules/mappers/mod_rewrite.dsp @@ -0,0 +1,117 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleRewrite" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleRewrite - 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 "ApacheModuleRewrite.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 "ApacheModuleRewrite.mak"\ + CFG="ApacheModuleRewrite - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleRewrite - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleRewrite - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleRewrite - 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 ".\ApacheModuleRewriteR" +# PROP Intermediate_Dir ".\ApacheModuleRewriteR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "NO_DBM_REWRITEMAP" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleRewrite - 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 ".\ApacheModuleRewriteD" +# PROP Intermediate_Dir ".\ApacheModuleRewriteD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "NO_DBM_REWRITEMAP" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleRewrite - Win32 Release" +# Name "ApacheModuleRewrite - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_rewrite.c +# End Source File +# Begin Source File + +SOURCE=.\passwd.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/mappers/mod_rewrite.h b/modules/mappers/mod_rewrite.h new file mode 100644 index 00000000000..22ff3375589 --- /dev/null +++ b/modules/mappers/mod_rewrite.h @@ -0,0 +1,497 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +#ifndef _MOD_REWRITE_H +#define _MOD_REWRITE_H 1 + +/* +** _ _ _ +** _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___ +** | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \ +** | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/ +** |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___| +** |_____| +** +** URL Rewriting Module +** +** This module uses a rule-based rewriting engine (based on a +** regular-expression parser) to rewrite requested URLs on the fly. +** +** It supports an unlimited number of additional rule conditions (which can +** operate on a lot of variables, even on HTTP headers) for granular +** matching and even external database lookups (either via plain text +** tables, DBM hash files or even external processes) for advanced URL +** substitution. +** +** It operates on the full URLs (including the PATH_INFO part) both in +** per-server context (httpd.conf) and per-dir context (.htaccess) and even +** can generate QUERY_STRING parts on result. The rewriting result finally +** can lead to internal subprocessing, external request redirection or even +** to internal proxy throughput. +** +** This module was originally written in April 1996 and +** gifted exclusively to the The Apache Group in July 1997 by +** +** Ralf S. Engelschall +** rse@engelschall.com +** www.engelschall.com +*/ + + + /* Include from the underlaying Unix system ... */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + /* Include from the Apache server ... */ +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_conf_globals.h" +#include "http_request.h" +#include "http_core.h" +#include "http_log.h" +#include "http_vhost.h" + + /* + * The key in the r->notes table wherein we store our accumulated + * Vary values, and the one used for per-condition checks in a chain. + */ +#define VARY_KEY "rewrite-Vary" +#define VARY_KEY_THIS "rewrite-Vary-this" + + /* The NDBM support: + * We support only NDBM files. + * But we have to stat the file for the mtime, + * so we also need to know the file extension + */ +#ifndef NO_DBM_REWRITEMAP +#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) \ + && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 +#include +#else +#include +#endif +#if defined(DBM_SUFFIX) +#define NDBM_FILE_SUFFIX DBM_SUFFIX +#elif defined(__FreeBSD__) || (defined(DB_LOCK) && defined(DB_SHMEM)) +#define NDBM_FILE_SUFFIX ".db" +#else +#define NDBM_FILE_SUFFIX ".pag" +#endif +#endif + + + /* The locking support: + * Try to determine whether we should use fcntl() or flock(). + * Would be better ap_config.h could provide this... :-( + */ +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) +#define USE_FCNTL 1 +#include +#endif +#if defined(USE_FLOCK_SERIALIZED_ACCEPT) +#define USE_FLOCK 1 +#include +#endif +#if !defined(USE_FCNTL) && !defined(USE_FLOCK) +#define USE_FLOCK 1 +#if !defined(MPE) && !defined(WIN32) && !defined(__TANDEM) +#include +#endif +#ifndef LOCK_UN +#undef USE_FLOCK +#define USE_FCNTL 1 +#include +#endif +#endif +#ifdef AIX +#undef USE_FLOCK +#define USE_FCNTL 1 +#include +#endif +#ifdef WIN32 +#undef USE_FCNTL +#define USE_LOCKING +#include +#endif + + +/* +** +** Some defines +** +*/ + +#define ENVVAR_SCRIPT_URL "SCRIPT_URL" +#define ENVVAR_SCRIPT_URI "SCRIPT_URI" + +#ifndef SUPPORT_DBM_REWRITEMAP +#define SUPPORT_DBM_REWRITEMAP 0 +#endif + +#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype" + +#define CONDFLAG_NONE 1<<0 +#define CONDFLAG_NOCASE 1<<1 +#define CONDFLAG_NOTMATCH 1<<2 +#define CONDFLAG_ORNEXT 1<<3 + +#define RULEFLAG_NONE 1<<0 +#define RULEFLAG_FORCEREDIRECT 1<<1 +#define RULEFLAG_LASTRULE 1<<2 +#define RULEFLAG_NEWROUND 1<<3 +#define RULEFLAG_CHAIN 1<<4 +#define RULEFLAG_IGNOREONSUBREQ 1<<5 +#define RULEFLAG_NOTMATCH 1<<6 +#define RULEFLAG_PROXY 1<<7 +#define RULEFLAG_PASSTHROUGH 1<<8 +#define RULEFLAG_FORBIDDEN 1<<9 +#define RULEFLAG_GONE 1<<10 +#define RULEFLAG_QSAPPEND 1<<11 +#define RULEFLAG_NOCASE 1<<12 + +#define MAPTYPE_TXT 1<<0 +#define MAPTYPE_DBM 1<<1 +#define MAPTYPE_PRG 1<<2 +#define MAPTYPE_INT 1<<3 +#define MAPTYPE_RND 1<<4 + +#define ENGINE_DISABLED 1<<0 +#define ENGINE_ENABLED 1<<1 + +#define OPTION_NONE 1<<0 +#define OPTION_INHERIT 1<<1 + +#define CACHEMODE_TS 1<<0 +#define CACHEMODE_TTL 1<<1 + +#define CACHE_TLB_ROWS 1024 +#define CACHE_TLB_COLS 4 + +#ifndef FALSE +#define FALSE 0 +#define TRUE !FALSE +#endif + +#ifndef NO +#define NO FALSE +#define YES TRUE +#endif + +#ifndef RAND_MAX +#define RAND_MAX 32767 +#endif + +#ifndef LONG_STRING_LEN +#define LONG_STRING_LEN 2048 +#endif + +#define MAX_ENV_FLAGS 15 + +#define MAX_NMATCH 10 + +/* +** +** our private data structures we handle with +** +*/ + + /* the list structures for holding the mapfile information + * and the rewrite rules + */ +typedef struct { + char *name; /* the name of the map */ + char *datafile; /* filename for map data files */ + char *checkfile; /* filename to check for map existence */ + int type; /* the type of the map */ + int fpin; /* in file pointer for program maps */ + int fpout; /* out file pointer for program maps */ + int fperr; /* err file pointer for program maps */ + char *(*func)(request_rec *, /* function pointer for internal maps */ + char *); +} rewritemap_entry; + +typedef struct { + char *input; /* Input string of RewriteCond */ + char *pattern; /* the RegExp pattern string */ + regex_t *regexp; + int flags; /* Flags which control the match */ +} rewritecond_entry; + +typedef struct { + array_header *rewriteconds; /* the corresponding RewriteCond entries */ + char *pattern; /* the RegExp pattern string */ + regex_t *regexp; /* the RegExp pattern compilation */ + char *output; /* the Substitution string */ + int flags; /* Flags which control the substitution */ + char *forced_mimetype; /* forced MIME type of substitution */ + int forced_responsecode; /* forced HTTP redirect response status */ + char *env[MAX_ENV_FLAGS+1]; /* added environment variables */ + int skip; /* number of next rules to skip */ +} rewriterule_entry; + + + /* the per-server or per-virtual-server configuration + * statically generated once on startup for every server + */ +typedef struct { + int state; /* the RewriteEngine state */ + int options; /* the RewriteOption state */ + char *rewritelogfile; /* the RewriteLog filename */ + int rewritelogfp; /* the RewriteLog open filepointer */ + int rewriteloglevel; /* the RewriteLog level of verbosity */ + array_header *rewritemaps; /* the RewriteMap entries */ + array_header *rewriteconds; /* the RewriteCond entries (temporary) */ + array_header *rewriterules; /* the RewriteRule entries */ + server_rec *server; /* the corresponding server indicator */ +} rewrite_server_conf; + + + /* the per-directory configuration + * generated on-the-fly by Apache server for current request + */ +typedef struct { + int state; /* the RewriteEngine state */ + int options; /* the RewriteOption state */ + array_header *rewriteconds; /* the RewriteCond entries (temporary) */ + array_header *rewriterules; /* the RewriteRule entries */ + char *directory; /* the directory where it applies */ + char *baseurl; /* the base-URL where it applies */ +} rewrite_perdir_conf; + + + /* the cache structures, + * a 4-way hash table with LRU functionality + */ +typedef struct cacheentry { + time_t time; + char *key; + char *value; +} cacheentry; + +typedef struct tlbentry { + int t[CACHE_TLB_COLS]; +} cachetlbentry; + +typedef struct cachelist { + char *resource; + array_header *entries; + array_header *tlb; +} cachelist; + +typedef struct cache { + pool *pool; + array_header *lists; +} cache; + + + /* the regex structure for the + * substitution of backreferences + */ +typedef struct backrefinfo { + char *source; + int nsub; + regmatch_t regmatch[10]; +} backrefinfo; + + +/* +** +** forward declarations +** +*/ + + /* config structure handling */ +static void *config_server_create(pool *p, server_rec *s); +static void *config_server_merge (pool *p, void *basev, void *overridesv); +static void *config_perdir_create(pool *p, char *path); +static void *config_perdir_merge (pool *p, void *basev, void *overridesv); + + /* config directive handling */ +static const char *cmd_rewriteengine(cmd_parms *cmd, + rewrite_perdir_conf *dconf, int flag); +static const char *cmd_rewriteoptions(cmd_parms *cmd, + rewrite_perdir_conf *dconf, + char *option); +static const char *cmd_rewriteoptions_setoption(pool *p, int *options, + char *name); +static const char *cmd_rewritelog (cmd_parms *cmd, void *dconf, char *a1); +static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, char *a1); +static const char *cmd_rewritemap (cmd_parms *cmd, void *dconf, char *a1, + char *a2); +static const char *cmd_rewritelock(cmd_parms *cmd, void *dconf, char *a1); +static const char *cmd_rewritebase(cmd_parms *cmd, rewrite_perdir_conf *dconf, + char *a1); +static const char *cmd_rewritecond(cmd_parms *cmd, rewrite_perdir_conf *dconf, + char *str); +static const char *cmd_rewritecond_parseflagfield(pool *p, + rewritecond_entry *new, + char *str); +static const char *cmd_rewritecond_setflag(pool *p, rewritecond_entry *cfg, + char *key, char *val); +static const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf, + char *str); +static const char *cmd_rewriterule_parseflagfield(pool *p, + rewriterule_entry *new, + char *str); +static const char *cmd_rewriterule_setflag(pool *p, rewriterule_entry *cfg, + char *key, char *val); + + /* initialisation */ +static void init_module(server_rec *s, pool *p); +static void init_child(server_rec *s, pool *p); + + /* runtime hooks */ +static int hook_uri2file (request_rec *r); +static int hook_mimetype (request_rec *r); +static int hook_fixup (request_rec *r); +static int handler_redirect(request_rec *r); + + /* rewriting engine */ +static int apply_rewrite_list(request_rec *r, array_header *rewriterules, + char *perdir); +static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p, + char *perdir); +static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p, + char *perdir, backrefinfo *briRR, + backrefinfo *briRC); + + /* URI transformation function */ +static void splitout_queryargs(request_rec *r, int qsappend); +static void fully_qualify_uri(request_rec *r); +static void reduce_uri(request_rec *r); +static void expand_backref_inbuffer(pool *p, char *buf, int nbuf, + backrefinfo *bri, char c); +static char *expand_tildepaths(request_rec *r, char *uri); +static void expand_map_lookups(request_rec *r, char *uri, int uri_len); + + /* rewrite map support functions */ +static char *lookup_map(request_rec *r, char *name, char *key); +static char *lookup_map_txtfile(request_rec *r, char *file, char *key); +#ifndef NO_DBM_REWRITEMAP +static char *lookup_map_dbmfile(request_rec *r, char *file, char *key); +#endif +static char *lookup_map_program(request_rec *r, int fpin, + int fpout, char *key); +static char *lookup_map_internal(request_rec *r, + char *(*func)(request_rec *r, char *key), + char *key); +static char *rewrite_mapfunc_toupper(request_rec *r, char *key); +static char *rewrite_mapfunc_tolower(request_rec *r, char *key); +static char *rewrite_mapfunc_escape(request_rec *r, char *key); +static char *rewrite_mapfunc_unescape(request_rec *r, char *key); +static char *select_random_value_part(request_rec *r, char *value); +static void rewrite_rand_init(void); +static int rewrite_rand(int l, int h); + + /* rewriting logfile support */ +static void open_rewritelog(server_rec *s, pool *p); +static void rewritelog(request_rec *r, int level, const char *text, ...) + __attribute__((format(printf,3,4))); +static char *current_logtime(request_rec *r); + + /* rewriting lockfile support */ +static void rewritelock_create(server_rec *s, pool *p); +static void rewritelock_open(server_rec *s, pool *p); +static void rewritelock_remove(void *data); +static void rewritelock_alloc(request_rec *r); +static void rewritelock_free(request_rec *r); + + /* program map support */ +static void run_rewritemap_programs(server_rec *s, pool *p); +static int rewritemap_program_child(void *cmd, child_info *pinfo); + + /* env variable support */ +static void expand_variables_inbuffer(request_rec *r, char *buf, int buf_len); +static char *expand_variables(request_rec *r, char *str); +static char *lookup_variable(request_rec *r, char *var); +static char *lookup_header(request_rec *r, const char *name); + + /* caching functions */ +static cache *init_cache(pool *p); +static char *get_cache_string(cache *c, char *res, int mode, time_t mtime, + char *key); +static void set_cache_string(cache *c, char *res, int mode, time_t mtime, + char *key, char *value); +static cacheentry *retrieve_cache_string(cache *c, char *res, char *key); +static void store_cache_string(cache *c, char *res, cacheentry *ce); + + /* misc functions */ +static char *subst_prefix_path(request_rec *r, char *input, char *match, + char *subst); +static int parseargline(char *str, char **a1, char **a2, char **a3); +static int prefix_stat(const char *path, struct stat *sb); +static void add_env_variable(request_rec *r, char *s); + + /* File locking */ +static void fd_lock(request_rec *r, int fd); +static void fd_unlock(request_rec *r, int fd); + + /* Lexicographic Comparison */ +static int compare_lexicography(char *cpNum1, char *cpNum2); + +#endif /* _MOD_REWRITE_H */ + +/*EOF*/ diff --git a/modules/mappers/mod_so.c b/modules/mappers/mod_so.c new file mode 100644 index 00000000000..0f0b63d37c6 --- /dev/null +++ b/modules/mappers/mod_so.c @@ -0,0 +1,360 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * This module is used to load Apache modules at runtime. This means that the + * server functionality can be extended without recompiling and even without + * taking the server down at all. Only a HUP or USR1 signal needs to be send + * to the server to reload the dynamically loaded modules. + * + * To use, you'll first need to build your module as a shared library, then + * update your configuration (httpd.conf) to get the Apache core to load the + * module at start-up. + * + * The easiest way to build a module as a shared library is to use the + * `SharedModule' command in the Configuration file, instead of `AddModule'. + * You should also change the file extension from `.o' to `.so'. So, for + * example, to build the status module as a shared library edit Configuration + * and change + * AddModule modules/standard/mod_status.o + * to + * SharedModule modules/standard/mod_status.so + * + * Run Configure and make. Now Apache's httpd binary will _not_ include + * mod_status. Instead a shared object called mod_status.so will be build, in + * the modules/standard directory. You can build most of the modules as shared + * libraries like this. + * + * To use the shared module, move the .so file(s) into an appropriate + * directory. You might like to create a directory called "modules" under you + * server root for this (e.g. /usr/local/httpd/modules). + * + * Then edit your conf/httpd.conf file, and add LoadModule lines. For + * example + * LoadModule status_module modules/mod_status.so + * + * The first argument is the module's structure name (look at the end of the + * module source to find this). The second option is the path to the module + * file, relative to the server root. Put these directives right at the top + * of your httpd.conf file. + * + * Now you can start Apache. A message will be logged at "debug" level to your + * error_log to confirm that the module(s) are loaded (use "LogLevel debug" + * directive to get these log messages). + * + * If you edit the LoadModule directives while the server is live you can get + * Apache to re-load the modules by sending it a HUP or USR1 signal as normal. + * You can use this to dynamically change the capability of your server + * without bringing it down. + * + * Because currently there is only limited built-in support in the Configure + * script for creating the shared library files (`.so'), please consult your + * vendors cc(1), ld(1) and dlopen(3) manpages to find out the appropriate + * compiler and linker flags and insert them manually into the Configuration + * file under CFLAGS_SHLIB, LDFLAGS_SHLIB and LDFLAGS_SHLIB_EXPORT. + * + * If you still have problems figuring out the flags both try the paper + * http://developer.netscape.com/library/documentation/enterprise + * /unix/svrplug.htm#1013807 + * or install a Perl 5 interpreter on your platform and then run the command + * + * $ perl -V:usedl -V:ccdlflags -V:cccdlflags -V:lddlflags + * + * This gives you what type of dynamic loading Perl 5 uses on your platform + * and which compiler and linker flags Perl 5 uses to create the shared object + * files. + * + * Another location where you can find useful hints is the `ltconfig' script + * of the GNU libtool 1.2 package. Search for your platform name inside the + * various "case" constructs. + * + */ + + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" + +module MODULE_VAR_EXPORT so_module; + + +/* + * Server configuration to keep track of actually + * loaded modules and the corresponding module name. + */ + +typedef struct moduleinfo { + char *name; + module *modp; +} moduleinfo; + +typedef struct so_server_conf { + array_header *loaded_modules; +} so_server_conf; + +static void *so_sconf_create(pool *p, server_rec *s) +{ + so_server_conf *soc; + + soc = (so_server_conf *)ap_pcalloc(p, sizeof(so_server_conf)); + soc->loaded_modules = ap_make_array(p, DYNAMIC_MODULE_LIMIT, + sizeof(moduleinfo)); +#ifndef NO_DLOPEN + ap_os_dso_init(); +#endif + + return (void *)soc; +} + +#ifndef NO_DLOPEN + +/* + * This is the cleanup for a loaded shared object. It unloads the module. + * This is called as a cleanup function from the core. + */ + +static void unload_module(moduleinfo *modi) +{ + /* only unload if module information is still existing */ + if (modi->modp == NULL) + return; + + /* remove the module pointer from the core structure */ + ap_remove_loaded_module(modi->modp); + + /* unload the module space itself */ + ap_os_dso_unload((ap_os_dso_handle_t)modi->modp->dynamic_load_handle); + + /* destroy the module information */ + modi->modp = NULL; + modi->name = NULL; +} + +/* + * This is the cleanup routine for files loaded by + * load_file(). Unfortunately we don't keep a record of the filename + * that was loaded, so we can't report the unload for debug purposes + * or include the filename in error message. + */ + +static void unload_file(void *handle) +{ + ap_os_dso_unload((ap_os_dso_handle_t)handle); +} + +/* + * This is called for the directive LoadModule and actually loads + * a shared object file into the address space of the server process. + */ + +static const char *load_module(cmd_parms *cmd, void *dummy, + char *modname, char *filename) +{ + ap_os_dso_handle_t modhandle; + module *modp; + const char *szModuleFile=ap_server_root_relative(cmd->pool, filename); + so_server_conf *sconf; + moduleinfo *modi; + moduleinfo *modie; + int i; + + /* + * check for already existing module + * If it already exists, we have nothing to do + */ + sconf = (so_server_conf *)ap_get_module_config(cmd->server->module_config, + &so_module); + modie = (moduleinfo *)sconf->loaded_modules->elts; + for (i = 0; i < sconf->loaded_modules->nelts; i++) { + modi = &modie[i]; + if (modi->name != NULL && strcmp(modi->name, modname) == 0) + return NULL; + } + modi = ap_push_array(sconf->loaded_modules); + modi->name = modname; + + /* + * Load the file into the Apache address space + */ + if (!(modhandle = ap_os_dso_load(szModuleFile))) { + const char *my_error = ap_os_dso_error(); + return ap_pstrcat (cmd->pool, "Cannot load ", szModuleFile, + " into server: ", + my_error ? my_error : "(reason unknown)", + NULL); + } + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, + "loaded module %s", modname); + + /* + * Retrieve the pointer to the module structure through the module name: + * First with the hidden variant (prefix `AP_') and then with the plain + * symbol name. + */ + if (!(modp = (module *)(ap_os_dso_sym(modhandle, modname)))) { + return ap_pstrcat(cmd->pool, "Can't locate API module structure `", modname, + "' in file ", szModuleFile, ": ", ap_os_dso_error(), NULL); + } + modi->modp = modp; + modp->dynamic_load_handle = (void *)modhandle; + + /* + * Make sure the found module structure is really a module structure + * + */ + if (modp->magic != MODULE_MAGIC_COOKIE) { + return ap_pstrcat(cmd->pool, "API module structure `", modname, + "' in file ", szModuleFile, " is garbled -" + " perhaps this is not an Apache module DSO?", NULL); + } + + /* + * Add this module to the Apache core structures + */ + ap_add_loaded_module(modp); + + /* + * Register a cleanup in the config pool (normally pconf). When + * we do a restart (or shutdown) this cleanup will cause the + * shared object to be unloaded. + */ + ap_register_cleanup(cmd->pool, modi, + (void (*)(void*))unload_module, ap_null_cleanup); + + /* + * Finally we need to run the configuration process for the module + */ + ap_single_module_configure(cmd->pool, cmd->server, modp); + + return NULL; +} + +/* + * This implements the LoadFile directive and loads an arbitrary + * shared object file into the adress space of the server process. + */ + +static const char *load_file(cmd_parms *cmd, void *dummy, char *filename) +{ + ap_os_dso_handle_t handle; + char *file; + + file = ap_server_root_relative(cmd->pool, filename); + + if (!(handle = ap_os_dso_load(file))) { + const char *my_error = ap_os_dso_error(); + return ap_pstrcat (cmd->pool, "Cannot load ", filename, + " into server:", + my_error ? my_error : "(reason unknown)", + NULL); + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, + "loaded file %s", filename); + + ap_register_cleanup(cmd->pool, (void *)handle, unload_file, ap_null_cleanup); + + return NULL; +} + +#else /* not NO_DLOPEN */ + +static const char *load_file(cmd_parms *cmd, void *dummy, char *filename) +{ + fprintf(stderr, "WARNING: LoadFile not supported on this platform\n"); + return NULL; +} + +static const char *load_module(cmd_parms *cmd, void *dummy, + char *modname, char *filename) +{ + fprintf(stderr, "WARNING: LoadModule not supported on this platform\n"); + return NULL; +} + +#endif /* NO_DLOPEN */ + +static const command_rec so_cmds[] = { + { "LoadModule", load_module, NULL, RSRC_CONF, TAKE2, + "a module name and the name of a shared object file to load it from"}, + { "LoadFile", load_file, NULL, RSRC_CONF, ITERATE, + "shared object file or library to load into the server at runtime"}, + { NULL } +}; + +module MODULE_VAR_EXPORT so_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + so_sconf_create, /* server config */ + NULL, /* merge server config */ + so_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixer_upper */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/mappers/mod_speling.c b/modules/mappers/mod_speling.c new file mode 100644 index 00000000000..067fd77c719 --- /dev/null +++ b/modules/mappers/mod_speling.c @@ -0,0 +1,558 @@ +#define WANT_BASENAME_MATCH +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#include "httpd.h" +#include "http_core.h" +#include "http_config.h" +#include "http_log.h" + +/* mod_speling.c - by Alexei Kosut June, 1996 + * + * This module is transparent, and simple. It attempts to correct + * misspellings of URLs that users might have entered, namely by checking + * capitalizations. If it finds a match, it sends a redirect. + * + * 08-Aug-1997 + * o Upgraded module interface to apache_1.3a2-dev API (more NULL's in + * speling_module). + * o Integrated tcsh's "spelling correction" routine which allows one + * misspelling (character insertion/omission/typo/transposition). + * Rewrote it to ignore case as well. This ought to catch the majority + * of misspelled requests. + * o Commented out the second pass where files' suffixes are stripped. + * Given the better hit rate of the first pass, this rather ugly + * (request index.html, receive index.db ?!?!) solution can be + * omitted. + * o wrote a "kind of" html page for mod_speling + * + * Activate it with "CheckSpelling On" + */ + +MODULE_VAR_EXPORT module speling_module; + +typedef struct { + int enabled; +} spconfig; + +/* + * Create a configuration specific to this module for a server or directory + * location, and fill it with the default settings. + * + * The API says that in the absence of a merge function, the record for the + * closest ancestor is used exclusively. That's what we want, so we don't + * bother to have such a function. + */ + +static void *mkconfig(pool *p) +{ + spconfig *cfg = ap_pcalloc(p, sizeof(spconfig)); + + cfg->enabled = 0; + return cfg; +} + +/* + * Respond to a callback to create configuration record for a server or + * vhost environment. + */ +static void *create_mconfig_for_server(pool *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(pool *p, char *dir) +{ + return mkconfig(p); +} + +/* + * Handler for the CheckSpelling directive, which is FLAG. + */ +static const char *set_speling(cmd_parms *cmd, void *mconfig, int arg) +{ + spconfig *cfg = (spconfig *) mconfig; + + cfg->enabled = arg; + return NULL; +} + +/* + * Define the directives specific to this module. This structure is referenced + * later by the 'module' structure. + */ +static const command_rec speling_cmds[] = +{ + { "CheckSpelling", set_speling, NULL, OR_OPTIONS, FLAG, + "whether or not to fix miscapitalized/misspelled requests" }, + { NULL } +}; + +typedef enum { + SP_IDENTICAL = 0, + SP_MISCAPITALIZED = 1, + SP_TRANSPOSITION = 2, + SP_MISSINGCHAR = 3, + SP_EXTRACHAR = 4, + SP_SIMPLETYPO = 5, + SP_VERYDIFFERENT = 6 +} sp_reason; + +static const char *sp_reason_str[] = +{ + "identical", + "miscapitalized", + "transposed characters", + "character missing", + "extra character", + "mistyped character", + "common basename", +}; + +typedef struct { + const char *name; + sp_reason quality; +} misspelled_file; + +/* + * spdist() is taken from Kernighan & Pike, + * _The_UNIX_Programming_Environment_ + * and adapted somewhat to correspond better to psychological reality. + * (Note the changes to the return values) + * + * According to Pollock and Zamora, CACM April 1984 (V. 27, No. 4), + * page 363, the correct order for this is: + * OMISSION = TRANSPOSITION > INSERTION > SUBSTITUTION + * thus, it was exactly backwards in the old version. -- PWP + * + * This routine was taken out of tcsh's spelling correction code + * (tcsh-6.07.04) and re-converted to apache data types ("char" type + * instead of tcsh's NLS'ed "Char"). Plus it now ignores the case + * during comparisons, so is a "approximate strcasecmp()". + * NOTE that is still allows only _one_ real "typo", + * it does NOT try to correct multiple errors. + */ + +static sp_reason spdist(const char *s, const char *t) +{ + for (; ap_tolower(*s) == ap_tolower(*t); t++, s++) { + if (*t == '\0') { + return SP_MISCAPITALIZED; /* exact match (sans case) */ + } + } + if (*s) { + if (*t) { + if (s[1] && t[1] && ap_tolower(*s) == ap_tolower(t[1]) + && ap_tolower(*t) == ap_tolower(s[1]) + && strcasecmp(s + 2, t + 2) == 0) { + return SP_TRANSPOSITION; /* transposition */ + } + if (strcasecmp(s + 1, t + 1) == 0) { + return SP_SIMPLETYPO; /* 1 char mismatch */ + } + } + if (strcasecmp(s + 1, t) == 0) { + return SP_EXTRACHAR; /* extra character */ + } + } + if (*t && strcasecmp(s, t + 1) == 0) { + return SP_MISSINGCHAR; /* missing character */ + } + return SP_VERYDIFFERENT; /* distance too large to fix. */ +} + +static int sort_by_quality(const void *left, const void *rite) +{ + return (int) (((misspelled_file *) left)->quality) + - (int) (((misspelled_file *) rite)->quality); +} + +static int check_speling(request_rec *r) +{ + spconfig *cfg; + char *good, *bad, *postgood, *url; + int filoc, dotloc, urlen, pglen; + DIR *dirp; + struct DIR_TYPE *dir_entry; + array_header *candidates = NULL; + + cfg = ap_get_module_config(r->per_dir_config, &speling_module); + if (!cfg->enabled) { + return DECLINED; + } + + /* We only want to worry about GETs */ + if (r->method_number != M_GET) { + return DECLINED; + } + + /* We've already got a file of some kind or another */ + if (r->proxyreq || (r->finfo.st_mode != 0)) { + return DECLINED; + } + + /* This is a sub request - don't mess with it */ + if (r->main) { + return DECLINED; + } + + /* + * The request should end up looking like this: + * r->uri: /correct-url/mispelling/more + * r->filename: /correct-file/mispelling r->path_info: /more + * + * So we do this in steps. First break r->filename into two pieces + */ + + filoc = ap_rind(r->filename, '/'); + /* + * Don't do anything if the request doesn't contain a slash, or + * requests "/" + */ + if (filoc == -1 || strcmp(r->uri, "/") == 0) { + return DECLINED; + } + + /* good = /correct-file */ + good = ap_pstrndup(r->pool, r->filename, filoc); + /* bad = mispelling */ + bad = ap_pstrdup(r->pool, r->filename + filoc + 1); + /* postgood = mispelling/more */ + postgood = ap_pstrcat(r->pool, bad, r->path_info, NULL); + + urlen = strlen(r->uri); + pglen = strlen(postgood); + + /* Check to see if the URL pieces add up */ + if (strcmp(postgood, r->uri + (urlen - pglen))) { + return DECLINED; + } + + /* url = /correct-url */ + url = ap_pstrndup(r->pool, r->uri, (urlen - pglen)); + + /* Now open the directory and do ourselves a check... */ + dirp = ap_popendir(r->pool, good); + if (dirp == NULL) { /* Oops, not a directory... */ + return DECLINED; + } + + candidates = ap_make_array(r->pool, 2, sizeof(misspelled_file)); + + dotloc = ap_ind(bad, '.'); + if (dotloc == -1) { + dotloc = strlen(bad); + } + + while ((dir_entry = readdir(dirp)) != NULL) { + sp_reason q; + + /* + * If we end up with a "fixed" URL which is identical to the + * requested one, we must have found a broken symlink or some such. + * Do _not_ try to redirect this, it causes a loop! + */ + if (strcmp(bad, dir_entry->d_name) == 0) { + ap_pclosedir(r->pool, dirp); + return OK; + } + /* + * miscapitalization errors are checked first (like, e.g., lower case + * file, upper case request) + */ + else if (strcasecmp(bad, dir_entry->d_name) == 0) { + misspelled_file *sp_new; + + sp_new = (misspelled_file *) ap_push_array(candidates); + sp_new->name = ap_pstrdup(r->pool, dir_entry->d_name); + sp_new->quality = SP_MISCAPITALIZED; + } + /* + * simple typing errors are checked next (like, e.g., + * missing/extra/transposed char) + */ + else if ((q = spdist(bad, dir_entry->d_name)) != SP_VERYDIFFERENT) { + misspelled_file *sp_new; + + sp_new = (misspelled_file *) ap_push_array(candidates); + sp_new->name = ap_pstrdup(r->pool, dir_entry->d_name); + sp_new->quality = q; + } + /* + * The spdist() should have found the majority of the misspelled + * requests. It is of questionable use to continue looking for + * files with the same base name, but potentially of totally wrong + * type (index.html <-> index.db). + * I would propose to not set the WANT_BASENAME_MATCH define. + * 08-Aug-1997 + * + * However, Alexei replied giving some reasons to add it anyway: + * > Oh, by the way, I remembered why having the + * > extension-stripping-and-matching stuff is a good idea: + * > + * > If you're using MultiViews, and have a file named foobar.html, + * > which you refer to as "foobar", and someone tried to access + * > "Foobar", mod_speling won't find it, because it won't find + * > anything matching that spelling. With the extension-munging, + * > it would locate "foobar.html". Not perfect, but I ran into + * > that problem when I first wrote the module. + */ + else { +#ifdef WANT_BASENAME_MATCH + /* + * Okay... we didn't find anything. Now we take out the hard-core + * power tools. There are several cases here. Someone might have + * entered a wrong extension (.htm instead of .html or vice + * versa) or the document could be negotiated. At any rate, now + * we just compare stuff before the first dot. If it matches, we + * figure we got us a match. This can result in wrong things if + * there are files of different content types but the same prefix + * (e.g. foo.gif and foo.html) This code will pick the first one + * it finds. Better than a Not Found, though. + */ + int entloc = ap_ind(dir_entry->d_name, '.'); + if (entloc == -1) { + entloc = strlen(dir_entry->d_name); + } + + if ((dotloc == entloc) + && !strncasecmp(bad, dir_entry->d_name, dotloc)) { + misspelled_file *sp_new; + + sp_new = (misspelled_file *) ap_push_array(candidates); + sp_new->name = ap_pstrdup(r->pool, dir_entry->d_name); + sp_new->quality = SP_VERYDIFFERENT; + } +#endif + } + } + ap_pclosedir(r->pool, dirp); + + if (candidates->nelts != 0) { + /* Wow... we found us a mispelling. Construct a fixed url */ + char *nuri; + const char *ref; + misspelled_file *variant = (misspelled_file *) candidates->elts; + int i; + + ref = ap_table_get(r->headers_in, "Referer"); + + qsort((void *) candidates->elts, candidates->nelts, + sizeof(misspelled_file), sort_by_quality); + + /* + * Conditions for immediate redirection: + * a) the first candidate was not found by stripping the suffix + * AND b) there exists only one candidate OR the best match is not + * ambiguous + * then return a redirection right away. + */ + if (variant[0].quality != SP_VERYDIFFERENT + && (candidates->nelts == 1 + || variant[0].quality != variant[1].quality)) { + + nuri = ap_pstrcat(r->pool, url, variant[0].name, r->path_info, + r->parsed_uri.query ? "?" : "", + r->parsed_uri.query ? r->parsed_uri.query : "", + NULL); + + ap_table_setn(r->headers_out, "Location", + ap_construct_url(r->pool, nuri, r)); + + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, r, + ref ? "Fixed spelling: %s to %s from %s" + : "Fixed spelling: %s to %s", + r->uri, nuri, ref); + + return HTTP_MOVED_PERMANENTLY; + } + /* + * Otherwise, a "[300] Multiple Choices" list with the variants is + * returned. + */ + else { + pool *p; + table *notes; + pool *sub_pool; + array_header *t; + array_header *v; + + + if (r->main == NULL) { + p = r->pool; + notes = r->notes; + } + else { + p = r->main->pool; + notes = r->main->notes; + } + + sub_pool = ap_make_sub_pool(p); + t = ap_make_array(sub_pool, candidates->nelts * 8 + 8, + sizeof(char *)); + v = ap_make_array(sub_pool, candidates->nelts * 5, + sizeof(char *)); + + /* Generate the response text. */ + + *(const char **)ap_push_array(t) = + "The document name you requested ("; + *(const char **)ap_push_array(t) = r->uri; + *(const char **)ap_push_array(t) = + ") could not be found on this server.\n" + "However, we found documents with names similar " + "to the one you requested.

" + "Available documents:\n

    \n"; + + for (i = 0; i < candidates->nelts; ++i) { + char *vuri; + const char *reason; + + reason = sp_reason_str[(int) (variant[i].quality)]; + /* The format isn't very neat... */ + vuri = ap_pstrcat(sub_pool, url, variant[i].name, r->path_info, + (r->parsed_uri.query != NULL) ? "?" : "", + (r->parsed_uri.query != NULL) + ? r->parsed_uri.query : "", + NULL); + *(const char **)ap_push_array(v) = "\""; + *(const char **)ap_push_array(v) = vuri; + *(const char **)ap_push_array(v) = "\";\""; + *(const char **)ap_push_array(v) = reason; + *(const char **)ap_push_array(v) = "\""; + + *(const char **)ap_push_array(t) = "
  • "; + *(const char **)ap_push_array(t) = vuri; + *(const char **)ap_push_array(t) = " ("; + *(const char **)ap_push_array(t) = reason; + *(const char **)ap_push_array(t) = ")\n"; + + /* + * when we have printed the "close matches" and there are + * more "distant matches" (matched by stripping the suffix), + * then we insert an additional separator text to suggest + * that the user LOOK CLOSELY whether these are really the + * files she wanted. + */ + if (i > 0 && i < candidates->nelts - 1 + && variant[i].quality != SP_VERYDIFFERENT + && variant[i + 1].quality == SP_VERYDIFFERENT) { + *(const char **)ap_push_array(t) = + "
\nFurthermore, the following related " + "documents were found:\n
    \n"; + } + } + *(const char **)ap_push_array(t) = "
\n"; + + /* If we know there was a referring page, add a note: */ + if (ref != NULL) { + *(const char **)ap_push_array(t) = + "Please consider informing the owner of the " + "referring page " + "about the broken link.\n"; + } + + + /* Pass our table to http_protocol.c (see mod_negotiation): */ + ap_table_setn(notes, "variant-list", ap_array_pstrcat(p, t, 0)); + + ap_table_mergen(r->subprocess_env, "VARIANTS", + ap_array_pstrcat(p, v, ',')); + + ap_destroy_pool(sub_pool); + + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, r, + ref ? "Spelling fix: %s: %d candidates from %s" + : "Spelling fix: %s: %d candidates", + r->uri, candidates->nelts, ref); + + return HTTP_MULTIPLE_CHOICES; + } + } + + return OK; +} + +module MODULE_VAR_EXPORT speling_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_mconfig_for_directory, /* create per-dir config */ + NULL, /* merge per-dir config */ + create_mconfig_for_server, /* server config */ + NULL, /* merge server config */ + speling_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + check_speling, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/mappers/mod_speling.dsp b/modules/mappers/mod_speling.dsp new file mode 100644 index 00000000000..499a72f3f72 --- /dev/null +++ b/modules/mappers/mod_speling.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleSpeling" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleSpeling - 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 "ApacheModuleSpeling.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 "ApacheModuleSpeling.mak"\ + CFG="ApacheModuleSpeling - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleSpeling - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleSpeling - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleSpeling - 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 ".\ApacheModuleSpelingR" +# PROP Intermediate_Dir ".\ApacheModuleSpelingR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleSpeling - 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 ".\ApacheModuleSpelingD" +# PROP Intermediate_Dir ".\ApacheModuleSpelingD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleSpeling - Win32 Release" +# Name "ApacheModuleSpeling - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_speling.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/mappers/mod_userdir.c b/modules/mappers/mod_userdir.c new file mode 100644 index 00000000000..5c0e26d2753 --- /dev/null +++ b/modules/mappers/mod_userdir.c @@ -0,0 +1,349 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_userdir... implement the UserDir command. Broken away from the + * Alias stuff for a couple of good and not-so-good reasons: + * + * 1) It shows a real minimal working example of how to do something like + * this. + * 2) I know people who are actually interested in changing this *particular* + * aspect of server functionality without changing the rest of it. That's + * what this whole modular arrangement is supposed to be good at... + * + * Modified by Alexei Kosut to support the following constructs + * (server running at www.foo.com, request for /~bar/one/two.html) + * + * UserDir public_html -> ~bar/public_html/one/two.html + * UserDir /usr/web -> /usr/web/bar/one/two.html + * UserDir /home/ * /www -> /home/bar/www/one/two.html + * NOTE: theses ^ ^ space only added allow it to work in a comment, ignore + * UserDir http://x/users -> (302) http://x/users/bar/one/two.html + * UserDir http://x/ * /y -> (302) http://x/bar/y/one/two.html + * NOTE: here also ^ ^ + * + * In addition, you can use multiple entries, to specify alternate + * user directories (a la Directory Index). For example: + * + * UserDir public_html /usr/web http://www.xyz.com/users + * + * Modified by Ken Coar to provide for the following: + * + * UserDir disable[d] username ... + * UserDir enable[d] username ... + * + * If "disabled" has no other arguments, *all* ~ references are + * disabled, except those explicitly turned on with the "enabled" keyword. + */ + +#include "httpd.h" +#include "http_config.h" + +module userdir_module; + +typedef struct userdir_config { + int globally_disabled; + char *userdir; + table *enabled_users; + table *disabled_users; +} userdir_config; + +/* + * Server config for this module: global disablement flag, a list of usernames + * ineligible for UserDir access, a list of those immune to global (but not + * explicit) disablement, and the replacement string for all others. + */ + +static void *create_userdir_config(pool *p, server_rec *s) +{ + userdir_config + * newcfg = (userdir_config *) ap_pcalloc(p, sizeof(userdir_config)); + + newcfg->globally_disabled = 0; + newcfg->userdir = DEFAULT_USER_DIR; + newcfg->enabled_users = ap_make_table(p, 4); + newcfg->disabled_users = ap_make_table(p, 4); + return (void *) newcfg; +} + +#define O_DEFAULT 0 +#define O_ENABLE 1 +#define O_DISABLE 2 + +static const char *set_user_dir(cmd_parms *cmd, void *dummy, char *arg) +{ + userdir_config + * s_cfg = (userdir_config *) ap_get_module_config + ( + cmd->server->module_config, + &userdir_module + ); + char *username; + const char + *usernames = arg; + char *kw = ap_getword_conf(cmd->pool, &usernames); + table *usertable; + + /* + * Let's do the comparisons once. + */ + if ((!strcasecmp(kw, "disable")) || (!strcasecmp(kw, "disabled"))) { + /* + * If there are no usernames specified, this is a global disable - we + * need do no more at this point than record the fact. + */ + if (strlen(usernames) == 0) { + s_cfg->globally_disabled = 1; + return NULL; + } + usertable = s_cfg->disabled_users; + } + else if ((!strcasecmp(kw, "enable")) || (!strcasecmp(kw, "enabled"))) { + /* + * The "disable" keyword can stand alone or take a list of names, but + * the "enable" keyword requires the list. Whinge if it doesn't have + * it. + */ + if (strlen(usernames) == 0) { + return "UserDir \"enable\" keyword requires a list of usernames"; + } + usertable = s_cfg->enabled_users; + } + else { + /* + * If the first (only?) value isn't one of our keywords, just copy + * the string to the userdir string. + */ + s_cfg->userdir = ap_pstrdup(cmd->pool, arg); + return NULL; + } + /* + * Now we just take each word in turn from the command line and add it to + * the appropriate table. + */ + while (*usernames) { + username = ap_getword_conf(cmd->pool, &usernames); + ap_table_setn(usertable, username, kw); + } + return NULL; +} + +static const command_rec userdir_cmds[] = { + {"UserDir", set_user_dir, NULL, RSRC_CONF, RAW_ARGS, + "the public subdirectory in users' home directories, or 'disabled', or 'disabled username username...', or 'enabled username username...'"}, + {NULL} +}; + +static int translate_userdir(request_rec *r) +{ + void *server_conf = r->server->module_config; + const userdir_config *s_cfg = + (userdir_config *) ap_get_module_config(server_conf, &userdir_module); + char *name = r->uri; + const char *userdirs = s_cfg->userdir; + const char *w, *dname; + char *redirect; + char *x = NULL; + struct stat statbuf; + + /* + * If the URI doesn't match our basic pattern, we've nothing to do with + * it. + */ + if ( + (s_cfg->userdir == NULL) || + (name[0] != '/') || + (name[1] != '~') + ) { + return DECLINED; + } + + dname = name + 2; + w = ap_getword(r->pool, &dname, '/'); + + /* + * The 'dname' funny business involves backing it up to capture the '/' + * delimiting the "/~user" part from the rest of the URL, in case there + * was one (the case where there wasn't being just "GET /~user HTTP/1.0", + * for which we don't want to tack on a '/' onto the filename). + */ + + if (dname[-1] == '/') { + --dname; + } + + /* + * If there's no username, it's not for us. Ignore . and .. as well. + */ + if (w[0] == '\0' || (w[1] == '.' && (w[2] == '\0' || (w[2] == '.' && w[3] == '\0')))) { + return DECLINED; + } + /* + * Nor if there's an username but it's in the disabled list. + */ + if (ap_table_get(s_cfg->disabled_users, w) != NULL) { + return DECLINED; + } + /* + * If there's a global interdiction on UserDirs, check to see if this + * name is one of the Blessed. + */ + if ( + s_cfg->globally_disabled && + (ap_table_get(s_cfg->enabled_users, w) == NULL) + ) { + return DECLINED; + } + + /* + * Special cases all checked, onward to normal substitution processing. + */ + + while (*userdirs) { + const char *userdir = ap_getword_conf(r->pool, &userdirs); + char *filename = NULL; + + if (strchr(userdir, '*')) + x = ap_getword(r->pool, &userdir, '*'); + + if (userdir[0] == '\0' || ap_os_is_path_absolute(userdir)) { + if (x) { +#ifdef HAVE_DRIVE_LETTERS + /* + * Crummy hack. Need to figure out whether we have been + * redirected to a URL or to a file on some drive. Since I + * know of no protocols that are a single letter, if the : is + * the second character, I will assume a file was specified + */ + if (strchr(x + 2, ':')) +#else + if (strchr(x, ':')) +#endif /* WIN32 */ + { + redirect = ap_pstrcat(r->pool, x, w, userdir, dname, NULL); + ap_table_setn(r->headers_out, "Location", redirect); + return REDIRECT; + } + else + filename = ap_pstrcat(r->pool, x, w, userdir, NULL); + } + else + filename = ap_pstrcat(r->pool, userdir, "/", w, NULL); + } + else if (strchr(userdir, ':')) { + redirect = ap_pstrcat(r->pool, userdir, "/", w, dname, NULL); + ap_table_setn(r->headers_out, "Location", redirect); + return REDIRECT; + } + else { +#ifdef WIN32 + /* Need to figure out home dirs on NT */ + return DECLINED; +#else /* WIN32 */ + struct passwd *pw; + if ((pw = getpwnam(w))) { +#ifdef OS2 + /* Need to manually add user name for OS/2 */ + filename = ap_pstrcat(r->pool, pw->pw_dir, w, "/", userdir, NULL); +#else + filename = ap_pstrcat(r->pool, pw->pw_dir, "/", userdir, NULL); +#endif + } +#endif /* WIN32 */ + } + + /* + * Now see if it exists, or we're at the last entry. If we are at the + * last entry, then use the filename generated (if there is one) + * anyway, in the hope that some handler might handle it. This can be + * used, for example, to run a CGI script for the user. + */ + if (filename && (!*userdirs || stat(filename, &statbuf) != -1)) { + r->filename = ap_pstrcat(r->pool, filename, dname, NULL); + /* when statbuf contains info on r->filename we can save a syscall + * by copying it to r->finfo + */ + if (*userdirs && dname[0] == 0) + r->finfo = statbuf; + return OK; + } + } + + return DECLINED; +} + +module userdir_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_userdir_config, /* server config */ + NULL, /* merge server config */ + userdir_cmds, /* command table */ + NULL, /* handlers */ + translate_userdir, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/mappers/mod_vhost_alias.c b/modules/mappers/mod_vhost_alias.c new file mode 100644 index 00000000000..65cc5a2ef41 --- /dev/null +++ b/modules/mappers/mod_vhost_alias.c @@ -0,0 +1,482 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_vhost_alias.c: support for dynamically configured mass virtual hosting + * + * Copyright (c) 1998-1999 Demon Internet Ltd. + * + * This software was submitted by Demon Internet to the Apache Group + * in May 1999. Future revisions and derivatives of this source code + * must acknowledge Demon Internet as the original contributor of + * this module. All other licensing and usage conditions are those + * of the Apache Group. + * + * Originally written by Tony Finch . + * + * Implementation ideas were taken from mod_alias.c. The overall + * concept is derived from the OVERRIDE_DOC_ROOT/OVERRIDE_CGIDIR + * patch to Apache 1.3b3 and a similar feature in Demon's thttpd, + * both written by James Grinter . + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" + + +module MODULE_VAR_EXPORT vhost_alias_module; + + +/* + * basic configuration things + * we abbreviate "mod_vhost_alias" to "mva" for shorter names + */ + +typedef enum { + VHOST_ALIAS_UNSET, VHOST_ALIAS_NONE, VHOST_ALIAS_NAME, VHOST_ALIAS_IP +} mva_mode_e; + +/* + * Per-server module config record. + */ +typedef struct mva_sconf_t { + char *doc_root; + char *cgi_root; + mva_mode_e doc_root_mode; + mva_mode_e cgi_root_mode; +} mva_sconf_t; + +static void *mva_create_server_config(pool *p, server_rec *s) +{ + mva_sconf_t *conf; + + conf = (mva_sconf_t *) ap_pcalloc(p, sizeof(mva_sconf_t)); + conf->doc_root = NULL; + conf->cgi_root = NULL; + conf->doc_root_mode = VHOST_ALIAS_UNSET; + conf->cgi_root_mode = VHOST_ALIAS_UNSET; + return conf; +} + +static void *mva_merge_server_config(pool *p, void *parentv, void *childv) +{ + mva_sconf_t *parent = (mva_sconf_t *) parentv; + mva_sconf_t *child = (mva_sconf_t *) childv; + mva_sconf_t *conf; + + conf = (mva_sconf_t *) ap_pcalloc(p, sizeof(*conf)); + if (child->doc_root_mode == VHOST_ALIAS_UNSET) { + conf->doc_root_mode = parent->doc_root_mode; + conf->doc_root = parent->doc_root; + } + else { + conf->doc_root_mode = child->doc_root_mode; + conf->doc_root = child->doc_root; + } + if (child->cgi_root_mode == VHOST_ALIAS_UNSET) { + conf->cgi_root_mode = parent->cgi_root_mode; + conf->cgi_root = parent->cgi_root; + } + else { + conf->cgi_root_mode = child->cgi_root_mode; + conf->cgi_root = child->cgi_root; + } + return conf; +} + + +/* + * These are just here to tell us what vhost_alias_set should do. + * We don't put anything into them; we just use the cell addresses. + */ +static int vhost_alias_set_doc_root_ip, + vhost_alias_set_cgi_root_ip, + vhost_alias_set_doc_root_name, + vhost_alias_set_cgi_root_name; + +static const char *vhost_alias_set(cmd_parms *cmd, void *dummy, char *map) +{ + mva_sconf_t *conf; + mva_mode_e mode, *pmode; + char **pmap; + char *p; + + conf = (mva_sconf_t *) ap_get_module_config(cmd->server->module_config, + &vhost_alias_module); + /* there ought to be a better way of doing this */ + if (&vhost_alias_set_doc_root_ip == cmd->info) { + mode = VHOST_ALIAS_IP; + pmap = &conf->doc_root; + pmode = &conf->doc_root_mode; + } + else if (&vhost_alias_set_cgi_root_ip == cmd->info) { + mode = VHOST_ALIAS_IP; + pmap = &conf->cgi_root; + pmode = &conf->cgi_root_mode; + } + else if (&vhost_alias_set_doc_root_name == cmd->info) { + mode = VHOST_ALIAS_NAME; + pmap = &conf->doc_root; + pmode = &conf->doc_root_mode; + } + else if (&vhost_alias_set_cgi_root_name == cmd->info) { + mode = VHOST_ALIAS_NAME; + pmap = &conf->cgi_root; + pmode = &conf->cgi_root_mode; + } + else { + return "INTERNAL ERROR: unknown command info"; + } + + if (*map != '/') { + if (strcasecmp(map, "none")) { + return "format string must start with '/' or be 'none'"; + } + *pmap = NULL; + *pmode = VHOST_ALIAS_NONE; + return NULL; + } + + /* sanity check */ + p = map; + while (*p != '\0') { + if (*p++ != '%') { + continue; + } + /* we just found a '%' */ + if (*p == 'p' || *p == '%') { + ++p; + continue; + } + /* optional dash */ + if (*p == '-') { + ++p; + } + /* digit N */ + if (ap_isdigit(*p)) { + ++p; + } + else { + return "syntax error in format string"; + } + /* optional plus */ + if (*p == '+') { + ++p; + } + /* do we end here? */ + if (*p != '.') { + continue; + } + ++p; + /* optional dash */ + if (*p == '-') { + ++p; + } + /* digit M */ + if (ap_isdigit(*p)) { + ++p; + } + else { + return "syntax error in format string"; + } + /* optional plus */ + if (*p == '+') { + ++p; + } + } + *pmap = map; + *pmode = mode; + return NULL; +} + +static const command_rec mva_commands[] = +{ + {"VirtualScriptAlias", vhost_alias_set, &vhost_alias_set_cgi_root_name, + RSRC_CONF, TAKE1, "how to create a ScriptAlias based on the host"}, + {"VirtualDocumentRoot", vhost_alias_set, &vhost_alias_set_doc_root_name, + RSRC_CONF, TAKE1, "how to create the DocumentRoot based on the host"}, + {"VirtualScriptAliasIP", vhost_alias_set, &vhost_alias_set_cgi_root_ip, + RSRC_CONF, TAKE1, "how to create a ScriptAlias based on the host"}, + {"VirtualDocumentRootIP", vhost_alias_set, &vhost_alias_set_doc_root_ip, + RSRC_CONF, TAKE1, "how to create the DocumentRoot based on the host"}, + { NULL } +}; + + +/* + * This really wants to be a nested function + * but C is too feeble to support them. + */ +static ap_inline void vhost_alias_checkspace(request_rec *r, char *buf, + char **pdest, int size) +{ + /* XXX: what if size > HUGE_STRING_LEN? */ + if (*pdest + size > buf + HUGE_STRING_LEN) { + **pdest = '\0'; + if (r->filename) { + r->filename = ap_pstrcat(r->pool, r->filename, buf, NULL); + } + else { + r->filename = ap_pstrdup(r->pool, buf); + } + *pdest = buf; + } +} + +static void vhost_alias_interpolate(request_rec *r, const char *name, + const char *map, const char *uri) +{ + /* 0..9 9..0 */ + enum { MAXDOTS = 19 }; + const char *dots[MAXDOTS+1]; + int ndots; + + char buf[HUGE_STRING_LEN]; + char *dest, last; + + int N, M, Np, Mp, Nd, Md; + const char *start, *end; + + const char *p; + + ndots = 0; + dots[ndots++] = name-1; /* slightly naughty */ + for (p = name; *p; ++p){ + if (*p == '.' && ndots < MAXDOTS) { + dots[ndots++] = p; + } + } + dots[ndots] = p; + + r->filename = NULL; + + dest = buf; + last = '\0'; + while (*map) { + if (*map != '%') { + /* normal characters */ + vhost_alias_checkspace(r, buf, &dest, 1); + last = *dest++ = *map++; + continue; + } + /* we are in a format specifier */ + ++map; + /* can't be a slash */ + last = '\0'; + /* %% -> % */ + if (*map == '%') { + ++map; + vhost_alias_checkspace(r, buf, &dest, 1); + *dest++ = '%'; + continue; + } + /* port number */ + if (*map == 'p') { + ++map; + /* no. of decimal digits in a short plus one */ + vhost_alias_checkspace(r, buf, &dest, 7); + dest += ap_snprintf(dest, 7, "%d", ap_get_server_port(r)); + continue; + } + /* deal with %-N+.-M+ -- syntax is already checked */ + N = M = 0; /* value */ + Np = Mp = 0; /* is there a plus? */ + Nd = Md = 0; /* is there a dash? */ + if (*map == '-') ++map, Nd = 1; + N = *map++ - '0'; + if (*map == '+') ++map, Np = 1; + if (*map == '.') { + ++map; + if (*map == '-') { + ++map, Md = 1; + } + M = *map++ - '0'; + if (*map == '+') { + ++map, Mp = 1; + } + } + /* note that N and M are one-based indices, not zero-based */ + start = dots[0]+1; /* ptr to the first character */ + end = dots[ndots]; /* ptr to the character after the last one */ + if (N != 0) { + if (N > ndots) { + start = "_"; + end = start+1; + } + else if (!Nd) { + start = dots[N-1]+1; + if (!Np) { + end = dots[N]; + } + } + else { + if (!Np) { + start = dots[ndots-N]+1; + } + end = dots[ndots-N+1]; + } + } + if (M != 0) { + if (M > end - start) { + start = "_"; + end = start+1; + } + else if (!Md) { + start = start+M-1; + if (!Mp) { + end = start+1; + } + } + else { + if (!Mp) { + start = end-M; + } + end = end-M+1; + } + } + vhost_alias_checkspace(r, buf, &dest, end - start); + for (p = start; p < end; ++p) { + *dest++ = ap_tolower(*p); + } + } + *dest = '\0'; + /* no double slashes */ + if (last == '/') { + ++uri; + } + if (r->filename) { + r->filename = ap_pstrcat(r->pool, r->filename, buf, uri, NULL); + } + else { + r->filename = ap_pstrcat(r->pool, buf, uri, NULL); + } +} + +static int mva_translate(request_rec *r) +{ + mva_sconf_t *conf; + const char *name, *map, *uri; + mva_mode_e mode; + int cgi; + + conf = (mva_sconf_t *) ap_get_module_config(r->server->module_config, + &vhost_alias_module); + if (!strncmp(r->uri, "/cgi-bin/", 9)) { + mode = conf->cgi_root_mode; + map = conf->cgi_root; + uri = r->uri + 8; + /* + * can't force cgi immediately because we might not handle this + * call if the mode is wrong + */ + cgi = 1; + } + else if (r->uri[0] == '/') { + mode = conf->doc_root_mode; + map = conf->doc_root; + uri = r->uri; + cgi = 0; + } + else { + return DECLINED; + } + + if (mode == VHOST_ALIAS_NAME) { + name = ap_get_server_name(r); + } + else if (mode == VHOST_ALIAS_IP) { + name = r->connection->local_ip; + } + else { + return DECLINED; + } + + vhost_alias_interpolate(r, name, map, uri); + + if (cgi) { + /* see is_scriptaliased() in mod_cgi */ + r->handler = "cgi-script"; + ap_table_setn(r->notes, "alias-forced-type", r->handler); + } + + return OK; +} + + +module MODULE_VAR_EXPORT vhost_alias_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + mva_create_server_config, /* server config */ + mva_merge_server_config, /* merge server configs */ + mva_commands, /* command table */ + NULL, /* handlers */ + mva_translate, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/metadata/.indent.pro b/modules/metadata/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/modules/metadata/.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/metadata/mod_cern_meta.c b/modules/metadata/mod_cern_meta.c new file mode 100644 index 00000000000..dbc1de6cabb --- /dev/null +++ b/modules/metadata/mod_cern_meta.c @@ -0,0 +1,395 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_cern_meta.c + * version 0.1.0 + * status beta + * + * Andrew Wilson 25.Jan.96 + * + * *** IMPORTANT *** + * This version of mod_cern_meta.c controls Meta File behaviour on a + * per-directory basis. Previous versions of the module defined behaviour + * on a per-server basis. The upshot is that you'll need to revisit your + * configuration files in order to make use of the new module. + * *** + * + * Emulate the CERN HTTPD Meta file semantics. Meta files are HTTP + * headers that can be output in addition to the normal range of + * headers for each file accessed. They appear rather like the Apache + * .asis files, and are able to provide a crude way of influencing + * the Expires: header, as well as providing other curiosities. + * There are many ways to manage meta information, this one was + * chosen because there is already a large number of CERN users + * who can exploit this module. It should be noted that there are probably + * more sensitive ways of managing the Expires: header specifically. + * + * The module obeys the following directives, which can appear + * in the server's .conf files and in .htaccess files. + * + * MetaFiles + * + * turns on|off meta file processing for any directory. + * Default value is off + * + * # turn on MetaFiles in this directory + * MetaFiles on + * + * MetaDir + * + * specifies the name of the directory in which Apache can find + * meta information files. The directory is usually a 'hidden' + * subdirectory of the directory that contains the file being + * accessed. eg: + * + * # .meta files are in the *same* directory as the + * # file being accessed + * MetaDir . + * + * the default is to look in a '.web' subdirectory. This is the + * same as for CERN 3.+ webservers and behaviour is the same as + * for the directive: + * + * MetaDir .web + * + * MetaSuffix + * + * specifies the file name suffix for the file containing the + * meta information. eg: + * + * # our meta files are suffixed with '.cern_meta' + * MetaSuffix .cern_meta + * + * the default is to look for files with the suffix '.meta'. This + * behaviour is the same as for the directive: + * + * MetaSuffix .meta + * + * When accessing the file + * + * DOCUMENT_ROOT/somedir/index.html + * + * this module will look for the file + * + * DOCUMENT_ROOT/somedir/.web/index.html.meta + * + * and will use its contents to generate additional MIME header + * information. + * + * For more information on the CERN Meta file semantics see: + * + * http://www.w3.org/hypertext/WWW/Daemon/User/Config/General.html#MetaDir + * + * Change-log: + * 29.Jan.96 pfopen/pfclose instead of fopen/fclose + * DECLINE when real file not found, we may be checking each + * of the index.html/index.shtml/index.htm variants and don't + * need to report missing ones as spurious errors. + * 31.Jan.96 log_error reports about a malformed .meta file, rather + * than a script error. + * 20.Jun.96 MetaFiles default off, added, so that module + * can be configured per-directory. Prior to this the module + * was running for each request anywhere on the server, naughty.. + * 29.Jun.96 All directives made per-directory. + */ + +#include "httpd.h" +#include "http_config.h" +#include +#include +#include "util_script.h" +#include "http_log.h" +#include "http_request.h" + +#define DIR_CMD_PERMS OR_INDEXES + +#define DEFAULT_METADIR ".web" +#define DEFAULT_METASUFFIX ".meta" +#define DEFAULT_METAFILES 0 + +module MODULE_VAR_EXPORT cern_meta_module; + +typedef struct { + char *metadir; + char *metasuffix; + char *metafiles; +} cern_meta_dir_config; + +static void *create_cern_meta_dir_config(pool *p, char *dummy) +{ + cern_meta_dir_config *new = + (cern_meta_dir_config *) ap_palloc(p, sizeof(cern_meta_dir_config)); + + new->metadir = NULL; + new->metasuffix = NULL; + new->metafiles = DEFAULT_METAFILES; + + return new; +} + +static void *merge_cern_meta_dir_configs(pool *p, void *basev, void *addv) +{ + cern_meta_dir_config *base = (cern_meta_dir_config *) basev; + cern_meta_dir_config *add = (cern_meta_dir_config *) addv; + cern_meta_dir_config *new = + (cern_meta_dir_config *) ap_palloc(p, sizeof(cern_meta_dir_config)); + + new->metadir = add->metadir ? add->metadir : base->metadir; + new->metasuffix = add->metasuffix ? add->metasuffix : base->metasuffix; + new->metafiles = add->metafiles; + + return new; +} + +static const char *set_metadir(cmd_parms *parms, cern_meta_dir_config * dconf, char *arg) +{ + dconf->metadir = arg; + return NULL; +} + +static const char *set_metasuffix(cmd_parms *parms, cern_meta_dir_config * dconf, char *arg) +{ + dconf->metasuffix = arg; + return NULL; +} + +static const char *set_metafiles(cmd_parms *parms, cern_meta_dir_config * dconf, char *arg) +{ + dconf->metafiles = arg; + return NULL; +} + + +static const command_rec cern_meta_cmds[] = +{ + {"MetaFiles", set_metafiles, NULL, DIR_CMD_PERMS, FLAG, + "Limited to 'on' or 'off'"}, + {"MetaDir", set_metadir, NULL, DIR_CMD_PERMS, TAKE1, + "the name of the directory containing meta files"}, + {"MetaSuffix", set_metasuffix, NULL, DIR_CMD_PERMS, TAKE1, + "the filename suffix for meta files"}, + {NULL} +}; + +/* XXX: this is very similar to ap_scan_script_header_err_core... + * are the differences deliberate, or just a result of bit rot? + */ +static int scan_meta_file(request_rec *r, FILE *f) +{ + char w[MAX_STRING_LEN]; + char *l; + int p; + table *tmp_headers; + + tmp_headers = ap_make_table(r->pool, 5); + while (fgets(w, MAX_STRING_LEN - 1, f) != NULL) { + + /* Delete terminal (CR?)LF */ + + p = strlen(w); + if (p > 0 && w[p - 1] == '\n') { + if (p > 1 && w[p - 2] == '\015') + w[p - 2] = '\0'; + else + w[p - 1] = '\0'; + } + + if (w[0] == '\0') { + return OK; + } + + /* if we see a bogus header don't ignore it. Shout and scream */ + + if (!(l = strchr(w, ':'))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "malformed header in meta file: %s", r->filename); + return SERVER_ERROR; + } + + *l++ = '\0'; + while (*l && ap_isspace(*l)) + ++l; + + if (!strcasecmp(w, "Content-type")) { + char *tmp; + /* Nuke trailing whitespace */ + + char *endp = l + strlen(l) - 1; + while (endp > l && ap_isspace(*endp)) + *endp-- = '\0'; + + tmp = ap_pstrdup(r->pool, l); + ap_content_type_tolower(tmp); + r->content_type = tmp; + } + else if (!strcasecmp(w, "Status")) { + sscanf(l, "%d", &r->status); + r->status_line = ap_pstrdup(r->pool, l); + } + else { + ap_table_set(tmp_headers, w, l); + } + } + ap_overlap_tables(r->headers_out, tmp_headers, AP_OVERLAP_TABLES_SET); + return OK; +} + +static int add_cern_meta_data(request_rec *r) +{ + char *metafilename; + char *last_slash; + char *real_file; + char *scrap_book; + FILE *f; + cern_meta_dir_config *dconf; + int rv; + request_rec *rr; + + dconf = ap_get_module_config(r->per_dir_config, &cern_meta_module); + + if (!dconf->metafiles) { + return DECLINED; + }; + + /* if ./.web/$1.meta exists then output 'asis' */ + + if (r->finfo.st_mode == 0) { + return DECLINED; + }; + + /* is this a directory? */ + if (S_ISDIR(r->finfo.st_mode) || r->uri[strlen(r->uri) - 1] == '/') { + return DECLINED; + }; + + /* what directory is this file in? */ + scrap_book = ap_pstrdup(r->pool, r->filename); + /* skip leading slash, recovered in later processing */ + scrap_book++; + last_slash = strrchr(scrap_book, '/'); + if (last_slash != NULL) { + /* skip over last slash */ + real_file = last_slash; + real_file++; + *last_slash = '\0'; + } + else { + /* no last slash, buh?! */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "internal error in mod_cern_meta: %s", r->filename); + /* should really barf, but hey, let's be friends... */ + return DECLINED; + }; + + metafilename = ap_pstrcat(r->pool, "/", scrap_book, "/", + dconf->metadir ? dconf->metadir : DEFAULT_METADIR, + "/", real_file, + dconf->metasuffix ? dconf->metasuffix : DEFAULT_METASUFFIX, + NULL); + + /* XXX: it sucks to require this subrequest to complete, because this + * means people must leave their meta files accessible to the world. + * A better solution might be a "safe open" feature of pfopen to avoid + * pipes, symlinks, and crap like that. + */ + rr = ap_sub_req_lookup_file(metafilename, r); + if (rr->status != HTTP_OK) { + ap_destroy_sub_req(rr); + return DECLINED; + } + ap_destroy_sub_req(rr); + + f = ap_pfopen(r->pool, metafilename, "r"); + if (f == NULL) { + if (errno == ENOENT) { + return DECLINED; + } + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "meta file permissions deny server access: %s", metafilename); + return FORBIDDEN; + }; + + /* read the headers in */ + rv = scan_meta_file(r, f); + ap_pfclose(r->pool, f); + + return rv; +} + +module MODULE_VAR_EXPORT cern_meta_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_cern_meta_dir_config, /* dir config creater */ + merge_cern_meta_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server configs */ + cern_meta_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + add_cern_meta_data, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/metadata/mod_cern_meta.dsp b/modules/metadata/mod_cern_meta.dsp new file mode 100644 index 00000000000..3564c0e2ef6 --- /dev/null +++ b/modules/metadata/mod_cern_meta.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleCERNMeta" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleCERNMeta - 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 "ApacheModuleCERNMeta.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 "ApacheModuleCERNMeta.mak"\ + CFG="ApacheModuleCERNMeta - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleCERNMeta - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleCERNMeta - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleCERNMeta - 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 ".\ApacheModuleCERNMetaR" +# PROP Intermediate_Dir ".\ApacheModuleCERNMetaR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleCERNMeta - 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 ".\ApacheModuleCERNMetaD" +# PROP Intermediate_Dir ".\ApacheModuleCERNMetaD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleCERNMeta - Win32 Release" +# Name "ApacheModuleCERNMeta - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_cern_meta.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/metadata/mod_env.c b/modules/metadata/mod_env.c new file mode 100644 index 00000000000..351a48712b0 --- /dev/null +++ b/modules/metadata/mod_env.c @@ -0,0 +1,270 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_env.c + * version 0.0.5 + * status beta + * Pass environment variables to CGI/SSI scripts. + * + * Andrew Wilson 06.Dec.95 + * + * Change log: + * 08.Dec.95 Now allows PassEnv directive to appear more than once in + * conf files. + * 10.Dec.95 optimisation. getenv() only called at startup and used + * to build a fast-to-access table. table used to build + * per-server environment for each request. + * robustness. better able to handle errors in configuration + * files: + * 1) PassEnv directive present, but no environment variable listed + * 2) PassEnv FOO present, but $FOO not present in environment + * 3) no PassEnv directive present + * 23.Dec.95 Now allows SetEnv directive with same semantics as 'sh' setenv: + * SetEnv Var sets Var to the empty string + * SetEnv Var Val sets Var to the value Val + * Values containing whitespace should be quoted, eg: + * SetEnv Var "this is some text" + * Environment variables take their value from the last instance + * of PassEnv / SetEnv to be reached in the configuration file. + * For example, the sequence: + * PassEnv FOO + * SetEnv FOO override + * Causes FOO to take the value 'override'. + * 23.Feb.96 Added UnsetEnv directive to allow environment variables + * to be removed. + * Virtual hosts now 'inherit' parent server environment which + * they're able to overwrite with their own directives or + * selectively ignore with UnsetEnv. + * *** IMPORTANT - the way that virtual hosts inherit their *** + * *** environment variables from the default server's *** + * *** configuration has changed. You should test your *** + * *** configuration carefully before accepting this *** + * *** version of the module in a live webserver which used *** + * *** older versions of the module. *** + */ + +#include "httpd.h" +#include "http_config.h" + +typedef struct { + table *vars; + char *unsetenv; + int vars_present; +} env_dir_config_rec; + +module MODULE_VAR_EXPORT env_module; + +static void *create_env_dir_config(pool *p, char *dummy) +{ + env_dir_config_rec *new = + (env_dir_config_rec *) ap_palloc(p, sizeof(env_dir_config_rec)); + new->vars = ap_make_table(p, 50); + new->unsetenv = ""; + new->vars_present = 0; + return (void *) new; +} + +static void *merge_env_dir_configs(pool *p, void *basev, void *addv) +{ + env_dir_config_rec *base = (env_dir_config_rec *) basev; + env_dir_config_rec *add = (env_dir_config_rec *) addv; + env_dir_config_rec *new = + (env_dir_config_rec *) ap_palloc(p, sizeof(env_dir_config_rec)); + + table *new_table; + table_entry *elts; + array_header *arr; + + int i; + const char *uenv, *unset; + + /* + * new_table = copy_table( p, base->vars ); + * foreach $element ( @add->vars ) { + * table_set( new_table, $element.key, $element.val ); + * }; + * foreach $unsetenv ( @UNSETENV ) { + * table_unset( new_table, $unsetenv ); + * } + */ + + new_table = ap_copy_table(p, base->vars); + + arr = ap_table_elts(add->vars); + elts = (table_entry *)arr->elts; + + for (i = 0; i < arr->nelts; ++i) { + ap_table_setn(new_table, elts[i].key, elts[i].val); + } + + unset = add->unsetenv; + uenv = ap_getword_conf(p, &unset); + while (uenv[0] != '\0') { + ap_table_unset(new_table, uenv); + uenv = ap_getword_conf(p, &unset); + } + + new->vars = new_table; + + new->vars_present = base->vars_present || add->vars_present; + + return new; +} + +static const char *add_env_module_vars_passed(cmd_parms *cmd, + env_dir_config_rec *sconf, + const char *arg) +{ + table *vars = sconf->vars; + char *env_var; + char *name_ptr; + + while (*arg) { + name_ptr = ap_getword_conf(cmd->pool, &arg); + env_var = getenv(name_ptr); + if (env_var != NULL) { + sconf->vars_present = 1; + ap_table_setn(vars, name_ptr, ap_pstrdup(cmd->pool, env_var)); + } + } + return NULL; +} + +static const char *add_env_module_vars_set(cmd_parms *cmd, + env_dir_config_rec *sconf, + const char *arg) +{ + table *vars = sconf->vars; + char *name, *value; + + name = ap_getword_conf(cmd->pool, &arg); + value = ap_getword_conf(cmd->pool, &arg); + + /* name is mandatory, value is optional. no value means + * set the variable to an empty string + */ + + + if ((*name == '\0') || (*arg != '\0')) { + return "SetEnv takes one or two arguments. An environment variable name and an optional value to pass to CGI."; + } + + sconf->vars_present = 1; + ap_table_setn(vars, name, value); + + return NULL; +} + +static const char *add_env_module_vars_unset(cmd_parms *cmd, + env_dir_config_rec *sconf, + char *arg) +{ + sconf->unsetenv = sconf->unsetenv ? + ap_pstrcat(cmd->pool, sconf->unsetenv, " ", arg, NULL) : + arg; + return NULL; +} + +static const command_rec env_module_cmds[] = +{ + {"PassEnv", add_env_module_vars_passed, NULL, + OR_FILEINFO, RAW_ARGS, "a list of environment variables to pass to CGI."}, + {"SetEnv", add_env_module_vars_set, NULL, + OR_FILEINFO, RAW_ARGS, "an environment variable name and a value to pass to CGI."}, + {"UnsetEnv", add_env_module_vars_unset, NULL, + OR_FILEINFO, RAW_ARGS, "a list of variables to remove from the CGI environment."}, + {NULL}, +}; + +static int fixup_env_module(request_rec *r) +{ + table *e = r->subprocess_env; + env_dir_config_rec *sconf = ap_get_module_config(r->per_dir_config, + &env_module); + table *vars = sconf->vars; + + if (!sconf->vars_present) + return DECLINED; + + r->subprocess_env = ap_overlay_tables(r->pool, e, vars); + + return OK; +} + +module MODULE_VAR_EXPORT env_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_env_dir_config, /* dir config creater */ + merge_env_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server configs */ + env_module_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + fixup_env_module, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/metadata/mod_expires.c b/modules/metadata/mod_expires.c new file mode 100644 index 00000000000..4fcf51a8106 --- /dev/null +++ b/modules/metadata/mod_expires.c @@ -0,0 +1,510 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_expires.c + * version 0.0.11 + * status beta + * + * Andrew Wilson 26.Jan.96 + * + * This module allows you to control the form of the Expires: header + * that Apache issues for each access. Directives can appear in + * configuration files or in .htaccess files so expiry semantics can + * be defined on a per-directory basis. + * + * DIRECTIVE SYNTAX + * + * Valid directives are: + * + * ExpiresActive on | off + * ExpiresDefault + * ExpiresByType type/encoding + * + * Valid values for are: + * + * 'M' expires header shows file modification date + + * 'A' expires header shows access time + + * + * [I'm not sure which of these is best under different + * circumstances, I guess it's for other people to explore. + * The effects may be indistinguishable for a number of cases] + * + * should be an integer value [acceptable to atoi()] + * + * There is NO space between the and . + * + * For example, a directory which contains information which changes + * frequently might contain: + * + * # reports generated by cron every hour. don't let caches + * # hold onto stale information + * ExpiresDefault M3600 + * + * Another example, our html pages can change all the time, the gifs + * tend not to change often: + * + * # pages are hot (1 week), images are cold (1 month) + * ExpiresByType text/html A604800 + * ExpiresByType image/gif A2592000 + * + * Expires can be turned on for all URLs on the server by placing the + * following directive in a conf file: + * + * ExpiresActive on + * + * ExpiresActive can also appear in .htaccess files, enabling the + * behaviour to be turned on or off for each chosen directory. + * + * # turn off Expires behaviour in this directory + * # and subdirectories + * ExpiresActive off + * + * Directives defined for a directory are valid in subdirectories + * unless explicitly overridden by new directives in the subdirectory + * .htaccess files. + * + * ALTERNATIVE DIRECTIVE SYNTAX + * + * Directives can also be defined in a more readable syntax of the form: + * + * ExpiresDefault " [plus] { }*" + * ExpiresByType type/encoding " [plus] { }*" + * + * where is one of: + * access + * now equivalent to 'access' + * modification + * + * where the 'plus' keyword is optional + * + * where should be an integer value [acceptable to atoi()] + * + * where is one of: + * years + * months + * weeks + * days + * hours + * minutes + * seconds + * + * For example, any of the following directives can be used to make + * documents expire 1 month after being accessed, by default: + * + * ExpiresDefault "access plus 1 month" + * ExpiresDefault "access plus 4 weeks" + * ExpiresDefault "access plus 30 days" + * + * The expiry time can be fine-tuned by adding several ' ' + * clauses: + * + * ExpiresByType text/html "access plus 1 month 15 days 2 hours" + * ExpiresByType image/gif "modification plus 5 hours 3 minutes" + * + * --- + * + * Change-log: + * 29.Jan.96 Hardened the add_* functions. Server will now bail out + * if bad directives are given in the conf files. + * 02.Feb.96 Returns DECLINED if not 'ExpiresActive on', giving other + * expires-aware modules a chance to play with the same + * directives. [Michael Rutman] + * 03.Feb.96 Call tzset() before localtime(). Trying to get the module + * to work properly in non GMT timezones. + * 12.Feb.96 Modified directive syntax to allow more readable commands: + * ExpiresDefault "now plus 10 days 20 seconds" + * ExpiresDefault "access plus 30 days" + * ExpiresDefault "modification plus 1 year 10 months 30 days" + * 13.Feb.96 Fix call to table_get() with NULL 2nd parameter [Rob Hartill] + * 19.Feb.96 Call gm_timestr_822() to get time formatted correctly, can't + * rely on presence of HTTP_TIME_FORMAT in Apache 1.1+. + * 21.Feb.96 This version (0.0.9) reverses assumptions made in 0.0.8 + * about star/star handlers. Reverting to 0.0.7 behaviour. + * 08.Jun.96 allows ExpiresDefault to be used with responses that use + * the DefaultType by not DECLINING, but instead skipping + * the table_get check and then looking for an ExpiresDefault. + * [Rob Hartill] + * 04.Nov.96 'const' definitions added. + * + * TODO + * add support for Cache-Control: max-age=20 from the HTTP/1.1 + * proposal (in this case, a ttl of 20 seconds) [ask roy] + * add per-file expiry and explicit expiry times - duplicates some + * of the mod_cern_meta.c functionality. eg: + * ExpiresExplicit index.html "modification plus 30 days" + * + * BUGS + * Hi, welcome to the internet. + */ + +#include +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" + +typedef struct { + int active; + char *expiresdefault; + table *expiresbytype; +} expires_dir_config; + +/* from mod_dir, why is this alias used? + */ +#define DIR_CMD_PERMS OR_INDEXES + +#define ACTIVE_ON 1 +#define ACTIVE_OFF 0 +#define ACTIVE_DONTCARE 2 + +module MODULE_VAR_EXPORT expires_module; + +static void *create_dir_expires_config(pool *p, char *dummy) +{ + expires_dir_config *new = + (expires_dir_config *) ap_pcalloc(p, sizeof(expires_dir_config)); + new->active = ACTIVE_DONTCARE; + new->expiresdefault = ""; + new->expiresbytype = ap_make_table(p, 4); + return (void *) new; +} + +static const char *set_expiresactive(cmd_parms *cmd, expires_dir_config * dir_config, int arg) +{ + /* if we're here at all it's because someone explicitly + * set the active flag + */ + dir_config->active = ACTIVE_ON; + if (arg == 0) { + dir_config->active = ACTIVE_OFF; + }; + return NULL; +} + +/* check_code() parse 'code' and return NULL or an error response + * string. If we return NULL then real_code contains code converted + * to the cnnnn format. + */ +static char *check_code(pool *p, const char *code, char **real_code) +{ + char *word; + char base = 'X'; + int modifier = 0; + int num = 0; + int factor = 0; + + /* 0.0.4 compatibility? + */ + if ((code[0] == 'A') || (code[0] == 'M')) { + *real_code = (char *)code; + return NULL; + }; + + /* [plus] { }* + */ + + /* + */ + word = ap_getword_conf(p, &code); + if (!strncasecmp(word, "now", 1) || + !strncasecmp(word, "access", 1)) { + base = 'A'; + } + else if (!strncasecmp(word, "modification", 1)) { + base = 'M'; + } + else { + return ap_pstrcat(p, "bad expires code, unrecognised '", + word, "'", NULL); + }; + + /* [plus] + */ + word = ap_getword_conf(p, &code); + if (!strncasecmp(word, "plus", 1)) { + word = ap_getword_conf(p, &code); + }; + + /* { }* + */ + while (word[0]) { + /* + */ + if (ap_isdigit(word[0])) { + num = atoi(word); + } + else { + return ap_pstrcat(p, "bad expires code, numeric value expected '", + word, "'", NULL); + }; + + /* + */ + word = ap_getword_conf(p, &code); + if (word[0]) { + /* do nothing */ + } + else { + return ap_pstrcat(p, "bad expires code, missing ", NULL); + }; + + factor = 0; + if (!strncasecmp(word, "years", 1)) { + factor = 60 * 60 * 24 * 365; + } + else if (!strncasecmp(word, "months", 2)) { + factor = 60 * 60 * 24 * 30; + } + else if (!strncasecmp(word, "weeks", 1)) { + factor = 60 * 60 * 24 * 7; + } + else if (!strncasecmp(word, "days", 1)) { + factor = 60 * 60 * 24; + } + else if (!strncasecmp(word, "hours", 1)) { + factor = 60 * 60; + } + else if (!strncasecmp(word, "minutes", 2)) { + factor = 60; + } + else if (!strncasecmp(word, "seconds", 1)) { + factor = 1; + } + else { + return ap_pstrcat(p, "bad expires code, unrecognised ", + "'", word, "'", NULL); + }; + + modifier = modifier + factor * num; + + /* next + */ + word = ap_getword_conf(p, &code); + }; + + *real_code = ap_psprintf(p, "%c%d", base, modifier); + + return NULL; +} + +static const char *set_expiresbytype(cmd_parms *cmd, expires_dir_config * dir_config, char *mime, char *code) +{ + char *response, *real_code; + + if ((response = check_code(cmd->pool, code, &real_code)) == NULL) { + ap_table_setn(dir_config->expiresbytype, mime, real_code); + return NULL; + }; + return ap_pstrcat(cmd->pool, + "'ExpiresByType ", mime, " ", code, "': ", response, NULL); +} + +static const char *set_expiresdefault(cmd_parms *cmd, expires_dir_config * dir_config, char *code) +{ + char *response, *real_code; + + if ((response = check_code(cmd->pool, code, &real_code)) == NULL) { + dir_config->expiresdefault = real_code; + return NULL; + }; + return ap_pstrcat(cmd->pool, + "'ExpiresDefault ", code, "': ", response, NULL); +} + +static const command_rec expires_cmds[] = +{ + {"ExpiresActive", set_expiresactive, NULL, DIR_CMD_PERMS, FLAG, + "Limited to 'on' or 'off'"}, + {"ExpiresBytype", set_expiresbytype, NULL, DIR_CMD_PERMS, TAKE2, + "a MIME type followed by an expiry date code"}, + {"ExpiresDefault", set_expiresdefault, NULL, DIR_CMD_PERMS, TAKE1, + "an expiry date code"}, + {NULL} +}; + +static void *merge_expires_dir_configs(pool *p, void *basev, void *addv) +{ + expires_dir_config *new = (expires_dir_config *) ap_pcalloc(p, sizeof(expires_dir_config)); + expires_dir_config *base = (expires_dir_config *) basev; + expires_dir_config *add = (expires_dir_config *) addv; + + if (add->active == ACTIVE_DONTCARE) { + new->active = base->active; + } + else { + new->active = add->active; + }; + + if (add->expiresdefault != '\0') { + new->expiresdefault = add->expiresdefault; + }; + + new->expiresbytype = ap_overlay_tables(p, add->expiresbytype, + base->expiresbytype); + return new; +} + +static int add_expires(request_rec *r) +{ + expires_dir_config *conf; + char *code; + time_t base; + time_t additional; + time_t expires; + char age[20]; + + if (ap_is_HTTP_ERROR(r->status)) /* Don't add Expires headers to errors */ + return DECLINED; + + if (r->main != NULL) /* Say no to subrequests */ + return DECLINED; + + conf = (expires_dir_config *) ap_get_module_config(r->per_dir_config, &expires_module); + if (conf == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "internal error: %s", r->filename); + return SERVER_ERROR; + }; + + if (conf->active != ACTIVE_ON) + return DECLINED; + + /* we perhaps could use the default_type(r) in its place but that + * may be 2nd guesing the desired configuration... calling table_get + * with a NULL key will SEGV us + * + * I still don't know *why* r->content_type would ever be NULL, this + * is possibly a result of fixups being called in many different + * places. Fixups is probably the wrong place to be doing all this + * work... Bah. + * + * Changed as of 08.Jun.96 don't DECLINE, look for an ExpiresDefault. + */ + if (r->content_type == NULL) + code = NULL; + else + code = (char *) ap_table_get(conf->expiresbytype, r->content_type); + + if (code == NULL) { + /* no expires defined for that type, is there a default? */ + code = conf->expiresdefault; + + if (code[0] == '\0') + return OK; + }; + + /* we have our code */ + + switch (code[0]) { + case 'M': + if (r->finfo.st_mode == 0) { + /* file doesn't exist on disk, so we can't do anything based on + * modification time. Note that this does _not_ log an error. + */ + return DECLINED; + } + base = r->finfo.st_mtime; + additional = atoi(&code[1]); + break; + case 'A': + /* there's been some discussion and it's possible that + * 'access time' will be stored in request structure + */ + base = r->request_time; + additional = atoi(&code[1]); + break; + default: + /* expecting the add_* routines to be case-hardened this + * is just a reminder that module is beta + */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "internal error: bad expires code: %s", r->filename); + return SERVER_ERROR; + }; + + expires = base + additional; + ap_snprintf(age, sizeof(age), "max-age=%d", (int) expires - (int) r->request_time); + ap_table_setn(r->headers_out, "Cache-Control", ap_pstrdup(r->pool, age)); + tzset(); /* redundant? called implicitly by localtime, at least + * under FreeBSD + */ + ap_table_setn(r->headers_out, "Expires", ap_gm_timestr_822(r->pool, expires)); + return OK; +} + +module MODULE_VAR_EXPORT expires_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_dir_expires_config, /* dir config creater */ + merge_expires_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server configs */ + expires_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + add_expires, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/metadata/mod_expires.dsp b/modules/metadata/mod_expires.dsp new file mode 100644 index 00000000000..0b2fe902bb2 --- /dev/null +++ b/modules/metadata/mod_expires.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleExpires" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleExpires - 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 "ApacheModuleExpires.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 "ApacheModuleExpires.mak"\ + CFG="ApacheModuleExpires - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleExpires - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleExpires - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleExpires - 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 ".\ApacheModuleExpiresR" +# PROP Intermediate_Dir ".\ApacheModuleExpiresR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleExpires - 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 ".\ApacheModuleExpiresD" +# PROP Intermediate_Dir ".\ApacheModuleExpiresD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleExpires - Win32 Release" +# Name "ApacheModuleExpires - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_expires.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/metadata/mod_headers.c b/modules/metadata/mod_headers.c new file mode 100644 index 00000000000..c3d50503eed --- /dev/null +++ b/modules/metadata/mod_headers.c @@ -0,0 +1,265 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_headers.c: Add/append/remove HTTP response headers + * Written by Paul Sutton, paul@ukweb.com, 1 Oct 1996 + * + * New directive, Header, can be used to add/replace/remove HTTP headers. + * Valid in both per-server and per-dir configurations. + * + * Syntax is: + * + * Header action header value + * + * Where action is one of: + * set - set this header, replacing any old value + * add - add this header, possible resulting in two or more + * headers with the same name + * append - append this text onto any existing header of this same + * unset - remove this header + * + * Where action is unset, the third argument (value) should not be given. + * The header name can include the colon, or not. + * + * The Header directive can only be used where allowed by the FileInfo + * override. + * + * When the request is processed, the header directives are processed in + * this order: firstly, the main server, then the virtual server handling + * this request (if any), then any sections (working downwards + * from the root dir), then an sections (working down from + * shortest URL component), the any sections. This order is + * important if any 'set' or 'unset' actions are used. For example, + * the following two directives have different effect if applied in + * the reverse order: + * + * Header append Author "John P. Doe" + * Header unset Author + * + * Examples: + * + * To set the "Author" header, use + * Header add Author "John P. Doe" + * + * To remove a header: + * Header unset Author + * + */ + +#include "httpd.h" +#include "http_config.h" + +typedef enum { + hdr_add = 'a', /* add header (could mean multiple hdrs) */ + hdr_set = 's', /* set (replace old value) */ + hdr_append = 'm', /* append (merge into any old value) */ + hdr_unset = 'u' /* unset header */ +} hdr_actions; + +typedef struct { + hdr_actions action; + char *header; + char *value; +} header_entry; + +/* + * headers_conf is our per-module configuration. This is used as both + * a per-dir and per-server config + */ +typedef struct { + array_header *headers; +} headers_conf; + +module MODULE_VAR_EXPORT headers_module; + +static void *create_headers_config(pool *p, server_rec *s) +{ + headers_conf *a = + (headers_conf *) ap_pcalloc(p, sizeof(headers_conf)); + + a->headers = ap_make_array(p, 2, sizeof(header_entry)); + return a; +} + +static void *create_headers_dir_config(pool *p, char *d) +{ + return (headers_conf *) create_headers_config(p, NULL); +} + +static void *merge_headers_config(pool *p, void *basev, void *overridesv) +{ + headers_conf *a = + (headers_conf *) ap_pcalloc(p, sizeof(headers_conf)); + headers_conf *base = (headers_conf *) basev, *overrides = (headers_conf *) overridesv; + + a->headers = ap_append_arrays(p, base->headers, overrides->headers); + + return a; +} + + +static const char *header_cmd(cmd_parms *cmd, headers_conf * dirconf, char *action, char *hdr, char *value) +{ + header_entry *new; + server_rec *s = cmd->server; + headers_conf *serverconf = + (headers_conf *) ap_get_module_config(s->module_config, &headers_module); + char *colon; + + if (cmd->path) { + new = (header_entry *) ap_push_array(dirconf->headers); + } + else { + new = (header_entry *) ap_push_array(serverconf->headers); + } + + if (!strcasecmp(action, "set")) + new->action = hdr_set; + else if (!strcasecmp(action, "add")) + new->action = hdr_add; + else if (!strcasecmp(action, "append")) + new->action = hdr_append; + else if (!strcasecmp(action, "unset")) + new->action = hdr_unset; + else + return "first argument must be add, set, append or unset."; + + if (new->action == hdr_unset) { + if (value) + return "Header unset takes two arguments"; + } + else if (!value) + return "Header requires three arguments"; + + if ((colon = strchr(hdr, ':'))) + *colon = '\0'; + + new->header = hdr; + new->value = value; + + return NULL; +} + +static const command_rec headers_cmds[] = +{ + {"Header", header_cmd, NULL, OR_FILEINFO, TAKE23, + "an action, header and value"}, + {NULL} +}; + +static void do_headers_fixup(request_rec *r, array_header *headers) +{ + int i; + + for (i = 0; i < headers->nelts; ++i) { + header_entry *hdr = &((header_entry *) (headers->elts))[i]; + switch (hdr->action) { + case hdr_add: + ap_table_addn(r->headers_out, hdr->header, hdr->value); + break; + case hdr_append: + ap_table_mergen(r->headers_out, hdr->header, hdr->value); + break; + case hdr_set: + ap_table_setn(r->headers_out, hdr->header, hdr->value); + break; + case hdr_unset: + ap_table_unset(r->headers_out, hdr->header); + break; + } + } + +} + +static int fixup_headers(request_rec *r) +{ + void *sconf = r->server->module_config; + headers_conf *serverconf = + (headers_conf *) ap_get_module_config(sconf, &headers_module); + void *dconf = r->per_dir_config; + headers_conf *dirconf = + (headers_conf *) ap_get_module_config(dconf, &headers_module); + + do_headers_fixup(r, serverconf->headers); + do_headers_fixup(r, dirconf->headers); + + return DECLINED; +} + +module MODULE_VAR_EXPORT headers_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_headers_dir_config, /* dir config creater */ + merge_headers_config, /* dir merger --- default is to override */ + create_headers_config, /* server config */ + merge_headers_config, /* merge server configs */ + headers_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + fixup_headers, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/metadata/mod_headers.dsp b/modules/metadata/mod_headers.dsp new file mode 100644 index 00000000000..1dc76027b31 --- /dev/null +++ b/modules/metadata/mod_headers.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleHeaders" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleHeaders - 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 "ApacheModuleHeaders.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 "ApacheModuleHeaders.mak"\ + CFG="ApacheModuleHeaders - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleHeaders - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleHeaders - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleHeaders - 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 ".\ApacheModuleHeadersR" +# PROP Intermediate_Dir ".\ApacheModuleHeadersR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleHeaders - 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 ".\ApacheModuleHeadersD" +# PROP Intermediate_Dir ".\ApacheModuleHeadersD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleHeaders - Win32 Release" +# Name "ApacheModuleHeaders - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_headers.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/metadata/mod_mime_magic.c b/modules/metadata/mod_mime_magic.c new file mode 100644 index 00000000000..7f4d6ba6e30 --- /dev/null +++ b/modules/metadata/mod_mime_magic.c @@ -0,0 +1,2474 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_mime_magic: MIME type lookup via file magic numbers + * Copyright (c) 1996-1997 Cisco Systems, Inc. + * + * This software was submitted by Cisco Systems to the Apache Group in July + * 1997. Future revisions and derivatives of this source code must + * acknowledge Cisco Systems as the original contributor of this module. + * All other licensing and usage conditions are those of the Apache Group. + * + * Some of this code is derived from the free version of the file command + * originally posted to comp.sources.unix. Copyright info for that program + * is included below as required. + * --------------------------------------------------------------------------- + * - Copyright (c) Ian F. Darwin, 1987. Written by Ian F. Darwin. + * + * This software is not subject to any license of the American Telephone and + * Telegraph Company or of the Regents of the University of California. + * + * Permission is granted to anyone to use this software for any purpose on any + * computer system, and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, credits + * must appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users ever read + * sources, credits must appear in the documentation. + * + * 4. This notice may not be removed or altered. + * ------------------------------------------------------------------------- + * + * For compliance with Mr Darwin's terms: this has been very significantly + * modified from the free "file" command. + * - all-in-one file for compilation convenience when moving from one + * version of Apache to the next. + * - Memory allocation is done through the Apache API's pool structure. + * - All functions have had necessary Apache API request or server + * structures passed to them where necessary to call other Apache API + * routines. (i.e. usually for logging, files, or memory allocation in + * itself or a called function.) + * - struct magic has been converted from an array to a single-ended linked + * list because it only grows one record at a time, it's only accessed + * sequentially, and the Apache API has no equivalent of realloc(). + * - Functions have been changed to get their parameters from the server + * configuration instead of globals. (It should be reentrant now but has + * not been tested in a threaded environment.) + * - Places where it used to print results to stdout now saves them in a + * list where they're used to set the MIME type in the Apache request + * record. + * - Command-line flags have been removed since they will never be used here. + * + * Ian Kluft + * Engineering Information Framework + * Central Engineering + * Cisco Systems, Inc. + * San Jose, CA, USA + * + * Initial installation July/August 1996 + * Misc bug fixes May 1997 + * Submission to Apache Group July 1997 + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" + +#include + + +/* + * data structures and related constants + */ + +#define MODNAME "mod_mime_magic" +#define MIME_MAGIC_DEBUG 0 + +#define MIME_BINARY_UNKNOWN "application/octet-stream" +#define MIME_TEXT_UNKNOWN "text/plain" + +#define MAXMIMESTRING 256 + +/* HOWMANY must be at least 4096 to make gzip -dcq work */ +#define HOWMANY 4096 +/* SMALL_HOWMANY limits how much work we do to figure out text files */ +#define SMALL_HOWMANY 1024 +#define MAXDESC 50 /* max leng of text description */ +#define MAXstring 64 /* max leng of "string" types */ + +struct magic { + struct magic *next; /* link to next entry */ + int lineno; /* line number from magic file */ + + short flag; +#define INDIR 1 /* if '>(...)' appears, */ +#define UNSIGNED 2 /* comparison is unsigned */ + short cont_level; /* level of ">" */ + struct { + char type; /* byte short long */ + long offset; /* offset from indirection */ + } in; + long offset; /* offset to magic number */ + unsigned char reln; /* relation (0=eq, '>'=gt, etc) */ + char type; /* int, short, long or string. */ + char vallen; /* length of string value, if any */ +#define BYTE 1 +#define SHORT 2 +#define LONG 4 +#define STRING 5 +#define DATE 6 +#define BESHORT 7 +#define BELONG 8 +#define BEDATE 9 +#define LESHORT 10 +#define LELONG 11 +#define LEDATE 12 + union VALUETYPE { + unsigned char b; + unsigned short h; + unsigned long l; + char s[MAXstring]; + unsigned char hs[2]; /* 2 bytes of a fixed-endian "short" */ + unsigned char hl[4]; /* 2 bytes of a fixed-endian "long" */ + } value; /* either number or string */ + unsigned long mask; /* mask before comparison with value */ + char nospflag; /* supress space character */ + + /* NOTE: this string is suspected of overrunning - find it! */ + char desc[MAXDESC]; /* description */ +}; + +/* + * data structures for tar file recognition + * -------------------------------------------------------------------------- + * Header file for public domain tar (tape archive) program. + * + * @(#)tar.h 1.20 86/10/29 Public Domain. Created 25 August 1985 by John + * Gilmore, ihnp4!hoptoad!gnu. + * + * Header block on tape. + * + * I'm going to use traditional DP naming conventions here. A "block" is a big + * chunk of stuff that we do I/O on. A "record" is a piece of info that we + * care about. Typically many "record"s fit into a "block". + */ +#define RECORDSIZE 512 +#define NAMSIZ 100 +#define TUNMLEN 32 +#define TGNMLEN 32 + +union record { + char charptr[RECORDSIZE]; + struct header { + char name[NAMSIZ]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char linkflag; + char linkname[NAMSIZ]; + char magic[8]; + char uname[TUNMLEN]; + char gname[TGNMLEN]; + char devmajor[8]; + char devminor[8]; + } header; +}; + +/* The magic field is filled with this if uname and gname are valid. */ +#define TMAGIC "ustar " /* 7 chars and a null */ + +/* + * file-function prototypes + */ +static int ascmagic(request_rec *, unsigned char *, int); +static int is_tar(unsigned char *, int); +static int softmagic(request_rec *, unsigned char *, int); +static void tryit(request_rec *, unsigned char *, int, int); +static int zmagic(request_rec *, unsigned char *, int); + +static int getvalue(server_rec *, struct magic *, char **); +static int hextoint(int); +static char *getstr(server_rec *, char *, char *, int, int *); +static int parse(server_rec *, pool *p, char *, int); + +static int match(request_rec *, unsigned char *, int); +static int mget(request_rec *, union VALUETYPE *, unsigned char *, + struct magic *, int); +static int mcheck(request_rec *, union VALUETYPE *, struct magic *); +static void mprint(request_rec *, union VALUETYPE *, struct magic *); + +static int uncompress(request_rec *, int, + unsigned char **, int); +static long from_oct(int, char *); +static int fsmagic(request_rec *r, const char *fn); + +/* + * includes for ASCII substring recognition formerly "names.h" in file + * command + * + * Original notes: names and types used by ascmagic in file(1). These tokens are + * here because they can appear anywhere in the first HOWMANY bytes, while + * tokens in /etc/magic must appear at fixed offsets into the file. Don't + * make HOWMANY too high unless you have a very fast CPU. + */ + +/* these types are used to index the table 'types': keep em in sync! */ +/* HTML inserted in first because this is a web server module now */ +#define L_HTML 0 /* HTML */ +#define L_C 1 /* first and foremost on UNIX */ +#define L_FORT 2 /* the oldest one */ +#define L_MAKE 3 /* Makefiles */ +#define L_PLI 4 /* PL/1 */ +#define L_MACH 5 /* some kinda assembler */ +#define L_ENG 6 /* English */ +#define L_PAS 7 /* Pascal */ +#define L_MAIL 8 /* Electronic mail */ +#define L_NEWS 9 /* Usenet Netnews */ + +static char *types[] = +{ + "text/html", /* HTML */ + "text/plain", /* "c program text", */ + "text/plain", /* "fortran program text", */ + "text/plain", /* "make commands text", */ + "text/plain", /* "pl/1 program text", */ + "text/plain", /* "assembler program text", */ + "text/plain", /* "English text", */ + "text/plain", /* "pascal program text", */ + "message/rfc822", /* "mail text", */ + "message/news", /* "news text", */ + "application/binary", /* "can't happen error on names.h/types", */ + 0 +}; + +static struct names { + char *name; + short type; +} names[] = { + + /* These must be sorted by eye for optimal hit rate */ + /* Add to this list only after substantial meditation */ + { + "", L_HTML + }, + { + "", L_HTML + }, + { + "", L_HTML + }, + { + "", L_HTML + }, + { + "", L_HTML + }, + { + "<TITLE>", L_HTML + }, + { + "<h1>", L_HTML + }, + { + "<H1>", L_HTML + }, + { + "<!--", L_HTML + }, + { + "<!DOCTYPE HTML", L_HTML + }, + { + "/*", L_C + }, /* must precede "The", "the", etc. */ + { + "#include", L_C + }, + { + "char", L_C + }, + { + "The", L_ENG + }, + { + "the", L_ENG + }, + { + "double", L_C + }, + { + "extern", L_C + }, + { + "float", L_C + }, + { + "real", L_C + }, + { + "struct", L_C + }, + { + "union", L_C + }, + { + "CFLAGS", L_MAKE + }, + { + "LDFLAGS", L_MAKE + }, + { + "all:", L_MAKE + }, + { + ".PRECIOUS", L_MAKE + }, + /* + * Too many files of text have these words in them. Find another way to + * recognize Fortrash. + */ +#ifdef NOTDEF + { + "subroutine", L_FORT + }, + { + "function", L_FORT + }, + { + "block", L_FORT + }, + { + "common", L_FORT + }, + { + "dimension", L_FORT + }, + { + "integer", L_FORT + }, + { + "data", L_FORT + }, +#endif /* NOTDEF */ + { + ".ascii", L_MACH + }, + { + ".asciiz", L_MACH + }, + { + ".byte", L_MACH + }, + { + ".even", L_MACH + }, + { + ".globl", L_MACH + }, + { + "clr", L_MACH + }, + { + "(input,", L_PAS + }, + { + "dcl", L_PLI + }, + { + "Received:", L_MAIL + }, + { + ">From", L_MAIL + }, + { + "Return-Path:", L_MAIL + }, + { + "Cc:", L_MAIL + }, + { + "Newsgroups:", L_NEWS + }, + { + "Path:", L_NEWS + }, + { + "Organization:", L_NEWS + }, + { + NULL, 0 + } +}; + +#define NNAMES ((sizeof(names)/sizeof(struct names)) - 1) + +/* + * Result String List (RSL) + * + * The file(1) command prints its output. Instead, we store the various + * "printed" strings in a list (allocating memory as we go) and concatenate + * them at the end when we finally know how much space they'll need. + */ + +typedef struct magic_rsl_s { + char *str; /* string, possibly a fragment */ + struct magic_rsl_s *next; /* pointer to next fragment */ +} magic_rsl; + +/* + * Apache module configuration structures + */ + +/* per-server info */ +typedef struct { + char *magicfile; /* where magic be found */ + struct magic *magic; /* head of magic config list */ + struct magic *last; +} magic_server_config_rec; + +/* per-request info */ +typedef struct { + magic_rsl *head; /* result string list */ + magic_rsl *tail; + unsigned suf_recursion; /* recursion depth in suffix check */ +} magic_req_rec; + +/* + * configuration functions - called by Apache API routines + */ + +module mime_magic_module; + +static void *create_magic_server_config(pool *p, server_rec *d) +{ + /* allocate the config - use pcalloc because it needs to be zeroed */ + return ap_pcalloc(p, sizeof(magic_server_config_rec)); +} + +static void *merge_magic_server_config(pool *p, void *basev, void *addv) +{ + magic_server_config_rec *base = (magic_server_config_rec *) basev; + magic_server_config_rec *add = (magic_server_config_rec *) addv; + magic_server_config_rec *new = (magic_server_config_rec *) + ap_palloc(p, sizeof(magic_server_config_rec)); + + new->magicfile = add->magicfile ? add->magicfile : base->magicfile; + new->magic = NULL; + new->last = NULL; + return new; +} + +static const char *set_magicfile(cmd_parms *cmd, char *d, char *arg) +{ + magic_server_config_rec *conf = (magic_server_config_rec *) + ap_get_module_config(cmd->server->module_config, + &mime_magic_module); + + if (!conf) { + return MODNAME ": server structure not allocated"; + } + conf->magicfile = arg; + return NULL; +} + +/* + * configuration file commands - exported to Apache API + */ + +static const command_rec mime_magic_cmds[] = +{ + {"MimeMagicFile", set_magicfile, NULL, RSRC_CONF, TAKE1, + "Path to MIME Magic file (in file(1) format)"}, + {NULL} +}; + +/* + * RSL (result string list) processing routines + * + * These collect strings that would have been printed in fragments by file(1) + * into a list of magic_rsl structures with the strings. When complete, + * they're concatenated together to become the MIME content and encoding + * types. + * + * return value conventions for these functions: functions which return int: + * failure = -1, other = result functions which return pointers: failure = 0, + * other = result + */ + +/* allocate a per-request structure and put it in the request record */ +static magic_req_rec *magic_set_config(request_rec *r) +{ + magic_req_rec *req_dat = (magic_req_rec *) ap_palloc(r->pool, + sizeof(magic_req_rec)); + + req_dat->head = req_dat->tail = (magic_rsl *) NULL; + ap_set_module_config(r->request_config, &mime_magic_module, req_dat); + return req_dat; +} + +/* add a string to the result string list for this request */ +/* it is the responsibility of the caller to allocate "str" */ +static int magic_rsl_add(request_rec *r, char *str) +{ + magic_req_rec *req_dat = (magic_req_rec *) + ap_get_module_config(r->request_config, &mime_magic_module); + magic_rsl *rsl; + + /* make sure we have a list to put it in */ + if (!req_dat) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, + MODNAME ": request config should not be NULL"); + if (!(req_dat = magic_set_config(r))) { + /* failure */ + return -1; + } + } + + /* allocate the list entry */ + rsl = (magic_rsl *) ap_palloc(r->pool, sizeof(magic_rsl)); + + /* fill it */ + rsl->str = str; + rsl->next = (magic_rsl *) NULL; + + /* append to the list */ + if (req_dat->head && req_dat->tail) { + req_dat->tail->next = rsl; + req_dat->tail = rsl; + } + else { + req_dat->head = req_dat->tail = rsl; + } + + /* success */ + return 0; +} + +/* RSL hook for puts-type functions */ +static int magic_rsl_puts(request_rec *r, char *str) +{ + return magic_rsl_add(r, str); +} + +/* RSL hook for printf-type functions */ +static int magic_rsl_printf(request_rec *r, char *str,...) +{ + va_list ap; + + char buf[MAXMIMESTRING]; + + /* assemble the string into the buffer */ + va_start(ap, str); + ap_vsnprintf(buf, sizeof(buf), str, ap); + va_end(ap); + + /* add the buffer to the list */ + return magic_rsl_add(r, strdup(buf)); +} + +/* RSL hook for putchar-type functions */ +static int magic_rsl_putchar(request_rec *r, char c) +{ + char str[2]; + + /* high overhead for 1 char - just hope they don't do this much */ + str[0] = c; + str[1] = '\0'; + return magic_rsl_add(r, str); +} + +/* allocate and copy a contiguous string from a result string list */ +static char *rsl_strdup(request_rec *r, int start_frag, int start_pos, int len) +{ + char *result; /* return value */ + int cur_frag, /* current fragment number/counter */ + cur_pos, /* current position within fragment */ + res_pos; /* position in result string */ + magic_rsl *frag; /* list-traversal pointer */ + magic_req_rec *req_dat = (magic_req_rec *) + ap_get_module_config(r->request_config, &mime_magic_module); + + /* allocate the result string */ + result = (char *) ap_palloc(r->pool, len + 1); + + /* loop through and collect the string */ + res_pos = 0; + for (frag = req_dat->head, cur_frag = 0; + frag->next; + frag = frag->next, cur_frag++) { + /* loop to the first fragment */ + if (cur_frag < start_frag) + continue; + + /* loop through and collect chars */ + for (cur_pos = (cur_frag == start_frag) ? start_pos : 0; + frag->str[cur_pos]; + cur_pos++) { + if (cur_frag >= start_frag + && cur_pos >= start_pos + && res_pos <= len) { + result[res_pos++] = frag->str[cur_pos]; + if (res_pos > len) { + break; + } + } + } + } + + /* clean up and return */ + result[res_pos] = 0; +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": rsl_strdup() %d chars: %s", res_pos - 1, result); +#endif + return result; +} + +/* states for the state-machine algorithm in magic_rsl_to_request() */ +typedef enum { + rsl_leading_space, rsl_type, rsl_subtype, rsl_separator, rsl_encoding +} rsl_states; + +/* process the RSL and set the MIME info in the request record */ +static int magic_rsl_to_request(request_rec *r) +{ + int cur_frag, /* current fragment number/counter */ + cur_pos, /* current position within fragment */ + type_frag, /* content type starting point: fragment */ + type_pos, /* content type starting point: position */ + type_len, /* content type length */ + encoding_frag, /* content encoding starting point: fragment */ + encoding_pos, /* content encoding starting point: position */ + encoding_len; /* content encoding length */ + + magic_rsl *frag; /* list-traversal pointer */ + rsl_states state; + + magic_req_rec *req_dat = (magic_req_rec *) + ap_get_module_config(r->request_config, &mime_magic_module); + + /* check if we have a result */ + if (!req_dat || !req_dat->head) { + /* empty - no match, we defer to other Apache modules */ + return DECLINED; + } + + /* start searching for the type and encoding */ + state = rsl_leading_space; + type_frag = type_pos = type_len = 0; + encoding_frag = encoding_pos = encoding_len = 0; + for (frag = req_dat->head, cur_frag = 0; + frag && frag->next; + frag = frag->next, cur_frag++) { + /* loop through the characters in the fragment */ + for (cur_pos = 0; frag->str[cur_pos]; cur_pos++) { + if (ap_isspace(frag->str[cur_pos])) { + /* process whitespace actions for each state */ + if (state == rsl_leading_space) { + /* eat whitespace in this state */ + continue; + } + else if (state == rsl_type) { + /* whitespace: type has no slash! */ + return DECLINED; + } + else if (state == rsl_subtype) { + /* whitespace: end of MIME type */ + state++; + continue; + } + else if (state == rsl_separator) { + /* eat whitespace in this state */ + continue; + } + else if (state == rsl_encoding) { + /* whitespace: end of MIME encoding */ + /* we're done */ + frag = req_dat->tail; + break; + } + else { + /* should not be possible */ + /* abandon malfunctioning module */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, + MODNAME ": bad state %d (ws)", state); + return DECLINED; + } + /* NOTREACHED */ + } + else if (state == rsl_type && + frag->str[cur_pos] == '/') { + /* copy the char and go to rsl_subtype state */ + type_len++; + state++; + } + else { + /* process non-space actions for each state */ + if (state == rsl_leading_space) { + /* non-space: begin MIME type */ + state++; + type_frag = cur_frag; + type_pos = cur_pos; + type_len = 1; + continue; + } + else if (state == rsl_type || + state == rsl_subtype) { + /* non-space: adds to type */ + type_len++; + continue; + } + else if (state == rsl_separator) { + /* non-space: begin MIME encoding */ + state++; + encoding_frag = cur_frag; + encoding_pos = cur_pos; + encoding_len = 1; + continue; + } + else if (state == rsl_encoding) { + /* non-space: adds to encoding */ + encoding_len++; + continue; + } + else { + /* should not be possible */ + /* abandon malfunctioning module */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, + MODNAME ": bad state %d (ns)", state); + return DECLINED; + } + /* NOTREACHED */ + } + /* NOTREACHED */ + } + } + + /* if we ended prior to state rsl_subtype, we had incomplete info */ + if (state != rsl_subtype && state != rsl_separator && + state != rsl_encoding) { + /* defer to other modules */ + return DECLINED; + } + + /* save the info in the request record */ + if (state == rsl_subtype || state == rsl_encoding || + state == rsl_encoding) { + char *tmp; + tmp = rsl_strdup(r, type_frag, type_pos, type_len); + /* XXX: this could be done at config time I'm sure... but I'm + * confused by all this magic_rsl stuff. -djg */ + ap_content_type_tolower(tmp); + r->content_type = tmp; + } + if (state == rsl_encoding) { + char *tmp; + tmp = rsl_strdup(r, encoding_frag, + encoding_pos, encoding_len); + /* XXX: this could be done at config time I'm sure... but I'm + * confused by all this magic_rsl stuff. -djg */ + ap_str_tolower(tmp); + r->content_encoding = tmp; + } + + /* detect memory allocation errors */ + if (!r->content_type || + (state == rsl_encoding && !r->content_encoding)) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* success! */ + return OK; +} + +/* + * magic_process - process input file r Apache API request record + * (formerly called "process" in file command, prefix added for clarity) Opens + * the file and reads a fixed-size buffer to begin processing the contents. + */ +static int magic_process(request_rec *r) +{ + int fd = 0; + unsigned char buf[HOWMANY + 1]; /* one extra for terminating '\0' */ + int nbytes = 0; /* number of bytes read from a datafile */ + int result; + + /* + * first try judging the file based on its filesystem status + */ + switch ((result = fsmagic(r, r->filename))) { + case DONE: + magic_rsl_putchar(r, '\n'); + return OK; + case OK: + break; + default: + /* fatal error, bail out */ + return result; + } + + if ((fd = ap_popenf(r->pool, r->filename, O_RDONLY, 0)) < 0) { + /* We can't open it, but we were able to stat it. */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + MODNAME ": can't read `%s'", r->filename); + /* let some other handler decide what the problem is */ + return DECLINED; + } + + /* + * try looking at the first HOWMANY bytes + */ + if ((nbytes = read(fd, (char *) buf, sizeof(buf) - 1)) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + MODNAME ": read failed: %s", r->filename); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (nbytes == 0) + magic_rsl_puts(r, MIME_TEXT_UNKNOWN); + else { + buf[nbytes++] = '\0'; /* null-terminate it */ + tryit(r, buf, nbytes, 1); + } + + (void) ap_pclosef(r->pool, fd); + (void) magic_rsl_putchar(r, '\n'); + + return OK; +} + + +static void tryit(request_rec *r, unsigned char *buf, int nb, int checkzmagic) +{ + /* + * Try compression stuff + */ + if (checkzmagic == 1) { + if (zmagic(r, buf, nb) == 1) + return; + } + + /* + * try tests in /etc/magic (or surrogate magic file) + */ + if (softmagic(r, buf, nb) == 1) + return; + + /* + * try known keywords, check for ascii-ness too. + */ + if (ascmagic(r, buf, nb) == 1) + return; + + /* + * abandon hope, all ye who remain here + */ + magic_rsl_puts(r, MIME_BINARY_UNKNOWN); +} + +#define EATAB {while (ap_isspace((unsigned char) *l)) ++l;} + +/* + * apprentice - load configuration from the magic file r + * API request record + */ +static int apprentice(server_rec *s, pool *p) +{ + FILE *f; + char line[BUFSIZ + 1]; + int errs = 0; + int lineno; +#if MIME_MAGIC_DEBUG + int rule = 0; + struct magic *m, *prevm; +#endif + char *fname; + + magic_server_config_rec *conf = (magic_server_config_rec *) + ap_get_module_config(s->module_config, &mime_magic_module); + + fname = ap_server_root_relative(p, conf->magicfile); + f = ap_pfopen(p, fname, "r"); + if (f == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + MODNAME ": can't read magic file %s", fname); + return -1; + } + + /* set up the magic list (empty) */ + conf->magic = conf->last = NULL; + + /* parse it */ + for (lineno = 1; fgets(line, BUFSIZ, f) != NULL; lineno++) { + int ws_offset; + + /* delete newline */ + if (line[0]) { + line[strlen(line) - 1] = '\0'; + } + + /* skip leading whitespace */ + ws_offset = 0; + while (line[ws_offset] && ap_isspace(line[ws_offset])) { + ws_offset++; + } + + /* skip blank lines */ + if (line[ws_offset] == 0) { + continue; + } + + /* comment, do not parse */ + if (line[ws_offset] == '#') + continue; + +#if MIME_MAGIC_DEBUG + /* if we get here, we're going to use it so count it */ + rule++; +#endif + + /* parse it */ + if (parse(s, p, line + ws_offset, lineno) != 0) + ++errs; + } + + (void) ap_pfclose(p, f); + +#if MIME_MAGIC_DEBUG + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, s, + MODNAME ": apprentice conf=%x file=%s m=%s m->next=%s last=%s", + conf, + conf->magicfile ? conf->magicfile : "NULL", + conf->magic ? "set" : "NULL", + (conf->magic && conf->magic->next) ? "set" : "NULL", + conf->last ? "set" : "NULL"); + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, s, + MODNAME ": apprentice read %d lines, %d rules, %d errors", + lineno, rule, errs); +#endif + +#if MIME_MAGIC_DEBUG + prevm = 0; + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, s, + MODNAME ": apprentice test"); + for (m = conf->magic; m; m = m->next) { + if (ap_isprint((((unsigned long) m) >> 24) & 255) && + ap_isprint((((unsigned long) m) >> 16) & 255) && + ap_isprint((((unsigned long) m) >> 8) & 255) && + ap_isprint(((unsigned long) m) & 255)) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, s, + MODNAME ": apprentice: POINTER CLOBBERED! " + "m=\"%c%c%c%c\" line=%d", + (((unsigned long) m) >> 24) & 255, + (((unsigned long) m) >> 16) & 255, + (((unsigned long) m) >> 8) & 255, + ((unsigned long) m) & 255, + prevm ? prevm->lineno : -1); + break; + } + prevm = m; + } +#endif + + return (errs ? -1 : 0); +} + +/* + * extend the sign bit if the comparison is to be signed + */ +static unsigned long signextend(server_rec *s, struct magic *m, unsigned long v) +{ + if (!(m->flag & UNSIGNED)) + switch (m->type) { + /* + * Do not remove the casts below. They are vital. When later + * compared with the data, the sign extension must have happened. + */ + case BYTE: + v = (char) v; + break; + case SHORT: + case BESHORT: + case LESHORT: + v = (short) v; + break; + case DATE: + case BEDATE: + case LEDATE: + case LONG: + case BELONG: + case LELONG: + v = (long) v; + break; + case STRING: + break; + default: + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, s, + MODNAME ": can't happen: m->type=%d", m->type); + return -1; + } + return v; +} + +/* + * parse one line from magic file, put into magic[index++] if valid + */ +static int parse(server_rec *serv, pool *p, char *l, int lineno) +{ + struct magic *m; + char *t, *s; + magic_server_config_rec *conf = (magic_server_config_rec *) + ap_get_module_config(serv->module_config, &mime_magic_module); + + /* allocate magic structure entry */ + m = (struct magic *) ap_pcalloc(p, sizeof(struct magic)); + + /* append to linked list */ + m->next = NULL; + if (!conf->magic || !conf->last) { + conf->magic = conf->last = m; + } + else { + conf->last->next = m; + conf->last = m; + } + + /* set values in magic structure */ + m->flag = 0; + m->cont_level = 0; + m->lineno = lineno; + + while (*l == '>') { + ++l; /* step over */ + m->cont_level++; + } + + if (m->cont_level != 0 && *l == '(') { + ++l; /* step over */ + m->flag |= INDIR; + } + + /* get offset, then skip over it */ + m->offset = (int) strtol(l, &t, 0); + if (l == t) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, serv, + MODNAME ": offset %s invalid", l); + } + l = t; + + if (m->flag & INDIR) { + m->in.type = LONG; + m->in.offset = 0; + /* + * read [.lbs][+-]nnnnn) + */ + if (*l == '.') { + switch (*++l) { + case 'l': + m->in.type = LONG; + break; + case 's': + m->in.type = SHORT; + break; + case 'b': + m->in.type = BYTE; + break; + default: + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, serv, + MODNAME ": indirect offset type %c invalid", *l); + break; + } + l++; + } + s = l; + if (*l == '+' || *l == '-') + l++; + if (ap_isdigit((unsigned char) *l)) { + m->in.offset = strtol(l, &t, 0); + if (*s == '-') + m->in.offset = -m->in.offset; + } + else + t = l; + if (*t++ != ')') { + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, serv, + MODNAME ": missing ')' in indirect offset"); + } + l = t; + } + + + while (ap_isdigit((unsigned char) *l)) + ++l; + EATAB; + +#define NBYTE 4 +#define NSHORT 5 +#define NLONG 4 +#define NSTRING 6 +#define NDATE 4 +#define NBESHORT 7 +#define NBELONG 6 +#define NBEDATE 6 +#define NLESHORT 7 +#define NLELONG 6 +#define NLEDATE 6 + + if (*l == 'u') { + ++l; + m->flag |= UNSIGNED; + } + + /* get type, skip it */ + if (strncmp(l, "byte", NBYTE) == 0) { + m->type = BYTE; + l += NBYTE; + } + else if (strncmp(l, "short", NSHORT) == 0) { + m->type = SHORT; + l += NSHORT; + } + else if (strncmp(l, "long", NLONG) == 0) { + m->type = LONG; + l += NLONG; + } + else if (strncmp(l, "string", NSTRING) == 0) { + m->type = STRING; + l += NSTRING; + } + else if (strncmp(l, "date", NDATE) == 0) { + m->type = DATE; + l += NDATE; + } + else if (strncmp(l, "beshort", NBESHORT) == 0) { + m->type = BESHORT; + l += NBESHORT; + } + else if (strncmp(l, "belong", NBELONG) == 0) { + m->type = BELONG; + l += NBELONG; + } + else if (strncmp(l, "bedate", NBEDATE) == 0) { + m->type = BEDATE; + l += NBEDATE; + } + else if (strncmp(l, "leshort", NLESHORT) == 0) { + m->type = LESHORT; + l += NLESHORT; + } + else if (strncmp(l, "lelong", NLELONG) == 0) { + m->type = LELONG; + l += NLELONG; + } + else if (strncmp(l, "ledate", NLEDATE) == 0) { + m->type = LEDATE; + l += NLEDATE; + } + else { + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, serv, + MODNAME ": type %s invalid", l); + return -1; + } + /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */ + if (*l == '&') { + ++l; + m->mask = signextend(serv, m, strtol(l, &l, 0)); + } + else + m->mask = ~0L; + EATAB; + + switch (*l) { + case '>': + case '<': + /* Old-style anding: "0 byte &0x80 dynamically linked" */ + case '&': + case '^': + case '=': + m->reln = *l; + ++l; + break; + case '!': + if (m->type != STRING) { + m->reln = *l; + ++l; + break; + } + /* FALL THROUGH */ + default: + if (*l == 'x' && ap_isspace((unsigned char) l[1])) { + m->reln = *l; + ++l; + goto GetDesc; /* Bill The Cat */ + } + m->reln = '='; + break; + } + EATAB; + + if (getvalue(serv, m, &l)) + return -1; + /* + * now get last part - the description + */ + GetDesc: + EATAB; + if (l[0] == '\b') { + ++l; + m->nospflag = 1; + } + else if ((l[0] == '\\') && (l[1] == 'b')) { + ++l; + ++l; + m->nospflag = 1; + } + else + m->nospflag = 0; + strncpy(m->desc, l, sizeof(m->desc) - 1); + m->desc[sizeof(m->desc) - 1] = '\0'; + +#if MIME_MAGIC_DEBUG + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, serv, + MODNAME ": parse line=%d m=%x next=%x cont=%d desc=%s", + lineno, m, m->next, m->cont_level, m->desc); +#endif /* MIME_MAGIC_DEBUG */ + + return 0; +} + +/* + * Read a numeric value from a pointer, into the value union of a magic + * pointer, according to the magic type. Update the string pointer to point + * just after the number read. Return 0 for success, non-zero for failure. + */ +static int getvalue(server_rec *s, struct magic *m, char **p) +{ + int slen; + + if (m->type == STRING) { + *p = getstr(s, *p, m->value.s, sizeof(m->value.s), &slen); + m->vallen = slen; + } + else if (m->reln != 'x') + m->value.l = signextend(s, m, strtol(*p, p, 0)); + return 0; +} + +/* + * Convert a string containing C character escapes. Stop at an unescaped + * space or tab. Copy the converted version to "p", returning its length in + * *slen. Return updated scan pointer as function result. + */ +static char *getstr(server_rec *serv, register char *s, register char *p, + int plen, int *slen) +{ + char *origs = s, *origp = p; + char *pmax = p + plen - 1; + register int c; + register int val; + + while ((c = *s++) != '\0') { + if (ap_isspace((unsigned char) c)) + break; + if (p >= pmax) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, serv, + MODNAME ": string too long: %s", origs); + break; + } + if (c == '\\') { + switch (c = *s++) { + + case '\0': + goto out; + + default: + *p++ = (char) c; + break; + + case 'n': + *p++ = '\n'; + break; + + case 'r': + *p++ = '\r'; + break; + + case 'b': + *p++ = '\b'; + break; + + case 't': + *p++ = '\t'; + break; + + case 'f': + *p++ = '\f'; + break; + + case 'v': + *p++ = '\v'; + break; + + /* \ and up to 3 octal digits */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val = c - '0'; + c = *s++; /* try for 2 */ + if (c >= '0' && c <= '7') { + val = (val << 3) | (c - '0'); + c = *s++; /* try for 3 */ + if (c >= '0' && c <= '7') + val = (val << 3) | (c - '0'); + else + --s; + } + else + --s; + *p++ = (char) val; + break; + + /* \x and up to 3 hex digits */ + case 'x': + val = 'x'; /* Default if no digits */ + c = hextoint(*s++); /* Get next char */ + if (c >= 0) { + val = c; + c = hextoint(*s++); + if (c >= 0) { + val = (val << 4) + c; + c = hextoint(*s++); + if (c >= 0) { + val = (val << 4) + c; + } + else + --s; + } + else + --s; + } + else + --s; + *p++ = (char) val; + break; + } + } + else + *p++ = (char) c; + } + out: + *p = '\0'; + *slen = p - origp; + return s; +} + + +/* Single hex char to int; -1 if not a hex char. */ +static int hextoint(int c) +{ + if (ap_isdigit((unsigned char) c)) + return c - '0'; + if ((c >= 'a') && (c <= 'f')) + return c + 10 - 'a'; + if ((c >= 'A') && (c <= 'F')) + return c + 10 - 'A'; + return -1; +} + + +/* + * return DONE to indicate it's been handled + * return OK to indicate it's a regular file still needing handling + * other returns indicate a failure of some sort + */ +static int fsmagic(request_rec *r, const char *fn) +{ + switch (r->finfo.st_mode & S_IFMT) { + case S_IFDIR: + magic_rsl_puts(r, DIR_MAGIC_TYPE); + return DONE; + case S_IFCHR: + /* + * (void) magic_rsl_printf(r,"character special (%d/%d)", + * major(sb->st_rdev), minor(sb->st_rdev)); + */ + (void) magic_rsl_puts(r, MIME_BINARY_UNKNOWN); + return DONE; +#ifdef S_IFBLK + case S_IFBLK: + /* + * (void) magic_rsl_printf(r,"block special (%d/%d)", + * major(sb->st_rdev), minor(sb->st_rdev)); + */ + (void) magic_rsl_puts(r, MIME_BINARY_UNKNOWN); + return DONE; + /* TODO add code to handle V7 MUX and Blit MUX files */ +#endif +#ifdef S_IFIFO + case S_IFIFO: + /* + * magic_rsl_puts(r,"fifo (named pipe)"); + */ + (void) magic_rsl_puts(r, MIME_BINARY_UNKNOWN); + return DONE; +#endif +#ifdef S_IFLNK + case S_IFLNK: + /* We used stat(), the only possible reason for this is that the + * symlink is broken. + */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, + MODNAME ": broken symlink (%s)", fn); + return HTTP_INTERNAL_SERVER_ERROR; +#endif +#ifdef S_IFSOCK +#ifndef __COHERENT__ + case S_IFSOCK: + magic_rsl_puts(r, MIME_BINARY_UNKNOWN); + return DONE; +#endif +#endif + case S_IFREG: + break; + default: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, + MODNAME ": invalid mode 0%o.", (unsigned int)r->finfo.st_mode); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* + * regular file, check next possibility + */ + if (r->finfo.st_size == 0) { + magic_rsl_puts(r, MIME_TEXT_UNKNOWN); + return DONE; + } + return OK; +} + +/* + * softmagic - lookup one file in database (already read from /etc/magic by + * apprentice.c). Passed the name and FILE * of one file to be typed. + */ + /* ARGSUSED1 *//* nbytes passed for regularity, maybe need later */ +static int softmagic(request_rec *r, unsigned char *buf, int nbytes) +{ + if (match(r, buf, nbytes)) + return 1; + + return 0; +} + +/* + * Go through the whole list, stopping if you find a match. Process all the + * continuations of that match before returning. + * + * We support multi-level continuations: + * + * At any time when processing a successful top-level match, there is a current + * continuation level; it represents the level of the last successfully + * matched continuation. + * + * Continuations above that level are skipped as, if we see one, it means that + * the continuation that controls them - i.e, the lower-level continuation + * preceding them - failed to match. + * + * Continuations below that level are processed as, if we see one, it means + * we've finished processing or skipping higher-level continuations under the + * control of a successful or unsuccessful lower-level continuation, and are + * now seeing the next lower-level continuation and should process it. The + * current continuation level reverts to the level of the one we're seeing. + * + * Continuations at the current level are processed as, if we see one, there's + * no lower-level continuation that may have failed. + * + * If a continuation matches, we bump the current continuation level so that + * higher-level continuations are processed. + */ +static int match(request_rec *r, unsigned char *s, int nbytes) +{ +#if MIME_MAGIC_DEBUG + int rule_counter = 0; +#endif + int cont_level = 0; + int need_separator = 0; + union VALUETYPE p; + magic_server_config_rec *conf = (magic_server_config_rec *) + ap_get_module_config(r->server->module_config, &mime_magic_module); + struct magic *m; + +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": match conf=%x file=%s m=%s m->next=%s last=%s", + conf, + conf->magicfile ? conf->magicfile : "NULL", + conf->magic ? "set" : "NULL", + (conf->magic && conf->magic->next) ? "set" : "NULL", + conf->last ? "set" : "NULL"); +#endif + +#if MIME_MAGIC_DEBUG + for (m = conf->magic; m; m = m->next) { + if (ap_isprint((((unsigned long) m) >> 24) & 255) && + ap_isprint((((unsigned long) m) >> 16) & 255) && + ap_isprint((((unsigned long) m) >> 8) & 255) && + ap_isprint(((unsigned long) m) & 255)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": match: POINTER CLOBBERED! " + "m=\"%c%c%c%c\"", + (((unsigned long) m) >> 24) & 255, + (((unsigned long) m) >> 16) & 255, + (((unsigned long) m) >> 8) & 255, + ((unsigned long) m) & 255); + break; + } + } +#endif + + for (m = conf->magic; m; m = m->next) { +#if MIME_MAGIC_DEBUG + rule_counter++; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": line=%d desc=%s", m->lineno, m->desc); +#endif + + /* check if main entry matches */ + if (!mget(r, &p, s, m, nbytes) || + !mcheck(r, &p, m)) { + struct magic *m_cont; + + /* + * main entry didn't match, flush its continuations + */ + if (!m->next || (m->next->cont_level == 0)) { + continue; + } + + m_cont = m->next; + while (m_cont && (m_cont->cont_level != 0)) { +#if MIME_MAGIC_DEBUG + rule_counter++; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": line=%d mc=%x mc->next=%x cont=%d desc=%s", + m_cont->lineno, m_cont, + m_cont->next, m_cont->cont_level, + m_cont->desc); +#endif + /* + * this trick allows us to keep *m in sync when the continue + * advances the pointer + */ + m = m_cont; + m_cont = m_cont->next; + } + continue; + } + + /* if we get here, the main entry rule was a match */ + /* this will be the last run through the loop */ +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": rule matched, line=%d type=%d %s", + m->lineno, m->type, + (m->type == STRING) ? m->value.s : ""); +#endif + + /* print the match */ + mprint(r, &p, m); + + /* + * If we printed something, we'll need to print a blank before we + * print something else. + */ + if (m->desc[0]) + need_separator = 1; + /* and any continuations that match */ + cont_level++; + /* + * while (m && m->next && m->next->cont_level != 0 && ( m = m->next + * )) + */ + m = m->next; + while (m && (m->cont_level != 0)) { +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": match line=%d cont=%d type=%d %s", + m->lineno, m->cont_level, m->type, + (m->type == STRING) ? m->value.s : ""); +#endif + if (cont_level >= m->cont_level) { + if (cont_level > m->cont_level) { + /* + * We're at the end of the level "cont_level" + * continuations. + */ + cont_level = m->cont_level; + } + if (mget(r, &p, s, m, nbytes) && + mcheck(r, &p, m)) { + /* + * This continuation matched. Print its message, with a + * blank before it if the previous item printed and this + * item isn't empty. + */ + /* space if previous printed */ + if (need_separator + && (m->nospflag == 0) + && (m->desc[0] != '\0') + ) { + (void) magic_rsl_putchar(r, ' '); + need_separator = 0; + } + mprint(r, &p, m); + if (m->desc[0]) + need_separator = 1; + + /* + * If we see any continuations at a higher level, process + * them. + */ + cont_level++; + } + } + + /* move to next continuation record */ + m = m->next; + } +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": matched after %d rules", rule_counter); +#endif + return 1; /* all through */ + } +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": failed after %d rules", rule_counter); +#endif + return 0; /* no match at all */ +} + +static void mprint(request_rec *r, union VALUETYPE *p, struct magic *m) +{ + char *pp, *rt; + unsigned long v; + + switch (m->type) { + case BYTE: + v = p->b; + break; + + case SHORT: + case BESHORT: + case LESHORT: + v = p->h; + break; + + case LONG: + case BELONG: + case LELONG: + v = p->l; + break; + + case STRING: + if (m->reln == '=') { + (void) magic_rsl_printf(r, m->desc, m->value.s); + } + else { + (void) magic_rsl_printf(r, m->desc, p->s); + } + return; + + case DATE: + case BEDATE: + case LEDATE: + /* XXX: not multithread safe */ + pp = ctime((time_t *) & p->l); + if ((rt = strchr(pp, '\n')) != NULL) + *rt = '\0'; + (void) magic_rsl_printf(r, m->desc, pp); + return; + default: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, + MODNAME ": invalid m->type (%d) in mprint().", + m->type); + return; + } + + v = signextend(r->server, m, v) & m->mask; + (void) magic_rsl_printf(r, m->desc, (unsigned long) v); +} + +/* + * Convert the byte order of the data we are looking at + */ +static int mconvert(request_rec *r, union VALUETYPE *p, struct magic *m) +{ + char *rt; + + switch (m->type) { + case BYTE: + case SHORT: + case LONG: + case DATE: + return 1; + case STRING: + /* Null terminate and eat the return */ + p->s[sizeof(p->s) - 1] = '\0'; + if ((rt = strchr(p->s, '\n')) != NULL) + *rt = '\0'; + return 1; + case BESHORT: + p->h = (short) ((p->hs[0] << 8) | (p->hs[1])); + return 1; + case BELONG: + case BEDATE: + p->l = (long) + ((p->hl[0] << 24) | (p->hl[1] << 16) | (p->hl[2] << 8) | (p->hl[3])); + return 1; + case LESHORT: + p->h = (short) ((p->hs[1] << 8) | (p->hs[0])); + return 1; + case LELONG: + case LEDATE: + p->l = (long) + ((p->hl[3] << 24) | (p->hl[2] << 16) | (p->hl[1] << 8) | (p->hl[0])); + return 1; + default: + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, + MODNAME ": invalid type %d in mconvert().", m->type); + return 0; + } +} + + +static int mget(request_rec *r, union VALUETYPE *p, unsigned char *s, + struct magic *m, int nbytes) +{ + long offset = m->offset; + + if (offset + sizeof(union VALUETYPE) > nbytes) + return 0; + + memcpy(p, s + offset, sizeof(union VALUETYPE)); + + if (!mconvert(r, p, m)) + return 0; + + if (m->flag & INDIR) { + + switch (m->in.type) { + case BYTE: + offset = p->b + m->in.offset; + break; + case SHORT: + offset = p->h + m->in.offset; + break; + case LONG: + offset = p->l + m->in.offset; + break; + } + + if (offset + sizeof(union VALUETYPE) > nbytes) + return 0; + + memcpy(p, s + offset, sizeof(union VALUETYPE)); + + if (!mconvert(r, p, m)) + return 0; + } + return 1; +} + +static int mcheck(request_rec *r, union VALUETYPE *p, struct magic *m) +{ + register unsigned long l = m->value.l; + register unsigned long v; + int matched; + + if ((m->value.s[0] == 'x') && (m->value.s[1] == '\0')) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, + MODNAME ": BOINK"); + return 1; + } + + switch (m->type) { + case BYTE: + v = p->b; + break; + + case SHORT: + case BESHORT: + case LESHORT: + v = p->h; + break; + + case LONG: + case BELONG: + case LELONG: + case DATE: + case BEDATE: + case LEDATE: + v = p->l; + break; + + case STRING: + l = 0; + /* + * What we want here is: v = strncmp(m->value.s, p->s, m->vallen); + * but ignoring any nulls. bcmp doesn't give -/+/0 and isn't + * universally available anyway. + */ + v = 0; + { + register unsigned char *a = (unsigned char *) m->value.s; + register unsigned char *b = (unsigned char *) p->s; + register int len = m->vallen; + + while (--len >= 0) + if ((v = *b++ - *a++) != 0) + break; + } + break; + default: + /* bogosity, pretend that it just wasn't a match */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, + MODNAME ": invalid type %d in mcheck().", m->type); + return 0; + } + + v = signextend(r->server, m, v) & m->mask; + + switch (m->reln) { + case 'x': +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + "%lu == *any* = 1", v); +#endif + matched = 1; + break; + + case '!': + matched = v != l; +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + "%lu != %lu = %d", v, l, matched); +#endif + break; + + case '=': + matched = v == l; +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + "%lu == %lu = %d", v, l, matched); +#endif + break; + + case '>': + if (m->flag & UNSIGNED) { + matched = v > l; +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + "%lu > %lu = %d", v, l, matched); +#endif + } + else { + matched = (long) v > (long) l; +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + "%ld > %ld = %d", v, l, matched); +#endif + } + break; + + case '<': + if (m->flag & UNSIGNED) { + matched = v < l; +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + "%lu < %lu = %d", v, l, matched); +#endif + } + else { + matched = (long) v < (long) l; +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + "%ld < %ld = %d", v, l, matched); +#endif + } + break; + + case '&': + matched = (v & l) == l; +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + "((%lx & %lx) == %lx) = %d", v, l, l, matched); +#endif + break; + + case '^': + matched = (v & l) != l; +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + "((%lx & %lx) != %lx) = %d", v, l, l, matched); +#endif + break; + + default: + /* bogosity, pretend it didn't match */ + matched = 0; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r, + MODNAME ": mcheck: can't happen: invalid relation %d.", + m->reln); + break; + } + + return matched; +} + +/* an optimization over plain strcmp() */ +#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) + +static int ascmagic(request_rec *r, unsigned char *buf, int nbytes) +{ + int has_escapes = 0; + unsigned char *s; + char nbuf[HOWMANY + 1]; /* one extra for terminating '\0' */ + char *token; + register struct names *p; + int small_nbytes; + + /* these are easy, do them first */ + + /* + * for troff, look for . + letter + letter or .\"; this must be done to + * disambiguate tar archives' ./file and other trash from real troff + * input. + */ + if (*buf == '.') { + unsigned char *tp = buf + 1; + + while (ap_isspace(*tp)) + ++tp; /* skip leading whitespace */ + if ((ap_isalnum(*tp) || *tp == '\\') && + (ap_isalnum(*(tp + 1)) || *tp == '"')) { + magic_rsl_puts(r, "application/x-troff"); + return 1; + } + } + if ((*buf == 'c' || *buf == 'C') && ap_isspace(*(buf + 1))) { + /* Fortran */ + magic_rsl_puts(r, "text/plain"); + return 1; + } + + /* look for tokens from names.h - this is expensive!, so we'll limit + * ourselves to only SMALL_HOWMANY bytes */ + small_nbytes = (nbytes > SMALL_HOWMANY) ? SMALL_HOWMANY : nbytes; + /* make a copy of the buffer here because strtok() will destroy it */ + s = (unsigned char *) memcpy(nbuf, buf, small_nbytes); + s[small_nbytes] = '\0'; + has_escapes = (memchr(s, '\033', small_nbytes) != NULL); + /* XXX: not multithread safe */ + while ((token = strtok((char *) s, " \t\n\r\f")) != NULL) { + s = NULL; /* make strtok() keep on tokin' */ + for (p = names; p < names + NNAMES; p++) { + if (STREQ(p->name, token)) { + magic_rsl_puts(r, types[p->type]); + if (has_escapes) + magic_rsl_puts(r, " (with escape sequences)"); + return 1; + } + } + } + + switch (is_tar(buf, nbytes)) { + case 1: + /* V7 tar archive */ + magic_rsl_puts(r, "application/x-tar"); + return 1; + case 2: + /* POSIX tar archive */ + magic_rsl_puts(r, "application/x-tar"); + return 1; + } + + /* all else fails, but it is ascii... */ + if (has_escapes) { + /* text with escape sequences */ + /* we leave this open for further differentiation later */ + magic_rsl_puts(r, "text/plain"); + } + else { + /* plain text */ + magic_rsl_puts(r, "text/plain"); + } + return 1; +} + + +/* + * compress routines: zmagic() - returns 0 if not recognized, uncompresses + * and prints information if recognized uncompress(s, method, old, n, newch) + * - uncompress old into new, using method, return sizeof new + */ + +static struct { + char *magic; + int maglen; + char *argv[3]; + int silent; + char *encoding; /* MUST be lowercase */ +} compr[] = { + + /* we use gzip here rather than uncompress because we have to pass + * it a full filename -- and uncompress only considers filenames + * ending with .Z + */ + { + "\037\235", 2, { + "gzip", "-dcq", NULL + }, 0, "x-compress" + }, + { + "\037\213", 2, { + "gzip", "-dcq", NULL + }, 1, "x-gzip" + }, + /* + * XXX pcat does not work, cause I don't know how to make it read stdin, + * so we use gzip + */ + { + "\037\036", 2, { + "gzip", "-dcq", NULL + }, 0, "x-gzip" + }, +}; + +static int ncompr = sizeof(compr) / sizeof(compr[0]); + +static int zmagic(request_rec *r, unsigned char *buf, int nbytes) +{ + unsigned char *newbuf; + int newsize; + int i; + + for (i = 0; i < ncompr; i++) { + if (nbytes < compr[i].maglen) + continue; + if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0) + break; + } + + if (i == ncompr) + return 0; + + if ((newsize = uncompress(r, i, &newbuf, nbytes)) > 0) { + tryit(r, newbuf, newsize, 0); + + /* set encoding type in the request record */ + r->content_encoding = compr[i].encoding; + } + return 1; +} + + +struct uncompress_parms { + request_rec *r; + int method; +}; + +static int uncompress_child(void *data, child_info *pinfo) +{ + struct uncompress_parms *parm = data; + char *new_argv[4]; + + new_argv[0] = compr[parm->method].argv[0]; + new_argv[1] = compr[parm->method].argv[1]; + new_argv[2] = parm->r->filename; + new_argv[3] = NULL; + +#if defined(WIN32) + int child_pid; +#endif + + if (compr[parm->method].silent) { + close(STDERR_FILENO); + } + +#if defined(WIN32) + child_pid = spawnvp(compr[parm->method].argv[0], + new_argv); + return (child_pid); +#else + execvp(compr[parm->method].argv[0], new_argv); + ap_log_rerror(APLOG_MARK, APLOG_ERR, parm->r, + MODNAME ": could not execute `%s'.", + compr[parm->method].argv[0]); + return -1; +#endif +} + + +static int uncompress(request_rec *r, int method, + unsigned char **newch, int n) +{ + struct uncompress_parms parm; + BUFF *bout; + pool *sub_pool; + + parm.r = r; + parm.method = method; + + /* We make a sub_pool so that we can collect our child early, otherwise + * there are cases (i.e. generating directory indicies with mod_autoindex) + * where we would end up with LOTS of zombies. + */ + sub_pool = ap_make_sub_pool(r->pool); + + if (!ap_bspawn_child(sub_pool, uncompress_child, &parm, kill_always, + NULL, &bout, NULL)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + MODNAME ": couldn't spawn uncompress process: %s", r->uri); + return -1; + } + + *newch = (unsigned char *) ap_palloc(r->pool, n); + if ((n = ap_bread(bout, *newch, n)) <= 0) { + ap_destroy_pool(sub_pool); + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + MODNAME ": read failed %s", r->filename); + return -1; + } + ap_destroy_pool(sub_pool); + return n; +} + +/* + * is_tar() -- figure out whether file is a tar archive. + * + * Stolen (by author of file utility) from the public domain tar program: Public + * Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu). + * + * @(#)list.c 1.18 9/23/86 Public Domain - gnu $Id: mod_mime_magic.c,v 1.7 + * 1997/06/24 00:41:02 ikluft Exp ikluft $ + * + * Comments changed and some code/comments reformatted for file command by Ian + * Darwin. + */ + +#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') ) + +/* + * Return 0 if the checksum is bad (i.e., probably not a tar archive), 1 for + * old UNIX tar file, 2 for Unix Std (POSIX) tar file. + */ + +static int is_tar(unsigned char *buf, int nbytes) +{ + register union record *header = (union record *) buf; + register int i; + register long sum, recsum; + register char *p; + + if (nbytes < sizeof(union record)) + return 0; + + recsum = from_oct(8, header->header.chksum); + + sum = 0; + p = header->charptr; + for (i = sizeof(union record); --i >= 0;) { + /* + * We can't use unsigned char here because of old compilers, e.g. V7. + */ + sum += 0xFF & *p++; + } + + /* Adjust checksum to count the "chksum" field as blanks. */ + for (i = sizeof(header->header.chksum); --i >= 0;) + sum -= 0xFF & header->header.chksum[i]; + sum += ' ' * sizeof header->header.chksum; + + if (sum != recsum) + return 0; /* Not a tar archive */ + + if (0 == strcmp(header->header.magic, TMAGIC)) + return 2; /* Unix Standard tar archive */ + + return 1; /* Old fashioned tar archive */ +} + + +/* + * Quick and dirty octal conversion. + * + * Result is -1 if the field is invalid (all blank, or nonoctal). + */ +static long from_oct(int digs, char *where) +{ + register long value; + + while (ap_isspace(*where)) { /* Skip spaces */ + where++; + if (--digs <= 0) + return -1; /* All blank field */ + } + value = 0; + while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */ + value = (value << 3) | (*where++ - '0'); + --digs; + } + + if (digs > 0 && *where && !ap_isspace(*where)) + return -1; /* Ended on non-space/nul */ + + return value; +} + +/* + * Check for file-revision suffix + * + * This is for an obscure document control system used on an intranet. + * The web representation of each file's revision has an @1, @2, etc + * appended with the revision number. This needs to be stripped off to + * find the file suffix, which can be recognized by sending the name back + * through a sub-request. The base file name (without the @num suffix) + * must exist because its type will be used as the result. + */ +static int revision_suffix(request_rec *r) +{ + int suffix_pos, result; + char *sub_filename; + request_rec *sub; + +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": revision_suffix checking %s", r->filename); +#endif /* MIME_MAGIC_DEBUG */ + + /* check for recognized revision suffix */ + suffix_pos = strlen(r->filename) - 1; + if (!ap_isdigit(r->filename[suffix_pos])) { + return 0; + } + while (suffix_pos >= 0 && ap_isdigit(r->filename[suffix_pos])) + suffix_pos--; + if (suffix_pos < 0 || r->filename[suffix_pos] != '@') { + return 0; + } + + /* perform sub-request for the file name without the suffix */ + result = 0; + sub_filename = ap_pstrndup(r->pool, r->filename, suffix_pos); +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": subrequest lookup for %s", sub_filename); +#endif /* MIME_MAGIC_DEBUG */ + sub = ap_sub_req_lookup_file(sub_filename, r); + + /* extract content type/encoding/language from sub-request */ + if (sub->content_type) { + r->content_type = ap_pstrdup(r->pool, sub->content_type); +#if MIME_MAGIC_DEBUG + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r, + MODNAME ": subrequest %s got %s", + sub_filename, r->content_type); +#endif /* MIME_MAGIC_DEBUG */ + if (sub->content_encoding) + r->content_encoding = + ap_pstrdup(r->pool, sub->content_encoding); + if (sub->content_language) + r->content_language = + ap_pstrdup(r->pool, sub->content_language); + result = 1; + } + + /* clean up */ + ap_destroy_sub_req(sub); + + return result; +} + +/* + * initialize the module + */ + +static void magic_init(server_rec *main_server, pool *p) +{ + int result; + magic_server_config_rec *conf; + magic_server_config_rec *main_conf; + server_rec *s; +#if MIME_MAGIC_DEBUG + struct magic *m, *prevm; +#endif /* MIME_MAGIC_DEBUG */ + + main_conf = ap_get_module_config(main_server->module_config, &mime_magic_module); + for (s = main_server; s; s = s->next) { + conf = ap_get_module_config(s->module_config, &mime_magic_module); + if (conf->magicfile == NULL && s != main_server) { + /* inherits from the parent */ + *conf = *main_conf; + } + else if (conf->magicfile) { + result = apprentice(s, p); + if (result == -1) + return; +#if MIME_MAGIC_DEBUG + prevm = 0; + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, s, + MODNAME ": magic_init 1 test"); + for (m = conf->magic; m; m = m->next) { + if (ap_isprint((((unsigned long) m) >> 24) & 255) && + ap_isprint((((unsigned long) m) >> 16) & 255) && + ap_isprint((((unsigned long) m) >> 8) & 255) && + ap_isprint(((unsigned long) m) & 255)) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, s, + MODNAME ": magic_init 1: POINTER CLOBBERED! " + "m=\"%c%c%c%c\" line=%d", + (((unsigned long) m) >> 24) & 255, + (((unsigned long) m) >> 16) & 255, + (((unsigned long) m) >> 8) & 255, + ((unsigned long) m) & 255, + prevm ? prevm->lineno : -1); + break; + } + prevm = m; + } +#endif + } + } +} + +/* + * Find the Content-Type from any resource this module has available + */ + +static int magic_find_ct(request_rec *r) +{ + int result; + magic_server_config_rec *conf; + + /* the file has to exist */ + if (r->finfo.st_mode == 0 || !r->filename) { + return DECLINED; + } + + /* was someone else already here? */ + if (r->content_type) { + return DECLINED; + } + + conf = ap_get_module_config(r->server->module_config, &mime_magic_module); + if (!conf || !conf->magic) { + return DECLINED; + } + + /* initialize per-request info */ + if (!magic_set_config(r)) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* try excluding file-revision suffixes */ + if (revision_suffix(r) != 1) { + /* process it based on the file contents */ + if ((result = magic_process(r)) != OK) { + return result; + } + } + + /* if we have any results, put them in the request structure */ + return magic_rsl_to_request(r); +} + +/* + * Apache API module interface + */ + +module mime_magic_module = +{ + STANDARD_MODULE_STUFF, + magic_init, /* initializer */ + NULL, /* dir config creator */ + NULL, /* dir merger --- default is to override */ + create_magic_server_config, /* server config */ + merge_magic_server_config, /* merge server config */ + mime_magic_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + magic_find_ct, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/metadata/mod_setenvif.c b/modules/metadata/mod_setenvif.c new file mode 100644 index 00000000000..27eef0aec94 --- /dev/null +++ b/modules/metadata/mod_setenvif.c @@ -0,0 +1,423 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +/* + * mod_setenvif.c + * Set environment variables based on matching request headers or + * attributes against regex strings + * + * Paul Sutton <paul@ukweb.com> 27 Oct 1996 + * Based on mod_browser by Alexei Kosut <akosut@organic.com> + */ + +/* + * Used to set environment variables based on the incoming request headers, + * or some selected other attributes of the request (e.g., the remote host + * name). + * + * Usage: + * + * SetEnvIf name regex var ... + * + * where name is either a HTTP request header name, or one of the + * special values (see below). The 'value' of the header (or the + * value of the special value from below) are compared against the + * regex argument. If this is a simple string, a simple sub-string + * match is performed. Otherwise, a request expression match is + * done. If the value matches the string or regular expression, the + * environment variables listed as var ... are set. Each var can + * be in one of three formats: var, which sets the named variable + * (the value value "1"); var=value, which sets the variable to + * the given value; or !var, which unsets the variable is it has + * been previously set. + * + * Normally the strings are compared with regard to case. To ignore + * case, use the directive SetEnvIfNoCase instead. + * + * Special values for 'name' are: + * + * remote_host Remote host name (if available) + * remote_addr Remote IP address + * remote_user Remote authenticated user (if any) + * request_method Request method (GET, POST, etc) + * request_uri Requested URI + * + * Examples: + * + * To set the enviroment variable LOCALHOST if the client is the local + * machine: + * + * SetEnvIf remote_addr 127.0.0.1 LOCALHOST + * + * To set LOCAL if the client is the local host, or within our company's + * domain (192.168.10): + * + * SetEnvIf remote_addr 192.168.10. LOCAL + * SetEnvIf remote_addr 127.0.0.1 LOCALHOST + * + * This could be written as: + * + * SetEnvIf remote_addr (127.0.0.1|192.168.10.) LOCAL + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" + +enum special { + SPECIAL_NOT, + SPECIAL_REMOTE_ADDR, + SPECIAL_REMOTE_HOST, + SPECIAL_REMOTE_USER, + SPECIAL_REQUEST_URI, + SPECIAL_REQUEST_METHOD, + SPECIAL_REQUEST_PROTOCOL +}; +typedef struct { + char *name; /* header name */ + char *regex; /* regex to match against */ + regex_t *preg; /* compiled regex */ + table *features; /* env vars to set (or unset) */ + ENUM_BITFIELD( /* is it a "special" header ? */ + enum special, + special_type,4); + unsigned icase : 1; /* ignoring case? */ +} sei_entry; + +typedef struct { + array_header *conditionals; +} sei_cfg_rec; + +module MODULE_VAR_EXPORT setenvif_module; + +static void *create_setenvif_config(pool *p, server_rec *dummy) +{ + sei_cfg_rec *new = (sei_cfg_rec *) ap_palloc(p, sizeof(sei_cfg_rec)); + + new->conditionals = ap_make_array(p, 20, sizeof(sei_entry)); + return (void *) new; +} + +static void *merge_setenvif_config(pool *p, void *basev, void *overridesv) +{ + sei_cfg_rec *a = ap_pcalloc(p, sizeof(sei_cfg_rec)); + sei_cfg_rec *base = basev, *overrides = overridesv; + + a->conditionals = ap_append_arrays(p, base->conditionals, + overrides->conditionals); + return a; +} + +/* any non-NULL magic constant will do... used to indicate if REG_ICASE should + * be used + */ +#define ICASE_MAGIC ((void *)(&setenvif_module)) + +static const char *add_setenvif_core(cmd_parms *cmd, void *mconfig, + char *fname, const char *args) +{ + char *regex; + const char *feature; + sei_cfg_rec *sconf = ap_get_module_config(cmd->server->module_config, + &setenvif_module); + sei_entry *new, *entries = (sei_entry *) sconf->conditionals->elts; + char *var; + int i; + int beenhere = 0; + unsigned icase; + + /* get regex */ + regex = ap_getword_conf(cmd->pool, &args); + if (!*regex) { + return ap_pstrcat(cmd->pool, "Missing regular expression for ", + cmd->cmd->name, NULL); + } + + /* + * If we've already got a sei_entry with the same name we want to + * just copy the name pointer... so that later on we can compare + * two header names just by comparing the pointers. + */ + + for (i = 0; i < sconf->conditionals->nelts; ++i) { + new = &entries[i]; + if (!strcasecmp(new->name, fname)) { + fname = new->name; + break; + } + } + + /* if the last entry has an idential headername and regex then + * merge with it + */ + i = sconf->conditionals->nelts - 1; + icase = cmd->info == ICASE_MAGIC; + if (i < 0 + || entries[i].name != fname + || entries[i].icase != icase + || strcmp(entries[i].regex, regex)) { + + /* no match, create a new entry */ + + new = ap_push_array(sconf->conditionals); + new->name = fname; + new->regex = regex; + new->icase = icase; + new->preg = ap_pregcomp(cmd->pool, regex, + (REG_EXTENDED | REG_NOSUB + | (icase ? REG_ICASE : 0))); + if (new->preg == NULL) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, + " regex could not be compiled.", NULL); + } + new->features = ap_make_table(cmd->pool, 2); + + if (!strcasecmp(fname, "remote_addr")) { + new->special_type = SPECIAL_REMOTE_ADDR; + } + else if (!strcasecmp(fname, "remote_host")) { + new->special_type = SPECIAL_REMOTE_HOST; + } + else if (!strcasecmp(fname, "remote_user")) { + new->special_type = SPECIAL_REMOTE_USER; + } + else if (!strcasecmp(fname, "request_uri")) { + new->special_type = SPECIAL_REQUEST_URI; + } + else if (!strcasecmp(fname, "request_method")) { + new->special_type = SPECIAL_REQUEST_METHOD; + } + else if (!strcasecmp(fname, "request_protocol")) { + new->special_type = SPECIAL_REQUEST_PROTOCOL; + } + else { + new->special_type = SPECIAL_NOT; + } + } + else { + new = &entries[i]; + } + + for ( ; ; ) { + feature = ap_getword_conf(cmd->pool, &args); + if (!*feature) { + break; + } + beenhere++; + + var = ap_getword(cmd->pool, &feature, '='); + if (*feature) { + ap_table_setn(new->features, var, feature); + } + else if (*var == '!') { + ap_table_setn(new->features, var + 1, "!"); + } + else { + ap_table_setn(new->features, var, "1"); + } + } + + if (!beenhere) { + return ap_pstrcat(cmd->pool, "Missing envariable expression for ", + cmd->cmd->name, NULL); + } + + return NULL; +} + +static const char *add_setenvif(cmd_parms *cmd, void *mconfig, + const char *args) +{ + char *fname; + + /* get header name */ + fname = ap_getword_conf(cmd->pool, &args); + if (!*fname) { + return ap_pstrcat(cmd->pool, "Missing header-field name for ", + cmd->cmd->name, NULL); + } + return add_setenvif_core(cmd, mconfig, fname, args); +} + +/* + * This routine handles the BrowserMatch* directives. It simply turns around + * and feeds them, with the appropriate embellishments, to the general-purpose + * command handler. + */ +static const char *add_browser(cmd_parms *cmd, void *mconfig, const char *args) +{ + return add_setenvif_core(cmd, mconfig, "User-Agent", args); +} + +static const command_rec setenvif_module_cmds[] = +{ + { "SetEnvIf", add_setenvif, NULL, + RSRC_CONF, RAW_ARGS, "A header-name, regex and a list of variables." }, + { "SetEnvIfNoCase", add_setenvif, ICASE_MAGIC, + RSRC_CONF, RAW_ARGS, "a header-name, regex and a list of variables." }, + { "BrowserMatch", add_browser, NULL, + RSRC_CONF, RAW_ARGS, "A browser regex and a list of variables." }, + { "BrowserMatchNoCase", add_browser, ICASE_MAGIC, + RSRC_CONF, RAW_ARGS, "A browser regex and a list of variables." }, + { NULL }, +}; + +static int match_headers(request_rec *r) +{ + server_rec *s = r->server; + sei_cfg_rec *sconf; + sei_entry *entries; + table_entry *elts; + const char *val; + int i, j; + char *last_name; + + sconf = (sei_cfg_rec *) ap_get_module_config(s->module_config, + &setenvif_module); + entries = (sei_entry *) sconf->conditionals->elts; + last_name = NULL; + val = NULL; + for (i = 0; i < sconf->conditionals->nelts; ++i) { + sei_entry *b = &entries[i]; + + /* Optimize the case where a bunch of directives in a row use the + * same header. Remember we don't need to strcmp the two header + * names because we made sure the pointers were equal during + * configuration. + */ + if (b->name != last_name) { + last_name = b->name; + switch (b->special_type) { + case SPECIAL_REMOTE_ADDR: + val = r->connection->remote_ip; + break; + case SPECIAL_REMOTE_HOST: + val = ap_get_remote_host(r->connection, r->per_dir_config, + REMOTE_NAME); + break; + case SPECIAL_REMOTE_USER: + val = r->connection->user; + break; + case SPECIAL_REQUEST_URI: + val = r->uri; + break; + case SPECIAL_REQUEST_METHOD: + val = r->method; + break; + case SPECIAL_REQUEST_PROTOCOL: + val = r->protocol; + break; + case SPECIAL_NOT: + val = ap_table_get(r->headers_in, b->name); + if (val == NULL) { + val = ap_table_get(r->subprocess_env, b->name); + } + break; + } + } + + /* + * A NULL value indicates that the header field or special entity + * wasn't present or is undefined. Represent that as an empty string + * so that REs like "^$" will work and allow envariable setting + * based on missing or empty field. + */ + if (val == NULL) { + val = ""; + } + + if (!ap_regexec(b->preg, val, 0, NULL, 0)) { + array_header *arr = ap_table_elts(b->features); + elts = (table_entry *) arr->elts; + + for (j = 0; j < arr->nelts; ++j) { + if (!strcmp(elts[j].val, "!")) { + ap_table_unset(r->subprocess_env, elts[j].key); + } + else { + ap_table_setn(r->subprocess_env, elts[j].key, elts[j].val); + } + } + } + } + + return DECLINED; +} + +module MODULE_VAR_EXPORT setenvif_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_setenvif_config, /* server config */ + merge_setenvif_config, /* merge server configs */ + setenvif_module_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* input header parse */ + NULL, /* child (process) initialization */ + NULL, /* child (process) rundown */ + match_headers /* post_read_request */ +}; diff --git a/modules/metadata/mod_unique_id.c b/modules/metadata/mod_unique_id.c new file mode 100644 index 00000000000..63474ed1feb --- /dev/null +++ b/modules/metadata/mod_unique_id.c @@ -0,0 +1,400 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +/* + * mod_unique_id.c: generate a unique identifier for each request + * + * Original author: Dean Gaudet <dgaudet@arctic.org> + * UUencoding modified by: Alvaro Martinez Echevarria <alvaro@lander.es> + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "multithread.h" + +#ifdef MULTITHREAD +#error sorry this module does not support multithreaded servers yet +#endif + +typedef struct { + unsigned int stamp; + unsigned int in_addr; + unsigned int pid; + unsigned short counter; +} unique_id_rec; + +/* Comments: + * + * We want an identifier which is unique across all hits, everywhere. + * "everywhere" includes multiple httpd instances on the same machine, or on + * multiple machines. Essentially "everywhere" should include all possible + * httpds across all servers at a particular "site". We make some assumptions + * that if the site has a cluster of machines then their time is relatively + * synchronized. We also assume that the first address returned by a + * gethostbyname (gethostname()) is unique across all the machines at the + * "site". + * + * We also further assume that pids fit in 32-bits. If something uses more + * than 32-bits, the fix is trivial, but it requires the unrolled uuencoding + * loop to be extended. * A similar fix is needed to support multithreaded + * servers, using a pid/tid combo. + * + * Together, the in_addr and pid are assumed to absolutely uniquely identify + * this one child from all other currently running children on all servers + * (including this physical server if it is running multiple httpds) from each + * other. + * + * The stamp and counter are used to distinguish all hits for a particular + * (in_addr,pid) pair. The stamp is updated using r->request_time, + * saving cpu cycles. The counter is never reset, and is used to permit up to + * 64k requests in a single second by a single child. + * + * The 112-bits of unique_id_rec are encoded using the alphabet + * [A-Za-z0-9@-], resulting in 19 bytes of printable characters. That is then + * stuffed into the environment variable UNIQUE_ID so that it is available to + * other modules. The alphabet choice differs from normal base64 encoding + * [A-Za-z0-9+/] because + and / are special characters in URLs and we want to + * make it easy to use UNIQUE_ID in URLs. + * + * Note that UNIQUE_ID should be considered an opaque token by other + * applications. No attempt should be made to dissect its internal components. + * It is an abstraction that may change in the future as the needs of this + * module change. + * + * It is highly desirable that identifiers exist for "eternity". But future + * needs (such as much faster webservers, moving to 64-bit pids, or moving to a + * multithreaded server) may dictate a need to change the contents of + * unique_id_rec. Such a future implementation should ensure that the first + * field is still a time_t stamp. By doing that, it is possible for a site to + * have a "flag second" in which they stop all of their old-format servers, + * wait one entire second, and then start all of their new-servers. This + * procedure will ensure that the new space of identifiers is completely unique + * from the old space. (Since the first four unencoded bytes always differ.) + */ +/* + * Sun Jun 7 05:43:49 CEST 1998 -- Alvaro + * More comments: + * 1) The UUencoding prodecure is now done in a general way, avoiding the problems + * with sizes and paddings that can arise depending on the architecture. Now the + * offsets and sizes of the elements of the unique_id_rec structure are calculated + * in unique_id_global_init; and then used to duplicate the structure without the + * paddings that might exist. The multithreaded server fix should be now very easy: + * just add a new "tid" field to the unique_id_rec structure, and increase by one + * UNIQUE_ID_REC_MAX. + * 2) unique_id_rec.stamp has been changed from "time_t" to "unsigned int", because + * its size is 64bits on some platforms (linux/alpha), and this caused problems with + * htonl/ntohl. Well, this shouldn't be a problem till year 2106. + */ + +static unsigned global_in_addr; + +static APACHE_TLS unique_id_rec cur_unique_id; + +/* + * Number of elements in the structure unique_id_rec. + */ +#define UNIQUE_ID_REC_MAX 4 + +static unsigned short unique_id_rec_offset[UNIQUE_ID_REC_MAX], + unique_id_rec_size[UNIQUE_ID_REC_MAX], + unique_id_rec_total_size, + unique_id_rec_size_uu; + +static void unique_id_global_init(server_rec *s, pool *p) +{ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + char str[MAXHOSTNAMELEN + 1]; + struct hostent *hent; +#ifndef NO_GETTIMEOFDAY + struct timeval tv; +#endif + + /* + * Calculate the sizes and offsets in cur_unique_id. + */ + unique_id_rec_offset[0] = XtOffsetOf(unique_id_rec, stamp); + unique_id_rec_size[0] = sizeof(cur_unique_id.stamp); + unique_id_rec_offset[1] = XtOffsetOf(unique_id_rec, in_addr); + unique_id_rec_size[1] = sizeof(cur_unique_id.in_addr); + unique_id_rec_offset[2] = XtOffsetOf(unique_id_rec, pid); + unique_id_rec_size[2] = sizeof(cur_unique_id.pid); + unique_id_rec_offset[3] = XtOffsetOf(unique_id_rec, counter); + unique_id_rec_size[3] = sizeof(cur_unique_id.counter); + unique_id_rec_total_size = unique_id_rec_size[0] + unique_id_rec_size[1] + + unique_id_rec_size[2] + unique_id_rec_size[3]; + + /* + * Calculate the size of the structure when encoded. + */ + unique_id_rec_size_uu = (unique_id_rec_total_size*8+5)/6; + + /* + * Now get the global in_addr. Note that it is not sufficient to use one + * of the addresses from the main_server, since those aren't as likely to + * be unique as the physical address of the machine + */ + if (gethostname(str, sizeof(str) - 1) != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, s, + "gethostname: mod_unique_id requires the hostname of the server"); + exit(1); + } + str[sizeof(str) - 1] = '\0'; + + if ((hent = gethostbyname(str)) == NULL) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, s, + "mod_unique_id: unable to gethostbyname(\"%s\")", str); + exit(1); + } + + global_in_addr = ((struct in_addr *) hent->h_addr_list[0])->s_addr; + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, s, + "mod_unique_id: using ip addr %s", + inet_ntoa(*(struct in_addr *) hent->h_addr_list[0])); + + /* + * If the server is pummelled with restart requests we could possibly end + * up in a situation where we're starting again during the same second + * that has been used in previous identifiers. Avoid that situation. + * + * In truth, for this to actually happen not only would it have to restart + * in the same second, but it would have to somehow get the same pids as + * one of the other servers that was running in that second. Which would + * mean a 64k wraparound on pids ... not very likely at all. + * + * But protecting against it is relatively cheap. We just sleep into the + * next second. + */ +#ifdef NO_GETTIMEOFDAY + sleep(1); +#else + if (gettimeofday(&tv, NULL) == -1) { + sleep(1); + } + else if (tv.tv_usec) { + tv.tv_sec = 0; + tv.tv_usec = 1000000 - tv.tv_usec; + select(0, NULL, NULL, NULL, &tv); + } +#endif +} + +static void unique_id_child_init(server_rec *s, pool *p) +{ + pid_t pid; +#ifndef NO_GETTIMEOFDAY + struct timeval tv; +#endif + + /* + * Note that we use the pid because it's possible that on the same + * physical machine there are multiple servers (i.e. using Listen). But + * it's guaranteed that none of them will share the same pids between + * children. + * + * XXX: for multithread this needs to use a pid/tid combo and probably + * needs to be expanded to 32 bits + */ + pid = getpid(); + cur_unique_id.pid = pid; + + /* + * Test our assumption that the pid is 32-bits. It's possible that + * 64-bit machines will declare pid_t to be 64 bits but only use 32 + * of them. It would have been really nice to test this during + * global_init ... but oh well. + */ + if (cur_unique_id.pid != pid) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, s, + "oh no! pids are greater than 32-bits! I'm broken!"); + } + + cur_unique_id.in_addr = global_in_addr; + + /* + * If we use 0 as the initial counter we have a little less protection + * against restart problems, and a little less protection against a clock + * going backwards in time. + */ +#ifndef NO_GETTIMEOFDAY + if (gettimeofday(&tv, NULL) == -1) { + cur_unique_id.counter = 0; + } + else { + /* Some systems have very low variance on the low end of their + * system counter, defend against that. + */ + cur_unique_id.counter = tv.tv_usec / 10; + } +#else + cur_unique_id.counter = 0; +#endif + + /* + * We must always use network ordering for these bytes, so that + * identifiers are comparable between machines of different byte + * orderings. Note in_addr is already in network order. + */ + cur_unique_id.pid = htonl(cur_unique_id.pid); + cur_unique_id.counter = htons(cur_unique_id.counter); +} + +/* NOTE: This is *NOT* the same encoding used by base64encode ... the last two + * characters should be + and /. But those two characters have very special + * meanings in URLs, and we want to make it easy to use identifiers in + * URLs. So we replace them with @ and -. + */ +static const char uuencoder[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '@', '-', +}; + +static int gen_unique_id(request_rec *r) +{ + char *str; + /* + * Buffer padded with two final bytes, used to copy the unique_id_red + * structure without the internal paddings that it could have. + */ + struct { + unique_id_rec foo; + unsigned char pad[2]; + } paddedbuf; + unsigned char *x,*y; + unsigned short counter; + const char *e; + int i,j,k; + + /* copy the unique_id if this is an internal redirect (we're never + * actually called for sub requests, so we don't need to test for + * them) */ + if (r->prev && (e = ap_table_get(r->subprocess_env, "REDIRECT_UNIQUE_ID"))) { + ap_table_setn(r->subprocess_env, "UNIQUE_ID", e); + return DECLINED; + } + + cur_unique_id.stamp = htonl((unsigned int)r->request_time); + + /* we'll use a temporal buffer to avoid uuencoding the possible internal + * paddings of the original structure */ + x = (unsigned char *) &paddedbuf; + y = (unsigned char *) &cur_unique_id; + k = 0; + for (i = 0; i < UNIQUE_ID_REC_MAX; i++) { + y = ((unsigned char *) &cur_unique_id) + unique_id_rec_offset[i]; + for (j = 0; j < unique_id_rec_size[i]; j++, k++) { + x[k] = y[j]; + } + } + /* + * We reset two more bytes just in case padding is needed for the uuencoding. + */ + x[k++] = '\0'; + x[k++] = '\0'; + + /* alloc str and do the uuencoding */ + str = (char *)ap_palloc(r->pool, unique_id_rec_size_uu + 1); + k = 0; + for (i = 0; i < unique_id_rec_total_size; i += 3) { + y = x + i; + str[k++] = uuencoder[y[0] >> 2]; + str[k++] = uuencoder[((y[0] & 0x03) << 4) | ((y[1] & 0xf0) >> 4)]; + if (k == unique_id_rec_size_uu) break; + str[k++] = uuencoder[((y[1] & 0x0f) << 2) | ((y[2] & 0xc0) >> 6)]; + if (k == unique_id_rec_size_uu) break; + str[k++] = uuencoder[y[2] & 0x3f]; + } + str[k++] = '\0'; + + /* set the environment variable */ + ap_table_setn(r->subprocess_env, "UNIQUE_ID", str); + + /* and increment the identifier for the next call */ + counter = ntohs(cur_unique_id.counter) + 1; + cur_unique_id.counter = htons(counter); + + return DECLINED; +} + + +module MODULE_VAR_EXPORT unique_id_module = { + STANDARD_MODULE_STUFF, + unique_id_global_init, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server configs */ + NULL, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + unique_id_child_init, /* child_init */ + NULL, /* child_exit */ + gen_unique_id /* post_read_request */ +}; diff --git a/modules/metadata/mod_usertrack.c b/modules/metadata/mod_usertrack.c new file mode 100644 index 00000000000..1b50e159967 --- /dev/null +++ b/modules/metadata/mod_usertrack.c @@ -0,0 +1,377 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +/* User Tracking Module (Was mod_cookies.c) + * + * This Apache module is designed to track users paths through a site. + * It uses the client-side state ("Cookie") protocol developed by Netscape. + * It is known to work on Netscape browsers, Microsoft Internet + * Explorer and others currently being developed. + * + * Each time a page is requested we look to see if the browser is sending + * us a Cookie: header that we previously generated. + * + * If we don't find one then the user hasn't been to this site since + * starting their browser or their browser doesn't support cookies. So + * we generate a unique Cookie for the transaction and send it back to + * the browser (via a "Set-Cookie" header) + * Future requests from the same browser should keep the same Cookie line. + * + * By matching up all the requests with the same cookie you can + * work out exactly what path a user took through your site. To log + * the cookie use the " %{Cookie}n " directive in a custom access log; + * + * Example 1 : If you currently use the standard Log file format (CLF) + * and use the command "TransferLog somefilename", add the line + * LogFormat "%h %l %u %t \"%r\" %s %b %{Cookie}n" + * to your config file. + * + * Example 2 : If you used to use the old "CookieLog" directive, you + * can emulate it by adding the following command to your config file + * CustomLog filename "%{Cookie}n \"%r\" %t" + * + * Notes: + * 1. This code now logs the initial transaction (the one that created + * the cookie to start with). + * 2. This module has been designed to not interfere with other Cookies + * your site may be using; just avoid sending out cookies with + * the name "Apache=" or things will get confused. + * 3. If you want you can modify the Set-Cookie line so that the Cookie + * never expires. You would then get the same Cookie each time the + * user revisits your site. + * + * Mark Cox, mark@ukweb.com, 6 July 95 + * + * This file replaces mod_cookies.c + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#if !defined(WIN32) && !defined(MPE) +#include <sys/time.h> +#endif + +module MODULE_VAR_EXPORT usertrack_module; + +typedef struct { + int always; + time_t expires; +} cookie_log_state; + +typedef struct { + int enabled; + char *cookie_name; +} cookie_dir_rec; + +/* Define this to allow post-2000 cookies. Cookies use two-digit dates, + * so it might be dicey. (Netscape does it correctly, but others may not) + */ +#define MILLENIAL_COOKIES + +/* Make Cookie: Now we have to generate something that is going to be + * pretty unique. We can base it on the pid, time, hostip */ + +#define COOKIE_NAME "Apache" + +static void make_cookie(request_rec *r) +{ + cookie_log_state *cls = ap_get_module_config(r->server->module_config, + &usertrack_module); +#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES) + clock_t mpe_times; + struct tms mpe_tms; +#elif !defined(WIN32) + struct timeval tv; + struct timezone tz = {0, 0}; +#endif + /* 1024 == hardcoded constant */ + char cookiebuf[1024]; + char *new_cookie; + const char *rname = ap_get_remote_host(r->connection, r->per_dir_config, + REMOTE_NAME); + cookie_dir_rec *dcfg; + + dcfg = ap_get_module_config(r->per_dir_config, &usertrack_module); + +#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES) +/* We lack gettimeofday(), so we must use time() to obtain the epoch + seconds, and then times() to obtain CPU clock ticks (milliseconds). + Combine this together to obtain a hopefully unique cookie ID. */ + + mpe_times = times(&mpe_tms); + + ap_snprintf(cookiebuf, sizeof(cookiebuf), "%s.%d%ld%ld", rname, + (int) getpid(), + (long) r->request_time, (long) mpe_tms.tms_utime); +#elif defined(WIN32) + /* + * We lack gettimeofday() and we lack times(). So we'll use a combination + * of time() and GetTickCount(), which returns milliseconds since Windows + * was started. It should be relatively unique. + */ + + ap_snprintf(cookiebuf, sizeof(cookiebuf), "%s.%d%ld%ld", rname, + (int) getpid(), + (long) r->request_time, (long) GetTickCount()); + +#else + gettimeofday(&tv, &tz); + + ap_snprintf(cookiebuf, sizeof(cookiebuf), "%s.%d%ld%d", rname, + (int) getpid(), + (long) tv.tv_sec, (int) tv.tv_usec / 1000); +#endif + + if (cls->expires) { + struct tm *tms; + time_t when = r->request_time + cls->expires; + +#ifndef MILLENIAL_COOKIES + /* + * Only two-digit date string, so we can't trust "00" or more. + * Therefore, we knock it all back to just before midnight on + * 1/1/2000 (which is 946684799) + */ + + if (when > 946684799) + when = 946684799; +#endif + tms = gmtime(&when); + + /* Cookie with date; as strftime '%a, %d-%h-%y %H:%M:%S GMT' */ + new_cookie = ap_psprintf(r->pool, + "%s=%s; path=/; expires=%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT", + dcfg->cookie_name, cookiebuf, ap_day_snames[tms->tm_wday], + tms->tm_mday, ap_month_snames[tms->tm_mon], + tms->tm_year % 100, + tms->tm_hour, tms->tm_min, tms->tm_sec); + } + else { + new_cookie = ap_psprintf(r->pool, "%s=%s; path=/", + dcfg->cookie_name, cookiebuf); + } + + ap_table_setn(r->headers_out, "Set-Cookie", new_cookie); + ap_table_setn(r->notes, "cookie", ap_pstrdup(r->pool, cookiebuf)); /* log first time */ + return; +} + +static int spot_cookie(request_rec *r) +{ + cookie_dir_rec *dcfg = ap_get_module_config(r->per_dir_config, + &usertrack_module); + const char *cookie; + char *value; + + if (!dcfg->enabled) { + return DECLINED; + } + + if ((cookie = ap_table_get(r->headers_in, "Cookie"))) + if ((value = strstr(cookie, dcfg->cookie_name))) { + char *cookiebuf, *cookieend; + + value += strlen(dcfg->cookie_name) + 1; /* Skip over the '=' */ + cookiebuf = ap_pstrdup(r->pool, value); + cookieend = strchr(cookiebuf, ';'); + if (cookieend) + *cookieend = '\0'; /* Ignore anything after a ; */ + + /* Set the cookie in a note, for logging */ + ap_table_setn(r->notes, "cookie", cookiebuf); + + return DECLINED; /* There's already a cookie, no new one */ + } + make_cookie(r); + return OK; /* We set our cookie */ +} + +static void *make_cookie_log_state(pool *p, server_rec *s) +{ + cookie_log_state *cls = + (cookie_log_state *) ap_palloc(p, sizeof(cookie_log_state)); + + cls->expires = 0; + + return (void *) cls; +} + +static void *make_cookie_dir(pool *p, char *d) +{ + cookie_dir_rec *dcfg; + + dcfg = (cookie_dir_rec *) ap_pcalloc(p, sizeof(cookie_dir_rec)); + dcfg->cookie_name = COOKIE_NAME; + dcfg->enabled = 0; + return dcfg; +} + +static const char *set_cookie_enable(cmd_parms *cmd, void *mconfig, int arg) +{ + cookie_dir_rec *dcfg = mconfig; + + dcfg->enabled = arg; + return NULL; +} + +static const char *set_cookie_exp(cmd_parms *parms, void *dummy, const char *arg) +{ + cookie_log_state *cls = ap_get_module_config(parms->server->module_config, + &usertrack_module); + time_t factor, modifier = 0; + time_t num = 0; + char *word; + + /* The simple case first - all numbers (we assume) */ + if (ap_isdigit(arg[0]) && ap_isdigit(arg[strlen(arg) - 1])) { + cls->expires = atol(arg); + return NULL; + } + + /* + * The harder case - stolen from mod_expires + * + * CookieExpires "[plus] {<num> <type>}*" + */ + + word = ap_getword_conf(parms->pool, &arg); + if (!strncasecmp(word, "plus", 1)) { + word = ap_getword_conf(parms->pool, &arg); + }; + + /* {<num> <type>}* */ + while (word[0]) { + /* <num> */ + if (ap_isdigit(word[0])) + num = atoi(word); + else + return "bad expires code, numeric value expected."; + + /* <type> */ + word = ap_getword_conf(parms->pool, &arg); + if (!word[0]) + return "bad expires code, missing <type>"; + + factor = 0; + if (!strncasecmp(word, "years", 1)) + factor = 60 * 60 * 24 * 365; + else if (!strncasecmp(word, "months", 2)) + factor = 60 * 60 * 24 * 30; + else if (!strncasecmp(word, "weeks", 1)) + factor = 60 * 60 * 24 * 7; + else if (!strncasecmp(word, "days", 1)) + factor = 60 * 60 * 24; + else if (!strncasecmp(word, "hours", 1)) + factor = 60 * 60; + else if (!strncasecmp(word, "minutes", 2)) + factor = 60; + else if (!strncasecmp(word, "seconds", 1)) + factor = 1; + else + return "bad expires code, unrecognized type"; + + modifier = modifier + factor * num; + + /* next <num> */ + word = ap_getword_conf(parms->pool, &arg); + } + + cls->expires = modifier; + + return NULL; +} + +static const char *set_cookie_name(cmd_parms *cmd, void *mconfig, char *name) +{ + cookie_dir_rec *dcfg = (cookie_dir_rec *) mconfig; + + dcfg->cookie_name = ap_pstrdup(cmd->pool, name); + return NULL; +} + +static const command_rec cookie_log_cmds[] = { + {"CookieExpires", set_cookie_exp, NULL, RSRC_CONF, TAKE1, + "an expiry date code"}, + {"CookieTracking", set_cookie_enable, NULL, OR_FILEINFO, FLAG, + "whether or not to enable cookies"}, + {"CookieName", set_cookie_name, NULL, OR_FILEINFO, TAKE1, + "name of the tracking cookie"}, + {NULL} +}; + +module MODULE_VAR_EXPORT usertrack_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + make_cookie_dir, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + make_cookie_log_state, /* server config */ + NULL, /* merge server configs */ + cookie_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + spot_cookie, /* fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post read-request */ +}; diff --git a/modules/metadata/mod_usertrack.dsp b/modules/metadata/mod_usertrack.dsp new file mode 100644 index 00000000000..e3aee558ce5 --- /dev/null +++ b/modules/metadata/mod_usertrack.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleUserTrack" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleUserTrack - 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 "ApacheModuleUserTrack.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 "ApacheModuleUserTrack.mak"\ + CFG="ApacheModuleUserTrack - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleUserTrack - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleUserTrack - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleUserTrack - 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 ".\ApacheModuleUserTrackR" +# PROP Intermediate_Dir ".\ApacheModuleUserTrackR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleUserTrack - 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 ".\ApacheModuleUserTrackD" +# PROP Intermediate_Dir ".\ApacheModuleUserTrackD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleUserTrack - Win32 Release" +# Name "ApacheModuleUserTrack - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_usertrack.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/proxy/.cvsignore b/modules/proxy/.cvsignore new file mode 100644 index 00000000000..e0eb7a9f308 --- /dev/null +++ b/modules/proxy/.cvsignore @@ -0,0 +1,12 @@ +Debug +Release +ApacheModuleProxy.dsw +ApacheModuleProxy.mdp +ApacheModuleProxy.ncb +ApacheModuleProxy.opt +ApacheModuleProxy.plg +Makefile +*.lo +*.so +*.dll +*.def diff --git a/modules/proxy/.indent.pro b/modules/proxy/.indent.pro new file mode 100644 index 00000000000..20c2d83371d --- /dev/null +++ b/modules/proxy/.indent.pro @@ -0,0 +1,55 @@ +-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 +-Tproxy_server_conf diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c new file mode 100644 index 00000000000..5d22d081643 --- /dev/null +++ b/modules/proxy/mod_proxy.c @@ -0,0 +1,898 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +#include "mod_proxy.h" + +#define CORE_PRIVATE + +#include "http_log.h" +#include "http_vhost.h" +#include "http_request.h" + +/* Some WWW schemes and their default ports; this is basically /etc/services */ +/* This will become global when the protocol abstraction comes */ +static struct proxy_services defports[] = +{ + {"http", DEFAULT_HTTP_PORT}, + {"ftp", DEFAULT_FTP_PORT}, + {"https", DEFAULT_HTTPS_PORT}, + {"gopher", DEFAULT_GOPHER_PORT}, + {"nntp", DEFAULT_NNTP_PORT}, + {"wais", DEFAULT_WAIS_PORT}, + {"snews", DEFAULT_SNEWS_PORT}, + {"prospero", DEFAULT_PROSPERO_PORT}, + {NULL, -1} /* unknown port */ +}; + +/* + * A Web proxy module. Stages: + * + * translate_name: set filename to proxy:<URL> + * type_checker: set type to PROXY_MAGIC_TYPE if filename begins proxy: + * fix_ups: convert the URL stored in the filename to the + * canonical form. + * handler: handle proxy requests + */ + +/* -------------------------------------------------------------- */ +/* Translate the URL into a 'filename' */ + +static int alias_match(const char *uri, const char *alias_fakename) +{ + const char *end_fakename = alias_fakename + strlen(alias_fakename); + const char *aliasp = alias_fakename, *urip = uri; + + while (aliasp < end_fakename) { + if (*aliasp == '/') { + /* any number of '/' in the alias matches any number in + * the supplied URI, but there must be at least one... + */ + if (*urip != '/') + return 0; + + while (*aliasp == '/') + ++aliasp; + while (*urip == '/') + ++urip; + } + else { + /* Other characters are compared literally */ + if (*urip++ != *aliasp++) + return 0; + } + } + + /* Check last alias path component matched all the way */ + + if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') + return 0; + + /* Return number of characters from URI which matched (may be + * greater than length of alias, since we may have matched + * doubled slashes) + */ + + return urip - uri; +} + +/* Detect if an absoluteURI should be proxied or not. Note that we + * have to do this during this phase because later phases are + * "short-circuiting"... i.e. translate_names will end when the first + * module returns OK. So for example, if the request is something like: + * + * GET http://othervhost/cgi-bin/printenv HTTP/1.0 + * + * mod_alias will notice the /cgi-bin part and ScriptAlias it and + * short-circuit the proxy... just because of the ordering in the + * configuration file. + */ +static int proxy_detect(request_rec *r) +{ + void *sconf = r->server->module_config; + proxy_server_conf *conf; + + conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + + if (conf->req && r->parsed_uri.scheme) { + /* but it might be something vhosted */ + if (!(r->parsed_uri.hostname + && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r)) + && ap_matches_request_vhost(r, r->parsed_uri.hostname, + r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) { + r->proxyreq = 1; + r->uri = r->unparsed_uri; + r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL); + r->handler = "proxy-server"; + } + } + /* We need special treatment for CONNECT proxying: it has no scheme part */ + else if (conf->req && r->method_number == M_CONNECT + && r->parsed_uri.hostname + && r->parsed_uri.port_str) { + r->proxyreq = 1; + r->uri = r->unparsed_uri; + r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL); + r->handler = "proxy-server"; + } + return DECLINED; +} + +static int proxy_trans(request_rec *r) +{ + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + int i, len; + struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts; + + if (r->proxyreq) { + /* someone has already set up the proxy, it was possibly ourselves + * in proxy_detect + */ + return OK; + } + + /* XXX: since r->uri has been manipulated already we're not really + * compliant with RFC1945 at this point. But this probably isn't + * an issue because this is a hybrid proxy/origin server. + */ + + for (i = 0; i < conf->aliases->nelts; i++) { + len = alias_match(r->uri, ent[i].fake); + + if (len > 0) { + r->filename = ap_pstrcat(r->pool, "proxy:", ent[i].real, + r->uri + len, NULL); + r->handler = "proxy-server"; + r->proxyreq = 1; + return OK; + } + } + return DECLINED; +} + +/* -------------------------------------------------------------- */ +/* Fixup the filename */ + +/* + * Canonicalise the URL + */ +static int proxy_fixup(request_rec *r) +{ + char *url, *p; + + if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0) + return DECLINED; + + url = &r->filename[6]; + +/* canonicalise each specific scheme */ + if (strncasecmp(url, "http:", 5) == 0) + return ap_proxy_http_canon(r, url + 5, "http", DEFAULT_HTTP_PORT); + else if (strncasecmp(url, "ftp:", 4) == 0) + return ap_proxy_ftp_canon(r, url + 4); + + p = strchr(url, ':'); + if (p == NULL || p == url) + return HTTP_BAD_REQUEST; + + return OK; /* otherwise; we've done the best we can */ +} + +static void proxy_init(server_rec *r, pool *p) +{ + ap_proxy_garbage_init(r, p); +} + + + +/* Send a redirection if the request contains a hostname which is not */ +/* fully qualified, i.e. doesn't have a domain name appended. Some proxy */ +/* servers like Netscape's allow this and access hosts from the local */ +/* domain in this case. I think it is better to redirect to a FQDN, since */ +/* these will later be found in the bookmarks files. */ +/* The "ProxyDomain" directive determines what domain will be appended */ +static int proxy_needsdomain(request_rec *r, const char *url, const char *domain) +{ + char *nuri; + const char *ref; + + /* We only want to worry about GETs */ + if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname) + return DECLINED; + + /* If host does contain a dot already, or it is "localhost", decline */ + if (strchr(r->parsed_uri.hostname, '.') != NULL + || strcasecmp(r->parsed_uri.hostname, "localhost") == 0) + return DECLINED; /* host name has a dot already */ + + ref = ap_table_get(r->headers_in, "Referer"); + + /* Reassemble the request, but insert the domain after the host name */ + /* Note that the domain name always starts with a dot */ + r->parsed_uri.hostname = ap_pstrcat(r->pool, r->parsed_uri.hostname, + domain, NULL); + nuri = ap_unparse_uri_components(r->pool, + &r->parsed_uri, + UNP_REVEALPASSWORD); + + ap_table_set(r->headers_out, "Location", nuri); + ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r, + "Domain missing: %s sent to %s%s%s", r->uri, + ap_unparse_uri_components(r->pool, &r->parsed_uri, + UNP_OMITUSERINFO), + ref ? " from " : "", ref ? ref : ""); + + return HTTP_MOVED_PERMANENTLY; +} + +/* -------------------------------------------------------------- */ +/* Invoke handler */ + +static int proxy_handler(request_rec *r) +{ + char *url, *scheme, *p; + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + array_header *proxies = conf->proxies; + struct proxy_remote *ents = (struct proxy_remote *) proxies->elts; + int i, rc; + cache_req *cr; + int direct_connect = 0; + const char *maxfwd_str; + + if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0) + return DECLINED; + + if (r->method_number == M_TRACE && + (maxfwd_str = ap_table_get(r->headers_in, "Max-Forwards")) != NULL) { + int maxfwd = strtol(maxfwd_str, NULL, 10); + if (maxfwd < 1) { + int access_status; + r->proxyreq = 0; + if ((access_status = ap_send_http_trace(r))) + ap_die(access_status, r); + else + ap_finalize_request_protocol(r); + return OK; + } + ap_table_setn(r->headers_in, "Max-Forwards", + ap_psprintf(r->pool, "%d", (maxfwd > 0) ? maxfwd-1 : 0)); + } + + if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) + return rc; + + url = r->filename + 6; + p = strchr(url, ':'); + if (p == NULL) + return HTTP_BAD_REQUEST; + + rc = ap_proxy_cache_check(r, url, &conf->cache, &cr); + if (rc != DECLINED) + return rc; + + /* If the host doesn't have a domain name, add one and redirect. */ + if (conf->domain != NULL) { + rc = proxy_needsdomain(r, url, conf->domain); + if (ap_is_HTTP_REDIRECT(rc)) + return HTTP_MOVED_PERMANENTLY; + } + + *p = '\0'; + scheme = ap_pstrdup(r->pool, url); + *p = ':'; + + /* Check URI's destination host against NoProxy hosts */ + /* Bypass ProxyRemote server lookup if configured as NoProxy */ + /* we only know how to handle communication to a proxy via http */ + /*if (strcasecmp(scheme, "http") == 0) */ + { + int ii; + struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts; + + for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) { + direct_connect = list[ii].matcher(&list[ii], r); + } +#if DEBUGGING + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r, + (direct_connect) ? "NoProxy for %s" : "UseProxy for %s", + r->uri); +#endif + } + +/* firstly, try a proxy, unless a NoProxy directive is active */ + + if (!direct_connect) + for (i = 0; i < proxies->nelts; i++) { + p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */ + if (strcmp(ents[i].scheme, "*") == 0 || + (p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) || + (p != NULL && + strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) { + /* CONNECT is a special method that bypasses the normal + * proxy code. + */ + if (r->method_number == M_CONNECT) + rc = ap_proxy_connect_handler(r, cr, url, ents[i].hostname, + ents[i].port); +/* we only know how to handle communication to a proxy via http */ + else if (strcasecmp(ents[i].protocol, "http") == 0) + rc = ap_proxy_http_handler(r, cr, url, ents[i].hostname, + ents[i].port); + else + rc = DECLINED; + + /* an error or success */ + if (rc != DECLINED && rc != HTTP_BAD_GATEWAY) + return rc; + /* we failed to talk to the upstream proxy */ + } + } + +/* otherwise, try it direct */ +/* N.B. what if we're behind a firewall, where we must use a proxy or + * give up?? + */ + /* handle the scheme */ + if (r->method_number == M_CONNECT) + return ap_proxy_connect_handler(r, cr, url, NULL, 0); + if (strcasecmp(scheme, "http") == 0) + return ap_proxy_http_handler(r, cr, url, NULL, 0); + if (strcasecmp(scheme, "ftp") == 0) + return ap_proxy_ftp_handler(r, cr, url); + else + return HTTP_FORBIDDEN; +} + +/* -------------------------------------------------------------- */ +/* Setup configurable data */ + +static void * + create_proxy_config(pool *p, server_rec *s) +{ + proxy_server_conf *ps = ap_pcalloc(p, sizeof(proxy_server_conf)); + + ps->proxies = ap_make_array(p, 10, sizeof(struct proxy_remote)); + ps->aliases = ap_make_array(p, 10, sizeof(struct proxy_alias)); + ps->raliases = ap_make_array(p, 10, sizeof(struct proxy_alias)); + ps->noproxies = ap_make_array(p, 10, sizeof(struct noproxy_entry)); + ps->dirconn = ap_make_array(p, 10, sizeof(struct dirconn_entry)); + ps->nocaches = ap_make_array(p, 10, sizeof(struct nocache_entry)); + ps->allowed_connect_ports = ap_make_array(p, 10, sizeof(int)); + ps->domain = NULL; + ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */ + ps->req = 0; + + ps->cache.root = NULL; + ps->cache.space = DEFAULT_CACHE_SPACE; + ps->cache.maxexpire = DEFAULT_CACHE_MAXEXPIRE; + ps->cache.defaultexpire = DEFAULT_CACHE_EXPIRE; + ps->cache.lmfactor = DEFAULT_CACHE_LMFACTOR; + ps->cache.gcinterval = -1; + /* at these levels, the cache can have 2^18 directories (256,000) */ + ps->cache.dirlevels = 3; + ps->cache.dirlength = 1; + ps->cache.cache_completion = DEFAULT_CACHE_COMPLETION; + + return ps; +} + +static const char * + add_proxy(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); + struct proxy_remote *new; + char *p, *q; + int port; + + p = strchr(r, ':'); + if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') + return "ProxyRemote: Bad syntax for a remote proxy server"; + q = strchr(p + 3, ':'); + if (q != NULL) { + if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) + return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)"; + *q = '\0'; + } + else + port = -1; + *p = '\0'; + if (strchr(f, ':') == NULL) + ap_str_tolower(f); /* lowercase scheme */ + ap_str_tolower(p + 3); /* lowercase hostname */ + + if (port == -1) { + int i; + for (i = 0; defports[i].scheme != NULL; i++) + if (strcasecmp(defports[i].scheme, r) == 0) + break; + port = defports[i].port; + } + + new = ap_push_array(conf->proxies); + new->scheme = f; + new->protocol = r; + new->hostname = p + 3; + new->port = port; + return NULL; +} + +static const char * + add_pass(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); + struct proxy_alias *new; + + new = ap_push_array(conf->aliases); + new->fake = f; + new->real = r; + return NULL; +} + +static const char * + add_pass_reverse(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf; + struct proxy_alias *new; + + conf = (proxy_server_conf *)ap_get_module_config(s->module_config, + &proxy_module); + new = ap_push_array(conf->raliases); + new->fake = f; + new->real = r; + return NULL; +} + +static const char * + set_proxy_exclude(cmd_parms *parms, void *dummy, char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + ap_get_module_config(s->module_config, &proxy_module); + struct noproxy_entry *new; + struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts; + struct hostent hp; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i = 0; i < conf->noproxies->nelts; i++) { + if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */ + found = 1; + } + + if (!found) { + new = ap_push_array(conf->noproxies); + new->name = arg; + /* Don't do name lookups on things that aren't dotted */ + if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL) + /*@@@FIXME: This copies only the first of (possibly many) IP addrs */ + memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr)); + else + new->addr.s_addr = 0; + } + return NULL; +} + +/* + * Set the ports CONNECT can use + */ +static const char * + set_allowed_ports(cmd_parms *parms, void *dummy, char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + ap_get_module_config(s->module_config, &proxy_module); + int *New; + + if (!ap_isdigit(arg[0])) + return "AllowCONNECT: port number must be numeric"; + + New = ap_push_array(conf->allowed_connect_ports); + *New = atoi(arg); + return NULL; +} + +/* Similar to set_proxy_exclude(), but defining directly connected hosts, + * which should never be accessed via the configured ProxyRemote servers + */ +static const char * + set_proxy_dirconn(cmd_parms *parms, void *dummy, char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + ap_get_module_config(s->module_config, &proxy_module); + struct dirconn_entry *New; + struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i = 0; i < conf->dirconn->nelts; i++) { + if (strcasecmp(arg, list[i].name) == 0) + found = 1; + } + + if (!found) { + New = ap_push_array(conf->dirconn); + New->name = arg; + New->hostentry = NULL; + + if (ap_proxy_is_ipaddr(New, parms->pool)) { +#if DEBUGGING + fprintf(stderr, "Parsed addr %s\n", inet_ntoa(New->addr)); + fprintf(stderr, "Parsed mask %s\n", inet_ntoa(New->mask)); +#endif + } + else if (ap_proxy_is_domainname(New, parms->pool)) { + ap_str_tolower(New->name); +#if DEBUGGING + fprintf(stderr, "Parsed domain %s\n", New->name); +#endif + } + else if (ap_proxy_is_hostname(New, parms->pool)) { + ap_str_tolower(New->name); +#if DEBUGGING + fprintf(stderr, "Parsed host %s\n", New->name); +#endif + } + else { + ap_proxy_is_word(New, parms->pool); +#if DEBUGGING + fprintf(stderr, "Parsed word %s\n", New->name); +#endif + } + } + return NULL; +} + +static const char * + set_proxy_domain(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + if (arg[0] != '.') + return "ProxyDomain: domain name must start with a dot."; + + psf->domain = arg; + return NULL; +} + +static const char * + set_proxy_req(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->req = flag; + return NULL; +} + + +static const char * + set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + int val; + + if (sscanf(arg, "%d", &val) != 1) + return "CacheSize value must be an integer (kBytes)"; + psf->cache.space = val; + return NULL; +} + +static const char * + set_cache_root(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->cache.root = arg; + + return NULL; +} + +static const char * + set_cache_factor(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) + return "CacheLastModifiedFactor value must be a float"; + psf->cache.lmfactor = val; + + return NULL; +} + +static const char * + set_cache_maxex(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) + return "CacheMaxExpire value must be a float"; + psf->cache.maxexpire = (int) (val * (double) SEC_ONE_HR); + return NULL; +} + +static const char * + set_cache_defex(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) + return "CacheDefaultExpire value must be a float"; + psf->cache.defaultexpire = (int) (val * (double) SEC_ONE_HR); + return NULL; +} + +static const char * + set_cache_gcint(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) + return "CacheGcInterval value must be a float"; + psf->cache.gcinterval = (int) (val * (double) SEC_ONE_HR); + return NULL; +} + +static const char * + set_cache_dirlevels(cmd_parms *parms, char *struct_ptr, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + int val; + + val = atoi(arg); + if (val < 1) + return "CacheDirLevels value must be an integer greater than 0"; + if (val * psf->cache.dirlength > CACHEFILE_LEN) + return "CacheDirLevels*CacheDirLength value must not be higher than 20"; + psf->cache.dirlevels = val; + return NULL; +} + +static const char * + set_cache_dirlength(cmd_parms *parms, char *struct_ptr, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + int val; + + val = atoi(arg); + if (val < 1) + return "CacheDirLength value must be an integer greater than 0"; + if (val * psf->cache.dirlevels > CACHEFILE_LEN) + return "CacheDirLevels*CacheDirLength value must not be higher than 20"; + psf->cache.dirlength = val; + return NULL; +} + +static const char * + set_cache_exclude(cmd_parms *parms, void *dummy, char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + ap_get_module_config(s->module_config, &proxy_module); + struct nocache_entry *new; + struct nocache_entry *list = (struct nocache_entry *) conf->nocaches->elts; + struct hostent hp; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i = 0; i < conf->nocaches->nelts; i++) { + if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */ + found = 1; + } + + if (!found) { + new = ap_push_array(conf->nocaches); + new->name = arg; + /* Don't do name lookups on things that aren't dotted */ + if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL) + /*@@@FIXME: This copies only the first of (possibly many) IP addrs */ + memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr)); + else + new->addr.s_addr = 0; + } + return NULL; +} + +static const char * + set_recv_buffer_size(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + int s = atoi(arg); + if (s < 512 && s != 0) { + return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default."; + } + + psf->recv_buffer_size = s; + return NULL; +} + +static const char* + set_cache_completion(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + int s = atoi(arg); + if (s > 100 || s < 0) { + return "CacheForceCompletion must be <= 100 percent, " + "or 0 for system default."; + } + + if (s > 0) + psf->cache.cache_completion = ((float)s / 100); + return NULL; +} + +static const char* + set_via_opt(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + if (strcasecmp(arg, "Off") == 0) + psf->viaopt = via_off; + else if (strcasecmp(arg, "On") == 0) + psf->viaopt = via_on; + else if (strcasecmp(arg, "Block") == 0) + psf->viaopt = via_block; + else if (strcasecmp(arg, "Full") == 0) + psf->viaopt = via_full; + else { + return "ProxyVia must be one of: " + "off | on | full | block"; + } + + return NULL; +} + +static const handler_rec proxy_handlers[] = +{ + {"proxy-server", proxy_handler}, + {NULL} +}; + +static const command_rec proxy_cmds[] = +{ + {"ProxyRequests", set_proxy_req, NULL, RSRC_CONF, FLAG, + "on if the true proxy requests should be accepted"}, + {"ProxyRemote", add_proxy, NULL, RSRC_CONF, TAKE2, + "a scheme, partial URL or '*' and a proxy server"}, + {"ProxyPass", add_pass, NULL, RSRC_CONF, TAKE2, + "a virtual path and a URL"}, + {"ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF, TAKE2, + "a virtual path and a URL for reverse proxy behaviour"}, + {"ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, ITERATE, + "A list of names, hosts or domains to which the proxy will not connect"}, + {"ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF, TAKE1, + "Receive buffer size for outgoing HTTP and FTP connections in bytes"}, + {"NoProxy", set_proxy_dirconn, NULL, RSRC_CONF, ITERATE, + "A list of domains, hosts, or subnets to which the proxy will connect directly"}, + {"ProxyDomain", set_proxy_domain, NULL, RSRC_CONF, TAKE1, + "The default intranet domain name (in absence of a domain in the URL)"}, + {"AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF, ITERATE, + "A list of ports which CONNECT may connect to"}, + {"CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1, + "The directory to store cache files"}, + {"CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1, + "The maximum disk space used by the cache in Kb"}, + {"CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1, + "The maximum time in hours to cache a document"}, + {"CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1, + "The default time in hours to cache a document"}, + {"CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1, + "The factor used to estimate Expires date from LastModified date"}, + {"CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1, + "The interval between garbage collections, in hours"}, + {"CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF, TAKE1, + "The number of levels of subdirectories in the cache"}, + {"CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF, TAKE1, + "The number of characters in subdirectory names"}, + {"NoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE, + "A list of names, hosts or domains for which caching is *not* provided"}, + {"CacheForceCompletion", set_cache_completion, NULL, RSRC_CONF, TAKE1, + "Force a http cache completion after this percentage is loaded"}, + {"ProxyVia", set_via_opt, NULL, RSRC_CONF, TAKE1, + "Configure Via: proxy header header to one of: on | off | block | full"}, + {NULL} +}; + +module MODULE_VAR_EXPORT proxy_module = +{ + STANDARD_MODULE_STUFF, + proxy_init, /* initializer */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + create_proxy_config, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + proxy_cmds, /* command table */ + proxy_handlers, /* handlers */ + proxy_trans, /* translate_handler */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + proxy_fixup, /* pre-run fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + proxy_detect /* post read-request */ +}; diff --git a/modules/proxy/mod_proxy.dsp b/modules/proxy/mod_proxy.dsp new file mode 100644 index 00000000000..6ee5cf30ddd --- /dev/null +++ b/modules/proxy/mod_proxy.dsp @@ -0,0 +1,133 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleProxy" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleProxy - 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 "ApacheModuleProxy.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 "ApacheModuleProxy.mak"\ + CFG="ApacheModuleProxy - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleProxy - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleProxy - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleProxy - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir ".\ApacheMo" +# PROP BASE Intermediate_Dir ".\ApacheMo" +# 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 /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleProxy - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\ApacheM0" +# PROP BASE Intermediate_Dir ".\ApacheM0" +# 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 /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleProxy - Win32 Release" +# Name "ApacheModuleProxy - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy.c +# End Source File +# Begin Source File + +SOURCE=.\proxy_cache.c +# End Source File +# Begin Source File + +SOURCE=.\proxy_connect.c +# End Source File +# Begin Source File + +SOURCE=.\proxy_ftp.c +# End Source File +# Begin Source File + +SOURCE=.\proxy_http.c +# End Source File +# Begin Source File + +SOURCE=.\proxy_util.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h new file mode 100644 index 00000000000..9eb3428d80c --- /dev/null +++ b/modules/proxy/mod_proxy.h @@ -0,0 +1,316 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +#ifndef MOD_PROXY_H +#define MOD_PROXY_H + +/* + * Main include file for the Apache proxy + */ + +/* + + Note that the Explain() stuff is not yet complete. + Also note numerous FIXMEs and CHECKMEs which should be eliminated. + + If TESTING is set, then garbage collection doesn't delete ... probably a good + idea when hacking. + + This code is still experimental! + + Things to do: + + 1. Make it garbage collect in the background, not while someone is waiting for + a response! + + 2. Check the logic thoroughly. + + 3. Empty directories are only removed the next time round (but this does avoid + two passes). Consider doing them the first time round. + + Ben Laurie <ben@algroup.co.uk> 30 Mar 96 + + More things to do: + + 0. Code cleanup (ongoing) + + 1. add 230 response output for ftp now that it works + + 2. Make the ftp proxy transparent, also same with (future) gopher & wais + + 3. Use protocol handler struct a la Apache module handlers (Dirk van Gulik) + + 4. Use a cache expiry database for more efficient GC (Jeremy Wohl) + + 5. Bulletproof GC against SIGALRM + + Chuck Murcko <chuck@topsail.org> 15 April 1997 + + */ + +#define TESTING 0 +#undef EXPLAIN + +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" + +#include "explain.h" + +extern module MODULE_VAR_EXPORT proxy_module; + + +/* for proxy_canonenc() */ +enum enctype { + enc_path, enc_search, enc_user, enc_fpath, enc_parm +}; + +#define HDR_APP (0) /* append header, for proxy_add_header() */ +#define HDR_REP (1) /* replace header, for proxy_add_header() */ + +/* number of characters in the hash */ +#define HASH_LEN (22*2) + +/* maximum 'CacheDirLevels*CacheDirLength' value */ +#define CACHEFILE_LEN 20 /* must be less than HASH_LEN/2 */ + +#ifdef CHARSET_EBCDIC +#define CRLF "\r\n" +#else /*CHARSET_EBCDIC*/ +#define CRLF "\015\012" +#endif /*CHARSET_EBCDIC*/ + + +#define SEC_ONE_DAY 86400 /* one day, in seconds */ +#define SEC_ONE_HR 3600 /* one hour, in seconds */ + +#define DEFAULT_FTP_DATA_PORT 20 +#define DEFAULT_FTP_PORT 21 +#define DEFAULT_GOPHER_PORT 70 +#define DEFAULT_NNTP_PORT 119 +#define DEFAULT_WAIS_PORT 210 +#define DEFAULT_HTTPS_PORT 443 +#define DEFAULT_SNEWS_PORT 563 +#define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */ + +/* Some WWW schemes and their default ports; this is basically /etc/services */ +struct proxy_services { + const char *scheme; + int port; +}; + +/* static information about a remote proxy */ +struct proxy_remote { + const char *scheme; /* the schemes handled by this proxy, or '*' */ + const char *protocol; /* the scheme used to talk to this proxy */ + const char *hostname; /* the hostname of this proxy */ + int port; /* the port for this proxy */ +}; + +struct proxy_alias { + char *real; + char *fake; +}; + +struct dirconn_entry { + char *name; + struct in_addr addr, mask; + struct hostent *hostentry; + int (*matcher) (struct dirconn_entry * This, request_rec *r); +}; + +struct noproxy_entry { + char *name; + struct in_addr addr; +}; + +struct nocache_entry { + char *name; + struct in_addr addr; +}; + +#define DEFAULT_CACHE_SPACE 5 +#define DEFAULT_CACHE_MAXEXPIRE SEC_ONE_DAY +#define DEFAULT_CACHE_EXPIRE SEC_ONE_HR +#define DEFAULT_CACHE_LMFACTOR (0.1) +#define DEFAULT_CACHE_COMPLETION (0.9) + +/* static information about the local cache */ +struct cache_conf { + const char *root; /* the location of the cache directory */ + off_t space; /* Maximum cache size (in 1024 bytes) */ + time_t maxexpire; /* Maximum time to keep cached files in secs */ + time_t defaultexpire; /* default time to keep cached file in secs */ + double lmfactor; /* factor for estimating expires date */ + time_t gcinterval; /* garbage collection interval, in seconds */ + int dirlevels; /* Number of levels of subdirectories */ + int dirlength; /* Length of subdirectory names */ + float cache_completion; /* Force cache completion after this point */ +}; + +typedef struct { + struct cache_conf cache; /* cache configuration */ + array_header *proxies; + array_header *aliases; + array_header *raliases; + array_header *noproxies; + array_header *dirconn; + array_header *nocaches; + array_header *allowed_connect_ports; + char *domain; /* domain name to use in absence of a domain name in the request */ + int req; /* true if proxy requests are enabled */ + enum { + via_off, + via_on, + via_block, + via_full + } viaopt; /* how to deal with proxy Via: headers */ + size_t recv_buffer_size; +} proxy_server_conf; + +struct hdr_entry { + const char *field; + const char *value; +}; + +/* caching information about a request */ +typedef struct { + request_rec *req; /* the request */ + char *url; /* the URL requested */ + char *filename; /* name of the cache file, or NULL if no cache */ + char *tempfile; /* name of the temporary file, of NULL if not caching */ + time_t ims; /* if-modified-since date of request; -1 if no header */ + BUFF *fp; /* the cache file descriptor if the file is cached + and may be returned, or NULL if the file is + not cached (or must be reloaded) */ + time_t expire; /* calculated expire date of cached entity */ + time_t lmod; /* last-modified date of cached entity */ + time_t date; /* the date the cached file was last touched */ + int version; /* update count of the file */ + off_t len; /* content length */ + char *protocol; /* Protocol, and major/minor number, e.g. HTTP/1.1 */ + int status; /* the status of the cached file */ + unsigned int written; /* total *content* bytes written to cache */ + float cache_completion; /* specific to this request */ + char *resp_line; /* the whole status like (protocol, code + message) */ + table *hdrs; /* the HTTP headers of the file */ +} cache_req; + +/* Additional information passed to the function called by ap_table_do() */ +struct tbl_do_args { + request_rec *req; + cache_req *cache; +}; + +/* Function prototypes */ + +/* proxy_cache.c */ + +void ap_proxy_cache_tidy(cache_req *c); +int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf, + cache_req **cr); +int ap_proxy_cache_update(cache_req *c, table *resp_hdrs, + const int is_HTTP1, int nocache); +void ap_proxy_garbage_coll(request_rec *r); + +/* proxy_connect.c */ + +int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, + const char *proxyhost, int proxyport); + +/* proxy_ftp.c */ + +int ap_proxy_ftp_canon(request_rec *r, char *url); +int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url); + +/* proxy_http.c */ + +int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, + int def_port); +int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, + const char *proxyhost, int proxyport); + +/* proxy_util.c */ + +int ap_proxy_hex2c(const char *x); +void ap_proxy_c2hex(int ch, char *x); +char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t, + int isenc); +char *ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp, + char **passwordp, char **hostp, int *port); +const char *ap_proxy_date_canon(pool *p, const char *x); +table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f); +long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c); +void ap_proxy_send_headers(request_rec *r, const char *respline, table *hdrs); +int ap_proxy_liststr(const char *list, const char *val); +void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength); +int ap_proxy_hex2sec(const char *x); +void ap_proxy_sec2hex(int t, char *y); +cache_req *ap_proxy_cache_error(cache_req *r); +int ap_proxyerror(request_rec *r, int statuscode, const char *message); +const char *ap_proxy_host2addr(const char *host, struct hostent *reqhp); +int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p); +int ap_proxy_is_domainname(struct dirconn_entry *This, pool *p); +int ap_proxy_is_hostname(struct dirconn_entry *This, pool *p); +int ap_proxy_is_word(struct dirconn_entry *This, pool *p); +int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r); +int ap_proxy_garbage_init(server_rec *, pool *); +/* This function is called by ap_table_do() for all header lines */ +int ap_proxy_send_hdr_line(void *p, const char *key, const char *value); +unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache); + +#endif /*MOD_PROXY_H*/ diff --git a/modules/proxy/proxy_connect.c b/modules/proxy/proxy_connect.c new file mode 100644 index 00000000000..82203f2c9ef --- /dev/null +++ b/modules/proxy/proxy_connect.c @@ -0,0 +1,288 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +/* CONNECT method for Apache proxy */ + +#include "mod_proxy.h" +#include "http_log.h" +#include "http_main.h" + +#ifdef HAVE_BSTRING_H +#include <bstring.h> /* for IRIX, FD_SET calls bzero() */ +#endif + +DEF_Explain + +/* + * This handles Netscape CONNECT method secure proxy requests. + * A connection is opened to the specified host and data is + * passed through between the WWW site and the browser. + * + * This code is based on the INTERNET-DRAFT document + * "Tunneling SSL Through a WWW Proxy" currently at + * http://www.mcom.com/newsref/std/tunneling_ssl.html. + * + * If proxyhost and proxyport are set, we send a CONNECT to + * the specified proxy.. + * + * FIXME: this is bad, because it does its own socket I/O + * instead of using the I/O in buff.c. However, + * the I/O in buff.c blocks on reads, and because + * this function doesn't know how much data will + * be sent either way (or when) it can't use blocking + * I/O. This may be very implementation-specific + * (to Linux). Any suggestions? + * FIXME: this doesn't log the number of bytes sent, but + * that may be okay, since the data is supposed to + * be transparent. In fact, this doesn't log at all + * yet. 8^) + * FIXME: doesn't check any headers initally sent from the + * client. + * FIXME: should allow authentication, but hopefully the + * generic proxy authentication is good enough. + * FIXME: no check for r->assbackwards, whatever that is. + */ + +static int +allowed_port(proxy_server_conf *conf, int port) +{ + int i; + int *list = (int *) conf->allowed_connect_ports->elts; + + for(i = 0; i < conf->allowed_connect_ports->nelts; i++) { + if(port == list[i]) + return 1; + } + return 0; +} + + +int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, + const char *proxyhost, int proxyport) +{ + struct sockaddr_in server; + struct in_addr destaddr; + struct hostent server_hp; + const char *host, *err; + char *p; + int port, sock; + char buffer[HUGE_STRING_LEN]; + int nbytes, i, j; + fd_set fds; + + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; + + memset(&server, '\0', sizeof(server)); + server.sin_family = AF_INET; + + /* Break the URL into host:port pairs */ + + host = url; + p = strchr(url, ':'); + if (p == NULL) + port = DEFAULT_HTTPS_PORT; + else { + port = atoi(p + 1); + *p = '\0'; + } + +/* check if ProxyBlock directive on this host */ + destaddr.s_addr = ap_inet_addr(host); + for (i = 0; i < conf->noproxies->nelts; i++) { + if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL) + || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*') + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } + + /* Check if it is an allowed port */ + if (conf->allowed_connect_ports->nelts == 0) { + /* Default setting if not overridden by AllowCONNECT */ + switch (port) { + case DEFAULT_HTTPS_PORT: + case DEFAULT_SNEWS_PORT: + break; + default: + return HTTP_FORBIDDEN; + } + } else if(!allowed_port(conf, port)) + return HTTP_FORBIDDEN; + + if (proxyhost) { + Explain2("CONNECT to remote proxy %s on port %d", proxyhost, proxyport); + } + else { + Explain2("CONNECT to %s on port %d", host, port); + } + + server.sin_port = (proxyport ? htons(proxyport) : htons(port)); + err = ap_proxy_host2addr(proxyhost ? proxyhost : host, &server_hp); + + if (err != NULL) + return ap_proxyerror(r, + proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR, + err); + + sock = ap_psocket(r->pool, PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating socket"); + return HTTP_INTERNAL_SERVER_ERROR; + } + +#ifdef CHECK_FD_SETSIZE + if (sock >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "proxy_connect_handler: filedescriptor (%u) " + "larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", sock, FD_SETSIZE); + ap_pclosesocket(r->pool, sock); + return HTTP_INTERNAL_SERVER_ERROR; + } +#endif + + j = 0; + while (server_hp.h_addr_list[j] != NULL) { + memcpy(&server.sin_addr, server_hp.h_addr_list[j], + sizeof(struct in_addr)); + i = ap_proxy_doconnect(sock, &server, r); + if (i == 0) + break; + j++; + } + if (i == -1) { + ap_pclosesocket(r->pool, sock); + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_pstrcat(r->pool, + "Could not connect to remote machine:<br>", + strerror(errno), NULL)); + } + + /* If we are connecting through a remote proxy, we need to pass + * the CONNECT request on to it. + */ + if (proxyport) { + /* FIXME: We should not be calling write() directly, but we currently + * have no alternative. Error checking ignored. Also, we force + * a HTTP/1.0 request to keep things simple. + */ + Explain0("Sending the CONNECT request to the remote proxy"); + ap_snprintf(buffer, sizeof(buffer), "CONNECT %s HTTP/1.0" CRLF, + r->uri); + write(sock, buffer, strlen(buffer)); + ap_snprintf(buffer, sizeof(buffer), + "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); + write(sock, buffer, strlen(buffer)); + } + else { + Explain0("Returning 200 OK Status"); + ap_rvputs(r, "HTTP/1.0 200 Connection established" CRLF, NULL); + ap_rvputs(r, "Proxy-agent: ", ap_get_server_version(), CRLF CRLF, NULL); + ap_bflush(r->connection->client); + } + + while (1) { /* Infinite loop until error (one side closes the connection) */ + FD_ZERO(&fds); + FD_SET(sock, &fds); + FD_SET(r->connection->client->fd, &fds); + + Explain0("Going to sleep (select)"); + i = ap_select((r->connection->client->fd > sock ? + r->connection->client->fd + 1 : + sock + 1), &fds, NULL, NULL, NULL); + Explain1("Woke from select(), i=%d", i); + + if (i) { + if (FD_ISSET(sock, &fds)) { + Explain0("sock was set"); + if ((nbytes = read(sock, buffer, HUGE_STRING_LEN)) != 0) { + if (nbytes == -1) + break; + if (write(r->connection->client->fd, buffer, nbytes) == EOF) + break; + Explain1("Wrote %d bytes to client", nbytes); + } + else + break; + } + else if (FD_ISSET(r->connection->client->fd, &fds)) { + Explain0("client->fd was set"); + if ((nbytes = read(r->connection->client->fd, buffer, + HUGE_STRING_LEN)) != 0) { + if (nbytes == -1) + break; + if (write(sock, buffer, nbytes) == EOF) + break; + Explain1("Wrote %d bytes to server", nbytes); + } + else + break; + } + else + break; /* Must be done waiting */ + } + else + break; + } + + ap_pclosesocket(r->pool, sock); + + return OK; +} diff --git a/modules/proxy/proxy_ftp.c b/modules/proxy/proxy_ftp.c new file mode 100644 index 00000000000..47ca9daf9d4 --- /dev/null +++ b/modules/proxy/proxy_ftp.c @@ -0,0 +1,1284 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +/* FTP routines for Apache proxy */ + +#include "mod_proxy.h" +#include "http_main.h" +#include "http_log.h" +#include "http_core.h" + +#define AUTODETECT_PWD + +DEF_Explain + +/* + * Decodes a '%' escaped string, and returns the number of characters + */ +static int decodeenc(char *x) +{ + int i, j, ch; + + if (x[0] == '\0') + return 0; /* special case for no characters */ + for (i = 0, j = 0; x[i] != '\0'; i++, j++) { +/* decode it if not already done */ + ch = x[i]; + if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) { + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + } + x[j] = ch; + } + x[j] = '\0'; + return j; +} + +/* + * checks an encoded ftp string for bad characters, namely, CR, LF or + * non-ascii character + */ +static int ftp_check_string(const char *x) +{ + int i, ch; + + for (i = 0; x[i] != '\0'; i++) { + ch = x[i]; + if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) { + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + } +#ifndef CHARSET_EBCDIC + if (ch == '\015' || ch == '\012' || (ch & 0x80)) +#else /*CHARSET_EBCDIC*/ + if (ch == '\r' || ch == '\n' || (os_toascii[ch] & 0x80)) +#endif /*CHARSET_EBCDIC*/ + return 0; + } + return 1; +} + +/* + * Canonicalise ftp URLs. + */ +int ap_proxy_ftp_canon(request_rec *r, char *url) +{ + char *user, *password, *host, *path, *parms, *strp, sport[7]; + pool *p = r->pool; + const char *err; + int port; + + port = DEFAULT_FTP_PORT; + err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port); + if (err) + return HTTP_BAD_REQUEST; + if (user != NULL && !ftp_check_string(user)) + return HTTP_BAD_REQUEST; + if (password != NULL && !ftp_check_string(password)) + return HTTP_BAD_REQUEST; + +/* now parse path/parameters args, according to rfc1738 */ +/* N.B. if this isn't a true proxy request, then the URL path + * (but not query args) has already been decoded. + * This gives rise to the problem of a ; being decoded into the + * path. + */ + strp = strchr(url, ';'); + if (strp != NULL) { + *(strp++) = '\0'; + parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm, r->proxyreq); + if (parms == NULL) + return HTTP_BAD_REQUEST; + } + else + parms = ""; + + path = ap_proxy_canonenc(p, url, strlen(url), enc_path, r->proxyreq); + if (path == NULL) + return HTTP_BAD_REQUEST; + if (!ftp_check_string(path)) + return HTTP_BAD_REQUEST; + + if (!r->proxyreq && r->args != NULL) { + if (strp != NULL) { + strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, 1); + if (strp == NULL) + return HTTP_BAD_REQUEST; + parms = ap_pstrcat(p, parms, "?", strp, NULL); + } + else { + strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, 1); + if (strp == NULL) + return HTTP_BAD_REQUEST; + path = ap_pstrcat(p, path, "?", strp, NULL); + } + r->args = NULL; + } + +/* now, rebuild URL */ + + if (port != DEFAULT_FTP_PORT) + ap_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; + + r->filename = ap_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "", + (password != NULL) ? ":" : "", + (password != NULL) ? password : "", + (user != NULL) ? "@" : "", host, sport, "/", path, + (parms[0] != '\0') ? ";" : "", parms, NULL); + + return OK; +} + +/* + * Returns the ftp status code; + * or -1 on I/O error, 0 on data error + */ +static int ftp_getrc(BUFF *f) +{ + int len, status; + char linebuff[100], buff[5]; + + len = ap_bgets(linebuff, sizeof linebuff, f); + if (len == -1) + return -1; +/* check format */ + if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) || + !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-')) + status = 0; + else + status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0'; + + if (linebuff[len - 1] != '\n') { + (void)ap_bskiplf(f); + } + +/* skip continuation lines */ + if (linebuff[3] == '-') { + memcpy(buff, linebuff, 3); + buff[3] = ' '; + do { + len = ap_bgets(linebuff, sizeof linebuff, f); + if (len == -1) + return -1; + if (linebuff[len - 1] != '\n') { + (void)ap_bskiplf(f); + } + } while (memcmp(linebuff, buff, 4) != 0); + } + + return status; +} + +/* + * Like ftp_getrc but returns both the ftp status code and + * remembers the response message in the supplied buffer + */ +static int ftp_getrc_msg(BUFF *f, char *msgbuf, int msglen) +{ + int len, status; + char linebuff[100], buff[5]; + char *mb = msgbuf, + *me = &msgbuf[msglen]; + + len = ap_bgets(linebuff, sizeof linebuff, f); + if (len == -1) + return -1; + if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) || + !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-')) + status = 0; + else + status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0'; + + mb = ap_cpystrn(mb, linebuff+4, me - mb); + + if (linebuff[len - 1] != '\n') + (void)ap_bskiplf(f); + + if (linebuff[3] == '-') { + memcpy(buff, linebuff, 3); + buff[3] = ' '; + do { + len = ap_bgets(linebuff, sizeof linebuff, f); + if (len == -1) + return -1; + if (linebuff[len - 1] != '\n') { + (void)ap_bskiplf(f); + } + mb = ap_cpystrn(mb, linebuff+4, me - mb); + } while (memcmp(linebuff, buff, 4) != 0); + } + return status; +} + +static long int send_dir(BUFF *f, request_rec *r, cache_req *c, char *cwd) +{ + char buf[IOBUFSIZE]; + char buf2[IOBUFSIZE]; + char *filename; + int searchidx = 0; + char *searchptr = NULL; + int firstfile = 1; + unsigned long total_bytes_sent = 0; + register int n, o, w; + conn_rec *con = r->connection; + char *dir, *path, *reldir, *site; + + /* Save "scheme://site" prefix without password */ + site = ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD|UNP_OMITPATHINFO); + /* ... and path without query args */ + path = ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITSITEPART|UNP_OMITQUERY); + (void)decodeenc(path); + + /* Copy path, strip (all except the last) trailing slashes */ + path = dir = ap_pstrcat(r->pool, path, "/", NULL); + while ((n = strlen(path)) > 1 && path[n-1] == '/' && path[n-2] == '/') + path[n-1] = '\0'; + + /* print "ftp://host/" */ + n = ap_snprintf(buf, sizeof(buf), DOCTYPE_HTML_3_2 + "<HTML><HEAD><TITLE>%s%s\n" + "\n" + "

Directory of " + "%s/", + site, path, site, path, site); + total_bytes_sent += ap_proxy_bputs2(buf, con->client, c); + + while ((dir = strchr(dir+1, '/')) != NULL) + { + *dir = '\0'; + if ((reldir = strrchr(path+1, '/'))==NULL) + reldir = path+1; + else + ++reldir; + /* print "path/" component */ + ap_snprintf(buf, sizeof(buf), "%s/", path+1, reldir); + total_bytes_sent += ap_proxy_bputs2(buf, con->client, c); + *dir = '/'; + } + /* If the caller has determined the current directory, and it differs */ + /* from what the client requested, then show the real name */ + if (cwd == NULL || strncmp (cwd, path, strlen(cwd)) == 0) { + ap_snprintf(buf, sizeof(buf), "

\n
");
+    } else {
+	ap_snprintf(buf, sizeof(buf), "\n(%s)\n
", cwd);
+    }
+    total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
+
+    while (!con->aborted) {
+	n = ap_bgets(buf, sizeof buf, f);
+	if (n == -1) {		/* input error */
+	    if (c != NULL) {
+		ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
+		    "proxy: error reading from %s", c->url);
+		c = ap_proxy_cache_error(c);
+	    }
+	    break;
+	}
+	if (n == 0)
+	    break;		/* EOF */
+	if (buf[0] == 'l' && (filename=strstr(buf, " -> ")) != NULL) {
+	    char *link_ptr = filename;
+
+	    do {
+		filename--;
+	    } while (filename[0] != ' ');
+	    *(filename++) = '\0';
+	    *(link_ptr++) = '\0';
+	    if ((n = strlen(link_ptr)) > 1 && link_ptr[n - 1] == '\n')
+	      link_ptr[n - 1] = '\0';
+	    ap_snprintf(buf2, sizeof(buf2), "%s %s %s\n", buf, filename, filename, link_ptr);
+	    ap_cpystrn(buf, buf2, sizeof(buf));
+	    n = strlen(buf);
+	}
+	else if (buf[0] == 'd' || buf[0] == '-' || buf[0] == 'l' || ap_isdigit(buf[0])) {
+	    if (ap_isdigit(buf[0])) {	/* handle DOS dir */
+		searchptr = strchr(buf, '<');
+		if (searchptr != NULL)
+		    *searchptr = '[';
+		searchptr = strchr(buf, '>');
+		if (searchptr != NULL)
+		    *searchptr = ']';
+	    }
+
+	    filename = strrchr(buf, ' ');
+	    *(filename++) = 0;
+	    filename[strlen(filename) - 1] = 0;
+
+	    /* handle filenames with spaces in 'em */
+	    if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
+		firstfile = 0;
+		searchidx = filename - buf;
+	    }
+	    else if (searchidx != 0 && buf[searchidx] != 0) {
+		*(--filename) = ' ';
+		buf[searchidx - 1] = 0;
+		filename = &buf[searchidx];
+	    }
+
+	    /* Special handling for '.' and '..' */
+	    if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') {
+		ap_snprintf(buf2, sizeof(buf2), "%s %s\n",
+		    buf, filename, filename);
+	    }
+	    else {
+		ap_snprintf(buf2, sizeof(buf2), "%s %s\n", buf, filename, filename);
+	    }
+	    ap_cpystrn(buf, buf2, sizeof(buf));
+	    n = strlen(buf);
+	}
+
+	o = 0;
+	total_bytes_sent += n;
+
+	if (c != NULL && c->fp && ap_bwrite(c->fp, buf, n) != n) {
+	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
+		"proxy: error writing to %s", c->tempfile);
+	    c = ap_proxy_cache_error(c);
+	}
+
+	while (n && !r->connection->aborted) {
+	    w = ap_bwrite(con->client, &buf[o], n);
+	    if (w <= 0)
+		break;
+	    ap_reset_timeout(r);	/* reset timeout after successfule write */
+	    n -= w;
+	    o += w;
+	}
+    }
+
+    total_bytes_sent += ap_proxy_bputs2("

\n", con->client, c); + total_bytes_sent += ap_proxy_bputs2(ap_psignature("", r), con->client, c); + total_bytes_sent += ap_proxy_bputs2("\n", con->client, c); + + ap_bflush(con->client); + + return total_bytes_sent; +} + +/* Common routine for failed authorization (i.e., missing or wrong password) + * to an ftp service. This causes most browsers to retry the request + * with username and password (which was presumably queried from the user) + * supplied in the Authorization: header. + * Note that we "invent" a realm name which consists of the + * ftp://user@host part of the reqest (sans password -if supplied but invalid-) + */ +static int ftp_unauthorized (request_rec *r, int log_it) +{ + r->proxyreq = 0; + /* Log failed requests if they supplied a password + * (log username/password guessing attempts) + */ + if (log_it) + ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r, + "proxy: missing or failed auth to %s", + ap_unparse_uri_components(r->pool, + &r->parsed_uri, UNP_OMITPATHINFO)); + + ap_table_setn(r->err_headers_out, "WWW-Authenticate", + ap_pstrcat(r->pool, "Basic realm=\"", + ap_unparse_uri_components(r->pool, &r->parsed_uri, + UNP_OMITPASSWORD|UNP_OMITPATHINFO), + "\"", NULL)); + + return HTTP_UNAUTHORIZED; +} + +/* + * Handles direct access of ftp:// URLs + * Original (Non-PASV) version from + * Troy Morrison + * PASV added by Chuck + */ +int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) +{ + char *host, *path, *strp, *parms; + char *cwd = NULL; + char *user = NULL; +/* char *account = NULL; how to supply an account in a URL? */ + const char *password = NULL; + const char *err; + int port, i, j, len, sock, dsock, rc, nocache = 0; + int csd = 0; + struct sockaddr_in server; + struct hostent server_hp; + struct in_addr destaddr; + table *resp_hdrs; + BUFF *f; + BUFF *data = NULL; + pool *p = r->pool; + int one = 1; + const long int zero = 0L; + NET_SIZE_T clen; + struct tbl_do_args tdo; + + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; + struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts; + +/* stuff for PASV mode */ + unsigned int presult, h0, h1, h2, h3, p0, p1; + unsigned int paddr; + unsigned short pport; + struct sockaddr_in data_addr; + int pasvmode = 0; + char pasv[64]; + char *pstr; + +/* stuff for responses */ + char resp[MAX_STRING_LEN]; + char *size = NULL; + +/* we only support GET and HEAD */ + + if (r->method_number != M_GET) + return HTTP_NOT_IMPLEMENTED; + +/* We break the URL into host, port, path-search */ + + host = r->parsed_uri.hostname; + port = (r->parsed_uri.port != 0) + ? r->parsed_uri.port + : ap_default_port_for_request(r); + path = ap_pstrdup(p, r->parsed_uri.path); + path = (path != NULL && path[0] != '\0') ? &path[1] : ""; + + /* The "Authorization:" header must be checked first. + * We allow the user to "override" the URL-coded user [ & password ] + * in the Browsers' User&Password Dialog. + * NOTE that this is only marginally more secure than having the + * password travel in plain as part of the URL, because Basic Auth + * simply uuencodes the plain text password. + * But chances are still smaller that the URL is logged regularly. + */ + if ((password = ap_table_get(r->headers_in, "Authorization")) != NULL + && strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0 + && (password = ap_pbase64decode(r->pool, password))[0] != ':') { + /* Note that this allocation has to be made from r->connection->pool + * because it has the lifetime of the connection. The other allocations + * are temporary and can be tossed away any time. + */ + user = ap_getword_nulls (r->connection->pool, &password, ':'); + r->connection->ap_auth_type = "Basic"; + r->connection->user = r->parsed_uri.user = user; + nocache = 1; /* This resource only accessible with username/password */ + } + else if ((user = r->parsed_uri.user) != NULL) { + user = ap_pstrdup(p, user); + decodeenc(user); + if ((password = r->parsed_uri.password) != NULL) { + char *tmp = ap_pstrdup(p, password); + decodeenc(tmp); + password = tmp; + } + nocache = 1; /* This resource only accessible with username/password */ + } + else { + user = "anonymous"; + password = "apache_proxy@"; + } + +/* check if ProxyBlock directive on this host */ + destaddr.s_addr = ap_inet_addr(host); + for (i = 0; i < conf->noproxies->nelts; i++) { + if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL) + || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*') + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } + + Explain2("FTP: connect to %s:%d", host, port); + + parms = strchr(path, ';'); + if (parms != NULL) + *(parms++) = '\0'; + + memset(&server, 0, sizeof(struct sockaddr_in)); + server.sin_family = AF_INET; + server.sin_port = htons(port); + err = ap_proxy_host2addr(host, &server_hp); + if (err != NULL) + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err); + + sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating socket"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (conf->recv_buffer_size > 0 + && setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (const char *) &conf->recv_buffer_size, sizeof(int)) + == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + } + + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &one, + sizeof(one)) == -1) { +#ifndef _OSD_POSIX /* BS2000 has this option "always on" */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error setting reuseaddr option: setsockopt(SO_REUSEADDR)"); + ap_pclosesocket(p, sock); + return HTTP_INTERNAL_SERVER_ERROR; +#endif /*_OSD_POSIX*/ + } + +#ifdef SINIX_D_RESOLVER_BUG + { + struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list; + + for (; ip_addr->s_addr != 0; ++ip_addr) { + memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr)); + i = ap_proxy_doconnect(sock, &server, r); + if (i == 0) + break; + } + } +#else + j = 0; + while (server_hp.h_addr_list[j] != NULL) { + memcpy(&server.sin_addr, server_hp.h_addr_list[j], + sizeof(struct in_addr)); + i = ap_proxy_doconnect(sock, &server, r); + if (i == 0) + break; + j++; + } +#endif + if (i == -1) { + ap_pclosesocket(p, sock); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool, + "Could not connect to remote machine: ", + strerror(errno), NULL)); + } + + f = ap_bcreate(p, B_RDWR | B_SOCKET); + ap_bpushfd(f, sock, sock); +/* shouldn't we implement telnet control options here? */ + +#ifdef CHARSET_EBCDIC + ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1); +#endif /*CHARSET_EBCDIC*/ + +/* possible results: */ + /* 120 Service ready in nnn minutes. */ + /* 220 Service ready for new user. */ + /* 421 Service not available, closing control connection. */ + ap_hard_timeout("proxy ftp", r); + i = ftp_getrc_msg(f, resp, sizeof resp); + Explain1("FTP: returned status %d", i); + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } +#if 0 + if (i == 120) { + ap_kill_timeout(r); + /* RFC2068 states: + * 14.38 Retry-After + * + * The Retry-After response-header field can be used with a 503 (Service + * Unavailable) response to indicate how long the service is expected to + * be unavailable to the requesting client. The value of this field can + * be either an HTTP-date or an integer number of seconds (in decimal) + * after the time of the response. + * Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds ) + */ + ap_set_header("Retry-After", ap_psprintf(p, "%u", 60*wait_mins); + return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, resp); + } +#endif + if (i != 220) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, resp); + } + + Explain0("FTP: connected."); + + ap_bvputs(f, "USER ", user, CRLF, NULL); + ap_bflush(f); /* capture any errors */ + Explain1("FTP: USER %s", user); + +/* possible results; 230, 331, 332, 421, 500, 501, 530 */ +/* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */ + /* 230 User logged in, proceed. */ + /* 331 User name okay, need password. */ + /* 332 Need account for login. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* (This may include errors such as command line too long.) */ + /* 501 Syntax error in parameters or arguments. */ + /* 530 Not logged in. */ + i = ftp_getrc(f); + Explain1("FTP: returned status %d", i); + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i == 530) { + ap_kill_timeout(r); + return ftp_unauthorized (r, 1); /* log it: user name guessing attempt? */ + } + if (i != 230 && i != 331) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } + + if (i == 331) { /* send password */ + if (password == NULL) { + return ftp_unauthorized (r, 0); + } + ap_bvputs(f, "PASS ", password, CRLF, NULL); + ap_bflush(f); + Explain1("FTP: PASS %s", password); +/* possible results 202, 230, 332, 421, 500, 501, 503, 530 */ + /* 230 User logged in, proceed. */ + /* 332 Need account for login. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 503 Bad sequence of commands. */ + /* 530 Not logged in. */ + i = ftp_getrc(f); + Explain1("FTP: returned status %d", i); + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i == 332) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_UNAUTHORIZED, + "Need account for login"); + } + /* @@@ questionable -- we might as well return a 403 Forbidden here */ + if (i == 530) { + ap_kill_timeout(r); + return ftp_unauthorized (r, 1); /* log it: passwd guessing attempt? */ + } + if (i != 230 && i != 202) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } + } + +/* set the directory (walk directory component by component): + * this is what we must do if we don't know the OS type of the remote + * machine + */ + for (;;) { + strp = strchr(path, '/'); + if (strp == NULL) + break; + *strp = '\0'; + + len = decodeenc(path); + ap_bvputs(f, "CWD ", path, CRLF, NULL); + ap_bflush(f); + Explain1("FTP: CWD %s", path); + *strp = '/'; +/* responses: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + i = ftp_getrc(f); + Explain1("FTP: returned status %d", i); + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i == 550) { + ap_kill_timeout(r); + return HTTP_NOT_FOUND; + } + if (i != 250) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } + + path = strp + 1; + } + + if (parms != NULL && strncmp(parms, "type=", 5) == 0) { + parms += 5; + if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') || + parms[1] != '\0') + parms = ""; + } + else + parms = ""; + + /* changed to make binary transfers the default */ + + if (parms[0] != 'a') { + /* set type to image */ + /* TM - Added \015\012 to the end of TYPE I, otherwise it hangs the + connection */ + ap_bputs("TYPE I" CRLF, f); + ap_bflush(f); + Explain0("FTP: TYPE I"); +/* responses: 200, 421, 500, 501, 504, 530 */ + /* 200 Command okay. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 504 Command not implemented for that parameter. */ + /* 530 Not logged in. */ + i = ftp_getrc(f); + Explain1("FTP: returned status %d", i); + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i != 200 && i != 504) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } +/* Allow not implemented */ + if (i == 504) + parms[0] = '\0'; + } + +/* try to set up PASV data connection first */ + dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (dsock == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating PASV socket"); + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (conf->recv_buffer_size) { + if (setsockopt(dsock, SOL_SOCKET, SO_RCVBUF, + (const char *) &conf->recv_buffer_size, sizeof(int)) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + } + } + + ap_bputs("PASV" CRLF, f); + ap_bflush(f); + Explain0("FTP: PASV command issued"); +/* possible results: 227, 421, 500, 501, 502, 530 */ + /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + i = ap_bgets(pasv, sizeof(pasv), f); + if (i == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, + "PASV: control connection is toast"); + ap_pclosesocket(p, dsock); + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_INTERNAL_SERVER_ERROR; + } + else { + pasv[i - 1] = '\0'; + pstr = strtok(pasv, " "); /* separate result code */ + if (pstr != NULL) { + presult = atoi(pstr); + if (*(pstr + strlen(pstr) + 1) == '=') + pstr += strlen(pstr) + 2; + else + { + pstr = strtok(NULL, "("); /* separate address & port params */ + if (pstr != NULL) + pstr = strtok(NULL, ")"); + } + } + else + presult = atoi(pasv); + + Explain1("FTP: returned status %d", presult); + + if (presult == 227 && pstr != NULL && (sscanf(pstr, + "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) { + /* pardon the parens, but it makes gcc happy */ + paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0; + pport = (p1 << 8) + p0; + Explain5("FTP: contacting host %d.%d.%d.%d:%d", + h3, h2, h1, h0, pport); + data_addr.sin_family = AF_INET; + data_addr.sin_addr.s_addr = htonl(paddr); + data_addr.sin_port = htons(pport); + i = ap_proxy_doconnect(dsock, &data_addr, r); + + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + ap_pstrcat(r->pool, + "Could not connect to remote machine: ", + strerror(errno), NULL)); + } + else { + pasvmode = 1; + } + } + else + ap_pclosesocket(p, dsock); /* and try the regular way */ + } + + if (!pasvmode) { /* set up data connection */ + clen = sizeof(struct sockaddr_in); + if (getsockname(sock, (struct sockaddr *) &server, &clen) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error getting socket address"); + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_INTERNAL_SERVER_ERROR; + } + + dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (dsock == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating socket"); + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (void *) &one, + sizeof(one)) == -1) { +#ifndef _OSD_POSIX /* BS2000 has this option "always on" */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error setting reuseaddr option"); + ap_pclosesocket(p, dsock); + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_INTERNAL_SERVER_ERROR; +#endif /*_OSD_POSIX*/ + } + + if (bind(dsock, (struct sockaddr *) &server, + sizeof(struct sockaddr_in)) == -1) { + char buff[22]; + + ap_snprintf(buff, sizeof(buff), "%s:%d", inet_ntoa(server.sin_addr), server.sin_port); + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error binding to ftp data socket %s", buff); + ap_bclose(f); + ap_pclosesocket(p, dsock); + return HTTP_INTERNAL_SERVER_ERROR; + } + listen(dsock, 2); /* only need a short queue */ + } + +/* set request; "path" holds last path component */ + len = decodeenc(path); + + /* TM - if len == 0 then it must be a directory (you can't RETR nothing) */ + + if (len == 0) { + parms = "d"; + } + else { + ap_bvputs(f, "SIZE ", path, CRLF, NULL); + ap_bflush(f); + Explain1("FTP: SIZE %s", path); + i = ftp_getrc_msg(f, resp, sizeof resp); + Explain2("FTP: returned status %d with response %s", i, resp); + if (i != 500) { /* Size command not recognized */ + if (i == 550) { /* Not a regular file */ + Explain0("FTP: SIZE shows this is a directory"); + parms = "d"; + ap_bvputs(f, "CWD ", path, CRLF, NULL); + ap_bflush(f); + Explain1("FTP: CWD %s", path); + i = ftp_getrc(f); + /* possible results: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + Explain1("FTP: returned status %d", i); + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i == 550) { + ap_kill_timeout(r); + return HTTP_NOT_FOUND; + } + if (i != 250) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } + path = ""; + len = 0; + } + else if (i == 213) { /* Size command ok */ + for (j = 0; j < sizeof resp && ap_isdigit(resp[j]); j++) + ; + resp[j] = '\0'; + if (resp[0] != '\0') + size = ap_pstrdup(p, resp); + } + } + } + +#ifdef AUTODETECT_PWD + ap_bvputs(f, "PWD", CRLF, NULL); + ap_bflush(f); + Explain0("FTP: PWD"); +/* responses: 257, 500, 501, 502, 421, 550 */ + /* 257 "" */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 550 Requested action not taken. */ + i = ftp_getrc_msg(f, resp, sizeof resp); + Explain1("FTP: PWD returned status %d", i); + if (i == -1 || i == 421) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i == 550) { + ap_kill_timeout(r); + return HTTP_NOT_FOUND; + } + if (i == 257) { + const char *dirp = resp; + cwd = ap_getword_conf(r->pool, &dirp); + } +#endif /*AUTODETECT_PWD*/ + + if (parms[0] == 'd') { + if (len != 0) + ap_bvputs(f, "LIST ", path, CRLF, NULL); + else + ap_bputs("LIST -lag" CRLF, f); + Explain1("FTP: LIST %s", (len == 0 ? "" : path)); + } + else { + ap_bvputs(f, "RETR ", path, CRLF, NULL); + Explain1("FTP: RETR %s", path); + } + ap_bflush(f); +/* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550 + NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, 530 */ + /* 110 Restart marker reply. */ + /* 125 Data connection already open; transfer starting. */ + /* 150 File status okay; about to open data connection. */ + /* 226 Closing data connection. */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 425 Can't open data connection. */ + /* 426 Connection closed; transfer aborted. */ + /* 450 Requested file action not taken. */ + /* 451 Requested action aborted. Local error in processing. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + rc = ftp_getrc(f); + Explain1("FTP: returned status %d", rc); + if (rc == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + Explain0("FTP: RETR failed, trying LIST instead"); + parms = "d"; + ap_bvputs(f, "CWD ", path, CRLF, NULL); + ap_bflush(f); + Explain1("FTP: CWD %s", path); + /* possible results: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + rc = ftp_getrc(f); + Explain1("FTP: returned status %d", rc); + if (rc == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + ap_kill_timeout(r); + return HTTP_NOT_FOUND; + } + if (rc != 250) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } + +#ifdef AUTODETECT_PWD + ap_bvputs(f, "PWD", CRLF, NULL); + ap_bflush(f); + Explain0("FTP: PWD"); +/* responses: 257, 500, 501, 502, 421, 550 */ + /* 257 "" */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 550 Requested action not taken. */ + i = ftp_getrc_msg(f, resp, sizeof resp); + Explain1("FTP: PWD returned status %d", i); + if (i == -1 || i == 421) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i == 550) { + ap_kill_timeout(r); + return HTTP_NOT_FOUND; + } + if (i == 257) { + const char *dirp = resp; + cwd = ap_getword_conf(r->pool, &dirp); + } +#endif /*AUTODETECT_PWD*/ + + ap_bputs("LIST -lag" CRLF, f); + ap_bflush(f); + Explain0("FTP: LIST -lag"); + rc = ftp_getrc(f); + Explain1("FTP: returned status %d", rc); + if (rc == -1) + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + ap_kill_timeout(r); + if (rc != 125 && rc != 150 && rc != 226 && rc != 250) + return HTTP_BAD_GATEWAY; + + r->status = HTTP_OK; + r->status_line = "200 OK"; + + resp_hdrs = ap_make_table(p, 2); + c->hdrs = resp_hdrs; + + ap_table_setn(resp_hdrs, "Date", ap_gm_timestr_822(r->pool, r->request_time)); + ap_table_setn(resp_hdrs, "Server", ap_get_server_version()); + + if (parms[0] == 'd') + ap_table_setn(resp_hdrs, "Content-Type", "text/html"); + else { + if (r->content_type != NULL) { + ap_table_setn(resp_hdrs, "Content-Type", r->content_type); + Explain1("FTP: Content-Type set to %s", r->content_type); + } + else { + ap_table_setn(resp_hdrs, "Content-Type", ap_default_type(r)); + } + if (parms[0] != 'a' && size != NULL) { + /* We "trust" the ftp server to really serve (size) bytes... */ + ap_table_set(resp_hdrs, "Content-Length", size); + Explain1("FTP: Content-Length set to %s", size); + } + } + if (r->content_encoding != NULL && r->content_encoding[0] != '\0') { + Explain1("FTP: Content-Encoding set to %s", r->content_encoding); + ap_table_setn(resp_hdrs, "Content-Encoding", r->content_encoding); + } + +/* check if NoCache directive on this host */ + for (i = 0; i < conf->nocaches->nelts; i++) { + if ((ncent[i].name != NULL && strstr(host, ncent[i].name) != NULL) + || destaddr.s_addr == ncent[i].addr.s_addr || ncent[i].name[0] == '*') + nocache = 1; + } + + i = ap_proxy_cache_update(c, resp_hdrs, 0, nocache); + + if (i != DECLINED) { + ap_pclosesocket(p, dsock); + ap_bclose(f); + return i; + } + + if (!pasvmode) { /* wait for connection */ + ap_hard_timeout("proxy ftp data connect", r); + clen = sizeof(struct sockaddr_in); + do + csd = accept(dsock, (struct sockaddr *) &server, &clen); + while (csd == -1 && errno == EINTR); + if (csd == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: failed to accept data connection"); + ap_pclosesocket(p, dsock); + ap_bclose(f); + ap_kill_timeout(r); + if (c != NULL) + c = ap_proxy_cache_error(c); + return HTTP_BAD_GATEWAY; + } + ap_note_cleanups_for_socket(p, csd); + data = ap_bcreate(p, B_RDWR | B_SOCKET); + ap_bpushfd(data, csd, -1); + ap_kill_timeout(r); + } + else { + data = ap_bcreate(p, B_RDWR | B_SOCKET); + ap_bpushfd(data, dsock, dsock); + } + + ap_hard_timeout("proxy receive", r); +/* send response */ +/* write status line */ + if (!r->assbackwards) + ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL); + if (c != NULL && c->fp != NULL + && ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing CRLF to %s", c->tempfile); + c = ap_proxy_cache_error(c); + } + +/* send headers */ + tdo.req = r; + tdo.cache = c; + ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL); + + if (!r->assbackwards) + ap_rputs(CRLF, r); + if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing CRLF to %s", c->tempfile); + c = ap_proxy_cache_error(c); + } + + ap_bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; +/* send body */ + if (!r->header_only) { + if (parms[0] != 'd') { +/* we need to set this for ap_proxy_send_fb()... */ + if (c != NULL) + c->cache_completion = 0; + ap_proxy_send_fb(data, r, c); + } else + send_dir(data, r, c, cwd); + + if (rc == 125 || rc == 150) + rc = ftp_getrc(f); + + /* XXX: we checked for 125||150||226||250 above. This is redundant. */ + if (rc != 226 && rc != 250) + /* XXX: we no longer log an "error writing to c->tempfile" - should we? */ + c = ap_proxy_cache_error(c); + } + else { +/* abort the transfer */ + ap_bputs("ABOR" CRLF, f); + ap_bflush(f); + if (!pasvmode) + ap_bclose(data); + Explain0("FTP: ABOR"); +/* responses: 225, 226, 421, 500, 501, 502 */ + /* 225 Data connection open; no transfer in progress. */ + /* 226 Closing data connection. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + i = ftp_getrc(f); + Explain1("FTP: returned status %d", i); + } + + ap_kill_timeout(r); + ap_proxy_cache_tidy(c); + +/* finish */ + ap_bputs("QUIT" CRLF, f); + ap_bflush(f); + Explain0("FTP: QUIT"); +/* responses: 221, 500 */ + /* 221 Service closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + i = ftp_getrc(f); + Explain1("FTP: QUIT: status %d", i); + + if (pasvmode) + ap_bclose(data); + ap_bclose(f); + + ap_rflush(r); /* flush before garbage collection */ + + ap_proxy_garbage_coll(r); + + return OK; +} diff --git a/modules/proxy/proxy_http.c b/modules/proxy/proxy_http.c new file mode 100644 index 00000000000..42938062d96 --- /dev/null +++ b/modules/proxy/proxy_http.c @@ -0,0 +1,543 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* HTTP routines for Apache proxy */ + +#include "mod_proxy.h" +#include "http_log.h" +#include "http_main.h" +#include "http_core.h" +#include "util_date.h" + +/* + * Canonicalise http-like URLs. + * scheme is the scheme for the URL + * url is the URL starting with the first '/' + * def_port is the default port for this scheme. + */ +int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, int def_port) +{ + char *host, *path, *search, sport[7]; + const char *err; + int port; + +/* do syntatic check. + * We break the URL into host, port, path, search + */ + port = def_port; + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) + return HTTP_BAD_REQUEST; + +/* now parse path/search args, according to rfc1738 */ +/* N.B. if this isn't a true proxy request, then the URL _path_ + * has already been decoded. True proxy requests have r->uri + * == r->unparsed_uri, and no others have that property. + */ + if (r->uri == r->unparsed_uri) { + search = strchr(url, '?'); + if (search != NULL) + *(search++) = '\0'; + } + else + search = r->args; + +/* process path */ + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq); + if (path == NULL) + return HTTP_BAD_REQUEST; + + if (port != def_port) + ap_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; + + r->filename = ap_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/", + path, (search) ? "?" : "", (search) ? search : "", NULL); + return OK; +} + +static const char *proxy_location_reverse_map(request_rec *r, const char *url) +{ + void *sconf; + proxy_server_conf *conf; + struct proxy_alias *ent; + int i, l1, l2; + char *u; + + sconf = r->server->module_config; + conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module); + l1 = strlen(url); + ent = (struct proxy_alias *)conf->raliases->elts; + for (i = 0; i < conf->raliases->nelts; i++) { + l2 = strlen(ent[i].real); + if (l1 >= l2 && strncmp(ent[i].real, url, l2) == 0) { + u = ap_pstrcat(r->pool, ent[i].fake, &url[l2], NULL); + return ap_construct_url(r->pool, u, r); + } + } + return url; +} + +/* Clear all connection-based headers from the incoming headers table */ +static void clear_connection(pool *p, table *headers) +{ + const char *name; + char *next = ap_pstrdup(p, ap_table_get(headers, "Connection")); + + ap_table_unset(headers, "Proxy-Connection"); + if (!next) + return; + + while (*next) { + name = next; + while (*next && !ap_isspace(*next) && (*next != ',')) + ++next; + while (*next && (ap_isspace(*next) || (*next == ','))) { + *next = '\0'; + ++next; + } + ap_table_unset(headers, name); + } + ap_table_unset(headers, "Connection"); +} + +/* + * This handles http:// URLs, and other URLs using a remote proxy over http + * If proxyhost is NULL, then contact the server directly, otherwise + * go via the proxy. + * Note that if a proxy is used, then URLs other than http: can be accessed, + * also, if we have trouble which is clearly specific to the proxy, then + * we return DECLINED so that we can try another proxy. (Or the direct + * route.) + */ +int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, + const char *proxyhost, int proxyport) +{ + const char *strp; + char *strp2; + const char *err, *desthost; + int i, j, sock, len, backasswards; + array_header *reqhdrs_arr; + table *resp_hdrs; + table_entry *reqhdrs; + struct sockaddr_in server; + struct in_addr destaddr; + struct hostent server_hp; + BUFF *f; + char buffer[HUGE_STRING_LEN]; + char portstr[32]; + pool *p = r->pool; + const long int zero = 0L; + int destport = 0; + char *destportstr = NULL; + const char *urlptr = NULL; + const char *datestr; + struct tbl_do_args tdo; + + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; + struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts; + int nocache = 0; + + memset(&server, '\0', sizeof(server)); + server.sin_family = AF_INET; + +/* We break the URL into host, port, path-search */ + + urlptr = strstr(url, "://"); + if (urlptr == NULL) + return HTTP_BAD_REQUEST; + urlptr += 3; + destport = DEFAULT_HTTP_PORT; + strp = strchr(urlptr, '/'); + if (strp == NULL) { + desthost = ap_pstrdup(p, urlptr); + urlptr = "/"; + } + else { + char *q = ap_palloc(p, strp - urlptr + 1); + memcpy(q, urlptr, strp - urlptr); + q[strp - urlptr] = '\0'; + urlptr = strp; + desthost = q; + } + + strp2 = strchr(desthost, ':'); + if (strp2 != NULL) { + *(strp2++) = '\0'; + if (ap_isdigit(*strp2)) { + destport = atoi(strp2); + destportstr = strp2; + } + } + +/* check if ProxyBlock directive on this host */ + destaddr.s_addr = ap_inet_addr(desthost); + for (i = 0; i < conf->noproxies->nelts; i++) { + if ((npent[i].name != NULL && strstr(desthost, npent[i].name) != NULL) + || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*') + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } + + if (proxyhost != NULL) { + server.sin_port = htons(proxyport); + err = ap_proxy_host2addr(proxyhost, &server_hp); + if (err != NULL) + return DECLINED; /* try another */ + } + else { + server.sin_port = htons(destport); + err = ap_proxy_host2addr(desthost, &server_hp); + if (err != NULL) + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err); + } + + sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating socket"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (conf->recv_buffer_size) { + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (const char *) &conf->recv_buffer_size, sizeof(int)) + == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + } + } + +#ifdef SINIX_D_RESOLVER_BUG + { + struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list; + + for (; ip_addr->s_addr != 0; ++ip_addr) { + memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr)); + i = ap_proxy_doconnect(sock, &server, r); + if (i == 0) + break; + } + } +#else + j = 0; + while (server_hp.h_addr_list[j] != NULL) { + memcpy(&server.sin_addr, server_hp.h_addr_list[j], + sizeof(struct in_addr)); + i = ap_proxy_doconnect(sock, &server, r); + if (i == 0) + break; + j++; + } +#endif + if (i == -1) { + if (proxyhost != NULL) + return DECLINED; /* try again another way */ + else + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool, + "Could not connect to remote machine: ", + strerror(errno), NULL)); + } + + clear_connection(r->pool, r->headers_in); /* Strip connection-based headers */ + + f = ap_bcreate(p, B_RDWR | B_SOCKET); + ap_bpushfd(f, sock, sock); + + ap_hard_timeout("proxy send", r); + ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.0" CRLF, + NULL); + if (destportstr != NULL && destport != DEFAULT_HTTP_PORT) + ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL); + else + ap_bvputs(f, "Host: ", desthost, CRLF, NULL); + + if (conf->viaopt == via_block) { + /* Block all outgoing Via: headers */ + ap_table_unset(r->headers_in, "Via"); + } else if (conf->viaopt != via_off) { + /* Create a "Via:" request header entry and merge it */ + i = ap_get_server_port(r); + if (ap_is_default_port(i,r)) { + strcpy(portstr,""); + } else { + ap_snprintf(portstr, sizeof portstr, ":%d", i); + } + /* Generate outgoing Via: header with/without server comment: */ + ap_table_mergen(r->headers_in, "Via", + (conf->viaopt == via_full) + ? ap_psprintf(p, "%d.%d %s%s (%s)", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + ap_get_server_name(r), portstr, + SERVER_BASEVERSION) + : ap_psprintf(p, "%d.%d %s%s", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + ap_get_server_name(r), portstr) + ); + } + + reqhdrs_arr = ap_table_elts(r->headers_in); + reqhdrs = (table_entry *) reqhdrs_arr->elts; + for (i = 0; i < reqhdrs_arr->nelts; i++) { + if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL + /* Clear out headers not to send */ + || !strcasecmp(reqhdrs[i].key, "Host") /* Already sent */ + /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be + * suppressed if THIS server requested the authentication, + * not when a frontend proxy requested it! + */ + || !strcasecmp(reqhdrs[i].key, "Proxy-Authorization")) + continue; + ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL); + } + + ap_bputs(CRLF, f); +/* send the request data, if any. */ + + if (ap_should_client_block(r)) { + while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) + ap_bwrite(f, buffer, i); + } + ap_bflush(f); + ap_kill_timeout(r); + + ap_hard_timeout("proxy receive", r); + + len = ap_bgets(buffer, sizeof buffer - 1, f); + if (len == -1) { + ap_bclose(f); + ap_kill_timeout(r); + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "ap_bgets() - proxy receive - Error reading from remote server %s (length %d)", + proxyhost ? proxyhost : desthost, len); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } else if (len == 0) { + ap_bclose(f); + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Document contains no data"); + } + +/* Is it an HTTP/1 response? This is buggy if we ever see an HTTP/1.10 */ + if (ap_checkmask(buffer, "HTTP/#.# ###*")) { + int major, minor; + if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) { + major = 1; + minor = 0; + } + +/* If not an HTTP/1 message or if the status line was > 8192 bytes */ + if (buffer[5] != '1' || buffer[len - 1] != '\n') { + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } + backasswards = 0; + buffer[--len] = '\0'; + + buffer[12] = '\0'; + r->status = atoi(&buffer[9]); + buffer[12] = ' '; + r->status_line = ap_pstrdup(p, &buffer[9]); + +/* read the headers. */ +/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */ +/* Also, take care with headers with multiple occurences. */ + + resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f); + if (resp_hdrs == NULL) { + ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server, + "proxy: Bad HTTP/%d.%d header returned by %s (%s)", + major, minor, r->uri, r->method); + resp_hdrs = ap_make_table(p, 20); + nocache = 1; /* do not cache this broken file */ + } + + if (conf->viaopt != via_off && conf->viaopt != via_block) { + /* Create a "Via:" response header entry and merge it */ + i = ap_get_server_port(r); + if (ap_is_default_port(i,r)) { + strcpy(portstr,""); + } else { + ap_snprintf(portstr, sizeof portstr, ":%d", i); + } + ap_table_mergen((table *)resp_hdrs, "Via", + (conf->viaopt == via_full) + ? ap_psprintf(p, "%d.%d %s%s (%s)", + major, minor, + ap_get_server_name(r), portstr, + SERVER_BASEVERSION) + : ap_psprintf(p, "%d.%d %s%s", + major, minor, + ap_get_server_name(r), portstr) + ); + } + + clear_connection(p, resp_hdrs); /* Strip Connection hdrs */ + } + else { +/* an http/0.9 response */ + backasswards = 1; + r->status = 200; + r->status_line = "200 OK"; + +/* no headers */ + resp_hdrs = ap_make_table(p, 20); + } + + c->hdrs = resp_hdrs; + + ap_kill_timeout(r); + +/* + * HTTP/1.0 requires us to accept 3 types of dates, but only generate + * one type + */ + if ((datestr = ap_table_get(resp_hdrs, "Date")) != NULL) + ap_table_set(resp_hdrs, "Date", ap_proxy_date_canon(p, datestr)); + if ((datestr = ap_table_get(resp_hdrs, "Last-Modified")) != NULL) + ap_table_set(resp_hdrs, "Last-Modified", ap_proxy_date_canon(p, datestr)); + if ((datestr = ap_table_get(resp_hdrs, "Expires")) != NULL) + ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, datestr)); + + if ((datestr = ap_table_get(resp_hdrs, "Location")) != NULL) + ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, datestr)); + if ((datestr = ap_table_get(resp_hdrs, "URI")) != NULL) + ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, datestr)); + +/* check if NoCache directive on this host */ + for (i = 0; i < conf->nocaches->nelts; i++) { + if ((ncent[i].name != NULL && strstr(desthost, ncent[i].name) != NULL) + || destaddr.s_addr == ncent[i].addr.s_addr || ncent[i].name[0] == '*') + nocache = 1; + } + + i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache); + if (i != DECLINED) { + ap_bclose(f); + return i; + } + + ap_hard_timeout("proxy receive", r); + +/* write status line */ + if (!r->assbackwards) + ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL); + if (c != NULL && c->fp != NULL && + ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing status line to %s", c->tempfile); + c = ap_proxy_cache_error(c); + } + +/* send headers */ + tdo.req = r; + tdo.cache = c; + ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL); + + if (!r->assbackwards) + ap_rputs(CRLF, r); + if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing CRLF to %s", c->tempfile); + c = ap_proxy_cache_error(c); + } + + ap_bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; +/* Is it an HTTP/0.9 respose? If so, send the extra data */ + if (backasswards) { + ap_bwrite(r->connection->client, buffer, len); + if (c != NULL && c->fp != NULL && ap_bwrite(c->fp, buffer, len) != len) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing extra data to %s", c->tempfile); + c = ap_proxy_cache_error(c); + } + } + ap_kill_timeout(r); + +#ifdef CHARSET_EBCDIC + /* What we read/write after the header should not be modified + * (i.e., the cache copy is ASCII, not EBCDIC, even for text/html) + */ + ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); + ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); +#endif + +/* send body */ +/* if header only, then cache will be NULL */ +/* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */ + if (!r->header_only) { +/* we need to set this for ap_proxy_send_fb()... */ + c->cache_completion = conf->cache.cache_completion; + ap_proxy_send_fb(f, r, c); + } + + ap_proxy_cache_tidy(c); + + ap_bclose(f); + + ap_proxy_garbage_coll(r); + return OK; +} diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c new file mode 100644 index 00000000000..c1258f14995 --- /dev/null +++ b/modules/proxy/proxy_util.c @@ -0,0 +1,1288 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* Utility routines for Apache proxy */ +#include "mod_proxy.h" +#include "http_main.h" +#include "ap_md5.h" +#include "multithread.h" +#include "http_log.h" +#include "util_uri.h" +#include "util_date.h" /* get ap_checkmask() decl. */ + +static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r); +static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r); +static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r); +static int proxy_match_word(struct dirconn_entry *This, request_rec *r); + +/* already called in the knowledge that the characters are hex digits */ +int ap_proxy_hex2c(const char *x) +{ + int i, ch; + +#ifndef CHARSET_EBCDIC + ch = x[0]; + if (ap_isdigit(ch)) + i = ch - '0'; + else if (ap_isupper(ch)) + i = ch - ('A' - 10); + else + i = ch - ('a' - 10); + i <<= 4; + + ch = x[1]; + if (ap_isdigit(ch)) + i += ch - '0'; + else if (ap_isupper(ch)) + i += ch - ('A' - 10); + else + i += ch - ('a' - 10); + return i; +#else /*CHARSET_EBCDIC*/ + return (1 == sscanf(x, "%2x", &i)) ? os_toebcdic[i&0xFF] : 0; +#endif /*CHARSET_EBCDIC*/ +} + +void ap_proxy_c2hex(int ch, char *x) +{ +#ifndef CHARSET_EBCDIC + int i; + + x[0] = '%'; + i = (ch & 0xF0) >> 4; + if (i >= 10) + x[1] = ('A' - 10) + i; + else + x[1] = '0' + i; + + i = ch & 0x0F; + if (i >= 10) + x[2] = ('A' - 10) + i; + else + x[2] = '0' + i; +#else /*CHARSET_EBCDIC*/ + static const char ntoa[] = { "0123456789ABCDEF" }; + ch &= 0xFF; + x[0] = '%'; + x[1] = ntoa[(os_toascii[ch]>>4)&0x0F]; + x[2] = ntoa[os_toascii[ch]&0x0F]; + x[3] = '\0'; +#endif /*CHARSET_EBCDIC*/ +} + +/* + * canonicalise a URL-encoded string + */ + +/* + * Convert a URL-encoded string to canonical form. + * It decodes characters which need not be encoded, + * and encodes those which must be encoded, and does not touch + * those which must not be touched. + */ +char * + ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t, int isenc) +{ + int i, j, ch; + char *y; + const char *allowed; /* characters which should not be encoded */ + const char *reserved; /* characters which much not be en/de-coded */ + +/* N.B. in addition to :@&=, this allows ';' in an http path + * and '?' in an ftp path -- this may be revised + * + * Also, it makes a '+' character in a search string reserved, as + * it may be form-encoded. (Although RFC 1738 doesn't allow this - + * it only permits ; / ? : @ = & as reserved chars.) + */ + if (t == enc_path) + allowed = "$-_.+!*'(),;:@&="; + else if (t == enc_search) + allowed = "$-_.!*'(),;:@&="; + else if (t == enc_user) + allowed = "$-_.+!*'(),;@&="; + else if (t == enc_fpath) + allowed = "$-_.+!*'(),?:@&="; + else /* if (t == enc_parm) */ + allowed = "$-_.+!*'(),?/:@&="; + + if (t == enc_path) + reserved = "/"; + else if (t == enc_search) + reserved = "+"; + else + reserved = ""; + + y = ap_palloc(p, 3 * len + 1); + + for (i = 0, j = 0; i < len; i++, j++) { +/* always handle '/' first */ + ch = x[i]; + if (strchr(reserved, ch)) { + y[j] = ch; + continue; + } +/* decode it if not already done */ + if (isenc && ch == '%') { + if (!ap_isxdigit(x[i + 1]) || !ap_isxdigit(x[i + 2])) + return NULL; + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */ + ap_proxy_c2hex(ch, &y[j]); + j += 2; + continue; + } + } +/* recode it, if necessary */ + if (!ap_isalnum(ch) && !strchr(allowed, ch)) { + ap_proxy_c2hex(ch, &y[j]); + j += 2; + } + else + y[j] = ch; + } + y[j] = '\0'; + return y; +} + +/* + * Parses network-location. + * urlp on input the URL; on output the path, after the leading / + * user NULL if no user/password permitted + * password holder for password + * host holder for host + * port port number; only set if one is supplied. + * + * Returns an error string. + */ +char * + ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp, + char **passwordp, char **hostp, int *port) +{ + int i; + char *strp, *host, *url = *urlp; + char *user = NULL, *password = NULL; + + if (url[0] != '/' || url[1] != '/') + return "Malformed URL"; + host = url + 2; + url = strchr(host, '/'); + if (url == NULL) + url = ""; + else + *(url++) = '\0'; /* skip seperating '/' */ + + /* find _last_ '@' since it might occur in user/password part */ + strp = strrchr(host, '@'); + + if (strp != NULL) { + *strp = '\0'; + user = host; + host = strp + 1; + +/* find password */ + strp = strchr(user, ':'); + if (strp != NULL) { + *strp = '\0'; + password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1); + if (password == NULL) + return "Bad %-escape in URL (password)"; + } + + user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1); + if (user == NULL) + return "Bad %-escape in URL (username)"; + } + if (userp != NULL) { + *userp = user; + } + if (passwordp != NULL) { + *passwordp = password; + } + + strp = strrchr(host, ':'); + if (strp != NULL) { + *(strp++) = '\0'; + + for (i = 0; strp[i] != '\0'; i++) + if (!ap_isdigit(strp[i])) + break; + + /* if (i == 0) the no port was given; keep default */ + if (strp[i] != '\0') { + return "Bad port number in URL"; + } else if (i > 0) { + *port = atoi(strp); + if (*port > 65535) + return "Port number in URL > 65535"; + } + } + ap_str_tolower(host); /* DNS names are case-insensitive */ + if (*host == '\0') + return "Missing host in URL"; +/* check hostname syntax */ + for (i = 0; host[i] != '\0'; i++) + if (!ap_isdigit(host[i]) && host[i] != '.') + break; + /* must be an IP address */ +#ifdef WIN32 + if (host[i] == '\0' && (inet_addr(host) == -1)) +#else + if (host[i] == '\0' && (ap_inet_addr(host) == -1 || inet_network(host) == -1)) +#endif + { + return "Bad IP address in URL"; + } + +/* if (strchr(host,'.') == NULL && domain != NULL) + host = pstrcat(p, host, domain, NULL); + */ + *urlp = url; + *hostp = host; + + return NULL; +} + +static const char * const lwday[7] = +{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; + +/* + * If the date is a valid RFC 850 date or asctime() date, then it + * is converted to the RFC 1123 format, otherwise it is not modified. + * This routine is not very fast at doing conversions, as it uses + * sscanf and sprintf. However, if the date is already correctly + * formatted, then it exits very quickly. + */ +const char * + ap_proxy_date_canon(pool *p, const char *x) +{ + int wk, mday, year, hour, min, sec, mon; + char *q, month[4], zone[4], week[4]; + + q = strchr(x, ','); + /* check for RFC 850 date */ + if (q != NULL && q - x > 3 && q[1] == ' ') { + *q = '\0'; + for (wk = 0; wk < 7; wk++) + if (strcmp(x, lwday[wk]) == 0) + break; + *q = ','; + if (wk == 7) + return x; /* not a valid date */ + if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' || + q[17] != ':' || strcmp(&q[20], " GMT") != 0) + return x; + if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year, + &hour, &min, &sec, zone) != 7) + return x; + if (year < 70) + year += 2000; + else + year += 1900; + } + else { +/* check for acstime() date */ + if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' || + x[16] != ':' || x[19] != ' ' || x[24] != '\0') + return x; + if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour, + &min, &sec, &year) != 7) + return x; + for (wk = 0; wk < 7; wk++) + if (strcmp(week, ap_day_snames[wk]) == 0) + break; + if (wk == 7) + return x; + } + +/* check date */ + for (mon = 0; mon < 12; mon++) + if (strcmp(month, ap_month_snames[mon]) == 0) + break; + if (mon == 12) + return x; + + q = ap_palloc(p, 30); + ap_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[wk], mday, + ap_month_snames[mon], year, hour, min, sec); + return q; +} + + +/* NOTE: This routine is taken from http_protocol::getline() + * because the old code found in the proxy module was too + * difficult to understand and maintain. + */ +/* Get a line of protocol input, including any continuation lines + * caused by MIME folding (or broken clients) if fold != 0, and place it + * in the buffer s, of size n bytes, without the ending newline. + * + * Returns -1 on error, or the length of s. + * + * Note: Because bgets uses 1 char for newline and 1 char for NUL, + * the most we can get is (n - 2) actual characters if it + * was ended by a newline, or (n - 1) characters if the line + * length exceeded (n - 1). So, if the result == (n - 1), + * then the actual input line exceeded the buffer length, + * and it would be a good idea for the caller to puke 400 or 414. + */ +static int proxy_getline(char *s, int n, BUFF *in, int fold) +{ + char *pos, next; + int retval; + int total = 0; + + pos = s; + + do { + retval = ap_bgets(pos, n, in); /* retval == -1 if error, 0 if EOF */ + + if (retval <= 0) + return ((retval < 0) && (total == 0)) ? -1 : total; + + /* retval is the number of characters read, not including NUL */ + + n -= retval; /* Keep track of how much of s is full */ + pos += (retval - 1); /* and where s ends */ + total += retval; /* and how long s has become */ + + if (*pos == '\n') { /* Did we get a full line of input? */ + *pos = '\0'; + --total; + ++n; + } + else + return total; /* if not, input line exceeded buffer size */ + + /* Continue appending if line folding is desired and + * the last line was not empty and we have room in the buffer and + * the next line begins with a continuation character. + */ + } while (fold && (retval != 1) && (n > 1) + && (ap_blookc(&next, in) == 1) + && ((next == ' ') || (next == '\t'))); + + return total; +} + + +/* + * Reads headers from a buffer and returns an array of headers. + * Returns NULL on file error + * This routine tries to deal with too long lines and continuation lines. + * @@@: XXX: FIXME: currently the headers are passed thru un-merged. + * Is that okay, or should they be collapsed where possible? + */ +table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f) +{ + table *resp_hdrs; + int len; + char *value, *end; + char field[MAX_STRING_LEN]; + + resp_hdrs = ap_make_table(r->pool, 20); + + /* + * Read header lines until we get the empty separator line, a read error, + * the connection closes (EOF), or we timeout. + */ + while ((len = proxy_getline(buffer, size, f, 1)) > 0) { + + if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ + + /* Buggy MS IIS servers sometimes return invalid headers + * (an extra "HTTP/1.0 200, OK" line sprinkled in between + * the usual MIME headers). Try to deal with it in a sensible + * way, but log the fact. + * XXX: The mask check is buggy if we ever see an HTTP/1.10 */ + + if (!ap_checkmask(buffer, "HTTP/#.# ###*")) { + /* Nope, it wasn't even an extra HTTP header. Give up. */ + return NULL; + } + + ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server, + "proxy: Ignoring duplicate HTTP header " + "returned by %s (%s)", r->uri, r->method); + continue; + } + + *value = '\0'; + ++value; + /* XXX: RFC2068 defines only SP and HT as whitespace, this test is + * wrong... and so are many others probably. + */ + while (ap_isspace(*value)) + ++value; /* Skip to start of value */ + + /* should strip trailing whitespace as well */ + for (end = &value[strlen(value)-1]; end > value && ap_isspace(*end); --end) + *end = '\0'; + + ap_table_add(resp_hdrs, buffer, value); + + /* the header was too long; at the least we should skip extra data */ + if (len >= size - 1) { + while ((len = proxy_getline(field, MAX_STRING_LEN, f, 1)) + >= MAX_STRING_LEN - 1) { + /* soak up the extra data */ + } + if (len == 0) /* time to exit the larger loop as well */ + break; + } + } + return resp_hdrs; +} + +long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c) +{ + int ok; + char buf[IOBUFSIZE]; + long total_bytes_rcvd; + register int n, o, w; + conn_rec *con = r->connection; + int alternate_timeouts = 1; /* 1 if we alternate between soft & hard timeouts */ + + total_bytes_rcvd = 0; + if (c != NULL) + c->written = 0; + +#ifdef CHARSET_EBCDIC + /* The cache copy is ASCII, not EBCDIC, even for text/html) */ + ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); + if (c != NULL && c->fp != NULL) + ap_bsetflag(c->fp, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); + ap_bsetflag(con->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); +#endif + + /* Since we are reading from one buffer and writing to another, + * it is unsafe to do a soft_timeout here, at least until the proxy + * has its own timeout handler which can set both buffers to EOUT. + */ + + ap_kill_timeout(r); + +#ifdef WIN32 + /* works fine under win32, so leave it */ + ap_hard_timeout("proxy send body", r); + alternate_timeouts = 0; +#else + /* CHECKME! Since hard_timeout won't work in unix on sends with partial + * cache completion, we have to alternate between hard_timeout + * for reads, and soft_timeout for send. This is because we need + * to get a return from ap_bwrite to be able to continue caching. + * BUT, if we *can't* continue anyway, just use hard_timeout. + * (Also, if no cache file is written, use hard timeouts) + */ + + if (c == NULL || c->len <= 0 || c->cache_completion == 1.0) { + ap_hard_timeout("proxy send body", r); + alternate_timeouts = 0; + } +#endif + + /* Loop and ap_bread() while we can successfully read and write, + * or (after the client aborted) while we can successfully + * read and finish the configured cache_completion. + */ + for (ok = 1; ok; ) { + if (alternate_timeouts) + ap_hard_timeout("proxy recv body from upstream server", r); + + /* Read block from server */ + n = ap_bread(f, buf, IOBUFSIZE); + + if (alternate_timeouts) + ap_kill_timeout(r); + else + ap_reset_timeout(r); + + if (n == -1) { /* input error */ + if (c != NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error reading from %s", c->url); + c = ap_proxy_cache_error(c); + } + break; + } + if (n == 0) + break; /* EOF */ + o = 0; + total_bytes_rcvd += n; + + /* Write to cache first. */ + /*@@@ XXX FIXME: Assuming that writing the cache file won't time out?!!? */ + if (c != NULL && c->fp != NULL) { + if (ap_bwrite(c->fp, &buf[0], n) != n) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing to %s", c->tempfile); + c = ap_proxy_cache_error(c); + } else { + c->written += n; + } + } + + /* Write the block to the client, detect aborted transfers */ + while (!con->aborted && n > 0) { + if (alternate_timeouts) + ap_soft_timeout("proxy send body", r); + + w = ap_bwrite(con->client, &buf[o], n); + + if (alternate_timeouts) + ap_kill_timeout(r); + else + ap_reset_timeout(r); + + if (w <= 0) { + if (c != NULL && c->fp != NULL) { + /* when a send failure occurs, we need to decide + * whether to continue loading and caching the + * document, or to abort the whole thing + */ + ok = (c->len > 0) && + (c->cache_completion > 0) && + (c->len * c->cache_completion < total_bytes_rcvd); + + if (! ok) { + ap_pclosef(c->req->pool, c->fp->fd); + c->fp = NULL; + unlink(c->tempfile); + c = NULL; + } + } + con->aborted = 1; + break; + } + n -= w; + o += w; + } /* while client alive and more data to send */ + } /* loop and ap_bread while "ok" */ + + if (!con->aborted) + ap_bflush(con->client); + + ap_kill_timeout(r); + return total_bytes_rcvd; +} + +/* + * Sends response line and headers. Uses the client fd and the + * headers_out array from the passed request_rec to talk to the client + * and to properly set the headers it sends for things such as logging. + * + * A timeout should be set before calling this routine. + */ +void ap_proxy_send_headers(request_rec *r, const char *respline, table *t) +{ + int i; + BUFF *fp = r->connection->client; + table_entry *elts = (table_entry *) ap_table_elts(t)->elts; + + ap_bvputs(fp, respline, CRLF, NULL); + + for (i = 0; i < ap_table_elts(t)->nelts; ++i) { + if (elts[i].key != NULL) { + ap_bvputs(fp, elts[i].key, ": ", elts[i].val, CRLF, NULL); + ap_table_addn(r->headers_out, elts[i].key, elts[i].val); + } + } + + ap_bputs(CRLF, fp); +} + + +/* + * list is a comma-separated list of case-insensitive tokens, with + * optional whitespace around the tokens. + * The return returns 1 if the token val is found in the list, or 0 + * otherwise. + */ +int ap_proxy_liststr(const char *list, const char *val) +{ + int len, i; + const char *p; + + len = strlen(val); + + while (list != NULL) { + p = strchr(list, ','); + if (p != NULL) { + i = p - list; + do + p++; + while (ap_isspace(*p)); + } + else + i = strlen(list); + + while (i > 0 && ap_isspace(list[i - 1])) + i--; + if (i == len && strncasecmp(list, val, len) == 0) + return 1; + list = p; + } + return 0; +} + +#ifdef CASE_BLIND_FILESYSTEM + +/* + * On some platforms, the file system is NOT case sensitive. So, a == A + * need to map to smaller set of characters + */ +void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength) +{ + AP_MD5_CTX context; + unsigned char digest[16]; + char tmp[26]; + int i, k, d; + unsigned int x; + static const char enc_table[32] = "abcdefghijklmnopqrstuvwxyz012345"; + + ap_MD5Init(&context); + ap_MD5Update(&context, (const unsigned char *) it, strlen(it)); + ap_MD5Final(digest, &context); + +/* encode 128 bits as 26 characters, using a modified uuencoding */ +/* the encoding is 5 bytes -> 8 characters + * i.e. 128 bits is 3 x 5 bytes + 1 byte -> 3 * 8 characters + 2 characters + */ + for (i = 0, k = 0; i < 15; i += 5) { + x = (digest[i] << 24) | (digest[i + 1] << 16) | (digest[i + 2] << 8) | digest[i + 3]; + tmp[k++] = enc_table[x >> 27]; + tmp[k++] = enc_table[(x >> 22) & 0x1f]; + tmp[k++] = enc_table[(x >> 17) & 0x1f]; + tmp[k++] = enc_table[(x >> 12) & 0x1f]; + tmp[k++] = enc_table[(x >> 7) & 0x1f]; + tmp[k++] = enc_table[(x >> 2) & 0x1f]; + x = ((x & 0x3) << 8) | digest[i + 4]; + tmp[k++] = enc_table[x >> 5]; + tmp[k++] = enc_table[x & 0x1f]; + } +/* one byte left */ + x = digest[15]; + tmp[k++] = enc_table[x >> 3]; /* use up 5 bits */ + tmp[k++] = enc_table[x & 0x7]; + /* now split into directory levels */ + + for (i = k = d = 0; d < ndepth; ++d) { + memcpy(&val[i], &tmp[k], nlength); + k += nlength; + val[i + nlength] = '/'; + i += nlength + 1; + } + memcpy(&val[i], &tmp[k], 26 - k); + val[i + 26 - k] = '\0'; +} + +#else + +void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength) +{ + AP_MD5_CTX context; + unsigned char digest[16]; + char tmp[22]; + int i, k, d; + unsigned int x; +#if defined(AIX) && defined(__ps2__) + /* Believe it or not, AIX 1.x does not allow you to name a file '@', + * so hack around it in the encoding. */ + static const char enc_table[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_%"; +#else + static const char enc_table[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@"; +#endif + + ap_MD5Init(&context); + ap_MD5Update(&context, (const unsigned char *) it, strlen(it)); + ap_MD5Final(digest, &context); + +/* encode 128 bits as 22 characters, using a modified uuencoding */ +/* the encoding is 3 bytes -> 4 characters + * i.e. 128 bits is 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters + */ + for (i = 0, k = 0; i < 15; i += 3) { + x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2]; + tmp[k++] = enc_table[x >> 18]; + tmp[k++] = enc_table[(x >> 12) & 0x3f]; + tmp[k++] = enc_table[(x >> 6) & 0x3f]; + tmp[k++] = enc_table[x & 0x3f]; + } +/* one byte left */ + x = digest[15]; + tmp[k++] = enc_table[x >> 2]; /* use up 6 bits */ + tmp[k++] = enc_table[(x << 4) & 0x3f]; + /* now split into directory levels */ + + for (i = k = d = 0; d < ndepth; ++d) { + memcpy(&val[i], &tmp[k], nlength); + k += nlength; + val[i + nlength] = '/'; + i += nlength + 1; + } + memcpy(&val[i], &tmp[k], 22 - k); + val[i + 22 - k] = '\0'; +} + +#endif /* CASE_BLIND_FILESYSTEM */ + +/* + * Converts 8 hex digits to a time integer + */ +int ap_proxy_hex2sec(const char *x) +{ + int i, ch; + unsigned int j; + + for (i = 0, j = 0; i < 8; i++) { + ch = x[i]; + j <<= 4; + if (ap_isdigit(ch)) + j |= ch - '0'; + else if (ap_isupper(ch)) + j |= ch - ('A' - 10); + else + j |= ch - ('a' - 10); + } + if (j == 0xffffffff) + return -1; /* so that it works with 8-byte ints */ + else + return j; +} + +/* + * Converts a time integer to 8 hex digits + */ +void ap_proxy_sec2hex(int t, char *y) +{ + int i, ch; + unsigned int j = t; + + for (i = 7; i >= 0; i--) { + ch = j & 0xF; + j >>= 4; + if (ch >= 10) + y[i] = ch + ('A' - 10); + else + y[i] = ch + '0'; + } + y[8] = '\0'; +} + + +cache_req *ap_proxy_cache_error(cache_req *c) +{ + if (c != NULL) { + if (c->fp != NULL) { + ap_pclosef(c->req->pool, c->fp->fd); + c->fp = NULL; + } + if (c->tempfile) unlink(c->tempfile); + } + return NULL; +} + +int ap_proxyerror(request_rec *r, int statuscode, const char *message) +{ + ap_table_setn(r->notes, "error-notes", + ap_pstrcat(r->pool, + "The proxy server could not handle the request " + "uri, "\">", + r->method, " ", r->uri, ".

\n" + "Reason: ", message, "", NULL)); + + /* Allow the "error-notes" string to be printed by ap_send_error_response() */ + ap_table_setn(r->notes, "verbose-error-to", ap_pstrdup(r->pool, "*")); + + r->status_line = ap_psprintf(r->pool, "%3.3u Proxy Error", statuscode); + return statuscode; +} + +/* + * This routine returns its own error message + */ +const char * + ap_proxy_host2addr(const char *host, struct hostent *reqhp) +{ + int i; + struct hostent *hp; + static APACHE_TLS struct hostent hpbuf; + static APACHE_TLS u_long ipaddr; + static APACHE_TLS char *charpbuf[2]; + + for (i = 0; host[i] != '\0'; i++) + if (!ap_isdigit(host[i]) && host[i] != '.') + break; + + if (host[i] != '\0') { + hp = gethostbyname(host); + if (hp == NULL) + return "Host not found"; + } + else { + ipaddr = ap_inet_addr(host); + hp = gethostbyaddr((char *) &ipaddr, sizeof(u_long), AF_INET); + if (hp == NULL) { + memset(&hpbuf, 0, sizeof(hpbuf)); + hpbuf.h_name = 0; + hpbuf.h_addrtype = AF_INET; + hpbuf.h_length = sizeof(u_long); + hpbuf.h_addr_list = charpbuf; + hpbuf.h_addr_list[0] = (char *) &ipaddr; + hpbuf.h_addr_list[1] = 0; + hp = &hpbuf; + } + } + *reqhp = *hp; + return NULL; +} + +static const char * + proxy_get_host_of_request(request_rec *r) +{ + char *url, *user = NULL, *password = NULL, *err, *host; + int port = -1; + + if (r->hostname != NULL) + return r->hostname; + + /* Set url to the first char after "scheme://" */ + if ((url = strchr(r->uri, ':')) == NULL + || url[1] != '/' || url[2] != '/') + return NULL; + + url = ap_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */ + + err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port); + + if (err != NULL) + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, + "%s", err); + + r->hostname = host; + + return host; /* ought to return the port, too */ +} + +/* Return TRUE if addr represents an IP address (or an IP network address) */ +int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p) +{ + const char *addr = This->name; + long ip_addr[4]; + int i, quads; + long bits; + + /* if the address is given with an explicit netmask, use that */ + /* Due to a deficiency in ap_inet_addr(), it is impossible to parse */ + /* "partial" addresses (with less than 4 quads) correctly, i.e. */ + /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */ + /* I therefore have to parse the IP address manually: */ + /*if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) */ + /* addr and mask were set by proxy_readmask() */ + /*return 1; */ + + /* Parse IP addr manually, optionally allowing */ + /* abbreviated net addresses like 192.168. */ + + /* Iterate over up to 4 (dotted) quads. */ + for (quads = 0; quads < 4 && *addr != '\0'; ++quads) { + char *tmp; + + if (*addr == '/' && quads > 0) /* netmask starts here. */ + break; + + if (!ap_isdigit(*addr)) + return 0; /* no digit at start of quad */ + + ip_addr[quads] = strtol(addr, &tmp, 0); + + if (tmp == addr) /* expected a digit, found something else */ + return 0; + + if (ip_addr[quads] < 0 || ip_addr[quads] > 255) { + /* invalid octet */ + return 0; + } + + addr = tmp; + + if (*addr == '.' && quads != 3) + ++addr; /* after the 4th quad, a dot would be illegal */ + } + + for (This->addr.s_addr = 0, i = 0; i < quads; ++i) + This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); + + if (addr[0] == '/' && ap_isdigit(addr[1])) { /* net mask follows: */ + char *tmp; + + ++addr; + + bits = strtol(addr, &tmp, 0); + + if (tmp == addr) /* expected a digit, found something else */ + return 0; + + addr = tmp; + + if (bits < 0 || bits > 32) /* netmask must be between 0 and 32 */ + return 0; + + } + else { + /* Determine (i.e., "guess") netmask by counting the */ + /* number of trailing .0's; reduce #quads appropriately */ + /* (so that 192.168.0.0 is equivalent to 192.168.) */ + while (quads > 0 && ip_addr[quads - 1] == 0) + --quads; + + /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */ + if (quads < 1) + return 0; + + /* every zero-byte counts as 8 zero-bits */ + bits = 8 * quads; + + if (bits != 32) /* no warning for fully qualified IP address */ + fprintf(stderr, "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n", + inet_ntoa(This->addr), bits); + } + + This->mask.s_addr = htonl(INADDR_NONE << (32 - bits)); + + if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) { + fprintf(stderr, "Warning: NetMask and IP-Addr disagree in %s/%ld\n", + inet_ntoa(This->addr), bits); + This->addr.s_addr &= This->mask.s_addr; + fprintf(stderr, " Set to %s/%ld\n", + inet_ntoa(This->addr), bits); + } + + if (*addr == '\0') { + This->matcher = proxy_match_ipaddr; + return 1; + } + else + return (*addr == '\0'); /* okay iff we've parsed the whole string */ +} + +/* Return TRUE if addr represents an IP address (or an IP network address) */ +static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r) +{ + int i; + int ip_addr[4]; + struct in_addr addr; + struct in_addr *ip_list; + char **ip_listptr; + const char *found; + const char *host = proxy_get_host_of_request(r); + + if (host == NULL) /* oops! */ + return 0; + + memset(&addr, '\0', sizeof addr); + memset(ip_addr, '\0', sizeof ip_addr); + + if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) { + for (addr.s_addr = 0, i = 0; i < 4; ++i) + addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); + + if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { +#if DEBUGGING + fprintf(stderr, "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); +#endif + return 1; + } +#if DEBUGGING + else { + fprintf(stderr, "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); + } +#endif + } + else { + struct hostent the_host; + + memset(&the_host, '\0', sizeof the_host); + found = ap_proxy_host2addr(host, &the_host); + + if (found != NULL) { +#if DEBUGGING + fprintf(stderr, "2)IP-NoMatch: hostname=%s msg=%s\n", host, found); +#endif + return 0; + } + + if (the_host.h_name != NULL) + found = the_host.h_name; + else + found = host; + + /* Try to deal with multiple IP addr's for a host */ + for (ip_listptr = the_host.h_addr_list; *ip_listptr; ++ip_listptr) { + ip_list = (struct in_addr *) *ip_listptr; + if (This->addr.s_addr == (ip_list->s_addr & This->mask.s_addr)) { +#if DEBUGGING + fprintf(stderr, "3)IP-Match: %s[%s] <-> ", found, inet_ntoa(*ip_list)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); +#endif + return 1; + } +#if DEBUGGING + else { + fprintf(stderr, "3)IP-NoMatch: %s[%s] <-> ", found, inet_ntoa(*ip_list)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); + } +#endif + } + } + + return 0; +} + +/* Return TRUE if addr represents a domain name */ +int ap_proxy_is_domainname(struct dirconn_entry *This, pool *p) +{ + char *addr = This->name; + int i; + + /* Domain name must start with a '.' */ + if (addr[0] != '.') + return 0; + + /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ + for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) + continue; + +#if 0 + if (addr[i] == ':') { + fprintf(stderr, "@@@@ handle optional port in proxy_is_domainname()\n"); + /* @@@@ handle optional port */ + } +#endif + + if (addr[i] != '\0') + return 0; + + /* Strip trailing dots */ + for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) + addr[i] = '\0'; + + This->matcher = proxy_match_domainname; + return 1; +} + +/* Return TRUE if host "host" is in domain "domain" */ +static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r) +{ + const char *host = proxy_get_host_of_request(r); + int d_len = strlen(This->name), h_len; + + if (host == NULL) /* some error was logged already */ + return 0; + + h_len = strlen(host); + + /* @@@ do this within the setup? */ + /* Ignore trailing dots in domain comparison: */ + while (d_len > 0 && This->name[d_len - 1] == '.') + --d_len; + while (h_len > 0 && host[h_len - 1] == '.') + --h_len; + return h_len > d_len + && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0; +} + +/* Return TRUE if addr represents a host name */ +int ap_proxy_is_hostname(struct dirconn_entry *This, pool *p) +{ + struct hostent host; + char *addr = This->name; + int i; + + /* Host names must not start with a '.' */ + if (addr[0] == '.') + return 0; + + /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ + for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i); + +#if 0 + if (addr[i] == ':') { + fprintf(stderr, "@@@@ handle optional port in proxy_is_hostname()\n"); + /* @@@@ handle optional port */ + } +#endif + + if (addr[i] != '\0' || ap_proxy_host2addr(addr, &host) != NULL) + return 0; + + This->hostentry = ap_pduphostent (p, &host); + + /* Strip trailing dots */ + for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) + addr[i] = '\0'; + + This->matcher = proxy_match_hostname; + return 1; +} + +/* Return TRUE if host "host" is equal to host2 "host2" */ +static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r) +{ + char *host = This->name; + const char *host2 = proxy_get_host_of_request(r); + int h2_len; + int h1_len; + + if (host == NULL || host2 == NULL) + return 0; /* oops! */ + + h2_len = strlen(host2); + h1_len = strlen(host); + +#if 0 + unsigned long *ip_list; + + /* Try to deal with multiple IP addr's for a host */ + for (ip_list = *This->hostentry->h_addr_list; *ip_list != 0UL; ++ip_list) + if (*ip_list == ? ? ? ? ? ? ? ? ? ? ? ? ?) + return 1; +#endif + + /* Ignore trailing dots in host2 comparison: */ + while (h2_len > 0 && host2[h2_len - 1] == '.') + --h2_len; + while (h1_len > 0 && host[h1_len - 1] == '.') + --h1_len; + return h1_len == h2_len + && strncasecmp(host, host2, h1_len) == 0; +} + +/* Return TRUE if addr is to be matched as a word */ +int ap_proxy_is_word(struct dirconn_entry *This, pool *p) +{ + This->matcher = proxy_match_word; + return 1; +} + +/* Return TRUE if string "str2" occurs literally in "str1" */ +static int proxy_match_word(struct dirconn_entry *This, request_rec *r) +{ + const char *host = proxy_get_host_of_request(r); + return host != NULL && strstr(host, This->name) != NULL; +} + +int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r) +{ + int i; + + ap_hard_timeout("proxy connect", r); + do { + i = connect(sock, (struct sockaddr *) addr, sizeof(struct sockaddr_in)); +#ifdef WIN32 + if (i == SOCKET_ERROR) + errno = WSAGetLastError(); +#endif /* WIN32 */ + } while (i == -1 && errno == EINTR); + if (i == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy connect to %s port %d failed", + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); + } + ap_kill_timeout(r); + + return i; +} + +/* This function is called by ap_table_do() for all header lines */ +/* (from proxy_http.c and proxy_ftp.c) */ +/* It is passed a table_do_args struct pointer and a MIME field and value pair */ +int ap_proxy_send_hdr_line(void *p, const char *key, const char *value) +{ + struct tbl_do_args *parm = (struct tbl_do_args *)p; + + if (key == NULL || value == NULL || value[0] == '\0') + return 1; + if (!parm->req->assbackwards) + ap_rvputs(parm->req, key, ": ", value, CRLF, NULL); + if (parm->cache != NULL && parm->cache->fp != NULL && + ap_bvputs(parm->cache->fp, key, ": ", value, CRLF, NULL) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, parm->cache->req, + "proxy: error writing header to %s", parm->cache->tempfile); + parm->cache = ap_proxy_cache_error(parm->cache); + } + return 1; /* tell ap_table_do() to continue calling us for more headers */ +} + +/* send a text line to one or two BUFF's; return line length */ +unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache) +{ + unsigned len = ap_bputs(data, client); + if (cache != NULL && cache->fp != NULL) + ap_bputs(data, cache->fp); + return len; +} + diff --git a/modules/test/.cvsignore b/modules/test/.cvsignore new file mode 100644 index 00000000000..8b4c6e3da42 --- /dev/null +++ b/modules/test/.cvsignore @@ -0,0 +1,3 @@ +Makefile +*.lo +*.so diff --git a/modules/test/.indent.pro b/modules/test/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/modules/test/.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/test/README b/modules/test/README new file mode 100644 index 00000000000..c61763c1dde --- /dev/null +++ b/modules/test/README @@ -0,0 +1,3 @@ +This directory is intended to house modules which are used for testing +server functionality. They're unsupported, and not guaranteed to remain +consistant between releases. You're on your own completely with these. diff --git a/os/.cvsignore b/os/.cvsignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/os/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/os/.indent.pro b/os/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/os/.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/os/Makefile.in b/os/Makefile.in new file mode 100644 index 00000000000..0e62c951ce8 --- /dev/null +++ b/os/Makefile.in @@ -0,0 +1,9 @@ + +DEPTH = .. +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +SUBDIRS = $(OS) + +include $(topsrcdir)/build/rules.mk diff --git a/os/beos/Makefile.in b/os/beos/Makefile.in new file mode 100644 index 00000000000..ea86214b21a --- /dev/null +++ b/os/beos/Makefile.in @@ -0,0 +1,10 @@ + +DEPTH = ../.. +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +LTLIBRARY_NAME = libos.la +LTLIBRARY_SOURCES = os.c os-inline.c beosd.c iol_socket.c + +include $(topsrcdir)/build/ltlib.mk diff --git a/os/beos/beosd.c b/os/beos/beosd.c new file mode 100644 index 00000000000..af364c69899 --- /dev/null +++ b/os/beos/beosd.c @@ -0,0 +1,227 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_main.h" +#include "http_log.h" +#include "unixd.h" + +unixd_config_rec unixd_config; + +void unixd_detach(void) +{ + int x; + pid_t pgrp; + + chdir("/"); + + if ((x = fork()) > 0) + exit(0); + else if (x == -1) { + perror("fork"); + fprintf(stderr, "%s: unable to fork new process\n", ap_server_argv0); + exit(1); + } + RAISE_SIGSTOP(DETACH); + +#ifndef NO_SETSID + if ((pgrp = setsid()) == -1) { + perror("setsid"); + fprintf(stderr, "%s: setsid failed\n", ap_server_argv0); + exit(1); + } +#else + if ((pgrp = setpgrp(getpid(), 0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "%s: setpgrp failed\n", ap_server_argv0); + exit(1); + } +#endif + + /* close out the standard file descriptors */ + if (freopen("/dev/null", "r", stdin) == NULL) { + fprintf(stderr, "%s: unable to replace stdin with /dev/null: %s\n", + ap_server_argv0, strerror(errno)); + /* continue anyhow -- note we can't close out descriptor 0 because we + * have nothing to replace it with, and if we didn't have a descriptor + * 0 the next file would be created with that value ... leading to + * havoc. + */ + } + if (freopen("/dev/null", "w", stdout) == NULL) { + fprintf(stderr, "%s: unable to replace stdout with /dev/null: %s\n", + ap_server_argv0, strerror(errno)); + } + /* stderr is a tricky one, we really want it to be the error_log, + * but we haven't opened that yet. So leave it alone for now and it'll + * be reopened moments later. + */ +} + +/* Set group privileges. + * + * Note that we use the username as set in the config files, rather than + * the lookup of to uid --- the same uid may have multiple passwd entries, + * with different sets of groups for each. + */ + +static int set_group_privs(void) +{ + if (!geteuid()) { + char *name; + + /* Get username if passed as a uid */ + + if (unixd_config.user_name[0] == '#') { + struct passwd *ent; + uid_t uid = atoi(&unixd_config.user_name[1]); + + if ((ent = getpwuid(uid)) == NULL) { + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "getpwuid: couldn't determine user name from uid %u, " + "you probably need to modify the User directive", + (unsigned)uid); + return -1; + } + + name = ent->pw_name; + } + else + name = unixd_config.user_name; + + if (setgid(unixd_config.group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "setgid: unable to set group id to Group %u", + (unsigned)unixd_config.group_id); + return -1; + } + + /* Reset `groups' attributes. */ + + if (initgroups(name, unixd_config.group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "initgroups: unable to set groups for User %s " + "and Group %u", name, (unsigned)unixd_config.group_id); + return -1; + } + } + return 0; +} + + +int unixd_setup_child(void) +{ + if (set_group_privs()) { + return -1; + } + + /* Only try to switch if we're running as root */ + if (!geteuid() && ( + setuid(unixd_config.user_id) == -1)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "setuid: unable to change uid"); + return -1; + } + return 0; +} + + +const char *unixd_set_user(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + unixd_config.user_name = arg; + unixd_config.user_id = ap_uname2id(arg); +#if !defined (BIG_SECURITY_HOLE) && !defined (OS2) + if (unixd_config.user_id == 0) { + return "Error:\tApache has not been designed to serve pages while\n" + "\trunning as root. There are known race conditions that\n" + "\twill allow any local user to read any file on the system.\n" + "\tIf you still desire to serve pages as root then\n" + "\tadd -DBIG_SECURITY_HOLE to the EXTRA_CFLAGS line in your\n" + "\tsrc/Configuration file and rebuild the server. It is\n" + "\tstrongly suggested that you instead modify the User\n" + "\tdirective in your httpd.conf file to list a non-root\n" + "\tuser.\n"; + } +#endif + + return NULL; +} + +const char *unixd_set_group(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + unixd_config.group_id = ap_gname2id(arg); + + return NULL; +} + +void unixd_pre_config(void) +{ + unixd_config.user_name = DEFAULT_USER; + unixd_config.user_id = ap_uname2id(DEFAULT_USER); + unixd_config.group_id = ap_gname2id(DEFAULT_GROUP); +} diff --git a/os/beos/beosd.h b/os/beos/beosd.h new file mode 100644 index 00000000000..37cb2f95b69 --- /dev/null +++ b/os/beos/beosd.h @@ -0,0 +1,82 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef UNIXD_H +#define UNIXD_H + +/* common stuff that unix MPMs will want */ + +typedef struct { + char *user_name; + uid_t user_id; + gid_t group_id; +} unixd_config_rec; +extern unixd_config_rec unixd_config; + +void unixd_detach(void); +int unixd_setup_child(void); +void unixd_pre_config(void); +const char *unixd_set_user(cmd_parms *cmd, void *dummy, char *arg); +const char *unixd_set_group(cmd_parms *cmd, void *dummy, char *arg); + +#define UNIX_DAEMON_COMMANDS \ +{ "User", unixd_set_user, NULL, RSRC_CONF, TAKE1, \ + "Effective user id for this server"}, \ +{ "Group", unixd_set_group, NULL, RSRC_CONF, TAKE1, \ + "Effective group id for this server"}, \ + +#endif diff --git a/os/beos/config.m4 b/os/beos/config.m4 new file mode 100644 index 00000000000..823ac063ab5 --- /dev/null +++ b/os/beos/config.m4 @@ -0,0 +1,3 @@ +if test "$OS" = "beos" ; then + CFLAGS="$CFLAGS -DBEOS" +fi diff --git a/os/beos/os.c b/os/beos/os.c new file mode 100644 index 00000000000..72281c296eb --- /dev/null +++ b/os/beos/os.c @@ -0,0 +1,75 @@ +/* + * This file will include OS specific functions which are not inlineable. + * Any inlineable functions should be defined in os-inline.c instead. + */ + +#include "ap_config.h" +#include "os.h" + +int ap_os_is_path_absolute(const char *file) +{ + return file[0] == '/'; +} + +int ap_spawnvp(const char *file, char *const argv[]) +{ + int pid; + + if ((pid = fork()) == -1) { + return pid; + } else if (pid == 0) { + if (execvp(file, argv) == -1) + return -1; + else + return -1; /* If we get, we have a real error, but this keeps + us from getting a warning during compile time. */ + } else + return pid; +} + + +/* some linkers complain unless there's at least one function in each + * .o file... and extra prototype is for gcc -Wmissing-prototypes + */ +extern void ap_is_not_here(void); +void ap_is_not_here(void) {} + +/* + * Abstraction layer for loading + * Apache modules under run-time via + * dynamic shared object (DSO) mechanism + */ + +void ap_os_dso_init(void) +{ + /* Nothing required to be done! */ +} + +void* ap_os_dso_load(const char *path) +{ + return (void*) load_add_on(path); +} + +void ap_os_dso_unload(void* handle) +{ + unload_add_on((image_id)handle); +} + +void *ap_os_dso_sym(void *handle, const char *symname) +{ + void * retval = 0; +#if defined(DLSYM_NEEDS_UNDERSCORE) + char *symbol = (char*)malloc(sizeof(char)*(strlen(symname)+2)); + sprintf(symbol, "_%s", symname); + get_image_symbol((image_id)handle, symbol, B_SYMBOL_TYPE_ANY, (void **)&retval); + free(symbol); + return retval; +#endif + get_image_symbol((image_id)handle, symname, B_SYMBOL_TYPE_ANY, (void **)&retval); + return retval; +} + +const char *ap_os_dso_error(void) +{ + return NULL; +} diff --git a/os/beos/os.h b/os/beos/os.h new file mode 100644 index 00000000000..a4af416269f --- /dev/null +++ b/os/beos/os.h @@ -0,0 +1,100 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#include "ap_config.h" +#include + +#ifndef PLATFORM +#define PLATFORM "BeOS" +#endif + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c or os-inline.c + */ + +extern int ap_os_is_path_absolute(const char *file); +extern int ap_spawnvp(const char *file, char *const argv[]); +#define ap_os_is_filename_valid(f) (1) +#define ap_os_kill(pid, sig) kill(pid, sig) + +/* + * Abstraction layer for loading + * Apache modules under run-time via + * dynamic shared object (DSO) mechanism + */ + +void *dlopen(const char *, int); +int dlclose(void *); +void *dlsym(void *, const char *); +const char *dlerror(void); + +#define RTLD_NOW 1 +#define RTLD_GLOBAL 0 + +#define ap_os_dso_handle_t image_id * +void ap_os_dso_init(void); +void * ap_os_dso_load(const char *); +void ap_os_dso_unload(void *); +void * ap_os_dso_sym(void *, const char *); +const char *ap_os_dso_error(void); + +#endif /* !APACHE_OS_H */ diff --git a/os/bs2000/.cvsignore b/os/bs2000/.cvsignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/os/bs2000/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/os/bs2000/ebcdic.c b/os/bs2000/ebcdic.c new file mode 100644 index 00000000000..0303fc6dbb7 --- /dev/null +++ b/os/bs2000/ebcdic.c @@ -0,0 +1,252 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +#ifdef CHARSET_EBCDIC +#include "ap_config.h" +#include "ebcdic.h" +/* + Initial Port for Apache-1.3 by + +"BS2000 OSD" is a POSIX on a main frame. It is made by Siemens AG, Germany. +Within the POSIX subsystem, the same character set was chosen as in +"native BS2000", namely EBCDIC. + +EBCDIC Table. (Yes, in EBCDIC, the letters 'a'..'z' are not contiguous!) +This table is bijective, i.e. there are no ambigous or duplicate characters +00 00 01 02 03 85 09 86 7f 87 8d 8e 0b 0c 0d 0e 0f *................* +10 10 11 12 13 8f 0a 08 97 18 19 9c 9d 1c 1d 1e 1f *................* +20 80 81 82 83 84 92 17 1b 88 89 8a 8b 8c 05 06 07 *................* +30 90 91 16 93 94 95 96 04 98 99 9a 9b 14 15 9e 1a *................* +40 20 a0 e2 e4 e0 e1 e3 e5 e7 f1 60 2e 3c 28 2b 7c * .........`.<(+|* +50 26 e9 ea eb e8 ed ee ef ec df 21 24 2a 29 3b 9f *&.........!$*);.* +60 2d 2f c2 c4 c0 c1 c3 c5 c7 d1 5e 2c 25 5f 3e 3f *-/........^,%_>?* +70 f8 c9 ca cb c8 cd ce cf cc a8 3a 23 40 27 3d 22 *..........:#@'="* +80 d8 61 62 63 64 65 66 67 68 69 ab bb f0 fd fe b1 *.abcdefghi......* +90 b0 6a 6b 6c 6d 6e 6f 70 71 72 aa ba e6 b8 c6 a4 *.jklmnopqr......* +a0 b5 af 73 74 75 76 77 78 79 7a a1 bf d0 dd de ae *..stuvwxyz......* +b0 a2 a3 a5 b7 a9 a7 b6 bc bd be ac 5b 5c 5d b4 d7 *...........[\]..* +c0 f9 41 42 43 44 45 46 47 48 49 ad f4 f6 f2 f3 f5 *.ABCDEFGHI......* +d0 a6 4a 4b 4c 4d 4e 4f 50 51 52 b9 fb fc db fa ff *.JKLMNOPQR......* +e0 d9 f7 53 54 55 56 57 58 59 5a b2 d4 d6 d2 d3 d5 *..STUVWXYZ......* +f0 30 31 32 33 34 35 36 37 38 39 b3 7b dc 7d da 7e *0123456789.{.}.~* +*/ + +/* The bijective ebcdic-to-ascii table: */ +const unsigned char os_toascii_strictly[256] = { +/*00*/ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, + 0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/ +/*10*/ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, + 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/ +/*20*/ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................*/ +/*30*/ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, + 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................*/ +/*40*/ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, + 0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+|*/ +/*50*/ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, + 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);.*/ +/*60*/ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, + 0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /*-/........^,%_>?*/ +/*70*/ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, + 0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'="*/ +/*80*/ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi......*/ +/*90*/ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr......*/ +/*a0*/ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz......*/ +/*b0*/ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, + 0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\]..*/ +/*c0*/ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI......*/ +/*d0*/ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR......*/ +/*e0*/ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ......*/ +/*f0*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e /*0123456789.{.}.~*/ +}; + +/* This table is (almost) identical to the previous one. The only difference + * is the fact that it maps every EBCDIC *except 0x0A* to its ASCII + * equivalent. The reason for this table is simple: Throughout the + * server, protocol strings are used in the form + * "Content-Type: text/plain\015\012". Now all the characters in the string + * are stored as EBCDIC, only the semantics of \012 is completely + * different from LF (look it up in the table above). \015 happens to be + * mapped to \015 anyway, so there's no special case for it. + * + * In THIS table, EBCDIC-\012 is mapped to ASCII-\012. + * This table is therefore used wherever an EBCDIC to ASCII conversion is + * needed in the server. + */ +/* ebcdic-to-ascii with \012 mapped to ASCII-\n */ +const unsigned char os_toascii[256] = { +/*00*/ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, + 0x87, 0x8d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/ +/*10*/ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, + 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/ +/*20*/ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................*/ +/*30*/ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, + 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................*/ +/*40*/ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, + 0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+|*/ +/*50*/ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, + 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);.*/ +/*60*/ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, + 0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /*-/........^,%_>?*/ +/*70*/ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, + 0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'="*/ +/*80*/ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi......*/ +/*90*/ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr......*/ +/*a0*/ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz......*/ +/*b0*/ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, + 0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\]..*/ +/*c0*/ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI......*/ +/*d0*/ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR......*/ +/*e0*/ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ......*/ +/*f0*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e /*0123456789.{.}.~*/ +}; + +/* The ascii-to-ebcdic table: +00 00 01 02 03 37 2d 2e 2f 16 05 15 0b 0c 0d 0e 0f *................* +10 10 11 12 13 3c 3d 32 26 18 19 3f 27 1c 1d 1e 1f *................* +20 40 5a 7f 7b 5b 6c 50 7d 4d 5d 5c 4e 6b 60 4b 61 * !"#$%&'()*+,-./ +30 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 7a 5e 4c 7e 6e 6f *0123456789:;<=>?* +40 7c c1 c2 c3 c4 c5 c6 c7 c8 c9 d1 d2 d3 d4 d5 d6 *@ABCDEFGHIJKLMNO* +50 d7 d8 d9 e2 e3 e4 e5 e6 e7 e8 e9 bb bc bd 6a 6d *PQRSTUVWXYZ[\]^_* +60 4a 81 82 83 84 85 86 87 88 89 91 92 93 94 95 96 *`abcdefghijklmno* +70 97 98 99 a2 a3 a4 a5 a6 a7 a8 a9 fb 4f fd ff 07 *pqrstuvwxyz{|}~.* +80 20 21 22 23 24 04 06 08 28 29 2a 2b 2c 09 0a 14 *................* +90 30 31 25 33 34 35 36 17 38 39 3a 3b 1a 1b 3e 5f *................* +a0 41 aa b0 b1 9f b2 d0 b5 79 b4 9a 8a ba ca af a1 *................* +b0 90 8f ea fa be a0 b6 b3 9d da 9b 8b b7 b8 b9 ab *................* +c0 64 65 62 66 63 67 9e 68 74 71 72 73 78 75 76 77 *................* +d0 ac 69 ed ee eb ef ec bf 80 e0 fe dd fc ad ae 59 *................* +e0 44 45 42 46 43 47 9c 48 54 51 52 53 58 55 56 57 *................* +f0 8c 49 cd ce cb cf cc e1 70 c0 de db dc 8d 8e df *................* +*/ +const unsigned char os_toebcdic[256] = { +/*00*/ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, + 0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/ +/*10*/ 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, + 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/ +/*20*/ 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, + 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /* !"#$%&'()*+,-./ */ +/*30*/ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /*0123456789:;<=>?*/ +/*40*/ 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /*@ABCDEFGHIJKLMNO*/ +/*50*/ 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xbb, 0xbc, 0xbd, 0x6a, 0x6d, /*PQRSTUVWXYZ[\]^_*/ +/*60*/ 0x4a, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /*`abcdefghijklmno*/ +/*70*/ 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, + 0xa7, 0xa8, 0xa9, 0xfb, 0x4f, 0xfd, 0xff, 0x07, /*pqrstuvwxyz{|}~.*/ +/*80*/ 0x20, 0x21, 0x22, 0x23, 0x24, 0x04, 0x06, 0x08, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x14, /*................*/ +/*90*/ 0x30, 0x31, 0x25, 0x33, 0x34, 0x35, 0x36, 0x17, + 0x38, 0x39, 0x3a, 0x3b, 0x1a, 0x1b, 0x3e, 0x5f, /*................*/ +/*a0*/ 0x41, 0xaa, 0xb0, 0xb1, 0x9f, 0xb2, 0xd0, 0xb5, + 0x79, 0xb4, 0x9a, 0x8a, 0xba, 0xca, 0xaf, 0xa1, /*................*/ +/*b0*/ 0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3, + 0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, /*................*/ +/*c0*/ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68, + 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, /*................*/ +/*d0*/ 0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf, + 0x80, 0xe0, 0xfe, 0xdd, 0xfc, 0xad, 0xae, 0x59, /*................*/ +/*e0*/ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48, + 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, /*................*/ +/*f0*/ 0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1, + 0x70, 0xc0, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf /*................*/ +}; + +/* Translate a memory block from EBCDIC (host charset) to ASCII (net charset) + * dest and srce may be identical, or separate memory blocks, but + * should not overlap. + */ +void +ebcdic2ascii(unsigned char *dest, const unsigned char *srce, size_t count) +{ + while (count-- != 0) { + *dest++ = os_toascii[*srce++]; + } +} +void +ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count) +{ + while (count-- != 0) { + *dest++ = os_toascii_strictly[*srce++]; + } +} +void +ascii2ebcdic(unsigned char *dest, const unsigned char *srce, size_t count) +{ + while (count-- != 0) { + *dest++ = os_toebcdic[*srce++]; + } +} +#endif /*CHARSET_EBCDIC*/ diff --git a/os/bs2000/ebcdic.h b/os/bs2000/ebcdic.h new file mode 100644 index 00000000000..267f9786df2 --- /dev/null +++ b/os/bs2000/ebcdic.h @@ -0,0 +1,8 @@ +#include + +extern const unsigned char os_toascii[256]; +extern const unsigned char os_toebcdic[256]; +void ebcdic2ascii(unsigned char *dest, const unsigned char *srce, size_t count); +void ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count); +void ascii2ebcdic(unsigned char *dest, const unsigned char *srce, size_t count); + diff --git a/os/bs2000/os.c b/os/bs2000/os.c new file mode 100644 index 00000000000..c33810a2f0d --- /dev/null +++ b/os/bs2000/os.c @@ -0,0 +1,103 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * This file will include OS specific functions which are not inlineable. + * Any inlineable functions should be defined in os-inline.c instead. + */ + +#include "httpd.h" +#include "http_core.h" +#include "os.h" + +/* Check the Content-Type to decide if conversion is needed */ +int ap_checkconv(struct request_rec *r) +{ + int convert_to_ascii; + const char *type; + + /* To make serving of "raw ASCII text" files easy (they serve faster + * since they don't have to be converted from EBCDIC), a new + * "magic" type prefix was invented: text/x-ascii-{plain,html,...} + * If we detect one of these content types here, we simply correct + * the type to the real text/{plain,html,...} type. Otherwise, we + * set a flag that translation is required later on. + */ + + type = (r->content_type == NULL) ? ap_default_type(r) : r->content_type; + + /* If no content type is set then treat it as (ebcdic) text/plain */ + convert_to_ascii = (type == NULL); + + /* Conversion is applied to text/ files only, if ever. */ + if (type && (strncasecmp(type, "text/", 5) == 0 || + strncasecmp(type, "message/", 8) == 0)) { + if (strncasecmp(type, ASCIITEXT_MAGIC_TYPE_PREFIX, + sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1) == 0) + r->content_type = ap_pstrcat(r->pool, "text/", + type+sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1, + NULL); + else + /* translate EBCDIC to ASCII */ + convert_to_ascii = 1; + } + /* Enable conversion if it's a text document */ + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert_to_ascii); + + return convert_to_ascii; +} + diff --git a/os/bs2000/os.h b/os/bs2000/os.h new file mode 100644 index 00000000000..1ede0813f2e --- /dev/null +++ b/os/bs2000/os.h @@ -0,0 +1,51 @@ +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#define PLATFORM "BS2000" + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c or os-inline.c + */ + +#if !defined(INLINE) && defined(USE_GNU_INLINE) +/* Compiler supports inline, so include the inlineable functions as + * part of the header + */ +#define INLINE extern ap_inline + +INLINE int ap_os_is_path_absolute(const char *file); + +#include "os-inline.c" +#endif + +#ifndef INLINE +/* Compiler does not support inline, so prototype the inlineable functions + * as normal + */ +extern int ap_os_is_path_absolute(const char *file); +#endif + +/* Other ap_os_ routines not used by this platform */ + +#define ap_os_is_filename_valid(f) (1) +#define ap_os_kill(pid, sig) kill(pid, sig) + +#if !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE) +typedef struct { + char *username; + char *account; + char *processor_name; +} _rini_struct; + +extern int _rini(_rini_struct *); +#endif /* !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE) */ + +/* Sorry if this is ugly, but the include order doesn't allow me + * to use request_rec here... */ +struct request_rec; +extern int ap_checkconv(struct request_rec *r); +extern pid_t os_fork(const char *user); + +#endif /*! APACHE_OS_H*/ diff --git a/os/config.m4 b/os/config.m4 new file mode 100644 index 00000000000..750f9b0183b --- /dev/null +++ b/os/config.m4 @@ -0,0 +1,8 @@ +AC_MSG_CHECKING(which OS this is) + +dnl ## XXX - I'm not sure, but this might not handle the non-Unix case yet +OS=unix +OS_DIR=os/$OS + +AC_MSG_RESULT([$OS]) +APACHE_OUTPUT(os/$OS/Makefile) diff --git a/os/os2/.cvsignore b/os/os2/.cvsignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/os/os2/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/os/os2/Makefile.in b/os/os2/Makefile.in new file mode 100644 index 00000000000..c03ac3fca15 --- /dev/null +++ b/os/os2/Makefile.in @@ -0,0 +1,10 @@ + +DEPTH = ../.. +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +LTLIBRARY_NAME = libos.la +LTLIBRARY_SOURCES = os.c os-inline.c util_os2.c iol_socket.c + +include $(topsrcdir)/build/ltlib.mk diff --git a/os/os2/config.m4 b/os/os2/config.m4 new file mode 100644 index 00000000000..d6bab02dd4e --- /dev/null +++ b/os/os2/config.m4 @@ -0,0 +1,4 @@ +if test "$OS" = "os2" ; then + CFLAGS="$CFLAGS -DOS2 -O2" + LDFLAGS="$LDFLAGS -Zexe" +fi diff --git a/os/os2/os.h b/os/os2/os.h new file mode 100644 index 00000000000..d717f1f8ca4 --- /dev/null +++ b/os/os2/os.h @@ -0,0 +1,52 @@ +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#define PLATFORM "OS/2" +#define HAVE_CANONICAL_FILENAME +#define HAVE_DRIVE_LETTERS + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c or os-inline.c + */ + +#if defined(__GNUC__) && !defined(INLINE) +/* Compiler supports inline, so include the inlineable functions as + * part of the header + */ +#define INLINE extern __inline__ + +INLINE int ap_os_is_path_absolute(const char *file); + +#include "os-inline.c" +#endif + +#ifndef INLINE +/* Compiler does not support inline, so prototype the inlineable functions + * as normal + */ +extern int ap_os_is_path_absolute(const char *file); +#endif + +/* FIXME: the following should be implemented on this platform */ +#define ap_os_is_filename_valid(f) (1) + +/* Use a specialized kill() function */ +int ap_os_kill(int pid, int sig); + +/* Maps an OS error code to an error message */ +char *ap_os_error_message(int err); + +/* OS/2 doesn't have symlinks so S_ISLNK is always false */ +#define S_ISLNK(m) 0 + +/* Dynamic loading functions */ +#define ap_os_dso_handle_t unsigned long +void ap_os_dso_init(void); +ap_os_dso_handle_t ap_os_dso_load(const char *); +void ap_os_dso_unload(ap_os_dso_handle_t); +void * ap_os_dso_sym(ap_os_dso_handle_t, const char *); +const char *ap_os_dso_error(void); + +#endif /* ! APACHE_OS_H */ diff --git a/os/os2/util_os2.c b/os/os2/util_os2.c new file mode 100644 index 00000000000..820ea618008 --- /dev/null +++ b/os/os2/util_os2.c @@ -0,0 +1,96 @@ +#define INCL_DOS +#define INCL_DOSERRORS +#include +#include "httpd.h" +#include "http_log.h" + + +API_EXPORT(char *)ap_os_canonical_filename(pool *pPool, const char *szFile) +{ + char buf[HUGE_STRING_LEN]; + char buf2[HUGE_STRING_LEN]; + int rc, len; + char *pos; + +/* Remove trailing slash unless it's a root directory */ + strcpy(buf, szFile); + len = strlen(buf); + + if (len > 3 && buf[len-1] == '/') + buf[--len] = 0; + + rc = DosQueryPathInfo(buf, FIL_QUERYFULLNAME, buf2, HUGE_STRING_LEN); + + if (rc) { + if ( rc != ERROR_INVALID_NAME ) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL, "OS/2 error %d for file %s", rc, szFile); + return ap_pstrdup(pPool, ""); + } else { + return ap_pstrdup(pPool, szFile); + } + } + + strlwr(buf2); + +/* Switch backslashes to forward */ + for (pos=buf2; *pos; pos++) + if (*pos == '\\') + *pos = '/'; + + return ap_pstrdup(pPool, buf2); +} + + + +int ap_os_kill(pid_t pid, int sig) +{ +/* SIGTERM's don't work too well in OS/2 (only affects other EMX programs). + CGIs may not be, esp. REXX scripts, so use a native call instead */ + + int rc; + + if ( sig == SIGTERM ) { + rc = DosSendSignalException( pid, XCPT_SIGNAL_BREAK ); + + if ( rc ) { + errno = ESRCH; + rc = -1; + } + } else { + rc = kill(pid, sig); + } + + return rc; +} + + + +char *ap_os_error_message(int err) +{ + static char result[200]; + char message[HUGE_STRING_LEN]; + ULONG len; + char *pos; + int c; + + if (DosGetMessage(NULL, 0, message, HUGE_STRING_LEN, err, "OSO001.MSG", &len) == 0) { + len--; + message[len] = 0; + pos = result; + + if (len >= sizeof(result)) + len = sizeof(result-1); + + for (c=0; c. + * + */ + + +#ifdef CHARSET_EBCDIC +#include "ap_config.h" +#include "ebcdic.h" +/* +This code does basic character mapping for IBM's TPF operating system. +It is a modified version of 's code for +the BS2000 (apache/src/os/bs2000/ebcdic.c). +*/ + +/* +Bijective EBCDIC (character set IBM-1047) to US-ASCII table: +This table is bijective - there are no ambigous or duplicate characters. +*/ +const unsigned char os_toascii_strictly[256] = { + 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, /* 00-0f: */ + 0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */ + 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, /* 10-1f: */ + 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, /* 20-2f: */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* ................ */ + 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, /* 30-3f: */ + 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* ................ */ + 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, /* 40-4f: */ + 0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* ...........<(+| */ + 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, /* 50-5f: */ + 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* &.........!$*);^ */ + 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, /* 60-6f: */ + 0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* -/.........,%_>? */ + 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, /* 70-7f: */ + 0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* .........`:#@'=" */ + 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 80-8f: */ + 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* .abcdefghi...... */ + 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* 90-9f: */ + 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* .jklmnopqr...... */ + 0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* a0-af: */ + 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, /* .~stuvwxyz...[.. */ + 0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, /* b0-bf: */ + 0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, /* .............].. */ + 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* c0-cf: */ + 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* {ABCDEFGHI...... */ + 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* d0-df: */ + 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, /* }JKLMNOPQR...... */ + 0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* e0-ef: */ + 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* \.STUVWXYZ...... */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* f0-ff: */ + 0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f /* 0123456789...... */ +}; + +/* +Server EBCDIC (character set IBM-1047) to US-ASCII table: +This table is a copy of the os_toascii_strictly bijective table above. +The only change is that hex 0a (\012 octal) is mapped to hex 0a +(ASCII's line feed) instead of hex 8e. This is done because throughout +Apache, protocol string definitions hardcode the linefeed as \012 (octal): +"Content-Type: text/plain\015\012". Without this kludge all protocol +string definitions would need to be changed from ...\012 to ...\025. +*/ +const unsigned char os_toascii[256] = { + 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, /* 00-0f: */ + 0x87, 0x8d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */ + 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, /* 10-1f: */ + 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, /* 20-2f: */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* ................ */ + 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, /* 30-3f: */ + 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* ................ */ + 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, /* 40-4f: */ + 0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* ...........<(+| */ + 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, /* 50-5f: */ + 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* &.........!$*);^ */ + 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, /* 60-6f: */ + 0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* -/.........,%_>? */ + 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, /* 70-7f: */ + 0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* .........`:#@'=" */ + 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 80-8f: */ + 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* .abcdefghi...... */ + 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* 90-9f: */ + 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* .jklmnopqr...... */ + 0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* a0-af: */ + 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, /* .~stuvwxyz...[.. */ + 0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, /* b0-bf: */ + 0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, /* .............].. */ + 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* c0-cf: */ + 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* {ABCDEFGHI...... */ + 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* d0-df: */ + 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, /* }JKLMNOPQR...... */ + 0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* e0-ef: */ + 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* \.STUVWXYZ...... */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* f0-ff: */ + 0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f /* 0123456789...... */ +}; + +/* +The US-ASCII to EBCDIC (character set IBM-1047) table: +This table is bijective (no ambiguous or duplicate characters) +*/ +const unsigned char os_toebcdic[256] = { + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, /* 00-0f: */ + 0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */ + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, /* 10-1f: */ + 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */ + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, /* 20-2f: */ + 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /* !"#$%&'()*+,-./ */ + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 30-3f: */ + 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /* 0123456789:;<=>? */ + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 40-4f: */ + 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /* @ABCDEFGHIJKLMNO */ + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, /* 50-5f: */ + 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, /* PQRSTUVWXYZ[\]^_ */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 60-6f: */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /* `abcdefghijklmno */ + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, /* 70-7f: */ + 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, /* pqrstuvwxyz{|}~. */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x04, 0x06, 0x08, /* 80-8f: */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x14, /* ................ */ + 0x30, 0x31, 0x25, 0x33, 0x34, 0x35, 0x36, 0x17, /* 90-9f: */ + 0x38, 0x39, 0x3a, 0x3b, 0x1a, 0x1b, 0x3e, 0xff, /* ................ */ + 0x41, 0xaa, 0x4a, 0xb1, 0x9f, 0xb2, 0x6a, 0xb5, /* a0-af: */ + 0xbb, 0xb4, 0x9a, 0x8a, 0xb0, 0xca, 0xaf, 0xbc, /* ................ */ + 0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3, /* b0-bf: */ + 0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, /* ................ */ + 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68, /* c0-cf: */ + 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, /* ................ */ + 0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf, /* d0-df: */ + 0x80, 0xfd, 0xfe, 0xfb, 0xfc, 0xba, 0xae, 0x59, /* ................ */ + 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48, /* e0-ef: */ + 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, /* ................ */ + 0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1, /* f0-ff: */ + 0x70, 0xdd, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf /* ................ */ +}; + +/* Translate a memory block from EBCDIC (host charset) to ASCII (net charset) + * dest and srce may be identical, or separate memory blocks, but + * should not overlap. + */ +void +ebcdic2ascii(void *dest, const void *srce, size_t count) +{ + unsigned char *udest = dest; + const unsigned char *usrce = srce; + while (count-- != 0) { + *udest++ = os_toascii[*usrce++]; + } +} +void +ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count) +{ + while (count-- != 0) { + *dest++ = os_toascii_strictly[*srce++]; + } +} +void +ascii2ebcdic(void *dest, const void *srce, size_t count) +{ + unsigned char *udest = dest; + const unsigned char *usrce = srce; + + while (count-- != 0) { + *udest++ = os_toebcdic[*usrce++]; + } +} +#endif /*CHARSET_EBCDIC*/ + diff --git a/os/tpf/ebcdic.h b/os/tpf/ebcdic.h new file mode 100644 index 00000000000..e9c4120e1a3 --- /dev/null +++ b/os/tpf/ebcdic.h @@ -0,0 +1,8 @@ +#include + +extern const unsigned char os_toascii[256]; +extern const unsigned char os_toebcdic[256]; +void ebcdic2ascii(void *dest, const void *srce, size_t count); +void ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count); +void ascii2ebcdic(void *dest, const void *srce, size_t count); + diff --git a/os/tpf/os.c b/os/tpf/os.c new file mode 100644 index 00000000000..e4682324cd1 --- /dev/null +++ b/os/tpf/os.c @@ -0,0 +1,414 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * This file will include OS specific functions which are not inlineable. + * Any inlineable functions should be defined in os-inline.c instead. + */ + +#include "httpd.h" +#include "http_core.h" +#include "os.h" +#include "scoreboard.h" +#include "http_log.h" +#include "http_conf_globals.h" + +static FILE *sock_fp; + +/* Check the Content-Type to decide if conversion is needed */ +int ap_checkconv(struct request_rec *r) +{ + int convert_to_ascii; + const char *type; + + /* To make serving of "raw ASCII text" files easy (they serve faster + * since they don't have to be converted from EBCDIC), a new + * "magic" type prefix was invented: text/x-ascii-{plain,html,...} + * If we detect one of these content types here, we simply correct + * the type to the real text/{plain,html,...} type. Otherwise, we + * set a flag that translation is required later on. + */ + + type = (r->content_type == NULL) ? ap_default_type(r) : r->content_type; + + /* If no content type is set then treat it as (ebcdic) text/plain */ + convert_to_ascii = (type == NULL); + + /* Conversion is applied to text/ files only, if ever. */ + if (type && (strncasecmp(type, "text/", 5) == 0 || + strncasecmp(type, "message/", 8) == 0)) { + if (strncasecmp(type, ASCIITEXT_MAGIC_TYPE_PREFIX, + sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1) == 0){ + r->content_type = ap_pstrcat(r->pool, "text/", + type+sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1, NULL); + if (r->method_number == M_PUT) + ap_bsetflag(r->connection->client, B_ASCII2EBCDIC, 0); + } + + else + /* translate EBCDIC to ASCII */ + convert_to_ascii = 1; + } + else{ + if (r->method_number == M_PUT) + ap_bsetflag(r->connection->client, B_ASCII2EBCDIC, 0); + /* don't translate non-text files to EBCDIC */ + } + /* Enable conversion if it's a text document */ + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert_to_ascii); + + return convert_to_ascii; +} + +int tpf_select(int maxfds, fd_set *reads, fd_set *writes, fd_set *excepts, struct timeval *tv) +{ +/* We're going to force our way through select. We're only interested reads and TPF allows + 2billion+ socket descriptors for we don't want an fd_set that big. Just assume that maxfds-1 + contains the socket descriptor we're interested in. If it's 0, leave it alone. */ + + int sockets[1]; + int no_reads = 0; + int no_writes = 0; + int no_excepts = 0; + int timeout = 0; + int rv; + + if(maxfds) { + if(tv) + timeout = tv->tv_sec * 1000 + tv->tv_usec; + sockets[0] = maxfds-1; + no_reads++; + } + else + sockets[0] = 0; + + ap_check_signals(); + rv = select(sockets, no_reads, no_writes, no_excepts, timeout); + ap_check_signals(); + + return rv; + +} + +int tpf_accept(int sockfd, struct sockaddr *peer, int *paddrlen) +{ + int socks[1]; + int rv; + + ap_check_signals(); + socks[0] = sockfd; + rv = select(socks, 1, 0, 0, 1000); + errno = sock_errno(); + if(rv>0) { + ap_check_signals(); + rv = accept(sockfd, peer, paddrlen); + errno = sock_errno(); + } + return rv; +} + +/* the getpass function is not usable on TPF */ +char *getpass(const char* prompt) +{ + errno = EIO; + return((char *)NULL); +} + +#ifndef __PIPE_ +int pipe(int fildes[2]) +{ + errno = ENOSYS; + return(-1); +} +#endif + +/* fork and exec functions are not defined on + TPF due to the implementation of tpf_fork() */ + +pid_t fork(void) +{ + errno = ENOSYS; + return(-1); +} + +int execl(const char *path, const char *arg0, ...) +{ + errno = ENOSYS; + return(-1); +} + +int execle(const char *path, const char *arg0, ...) +{ + errno = ENOSYS; + return(-1); +} + +int execve(const char *path, char *const argv[], char *const envp[]) +{ + errno = ENOSYS; + return(-1); +} + +int execvp(const char *file, char *const argv[]) +{ + errno = ENOSYS; + return(-1); +} + + + +int ap_tpf_spawn_child(pool *p, int (*func) (void *, child_info *), + void *data, enum kill_conditions kill_how, + int *pipe_in, int *pipe_out, int *pipe_err, + int out_fds[], int in_fds[], int err_fds[]) + +{ + + int i, temp_out, temp_in, temp_err, save_errno, pid, result=0; + int fd_flags_out, fd_flags_in, fd_flags_err; + struct tpf_fork_input fork_input; + TPF_FORK_CHILD *cld = (TPF_FORK_CHILD *) data; + array_header *env_arr = ap_table_elts ((array_header *) cld->subprocess_env); + table_entry *elts = (table_entry *) env_arr->elts; + + + + if (func) { + if (result=func(data, NULL)) { + return 0; /* error from child function */ + } + } + + if (pipe_out) { + fd_flags_out = fcntl(out_fds[0], F_GETFD); + fcntl(out_fds[0], F_SETFD, FD_CLOEXEC); + temp_out = dup(STDOUT_FILENO); + fcntl(temp_out, F_SETFD, FD_CLOEXEC); + dup2(out_fds[1], STDOUT_FILENO); + } + + + if (pipe_in) { + fd_flags_in = fcntl(in_fds[1], F_GETFD); + fcntl(in_fds[1], F_SETFD, FD_CLOEXEC); + temp_in = dup(STDIN_FILENO); + fcntl(temp_in, F_SETFD, FD_CLOEXEC); + dup2(in_fds[0], STDIN_FILENO); + } + + if (pipe_err) { + fd_flags_err = fcntl(err_fds[0], F_GETFD); + fcntl(err_fds[0], F_SETFD, FD_CLOEXEC); + temp_err = dup(STDERR_FILENO); + fcntl(temp_err, F_SETFD, FD_CLOEXEC); + dup2(err_fds[1], STDERR_FILENO); + } + + if (cld->subprocess_env) { + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) + continue; + setenv (elts[i].key, elts[i].val, 1); + } + } + + fork_input.program = (const char*) cld->filename; + fork_input.prog_type = cld->prog_type; + fork_input.istream = TPF_FORK_IS_BALANCE; + fork_input.ebw_data_length = 0; + fork_input.ebw_data = NULL; + fork_input.parm_data = NULL; + + + if ((pid = tpf_fork(&fork_input)) < 0) { + save_errno = errno; + if (pipe_out) { + close(out_fds[0]); + } + if (pipe_in) { + close(in_fds[1]); + } + if (pipe_err) { + close(err_fds[0]); + } + errno = save_errno; + pid = 0; + } + + if (cld->subprocess_env) { + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) + continue; + unsetenv (elts[i].key); + } + } + + if (pipe_out) { + close(out_fds[1]); + dup2(temp_out, STDOUT_FILENO); + close(temp_out); + fcntl(out_fds[0], F_SETFD, fd_flags_out); + } + + if (pipe_in) { + close(in_fds[0]); + dup2(temp_in, STDIN_FILENO); + close(temp_in); + fcntl(in_fds[1], F_SETFD, fd_flags_in); + } + + + if (pipe_err) { + close(err_fds[1]); + dup2(temp_err, STDERR_FILENO); + close(temp_err); + fcntl(err_fds[0], F_SETFD, fd_flags_err); + } + + + if (pid) { + + ap_note_subprocess(p, pid, kill_how); + + if (pipe_out) { + *pipe_out = out_fds[0]; + } + if (pipe_in) { + *pipe_in = in_fds[1]; + } + if (pipe_err) { + *pipe_err = err_fds[0]; + } + } + + return pid; + +} + +pid_t os_fork(server_rec *s, int slot) +{ + struct tpf_fork_input fork_input; + APACHE_TPF_INPUT input_parms; + int count; + listen_rec *lr; + + fflush(stdin); + if (dup2(fileno(sock_fp), STDIN_FILENO) == -1) + ap_log_error(APLOG_MARK, APLOG_CRIT, s, + "unable to replace stdin with sock device driver"); + fflush(stdout); + if (dup2(fileno(sock_fp), STDOUT_FILENO) == -1) + ap_log_error(APLOG_MARK, APLOG_CRIT, s, + "unable to replace stdout with sock device driver"); + input_parms.generation = ap_my_generation; +#ifdef SCOREBOARD_FILE + input_parms.scoreboard_fd = scoreboard_fd; +#else /* must be USE_TPF_SCOREBOARD or USE_SHMGET_SCOREBOARD */ + input_parms.scoreboard_heap = ap_scoreboard_image; +#endif + + lr = ap_listeners; + count = 0; + do { + input_parms.listeners[count] = lr->fd; + lr = lr->next; + count++; + } while(lr != ap_listeners); + + input_parms.slot = slot; + input_parms.restart_time = ap_restart_time; + fork_input.ebw_data = &input_parms; + fork_input.program = ap_server_argv0; + fork_input.prog_type = TPF_FORK_NAME; + fork_input.istream = TPF_FORK_IS_BALANCE; + fork_input.ebw_data_length = sizeof(input_parms); + fork_input.parm_data = "-x"; + return tpf_fork(&fork_input); +} + +int os_check_server(char *server) { + #ifndef USE_TPF_DAEMON + int rv; + int *current_acn; + if((rv = inetd_getServerStatus(server)) == INETD_SERVER_STATUS_INACTIVE) + return 1; + else { + current_acn = (int *)cinfc_fast(CINFC_CMMACNUM); + if(ecbp2()->ce2acn != *current_acn) + return 1; + } + #endif + return 0; +} + +void os_note_additional_cleanups(pool *p, int sd) { + char sockfilename[50]; + /* write the socket to file so that TPF socket device driver will close socket in case + we happen to abend. */ + sprintf(sockfilename, "/dev/tpf.socket.file/%.8X", sd); + sock_fp = fopen(sockfilename, "r+"); + ap_note_cleanups_for_file(p, sock_fp); /* arrange to close on exec or restart */ + fcntl(sd,F_SETFD,FD_CLOEXEC); +} + +void os_tpf_child(APACHE_TPF_INPUT *input_parms) { + tpf_child = 1; + ap_my_generation = input_parms->generation; + ap_restart_time = input_parms->restart_time; +} + + diff --git a/os/tpf/os.h b/os/tpf/os.h new file mode 100644 index 00000000000..ff653229029 --- /dev/null +++ b/os/tpf/os.h @@ -0,0 +1,108 @@ +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#define PLATFORM "TPF" + +#ifdef errno +#undef errno +#endif + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c or os-inline.c + */ + +#include "ap_config.h" + +#if !defined(INLINE) && defined(USE_GNU_INLINE) +/* Compiler supports inline, so include the inlineable functions as + * part of the header + */ +#define INLINE extern ap_inline +#include "os-inline.c" +#endif + +#ifndef INLINE +/* Compiler does not support inline, so prototype the inlineable functions + * as normal + */ +extern int ap_os_is_path_absolute(const char *f); +#endif + +/* Other ap_os_ routines not used by this platform */ + +#define ap_os_is_filename_valid(f) (1) +#define ap_os_kill(pid, sig) kill(pid, sig) + +/* Sorry if this is ugly, but the include order doesn't allow me + * to use request_rec here... */ +struct request_rec; +extern int ap_checkconv(struct request_rec *r); + +#include +#ifndef __strings_h + +#define FD_SETSIZE 2048 + +typedef long fd_mask; + +#define NBBY 8 /* number of bits in a byte */ +#define NFDBITS (sizeof(fd_mask) * NBBY) +#define howmany(x, y) (((x)+((y)-1))/(y)) + +typedef struct fd_set { + fd_mask fds_bits [howmany(FD_SETSIZE, NFDBITS)]; +} fd_set; + +#define FD_CLR(n, p)((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p)((p)->fds_bits[(n)/NFDBITS] & (1 <<((n) % NFDBITS))) +#define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p))) +#endif + +#ifdef FD_SET +#undef FD_SET +#define FD_SET(n, p) (0) +#endif + +#define RESOURCE_KEY ((void*) 0xC1C2C1C3) + +/* TPF doesn't have, or need, tzset (it is used in mod_expires.c) */ +#define tzset() + +#include +struct apache_input { + INETD_SERVER_INPUT inetd_server; + void *scoreboard_heap; /* scoreboard system heap address */ + int scoreboard_fd; /* scoreboard file descriptor */ + int slot; /* child number */ + int generation; /* server generation number */ + int listeners[10]; + time_t restart_time; +}; + +typedef struct apache_input APACHE_TPF_INPUT; + +typedef struct tpf_fork_child { + char *filename; + enum { FORK_NAME = 1, FORK_FILE = 2 } prog_type; + void *subprocess_env; +}TPF_FORK_CHILD; + +int tpf_accept(int sockfd, struct sockaddr *peer, int *paddrlen); +extern int tpf_child; + +struct server_rec; +pid_t os_fork(struct server_rec *s, int slot); +int os_check_server(char *server); +char *getpass(const char *prompt); +extern char *ap_server_argv0; +extern int scoreboard_fd; +#include +#ifndef SIGPIPE +#define SIGPIPE 14 +#endif +#ifdef NSIG +#undef NSIG +#endif +#endif /*! APACHE_OS_H*/ diff --git a/os/tpf/samples/linkdll.jcl b/os/tpf/samples/linkdll.jcl new file mode 100644 index 00000000000..6bcd17c6b07 --- /dev/null +++ b/os/tpf/samples/linkdll.jcl @@ -0,0 +1,121 @@ +//APACH JOB MSGLEVEL=(1,1),CLASS=A,MSGCLASS=A +/*ROUTE PRINT XXXXXX.XXXXXX +/*ROUTE PUNCH XXXXXX.XXXXXX +/*NOTIFY XXXXXX.XXXXXX +//CCLE JCLLIB ORDER=(SYS1.CBC.SCBCPRC,SYS1.CEE.SCEEPROC) +//PRELINK EXEC EDCPL,COND.LKED=(0,NE), +// PPARM='OMVS,DLLNAME(pppp)', +// LREGSIZ='2048K', +// LPARM='AMODE=31,RMODE=ANY,LIST,XREF' +//PLKED.SYSLIB DD DISP=SHR,DSN=FSE0000.DEVP.STUB.OB +// DD DISP=SHR,DSN=FSE0000.DEVP.CLIB.OB +// DD DISP=SHR,DSN=ACP.CLIB.RLSE46.WEB +// DD DISP=SHR,DSN=ACP.STUB.RLSE46.WEB +// DD DISP=SHR,DSN=ACP.CLIB.RLSE40 +// DD DISP=SHR,DSN=ACP.STUB.RLSE40 +//PLKED.SYSDEFSD DD DSN=APA0000.DEVP.IMPORTS.DSD(ppppvv),DISP=SHR +//PLKED.DSD DD DSN=APA0000.DEVP.IMPORTS.DSD,DISP=SHR +//PLKED.OBJLIB DD DISP=SHR,DSN=FSE0000.DEVP.TEST.OB +// DD DISP=SHR,DSN=ACP.OBJ.RLSE46.WEB +// DD DISP=SHR,DSN=ACP.OBJ.INTG98.NBS +// DD DISP=SHR,DSN=ACP.MAIN.SYST.OBBSS +// DD DISP=SHR,DSN=ACP.DF.MAIN.SYST.OBBSS +// DD DISP=SHR,DSN=ACP.OBJ.RLSE40.BSS +//PLKED.OBJ1 DD PATH='/usr/local/apache/src/ap/ap_cpystrn.o' +//PLKED.OBJ2 DD PATH='/usr/local/apache/src/ap/ap_execve.o' +//PLKED.OBJ3 DD PATH='/usr/local/apache/src/ap/ap_signal.o' +//PLKED.OBJ4 DD PATH='/usr/local/apache/src/ap/ap_slack.o' +//PLKED.OBJ5 DD PATH='/usr/local/apache/src/ap/ap_snprintf.o' +//PLKED.OBJ6 DD PATH='/usr/local/apache/src/ap/ap_strings.o' +//PLKED.OBJ7 DD PATH='/usr/local/apache/src/os/tpf/ebcdic.o' +//PLKED.OBJ8 DD PATH='/usr/local/apache/src/os/tpf/os.o' +//PLKED.OBJ9 DD PATH='/usr/local/apache/src/os/tpf/os-inline.o' +//PLKED.OBJ10 DD PATH='/usr/local/apache/src/regex/regcomp.o' +//PLKED.OBJ11 DD PATH='/usr/local/apache/src/regex/regerror.o' +//PLKED.OBJ12 DD PATH='/usr/local/apache/src/regex/regexec.o' +//PLKED.OBJ13 DD PATH='/usr/local/apache/src/regex/regfree.o' +//PLKED.OBJ14 DD PATH='/usr/local/apache/src/main/alloc.o' +//PLKED.OBJ15 DD PATH='/usr/local/apache/src/main/buff.o' +//PLKED.OBJ16 DD PATH='/usr/local/apache/src/main/fnmatch.o' +//PLKED.OBJ17 DD PATH='/usr/local/apache/src/main/http_config.o' +//PLKED.OBJ18 DD PATH='/usr/local/apache/src/main/http_core.o' +//PLKED.OBJ19 DD PATH='/usr/local/apache/src/main/http_log.o' +//PLKED.OBJ20 DD PATH='/usr/local/apache/src/main/http_main.o' +//PLKED.OBJ21 DD PATH='/usr/local/apache/src/main/http_protocol.o' +//PLKED.OBJ22 DD PATH='/usr/local/apache/src/main/http_request.o' +//PLKED.OBJ23 DD PATH='/usr/local/apache/src/main/http_vhost.o' +//PLKED.OBJ24 DD PATH='/usr/local/apache/src/main/md5c.o' +//PLKED.OBJ25 DD PATH='/usr/local/apache/src/main/rfc1413.o' +//PLKED.OBJ26 DD PATH='/usr/local/apache/src/main/util.o' +//PLKED.OBJ27 DD PATH='/usr/local/apache/src/main/util_date.o' +//PLKED.OBJ28 DD PATH='/usr/local/apache/src/main/util_md5.o' +//PLKED.OBJ29 DD PATH='/usr/local/apache/src/main/util_script.o' +//PLKED.OBJ30 DD PATH='/usr/local/apache/src/main/util_uri.o' +//PLKED.OBJ31 DD PATH='/usr/local/apache/src/modules.o' +//PLKED.OBJ32 DD PATH='/usr/local/apache/src/buildmark.o' +//PLKED.OBJ33 DD PATH='/usr/local/apache/src/modules/standard/mod_auto\ +// index.o' +//PLKED.OBJ34 DD PATH='/usr/local/apache/src/modules/standard/mod_dir.\ +// o' +//PLKED.OBJ35 DD PATH='/usr/local/apache/src/modules/standard/mod_mime\ +// .o' +//PLKED.OBJ36 DD PATH='/usr/local/apache/src/modules/standard/mod_sete\ +// nvif.o' +//PLKED.OBJ37 DD PATH='/usr/local/apache/src/modules/standard/mod_alia\ +// s.o' +//PLKED.OBJ38 DD PATH='/usr/local/apache/src/modules/standard/mod_acce\ +// ss.o' +//PLKED.OBJ39 DD PATH='/usr/local/apache/src/modules/standard/mod_user\ +// dir.o' +//PLKED.OBJ40 DD PATH='/usr/local/apache/src/modules/standard/mod_spel\ +// ing.o' +//PLKED.OBJ41 DD PATH='/usr/local/apache/src/modules/standard/mod_nego\ +// tiation.o' +//PLKED.SYSIN DD * + ORDER @@DLMHDR + INCLUDE OBJLIB(CSTRTD40) + INCLUDE OBJ1 + INCLUDE OBJ2 + INCLUDE OBJ3 + INCLUDE OBJ4 + INCLUDE OBJ5 + INCLUDE OBJ6 + INCLUDE OBJ7 + INCLUDE OBJ8 + INCLUDE OBJ9 + INCLUDE OBJ10 + INCLUDE OBJ11 + INCLUDE OBJ12 + INCLUDE OBJ13 + INCLUDE OBJ14 + INCLUDE OBJ15 + INCLUDE OBJ16 + INCLUDE OBJ17 + INCLUDE OBJ18 + INCLUDE OBJ19 + INCLUDE OBJ20 + INCLUDE OBJ21 + INCLUDE OBJ22 + INCLUDE OBJ23 + INCLUDE OBJ24 + INCLUDE OBJ25 + INCLUDE OBJ26 + INCLUDE OBJ27 + INCLUDE OBJ28 + INCLUDE OBJ29 + INCLUDE OBJ30 + INCLUDE OBJ31 + INCLUDE OBJ32 + INCLUDE OBJ33 + INCLUDE OBJ34 + INCLUDE OBJ35 + INCLUDE OBJ36 + INCLUDE OBJ37 + INCLUDE OBJ38 + INCLUDE OBJ39 + INCLUDE OBJ40 + INCLUDE OBJ41 +/* +//*** WARNING *** NEVER change .LK to .OB in SYSLMOD!!! +//LKED.SYSLMOD DD DISP=OLD,DSN=xxxxxx.xxxx(ppppvv) +// diff --git a/os/tpf/samples/loadset.jcl b/os/tpf/samples/loadset.jcl new file mode 100644 index 00000000000..c0134d4cb82 --- /dev/null +++ b/os/tpf/samples/loadset.jcl @@ -0,0 +1,58 @@ +//OLDRWEB JOB MSGLEVEL=1,CLASS=A,MSGCLASS=S +//JOBCAT DD DSN=ICFCAT.ESAWK2,DISP=SHR +/*ROUTE PRINT xxxxxx.xxxxxxx +/*ROUTE PUNCH xxxxxx.xxxxxxx +//TLDR EXEC PGM=TPFLDRCA,REGION=8M, +// PARM='OLDR,SYS=ACP,CLMSIZE=8000000' +//STEPLIB DD DSN=ACP.LINK.RLSE46.WEB,DISP=SHR +// DD DSN=ACP.LINK.RLSE40.BSS,DISP=SHR +// DD DSN=VIS0000.DEVP.TEST.LK,DISP=SHR +// DD DSN=SYS1.CEE.SCEERUN,DISP=SHR +//SALTB DD DSN=ACP.SALTBL.RLSE46.WEB,DISP=SHR +// DD DSN=ACP.SALTBL.INTG46.WEB,DISP=SHR +//OBJLIB DD DSN=FSE0000.DEVP.TEST.OB,DISP=SHR +// DD DSN=APA0000.DEVP.TEST.OB,DISP=SHR +// DD DSN=ACP.DRVE.TEST.OB,DISP=SHR +// DD DSN=ACP.OBJ.RLSE46.WEB,DISP=SHR +// DD DSN=ACP.OBJ.INTG36.DRV,DISP=SHR +// DD DSN=ACP.OBJ.INTG46.WEB,DISP=SHR +// DD DSN=ACP.OBJ.INTG40.BSS,DISP=SHR +//LOADMOD DD DSN=FSE0000.DEVP.TEST.LK,DISP=SHR +// DD DSN=APA0000.DEVP.TEST.LK,DISP=SHR +// DD DSN=CWEISS.LINK,DISP=SHR +// DD DSN=ACP.DRVE.TEST.LK,DISP=SHR +// DD DSN=ACP.LINK.RLSE46.WEB,DISP=SHR +// DD DSN=ACP.LINK.INTG98.NBS,DISP=SHR +// DD DSN=ACP.LINK.INTG46.WEB,DISP=SHR +// DD DSN=ACP.LINK.INTG36.DRV,DISP=SHR +// DD DSN=ACP.LINK.INTG40.BSS,DISP=SHR +//LOADSUM DD DSN=&&LOADSUM,DISP=(NEW,PASS),UNIT=SYSDA, +// LRECL=133,SPACE=(TRK,(10,10)),RECFM=FBA +//CPRTEMP DD UNIT=SYSDA, +// DSN=&&CPRTEMP,SPACE=(TRK,(100,20)), +// DCB=(RECFM=FB,BLKSIZE=4095,LRECL=4095), +// DISP=(NEW,DELETE) +//PROGTEMP DD UNIT=SYSDA, +// DSN=&&PRTEMP,SPACE=(TRK,(100,20)), +// DCB=(RECFM=FB,BLKSIZE=4095,LRECL=4095), +// DISP=(NEW,DELETE) +//OUTPUT DD DSN=&&VRDROUT,DISP=(NEW,PASS),UNIT=SYSDA, +// DCB=(RECFM=F,BLKSIZE=4095,LRECL=4095) +//SYSUDUMP DD DUMMY +//SYSABEND DD DUMMY +//SYSOUT DD SYSOUT=A +//SYSPRINT DD SYSOUT=A +//PRINTER DD SYSOUT=A +//CEEDUMP DD SYSOUT=A +//SYSIN DD * +SYSID=BSS +PATVERS=NONE +SALVERS=40 +LOADER LOADSET lllllll +LOADER CALL PROG ppppvv +/* +//TRANSMIT EXEC PGM=IKJEFT01, +// PARM='TRANSMIT xxxxxx.xxxxxx DDNAME(SYSTSIN) NOLOG NONOTIFY SEQ' +//SYSTSIN DD UNIT=SYSDA, +// DSN=&&VRDROUT,DISP=(OLD,DELETE) +//SYSTSPRT DD DUMMY diff --git a/os/unix/.cvsignore b/os/unix/.cvsignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/os/unix/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/os/unix/Makefile.in b/os/unix/Makefile.in new file mode 100644 index 00000000000..82b2dfc8cad --- /dev/null +++ b/os/unix/Makefile.in @@ -0,0 +1,10 @@ + +DEPTH = ../.. +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +LTLIBRARY_NAME = libos.la +LTLIBRARY_SOURCES = os.c os-inline.c unixd.c iol_socket.c + +include $(topsrcdir)/build/ltlib.mk diff --git a/os/unix/config.m4 b/os/unix/config.m4 new file mode 100644 index 00000000000..86469135b60 --- /dev/null +++ b/os/unix/config.m4 @@ -0,0 +1,7 @@ +AC_CHECK_FUNCS( \ +setsid \ +killpg \ +) + +dnl XXX - This doesn't deal with _sys_siglist. Probably have to roll our own +AC_DECL_SYS_SIGLIST diff --git a/os/unix/os.h b/os/unix/os.h new file mode 100644 index 00000000000..6689dfae5c6 --- /dev/null +++ b/os/unix/os.h @@ -0,0 +1,147 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#include "ap_config.h" + +#ifndef PLATFORM +#define PLATFORM "Unix" +#endif + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c or os-inline.c + */ + +#if !defined(INLINE) && defined(USE_GNU_INLINE) +/* Compiler supports inline, so include the inlineable functions as + * part of the header + */ +#define INLINE extern ap_inline + +INLINE int ap_os_is_path_absolute(const char *file); + +#include "os-inline.c" + +#else + +/* Compiler does not support inline, so prototype the inlineable functions + * as normal + */ +extern int ap_os_is_path_absolute(const char *file); +#endif + +/* Other ap_os_ routines not used by this platform */ + +#define ap_os_is_filename_valid(f) (1) +#define ap_os_kill(pid, sig) kill(pid, sig) + +/* + * Abstraction layer for loading + * Apache modules under run-time via + * dynamic shared object (DSO) mechanism + */ + +#ifdef HAVE_DL_H +#include +#endif + +/* + * Do not use native AIX DSO support + */ +#ifdef AIX +#undef HAVE_DLFCN_H +#endif + +#ifdef HAVE_DLFCN_H +#include +#else +void *dlopen(const char *, int); +int dlclose(void *); +void *dlsym(void *, const char *); +const char *dlerror(void); +#endif + +/* probably on an older system that doesn't support RTLD_NOW or RTLD_LAZY. + * The below define is a lie since we are really doing RTLD_LAZY since the + * system doesn't support RTLD_NOW. + */ +#ifndef RTLD_NOW +#define RTLD_NOW 1 +#endif + +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +#if (defined(__FreeBSD__) ||\ + defined(__OpenBSD__) ||\ + defined(__NetBSD__) ) && !defined(__ELF__) +#define DLSYM_NEEDS_UNDERSCORE +#endif + +#define ap_os_dso_handle_t void * +void ap_os_dso_init(void); +void * ap_os_dso_load(const char *); +void ap_os_dso_unload(void *); +void * ap_os_dso_sym(void *, const char *); +const char *ap_os_dso_error(void); + +#endif /* !APACHE_OS_H */ diff --git a/os/unix/unixd.c b/os/unix/unixd.c new file mode 100644 index 00000000000..5af54ec011b --- /dev/null +++ b/os/unix/unixd.c @@ -0,0 +1,265 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_main.h" +#include "http_log.h" +#include "unixd.h" + +unixd_config_rec unixd_config; + +void unixd_detach(void) +{ + int x; + pid_t pgrp; + + chdir("/"); +#if !defined(MPE) && !defined(OS2) && !defined(TPF) +/* Don't detach for MPE because child processes can't survive the death of + the parent. */ + if ((x = fork()) > 0) + exit(0); + else if (x == -1) { + perror("fork"); + fprintf(stderr, "%s: unable to fork new process\n", ap_server_argv0); + exit(1); + } + RAISE_SIGSTOP(DETACH); +#endif +#ifndef NO_SETSID + if ((pgrp = setsid()) == -1) { + perror("setsid"); + fprintf(stderr, "%s: setsid failed\n", ap_server_argv0); + exit(1); + } +#elif defined(NEXT) || defined(NEWSOS) + if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "%s: setpgrp or getpgrp failed\n", ap_server_argv0); + exit(1); + } +#elif defined(OS2) || defined(TPF) + /* OS/2 and TPF don't support process group IDs */ + pgrp = getpid(); +#elif defined(MPE) + /* MPE uses negative pid for process group */ + pgrp = -getpid(); +#else + if ((pgrp = setpgrp(getpid(), 0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "%s: setpgrp failed\n", ap_server_argv0); + exit(1); + } +#endif + + /* close out the standard file descriptors */ + if (freopen("/dev/null", "r", stdin) == NULL) { + fprintf(stderr, "%s: unable to replace stdin with /dev/null: %s\n", + ap_server_argv0, strerror(errno)); + /* continue anyhow -- note we can't close out descriptor 0 because we + * have nothing to replace it with, and if we didn't have a descriptor + * 0 the next file would be created with that value ... leading to + * havoc. + */ + } + if (freopen("/dev/null", "w", stdout) == NULL) { + fprintf(stderr, "%s: unable to replace stdout with /dev/null: %s\n", + ap_server_argv0, strerror(errno)); + } + /* stderr is a tricky one, we really want it to be the error_log, + * but we haven't opened that yet. So leave it alone for now and it'll + * be reopened moments later. + */ +} + +/* Set group privileges. + * + * Note that we use the username as set in the config files, rather than + * the lookup of to uid --- the same uid may have multiple passwd entries, + * with different sets of groups for each. + */ + +static int set_group_privs(void) +{ + if (!geteuid()) { + char *name; + + /* Get username if passed as a uid */ + + if (unixd_config.user_name[0] == '#') { + struct passwd *ent; + uid_t uid = atoi(&unixd_config.user_name[1]); + + if ((ent = getpwuid(uid)) == NULL) { + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "getpwuid: couldn't determine user name from uid %u, " + "you probably need to modify the User directive", + (unsigned)uid); + return -1; + } + + name = ent->pw_name; + } + else + name = unixd_config.user_name; + +#if !defined(OS2) && !defined(TPF) + /* OS/2 and TPF don't support groups. */ + + /* + * Set the GID before initgroups(), since on some platforms + * setgid() is known to zap the group list. + */ + if (setgid(unixd_config.group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "setgid: unable to set group id to Group %u", + (unsigned)unixd_config.group_id); + return -1; + } + + /* Reset `groups' attributes. */ + + if (initgroups(name, unixd_config.group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "initgroups: unable to set groups for User %s " + "and Group %u", name, (unsigned)unixd_config.group_id); + return -1; + } +#endif /* !defined(OS2) && !defined(TPF) */ + } + return 0; +} + + +int unixd_setup_child(void) +{ + if (set_group_privs()) { + return -1; + } +#ifdef MPE + /* Only try to switch if we're running as MANAGER.SYS */ + if (geteuid() == 1 && unixd_config.user_id > 1) { + GETPRIVMODE(); + if (setuid(unixd_config.user_id) == -1) { + GETUSERMODE(); + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "setuid: unable to change uid"); + exit(1); + } + GETUSERMODE(); + } +#else + /* Only try to switch if we're running as root */ + if (!geteuid() && ( +#ifdef _OSD_POSIX + os_init_job_environment(server_conf, unixd_config.user_name, one_process) != 0 || +#endif + setuid(unixd_config.user_id) == -1)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "setuid: unable to change uid"); + return -1; + } +#endif + return 0; +} + + +const char *unixd_set_user(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + unixd_config.user_name = arg; + unixd_config.user_id = ap_uname2id(arg); +#if !defined (BIG_SECURITY_HOLE) && !defined (OS2) + if (unixd_config.user_id == 0) { + return "Error:\tApache has not been designed to serve pages while\n" + "\trunning as root. There are known race conditions that\n" + "\twill allow any local user to read any file on the system.\n" + "\tIf you still desire to serve pages as root then\n" + "\tadd -DBIG_SECURITY_HOLE to the EXTRA_CFLAGS line in your\n" + "\tsrc/Configuration file and rebuild the server. It is\n" + "\tstrongly suggested that you instead modify the User\n" + "\tdirective in your httpd.conf file to list a non-root\n" + "\tuser.\n"; + } +#endif + + return NULL; +} + +const char *unixd_set_group(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + unixd_config.group_id = ap_gname2id(arg); + + return NULL; +} + +void unixd_pre_config(void) +{ + unixd_config.user_name = DEFAULT_USER; + unixd_config.user_id = ap_uname2id(DEFAULT_USER); + unixd_config.group_id = ap_gname2id(DEFAULT_GROUP); +} diff --git a/os/unix/unixd.h b/os/unix/unixd.h new file mode 100644 index 00000000000..37cb2f95b69 --- /dev/null +++ b/os/unix/unixd.h @@ -0,0 +1,82 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef UNIXD_H +#define UNIXD_H + +/* common stuff that unix MPMs will want */ + +typedef struct { + char *user_name; + uid_t user_id; + gid_t group_id; +} unixd_config_rec; +extern unixd_config_rec unixd_config; + +void unixd_detach(void); +int unixd_setup_child(void); +void unixd_pre_config(void); +const char *unixd_set_user(cmd_parms *cmd, void *dummy, char *arg); +const char *unixd_set_group(cmd_parms *cmd, void *dummy, char *arg); + +#define UNIX_DAEMON_COMMANDS \ +{ "User", unixd_set_user, NULL, RSRC_CONF, TAKE1, \ + "Effective user id for this server"}, \ +{ "Group", unixd_set_group, NULL, RSRC_CONF, TAKE1, \ + "Effective group id for this server"}, \ + +#endif diff --git a/os/win32/BaseAddr.ref b/os/win32/BaseAddr.ref new file mode 100644 index 00000000000..7f5a7f66cae --- /dev/null +++ b/os/win32/BaseAddr.ref @@ -0,0 +1,24 @@ +-- Begin New BaseAddr.ref -- +; os/win32/BaseAddr.ref contains the central repository +; of all module base addresses +; to avoid relocation + +; WARNING: Update this file by reviewing the image size +; of the debug-generated dll files; release images +; should fit in the larger debug-sized space. + +; module name base-address max-size + +aprlib 0x6FFA0000 0x00060000 +ApacheCore 0x6FF00000 0x000A0000 +mod_auth_anon 0x6FEF0000 0x00010000 +mod_cern_meta 0x6FEE0000 0x00010000 +mod_digest 0x6FED0000 0x00010000 +mod_expires 0x6FEC0000 0x00010000 +mod_headers 0x6FEB0000 0x00010000 +mod_info 0x6FEA0000 0x00010000 +mod_rewrite 0x6FE80000 0x00020000 +mod_speling 0x6FE70000 0x00010000 +mod_status 0x6FE60000 0x00010000 +mod_usertrack 0x6FE50000 0x00010000 +mod_proxy 0x6FE30000 0x00020000 diff --git a/os/win32/modules.c b/os/win32/modules.c new file mode 100644 index 00000000000..d55d3d832c5 --- /dev/null +++ b/os/win32/modules.c @@ -0,0 +1,72 @@ +/* modules.c --- major modules compiled into Apache for Win32. + * Only insert an entry for a module if it must be compiled into + * the core server + */ + +#include "httpd.h" +#include "http_config.h" + +extern module core_module; +extern module so_module; +extern module mime_module; +extern module access_module; +extern module auth_module; +extern module negotiation_module; +extern module includes_module; +extern module autoindex_module; +extern module dir_module; +extern module cgi_module; +extern module userdir_module; +extern module alias_module; +extern module env_module; +extern module config_log_module; +extern module asis_module; +extern module imap_module; +extern module action_module; +extern module setenvif_module; +extern module isapi_module; + +module *ap_prelinked_modules[] = { + &core_module, + &so_module, + &mime_module, + &access_module, + &auth_module, + &negotiation_module, + &includes_module, + &autoindex_module, + &dir_module, + &cgi_module, + &userdir_module, + &alias_module, + &env_module, + &config_log_module, + &asis_module, + &imap_module, + &action_module, + &setenvif_module, + &isapi_module, + NULL +}; +module *ap_preloaded_modules[] = { + &core_module, + &so_module, + &mime_module, + &access_module, + &auth_module, + &negotiation_module, + &includes_module, + &autoindex_module, + &dir_module, + &cgi_module, + &userdir_module, + &alias_module, + &env_module, + &config_log_module, + &asis_module, + &imap_module, + &action_module, + &setenvif_module, + &isapi_module, + NULL +}; diff --git a/os/win32/os.h b/os/win32/os.h new file mode 100644 index 00000000000..80ffd7e65e3 --- /dev/null +++ b/os/win32/os.h @@ -0,0 +1,126 @@ +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#define PLATFORM "Win32" + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c + */ + +/* temporarily replace crypt */ +/* char *crypt(const char *pw, const char *salt); */ +#define crypt(buf,salt) (buf) + +/* Although DIR_TYPE is dirent (see nt/readdir.h) we need direct.h for + chdir() */ +#include + +#define STATUS +/*#define WIN32_LEAN_AND_MEAN Now defined in project files */ +#ifndef STRICT + #define STRICT +#endif +#define CASE_BLIND_FILESYSTEM +#define NO_WRITEV +#define NO_SETSID +#define NO_USE_SIGACTION +#define NO_TIMES +#define NO_GETTIMEOFDAY +//#define NEED_PROCESS_H although we do, this is specially handled in ap_config.h +#define USE_LONGJMP +#define HAVE_MMAP +#define USE_MMAP_SCOREBOARD +#define MULTITHREAD +#define HAVE_CANONICAL_FILENAME +#define HAVE_DRIVE_LETTERS +typedef int uid_t; +typedef int gid_t; +typedef int pid_t; +typedef int mode_t; +typedef char * caddr_t; + +/* +Define export types. API_EXPORT_NONSTD is a nasty hack to avoid having to declare +every configuration function as __stdcall. +*/ + +#ifdef SHARED_MODULE +# define API_VAR_EXPORT __declspec(dllimport) +# define API_EXPORT(type) __declspec(dllimport) type __stdcall +# define API_EXPORT_NONSTD(type) __declspec(dllimport) type +#else +# define API_VAR_EXPORT __declspec(dllexport) +# define API_EXPORT(type) __declspec(dllexport) type __stdcall +# define API_EXPORT_NONSTD(type) __declspec(dllexport) type +#endif +#define MODULE_VAR_EXPORT __declspec(dllexport) + +#define strcasecmp(s1, s2) stricmp(s1, s2) +#define strncasecmp(s1, s2, n) strnicmp(s1, s2, n) +#define lstat(x, y) stat(x, y) +#define S_ISLNK(m) (0) +#define S_ISREG(m) ((m & _S_IFREG) == _S_IFREG) +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(m) (((m)&(S_IFREG)) == (S_IFREG)) +#endif +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define JMP_BUF jmp_buf +#define sleep(t) Sleep(t*1000) +#define O_CREAT _O_CREAT +#define O_RDWR _O_RDWR +#define SIGPIPE 17 +/* Seems Windows is not a subgenius */ +#define NO_SLACK +#include + +#define NO_OTHER_CHILD +#define NO_RELIABLE_PIPED_LOGS + +__inline int ap_os_is_path_absolute(const char *file) +{ + /* For now, just do the same check that http_request.c and mod_alias.c + * do. + */ + return file[0] == '/' || file[1] == ':'; +} + +#define stat(f,ps) os_stat(f,ps) +API_EXPORT(int) os_stat(const char *szPath,struct stat *pStat); + +API_EXPORT(int) os_strftime(char *s, size_t max, const char *format, const struct tm *tm); + +#define _spawnv(mode,cmdname,argv) os_spawnv(mode,cmdname,argv) +#define spawnv(mode,cmdname,argv) os_spawnv(mode,cmdname,argv) +API_EXPORT(int) os_spawnv(int mode,const char *cmdname,const char *const *argv); +#define _spawnve(mode,cmdname,argv,envp) os_spawnve(mode,cmdname,argv,envp) +#define spawnve(mode,cmdname,argv,envp) os_spawnve(mode,cmdname,argv,envp) +API_EXPORT(int) os_spawnve(int mode,const char *cmdname,const char *const *argv,const char *const *envp); +#define _spawnle os_spawnle +#define spawnle os_spawnle +API_EXPORT(int) os_spawnle(int mode,const char *cmdname,...); + +/* OS-dependent filename routines in util_win32.c */ + +API_EXPORT(int) ap_os_is_filename_valid(const char *file); + +/* Abstractions for dealing with shared object files (DLLs on Win32). + * These are used by mod_so.c + */ +#define ap_os_dso_handle_t HINSTANCE +#define ap_os_dso_init() +#define ap_os_dso_load(l) LoadLibraryEx(l, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) +#define ap_os_dso_unload(l) FreeLibrary(l) +#define ap_os_dso_sym(h,s) GetProcAddress(h,s) +#define ap_os_dso_error() "" /* for now */ + +/* Other ap_os_ routines not used by this platform */ +#define ap_os_kill(pid, sig) kill(pid, sig) + +#endif /* ! APACHE_OS_H */ diff --git a/os/win32/util_win32.c b/os/win32/util_win32.c new file mode 100644 index 00000000000..348e0f9e2aa --- /dev/null +++ b/os/win32/util_win32.c @@ -0,0 +1,677 @@ +#include +#include +#include +#include +#include + +#include "httpd.h" +#include "http_log.h" + +/* Returns TRUE if the input string is a string + * of one or more '.' characters. + */ +static BOOL OnlyDots(char *pString) +{ + char *c; + + if (*pString == '\0') + return FALSE; + + for (c = pString;*c;c++) + if (*c != '.') + return FALSE; + + return TRUE; +} + +/* Accepts as input a pathname, and tries to match it to an + * existing path and return the pathname in the case that + * is present on the existing path. This routine also + * converts alias names to long names. + */ +API_EXPORT(char *) ap_os_systemcase_filename(pool *pPool, + const char *szFile) +{ + char buf[HUGE_STRING_LEN]; + char *pInputName; + char *p, *q; + BOOL bDone = FALSE; + BOOL bFileExists = TRUE; + HANDLE hFind; + WIN32_FIND_DATA wfd; + + if (!szFile || strlen(szFile) == 0 || strlen(szFile) >= sizeof(buf)) + return ap_pstrdup(pPool, ""); + + buf[0] = '\0'; + pInputName = ap_pstrdup(pPool, szFile); + + /* First convert all slashes to \ so Win32 calls work OK */ + for (p = pInputName; *p; p++) { + if (*p == '/') + *p = '\\'; + } + + p = pInputName; + /* If there is drive information, copy it over. */ + if (pInputName[1] == ':') { + buf[0] = tolower(*p++); + buf[1] = *p++; + buf[2] = '\0'; + + /* If all we have is a drive letter, then we are done */ + if (strlen(pInputName) == 2) + bDone = TRUE; + } + + q = p; + if (*p == '\\') { + p++; + if (*p == '\\') /* Possible UNC name */ + { + p++; + /* Get past the machine name. FindFirstFile */ + /* will not find a machine name only */ + p = strchr(p, '\\'); + if (p) + { + p++; + /* Get past the share name. FindFirstFile */ + /* will not find a \\machine\share name only */ + p = strchr(p, '\\'); + if (p) { + strncat(buf,q,p-q); + q = p; + p++; + } + } + + if (!p) + p = q; + } + } + + p = strchr(p, '\\'); + + while (!bDone) { + if (p) + *p = '\0'; + + if (strchr(q, '*') || strchr(q, '?')) + bFileExists = FALSE; + + /* If the path exists so far, call FindFirstFile + * again. However, if this portion of the path contains + * only '.' charaters, skip the call to FindFirstFile + * since it will convert '.' and '..' to actual names. + * Note: in the call to OnlyDots, we may have to skip + * a leading slash. + */ + if (bFileExists && !OnlyDots((*q == '.' ? q : q+1))) { + hFind = FindFirstFile(pInputName, &wfd); + + if (hFind == INVALID_HANDLE_VALUE) { + bFileExists = FALSE; + } + else { + FindClose(hFind); + + if (*q == '\\') + strcat(buf,"\\"); + strcat(buf, wfd.cFileName); + } + } + + if (!bFileExists || OnlyDots((*q == '.' ? q : q+1))) { + strcat(buf, q); + } + + if (p) { + q = p; + *p++ = '\\'; + p = strchr(p, '\\'); + } + else { + bDone = TRUE; + } + } + + /* First convert all slashes to / so server code handles it ok */ + for (p = buf; *p; p++) { + if (*p == '\\') + *p = '/'; + } + + return ap_pstrdup(pPool, buf); +} + + +/* Perform canonicalization with the exception that the + * input case is preserved. + */ +API_EXPORT(char *) ap_os_case_canonical_filename(pool *pPool, + const char *szFile) +{ + char *pNewStr; + char *s; + char *p; + char *q; + + if (szFile == NULL || strlen(szFile) == 0) + return ap_pstrdup(pPool, ""); + + pNewStr = ap_pstrdup(pPool, szFile); + + /* Change all '\' characters to '/' characters. + * While doing this, remove any trailing '.'. + * Also, blow away any directories with 3 or + * more '.' + */ + for (p = pNewStr,s = pNewStr; *s; s++,p++) { + if (*s == '\\' || *s == '/') { + + q = p; + while (p > pNewStr && *(p-1) == '.') + p--; + + if (p == pNewStr && q-p <= 2 && *p == '.') + p = q; + else if (p > pNewStr && p < q && *(p-1) == '/') { + if (q-p > 2) + p--; + else + p = q; + } + + *p = '/'; + } + else { + *p = *s; + } + } + *p = '\0'; + + /* Blow away any final trailing '.' since on Win32 + * foo.bat == foo.bat. == foo.bat... etc. + * Also blow away any trailing spaces since + * "filename" == "filename " + */ + q = p; + while (p > pNewStr && (*(p-1) == '.' || *(p-1) == ' ')) + p--; + if ((p > pNewStr) || + (p == pNewStr && q-p > 2)) + *p = '\0'; + + + /* One more security issue to deal with. Win32 allows + * you to create long filenames. However, alias filenames + * are always created so that the filename will + * conform to 8.3 rules. According to the Microsoft + * Developer's network CD (1/98) + * "Automatically generated aliases are composed of the + * first six characters of the filename plus ~n + * (where n is a number) and the first three characters + * after the last period." + * Here, we attempt to detect and decode these names. + */ + p = strchr(pNewStr, '~'); + if (p != NULL) { + char *pConvertedName, *pQstr, *pPstr; + char buf[HUGE_STRING_LEN]; + /* We potentially have a short name. Call + * ap_os_systemcase_filename to examine the filesystem + * and possibly extract the long name. + */ + pConvertedName = ap_os_systemcase_filename(pPool, pNewStr); + + /* Since we want to preserve the incoming case as much + * as we can, compare for differences in the string and + * only substitute in the path names that changed. + */ + if (stricmp(pNewStr, pConvertedName)) { + buf[0] = '\0'; + + q = pQstr = pConvertedName; + p = pPstr = pNewStr; + do { + q = strchr(q,'/'); + p = strchr(p,'/'); + + if (p != NULL) { + *q = '\0'; + *p = '\0'; + } + + if (stricmp(pQstr, pPstr)) + strcat(buf, pQstr); /* Converted name */ + else + strcat(buf, pPstr); /* Original name */ + + + if (p != NULL) { + pQstr = q; + pPstr = p; + *q++ = '/'; + *p++ = '/'; + } + + } while (p != NULL); + + pNewStr = ap_pstrdup(pPool, buf); + } + } + + + return pNewStr; +} + +/* Perform complete canonicalization. + */ +API_EXPORT(char *) ap_os_canonical_filename(pool *pPool, const char *szFile) +{ + char *pNewName; + pNewName = ap_os_case_canonical_filename(pPool, szFile); + strlwr(pNewName); + return pNewName; +} + +/* Win95 doesn't like trailing /s. NT and Unix don't mind. This works + * around the problem. + * Errr... except if it is UNC and we are referring to the root of + * the UNC, we MUST have a trailing \ and we can't use /s. Jeez. + * Not sure if this refers to all UNCs or just roots, + * but I'm going to fix it for all cases for now. (Ben) + */ + +#undef stat +API_EXPORT(int) os_stat(const char *szPath, struct stat *pStat) +{ + int n; + + if (strlen(szPath) == 0) { + return -1; + } + + if (szPath[0] == '/' && szPath[1] == '/') { + char buf[_MAX_PATH]; + char *s; + int nSlashes = 0; + + ap_assert(strlen(szPath) < _MAX_PATH); + strcpy(buf, szPath); + for (s = buf; *s; ++s) { + if (*s == '/') { + *s = '\\'; + ++nSlashes; + } + } + /* then we need to add one more to get \\machine\share\ */ + if (nSlashes == 3) { + *s++ = '\\'; + } + *s = '\0'; + return stat(buf, pStat); + } + + /* + * Below removes the trailing /, however, do not remove + * it in the case of 'x:/' or stat will fail + */ + n = strlen(szPath); + if ((szPath[n - 1] == '\\' || szPath[n - 1] == '/') && + !(n == 3 && szPath[1] == ':')) { + char buf[_MAX_PATH]; + + ap_assert(n < _MAX_PATH); + strcpy(buf, szPath); + buf[n - 1] = '\0'; + + return stat(buf, pStat); + } + return stat(szPath, pStat); +} + +/* Fix two really crap problems with Win32 spawn[lv]e*: + * + * 1. Win32 doesn't deal with spaces in argv. + * 2. Win95 doesn't like / in cmdname. + */ + +#undef _spawnv +API_EXPORT(int) os_spawnv(int mode, const char *cmdname, + const char *const *argv) +{ + int n; + char **aszArgs; + const char *szArg; + char *szCmd; + char *s; + + szCmd = _alloca(strlen(cmdname)+1); + strcpy(szCmd, cmdname); + for (s = szCmd; *s; ++s) { + if (*s == '/') { + *s = '\\'; + } + } + + for (n = 0; argv[n]; ++n) + ; + + aszArgs = _alloca((n + 1) * sizeof(const char *)); + + for (n = 0; szArg = argv[n]; ++n) { + if (strchr(szArg, ' ')) { + int l = strlen(szArg); + + aszArgs[n] = _alloca(l + 2 + 1); + aszArgs[n][0] = '"'; + strcpy(&aszArgs[n][1], szArg); + aszArgs[n][l + 1] = '"'; + aszArgs[n][l + 2] = '\0'; + } + else { + aszArgs[n] = (char *)szArg; + } + } + + aszArgs[n] = NULL; + + return _spawnv(mode, szCmd, aszArgs); +} + +#undef _spawnve +API_EXPORT(int) os_spawnve(int mode, const char *cmdname, + const char *const *argv, const char *const *envp) +{ + int n; + char **aszArgs; + const char *szArg; + char *szCmd; + char *s; + + szCmd = _alloca(strlen(cmdname)+1); + strcpy(szCmd, cmdname); + for (s = szCmd; *s; ++s) { + if (*s == '/') { + *s = '\\'; + } + } + + for (n = 0; argv[n]; ++n) + ; + + aszArgs = _alloca((n + 1)*sizeof(const char *)); + + for (n = 0; szArg = argv[n]; ++n){ + if (strchr(szArg, ' ')) { + int l = strlen(szArg); + + aszArgs[n] = _alloca(l + 2 + 1); + aszArgs[n][0] = '"'; + strcpy(&aszArgs[n][1], szArg); + aszArgs[n][l + 1] = '"'; + aszArgs[n][l + 2] = '\0'; + } + else { + aszArgs[n] = (char *)szArg; + } + } + + aszArgs[n] = NULL; + + return _spawnve(mode, szCmd, aszArgs, envp); +} + +API_EXPORT(int) os_spawnle(int mode, const char *cmdname, ...) +{ + int n; + va_list vlist; + char **aszArgs; + const char *szArg; + const char *const *aszEnv; + char *szCmd; + char *s; + + szCmd = _alloca(strlen(cmdname)+1); + strcpy(szCmd, cmdname); + for (s = szCmd; *s; ++s) { + if (*s == '/') { + *s = '\\'; + } + } + + va_start(vlist, cmdname); + for (n = 0; va_arg(vlist, const char *); ++n) + ; + va_end(vlist); + + aszArgs = _alloca((n + 1) * sizeof(const char *)); + + va_start(vlist, cmdname); + for (n = 0; szArg = va_arg(vlist, const char *); ++n) { + if (strchr(szArg, ' ')) { + int l = strlen(szArg); + + aszArgs[n] = _alloca(l + 2 + 1); + aszArgs[n][0] = '"'; + strcpy(&aszArgs[n][1], szArg); + aszArgs[n][l + 1] = '"'; + aszArgs[n][l + 2] = '\0'; + } + else { + aszArgs[n] = (char *)szArg; + } + } + + aszArgs[n] = NULL; + + aszEnv = va_arg(vlist, const char *const *); + va_end(vlist); + + return _spawnve(mode, szCmd, aszArgs, aszEnv); +} + +#undef strftime + +/* Partial replacement for strftime. This adds certain expandos to the + * Windows version + */ + +API_EXPORT(int) os_strftime(char *s, size_t max, const char *format, + const struct tm *tm) { + /* If the new format string is bigger than max, the result string probably + * won't fit anyway. When %-expandos are added, made sure the padding below + * is enough. + */ + char *new_format = (char *) _alloca(max + 11); + size_t i, j, format_length = strlen(format); + int return_value; + int length_written; + + for (i = 0, j = 0; (i < format_length && j < max);) { + if (format[i] != '%') { + new_format[j++] = format[i++]; + continue; + } + switch (format[i+1]) { + case 'D': + /* Is this locale dependent? Shouldn't be... + Also note the year 2000 exposure here */ + memcpy(new_format + j, "%m/%d/%y", 8); + i += 2; + j += 8; + break; + case 'r': + memcpy(new_format + j, "%I:%M:%S %p", 11); + i += 2; + j += 11; + break; + case 'T': + memcpy(new_format + j, "%H:%M:%S", 8); + i += 2; + j += 8; + break; + case 'e': + length_written = ap_snprintf(new_format + j, max - j, "%2d", + tm->tm_mday); + j = (length_written == -1) ? max : (j + length_written); + i += 2; + break; + default: + /* We know we can advance two characters forward here. */ + new_format[j++] = format[i++]; + new_format[j++] = format[i++]; + } + } + if (j >= max) { + *s = '\0'; /* Defensive programming, okay since output is undefined */ + return_value = 0; + } else { + new_format[j] = '\0'; + return_value = strftime(s, max, new_format, tm); + } + return return_value; +} + +/* + * ap_os_is_filename_valid is given a filename, and returns 0 if the filename + * is not valid for use on this system. On Windows, this means it fails any + * of the tests below. Otherwise returns 1. + * + * Test for filename validity on Win32. This is of tests come in part from + * the MSDN article at "Technical Articles, Windows Platform, Base Services, + * Guidelines, Making Room for Long Filenames" although the information + * in MSDN about filename testing is incomplete or conflicting. There is a + * similar set of tests in "Technical Articles, Windows Platform, Base Services, + * Guidelines, Moving Unix Applications to Windows NT". + * + * The tests are: + * + * 1) total path length greater than MAX_PATH + * + * 2) anything using the octets 0-31 or characters " < > | : + * (these are reserved for Windows use in filenames. In addition + * each file system has its own additional characters that are + * invalid. See KB article Q100108 for more details). + * + * 3) anything ending in "." (no matter how many) + * (filename doc, doc. and doc... all refer to the same file) + * + * 4) any segment in which the basename (before first period) matches + * one of the DOS device names + * (the list comes from KB article Q100108 although some people + * reports that additional names such as "COM5" are also special + * devices). + * + * If the path fails ANY of these tests, the result must be to deny access. + */ + +API_EXPORT(int) ap_os_is_filename_valid(const char *file) +{ + const char *segstart; + unsigned int seglength; + const char *pos; + static const char * const invalid_characters = "?\"<>*|:"; + static const char * const invalid_filenames[] = { + "CON", "AUX", "COM1", "COM2", "COM3", + "COM4", "LPT1", "LPT2", "LPT3", "PRN", "NUL", NULL + }; + + /* Test 1 */ + if (strlen(file) > MAX_PATH) { + /* Path too long for Windows. Note that this test is not valid + * if the path starts with //?/ or \\?\. */ + return 0; + } + + pos = file; + + /* Skip any leading non-path components. This can be either a + * drive letter such as C:, or a UNC path such as \\SERVER\SHARE\. + * We continue and check the rest of the path based on the rules above. + * This means we could eliminate valid filenames from servers which + * are not running NT (such as Samba). + */ + + if (pos[0] && pos[1] == ':') { + /* Skip leading drive letter */ + pos += 2; + } + else { + if ((pos[0] == '\\' || pos[0] == '/') && + (pos[1] == '\\' || pos[1] == '/')) { + /* Is a UNC, so skip the server name and share name */ + pos += 2; + while (*pos && *pos != '/' && *pos != '\\') + pos++; + if (!*pos) { + /* No share name */ + return 0; + } + pos++; /* Move to start of share name */ + while (*pos && *pos != '/' && *pos != '\\') + pos++; + if (!*pos) { + /* No path information */ + return 0; + } + } + } + + while (*pos) { + unsigned int idx; + unsigned int baselength; + + while (*pos == '/' || *pos == '\\') { + pos++; + } + if (*pos == '\0') { + break; + } + segstart = pos; /* start of segment */ + while (*pos && *pos != '/' && *pos != '\\') { + pos++; + } + seglength = pos - segstart; + /* + * Now we have a segment of the path, starting at position "segstart" + * and length "seglength" + */ + + /* Test 2 */ + for (idx = 0; idx < seglength; idx++) { + if ((segstart[idx] > 0 && segstart[idx] < 32) || + strchr(invalid_characters, segstart[idx])) { + return 0; + } + } + + /* Test 3 */ + if (segstart[seglength-1] == '.') { + return 0; + } + + /* Test 4 */ + for (baselength = 0; baselength < seglength; baselength++) { + if (segstart[baselength] == '.') { + break; + } + } + + /* baselength is the number of characters in the base path of + * the segment (which could be the same as the whole segment length, + * if it does not include any dot characters). */ + if (baselength == 3 || baselength == 4) { + for (idx = 0; invalid_filenames[idx]; idx++) { + if (strlen(invalid_filenames[idx]) == baselength && + !strnicmp(invalid_filenames[idx], segstart, baselength)) { + return 0; + } + } + } + } + + return 1; +} diff --git a/server/.cvsignore b/server/.cvsignore new file mode 100644 index 00000000000..f8193cb91be --- /dev/null +++ b/server/.cvsignore @@ -0,0 +1,13 @@ +Makefile +uri_delims.h +gen_uri_delims +test_char.h +gen_test_char +gen_uri_delims_R +gen_test_char_R +gen_uri_delims_D +gen_uri_delims.ilk +gen_uri_delims.pdb +gen_test_char_D +gen_test_char.ilk +gen_test_char.pdb diff --git a/server/.indent.pro b/server/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/server/.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/server/Makefile.in b/server/Makefile.in new file mode 100644 index 00000000000..86facf2fddb --- /dev/null +++ b/server/Makefile.in @@ -0,0 +1,32 @@ + +DEPTH = .. +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +LTLIBRARY_NAME = libmain.la +LTLIBRARY_SOURCES = \ + uri_delims.h test_char.h \ + buff.c http_config.c http_core.c http_log.c http_main.c \ + http_protocol.c http_request.c http_vhost.c util.c util_date.c \ + util_script.c util_uri.c util_md5.c \ + rfc1413.c http_connection.c iol_file.c listen.c + +include $(topsrcdir)/build/ltlib.mk + +gen_uri_delims_OBJECTS = gen_uri_delims.lo +gen_uri_delims: $(gen_uri_delims_OBJECTS) + $(LINK) $(gen_uri_delims_OBJECTS) + +gen_test_char_OBJECTS = gen_test_char.lo +gen_test_char: $(gen_test_char_OBJECTS) + $(LINK) $(gen_test_char_OBJECTS) + +uri_delims.h: gen_uri_delims + ./gen_uri_delims > uri_delims.h + +test_char.h: gen_test_char + ./gen_test_char > test_char.h + +util_uri.c: uri_delims.h +util.c: test_char.h diff --git a/server/buildmark.c b/server/buildmark.c new file mode 100644 index 00000000000..575b9731a09 --- /dev/null +++ b/server/buildmark.c @@ -0,0 +1,70 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#include "ap_config.h" +#include "httpd.h" + +#if defined(__DATE__) && defined(__TIME__) +static const char server_built[] = __DATE__ " " __TIME__; +#else +static const char server_built[] = "unknown"; +#endif + +API_EXPORT(const char *) ap_get_server_built() +{ + return server_built; +} diff --git a/server/config.c b/server/config.c new file mode 100644 index 00000000000..8980f98ec48 --- /dev/null +++ b/server/config.c @@ -0,0 +1,1641 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_config.c: once was auxillary functions for reading httpd's config + * file and converting filenames into a namespace + * + * Rob McCool + * + * Wall-to-wall rewrite for Apache... commands which are part of the + * server core can now be found next door in "http_core.c". Now contains + * general command loop, and functions which do bookkeeping for the new + * Apache config stuff (modules and configuration vectors). + * + * rst + * + */ + +#define CORE_PRIVATE + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" /* for errors in parse_htaccess */ +#include "http_request.h" /* for default_handler (see invoke_handler) */ +#include "http_conf_globals.h" /* Sigh... */ +#include "http_vhost.h" +#include "explain.h" + +DEF_Explain + +/**************************************************************** + * + * We begin with the functions which deal with the linked list + * of modules which control just about all of the server operation. + */ + +/* total_modules is the number of modules that have been linked + * into the server. + */ +static int total_modules = 0; +/* dynamic_modules is the number of modules that have been added + * after the pre-loaded ones have been set up. It shouldn't be larger + * than DYNAMIC_MODULE_LIMIT. + */ +static int dynamic_modules = 0; +API_VAR_EXPORT module *top_module = NULL; +API_VAR_EXPORT module **ap_loaded_modules=NULL; + +typedef int (*handler_func) (request_rec *); +typedef void *(*dir_maker_func) (pool *, char *); +typedef void *(*merger_func) (pool *, void *, void *); + +/* Dealing with config vectors. These are associated with per-directory, + * per-server, and per-request configuration, and have a void* pointer for + * each modules. The nature of the structure pointed to is private to the + * module in question... the core doesn't (and can't) know. However, there + * are defined interfaces which allow it to create instances of its private + * per-directory and per-server structures, and to merge the per-directory + * structures of a directory and its subdirectory (producing a new one in + * which the defaults applying to the base directory have been properly + * overridden). + */ + +#ifndef ap_get_module_config +API_EXPORT(void *) ap_get_module_config(void *conf_vector, module *m) +{ + void **confv = (void **) conf_vector; + return confv[m->module_index]; +} +#endif + +#ifndef ap_set_module_config +API_EXPORT(void) ap_set_module_config(void *conf_vector, module *m, void *val) +{ + void **confv = (void **) conf_vector; + confv[m->module_index] = val; +} +#endif + +static void *create_empty_config(pool *p) +{ + void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) * + (total_modules + DYNAMIC_MODULE_LIMIT)); + return (void *) conf_vector; +} + +static void *create_default_per_dir_config(pool *p) +{ + void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) * (total_modules + DYNAMIC_MODULE_LIMIT)); + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + dir_maker_func df = modp->create_dir_config; + + if (df) + conf_vector[modp->module_index] = (*df) (p, NULL); + } + + return (void *) conf_vector; +} + +void * + ap_merge_per_dir_configs(pool *p, void *base, void *new) +{ + void **conf_vector = (void **) ap_palloc(p, sizeof(void *) * total_modules); + void **base_vector = (void **) base; + void **new_vector = (void **) new; + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + merger_func df = modp->merge_dir_config; + int i = modp->module_index; + + if (df && new_vector[i]) + conf_vector[i] = (*df) (p, base_vector[i], new_vector[i]); + else + conf_vector[i] = new_vector[i] ? new_vector[i] : base_vector[i]; + } + + return (void *) conf_vector; +} + +static void *create_server_config(pool *p, server_rec *s) +{ + void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) * (total_modules + DYNAMIC_MODULE_LIMIT)); + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + if (modp->create_server_config) + conf_vector[modp->module_index] = (*modp->create_server_config) (p, s); + } + + return (void *) conf_vector; +} + +static void merge_server_configs(pool *p, void *base, void *virt) +{ + /* Can reuse the 'virt' vector for the spine of it, since we don't + * have to deal with the moral equivalent of .htaccess files here... + */ + + void **base_vector = (void **) base; + void **virt_vector = (void **) virt; + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + merger_func df = modp->merge_server_config; + int i = modp->module_index; + + if (!virt_vector[i]) + virt_vector[i] = base_vector[i]; + else if (df) + virt_vector[i] = (*df) (p, base_vector[i], virt_vector[i]); + } +} + +void *ap_create_request_config(pool *p) +{ + return create_empty_config(p); +} + +CORE_EXPORT(void *) ap_create_per_dir_config(pool *p) +{ + return create_empty_config(p); +} + +#ifdef EXPLAIN + +struct { + int offset; + char *method; +} aMethods[] = + +{ +#define m(meth) { XtOffsetOf(module,meth),#meth } + m(translate_handler), + m(ap_check_user_id), + m(auth_checker), + m(type_checker), + m(fixer_upper), + m(logger), + { -1, "?" }, +#undef m +}; + +char *ShowMethod(module *modp, int offset) +{ + int n; + static char buf[200]; + + for (n = 0; aMethods[n].offset >= 0; ++n) + if (aMethods[n].offset == offset) + break; + ap_snprintf(buf, sizeof(buf), "%s:%s", modp->name, aMethods[n].method); + return buf; +} +#else +#define ShowMethod(modp,offset) +#endif + +/**************************************************************** + * + * Dispatch through the modules to find handlers for various phases + * of request handling. These are invoked by http_request.c to actually + * do the dirty work of slogging through the module structures. + */ + +/* + * Optimized run_method routines. The observation here is that many modules + * have NULL for most of the methods. So we build optimized lists of + * everything. If you think about it, this is really just like a sparse array + * implementation to avoid scanning the zero entries. + */ +static const int method_offsets[] = +{ + XtOffsetOf(module, translate_handler), + XtOffsetOf(module, ap_check_user_id), + XtOffsetOf(module, auth_checker), + XtOffsetOf(module, access_checker), + XtOffsetOf(module, type_checker), + XtOffsetOf(module, fixer_upper), + XtOffsetOf(module, logger), + XtOffsetOf(module, header_parser), + XtOffsetOf(module, post_read_request) +}; +#define NMETHODS (sizeof (method_offsets)/sizeof (method_offsets[0])) + +static struct { + int translate_handler; + int ap_check_user_id; + int auth_checker; + int access_checker; + int type_checker; + int fixer_upper; + int logger; + int header_parser; + int post_read_request; +} offsets_into_method_ptrs; + +/* + * This is just one big array of method_ptrs. It's constructed such that, + * for example, method_ptrs[ offsets_into_method_ptrs.logger ] is the first + * logger function. You go one-by-one from there until you hit a NULL. + * This structure was designed to hopefully maximize cache-coolness. + */ +static handler_func *method_ptrs; + +/* routine to reconstruct all these shortcuts... called after every + * add_module. + * XXX: this breaks if modules dink with their methods pointers + */ +static void build_method_shortcuts(void) +{ + module *modp; + int how_many_ptrs; + int i; + int next_ptr; + handler_func fp; + + if (method_ptrs) { + /* free up any previous set of method_ptrs */ + free(method_ptrs); + } + + /* first we count how many functions we have */ + how_many_ptrs = 0; + for (modp = top_module; modp; modp = modp->next) { + for (i = 0; i < NMETHODS; ++i) { + if (*(handler_func *) (method_offsets[i] + (char *) modp)) { + ++how_many_ptrs; + } + } + } + method_ptrs = malloc((how_many_ptrs + NMETHODS) * sizeof(handler_func)); + if (method_ptrs == NULL) { + fprintf(stderr, "Ouch! Out of memory in build_method_shortcuts()!\n"); + } + next_ptr = 0; + for (i = 0; i < NMETHODS; ++i) { + /* XXX: This is an itsy bit presumptuous about the alignment + * constraints on offsets_into_method_ptrs. I can't remember if + * ANSI says this has to be true... -djg */ + ((int *) &offsets_into_method_ptrs)[i] = next_ptr; + for (modp = top_module; modp; modp = modp->next) { + fp = *(handler_func *) (method_offsets[i] + (char *) modp); + if (fp) { + method_ptrs[next_ptr++] = fp; + } + } + method_ptrs[next_ptr++] = NULL; + } +} + + +static int run_method(request_rec *r, int offset, int run_all) +{ + int i; + + for (i = offset; method_ptrs[i]; ++i) { + handler_func mod_handler = method_ptrs[i]; + + if (mod_handler) { + int result; + + result = (*mod_handler) (r); + + if (result != DECLINED && (!run_all || result != OK)) + return result; + } + } + + return run_all ? OK : DECLINED; +} + +int ap_translate_name(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.translate_handler, 0); +} + +int ap_check_access(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.access_checker, 1); +} + +int ap_find_types(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.type_checker, 0); +} + +int ap_run_fixups(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.fixer_upper, 1); +} + +int ap_log_transaction(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.logger, 1); +} + +int ap_header_parse(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.header_parser, 1); +} + +int ap_run_post_read_request(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.post_read_request, 1); +} + +/* Auth stuff --- anything that defines one of these will presumably + * want to define something for the other. Note that check_auth is + * separate from check_access to make catching some config errors easier. + */ + +int ap_check_user_id(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.ap_check_user_id, 0); +} + +int ap_check_auth(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.auth_checker, 0); +} + +/* + * For speed/efficiency we generate a compact list of all the handlers + * and wildcard handlers. This means we won't have to scan the entire + * module list looking for handlers... where we'll find a whole whack + * of NULLs. + */ +typedef struct { + handler_rec hr; + size_t len; +} fast_handler_rec; + +static fast_handler_rec *handlers; +static fast_handler_rec *wildhandlers; + +static void init_handlers(pool *p) +{ + module *modp; + int nhandlers = 0; + int nwildhandlers = 0; + const handler_rec *handp; + fast_handler_rec *ph, *pw; + char *starp; + + for (modp = top_module; modp; modp = modp->next) { + if (!modp->handlers) + continue; + for (handp = modp->handlers; handp->content_type; ++handp) { + if (strchr(handp->content_type, '*')) { + nwildhandlers ++; + } else { + nhandlers ++; + } + } + } + ph = handlers = ap_palloc(p, sizeof(*ph)*(nhandlers + 1)); + pw = wildhandlers = ap_palloc(p, sizeof(*pw)*(nwildhandlers + 1)); + for (modp = top_module; modp; modp = modp->next) { + if (!modp->handlers) + continue; + for (handp = modp->handlers; handp->content_type; ++handp) { + if ((starp = strchr(handp->content_type, '*'))) { + pw->hr.content_type = handp->content_type; + pw->hr.handler = handp->handler; + pw->len = starp - handp->content_type; + pw ++; + } else { + ph->hr.content_type = handp->content_type; + ph->hr.handler = handp->handler; + ph->len = strlen(handp->content_type); + ph ++; + } + } + } + pw->hr.content_type = NULL; + pw->hr.handler = NULL; + ph->hr.content_type = NULL; + ph->hr.handler = NULL; +} + +int ap_invoke_handler(request_rec *r) +{ + fast_handler_rec *handp; + const char *handler; + char *p; + size_t handler_len; + int result = HTTP_INTERNAL_SERVER_ERROR; + + if (r->handler) { + handler = r->handler; + handler_len = strlen(handler); + } + else { + handler = r->content_type ? r->content_type : ap_default_type(r); + if ((p = strchr(handler, ';')) != NULL) { /* MIME type arguments */ + while (p > handler && p[-1] == ' ') + --p; /* strip trailing spaces */ + handler_len = p - handler; + } + else { + handler_len = strlen(handler); + } + } + + /* Pass one --- direct matches */ + + for (handp = handlers; handp->hr.content_type; ++handp) { + if (handler_len == handp->len + && !strncmp(handler, handp->hr.content_type, handler_len)) { + result = (*handp->hr.handler) (r); + + if (result != DECLINED) + return result; + } + } + + if (result == HTTP_INTERNAL_SERVER_ERROR && r->handler) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, + "handler \"%s\" not found for: %s", r->handler, r->filename); + } + + /* Pass two --- wildcard matches */ + + for (handp = wildhandlers; handp->hr.content_type; ++handp) { + if (handler_len >= handp->len + && !strncmp(handler, handp->hr.content_type, handp->len)) { + result = (*handp->hr.handler) (r); + + if (result != DECLINED) + return result; + } + } + + return HTTP_INTERNAL_SERVER_ERROR; +} + +/* One-time setup for precompiled modules --- NOT to be done on restart */ + +API_EXPORT(void) ap_add_module(module *m) +{ + /* This could be called from an AddModule httpd.conf command, + * after the file has been linked and the module structure within it + * teased out... + */ + + if (m->version != MODULE_MAGIC_NUMBER_MAJOR) { + fprintf(stderr, "%s: module \"%s\" is not compatible with this " + "version of Apache.\n", ap_server_argv0, m->name); + fprintf(stderr, "Please contact the vendor for the correct version.\n"); + exit(1); + } + + if (m->next == NULL) { + m->next = top_module; + top_module = m; + } + if (m->module_index == -1) { + m->module_index = total_modules++; + dynamic_modules++; + + if (dynamic_modules > DYNAMIC_MODULE_LIMIT) { + fprintf(stderr, "%s: module \"%s\" could not be loaded, because" + " the dynamic\n", ap_server_argv0, m->name); + fprintf(stderr, "module limit was reached. Please increase " + "DYNAMIC_MODULE_LIMIT and recompile.\n"); + exit(1); + } + } + + /* Some C compilers put a complete path into __FILE__, but we want + * only the filename (e.g. mod_includes.c). So check for path + * components (Unix and DOS), and remove them. + */ + + if (strrchr(m->name, '/')) + m->name = 1 + strrchr(m->name, '/'); + if (strrchr(m->name, '\\')) + m->name = 1 + strrchr(m->name, '\\'); + +#ifdef _OSD_POSIX /* __FILE__="*POSIX(/home/martin/apache/src/modules/standard/mod_info.c)" */ + /* We cannot fix the string in-place, because it's const */ + if (m->name[strlen(m->name)-1]==')') { + char *tmp = strdup(m->name); /* FIXME:memory leak, albeit a small one */ + tmp[strlen(tmp)-1] = '\0'; + m->name = tmp; + } +#endif /*_OSD_POSIX*/ +} + +/* + * remove_module undoes what add_module did. There are some caveats: + * when the module is removed, its slot is lost so all the current + * per-dir and per-server configurations are invalid. So we should + * only ever call this function when you are invalidating almost + * all our current data. I.e. when doing a restart. + */ + +API_EXPORT(void) ap_remove_module(module *m) +{ + module *modp; + + modp = top_module; + if (modp == m) { + /* We are the top module, special case */ + top_module = modp->next; + m->next = NULL; + } + else { + /* Not the top module, find use. When found modp will + * point to the module _before_ us in the list + */ + + while (modp && modp->next != m) { + modp = modp->next; + } + if (!modp) { + /* Uh-oh, this module doesn't exist */ + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL, + "Cannot remove module %s: not found in module list", + m->name); + return; + } + /* Eliminate us from the module list */ + modp->next = modp->next->next; + } + + m->module_index = -1; /* simulate being unloaded, should + * be unnecessary */ + dynamic_modules--; + total_modules--; +} + +API_EXPORT(void) ap_add_loaded_module(module *mod) +{ + module **m; + + /* + * Add module pointer to top of chained module list + */ + ap_add_module(mod); + + /* + * And module pointer to list of loaded modules + * + * Notes: 1. ap_add_module() would already complain if no more space + * exists for adding a dynamically loaded module + * 2. ap_add_module() accepts double-inclusion, so we have + * to accept this, too. + */ + for (m = ap_loaded_modules; *m != NULL; m++) + ; + *m++ = mod; + *m = NULL; +} + +API_EXPORT(void) ap_remove_loaded_module(module *mod) +{ + module **m; + module **m2; + int done; + + /* + * Remove module pointer from chained module list + */ + ap_remove_module(mod); + + /* + * Remove module pointer from list of loaded modules + * + * Note: 1. We cannot determine if the module was successfully + * removed by ap_remove_module(). + * 2. We have not to complain explicity when the module + * is not found because ap_remove_module() did it + * for us already. + */ + for (m = m2 = ap_loaded_modules, done = 0; *m2 != NULL; m2++) { + if (*m2 == mod && done == 0) + done = 1; + else + *m++ = *m2; + } + *m = NULL; +} + +void ap_setup_prelinked_modules() +{ + module **m; + module **m2; + + /* + * Initialise total_modules variable and module indices + */ + total_modules = 0; + for (m = ap_preloaded_modules; *m != NULL; m++) + (*m)->module_index = total_modules++; + + /* + * Initialise list of loaded modules + */ + ap_loaded_modules = (module **)malloc( + sizeof(module *)*(total_modules+DYNAMIC_MODULE_LIMIT+1)); + if (ap_loaded_modules == NULL) { + fprintf(stderr, "Ouch! Out of memory in ap_setup_prelinked_modules()!\n"); + } + for (m = ap_preloaded_modules, m2 = ap_loaded_modules; *m != NULL; ) + *m2++ = *m++; + *m2 = NULL; + + /* + * Initialize chain of linked (=activate) modules + */ + for (m = ap_prelinked_modules; *m != NULL; m++) + ap_add_module(*m); +} + +API_EXPORT(const char *) ap_find_module_name(module *m) +{ + return m->name; +} + +API_EXPORT(module *) ap_find_linked_module(const char *name) +{ + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + if (strcmp(modp->name, name) == 0) + return modp; + } + return NULL; +} + +/* Add a named module. Returns 1 if module found, 0 otherwise. */ +API_EXPORT(int) ap_add_named_module(const char *name) +{ + module *modp; + int i = 0; + + for (modp = ap_loaded_modules[i]; modp; modp = ap_loaded_modules[++i]) { + if (strcmp(modp->name, name) == 0) { + /* Only add modules that are not already enabled. */ + if (modp->next == NULL) { + ap_add_module(modp); + } + return 1; + } + } + + return 0; +} + +/* Clear the internal list of modules, in preparation for starting over. */ +API_EXPORT(void) ap_clear_module_list() +{ + module **m = &top_module; + module **next_m; + + while (*m) { + next_m = &((*m)->next); + *m = NULL; + m = next_m; + } + + /* This is required; so we add it always. */ + ap_add_named_module("http_core.c"); +} + +/***************************************************************** + * + * Resource, access, and .htaccess config files now parsed by a common + * command loop. + * + * Let's begin with the basics; parsing the line and + * invoking the function... + */ + +static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms, + void *mconfig, const char *args) +{ + char *w, *w2, *w3; + const char *errmsg; + + if ((parms->override & cmd->req_override) == 0) + return ap_pstrcat(parms->pool, cmd->name, " not allowed here", NULL); + + parms->info = cmd->cmd_data; + parms->cmd = cmd; + + switch (cmd->args_how) { + case RAW_ARGS: + return ((const char *(*)(cmd_parms *, void *, const char *)) + (cmd->func)) (parms, mconfig, args); + + case NO_ARGS: + if (*args != 0) + return ap_pstrcat(parms->pool, cmd->name, " takes no arguments", + NULL); + + return ((const char *(*)(cmd_parms *, void *)) + (cmd->func)) (parms, mconfig); + + case TAKE1: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, " takes one argument", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *)) + (cmd->func)) (parms, mconfig, w); + + case TAKE2: + + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, " takes two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *)) (cmd->func)) (parms, mconfig, w, w2); + + case TAKE12: + + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *)) (cmd->func)) (parms, mconfig, w, + *w2 ? w2 : NULL); + + case TAKE3: + + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + w3 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, " takes three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *, const char *)) (cmd->func)) (parms, + mconfig, w, w2, w3); + + case TAKE23: + + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, + " takes two or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *, const char *)) (cmd->func)) (parms, + mconfig, w, w2, w3); + + case TAKE123: + + w = ap_getword_conf(parms->pool, &args); + w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, + " takes one, two or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *, const char *)) (cmd->func)) (parms, + mconfig, w, w2, w3); + + case TAKE13: + + w = ap_getword_conf(parms->pool, &args); + w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || (*w2 && !w3) || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, + " takes one or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *, const char *)) (cmd->func)) (parms, + mconfig, w, w2, w3); + + case ITERATE: + + while (*(w = ap_getword_conf(parms->pool, &args)) != '\0') + if ((errmsg = ((const char *(*)(cmd_parms *, void *, + const char *)) (cmd->func)) (parms, mconfig, w))) + return errmsg; + + return NULL; + + case ITERATE2: + + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args == 0) + return ap_pstrcat(parms->pool, cmd->name, + " requires at least two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + + while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0') + if ((errmsg = ((const char *(*)(cmd_parms *, void *, + const char *, const char *)) (cmd->func)) (parms, + mconfig, w, w2))) + return errmsg; + + return NULL; + + case FLAG: + + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off"))) + return ap_pstrcat(parms->pool, cmd->name, " must be On or Off", + NULL); + + return ((const char *(*)(cmd_parms *, void *, int)) + (cmd->func)) (parms, mconfig, strcasecmp(w, "off") != 0); + + default: + + return ap_pstrcat(parms->pool, cmd->name, + " is improperly configured internally (server bug)", + NULL); + } +} + +CORE_EXPORT(const command_rec *) ap_find_command(const char *name, const command_rec *cmds) +{ + while (cmds->name) + if (!strcasecmp(name, cmds->name)) + return cmds; + else + ++cmds; + + return NULL; +} + +CORE_EXPORT(const command_rec *) ap_find_command_in_modules(const char *cmd_name, module **mod) +{ + const command_rec *cmdp; + module *modp; + + for (modp = *mod; modp; modp = modp->next) + if (modp->cmds && (cmdp = ap_find_command(cmd_name, modp->cmds))) { + *mod = modp; + return cmdp; + } + + return NULL; +} + +CORE_EXPORT(void *) ap_set_config_vectors(cmd_parms *parms, void *config, module *mod) +{ + void *mconfig = ap_get_module_config(config, mod); + void *sconfig = ap_get_module_config(parms->server->module_config, mod); + + if (!mconfig && mod->create_dir_config) { + mconfig = (*mod->create_dir_config) (parms->pool, parms->path); + ap_set_module_config(config, mod, mconfig); + } + + if (!sconfig && mod->create_server_config) { + sconfig = (*mod->create_server_config) (parms->pool, parms->server); + ap_set_module_config(parms->server->module_config, mod, sconfig); + } + return mconfig; +} + +CORE_EXPORT(const char *) ap_handle_command(cmd_parms *parms, void *config, const char *l) +{ + void *oldconfig; + const char *args, *cmd_name, *retval; + const command_rec *cmd; + module *mod = top_module; + + if ((l[0] == '#') || (!l[0])) + return NULL; + + args = l; + cmd_name = ap_getword_conf(parms->temp_pool, &args); + if (*cmd_name == '\0') + return NULL; + + oldconfig = parms->context; + parms->context = config; + do { + if (!(cmd = ap_find_command_in_modules(cmd_name, &mod))) { + errno = EINVAL; + return ap_pstrcat(parms->pool, "Invalid command '", cmd_name, + "', perhaps mis-spelled or defined by a module " + "not included in the server configuration", NULL); + } + else { + void *mconfig = ap_set_config_vectors(parms,config, mod); + + retval = invoke_cmd(cmd, parms, mconfig, args); + mod = mod->next; /* Next time around, skip this one */ + } + } while (retval && !strcmp(retval, DECLINE_CMD)); + parms->context = oldconfig; + + return retval; +} + +API_EXPORT(const char *) ap_srm_command_loop(cmd_parms *parms, void *config) +{ + char l[MAX_STRING_LEN]; + + while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) { + const char *errmsg = ap_handle_command(parms, config, l); + if (errmsg) { + return errmsg; + } + } + + return NULL; +} + +/* + * Generic command functions... + */ + +API_EXPORT_NONSTD(const char *) ap_set_string_slot(cmd_parms *cmd, + char *struct_ptr, char *arg) +{ + /* This one's pretty generic... */ + + int offset = (int) (long) cmd->info; + *(char **) (struct_ptr + offset) = arg; + return NULL; +} + +API_EXPORT_NONSTD(const char *) ap_set_string_slot_lower(cmd_parms *cmd, + char *struct_ptr, char *arg) +{ + /* This one's pretty generic... */ + + int offset = (int) (long) cmd->info; + ap_str_tolower(arg); + *(char **) (struct_ptr + offset) = arg; + return NULL; +} + +API_EXPORT_NONSTD(const char *) ap_set_flag_slot(cmd_parms *cmd, + char *struct_ptr, int arg) +{ + /* This one's pretty generic too... */ + + int offset = (int) (long) cmd->info; + *(int *) (struct_ptr + offset) = arg ? 1 : 0; + return NULL; +} + +API_EXPORT_NONSTD(const char *) ap_set_file_slot(cmd_parms *cmd, char *struct_ptr, char *arg) +{ + /* Prepend server_root to relative arg. + This allows .htaccess to be independent of server_root, + so the server can be moved or mirrored with less pain. */ + char *p; + int offset = (int) (long) cmd->info; + if (ap_os_is_path_absolute(arg)) + p = arg; + else + p = ap_make_full_path(cmd->pool, ap_server_root, arg); + *(char **) (struct_ptr + offset) = p; + return NULL; +} + +/***************************************************************** + * + * Reading whole config files... + */ + +static cmd_parms default_parms = +{NULL, 0, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +API_EXPORT(char *) ap_server_root_relative(pool *p, char *file) +{ + if(ap_os_is_path_absolute(file)) + return file; + return ap_make_full_path(p, ap_server_root, file); +} + + +/* This structure and the following functions are needed for the + * table-based config file reading. They are passed to the + * cfg_open_custom() routine. + */ + +/* Structure to be passed to cfg_open_custom(): it contains an + * index which is incremented from 0 to nelts on each call to + * cfg_getline() (which in turn calls arr_elts_getstr()) + * and an array_header pointer for the string array. + */ +typedef struct { + array_header *array; + int curr_idx; +} arr_elts_param_t; + + +/* arr_elts_getstr() returns the next line from the string array. */ +static void *arr_elts_getstr(void *buf, size_t bufsiz, void *param) +{ + arr_elts_param_t *arr_param = (arr_elts_param_t *) param; + + /* End of array reached? */ + if (++arr_param->curr_idx > arr_param->array->nelts) + return NULL; + + /* return the line */ + ap_cpystrn(buf, ((char **) arr_param->array->elts)[arr_param->curr_idx - 1], bufsiz); + + return buf; +} + + +/* arr_elts_close(): dummy close routine (makes sure no more lines can be read) */ +static int arr_elts_close(void *param) +{ + arr_elts_param_t *arr_param = (arr_elts_param_t *) param; + arr_param->curr_idx = arr_param->array->nelts; + return 0; +} + +static void process_command_config(server_rec *s, array_header *arr, pool *p, + pool *ptemp) +{ + const char *errmsg; + cmd_parms parms; + arr_elts_param_t arr_parms; + + arr_parms.curr_idx = 0; + arr_parms.array = arr; + + parms = default_parms; + parms.pool = p; + parms.temp_pool = ptemp; + parms.server = s; + parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); + parms.config_file = ap_pcfg_open_custom(p, "-c/-C directives", + &arr_parms, NULL, + arr_elts_getstr, arr_elts_close); + + errmsg = ap_srm_command_loop(&parms, s->lookup_defaults); + + if (errmsg) { + fprintf(stderr, "Syntax error in -C/-c directive:\n%s\n", errmsg); + exit(1); + } + + ap_cfg_closefile(parms.config_file); +} + +void ap_process_resource_config(server_rec *s, char *fname, pool *p, pool *ptemp) +{ + const char *errmsg; + cmd_parms parms; + struct stat finfo; + + fname = ap_server_root_relative(p, fname); + + if (!(strcmp(fname, ap_server_root_relative(p, RESOURCE_CONFIG_FILE))) || + !(strcmp(fname, ap_server_root_relative(p, ACCESS_CONFIG_FILE)))) { + if (stat(fname, &finfo) == -1) + return; + } + + /* don't require conf/httpd.conf if we have a -C or -c switch */ + if((ap_server_pre_read_config->nelts || ap_server_post_read_config->nelts) && + !(strcmp(fname, ap_server_root_relative(p, SERVER_CONFIG_FILE)))) { + if (stat(fname, &finfo) == -1) + return; + } + + /* GCC's initialization extensions are soooo nice here... */ + + parms = default_parms; + parms.pool = p; + parms.temp_pool = ptemp; + parms.server = s; + parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); + + if (!(parms.config_file = ap_pcfg_openfile(p,fname))) { + perror("fopen"); + fprintf(stderr, "%s: could not open document config file %s\n", + ap_server_argv0, fname); + exit(1); + } + + errmsg = ap_srm_command_loop(&parms, s->lookup_defaults); + + if (errmsg) { + fprintf(stderr, "Syntax error on line %d of %s:\n", + parms.config_file->line_number, parms.config_file->name); + fprintf(stderr, "%s\n", errmsg); + exit(1); + } + + ap_cfg_closefile(parms.config_file); +} + + +int ap_parse_htaccess(void **result, request_rec *r, int override, + const char *d, const char *access_name) +{ + configfile_t *f = NULL; + cmd_parms parms; + const char *errmsg; + char *filename = NULL; + const struct htaccess_result *cache; + struct htaccess_result *new; + void *dc = NULL; + +/* firstly, search cache */ + for (cache = r->htaccess; cache != NULL; cache = cache->next) + if (cache->override == override && strcmp(cache->dir, d) == 0) { + if (cache->htaccess != NULL) + *result = cache->htaccess; + return OK; + } + + parms = default_parms; + parms.override = override; + parms.pool = r->pool; + parms.temp_pool = r->pool; + parms.server = r->server; + parms.path = ap_pstrdup(r->pool, d); + + /* loop through the access names and find the first one */ + + while (access_name[0]) { + filename = ap_make_full_path(r->pool, d, + ap_getword_conf(r->pool, &access_name)); + + if ((f = ap_pcfg_openfile(r->pool, filename)) != NULL) { + + dc = ap_create_per_dir_config(r->pool); + + parms.config_file = f; + + errmsg = ap_srm_command_loop(&parms, dc); + + ap_cfg_closefile(f); + + if (errmsg) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, r, + "%s: %s", filename, errmsg); + return HTTP_INTERNAL_SERVER_ERROR; + } + *result = dc; + break; + } + else if (errno != ENOENT && errno != ENOTDIR) { + ap_log_rerror(APLOG_MARK, APLOG_CRIT, r, + "%s pcfg_openfile: unable to check htaccess file, " + "ensure it is readable", + filename); + ap_table_setn(r->notes, "error-notes", + "Server unable to read htaccess file, denying " + "access to be safe"); + return HTTP_FORBIDDEN; + } + } + +/* cache it */ + new = ap_palloc(r->pool, sizeof(struct htaccess_result)); + new->dir = parms.path; + new->override = override; + new->htaccess = dc; +/* add to head of list */ + new->next = r->htaccess; + r->htaccess = new; + + return OK; +} + + +CORE_EXPORT(const char *) ap_init_virtual_host(pool *p, const char *hostname, + server_rec *main_server, server_rec **ps) +{ + server_rec *s = (server_rec *) ap_pcalloc(p, sizeof(server_rec)); + +#ifdef RLIMIT_NOFILE + struct rlimit limits; + + getrlimit(RLIMIT_NOFILE, &limits); + if (limits.rlim_cur < limits.rlim_max) { + limits.rlim_cur += 2; + if (setrlimit(RLIMIT_NOFILE, &limits) < 0) { + perror("setrlimit(RLIMIT_NOFILE)"); + fprintf(stderr, "Cannot exceed hard limit for open files"); + } + } +#endif + + s->server_admin = NULL; + s->server_hostname = NULL; + s->error_fname = NULL; + s->srm_confname = NULL; + s->access_confname = NULL; + s->timeout = 0; + s->keep_alive_timeout = 0; + s->keep_alive = -1; + s->keep_alive_max = -1; + s->error_log = main_server->error_log; + s->loglevel = main_server->loglevel; + /* useful default, otherwise we get a port of 0 on redirects */ + s->port = main_server->port; + s->next = NULL; + + s->is_virtual = 1; + s->names = ap_make_array(p, 4, sizeof(char **)); + s->wild_names = ap_make_array(p, 4, sizeof(char **)); + + s->module_config = create_empty_config(p); + s->lookup_defaults = ap_create_per_dir_config(p); + + s->server_uid = ap_user_id; + s->server_gid = ap_group_id; + + s->limit_req_line = main_server->limit_req_line; + s->limit_req_fieldsize = main_server->limit_req_fieldsize; + s->limit_req_fields = main_server->limit_req_fields; + + *ps = s; + + return ap_parse_vhost_addrs(p, hostname, s); +} + + +static void fixup_virtual_hosts(pool *p, server_rec *main_server) +{ + server_rec *virt; + + for (virt = main_server->next; virt; virt = virt->next) { + merge_server_configs(p, main_server->module_config, + virt->module_config); + + virt->lookup_defaults = + ap_merge_per_dir_configs(p, main_server->lookup_defaults, + virt->lookup_defaults); + + if (virt->server_admin == NULL) + virt->server_admin = main_server->server_admin; + + if (virt->srm_confname == NULL) + virt->srm_confname = main_server->srm_confname; + + if (virt->access_confname == NULL) + virt->access_confname = main_server->access_confname; + + if (virt->timeout == 0) + virt->timeout = main_server->timeout; + + if (virt->keep_alive_timeout == 0) + virt->keep_alive_timeout = main_server->keep_alive_timeout; + + if (virt->keep_alive == -1) + virt->keep_alive = main_server->keep_alive; + + if (virt->keep_alive_max == -1) + virt->keep_alive_max = main_server->keep_alive_max; + + if (virt->send_buffer_size == 0) + virt->send_buffer_size = main_server->send_buffer_size; + + /* XXX: this is really something that should be dealt with by a + * post-config api phase */ + ap_core_reorder_directories(p, virt); + } + ap_core_reorder_directories(p, main_server); +} + +/***************************************************************** + * + * Getting *everything* configured... + */ + +static void init_config_globals(pool *p) +{ + /* ServerRoot, server_confname set in httpd.c */ + + ap_standalone = 1; + ap_user_name = DEFAULT_USER; + ap_user_id = ap_uname2id(DEFAULT_USER); + ap_group_id = ap_gname2id(DEFAULT_GROUP); + ap_daemons_to_start = DEFAULT_START_DAEMON; + ap_daemons_min_free = DEFAULT_MIN_FREE_DAEMON; + ap_daemons_max_free = DEFAULT_MAX_FREE_DAEMON; + ap_daemons_limit = HARD_SERVER_LIMIT; + ap_pid_fname = DEFAULT_PIDLOG; + ap_scoreboard_fname = DEFAULT_SCOREBOARD; + ap_lock_fname = DEFAULT_LOCKFILE; + ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; + ap_bind_address.s_addr = htonl(INADDR_ANY); + ap_listeners = NULL; + ap_listenbacklog = DEFAULT_LISTENBACKLOG; + ap_extended_status = 0; + + /* Global virtual host hash bucket pointers. Init to null. */ + ap_init_vhost_config(p); + + ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); +} + +static server_rec *init_server_config(pool *p) +{ + server_rec *s = (server_rec *) ap_pcalloc(p, sizeof(server_rec)); + + s->port = 0; + s->server_admin = DEFAULT_ADMIN; + s->server_hostname = NULL; + s->error_fname = DEFAULT_ERRORLOG; + s->error_log = stderr; + s->loglevel = DEFAULT_LOGLEVEL; + s->srm_confname = RESOURCE_CONFIG_FILE; + s->access_confname = ACCESS_CONFIG_FILE; + s->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE; + s->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE; + s->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS; + s->timeout = DEFAULT_TIMEOUT; + s->keep_alive_timeout = DEFAULT_KEEPALIVE_TIMEOUT; + s->keep_alive_max = DEFAULT_KEEPALIVE; + s->keep_alive = 1; + s->next = NULL; + s->addrs = ap_pcalloc(p, sizeof(server_addr_rec)); + /* NOT virtual host; don't match any real network interface */ + s->addrs->host_addr.s_addr = htonl(INADDR_ANY); + s->addrs->host_port = 0; /* matches any port */ + s->addrs->virthost = ""; /* must be non-NULL */ + s->names = s->wild_names = NULL; + + s->module_config = create_server_config(p, s); + s->lookup_defaults = create_default_per_dir_config(p); + + return s; +} + + +static void default_listeners(pool *p, server_rec *s) +{ + listen_rec *new; + + if (ap_listeners != NULL) { + return; + } + /* allocate a default listener */ + new = ap_pcalloc(p, sizeof(listen_rec)); + new->local_addr.sin_family = AF_INET; + new->local_addr.sin_addr = ap_bind_address; + new->local_addr.sin_port = htons(s->port ? s->port : DEFAULT_HTTP_PORT); + new->fd = -1; + new->used = 0; + new->next = NULL; + ap_listeners = new; +} + + +server_rec *ap_read_config(pool *p, pool *ptemp, char *confname) +{ + server_rec *s = init_server_config(p); + + init_config_globals(p); + + /* All server-wide config files now have the SAME syntax... */ + + process_command_config(s, ap_server_pre_read_config, p, ptemp); + + ap_process_resource_config(s, confname, p, ptemp); + ap_process_resource_config(s, s->srm_confname, p, ptemp); + ap_process_resource_config(s, s->access_confname, p, ptemp); + + process_command_config(s, ap_server_post_read_config, p, ptemp); + + fixup_virtual_hosts(p, s); + default_listeners(p, s); + ap_fini_vhost_config(p, s); + + return s; +} + +void ap_single_module_configure(pool *p, server_rec *s, module *m) +{ + if (m->create_server_config) + ap_set_module_config(s->module_config, m, + (*m->create_server_config)(p, s)); + if (m->create_dir_config) + ap_set_module_config(s->lookup_defaults, m, + (*m->create_dir_config)(p, NULL)); +} + +void ap_init_modules(pool *p, server_rec *s) +{ + module *m; + + for (m = top_module; m; m = m->next) + if (m->init) + (*m->init) (s, p); + build_method_shortcuts(); + init_handlers(p); +} + +void ap_child_init_modules(pool *p, server_rec *s) +{ + module *m; + + for (m = top_module; m; m = m->next) + if (m->child_init) + (*m->child_init) (s, p); +} + +void ap_child_exit_modules(pool *p, server_rec *s) +{ + module *m; + +#ifdef SIGHUP + signal(SIGHUP, SIG_IGN); +#endif +#ifdef SIGUSR1 + signal(SIGUSR1, SIG_IGN); +#endif + + for (m = top_module; m; m = m->next) + if (m->child_exit) + (*m->child_exit) (s, p); + +} + +/******************************************************************** + * Configuration directives are restricted in terms of where they may + * appear in the main configuration files and/or .htaccess files according + * to the bitmask req_override in the command_rec structure. + * If any of the overrides set in req_override are also allowed in the + * context in which the command is read, then the command is allowed. + * The context is determined as follows: + * + * inside *.conf --> override = (RSRC_CONF|OR_ALL)&~(OR_AUTHCFG|OR_LIMIT); + * within or --> override = OR_ALL|ACCESS_CONF; + * within .htaccess --> override = AllowOverride for current directory; + * + * the result is, well, a rather confusing set of possibilities for when + * a particular directive is allowed to be used. This procedure prints + * in English where the given (pc) directive can be used. + */ +static void show_overrides(const command_rec *pc, module *pm) +{ + int n = 0; + + printf("\tAllowed in *.conf "); + if ((pc->req_override & (OR_OPTIONS | OR_FILEINFO | OR_INDEXES)) || + ((pc->req_override & RSRC_CONF) && + ((pc->req_override & (ACCESS_CONF | OR_AUTHCFG | OR_LIMIT))))) + printf("anywhere"); + else if (pc->req_override & RSRC_CONF) + printf("only outside , or "); + else + printf("only inside , or "); + + /* Warn if the directive is allowed inside or .htaccess + * but module doesn't support per-dir configuration */ + + if ((pc->req_override & (OR_ALL | ACCESS_CONF)) && !pm->create_dir_config) + printf(" [no per-dir config]"); + + if (pc->req_override & OR_ALL) { + printf(" and in .htaccess\n\twhen AllowOverride"); + + if ((pc->req_override & OR_ALL) == OR_ALL) + printf(" isn't None"); + else { + printf(" includes "); + + if (pc->req_override & OR_AUTHCFG) { + if (n++) + printf(" or "); + printf("AuthConfig"); + } + if (pc->req_override & OR_LIMIT) { + if (n++) + printf(" or "); + printf("Limit"); + } + if (pc->req_override & OR_OPTIONS) { + if (n++) + printf(" or "); + printf("Options"); + } + if (pc->req_override & OR_FILEINFO) { + if (n++) + printf(" or "); + printf("FileInfo"); + } + if (pc->req_override & OR_INDEXES) { + if (n++) + printf(" or "); + printf("Indexes"); + } + } + } + printf("\n"); +} + +/* Show the preloaded configuration directives, the help string explaining + * the directive arguments, in what module they are handled, and in + * what parts of the configuration they are allowed. Used for httpd -h. + */ +void ap_show_directives() +{ + const command_rec *pc; + int n; + + for (n = 0; ap_loaded_modules[n]; ++n) + for (pc = ap_loaded_modules[n]->cmds; pc && pc->name; ++pc) { + printf("%s (%s)\n", pc->name, ap_loaded_modules[n]->name); + if (pc->errmsg) + printf("\t%s\n", pc->errmsg); + show_overrides(pc, ap_loaded_modules[n]); + } +} + +/* Show the preloaded module names. Used for httpd -l. */ +void ap_show_modules() +{ + int n; + + printf("Compiled-in modules:\n"); + for (n = 0; ap_loaded_modules[n]; ++n) + printf(" %s\n", ap_loaded_modules[n]->name); +} diff --git a/server/config.m4 b/server/config.m4 new file mode 100644 index 00000000000..e37203db85a --- /dev/null +++ b/server/config.m4 @@ -0,0 +1,53 @@ +dnl ## Check for libraries + +AC_CHECK_LIB(nsl, gethostname, [ + AC_ADD_LIBRARY(nsl) ], []) + +AC_CHECK_LIB(socket, socket, [ + AC_ADD_LIBRARY(socket) ], []) + +AC_CHECK_LIB(nsl, gethostbyaddr, [ + AC_ADD_LIBRARY(nsl) ], []) + +AC_CHECK_LIB(crypt, crypt, [ + AC_ADD_LIBRARY(crypt) + AC_DEFINE(HAVE_CRYPT)], []) + +AC_CHECK_LIB(c, crypt, [ + AC_DEFINE(HAVE_CRYPT)], []) + +dnl ## Check for header files + +AC_HEADER_STDC +AC_CHECK_HEADERS( +bstring.h \ +crypt.h \ +unistd.h \ +sys/resource.h \ +sys/select.h \ +sys/processor.h \ +) + +dnl ## Check for typedefs, structures, and compiler characteristics. + +AC_CACHE_CHECK([for tm_gmtoff in struct tm], ac_cv_struct_tm_gmtoff, +[AC_TRY_COMPILE([#include +#include <$ac_cv_struct_tm>], [struct tm tm; tm.tm_gmtoff;], + ac_cv_struct_tm_gmtoff=yes, ac_cv_struct_tm_gmtoff=no)]) + +if test "$ac_cv_struct_tm_gmtoff" = "yes"; then + AC_DEFINE(HAVE_GMTOFF) +fi + +dnl ## Check for library functions + +AC_CHECK_FUNCS( +difftime \ +syslog \ +) +AC_FUNC_MMAP + +dnl XXX - is autoconf's detection routine good enough? +if test "$ac_cv_func_mmap_fixed_mapped" = "yes"; then + AC_DEFINE(USE_MMAP_FILES) +fi diff --git a/server/gen_test_char.c b/server/gen_test_char.c new file mode 100644 index 00000000000..dc33a73d86b --- /dev/null +++ b/server/gen_test_char.c @@ -0,0 +1,62 @@ +/* we need some of the portability definitions... for strchr */ +#include "httpd.h" + +/* A bunch of functions in util.c scan strings looking for certain characters. + * To make that more efficient we encode a lookup table. + */ +#define T_ESCAPE_SHELL_CMD (0x01) +#define T_ESCAPE_PATH_SEGMENT (0x02) +#define T_OS_ESCAPE_PATH (0x04) +#define T_HTTP_TOKEN_STOP (0x08) + +int main(int argc, char *argv[]) +{ + unsigned c; + unsigned char flags; + + printf( +"/* this file is automatically generated by gen_test_char, do not edit */\n" +"#define T_ESCAPE_SHELL_CMD (%u)\n" +"#define T_ESCAPE_PATH_SEGMENT (%u)\n" +"#define T_OS_ESCAPE_PATH (%u)\n" +"#define T_HTTP_TOKEN_STOP (%u)\n" +"\n" +"static const unsigned char test_char_table[256] = {\n" +" 0,", + T_ESCAPE_SHELL_CMD, + T_ESCAPE_PATH_SEGMENT, + T_OS_ESCAPE_PATH, + T_HTTP_TOKEN_STOP); + + /* we explicitly dealt with NUL above + * in case some strchr() do bogosity with it */ + + for (c = 1; c < 256; ++c) { + flags = 0; + if (c % 20 == 0) + printf("\n "); + + /* escape_shell_cmd */ + if (strchr("&;`'\"|*?~<>^()[]{}$\\\n", c)) { + flags |= T_ESCAPE_SHELL_CMD; + } + + if (!ap_isalnum(c) && !strchr("$-_.+!*'(),:@&=~", c)) { + flags |= T_ESCAPE_PATH_SEGMENT; + } + + if (!ap_isalnum(c) && !strchr("$-_.+!*'(),:@&=/~", c)) { + flags |= T_OS_ESCAPE_PATH; + } + + /* these are the "tspecials" from RFC2068 */ + if (ap_iscntrl(c) || strchr(" \t()<>@,;:\\/[]?={}", c)) { + flags |= T_HTTP_TOKEN_STOP; + } + printf("%u%c", flags, (c < 255) ? ',' : ' '); + + } + printf("\n};\n"); + + return 0; +} diff --git a/server/gen_test_char.dsp b/server/gen_test_char.dsp new file mode 100644 index 00000000000..c2630ce9d14 --- /dev/null +++ b/server/gen_test_char.dsp @@ -0,0 +1,105 @@ +# Microsoft Developer Studio Project File - Name="gen_test_char" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=gen_test_char - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "gen_test_char.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 "gen_test_char.mak" CFG="gen_test_char - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "gen_test_char - Win32 Release" (based on\ + "Win32 (x86) Console Application") +!MESSAGE "gen_test_char - Win32 Debug" (based on\ + "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "gen_test_char - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "gen_test" +# PROP BASE Intermediate_Dir "gen_test" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "gen_test_char_R" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +SOURCE=$(InputPath) +PostBuild_Desc=Create test_char.h +PostBuild_Cmds=.\gen_test_char > test_char.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "gen_test_char - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "gen_tes0" +# PROP BASE Intermediate_Dir "gen_tes0" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "gen_test_char_D" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +SOURCE=$(InputPath) +PostBuild_Desc=Create test_char.h +PostBuild_Cmds=.\gen_test_char > test_char.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "gen_test_char - Win32 Release" +# Name "gen_test_char - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\gen_test_char.c +# End Source File +# End Group +# End Target +# End Project diff --git a/server/listen.c b/server/listen.c new file mode 100644 index 00000000000..9f7c121b734 --- /dev/null +++ b/server/listen.c @@ -0,0 +1,310 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "ap_listen.h" +#include "http_log.h" + +ap_listen_rec *ap_listeners; +static ap_listen_rec *old_listeners; +static int ap_listenbacklog; +static int send_buffer_size; + +/* TODO: make_sock is just begging and screaming for APR abstraction */ +static int make_sock(const struct sockaddr_in *server) +{ + int s; + int one = 1; + char addr[512]; + + if (server->sin_addr.s_addr != htonl(INADDR_ANY)) + ap_snprintf(addr, sizeof(addr), "address %s port %d", + inet_ntoa(server->sin_addr), ntohs(server->sin_port)); + else + ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port)); + + if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, NULL, + "make_sock: failed to get a socket for %s", addr); + return -1; + } + +#ifdef SO_REUSEADDR + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, NULL, + "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr); + close(s); + return -1; + } +#endif + one = 1; +#ifdef SO_KEEPALIVE + if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, NULL, + "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr); + close(s); + return -1; + } +#endif + + /* + * To send data over high bandwidth-delay connections at full + * speed we must force the TCP window to open wide enough to keep the + * pipe full. The default window size on many systems + * is only 4kB. Cross-country WAN connections of 100ms + * at 1Mb/s are not impossible for well connected sites. + * If we assume 100ms cross-country latency, + * a 4kB buffer limits throughput to 40kB/s. + * + * To avoid this problem I've added the SendBufferSize directive + * to allow the web master to configure send buffer size. + * + * The trade-off of larger buffers is that more kernel memory + * is consumed. YMMV, know your customers and your network! + * + * -John Heidemann 25-Oct-96 + * + * If no size is specified, use the kernel default. + */ +#ifndef SO_SNDBUF + if (send_buffer_size) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, + (char *) &send_buffer_size, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "make_sock: failed to set SendBufferSize for %s, " + "using default", addr); + /* not a fatal error */ + } + } +#endif + + if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, NULL, + "make_sock: could not bind to %s", addr); + close(s); + return -1; + } + + if (listen(s, ap_listenbacklog) == -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, NULL, + "make_sock: unable to listen for connections on %s", addr); + close(s); + return -1; + } + + return s; +} + + +static void close_listeners_on_exec(void *v) +{ + ap_listen_rec *lr; + + for (lr = ap_listeners; lr; lr = lr->next) { + close(lr->fd); + } +} + + +static void alloc_listener(struct sockaddr_in *local_addr) +{ + ap_listen_rec **walk; + ap_listen_rec *new; + + /* see if we've got an old listener for this address:port */ + for (walk = &old_listeners; *walk; walk = &(*walk)->next) { + if (!memcmp(&(*walk)->local_addr, local_addr, sizeof(local_addr))) { + /* re-use existing record */ + new = *walk; + *walk = new->next; + new->next = ap_listeners; + ap_listeners = new; + return; + } + } + + /* this has to survive restarts */ + new = malloc(sizeof(ap_listen_rec)); + new->local_addr = *local_addr; + new->fd = -1; + new->next = ap_listeners; + ap_listeners = new; +} + + +int ap_listen_open(pool *pconf, unsigned port) +{ + ap_listen_rec *lr; + ap_listen_rec *next; + int num_open; + struct sockaddr_in local_addr; + + /* allocate a default listener if necessary */ + if (ap_listeners == NULL) { + local_addr.sin_family = AF_INET; + local_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* XXX */ + local_addr.sin_port = htons(port ? port : DEFAULT_HTTP_PORT); + alloc_listener(&local_addr); + } + + num_open = 0; + for (lr = ap_listeners; lr; lr = lr->next) { + if (lr->fd < 0) { + lr->fd = make_sock(&lr->local_addr); + } + if (lr->fd >= 0) { + ++num_open; + } + } + + /* close the old listeners */ + for (lr = old_listeners; lr; lr = next) { + close(lr->fd); + next = lr->next; + free(lr); + } + old_listeners = NULL; + + ap_register_cleanup(pconf, NULL, ap_null_cleanup, close_listeners_on_exec); + + return num_open ? 0 : -1; +} + + +void ap_listen_pre_config(void) +{ + old_listeners = ap_listeners; + ap_listeners = NULL; + ap_listenbacklog = DEFAULT_LISTENBACKLOG; +} + + +const char *ap_set_listener(cmd_parms *cmd, void *dummy, char *ips) +{ + char *ports; + unsigned short port; + struct sockaddr_in local_addr; + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ports = strchr(ips, ':'); + if (ports != NULL) { + if (ports == ips) { + return "Missing IP address"; + } + else if (ports[1] == '\0') { + return "Address must end in :"; + } + *(ports++) = '\0'; + } + else { + ports = ips; + } + + local_addr.sin_family = AF_INET; + if (ports == ips) { /* no address */ + local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + } + else { + local_addr.sin_addr.s_addr = ap_get_virthost_addr(ips, NULL); + } + port = atoi(ports); + if (!port) { + return "Port must be numeric"; + } + local_addr.sin_port = htons(port); + + alloc_listener(&local_addr); + + return NULL; +} + +const char *ap_set_listenbacklog(cmd_parms *cmd, void *dummy, char *arg) +{ + int b; + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + b = atoi(arg); + if (b < 1) { + return "ListenBacklog must be > 0"; + } + ap_listenbacklog = b; + return NULL; +} + +const char *ap_set_send_buffer_size(cmd_parms *cmd, void *dummy, char *arg) +{ + int s = atoi(arg); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (s < 512 && s != 0) { + return "SendBufferSize must be >= 512 bytes, or 0 for system default."; + } + send_buffer_size = s; + return NULL; +} diff --git a/server/log.c b/server/log.c new file mode 100644 index 00000000000..7a1e0ea5d0c --- /dev/null +++ b/server/log.c @@ -0,0 +1,777 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_log.c: Dealing with the logs and errors + * + * Rob McCool + * + */ + + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_conf_globals.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_main.h" + +#include + +typedef struct { + char *t_name; + int t_val; +} TRANS; + +#ifdef HAVE_SYSLOG + +static const TRANS facilities[] = { + {"auth", LOG_AUTH}, +#ifdef LOG_AUTHPRIV + {"authpriv",LOG_AUTHPRIV}, +#endif +#ifdef LOG_CRON + {"cron", LOG_CRON}, +#endif +#ifdef LOG_DAEMON + {"daemon", LOG_DAEMON}, +#endif +#ifdef LOG_FTP + {"ftp", LOG_FTP}, +#endif +#ifdef LOG_KERN + {"kern", LOG_KERN}, +#endif +#ifdef LOG_LPR + {"lpr", LOG_LPR}, +#endif +#ifdef LOG_MAIL + {"mail", LOG_MAIL}, +#endif +#ifdef LOG_NEWS + {"news", LOG_NEWS}, +#endif +#ifdef LOG_SYSLOG + {"syslog", LOG_SYSLOG}, +#endif +#ifdef LOG_USER + {"user", LOG_USER}, +#endif +#ifdef LOG_UUCP + {"uucp", LOG_UUCP}, +#endif +#ifdef LOG_LOCAL0 + {"local0", LOG_LOCAL0}, +#endif +#ifdef LOG_LOCAL1 + {"local1", LOG_LOCAL1}, +#endif +#ifdef LOG_LOCAL2 + {"local2", LOG_LOCAL2}, +#endif +#ifdef LOG_LOCAL3 + {"local3", LOG_LOCAL3}, +#endif +#ifdef LOG_LOCAL4 + {"local4", LOG_LOCAL4}, +#endif +#ifdef LOG_LOCAL5 + {"local5", LOG_LOCAL5}, +#endif +#ifdef LOG_LOCAL6 + {"local6", LOG_LOCAL6}, +#endif +#ifdef LOG_LOCAL7 + {"local7", LOG_LOCAL7}, +#endif + {NULL, -1}, +}; +#endif + +static const TRANS priorities[] = { + {"emerg", APLOG_EMERG}, + {"alert", APLOG_ALERT}, + {"crit", APLOG_CRIT}, + {"error", APLOG_ERR}, + {"warn", APLOG_WARNING}, + {"notice", APLOG_NOTICE}, + {"info", APLOG_INFO}, + {"debug", APLOG_DEBUG}, + {NULL, -1}, +}; + +static int error_log_child(void *cmd, child_info *pinfo) +{ + /* Child process code for 'ErrorLog "|..."'; + * may want a common framework for this, since I expect it will + * be common for other foo-loggers to want this sort of thing... + */ + int child_pid = 0; + + ap_cleanup_for_exec(); +#ifdef SIGHUP + /* No concept of a child process on Win32 */ + signal(SIGHUP, SIG_IGN); +#endif /* ndef SIGHUP */ +#if defined(WIN32) + child_pid = spawnl(_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); + return(child_pid); +#elif defined(OS2) + /* For OS/2 we need to use a '/' and spawn the child rather than exec as + * we haven't forked */ + child_pid = spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); + return(child_pid); +#else + execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + exit(1); + /* NOT REACHED */ + return(child_pid); +} + +static void open_error_log(server_rec *s, pool *p) +{ + char *fname; + + if (*s->error_fname == '|') { + FILE *dummy; +#ifdef TPF + TPF_FORK_CHILD cld; + cld.filename = s->error_fname+1; + cld.subprocess_env = NULL; + cld.prog_type = FORK_NAME; + if (!ap_spawn_child(p, NULL, &cld, + kill_after_timeout, &dummy, NULL, NULL)) { +#else + if (!ap_spawn_child(p, error_log_child, (void *)(s->error_fname+1), + kill_after_timeout, &dummy, NULL, NULL)) { +#endif /* TPF */ + perror("ap_spawn_child"); + fprintf(stderr, "Couldn't fork child for ErrorLog process\n"); + exit(1); + } + + s->error_log = dummy; + } + +#ifdef HAVE_SYSLOG + else if (!strncasecmp(s->error_fname, "syslog", 6)) { + if ((fname = strchr(s->error_fname, ':'))) { + const TRANS *fac; + + fname++; + for (fac = facilities; fac->t_name; fac++) { + if (!strcasecmp(fname, fac->t_name)) { + openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID, + fac->t_val); + s->error_log = NULL; + return; + } + } + } + else + openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID, LOG_LOCAL7); + + s->error_log = NULL; + } +#endif + else { + fname = ap_server_root_relative(p, s->error_fname); + if (!(s->error_log = ap_pfopen(p, fname, "a"))) { + perror("fopen"); + fprintf(stderr, "%s: could not open error log file %s.\n", + ap_server_argv0, fname); + exit(1); + } + } +} + +void ap_open_logs(server_rec *s_main, pool *p) +{ + server_rec *virt, *q; + int replace_stderr; + + open_error_log(s_main, p); + + replace_stderr = 1; + if (s_main->error_log) { + /* replace stderr with this new log */ + fflush(stderr); + if (dup2(fileno(s_main->error_log), STDERR_FILENO) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, s_main, + "unable to replace stderr with error_log"); + } else { + replace_stderr = 0; + } + } + /* note that stderr may still need to be replaced with something + * because it points to the old error log, or back to the tty + * of the submitter. + */ + if (replace_stderr && freopen("/dev/null", "w", stderr) == NULL) { + ap_log_error(APLOG_MARK, APLOG_CRIT, s_main, + "unable to replace stderr with /dev/null"); + } + + for (virt = s_main->next; virt; virt = virt->next) { + if (virt->error_fname) + { + for (q=s_main; q != virt; q = q->next) + if (q->error_fname != NULL && + strcmp(q->error_fname, virt->error_fname) == 0) + break; + if (q == virt) + open_error_log(virt, p); + else + virt->error_log = q->error_log; + } + else + virt->error_log = s_main->error_log; + } +} + +API_EXPORT(void) ap_error_log2stderr(server_rec *s) { + if ( s->error_log != NULL + && fileno(s->error_log) != STDERR_FILENO) + dup2(fileno(s->error_log), STDERR_FILENO); +} + +static void log_error_core(const char *file, int line, int level, + const server_rec *s, const request_rec *r, + const char *fmt, va_list args) +{ + char errstr[MAX_STRING_LEN]; + size_t len; + int save_errno = errno; + FILE *logf; + + if (s == NULL) { + /* + * If we are doing stderr logging (startup), don't log messages that are + * above the default server log level unless it is a startup/shutdown + * notice + */ + if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) && + ((level & APLOG_LEVELMASK) > DEFAULT_LOGLEVEL)) + return; + logf = stderr; + } + else if (s->error_log) { + /* + * If we are doing normal logging, don't log messages that are + * above the server log level unless it is a startup/shutdown notice + */ + if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) && + ((level & APLOG_LEVELMASK) > s->loglevel)) + return; + logf = s->error_log; + } +#ifdef TPF + else if (tpf_child) { + /* + * If we are doing normal logging, don't log messages that are + * above the server log level unless it is a startup/shutdown notice + */ + if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) && + ((level & APLOG_LEVELMASK) > s->loglevel)) + return; + logf = stderr; + } +#endif /* TPF */ + else { + /* + * If we are doing syslog logging, don't log messages that are + * above the server log level (including a startup/shutdown notice) + */ + if ((level & APLOG_LEVELMASK) > s->loglevel) + return; + logf = NULL; + } + + if (logf) { + len = ap_snprintf(errstr, sizeof(errstr), "[%s] ", ap_get_time()); + } else { + len = 0; + } + + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "[%s] ", priorities[level & APLOG_LEVELMASK].t_name); + +#ifndef TPF + if (file && (level & APLOG_LEVELMASK) == APLOG_DEBUG) { +#ifdef _OSD_POSIX + char tmp[256]; + char *e = strrchr(file, '/'); + + /* In OSD/POSIX, the compiler returns for __FILE__ + * a string like: __FILE__="*POSIX(/usr/include/stdio.h)" + * (it even returns an absolute path for sources in + * the current directory). Here we try to strip this + * down to the basename. + */ + if (e != NULL && e[1] != '\0') { + ap_snprintf(tmp, sizeof(tmp), "%s", &e[1]); + e = &tmp[strlen(tmp)-1]; + if (*e == ')') + *e = '\0'; + file = tmp; + } +#endif /*_OSD_POSIX*/ + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "%s(%d): ", file, line); + } +#endif /* TPF */ + if (r) { + /* XXX: TODO: add a method of selecting whether logged client + * addresses are in dotted quad or resolved form... dotted + * quad is the most secure, which is why I'm implementing it + * first. -djg + */ + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "[client %s] ", r->connection->remote_ip); + } + if (!(level & APLOG_NOERRNO) + && (save_errno != 0) +#ifdef WIN32 + && !(level & APLOG_WIN32ERROR) +#endif + ) { + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "(%d)%s: ", save_errno, strerror(save_errno)); + } +#ifdef WIN32 + if (level & APLOG_WIN32ERROR) { + int nChars; + int nErrorCode; + + nErrorCode = GetLastError(); + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "(%d)", nErrorCode); + + nChars = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + nErrorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) errstr + len, + sizeof(errstr) - len, + NULL + ); + len += nChars; + if (nChars == 0) { + /* Um, error occurred, but we can't recurse to log it again + * (and it would probably only fail anyway), so lets just + * log the numeric value. + */ + nErrorCode = GetLastError(); + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "(FormatMessage failed with code %d): ", + nErrorCode); + } + else { + /* FormatMessage put the message in the buffer, but it may + * have appended a newline (\r\n). So remove it and use + * ": " instead like the Unix errors. The error may also + * end with a . before the return - if so, trash it. + */ + if (len > 1 && errstr[len-2] == '\r' && errstr[len-1] == '\n') { + if (len > 2 && errstr[len-3] == '.') + len--; + errstr[len-2] = ':'; + errstr[len-1] = ' '; + } + } + } +#endif + + len += ap_vsnprintf(errstr + len, sizeof(errstr) - len, fmt, args); + + /* NULL if we are logging to syslog */ + if (logf) { + fputs(errstr, logf); + fputc('\n', logf); + fflush(logf); + } +#ifdef HAVE_SYSLOG + else { + syslog(level & APLOG_LEVELMASK, "%s", errstr); + } +#endif +} + +API_EXPORT(void) ap_log_error(const char *file, int line, int level, + const server_rec *s, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + log_error_core(file, line, level, s, NULL, fmt, args); + va_end(args); +} + +API_EXPORT(void) ap_log_rerror(const char *file, int line, int level, + const request_rec *r, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + log_error_core(file, line, level, r->server, r, fmt, args); + /* + * IF the error level is 'warning' or more severe, + * AND there isn't already error text associated with this request, + * THEN make the message text available to ErrorDocument and + * other error processors. This can be disabled by stuffing + * something, even an empty string, into the "error-notes" cell + * before calling this routine. + */ + va_end(args); + va_start(args,fmt); + if (((level & APLOG_LEVELMASK) <= APLOG_WARNING) + && (ap_table_get(r->notes, "error-notes") == NULL)) { + ap_table_setn(r->notes, "error-notes", + ap_pvsprintf(r->pool, fmt, args)); + } + va_end(args); +} + +void ap_log_pid(pool *p, char *fname) +{ + FILE *pid_file; + struct stat finfo; + static pid_t saved_pid = -1; + pid_t mypid; + + if (!fname) + return; + + fname = ap_server_root_relative(p, fname); + mypid = getpid(); + if (mypid != saved_pid && stat(fname, &finfo) == 0) { + /* USR1 and HUP call this on each restart. + * Only warn on first time through for this pid. + * + * XXX: Could just write first time through too, although + * that may screw up scripts written to do something + * based on the last modification time of the pid file. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + ap_psprintf(p, + "pid file %s overwritten -- Unclean shutdown of previous Apache run?", + fname) + ); + } + + if(!(pid_file = fopen(fname, "w"))) { + perror("fopen"); + fprintf(stderr, "%s: could not log pid to file %s\n", + ap_server_argv0, fname); + exit(1); + } + fprintf(pid_file, "%ld\n", (long)mypid); + fclose(pid_file); + saved_pid = mypid; +} + +API_EXPORT(void) ap_log_error_old(const char *err, server_rec *s) +{ + ap_log_error(APLOG_MARK, APLOG_ERR, s, "%s", err); +} + +API_EXPORT(void) ap_log_unixerr(const char *routine, const char *file, + const char *msg, server_rec *s) +{ + ap_log_error(file, 0, APLOG_ERR, s, "%s", msg); +} + +API_EXPORT(void) ap_log_printf(const server_rec *s, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + log_error_core(APLOG_MARK, APLOG_ERR, s, NULL, fmt, args); + va_end(args); +} + +API_EXPORT(void) ap_log_reason(const char *reason, const char *file, request_rec *r) +{ + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "access to %s failed for %s, reason: %s", + file, + ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME), + reason); +} + +API_EXPORT(void) ap_log_assert(const char *szExp, const char *szFile, int nLine) +{ + fprintf(stderr, "[%s] file %s, line %d, assertion \"%s\" failed\n", + ap_get_time(), szFile, nLine, szExp); +#ifndef WIN32 + /* unix assert does an abort leading to a core dump */ + abort(); +#else + exit(1); +#endif +} + +/* piped log support */ + +#ifndef NO_RELIABLE_PIPED_LOGS +/* forward declaration */ +static void piped_log_maintenance(int reason, void *data, ap_wait_t status); + +static int piped_log_spawn(piped_log *pl) +{ + int pid; + + ap_block_alarms(); + pid = fork(); + if (pid == 0) { + /* XXX: this needs porting to OS2 and WIN32 */ + /* XXX: need to check what open fds the logger is actually passed, + * XXX: and CGIs for that matter ... cleanup_for_exec *should* + * XXX: close all the relevant stuff, but hey, it could be broken. */ + RAISE_SIGSTOP(PIPED_LOG_SPAWN); + /* we're now in the child */ + close(STDIN_FILENO); + dup2(pl->fds[0], STDIN_FILENO); + + ap_cleanup_for_exec(); + signal(SIGCHLD, SIG_DFL); /* for HPUX */ + signal(SIGHUP, SIG_IGN); + execl(SHELL_PATH, SHELL_PATH, "-c", pl->program, NULL); + fprintf(stderr, + "piped_log_spawn: unable to exec %s -c '%s': %s\n", + SHELL_PATH, pl->program, strerror (errno)); + exit(1); + } + if (pid == -1) { + fprintf(stderr, + "piped_log_spawn: unable to fork(): %s\n", strerror (errno)); + ap_unblock_alarms(); + return -1; + } + ap_unblock_alarms(); + pl->pid = pid; + ap_register_other_child(pid, piped_log_maintenance, pl, pl->fds[1]); + return 0; +} + + +static void piped_log_maintenance(int reason, void *data, ap_wait_t status) +{ + piped_log *pl = data; + + switch (reason) { + case OC_REASON_DEATH: + case OC_REASON_LOST: + pl->pid = -1; + ap_unregister_other_child(pl); + if (pl->program == NULL) { + /* during a restart */ + break; + } + if (piped_log_spawn(pl) == -1) { + /* what can we do? This could be the error log we're having + * problems opening up... */ + fprintf(stderr, + "piped_log_maintenance: unable to respawn '%s': %s\n", + pl->program, strerror(errno)); + } + break; + + case OC_REASON_UNWRITABLE: + if (pl->pid != -1) { + kill(pl->pid, SIGTERM); + } + break; + + case OC_REASON_RESTART: + pl->program = NULL; + if (pl->pid != -1) { + kill(pl->pid, SIGTERM); + } + break; + + case OC_REASON_UNREGISTER: + break; + } +} + + +static void piped_log_cleanup(void *data) +{ + piped_log *pl = data; + + if (pl->pid != -1) { + kill(pl->pid, SIGTERM); + } + ap_unregister_other_child(pl); + close(pl->fds[0]); + close(pl->fds[1]); +} + + +static void piped_log_cleanup_for_exec(void *data) +{ + piped_log *pl = data; + + close(pl->fds[0]); + close(pl->fds[1]); +} + + +API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program) +{ + piped_log *pl; + + pl = ap_palloc(p, sizeof (*pl)); + pl->p = p; + pl->program = ap_pstrdup(p, program); + pl->pid = -1; + ap_block_alarms (); + if (pipe(pl->fds) == -1) { + int save_errno = errno; + ap_unblock_alarms(); + errno = save_errno; + return NULL; + } + ap_register_cleanup(p, pl, piped_log_cleanup, piped_log_cleanup_for_exec); + if (piped_log_spawn(pl) == -1) { + int save_errno = errno; + ap_kill_cleanup(p, pl, piped_log_cleanup); + close(pl->fds[0]); + close(pl->fds[1]); + ap_unblock_alarms(); + errno = save_errno; + return NULL; + } + ap_unblock_alarms(); + return pl; +} + +API_EXPORT(void) ap_close_piped_log(piped_log *pl) +{ + ap_block_alarms(); + piped_log_cleanup(pl); + ap_kill_cleanup(pl->p, pl, piped_log_cleanup); + ap_unblock_alarms(); +} + +#else +static int piped_log_child(void *cmd, child_info *pinfo) +{ + /* Child process code for 'TransferLog "|..."'; + * may want a common framework for this, since I expect it will + * be common for other foo-loggers to want this sort of thing... + */ + int child_pid = 1; + + ap_cleanup_for_exec(); +#ifdef SIGHUP + signal(SIGHUP, SIG_IGN); +#endif +#if defined(WIN32) + child_pid = spawnl(_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); + return(child_pid); +#elif defined(OS2) + /* For OS/2 we need to use a '/' and spawn the child rather than exec as + * we haven't forked */ + child_pid = spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); + return(child_pid); +#else + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + perror("exec"); + fprintf(stderr, "Exec of shell for logging failed!!!\n"); + return(child_pid); +} + + +API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program) +{ + piped_log *pl; + FILE *dummy; +#ifdef TPF + TPF_FORK_CHILD cld; + cld.filename = (char *)program; + cld.subprocess_env = NULL; + cld.prog_type = FORK_NAME; + + if (!ap_spawn_child (p, NULL, &cld, + kill_after_timeout, &dummy, NULL, NULL)){ +#else + if (!ap_spawn_child(p, piped_log_child, (void *)program, + kill_after_timeout, &dummy, NULL, NULL)) { +#endif /* TPF */ + perror("ap_spawn_child"); + fprintf(stderr, "Couldn't fork child for piped log process\n"); + exit (1); + } + pl = ap_palloc(p, sizeof (*pl)); + pl->p = p; + pl->write_f = dummy; + + return pl; +} + + +API_EXPORT(void) ap_close_piped_log(piped_log *pl) +{ + ap_pfclose(pl->p, pl->write_f); +} +#endif diff --git a/server/main.c b/server/main.c new file mode 100644 index 00000000000..5bd372bbc4b --- /dev/null +++ b/server/main.c @@ -0,0 +1,6669 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * httpd.c: simple http daemon for answering WWW file requests + * + * + * 03-21-93 Rob McCool wrote original code (up to NCSA HTTPd 1.3) + * + * 03-06-95 blong + * changed server number for child-alone processes to 0 and changed name + * of processes + * + * 03-10-95 blong + * Added numerous speed hacks proposed by Robert S. Thau (rst@ai.mit.edu) + * including set group before fork, and call gettime before to fork + * to set up libraries. + * + * 04-14-95 rst / rh + * Brandon's code snarfed from NCSA 1.4, but tinkered to work with the + * Apache server, and also to have child processes do accept() directly. + * + * April-July '95 rst + * Extensive rework for Apache. + */ + +#ifndef SHARED_CORE_BOOTSTRAP +#ifndef SHARED_CORE_TIESTATIC + +#ifdef SHARED_CORE +#define REALMAIN ap_main +int ap_main(int argc, char *argv[]); +#else +#define REALMAIN main +#endif + +#define CORE_PRIVATE + +#include "httpd.h" +#include "http_main.h" +#include "http_log.h" +#include "http_config.h" /* for read_config */ +#include "http_protocol.h" /* for read_request */ +#include "http_request.h" /* for process_request */ +#include "http_conf_globals.h" +#include "http_core.h" /* for get_remote_host */ +#include "http_vhost.h" +#include "util_script.h" /* to force util_script.c linking */ +#include "util_uri.h" +#include "scoreboard.h" +#include "multithread.h" +#include +#ifdef USE_SHMGET_SCOREBOARD +#include +#include +#include +#endif +#ifdef SecureWare +#include +#include +#include +#endif +#ifdef WIN32 +#include "../os/win32/getopt.h" +#elif !defined(BEOS) && !defined(TPF) +#include +#endif + +#ifdef HAVE_BSTRING_H +#include /* for IRIX, FD_SET calls bzero() */ +#endif + +#ifdef MULTITHREAD +/* special debug stuff -- PCS */ + +/* Set this non-zero if you are prepared to put up with more than one log entry per second */ +#define SEVERELY_VERBOSE 0 + + /* APD1() to APD5() are macros to help us debug. They can either + * log to the screen or the error_log file. In release builds, these + * macros do nothing. In debug builds, they send messages at priority + * "debug" to the error log file, or if DEBUG_TO_CONSOLE is defined, + * to the console. + */ + +# ifdef _DEBUG +# ifndef DEBUG_TO_CONSOLE +# define APD1(a) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a) +# define APD2(a,b) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b) +# define APD3(a,b,c) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c) +# define APD4(a,b,c,d) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c,d) +# define APD5(a,b,c,d,e) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c,d,e) +# else +# define APD1(a) printf("%s\n",a) +# define APD2(a,b) do { printf(a,b);putchar('\n'); } while(0); +# define APD3(a,b,c) do { printf(a,b,c);putchar('\n'); } while(0); +# define APD4(a,b,c,d) do { printf(a,b,c,d);putchar('\n'); } while(0); +# define APD5(a,b,c,d,e) do { printf(a,b,c,d,e);putchar('\n'); } while(0); +# endif +# else /* !_DEBUG */ +# define APD1(a) +# define APD2(a,b) +# define APD3(a,b,c) +# define APD4(a,b,c,d) +# define APD5(a,b,c,d,e) +# endif /* _DEBUG */ +#endif /* MULTITHREAD */ + +/* This next function is never used. It is here to ensure that if we + * make all the modules into shared libraries that core httpd still + * includes the full Apache API. Without this function the objects in + * main/util_script.c would not be linked into a minimal httpd. + * And the extra prototype is to make gcc -Wmissing-prototypes quiet. + */ +extern void ap_force_library_loading(void); +void ap_force_library_loading(void) { + ap_add_cgi_vars(NULL); +} + +#include "explain.h" + +#if !defined(max) +#define max(a,b) (a > b ? a : b) +#endif + +#ifdef WIN32 +#include "../os/win32/service.h" +#include "../os/win32/registry.h" +#define DEFAULTSERVICENAME "Apache" +#define PATHSEPARATOR '\\' +#else +#define PATHSEPARATOR '/' +#endif + + +#ifdef MINT +long _stksize = 32768; +#endif + +#ifdef USE_OS2_SCOREBOARD + /* Add MMAP style functionality to OS/2 */ +#define INCL_DOSMEMMGR +#define INCL_DOSEXCEPTIONS +#define INCL_DOSSEMAPHORES +#include +#include +#include +caddr_t create_shared_heap(const char *, size_t); +caddr_t get_shared_heap(const char *); +#endif + +DEF_Explain + +/* Defining GPROF when compiling uses the moncontrol() function to + * disable gprof profiling in the parent, and enable it only for + * request processing in children (or in one_process mode). It's + * absolutely required to get useful gprof results under linux + * because the profile itimers and such are disabled across a + * fork(). It's probably useful elsewhere as well. + */ +#ifdef GPROF +extern void moncontrol(int); +#define MONCONTROL(x) moncontrol(x) +#else +#define MONCONTROL(x) +#endif + +#ifndef MULTITHREAD +/* this just need to be anything non-NULL */ +void *ap_dummy_mutex = &ap_dummy_mutex; +#endif + +/* + * Actual definitions of config globals... here because this is + * for the most part the only code that acts on 'em. (Hmmm... mod_main.c?) + */ + +int ap_standalone=0; +int ap_configtestonly=0; +int ap_docrootcheck=1; +uid_t ap_user_id=0; +char *ap_user_name=NULL; +gid_t ap_group_id=0; +#ifdef MULTIPLE_GROUPS +gid_t group_id_list[NGROUPS_MAX]; +#endif +int ap_max_requests_per_child=0; +int ap_threads_per_child=0; +int ap_excess_requests_per_child=0; +char *ap_pid_fname=NULL; +char *ap_scoreboard_fname=NULL; +char *ap_lock_fname; +char *ap_server_argv0=NULL; +struct in_addr ap_bind_address; +int ap_daemons_to_start=0; +int ap_daemons_min_free=0; +int ap_daemons_max_free=0; +int ap_daemons_limit=0; +time_t ap_restart_time=0; +int ap_suexec_enabled = 0; +int ap_listenbacklog; +int ap_dump_settings = 0; +API_VAR_EXPORT int ap_extended_status = 0; + +/* + * The max child slot ever assigned, preserved across restarts. Necessary + * to deal with MaxClients changes across SIGUSR1 restarts. We use this + * value to optimize routines that have to scan the entire scoreboard. + */ +static int max_daemons_limit = -1; + +/* + * During config time, listeners is treated as a NULL-terminated list. + * child_main previously would start at the beginning of the list each time + * through the loop, so a socket early on in the list could easily starve out + * sockets later on in the list. The solution is to start at the listener + * after the last one processed. But to do that fast/easily in child_main it's + * way more convenient for listeners to be a ring that loops back on itself. + * The routine setup_listeners() is called after config time to both open up + * the sockets and to turn the NULL-terminated list into a ring that loops back + * on itself. + * + * head_listener is used by each child to keep track of what they consider + * to be the "start" of the ring. It is also set by make_child to ensure + * that new children also don't starve any sockets. + * + * Note that listeners != NULL is ensured by read_config(). + */ +listen_rec *ap_listeners; +static listen_rec *head_listener; + +API_VAR_EXPORT char ap_server_root[MAX_STRING_LEN]=""; +char ap_server_confname[MAX_STRING_LEN]=""; +char ap_coredump_dir[MAX_STRING_LEN]; + +array_header *ap_server_pre_read_config; +array_header *ap_server_post_read_config; +array_header *ap_server_config_defines; + +/* *Non*-shared http_main globals... */ + +static server_rec *server_conf; +static JMP_BUF APACHE_TLS jmpbuffer; +static int sd; +static fd_set listenfds; +static int listenmaxfd; +static pid_t pgrp; + +/* one_process --- debugging mode variable; can be set from the command line + * with the -X flag. If set, this gets you the child_main loop running + * in the process which originally started up (no detach, no make_child), + * which is a pretty nice debugging environment. (You'll get a SIGHUP + * early in standalone_main; just continue through. This is the server + * trying to kill off any child processes which it might have lying + * around --- Apache doesn't keep track of their pids, it just sends + * SIGHUP to the process group, ignoring it in the root process. + * Continue through and you'll be fine.). + */ + +static int one_process = 0; + +/* set if timeouts are to be handled by the children and not by the parent. + * i.e. child_timeouts = !standalone || one_process. + */ +static int child_timeouts; + +#ifdef DEBUG_SIGSTOP +int raise_sigstop_flags; +#endif + +#ifndef NO_OTHER_CHILD +/* used to maintain list of children which aren't part of the scoreboard */ +typedef struct other_child_rec other_child_rec; +struct other_child_rec { + other_child_rec *next; + int pid; + void (*maintenance) (int, void *, ap_wait_t); + void *data; + int write_fd; +}; +static other_child_rec *other_children; +#endif + +static pool *pglobal; /* Global pool */ +static pool *pconf; /* Pool for config stuff */ +static pool *plog; /* Pool for error-logging files */ +static pool *ptrans; /* Pool for per-transaction stuff */ +static pool *pchild; /* Pool for httpd child stuff */ +static pool *pcommands; /* Pool for -C and -c switches */ + +static int APACHE_TLS my_pid; /* it seems silly to call getpid all the time */ +#ifndef MULTITHREAD +static int my_child_num; +#endif + +#ifdef TPF +int tpf_child = 0; +char tpf_server_name[INETD_SERVNAME_LENGTH+1]; +#endif /* TPF */ + +scoreboard *ap_scoreboard_image = NULL; + +/* + * Pieces for managing the contents of the Server response header + * field. + */ +static char *server_version = NULL; +static int version_locked = 0; + +/* Global, alas, so http_core can talk to us */ +enum server_token_type ap_server_tokens = SrvTk_FULL; + +/* + * This routine is called when the pconf pool is vacuumed. It resets the + * server version string to a known value and [re]enables modifications + * (which are disabled by configuration completion). + */ +static void reset_version(void *dummy) +{ + version_locked = 0; + ap_server_tokens = SrvTk_FULL; + server_version = NULL; +} + +API_EXPORT(const char *) ap_get_server_version(void) +{ + return (server_version ? server_version : SERVER_BASEVERSION); +} + +API_EXPORT(void) ap_add_version_component(const char *component) +{ + if (! version_locked) { + /* + * If the version string is null, register our cleanup to reset the + * pointer on pool destruction. We also know that, if NULL, + * we are adding the original SERVER_BASEVERSION string. + */ + if (server_version == NULL) { + ap_register_cleanup(pconf, NULL, (void (*)(void *))reset_version, + ap_null_cleanup); + server_version = ap_pstrdup(pconf, component); + } + else { + /* + * Tack the given component identifier to the end of + * the existing string. + */ + server_version = ap_pstrcat(pconf, server_version, " ", + component, NULL); + } + } +} + +/* + * This routine adds the real server base identity to the version string, + * and then locks out changes until the next reconfig. + */ +static void ap_set_version(void) +{ + if (ap_server_tokens == SrvTk_MIN) { + ap_add_version_component(SERVER_BASEVERSION); + } + else { + ap_add_version_component(SERVER_BASEVERSION " (" PLATFORM ")"); + } + /* + * Lock the server_version string if we're not displaying + * the full set of tokens + */ + if (ap_server_tokens != SrvTk_FULL) { + version_locked++; + } +} + +static APACHE_TLS int volatile exit_after_unblock = 0; + +#ifdef GPROF +/* + * change directory for gprof to plop the gmon.out file + * configure in httpd.conf: + * GprofDir logs/ -> $ServerRoot/logs/gmon.out + * GprofDir logs/% -> $ServerRoot/logs/gprof.$pid/gmon.out + */ +static void chdir_for_gprof(void) +{ + core_server_config *sconf = + ap_get_module_config(server_conf->module_config, &core_module); + char *dir = sconf->gprof_dir; + + if(dir) { + char buf[512]; + int len = strlen(sconf->gprof_dir) - 1; + if(*(dir + len) == '%') { + dir[len] = '\0'; + ap_snprintf(buf, sizeof(buf), "%sgprof.%d", dir, (int)getpid()); + } + dir = ap_server_root_relative(pconf, buf[0] ? buf : dir); + if(mkdir(dir, 0755) < 0 && errno != EEXIST) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "gprof: error creating directory %s", dir); + } + } + else { + dir = ap_server_root_relative(pconf, "logs"); + } + + chdir(dir); +} +#else +#define chdir_for_gprof() +#endif + +/* a clean exit from a child with proper cleanup */ +static void clean_child_exit(int code) __attribute__ ((noreturn)); +static void clean_child_exit(int code) +{ + if (pchild) { + ap_child_exit_modules(pchild, server_conf); + ap_destroy_pool(pchild); + } + chdir_for_gprof(); + exit(code); +} + +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) || defined(USE_FLOCK_SERIALIZED_ACCEPT) +static void expand_lock_fname(pool *p) +{ + /* XXXX possibly bogus cast */ + ap_lock_fname = ap_psprintf(p, "%s.%lu", + ap_server_root_relative(p, ap_lock_fname), (unsigned long)getpid()); +} +#endif + +#if defined (USE_USLOCK_SERIALIZED_ACCEPT) + +#include + +static ulock_t uslock = NULL; + +#define accept_mutex_child_init(x) + +static void accept_mutex_init(pool *p) +{ + ptrdiff_t old; + usptr_t *us; + + + /* default is 8, allocate enough for all the children plus the parent */ + if ((old = usconfig(CONF_INITUSERS, HARD_SERVER_LIMIT + 1)) == -1) { + perror("usconfig(CONF_INITUSERS)"); + exit(-1); + } + + if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) { + perror("usconfig(CONF_LOCKTYPE)"); + exit(-1); + } + if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) { + perror("usconfig(CONF_ARENATYPE)"); + exit(-1); + } + if ((us = usinit("/dev/zero")) == NULL) { + perror("usinit"); + exit(-1); + } + + if ((uslock = usnewlock(us)) == NULL) { + perror("usnewlock"); + exit(-1); + } +} + +static void accept_mutex_on(void) +{ + switch (ussetlock(uslock)) { + case 1: + /* got lock */ + break; + case 0: + fprintf(stderr, "didn't get lock\n"); + clean_child_exit(APEXIT_CHILDFATAL); + case -1: + perror("ussetlock"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + if (usunsetlock(uslock) == -1) { + perror("usunsetlock"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT) + +/* This code probably only works on Solaris ... but it works really fast + * on Solaris. Note that pthread mutexes are *NOT* released when a task + * dies ... the task has to free it itself. So we block signals and + * try to be nice about releasing the mutex. + */ + +#include + +static pthread_mutex_t *accept_mutex = (void *)(caddr_t) -1; +static int have_accept_mutex; +static sigset_t accept_block_mask; +static sigset_t accept_previous_mask; + +static void accept_mutex_child_cleanup(void *foo) +{ + if (accept_mutex != (void *)(caddr_t)-1 + && have_accept_mutex) { + pthread_mutex_unlock(accept_mutex); + } +} + +static void accept_mutex_child_init(pool *p) +{ + ap_register_cleanup(p, NULL, accept_mutex_child_cleanup, ap_null_cleanup); +} + +static void accept_mutex_cleanup(void *foo) +{ + if (accept_mutex != (void *)(caddr_t)-1 + && munmap((caddr_t) accept_mutex, sizeof(*accept_mutex))) { + perror("munmap"); + } + accept_mutex = (void *)(caddr_t)-1; +} + +static void accept_mutex_init(pool *p) +{ + pthread_mutexattr_t mattr; + int fd; + + fd = open("/dev/zero", O_RDWR); + if (fd == -1) { + perror("open(/dev/zero)"); + exit(APEXIT_INIT); + } + accept_mutex = (pthread_mutex_t *) mmap((caddr_t) 0, sizeof(*accept_mutex), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (accept_mutex == (void *) (caddr_t) - 1) { + perror("mmap"); + exit(APEXIT_INIT); + } + close(fd); + if ((errno = pthread_mutexattr_init(&mattr))) { + perror("pthread_mutexattr_init"); + exit(APEXIT_INIT); + } + if ((errno = pthread_mutexattr_setpshared(&mattr, + PTHREAD_PROCESS_SHARED))) { + perror("pthread_mutexattr_setpshared"); + exit(APEXIT_INIT); + } + if ((errno = pthread_mutex_init(accept_mutex, &mattr))) { + perror("pthread_mutex_init"); + exit(APEXIT_INIT); + } + sigfillset(&accept_block_mask); + sigdelset(&accept_block_mask, SIGHUP); + sigdelset(&accept_block_mask, SIGTERM); + sigdelset(&accept_block_mask, SIGUSR1); + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); +} + +static void accept_mutex_on(void) +{ + int err; + + if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) { + perror("sigprocmask(SIG_BLOCK)"); + clean_child_exit(APEXIT_CHILDFATAL); + } + if ((err = pthread_mutex_lock(accept_mutex))) { + errno = err; + perror("pthread_mutex_lock"); + clean_child_exit(APEXIT_CHILDFATAL); + } + have_accept_mutex = 1; +} + +static void accept_mutex_off(void) +{ + int err; + + if ((err = pthread_mutex_unlock(accept_mutex))) { + errno = err; + perror("pthread_mutex_unlock"); + clean_child_exit(APEXIT_CHILDFATAL); + } + /* There is a slight race condition right here... if we were to die right + * now, we'd do another pthread_mutex_unlock. Now, doing that would let + * another process into the mutex. pthread mutexes are designed to be + * fast, as such they don't have protection for things like testing if the + * thread owning a mutex is actually unlocking it (or even any way of + * testing who owns the mutex). + * + * If we were to unset have_accept_mutex prior to releasing the mutex + * then the race could result in the server unable to serve hits. Doing + * it this way means that the server can continue, but an additional + * child might be in the critical section ... at least it's still serving + * hits. + */ + have_accept_mutex = 0; + if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) { + perror("sigprocmask(SIG_SETMASK)"); + clean_child_exit(1); + } +} + +#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT) + +#include +#include +#include + +#ifdef NEED_UNION_SEMUN +/* it makes no sense, but this isn't defined on solaris */ +union semun { + long val; + struct semid_ds *buf; + ushort *array; +}; + +#endif + +static int sem_id = -1; +static struct sembuf op_on; +static struct sembuf op_off; + +/* We get a random semaphore ... the lame sysv semaphore interface + * means we have to be sure to clean this up or else we'll leak + * semaphores. + */ +static void accept_mutex_cleanup(void *foo) +{ + union semun ick; + + if (sem_id < 0) + return; + /* this is ignored anyhow */ + ick.val = 0; + semctl(sem_id, 0, IPC_RMID, ick); +} + +#define accept_mutex_child_init(x) + +static void accept_mutex_init(pool *p) +{ + union semun ick; + struct semid_ds buf; + + /* acquire the semaphore */ + sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600); + if (sem_id < 0) { + perror("semget"); + exit(APEXIT_INIT); + } + ick.val = 1; + if (semctl(sem_id, 0, SETVAL, ick) < 0) { + perror("semctl(SETVAL)"); + exit(APEXIT_INIT); + } + if (!getuid()) { + /* restrict it to use only by the appropriate user_id ... not that this + * stops CGIs from acquiring it and dinking around with it. + */ + buf.sem_perm.uid = ap_user_id; + buf.sem_perm.gid = ap_group_id; + buf.sem_perm.mode = 0600; + ick.buf = &buf; + if (semctl(sem_id, 0, IPC_SET, ick) < 0) { + perror("semctl(IPC_SET)"); + exit(APEXIT_INIT); + } + } + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); + + /* pre-initialize these */ + op_on.sem_num = 0; + op_on.sem_op = -1; + op_on.sem_flg = SEM_UNDO; + op_off.sem_num = 0; + op_off.sem_op = 1; + op_off.sem_flg = SEM_UNDO; +} + +static void accept_mutex_on(void) +{ + while (semop(sem_id, &op_on, 1) < 0) { + if (errno != EINTR) { + perror("accept_mutex_on"); + clean_child_exit(APEXIT_CHILDFATAL); + } + } +} + +static void accept_mutex_off(void) +{ + while (semop(sem_id, &op_off, 1) < 0) { + if (errno != EINTR) { + perror("accept_mutex_off"); + clean_child_exit(APEXIT_CHILDFATAL); + } + } +} + +#elif defined(USE_FCNTL_SERIALIZED_ACCEPT) +static struct flock lock_it; +static struct flock unlock_it; + +static int lock_fd = -1; + +#define accept_mutex_child_init(x) + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +static void accept_mutex_init(pool *p) +{ + + lock_it.l_whence = SEEK_SET; /* from current point */ + lock_it.l_start = 0; /* -"- */ + lock_it.l_len = 0; /* until end of file */ + lock_it.l_type = F_WRLCK; /* set exclusive/write lock */ + lock_it.l_pid = 0; /* pid not actually interesting */ + unlock_it.l_whence = SEEK_SET; /* from current point */ + unlock_it.l_start = 0; /* -"- */ + unlock_it.l_len = 0; /* until end of file */ + unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */ + unlock_it.l_pid = 0; /* pid not actually interesting */ + + expand_lock_fname(p); + lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0644); + if (lock_fd == -1) { + perror("open"); + fprintf(stderr, "Cannot open lock file: %s\n", ap_lock_fname); + exit(APEXIT_INIT); + } + unlink(ap_lock_fname); +} + +static void accept_mutex_on(void) +{ + int ret; + + while ((ret = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) { + /* nop */ + } + + if (ret < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "fcntl: F_SETLKW: Error getting accept lock, exiting! " + "Perhaps you need to use the LockFile directive to place " + "your lock file on a local disk!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + int ret; + + while ((ret = fcntl(lock_fd, F_SETLKW, &unlock_it)) < 0 && errno == EINTR) { + /* nop */ + } + if (ret < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "fcntl: F_SETLKW: Error freeing accept lock, exiting! " + "Perhaps you need to use the LockFile directive to place " + "your lock file on a local disk!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined(USE_FLOCK_SERIALIZED_ACCEPT) + +static int lock_fd = -1; + +static void accept_mutex_cleanup(void *foo) +{ + unlink(ap_lock_fname); +} + +/* + * Initialize mutex lock. + * Done by each child at it's birth + */ +static void accept_mutex_child_init(pool *p) +{ + + lock_fd = ap_popenf(p, ap_lock_fname, O_WRONLY, 0600); + if (lock_fd == -1) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "Child cannot open lock file: %s", ap_lock_fname); + clean_child_exit(APEXIT_CHILDINIT); + } +} + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +static void accept_mutex_init(pool *p) +{ + expand_lock_fname(p); + unlink(ap_lock_fname); + lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0600); + if (lock_fd == -1) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "Parent cannot open lock file: %s", ap_lock_fname); + exit(APEXIT_INIT); + } + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); +} + +static void accept_mutex_on(void) +{ + int ret; + + while ((ret = flock(lock_fd, LOCK_EX)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "flock: LOCK_EX: Error getting accept lock. Exiting!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + if (flock(lock_fd, LOCK_UN) < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "flock: LOCK_UN: Error freeing accept lock. Exiting!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined(USE_OS2SEM_SERIALIZED_ACCEPT) + +static HMTX lock_sem = -1; + +static void accept_mutex_cleanup(void *foo) +{ + DosReleaseMutexSem(lock_sem); + DosCloseMutexSem(lock_sem); +} + +/* + * Initialize mutex lock. + * Done by each child at it's birth + */ +static void accept_mutex_child_init(pool *p) +{ + int rc = DosOpenMutexSem(NULL, &lock_sem); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "Child cannot open lock semaphore, rc=%d", rc); + clean_child_exit(APEXIT_CHILDINIT); + } else { + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); + } +} + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +static void accept_mutex_init(pool *p) +{ + int rc = DosCreateMutexSem(NULL, &lock_sem, DC_SEM_SHARED, FALSE); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "Parent cannot create lock semaphore, rc=%d", rc); + exit(APEXIT_INIT); + } + + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); +} + +static void accept_mutex_on(void) +{ + int rc = DosRequestMutexSem(lock_sem, SEM_INDEFINITE_WAIT); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "OS2SEM: Error %d getting accept lock. Exiting!", rc); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + int rc = DosReleaseMutexSem(lock_sem); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "OS2SEM: Error %d freeing accept lock. Exiting!", rc); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined(USE_TPF_CORE_SERIALIZED_ACCEPT) + +static int tpf_core_held; + +static void accept_mutex_cleanup(void *foo) +{ + if(tpf_core_held) + coruc(RESOURCE_KEY); +} + +#define accept_mutex_init(x) + +static void accept_mutex_child_init(pool *p) +{ + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); + tpf_core_held = 0; +} + +static void accept_mutex_on(void) +{ + corhc(RESOURCE_KEY); + tpf_core_held = 1; + ap_check_signals(); +} + +static void accept_mutex_off(void) +{ + coruc(RESOURCE_KEY); + tpf_core_held = 0; + ap_check_signals(); +} + +#else +/* Default --- no serialization. Other methods *could* go here, + * as #elifs... + */ +#if !defined(MULTITHREAD) +/* Multithreaded systems don't complete between processes for + * the sockets. */ +#define NO_SERIALIZED_ACCEPT +#define accept_mutex_child_init(x) +#define accept_mutex_init(x) +#define accept_mutex_on() +#define accept_mutex_off() +#endif +#endif + +/* On some architectures it's safe to do unserialized accept()s in the single + * Listen case. But it's never safe to do it in the case where there's + * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT + * when it's safe in the single Listen case. + */ +#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT +#define SAFE_ACCEPT(stmt) do {if(ap_listeners->next != ap_listeners) {stmt;}} while(0) +#else +#define SAFE_ACCEPT(stmt) do {stmt;} while(0) +#endif + +static void usage(char *bin) +{ + char pad[MAX_STRING_LEN]; + unsigned i; + + for (i = 0; i < strlen(bin); i++) + pad[i] = ' '; + pad[i] = '\0'; +#ifdef SHARED_CORE + fprintf(stderr, "Usage: %s [-R directory] [-D name] [-d directory] [-f file]\n", bin); +#else + fprintf(stderr, "Usage: %s [-D name] [-d directory] [-f file]\n", bin); +#endif + fprintf(stderr, " %s [-C \"directive\"] [-c \"directive\"]\n", pad); + fprintf(stderr, " %s [-v] [-V] [-h] [-l] [-L] [-S] [-t] [-T]\n", pad); +#ifdef WIN32 + fprintf(stderr, " %s [-n service] [-k signal] [-i] [-u]\n", pad); +#endif + fprintf(stderr, "Options:\n"); +#ifdef SHARED_CORE + fprintf(stderr, " -R directory : specify an alternate location for shared object files\n"); +#endif + fprintf(stderr, " -D name : define a name for use in directives\n"); + fprintf(stderr, " -d directory : specify an alternate initial ServerRoot\n"); + fprintf(stderr, " -f file : specify an alternate ServerConfigFile\n"); + fprintf(stderr, " -C \"directive\" : process directive before reading config files\n"); + fprintf(stderr, " -c \"directive\" : process directive after reading config files\n"); + fprintf(stderr, " -v : show version number\n"); + fprintf(stderr, " -V : show compile settings\n"); + fprintf(stderr, " -h : list available command line options (this page)\n"); + fprintf(stderr, " -l : list compiled-in modules\n"); + fprintf(stderr, " -L : list available configuration directives\n"); + fprintf(stderr, " -S : show parsed settings (currently only vhost settings)\n"); + fprintf(stderr, " -t : run syntax check for config files (with docroot check)\n"); + fprintf(stderr, " -T : run syntax check for config files (without docroot check)\n"); +#ifdef WIN32 + fprintf(stderr, " -n name : set service name and use its ServerConfigFile\n"); + fprintf(stderr, " -k shutdown : tell running Apache to shutdown\n"); + fprintf(stderr, " -k restart : tell running Apache to do a graceful restart\n"); + fprintf(stderr, " -k start : tell Apache to start\n"); + fprintf(stderr, " -i : install an Apache service\n"); + fprintf(stderr, " -u : uninstall an Apache service\n"); +#endif + exit(1); +} + +/***************************************************************** + * + * Timeout handling. DISTINCTLY not thread-safe, but all this stuff + * has to change for threads anyway. Note that this code allows only + * one timeout in progress at a time... + */ + +static APACHE_TLS conn_rec *volatile current_conn; +static APACHE_TLS request_rec *volatile timeout_req; +static APACHE_TLS const char *volatile timeout_name = NULL; +static APACHE_TLS int volatile alarms_blocked = 0; +static APACHE_TLS int volatile alarm_pending = 0; + +static void timeout(int sig) +{ + void *dirconf; + + if (alarms_blocked) { + alarm_pending = 1; + return; + } + if (exit_after_unblock) { + clean_child_exit(0); + } + + if (!current_conn) { + ap_longjmp(jmpbuffer, 1); + } + + if (timeout_req != NULL) + dirconf = timeout_req->per_dir_config; + else + dirconf = current_conn->server->lookup_defaults; + if (!current_conn->keptalive) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + current_conn->server, "[client %s] %s timed out", + current_conn->remote_ip, + timeout_name ? timeout_name : "request"); + } + + if (timeout_req) { + /* Someone has asked for this transaction to just be aborted + * if it times out... + */ + + request_rec *log_req = timeout_req; + request_rec *save_req = timeout_req; + + /* avoid looping... if ap_log_transaction started another + * timer (say via rfc1413.c) we could loop... + */ + timeout_req = NULL; + + while (log_req->main || log_req->prev) { + /* Get back to original request... */ + if (log_req->main) + log_req = log_req->main; + else + log_req = log_req->prev; + } + + if (!current_conn->keptalive) { + /* in some cases we come here before setting the time */ + if (log_req->request_time == 0) { + log_req->request_time = time(0); + } + ap_log_transaction(log_req); + } + + ap_bsetflag(save_req->connection->client, B_EOUT, 1); + ap_bclose(save_req->connection->client); + + if (!ap_standalone) + exit(0); + + ap_longjmp(jmpbuffer, 1); + } + else { /* abort the connection */ + ap_bsetflag(current_conn->client, B_EOUT, 1); + ap_bclose(current_conn->client); + current_conn->aborted = 1; + } +} + +#ifndef TPF +/* + * These two called from alloc.c to protect its critical sections... + * Note that they can nest (as when destroying the sub_pools of a pool + * which is itself being cleared); we have to support that here. + */ + +API_EXPORT(void) ap_block_alarms(void) +{ + ++alarms_blocked; +} + +API_EXPORT(void) ap_unblock_alarms(void) +{ + --alarms_blocked; + if (alarms_blocked == 0) { + if (exit_after_unblock) { + /* We have a couple race conditions to deal with here, we can't + * allow a timeout that comes in this small interval to allow + * the child to jump back to the main loop. Instead we block + * alarms again, and then note that exit_after_unblock is + * being dealt with. We choose this way to solve this so that + * the common path through unblock_alarms() is really short. + */ + ++alarms_blocked; + exit_after_unblock = 0; + clean_child_exit(0); + } + if (alarm_pending) { + alarm_pending = 0; + timeout(0); + } + } +} +#endif /* TPF */ + +static APACHE_TLS void (*volatile alarm_fn) (int) = NULL; +#ifdef WIN32 +static APACHE_TLS unsigned int alarm_expiry_time = 0; +#endif /* WIN32 */ + +#ifndef WIN32 +static void alrm_handler(int sig) +{ + if (alarm_fn) { + (*alarm_fn) (sig); + } +} +#endif + +unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x) +{ + unsigned int old; + +#ifdef WIN32 + old = alarm_expiry_time; + if (old) + old -= time(0); + if (x == 0) { + alarm_fn = NULL; + alarm_expiry_time = 0; + } + else { + alarm_fn = fn; + alarm_expiry_time = time(NULL) + x; + } +#else + if (alarm_fn && x && fn != alarm_fn) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL, + "ap_set_callback_and_alarm: possible nested timer!"); + } + alarm_fn = fn; +#ifndef OPTIMIZE_TIMEOUTS + old = alarm(x); +#else + if (child_timeouts) { + old = alarm(x); + } + else { + /* Just note the timeout in our scoreboard, no need to call the system. + * We also note that the virtual time has gone forward. + */ + ap_check_signals(); + old = ap_scoreboard_image->servers[my_child_num].timeout_len; + ap_scoreboard_image->servers[my_child_num].timeout_len = x; + ++ap_scoreboard_image->servers[my_child_num].cur_vtime; + } +#endif +#endif + return (old); +} + + +#ifdef WIN32 +API_EXPORT(int) ap_check_alarm(void) +{ + if (alarm_expiry_time) { + unsigned int t; + + t = time(NULL); + if (t >= alarm_expiry_time) { + alarm_expiry_time = 0; + (*alarm_fn) (0); + return (-1); + } + else { + return (alarm_expiry_time - t); + } + } + else + return (0); +} +#endif /* WIN32 */ + + + +/* reset_timeout (request_rec *) resets the timeout in effect, + * as long as it hasn't expired already. + */ + +API_EXPORT(void) ap_reset_timeout(request_rec *r) +{ + int i; + + if (timeout_name) { /* timeout has been set */ + i = ap_set_callback_and_alarm(alarm_fn, r->server->timeout); + if (i == 0) /* timeout already expired, so set it back to 0 */ + ap_set_callback_and_alarm(alarm_fn, 0); + } +} + + + + +void ap_keepalive_timeout(char *name, request_rec *r) +{ + unsigned int to; + + timeout_req = r; + timeout_name = name; + + if (r->connection->keptalive) + to = r->server->keep_alive_timeout; + else + to = r->server->timeout; + ap_set_callback_and_alarm(timeout, to); + +} + +API_EXPORT(void) ap_hard_timeout(char *name, request_rec *r) +{ + timeout_req = r; + timeout_name = name; + + ap_set_callback_and_alarm(timeout, r->server->timeout); + +} + +API_EXPORT(void) ap_soft_timeout(char *name, request_rec *r) +{ + timeout_name = name; + + ap_set_callback_and_alarm(timeout, r->server->timeout); + +} + +API_EXPORT(void) ap_kill_timeout(request_rec *dummy) +{ + ap_check_signals(); + ap_set_callback_and_alarm(NULL, 0); + timeout_req = NULL; + timeout_name = NULL; +} + + +/* + * More machine-dependent networking gooo... on some systems, + * you've got to be *really* sure that all the packets are acknowledged + * before closing the connection, since the client will not be able + * to see the last response if their TCP buffer is flushed by a RST + * packet from us, which is what the server's TCP stack will send + * if it receives any request data after closing the connection. + * + * In an ideal world, this function would be accomplished by simply + * setting the socket option SO_LINGER and handling it within the + * server's TCP stack while the process continues on to the next request. + * Unfortunately, it seems that most (if not all) operating systems + * block the server process on close() when SO_LINGER is used. + * For those that don't, see USE_SO_LINGER below. For the rest, + * we have created a home-brew lingering_close. + * + * Many operating systems tend to block, puke, or otherwise mishandle + * calls to shutdown only half of the connection. You should define + * NO_LINGCLOSE in ap_config.h if such is the case for your system. + */ +#ifndef MAX_SECS_TO_LINGER +#define MAX_SECS_TO_LINGER 30 +#endif + +#ifdef USE_SO_LINGER +#define NO_LINGCLOSE /* The two lingering options are exclusive */ + +static void sock_enable_linger(int s) +{ + struct linger li; + + li.l_onoff = 1; + li.l_linger = MAX_SECS_TO_LINGER; + + if (setsockopt(s, SOL_SOCKET, SO_LINGER, + (char *) &li, sizeof(struct linger)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "setsockopt: (SO_LINGER)"); + /* not a fatal error */ + } +} + +#else +#define sock_enable_linger(s) /* NOOP */ +#endif /* USE_SO_LINGER */ + +#ifndef NO_LINGCLOSE + +/* Special version of timeout for lingering_close */ + +static void lingerout(int sig) +{ + if (alarms_blocked) { + alarm_pending = 1; + return; + } + + if (!current_conn) { + ap_longjmp(jmpbuffer, 1); + } + ap_bsetflag(current_conn->client, B_EOUT, 1); + current_conn->aborted = 1; +} + +static void linger_timeout(void) +{ + timeout_name = "lingering close"; + + ap_set_callback_and_alarm(lingerout, MAX_SECS_TO_LINGER); +} + +/* Since many clients will abort a connection instead of closing it, + * attempting to log an error message from this routine will only + * confuse the webmaster. There doesn't seem to be any portable way to + * distinguish between a dropped connection and something that might be + * worth logging. + */ +static void lingering_close(request_rec *r) +{ + char dummybuf[512]; + struct timeval tv; + fd_set lfds; + int select_rv; + int lsd; + + /* Prevent a slow-drip client from holding us here indefinitely */ + + linger_timeout(); + + /* Send any leftover data to the client, but never try to again */ + + if (ap_bflush(r->connection->client) == -1) { + ap_kill_timeout(r); + ap_bclose(r->connection->client); + return; + } + ap_bsetflag(r->connection->client, B_EOUT, 1); + + /* Close our half of the connection --- send the client a FIN */ + + lsd = r->connection->client->fd; + + if ((shutdown(lsd, 1) != 0) || r->connection->aborted) { + ap_kill_timeout(r); + ap_bclose(r->connection->client); + return; + } + + /* Set up to wait for readable data on socket... */ + + FD_ZERO(&lfds); + + /* Wait for readable data or error condition on socket; + * slurp up any data that arrives... We exit when we go for an + * interval of tv length without getting any more data, get an error + * from select(), get an error or EOF on a read, or the timer expires. + */ + + do { + /* We use a 2 second timeout because current (Feb 97) browsers + * fail to close a connection after the server closes it. Thus, + * to avoid keeping the child busy, we are only lingering long enough + * for a client that is actively sending data on a connection. + * This should be sufficient unless the connection is massively + * losing packets, in which case we might have missed the RST anyway. + * These parameters are reset on each pass, since they might be + * changed by select. + */ + FD_SET(lsd, &lfds); + tv.tv_sec = 2; + tv.tv_usec = 0; + + select_rv = ap_select(lsd + 1, &lfds, NULL, NULL, &tv); + + } while ((select_rv > 0) && + (read(lsd, dummybuf, sizeof dummybuf) > 0)); + + /* Should now have seen final ack. Safe to finally kill socket */ + + ap_bclose(r->connection->client); + + ap_kill_timeout(r); +} +#endif /* ndef NO_LINGCLOSE */ + +/***************************************************************** + * dealing with other children + */ + +#ifndef NO_OTHER_CHILD +API_EXPORT(void) ap_register_other_child(int pid, + void (*maintenance) (int reason, void *, ap_wait_t status), + void *data, int write_fd) +{ + other_child_rec *ocr; + + ocr = ap_palloc(pconf, sizeof(*ocr)); + ocr->pid = pid; + ocr->maintenance = maintenance; + ocr->data = data; + ocr->write_fd = write_fd; + ocr->next = other_children; + other_children = ocr; +} + +/* note that since this can be called by a maintenance function while we're + * scanning the other_children list, all scanners should protect themself + * by loading ocr->next before calling any maintenance function. + */ +API_EXPORT(void) ap_unregister_other_child(void *data) +{ + other_child_rec **pocr, *nocr; + + for (pocr = &other_children; *pocr; pocr = &(*pocr)->next) { + if ((*pocr)->data == data) { + nocr = (*pocr)->next; + (*(*pocr)->maintenance) (OC_REASON_UNREGISTER, (*pocr)->data, -1); + *pocr = nocr; + /* XXX: um, well we've just wasted some space in pconf ? */ + return; + } + } +} + +/* test to ensure that the write_fds are all still writable, otherwise + * invoke the maintenance functions as appropriate */ +static void probe_writable_fds(void) +{ + fd_set writable_fds; + int fd_max; + other_child_rec *ocr, *nocr; + struct timeval tv; + int rc; + + if (other_children == NULL) + return; + + fd_max = 0; + FD_ZERO(&writable_fds); + do { + for (ocr = other_children; ocr; ocr = ocr->next) { + if (ocr->write_fd == -1) + continue; + FD_SET(ocr->write_fd, &writable_fds); + if (ocr->write_fd > fd_max) { + fd_max = ocr->write_fd; + } + } + if (fd_max == 0) + return; + + tv.tv_sec = 0; + tv.tv_usec = 0; + rc = ap_select(fd_max + 1, NULL, &writable_fds, NULL, &tv); + } while (rc == -1 && errno == EINTR); + + if (rc == -1) { + /* XXX: uhh this could be really bad, we could have a bad file + * descriptor due to a bug in one of the maintenance routines */ + ap_log_unixerr("probe_writable_fds", "select", + "could not probe writable fds", server_conf); + return; + } + if (rc == 0) + return; + + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->write_fd == -1) + continue; + if (FD_ISSET(ocr->write_fd, &writable_fds)) + continue; + (*ocr->maintenance) (OC_REASON_UNWRITABLE, ocr->data, -1); + } +} + +/* possibly reap an other_child, return 0 if yes, -1 if not */ +static int reap_other_child(int pid, ap_wait_t status) +{ + other_child_rec *ocr, *nocr; + + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->pid != pid) + continue; + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status); + return 0; + } + return -1; +} +#endif + +/***************************************************************** + * + * Dealing with the scoreboard... a lot of these variables are global + * only to avoid getting clobbered by the longjmp() that happens when + * a hard timeout expires... + * + * We begin with routines which deal with the file itself... + */ + +#ifdef MULTITHREAD +/* + * In the multithreaded mode, have multiple threads - not multiple + * processes that need to talk to each other. Just use a simple + * malloc. But let the routines that follow, think that you have + * shared memory (so they use memcpy etc.) + */ + +static void reinit_scoreboard(pool *p) +{ + ap_assert(!ap_scoreboard_image); + ap_scoreboard_image = (scoreboard *) malloc(SCOREBOARD_SIZE); + if (ap_scoreboard_image == NULL) { + fprintf(stderr, "Ouch! Out of memory reiniting scoreboard!\n"); + } + memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); +} + +void cleanup_scoreboard(void) +{ + ap_assert(ap_scoreboard_image); + free(ap_scoreboard_image); + ap_scoreboard_image = NULL; +} + +API_EXPORT(void) ap_sync_scoreboard_image(void) +{ +} + + +#else /* MULTITHREAD */ +#if defined(USE_OS2_SCOREBOARD) + +/* The next two routines are used to access shared memory under OS/2. */ +/* This requires EMX v09c to be installed. */ + +caddr_t create_shared_heap(const char *name, size_t size) +{ + ULONG rc; + void *mem; + Heap_t h; + + rc = DosAllocSharedMem(&mem, name, size, + PAG_COMMIT | PAG_READ | PAG_WRITE); + if (rc != 0) + return NULL; + h = _ucreate(mem, size, !_BLOCK_CLEAN, _HEAP_REGULAR | _HEAP_SHARED, + NULL, NULL); + if (h == NULL) + DosFreeMem(mem); + return (caddr_t) h; +} + +caddr_t get_shared_heap(const char *Name) +{ + + PVOID BaseAddress; /* Pointer to the base address of + the shared memory object */ + ULONG AttributeFlags; /* Flags describing characteristics + of the shared memory object */ + APIRET rc; /* Return code */ + + /* Request read and write access to */ + /* the shared memory object */ + AttributeFlags = PAG_WRITE | PAG_READ; + + rc = DosGetNamedSharedMem(&BaseAddress, Name, AttributeFlags); + + if (rc != 0) { + printf("DosGetNamedSharedMem error: return code = %ld", rc); + return 0; + } + + return BaseAddress; +} + +static void setup_shared_mem(pool *p) +{ + caddr_t m; + + int rc; + + m = (caddr_t) create_shared_heap("\\SHAREMEM\\SCOREBOARD", SCOREBOARD_SIZE); + if (m == 0) { + fprintf(stderr, "%s: Could not create OS/2 Shared memory pool.\n", + ap_server_argv0); + exit(APEXIT_INIT); + } + + rc = _uopen((Heap_t) m); + if (rc != 0) { + fprintf(stderr, + "%s: Could not uopen() newly created OS/2 Shared memory pool.\n", + ap_server_argv0); + } + ap_scoreboard_image = (scoreboard *) m; + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ + caddr_t m; + int rc; + + m = (caddr_t) get_shared_heap("\\SHAREMEM\\SCOREBOARD"); + if (m == 0) { + fprintf(stderr, "%s: Could not find existing OS/2 Shared memory pool.\n", + ap_server_argv0); + exit(APEXIT_INIT); + } + + rc = _uopen((Heap_t) m); + ap_scoreboard_image = (scoreboard *) m; +} + +#elif defined(USE_POSIX_SCOREBOARD) +#include +/* + * POSIX 1003.4 style + * + * Note 1: + * As of version 4.23A, shared memory in QNX must reside under /dev/shmem, + * where no subdirectories allowed. + * + * POSIX shm_open() and shm_unlink() will take care about this issue, + * but to avoid confusion, I suggest to redefine scoreboard file name + * in httpd.conf to cut "logs/" from it. With default setup actual name + * will be "/dev/shmem/logs.apache_status". + * + * If something went wrong and Apache did not unlinked this object upon + * exit, you can remove it manually, using "rm -f" command. + * + * Note 2: + * in QNX defines MAP_ANON, but current implementation + * does NOT support BSD style anonymous mapping. So, the order of + * conditional compilation is important: + * this #ifdef section must be ABOVE the next one (BSD style). + * + * I tested this stuff and it works fine for me, but if it provides + * trouble for you, just comment out USE_MMAP_SCOREBOARD in QNX section + * of ap_config.h + * + * June 5, 1997, + * Igor N. Kovalenko -- infoh@mail.wplus.net + */ + +static void cleanup_shared_mem(void *d) +{ + shm_unlink(ap_scoreboard_fname); +} + +static void setup_shared_mem(pool *p) +{ + char buf[512]; + caddr_t m; + int fd; + + fd = shm_open(ap_scoreboard_fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + ap_snprintf(buf, sizeof(buf), "%s: could not open(create) scoreboard", + ap_server_argv0); + perror(buf); + exit(APEXIT_INIT); + } + if (ltrunc(fd, (off_t) SCOREBOARD_SIZE, SEEK_SET) == -1) { + ap_snprintf(buf, sizeof(buf), "%s: could not ltrunc scoreboard", + ap_server_argv0); + perror(buf); + shm_unlink(ap_scoreboard_fname); + exit(APEXIT_INIT); + } + if ((m = (caddr_t) mmap((caddr_t) 0, + (size_t) SCOREBOARD_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, (off_t) 0)) == (caddr_t) - 1) { + ap_snprintf(buf, sizeof(buf), "%s: cannot mmap scoreboard", + ap_server_argv0); + perror(buf); + shm_unlink(ap_scoreboard_fname); + exit(APEXIT_INIT); + } + close(fd); + ap_register_cleanup(p, NULL, cleanup_shared_mem, ap_null_cleanup); + ap_scoreboard_image = (scoreboard *) m; + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ +} + +#elif defined(USE_MMAP_SCOREBOARD) + +static void setup_shared_mem(pool *p) +{ + caddr_t m; + +#if defined(MAP_ANON) +/* BSD style */ +#ifdef CONVEXOS11 + /* + * 9-Aug-97 - Jeff Venters (venters@convex.hp.com) + * ConvexOS maps address space as follows: + * 0x00000000 - 0x7fffffff : Kernel + * 0x80000000 - 0xffffffff : User + * Start mmapped area 1GB above start of text. + * + * Also, the length requires a pointer as the actual length is + * returned (rounded up to a page boundary). + */ + { + unsigned len = SCOREBOARD_SIZE; + + m = mmap((caddr_t) 0xC0000000, &len, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, NOFD, 0); + } +#elif defined(MAP_TMPFILE) + { + char mfile[] = "/tmp/apache_shmem_XXXX"; + int fd = mkstemp(mfile); + if (fd == -1) { + perror("open"); + fprintf(stderr, "%s: Could not open %s\n", ap_server_argv0, mfile); + exit(APEXIT_INIT); + } + m = mmap((caddr_t) 0, SCOREBOARD_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m == (caddr_t) - 1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap %s\n", ap_server_argv0, mfile); + exit(APEXIT_INIT); + } + close(fd); + unlink(mfile); + } +#else + m = mmap((caddr_t) 0, SCOREBOARD_SIZE, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); +#endif + if (m == (caddr_t) - 1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0); + exit(APEXIT_INIT); + } +#else +/* Sun style */ + int fd; + + fd = open("/dev/zero", O_RDWR); + if (fd == -1) { + perror("open"); + fprintf(stderr, "%s: Could not open /dev/zero\n", ap_server_argv0); + exit(APEXIT_INIT); + } + m = mmap((caddr_t) 0, SCOREBOARD_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m == (caddr_t) - 1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap /dev/zero\n", ap_server_argv0); + exit(APEXIT_INIT); + } + close(fd); +#endif + ap_scoreboard_image = (scoreboard *) m; + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ +} + +#elif defined(USE_SHMGET_SCOREBOARD) +static key_t shmkey = IPC_PRIVATE; +static int shmid = -1; + +static void setup_shared_mem(pool *p) +{ + struct shmid_ds shmbuf; +#ifdef MOVEBREAK + char *obrk; +#endif + + if ((shmid = shmget(shmkey, SCOREBOARD_SIZE, IPC_CREAT | SHM_R | SHM_W)) == -1) { +#ifdef LINUX + if (errno == ENOSYS) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "Your kernel was built without CONFIG_SYSVIPC\n" + "%s: Please consult the Apache FAQ for details", + ap_server_argv0); + } +#endif + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "could not call shmget"); + exit(APEXIT_INIT); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "created shared memory segment #%d", shmid); + +#ifdef MOVEBREAK + /* + * Some SysV systems place the shared segment WAY too close + * to the dynamic memory break point (sbrk(0)). This severely + * limits the use of malloc/sbrk in the program since sbrk will + * refuse to move past that point. + * + * To get around this, we move the break point "way up there", + * attach the segment and then move break back down. Ugly + */ + if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "sbrk() could not move break"); + } +#endif + +#define BADSHMAT ((scoreboard *)(-1)) + if ((ap_scoreboard_image = (scoreboard *) shmat(shmid, 0, 0)) == BADSHMAT) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, "shmat error"); + /* + * We exit below, after we try to remove the segment + */ + } + else { /* only worry about permissions if we attached the segment */ + if (shmctl(shmid, IPC_STAT, &shmbuf) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "shmctl() could not stat segment #%d", shmid); + } + else { + shmbuf.shm_perm.uid = ap_user_id; + shmbuf.shm_perm.gid = ap_group_id; + if (shmctl(shmid, IPC_SET, &shmbuf) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "shmctl() could not set segment #%d", shmid); + } + } + } + /* + * We must avoid leaving segments in the kernel's + * (small) tables. + */ + if (shmctl(shmid, IPC_RMID, NULL) != 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "shmctl: IPC_RMID: could not remove shared memory segment #%d", + shmid); + } + if (ap_scoreboard_image == BADSHMAT) /* now bailout */ + exit(APEXIT_INIT); + +#ifdef MOVEBREAK + if (obrk == (char *) -1) + return; /* nothing else to do */ + if (sbrk(-(MOVEBREAK)) == (char *) -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "sbrk() could not move break back"); + } +#endif + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ +} + +#elif defined(USE_TPF_SCOREBOARD) + +static void cleanup_scoreboard_heap() +{ + int rv; + rv = rsysc(ap_scoreboard_image, SCOREBOARD_FRAMES, SCOREBOARD_NAME); + if(rv == RSYSC_ERROR) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "rsysc() could not release scoreboard system heap"); + } +} + +static void setup_shared_mem(pool *p) +{ + cinfc(CINFC_WRITE, CINFC_CMMCTK2); + ap_scoreboard_image = (scoreboard *) gsysc(SCOREBOARD_FRAMES, SCOREBOARD_NAME); + + if (!ap_scoreboard_image) { + fprintf(stderr, "httpd: Could not create scoreboard system heap storage.\n"); + exit(APEXIT_INIT); + } + + ap_register_cleanup(p, NULL, cleanup_scoreboard_heap, ap_null_cleanup); + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ + cinfc(CINFC_WRITE, CINFC_CMMCTK2); +} + +#else +#define SCOREBOARD_FILE +static scoreboard _scoreboard_image; +static int scoreboard_fd = -1; + +/* XXX: things are seriously screwed if we ever have to do a partial + * read or write ... we could get a corrupted scoreboard + */ +static int force_write(int fd, void *buffer, int bufsz) +{ + int rv, orig_sz = bufsz; + + do { + rv = write(fd, buffer, bufsz); + if (rv > 0) { + buffer = (char *) buffer + rv; + bufsz -= rv; + } + } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR)); + + return rv < 0 ? rv : orig_sz - bufsz; +} + +static int force_read(int fd, void *buffer, int bufsz) +{ + int rv, orig_sz = bufsz; + + do { + rv = read(fd, buffer, bufsz); + if (rv > 0) { + buffer = (char *) buffer + rv; + bufsz -= rv; + } + } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR)); + + return rv < 0 ? rv : orig_sz - bufsz; +} + +static void cleanup_scoreboard_file(void *foo) +{ + unlink(ap_scoreboard_fname); +} + +void reopen_scoreboard(pool *p) +{ + if (scoreboard_fd != -1) + ap_pclosef(p, scoreboard_fd); + +#ifdef TPF + ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname); +#endif /* TPF */ + scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0666); + if (scoreboard_fd == -1) { + perror(ap_scoreboard_fname); + fprintf(stderr, "Cannot open scoreboard file:\n"); + clean_child_exit(1); + } +} +#endif + +/* Called by parent process */ +static void reinit_scoreboard(pool *p) +{ + int running_gen = 0; + if (ap_scoreboard_image) + running_gen = ap_scoreboard_image->global.running_generation; + +#ifndef SCOREBOARD_FILE + if (ap_scoreboard_image == NULL) { + setup_shared_mem(p); + } + memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); + ap_scoreboard_image->global.running_generation = running_gen; +#else + ap_scoreboard_image = &_scoreboard_image; + ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname); + + scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0644); + if (scoreboard_fd == -1) { + perror(ap_scoreboard_fname); + fprintf(stderr, "Cannot open scoreboard file:\n"); + exit(APEXIT_INIT); + } + ap_register_cleanup(p, NULL, cleanup_scoreboard_file, ap_null_cleanup); + + memset((char *) ap_scoreboard_image, 0, sizeof(*ap_scoreboard_image)); + ap_scoreboard_image->global.running_generation = running_gen; + force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image)); +#endif +} + +/* Routines called to deal with the scoreboard image + * --- note that we do *not* need write locks, since update_child_status + * only updates a *single* record in place, and only one process writes to + * a given scoreboard slot at a time (either the child process owning that + * slot, or the parent, noting that the child has died). + * + * As a final note --- setting the score entry to getpid() is always safe, + * since when the parent is writing an entry, it's only noting SERVER_DEAD + * anyway. + */ + +ap_inline void ap_sync_scoreboard_image(void) +{ +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, 0L, 0); + force_read(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image)); +#endif +} + +#endif /* MULTITHREAD */ + +API_EXPORT(int) ap_exists_scoreboard_image(void) +{ + return (ap_scoreboard_image ? 1 : 0); +} + +static ap_inline void put_scoreboard_info(int child_num, + short_score *new_score_rec) +{ +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, (long) child_num * sizeof(short_score), 0); + force_write(scoreboard_fd, new_score_rec, sizeof(short_score)); +#endif +} + +/* a clean exit from the parent with proper cleanup */ +static void clean_parent_exit(int code) __attribute__((noreturn)); +static void clean_parent_exit(int code) +{ + /* Clear the pool - including any registered cleanups */ + ap_destroy_pool(pglobal); + exit(code); +} + +int ap_update_child_status(int child_num, int status, request_rec *r) +{ + int old_status; + short_score *ss; + + if (child_num < 0) + return -1; + + ap_check_signals(); + + ap_sync_scoreboard_image(); + ss = &ap_scoreboard_image->servers[child_num]; + old_status = ss->status; + ss->status = status; +#ifdef OPTIMIZE_TIMEOUTS + ++ss->cur_vtime; +#endif + + if (ap_extended_status) { +#ifndef OPTIMIZE_TIMEOUTS + ss->last_used = time(NULL); +#endif + if (status == SERVER_READY || status == SERVER_DEAD) { + /* + * Reset individual counters + */ + if (status == SERVER_DEAD) { + ss->my_access_count = 0L; + ss->my_bytes_served = 0L; + } + ss->conn_count = (unsigned short) 0; + ss->conn_bytes = (unsigned long) 0; + } + if (r) { + conn_rec *c = r->connection; + ap_cpystrn(ss->client, ap_get_remote_host(c, r->per_dir_config, + REMOTE_NOLOOKUP), sizeof(ss->client)); + if (r->the_request == NULL) { + ap_cpystrn(ss->request, "NULL", sizeof(ss->request)); + } else if (r->parsed_uri.password == NULL) { + ap_cpystrn(ss->request, r->the_request, sizeof(ss->request)); + } else { + /* Don't reveal the password in the server-status view */ + ap_cpystrn(ss->request, ap_pstrcat(r->pool, r->method, " ", + ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD), + r->assbackwards ? NULL : " ", r->protocol, NULL), + sizeof(ss->request)); + } + ss->vhostrec = r->server; + } + } + if (status == SERVER_STARTING && r == NULL) { + /* clean up the slot's vhostrec pointer (maybe re-used) + * and mark the slot as belonging to a new generation. + */ + ss->vhostrec = NULL; + ap_scoreboard_image->parent[child_num].generation = ap_my_generation; +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[child_num]), 0); + force_write(scoreboard_fd, &ap_scoreboard_image->parent[child_num], + sizeof(parent_score)); +#endif + } + put_scoreboard_info(child_num, ss); + + return old_status; +} + +static void update_scoreboard_global(void) +{ +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, + (char *) &ap_scoreboard_image->global -(char *) ap_scoreboard_image, 0); + force_write(scoreboard_fd, &ap_scoreboard_image->global, + sizeof ap_scoreboard_image->global); +#endif +} + +void ap_time_process_request(int child_num, int status) +{ + short_score *ss; +#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES) + struct tms tms_blk; +#endif + + if (child_num < 0) + return; + + ap_sync_scoreboard_image(); + ss = &ap_scoreboard_image->servers[child_num]; + + if (status == START_PREQUEST) { +#if defined(NO_GETTIMEOFDAY) +#ifndef NO_TIMES + if ((ss->start_time = times(&tms_blk)) == -1) +#endif /* NO_TIMES */ + ss->start_time = (clock_t) 0; +#else + if (gettimeofday(&ss->start_time, (struct timezone *) 0) < 0) + ss->start_time.tv_sec = + ss->start_time.tv_usec = 0L; +#endif + } + else if (status == STOP_PREQUEST) { +#if defined(NO_GETTIMEOFDAY) +#ifndef NO_TIMES + if ((ss->stop_time = times(&tms_blk)) == -1) +#endif + ss->stop_time = ss->start_time = (clock_t) 0; +#else + if (gettimeofday(&ss->stop_time, (struct timezone *) 0) < 0) + ss->stop_time.tv_sec = + ss->stop_time.tv_usec = + ss->start_time.tv_sec = + ss->start_time.tv_usec = 0L; +#endif + + } + + put_scoreboard_info(child_num, ss); +} + +static void increment_counts(int child_num, request_rec *r) +{ + long int bs = 0; + short_score *ss; + + ap_sync_scoreboard_image(); + ss = &ap_scoreboard_image->servers[child_num]; + + if (r->sent_bodyct) + ap_bgetopt(r->connection->client, BO_BYTECT, &bs); + +#ifndef NO_TIMES + times(&ss->times); +#endif + ss->access_count++; + ss->my_access_count++; + ss->conn_count++; + ss->bytes_served += (unsigned long) bs; + ss->my_bytes_served += (unsigned long) bs; + ss->conn_bytes += (unsigned long) bs; + + put_scoreboard_info(child_num, ss); +} + +static int find_child_by_pid(int pid) +{ + int i; + + for (i = 0; i < max_daemons_limit; ++i) + if (ap_scoreboard_image->parent[i].pid == pid) + return i; + + return -1; +} + +static void reclaim_child_processes(int terminate) +{ +#ifndef MULTITHREAD + int i, status; + long int waittime = 1024 * 16; /* in usecs */ + struct timeval tv; + int waitret, tries; + int not_dead_yet; +#ifndef NO_OTHER_CHILD + other_child_rec *ocr, *nocr; +#endif + + ap_sync_scoreboard_image(); + + for (tries = terminate ? 4 : 1; tries <= 9; ++tries) { + /* don't want to hold up progress any more than + * necessary, but we need to allow children a few moments to exit. + * Set delay with an exponential backoff. + */ + tv.tv_sec = waittime / 1000000; + tv.tv_usec = waittime % 1000000; + waittime = waittime * 4; + ap_select(0, NULL, NULL, NULL, &tv); + + /* now see who is done */ + not_dead_yet = 0; + for (i = 0; i < max_daemons_limit; ++i) { + int pid = ap_scoreboard_image->parent[i].pid; + + if (pid == my_pid || pid == 0) + continue; + + waitret = waitpid(pid, &status, WNOHANG); + if (waitret == pid || waitret == -1) { + ap_scoreboard_image->parent[i].pid = 0; + continue; + } + ++not_dead_yet; + switch (tries) { + case 1: /* 16ms */ + case 2: /* 82ms */ + break; + case 3: /* 344ms */ + /* perhaps it missed the SIGHUP, lets try again */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, + server_conf, + "child process %d did not exit, sending another SIGHUP", + pid); + kill(pid, SIGHUP); + waittime = 1024 * 16; + break; + case 4: /* 16ms */ + case 5: /* 82ms */ + case 6: /* 344ms */ + break; + case 7: /* 1.4sec */ + /* ok, now it's being annoying */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, + server_conf, + "child process %d still did not exit, sending a SIGTERM", + pid); + kill(pid, SIGTERM); + break; + case 8: /* 6 sec */ + /* die child scum */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "child process %d still did not exit, sending a SIGKILL", + pid); + kill(pid, SIGKILL); + break; + case 9: /* 14 sec */ + /* gave it our best shot, but alas... If this really + * is a child we are trying to kill and it really hasn't + * exited, we will likely fail to bind to the port + * after the restart. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "could not make child process %d exit, " + "attempting to continue anyway", pid); + break; + } + } +#ifndef NO_OTHER_CHILD + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->pid == -1) + continue; + + waitret = waitpid(ocr->pid, &status, WNOHANG); + if (waitret == ocr->pid) { + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status); + } + else if (waitret == 0) { + (*ocr->maintenance) (OC_REASON_RESTART, ocr->data, -1); + ++not_dead_yet; + } + else if (waitret == -1) { + /* uh what the heck? they didn't call unregister? */ + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_LOST, ocr->data, -1); + } + } +#endif + if (!not_dead_yet) { + /* nothing left to wait for */ + break; + } + } +#endif /* ndef MULTITHREAD */ +} + + +#if defined(NEED_WAITPID) +/* + Systems without a real waitpid sometimes lose a child's exit while waiting + for another. Search through the scoreboard for missing children. + */ +int reap_children(ap_wait_t *status) +{ + int n, pid; + + for (n = 0; n < max_daemons_limit; ++n) { + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->servers[n].status != SERVER_DEAD && + kill((pid = ap_scoreboard_image->parent[n].pid), 0) == -1) { + ap_update_child_status(n, SERVER_DEAD, NULL); + /* just mark it as having a successful exit status */ + bzero((char *) status, sizeof(ap_wait_t)); + return(pid); + } + } + return 0; +} +#endif + +/* Finally, this routine is used by the caretaker process to wait for + * a while... + */ + +/* number of calls to wait_or_timeout between writable probes */ +#ifndef INTERVAL_OF_WRITABLE_PROBES +#define INTERVAL_OF_WRITABLE_PROBES 10 +#endif +static int wait_or_timeout_counter; + +static int wait_or_timeout(ap_wait_t *status) +{ +#ifdef WIN32 +#define MAXWAITOBJ MAXIMUM_WAIT_OBJECTS + HANDLE h[MAXWAITOBJ]; + int e[MAXWAITOBJ]; + int round, pi, hi, rv, err; + for (round = 0; round <= (HARD_SERVER_LIMIT - 1) / MAXWAITOBJ + 1; round++) { + hi = 0; + for (pi = round * MAXWAITOBJ; + (pi < (round + 1) * MAXWAITOBJ) && (pi < HARD_SERVER_LIMIT); + pi++) { + if (ap_scoreboard_image->servers[pi].status != SERVER_DEAD) { + e[hi] = pi; + h[hi++] = (HANDLE) ap_scoreboard_image->parent[pi].pid; + } + + } + if (hi > 0) { + rv = WaitForMultipleObjects(hi, h, FALSE, 10000); + if (rv == -1) + err = GetLastError(); + if ((WAIT_OBJECT_0 <= (unsigned int) rv) && ((unsigned int) rv < (WAIT_OBJECT_0 + hi))) + return (ap_scoreboard_image->parent[e[rv - WAIT_OBJECT_0]].pid); + else if ((WAIT_ABANDONED_0 <= (unsigned int) rv) && ((unsigned int) rv < (WAIT_ABANDONED_0 + hi))) + return (ap_scoreboard_image->parent[e[rv - WAIT_ABANDONED_0]].pid); + + } + } + return (-1); + +#else /* WIN32 */ + struct timeval tv; + int ret; + + ++wait_or_timeout_counter; + if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) { + wait_or_timeout_counter = 0; +#ifndef NO_OTHER_CHILD + probe_writable_fds(); +#endif + } + ret = waitpid(-1, status, WNOHANG); + if (ret == -1 && errno == EINTR) { + return -1; + } + if (ret > 0) { + return ret; + } +#ifdef NEED_WAITPID + if ((ret = reap_children(status)) > 0) { + return ret; + } +#endif + tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000; + tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000; + ap_select(0, NULL, NULL, NULL, &tv); + return -1; +#endif /* WIN32 */ +} + + +#if defined(NSIG) +#define NumSIG NSIG +#elif defined(_NSIG) +#define NumSIG _NSIG +#elif defined(__NSIG) +#define NumSIG __NSIG +#else +#define NumSIG 32 /* for 1998's unixes, this is still a good assumption */ +#endif + +#ifdef SYS_SIGLIST /* platform has sys_siglist[] */ +#define INIT_SIGLIST() /*nothing*/ +#else /* platform has no sys_siglist[], define our own */ +#define SYS_SIGLIST ap_sys_siglist +#define INIT_SIGLIST() siglist_init(); + +const char *ap_sys_siglist[NumSIG]; + +static void siglist_init(void) +{ + int sig; + + ap_sys_siglist[0] = "Signal 0"; +#ifdef SIGHUP + ap_sys_siglist[SIGHUP] = "Hangup"; +#endif +#ifdef SIGINT + ap_sys_siglist[SIGINT] = "Interrupt"; +#endif +#ifdef SIGQUIT + ap_sys_siglist[SIGQUIT] = "Quit"; +#endif +#ifdef SIGILL + ap_sys_siglist[SIGILL] = "Illegal instruction"; +#endif +#ifdef SIGTRAP + ap_sys_siglist[SIGTRAP] = "Trace/BPT trap"; +#endif +#ifdef SIGIOT + ap_sys_siglist[SIGIOT] = "IOT instruction"; +#endif +#ifdef SIGABRT + ap_sys_siglist[SIGABRT] = "Abort"; +#endif +#ifdef SIGEMT + ap_sys_siglist[SIGEMT] = "Emulator trap"; +#endif +#ifdef SIGFPE + ap_sys_siglist[SIGFPE] = "Arithmetic exception"; +#endif +#ifdef SIGKILL + ap_sys_siglist[SIGKILL] = "Killed"; +#endif +#ifdef SIGBUS + ap_sys_siglist[SIGBUS] = "Bus error"; +#endif +#ifdef SIGSEGV + ap_sys_siglist[SIGSEGV] = "Segmentation fault"; +#endif +#ifdef SIGSYS + ap_sys_siglist[SIGSYS] = "Bad system call"; +#endif +#ifdef SIGPIPE + ap_sys_siglist[SIGPIPE] = "Broken pipe"; +#endif +#ifdef SIGALRM + ap_sys_siglist[SIGALRM] = "Alarm clock"; +#endif +#ifdef SIGTERM + ap_sys_siglist[SIGTERM] = "Terminated"; +#endif +#ifdef SIGUSR1 + ap_sys_siglist[SIGUSR1] = "User defined signal 1"; +#endif +#ifdef SIGUSR2 + ap_sys_siglist[SIGUSR2] = "User defined signal 2"; +#endif +#ifdef SIGCLD + ap_sys_siglist[SIGCLD] = "Child status change"; +#endif +#ifdef SIGCHLD + ap_sys_siglist[SIGCHLD] = "Child status change"; +#endif +#ifdef SIGPWR + ap_sys_siglist[SIGPWR] = "Power-fail restart"; +#endif +#ifdef SIGWINCH + ap_sys_siglist[SIGWINCH] = "Window changed"; +#endif +#ifdef SIGURG + ap_sys_siglist[SIGURG] = "urgent socket condition"; +#endif +#ifdef SIGPOLL + ap_sys_siglist[SIGPOLL] = "Pollable event occurred"; +#endif +#ifdef SIGIO + ap_sys_siglist[SIGIO] = "socket I/O possible"; +#endif +#ifdef SIGSTOP + ap_sys_siglist[SIGSTOP] = "Stopped (signal)"; +#endif +#ifdef SIGTSTP + ap_sys_siglist[SIGTSTP] = "Stopped"; +#endif +#ifdef SIGCONT + ap_sys_siglist[SIGCONT] = "Continued"; +#endif +#ifdef SIGTTIN + ap_sys_siglist[SIGTTIN] = "Stopped (tty input)"; +#endif +#ifdef SIGTTOU + ap_sys_siglist[SIGTTOU] = "Stopped (tty output)"; +#endif +#ifdef SIGVTALRM + ap_sys_siglist[SIGVTALRM] = "virtual timer expired"; +#endif +#ifdef SIGPROF + ap_sys_siglist[SIGPROF] = "profiling timer expired"; +#endif +#ifdef SIGXCPU + ap_sys_siglist[SIGXCPU] = "exceeded cpu limit"; +#endif +#ifdef SIGXFSZ + ap_sys_siglist[SIGXFSZ] = "exceeded file size limit"; +#endif + for (sig=0; sig < sizeof(ap_sys_siglist)/sizeof(ap_sys_siglist[0]); ++sig) + if (ap_sys_siglist[sig] == NULL) + ap_sys_siglist[sig] = ""; +} +#endif /* platform has sys_siglist[] */ + + +/* handle all varieties of core dumping signals */ +static void sig_coredump(int sig) +{ + chdir(ap_coredump_dir); + signal(sig, SIG_DFL); +#ifndef WIN32 + kill(getpid(), sig); +#else + raise(sig); +#endif + /* At this point we've got sig blocked, because we're still inside + * the signal handler. When we leave the signal handler it will + * be unblocked, and we'll take the signal... and coredump or whatever + * is appropriate for this particular Unix. In addition the parent + * will see the real signal we received -- whereas if we called + * abort() here, the parent would only see SIGABRT. + */ +} + +/***************************************************************** + * Connection structures and accounting... + */ + +static void just_die(int sig) +{ /* SIGHUP to child process??? */ + /* if alarms are blocked we have to wait to die otherwise we might + * end up with corruption in alloc.c's internal structures */ + if (alarms_blocked) { + exit_after_unblock = 1; + } + else { + clean_child_exit(0); + } +} + +static int volatile usr1_just_die = 1; +static int volatile deferred_die; + +static void usr1_handler(int sig) +{ + if (usr1_just_die) { + just_die(sig); + } + deferred_die = 1; +} + +/* volatile just in case */ +static int volatile shutdown_pending; +static int volatile restart_pending; +static int volatile is_graceful; +ap_generation_t volatile ap_my_generation=0; + +#ifdef WIN32 +/* + * Signalling Apache on NT. + * + * Under Unix, Apache can be told to shutdown or restart by sending various + * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so + * we use "events" instead. The parent apache process goes into a loop + * where it waits forever for a set of events. Two of those events are + * called + * + * apPID_shutdown + * apPID_restart + * + * (where PID is the PID of the apache parent process). When one of these + * is signalled, the Apache parent performs the appropriate action. The events + * can become signalled through internal Apache methods (e.g. if the child + * finds a fatal error and needs to kill its parent), via the service + * control manager (the control thread will signal the shutdown event when + * requested to stop the Apache service), from the -k Apache command line, + * or from any external program which finds the Apache PID from the + * httpd.pid file. + * + * The signal_parent() function, below, is used to signal one of these events. + * It can be called by any child or parent process, since it does not + * rely on global variables. + * + * On entry, type gives the event to signal. 0 means shutdown, 1 means + * graceful restart. + */ + +static void signal_parent(int type) +{ + HANDLE e; + char *signal_name; + extern char signal_shutdown_name[]; + extern char signal_restart_name[]; + + /* after updating the shutdown_pending or restart flags, we need + * to wake up the parent process so it can see the changes. The + * parent will normally be waiting for either a child process + * to die, or for a signal on the "spache-signal" event. So set the + * "apache-signal" event here. + */ + + if (one_process) { + return; + } + + switch(type) { + case 0: signal_name = signal_shutdown_name; break; + case 1: signal_name = signal_restart_name; break; + default: return; + } + + APD2("signal_parent signalling event \"%s\"", signal_name); + + e = OpenEvent(EVENT_ALL_ACCESS, FALSE, signal_name); + if (!e) { + /* Um, problem, can't signal the parent, which means we can't + * signal ourselves to die. Ignore for now... + */ + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, + "OpenEvent on %s event", signal_name); + return; + } + if (SetEvent(e) == 0) { + /* Same problem as above */ + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, + "SetEvent on %s event", signal_name); + CloseHandle(e); + return; + } + CloseHandle(e); +} +#endif + +/* + * ap_start_shutdown() and ap_start_restart(), below, are a first stab at + * functions to initiate shutdown or restart without relying on signals. + * Previously this was initiated in sig_term() and restart() signal handlers, + * but we want to be able to start a shutdown/restart from other sources -- + * e.g. on Win32, from the service manager. Now the service manager can + * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that + * these functions can also be called by the child processes, since global + * variables are no longer used to pass on the required action to the parent. + */ + +void ap_start_shutdown(void) +{ +#ifndef WIN32 + if (shutdown_pending == 1) { + /* Um, is this _probably_ not an error, if the user has + * tried to do a shutdown twice quickly, so we won't + * worry about reporting it. + */ + return; + } + shutdown_pending = 1; +#else + signal_parent(0); /* get the parent process to wake up */ +#endif +} + +/* do a graceful restart if graceful == 1 */ +void ap_start_restart(int graceful) +{ +#ifndef WIN32 + if (restart_pending == 1) { + /* Probably not an error - don't bother reporting it */ + return; + } + restart_pending = 1; + is_graceful = graceful; +#else + signal_parent(1); /* get the parent process to wake up */ +#endif /* WIN32 */ +} + +static void sig_term(int sig) +{ + ap_start_shutdown(); +} + +static void restart(int sig) +{ +#ifndef WIN32 + ap_start_restart(sig == SIGUSR1); +#else + ap_start_restart(1); +#endif +} + +static void set_signals(void) +{ +#ifndef NO_USE_SIGACTION + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + if (!one_process) { + sa.sa_handler = sig_coredump; +#if defined(SA_ONESHOT) + sa.sa_flags = SA_ONESHOT; +#elif defined(SA_RESETHAND) + sa.sa_flags = SA_RESETHAND; +#endif + if (sigaction(SIGSEGV, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGSEGV)"); +#ifdef SIGBUS + if (sigaction(SIGBUS, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGBUS)"); +#endif +#ifdef SIGABORT + if (sigaction(SIGABORT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABORT)"); +#endif +#ifdef SIGABRT + if (sigaction(SIGABRT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABRT)"); +#endif +#ifdef SIGILL + if (sigaction(SIGILL, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGILL)"); +#endif + sa.sa_flags = 0; + } + sa.sa_handler = sig_term; + if (sigaction(SIGTERM, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGTERM)"); +#ifdef SIGINT + if (sigaction(SIGINT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGINT)"); +#endif +#ifdef SIGXCPU + sa.sa_handler = SIG_DFL; + if (sigaction(SIGXCPU, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXCPU)"); +#endif +#ifdef SIGXFSZ + sa.sa_handler = SIG_DFL; + if (sigaction(SIGXFSZ, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXFSZ)"); +#endif +#ifdef SIGPIPE + sa.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGPIPE)"); +#endif + + /* we want to ignore HUPs and USR1 while we're busy processing one */ + sigaddset(&sa.sa_mask, SIGHUP); + sigaddset(&sa.sa_mask, SIGUSR1); + sa.sa_handler = restart; + if (sigaction(SIGHUP, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGHUP)"); + if (sigaction(SIGUSR1, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGUSR1)"); +#else + if (!one_process) { + signal(SIGSEGV, sig_coredump); +#ifdef SIGBUS + signal(SIGBUS, sig_coredump); +#endif /* SIGBUS */ +#ifdef SIGABORT + signal(SIGABORT, sig_coredump); +#endif /* SIGABORT */ +#ifdef SIGABRT + signal(SIGABRT, sig_coredump); +#endif /* SIGABRT */ +#ifdef SIGILL + signal(SIGILL, sig_coredump); +#endif /* SIGILL */ +#ifdef SIGXCPU + signal(SIGXCPU, SIG_DFL); +#endif /* SIGXCPU */ +#ifdef SIGXFSZ + signal(SIGXFSZ, SIG_DFL); +#endif /* SIGXFSZ */ + } + + signal(SIGTERM, sig_term); +#ifdef SIGHUP + signal(SIGHUP, restart); +#endif /* SIGHUP */ +#ifdef SIGUSR1 + signal(SIGUSR1, restart); +#endif /* SIGUSR1 */ +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif /* SIGPIPE */ + +#endif +} + + +/***************************************************************** + * Here follows a long bunch of generic server bookkeeping stuff... + */ + +static void detach(void) +{ +#if !defined(WIN32) + int x; + + chdir("/"); +#if !defined(MPE) && !defined(OS2) && !defined(TPF) +/* Don't detach for MPE because child processes can't survive the death of + the parent. */ + if ((x = fork()) > 0) + exit(0); + else if (x == -1) { + perror("fork"); + fprintf(stderr, "%s: unable to fork new process\n", ap_server_argv0); + exit(1); + } + RAISE_SIGSTOP(DETACH); +#endif +#ifndef NO_SETSID + if ((pgrp = setsid()) == -1) { + perror("setsid"); + fprintf(stderr, "%s: setsid failed\n", ap_server_argv0); + exit(1); + } +#elif defined(NEXT) || defined(NEWSOS) + if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "%s: setpgrp or getpgrp failed\n", ap_server_argv0); + exit(1); + } +#elif defined(OS2) || defined(TPF) + /* OS/2 and TPF don't support process group IDs */ + pgrp = getpid(); +#elif defined(MPE) + /* MPE uses negative pid for process group */ + pgrp = -getpid(); +#else + if ((pgrp = setpgrp(getpid(), 0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "%s: setpgrp failed\n", ap_server_argv0); + exit(1); + } +#endif + + /* close out the standard file descriptors */ + if (freopen("/dev/null", "r", stdin) == NULL) { + fprintf(stderr, "%s: unable to replace stdin with /dev/null: %s\n", + ap_server_argv0, strerror(errno)); + /* continue anyhow -- note we can't close out descriptor 0 because we + * have nothing to replace it with, and if we didn't have a descriptor + * 0 the next file would be created with that value ... leading to + * havoc. + */ + } + if (freopen("/dev/null", "w", stdout) == NULL) { + fprintf(stderr, "%s: unable to replace stdout with /dev/null: %s\n", + ap_server_argv0, strerror(errno)); + } + /* stderr is a tricky one, we really want it to be the error_log, + * but we haven't opened that yet. So leave it alone for now and it'll + * be reopened moments later. + */ +#endif /* ndef WIN32 */ +} + +/* Set group privileges. + * + * Note that we use the username as set in the config files, rather than + * the lookup of to uid --- the same uid may have multiple passwd entries, + * with different sets of groups for each. + */ + +static void set_group_privs(void) +{ +#ifndef WIN32 + if (!geteuid()) { + char *name; + + /* Get username if passed as a uid */ + + if (ap_user_name[0] == '#') { + struct passwd *ent; + uid_t uid = atoi(&ap_user_name[1]); + + if ((ent = getpwuid(uid)) == NULL) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "getpwuid: couldn't determine user name from uid %u, " + "you probably need to modify the User directive", + (unsigned)uid); + clean_child_exit(APEXIT_CHILDFATAL); + } + + name = ent->pw_name; + } + else + name = ap_user_name; + +#if !defined(OS2) && !defined(TPF) + /* OS/2 and TPF don't support groups. */ + + /* + * Set the GID before initgroups(), since on some platforms + * setgid() is known to zap the group list. + */ + if (setgid(ap_group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setgid: unable to set group id to Group %u", + (unsigned)ap_group_id); + clean_child_exit(APEXIT_CHILDFATAL); + } + + /* Reset `groups' attributes. */ + + if (initgroups(name, ap_group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "initgroups: unable to set groups for User %s " + "and Group %u", name, (unsigned)ap_group_id); + clean_child_exit(APEXIT_CHILDFATAL); + } +#ifdef MULTIPLE_GROUPS + if (getgroups(NGROUPS_MAX, group_id_list) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "getgroups: unable to get group list"); + clean_child_exit(APEXIT_CHILDFATAL); + } +#endif /* MULTIPLE_GROUPS */ +#endif /* !defined(OS2) && !defined(TPF) */ + } +#endif /* ndef WIN32 */ +} + +/* check to see if we have the 'suexec' setuid wrapper installed */ +static int init_suexec(void) +{ +#ifndef WIN32 + struct stat wrapper; + + if ((stat(SUEXEC_BIN, &wrapper)) != 0) + return (ap_suexec_enabled); + + if ((wrapper.st_mode & S_ISUID) && wrapper.st_uid == 0) { + ap_suexec_enabled = 1; + } +#endif /* ndef WIN32 */ + return (ap_suexec_enabled); +} + +/***************************************************************** + * Connection structures and accounting... + */ + + +static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout, + const struct sockaddr_in *remaddr, + const struct sockaddr_in *saddr, + int child_num) +{ + conn_rec *conn = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec)); + + /* Got a connection structure, so initialize what fields we can + * (the rest are zeroed out by pcalloc). + */ + + conn->child_num = child_num; + + conn->pool = p; + conn->local_addr = *saddr; + conn->local_ip = ap_pstrdup(conn->pool, + inet_ntoa(conn->local_addr.sin_addr)); + conn->server = server; /* just a guess for now */ + ap_update_vhost_given_ip(conn); + conn->base_server = conn->server; + conn->client = inout; + + conn->remote_addr = *remaddr; + conn->remote_ip = ap_pstrdup(conn->pool, + inet_ntoa(conn->remote_addr.sin_addr)); + + return conn; +} + +#if defined(TCP_NODELAY) && !defined(MPE) && !defined(TPF) +static void sock_disable_nagle(int s) +{ + /* The Nagle algorithm says that we should delay sending partial + * packets in hopes of getting more data. We don't want to do + * this; we are not telnet. There are bad interactions between + * persistent connections and Nagle's algorithm that have very severe + * performance penalties. (Failing to disable Nagle is not much of a + * problem with simple HTTP.) + * + * In spite of these problems, failure here is not a shooting offense. + */ + int just_say_no = 1; + + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no, + sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "setsockopt: (TCP_NODELAY)"); + } +} + +#else +#define sock_disable_nagle(s) /* NOOP */ +#endif + + +static int make_sock(pool *p, const struct sockaddr_in *server) +{ + int s; + int one = 1; + char addr[512]; + + if (server->sin_addr.s_addr != htonl(INADDR_ANY)) + ap_snprintf(addr, sizeof(addr), "address %s port %d", + inet_ntoa(server->sin_addr), ntohs(server->sin_port)); + else + ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port)); + + /* note that because we're about to slack we don't use psocket */ + ap_block_alarms(); + if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: failed to get a socket for %s", addr); + ap_unblock_alarms(); + exit(1); + } + + /* Solaris (probably versions 2.4, 2.5, and 2.5.1 with various levels + * of tcp patches) has some really weird bugs where if you dup the + * socket now it breaks things across SIGHUP restarts. It'll either + * be unable to bind, or it won't respond. + */ +#if defined (SOLARIS2) && SOLARIS2 < 260 +#define WORKAROUND_SOLARIS_BUG +#endif + + /* PR#1282 Unixware 1.x appears to have the same problem as solaris */ +#if defined (UW) && UW < 200 +#define WORKAROUND_SOLARIS_BUG +#endif + + /* PR#1973 NCR SVR4 systems appear to have the same problem */ +#if defined (MPRAS) +#define WORKAROUND_SOLARIS_BUG +#endif + +#ifndef WORKAROUND_SOLARIS_BUG +#ifndef BEOS /* this won't work for BeOS sockets!! */ + s = ap_slack(s, AP_SLACK_HIGH); +#endif + + ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ +#ifdef TPF + os_note_additional_cleanups(p, s); +#endif /* TPF */ +#endif + +#ifndef MPE +/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */ +#ifndef _OSD_POSIX + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr); +#ifdef BEOS + closesocket(s); +#else + close(s); +#endif + ap_unblock_alarms(); + return -1; + } +#endif /*_OSD_POSIX*/ + one = 1; +#ifdef SO_KEEPALIVE + if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr); +#ifdef BEOS + closesocket(s); +#else + close(s); +#endif + + ap_unblock_alarms(); + return -1; + } +#endif +#endif + + sock_disable_nagle(s); + sock_enable_linger(s); + + /* + * To send data over high bandwidth-delay connections at full + * speed we must force the TCP window to open wide enough to keep the + * pipe full. The default window size on many systems + * is only 4kB. Cross-country WAN connections of 100ms + * at 1Mb/s are not impossible for well connected sites. + * If we assume 100ms cross-country latency, + * a 4kB buffer limits throughput to 40kB/s. + * + * To avoid this problem I've added the SendBufferSize directive + * to allow the web master to configure send buffer size. + * + * The trade-off of larger buffers is that more kernel memory + * is consumed. YMMV, know your customers and your network! + * + * -John Heidemann 25-Oct-96 + * + * If no size is specified, use the kernel default. + */ +#ifndef BEOS /* BeOS does not support SO_SNDBUF */ + if (server_conf->send_buffer_size) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, + (char *) &server_conf->send_buffer_size, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "make_sock: failed to set SendBufferSize for %s, " + "using default", addr); + /* not a fatal error */ + } + } +#endif + +#ifdef MPE +/* MPE requires CAP=PM and GETPRIVMODE to bind to ports less than 1024 */ + if (ntohs(server->sin_port) < 1024) + GETPRIVMODE(); +#endif + + if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: could not bind to %s", addr); +#ifdef MPE + if (ntohs(server->sin_port) < 1024) + GETUSERMODE(); +#endif + +#ifdef BEOS + closesocket(s); +#else + close(s); +#endif + ap_unblock_alarms(); + exit(1); + } +#ifdef MPE + if (ntohs(server->sin_port) < 1024) + GETUSERMODE(); +#endif + + if (listen(s, ap_listenbacklog) == -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "make_sock: unable to listen for connections on %s", addr); +#ifdef BEOS + closesocket(s); +#else + close(s); +#endif + ap_unblock_alarms(); + exit(1); + } + +#ifdef WORKAROUND_SOLARIS_BUG + s = ap_slack(s, AP_SLACK_HIGH); + + ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ +#endif + ap_unblock_alarms(); + +#ifdef CHECK_FD_SETSIZE + /* protect various fd_sets */ + if (s >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "make_sock: problem listening on %s, filedescriptor (%u) " + "larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", addr, s, FD_SETSIZE); +#ifdef BEOS + closesocket(s); +#else + close(s); +#endif + return -1; + } +#endif + + return s; +} + + +/* + * During a restart we keep track of the old listeners here, so that we + * can re-use the sockets. We have to do this because we won't be able + * to re-open the sockets ("Address already in use"). + * + * Unlike the listeners ring, old_listeners is a NULL terminated list. + * + * copy_listeners() makes the copy, find_listener() finds an old listener + * and close_unused_listener() cleans up whatever wasn't used. + */ +static listen_rec *old_listeners; + +/* unfortunately copy_listeners may be called before listeners is a ring */ +static void copy_listeners(pool *p) +{ + listen_rec *lr; + + ap_assert(old_listeners == NULL); + if (ap_listeners == NULL) { + return; + } + lr = ap_listeners; + do { + listen_rec *nr = malloc(sizeof *nr); + if (nr == NULL) { + fprintf(stderr, "Ouch! malloc failed in copy_listeners()\n"); + exit(1); + } + *nr = *lr; + ap_kill_cleanups_for_socket(p, nr->fd); + nr->next = old_listeners; + ap_assert(!nr->used); + old_listeners = nr; + lr = lr->next; + } while (lr && lr != ap_listeners); +} + + +static int find_listener(listen_rec *lr) +{ + listen_rec *or; + + for (or = old_listeners; or; or = or->next) { + if (!memcmp(&or->local_addr, &lr->local_addr, sizeof(or->local_addr))) { + or->used = 1; + return or->fd; + } + } + return -1; +} + + +static void close_unused_listeners(void) +{ + listen_rec *or, *next; + + for (or = old_listeners; or; or = next) { + next = or->next; + if (!or->used) + closesocket(or->fd); + free(or); + } + old_listeners = NULL; +} + + +/* open sockets, and turn the listeners list into a singly linked ring */ +static void setup_listeners(pool *p) +{ + listen_rec *lr; + int fd; + + listenmaxfd = -1; + FD_ZERO(&listenfds); + lr = ap_listeners; + for (;;) { + fd = find_listener(lr); + if (fd < 0) { + fd = make_sock(p, &lr->local_addr); + } + else { + ap_note_cleanups_for_socket(p, fd); + } + if (fd >= 0) { + FD_SET(fd, &listenfds); + if (fd > listenmaxfd) + listenmaxfd = fd; + } + lr->fd = fd; + if (lr->next == NULL) + break; + lr = lr->next; + } + /* turn the list into a ring */ + lr->next = ap_listeners; + head_listener = ap_listeners; + close_unused_listeners(); + +#ifdef NO_SERIALIZED_ACCEPT + /* warn them about the starvation problem if they're using multiple + * sockets + */ + if (ap_listeners->next != ap_listeners) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, NULL, + "You cannot use multiple Listens safely on your system, " + "proceeding anyway. See src/PORTING, search for " + "SERIALIZED_ACCEPT."); + } +#endif +} + + +/* + * Find a listener which is ready for accept(). This advances the + * head_listener global. + */ +static ap_inline listen_rec *find_ready_listener(fd_set * main_fds) +{ + listen_rec *lr; + + lr = head_listener; + do { + if (FD_ISSET(lr->fd, main_fds)) { + head_listener = lr->next; + return (lr); + } + lr = lr->next; + } while (lr != head_listener); + return NULL; +} + + +#ifdef WIN32 +static int s_iInitCount = 0; + +static int AMCSocketInitialize(void) +{ + int iVersionRequested; + WSADATA wsaData; + int err; + + if (s_iInitCount > 0) { + s_iInitCount++; + return (0); + } + else if (s_iInitCount < 0) + return (s_iInitCount); + + /* s_iInitCount == 0. Do the initailization */ + iVersionRequested = MAKEWORD(1, 1); + err = WSAStartup((WORD) iVersionRequested, &wsaData); + if (err) { + s_iInitCount = -1; + return (s_iInitCount); + } + if (LOBYTE(wsaData.wVersion) != 1 || + HIBYTE(wsaData.wVersion) != 1) { + s_iInitCount = -2; + WSACleanup(); + return (s_iInitCount); + } + + s_iInitCount++; + return (s_iInitCount); + +} + + +static void AMCSocketCleanup(void) +{ + if (--s_iInitCount == 0) + WSACleanup(); + return; +} +#endif + +static void show_compile_settings(void) +{ + printf("Server version: %s\n", ap_get_server_version()); + printf("Server built: %s\n", ap_get_server_built()); + printf("Server's Module Magic Number: %u:%u\n", + MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR); + printf("Server compiled with....\n"); +#ifdef BIG_SECURITY_HOLE + printf(" -D BIG_SECURITY_HOLE\n"); +#endif +#ifdef SECURITY_HOLE_PASS_AUTHORIZATION + printf(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n"); +#endif +#ifdef HAVE_MMAP + printf(" -D HAVE_MMAP\n"); +#endif +#ifdef HAVE_SHMGET + printf(" -D HAVE_SHMGET\n"); +#endif +#ifdef USE_MMAP_SCOREBOARD + printf(" -D USE_MMAP_SCOREBOARD\n"); +#endif +#ifdef USE_SHMGET_SCOREBOARD + printf(" -D USE_SHMGET_SCOREBOARD\n"); +#endif +#ifdef USE_OS2_SCOREBOARD + printf(" -D USE_OS2_SCOREBOARD\n"); +#endif +#ifdef USE_POSIX_SCOREBOARD + printf(" -D USE_POSIX_SCOREBOARD\n"); +#endif +#ifdef USE_MMAP_FILES + printf(" -D USE_MMAP_FILES\n"); +#ifdef MMAP_SEGMENT_SIZE + printf(" -D MMAP_SEGMENT_SIZE=%ld\n",(long)MMAP_SEGMENT_SIZE); +#endif +#endif /*USE_MMAP_FILES*/ +#ifdef NO_WRITEV + printf(" -D NO_WRITEV\n"); +#endif +#ifdef NO_LINGCLOSE + printf(" -D NO_LINGCLOSE\n"); +#endif +#ifdef USE_FCNTL_SERIALIZED_ACCEPT + printf(" -D USE_FCNTL_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_FLOCK_SERIALIZED_ACCEPT + printf(" -D USE_FLOCK_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_USLOCK_SERIALIZED_ACCEPT + printf(" -D USE_USLOCK_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_SYSVSEM_SERIALIZED_ACCEPT + printf(" -D USE_SYSVSEM_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_PTHREAD_SERIALIZED_ACCEPT + printf(" -D USE_PTHREAD_SERIALIZED_ACCEPT\n"); +#endif +#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT + printf(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n"); +#endif +#ifdef NO_OTHER_CHILD + printf(" -D NO_OTHER_CHILD\n"); +#endif +#ifdef NO_RELIABLE_PIPED_LOGS + printf(" -D NO_RELIABLE_PIPED_LOGS\n"); +#endif +#ifdef BUFFERED_LOGS + printf(" -D BUFFERED_LOGS\n"); +#ifdef PIPE_BUF + printf(" -D PIPE_BUF=%ld\n",(long)PIPE_BUF); +#endif +#endif +#ifdef MULTITHREAD + printf(" -D MULTITHREAD\n"); +#endif +#ifdef CHARSET_EBCDIC + printf(" -D CHARSET_EBCDIC\n"); +#endif +#ifdef NEED_HASHBANG_EMUL + printf(" -D NEED_HASHBANG_EMUL\n"); +#endif +#ifdef SHARED_CORE + printf(" -D SHARED_CORE\n"); +#endif + +/* This list displays the compiled-in default paths: */ +#ifdef HTTPD_ROOT + printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n"); +#endif +#ifdef SUEXEC_BIN + printf(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n"); +#endif +#if defined(SHARED_CORE) && defined(SHARED_CORE_DIR) + printf(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n"); +#endif +#ifdef DEFAULT_PIDLOG + printf(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n"); +#endif +#ifdef DEFAULT_SCOREBOARD + printf(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n"); +#endif +#ifdef DEFAULT_LOCKFILE + printf(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n"); +#endif +#ifdef DEFAULT_XFERLOG + printf(" -D DEFAULT_XFERLOG=\"" DEFAULT_XFERLOG "\"\n"); +#endif +#ifdef DEFAULT_ERRORLOG + printf(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n"); +#endif +#ifdef TYPES_CONFIG_FILE + printf(" -D TYPES_CONFIG_FILE=\"" TYPES_CONFIG_FILE "\"\n"); +#endif +#ifdef SERVER_CONFIG_FILE + printf(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n"); +#endif +#ifdef ACCESS_CONFIG_FILE + printf(" -D ACCESS_CONFIG_FILE=\"" ACCESS_CONFIG_FILE "\"\n"); +#endif +#ifdef RESOURCE_CONFIG_FILE + printf(" -D RESOURCE_CONFIG_FILE=\"" RESOURCE_CONFIG_FILE "\"\n"); +#endif +} + + +/* Some init code that's common between win32 and unix... well actually + * some of it is #ifdef'd but was duplicated before anyhow. This stuff + * is still a mess. + */ +static void common_init(void) +{ + INIT_SIGLIST() +#ifdef AUX3 + (void) set42sig(); +#endif + +#ifdef WIN32 + /* Initialize the stupid sockets */ + AMCSocketInitialize(); +#endif /* WIN32 */ + + pglobal = ap_init_alloc(); + pconf = ap_make_sub_pool(pglobal); + plog = ap_make_sub_pool(pglobal); + ptrans = ap_make_sub_pool(pconf); + + ap_util_init(); + ap_util_uri_init(); + + pcommands = ap_make_sub_pool(NULL); + ap_server_pre_read_config = ap_make_array(pcommands, 1, sizeof(char *)); + ap_server_post_read_config = ap_make_array(pcommands, 1, sizeof(char *)); + ap_server_config_defines = ap_make_array(pcommands, 1, sizeof(char *)); +} + +#ifndef MULTITHREAD +/***************************************************************** + * Child process main loop. + * The following vars are static to avoid getting clobbered by longjmp(); + * they are really private to child_main. + */ + +static int srv; +static int csd; +static int dupped_csd; +static int requests_this_child; +static fd_set main_fds; + +API_EXPORT(void) ap_child_terminate(request_rec *r) +{ + r->connection->keepalive = 0; + requests_this_child = ap_max_requests_per_child = 1; +} + +static void child_main(int child_num_arg) +{ + NET_SIZE_T clen; + struct sockaddr sa_server; + struct sockaddr sa_client; + listen_rec *lr; + + /* All of initialization is a critical section, we don't care if we're + * told to HUP or USR1 before we're done initializing. For example, + * we could be half way through child_init_modules() when a restart + * signal arrives, and we'd have no real way to recover gracefully + * and exit properly. + * + * I suppose a module could take forever to initialize, but that would + * be either a broken module, or a broken configuration (i.e. network + * problems, file locking problems, whatever). -djg + */ + ap_block_alarms(); + + my_pid = getpid(); + csd = -1; + dupped_csd = -1; + my_child_num = child_num_arg; + requests_this_child = 0; + + /* Get a sub pool for global allocations in this child, so that + * we can have cleanups occur when the child exits. + */ + pchild = ap_make_sub_pool(pconf); + + /* needs to be done before we switch UIDs so we have permissions */ + reopen_scoreboard(pchild); + SAFE_ACCEPT(accept_mutex_child_init(pchild)); + + set_group_privs(); +#ifdef MPE + /* Only try to switch if we're running as MANAGER.SYS */ + if (geteuid() == 1 && ap_user_id > 1) { + GETPRIVMODE(); + if (setuid(ap_user_id) == -1) { + GETUSERMODE(); + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change to uid: %d", ap_user_id); + exit(1); + } + GETUSERMODE(); + } +#else + /* Only try to switch if we're running as root */ + if (!geteuid() && ( +#ifdef _OSD_POSIX + os_init_job_environment(server_conf, ap_user_name, one_process) != 0 || +#endif + setuid(ap_user_id) == -1)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change to uid: %ld", (long) ap_user_id); + clean_child_exit(APEXIT_CHILDFATAL); + } +#endif + + ap_child_init_modules(pchild, server_conf); + + /* done with the initialization critical section */ + ap_unblock_alarms(); + + (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL); + + /* + * Setup the jump buffers so that we can return here after a timeout + */ + ap_setjmp(jmpbuffer); +#ifndef OS2 +#ifdef SIGURG + signal(SIGURG, timeout); +#endif +#endif + signal(SIGALRM, alrm_handler); +#ifdef TPF + signal(SIGHUP, just_die); + signal(SIGTERM, just_die); + signal(SIGUSR1, just_die); +#endif /* TPF */ + +#ifdef OS2 +/* Stop Ctrl-C/Ctrl-Break signals going to child processes */ + { + unsigned long ulTimes; + DosSetSignalExceptionFocus(0, &ulTimes); + } +#endif + + while (1) { + BUFF *conn_io; + request_rec *r; + + /* Prepare to receive a SIGUSR1 due to graceful restart so that + * we can exit cleanly. Since we're between connections right + * now it's the right time to exit, but we might be blocked in a + * system call when the graceful restart request is made. */ + usr1_just_die = 1; + signal(SIGUSR1, usr1_handler); + + /* + * (Re)initialize this child to a pre-connection state. + */ + + ap_kill_timeout(0); /* Cancel any outstanding alarms. */ + current_conn = NULL; + + ap_clear_pool(ptrans); + + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->global.running_generation != ap_my_generation) { + clean_child_exit(0); + } + +#ifndef WIN32 + if ((ap_max_requests_per_child > 0 + && requests_this_child++ >= ap_max_requests_per_child)) { + clean_child_exit(0); + } +#else + ++requests_this_child; +#endif + + (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL); + + /* + * Wait for an acceptable connection to arrive. + */ + + /* Lock around "accept", if necessary */ + SAFE_ACCEPT(accept_mutex_on()); + + for (;;) { + if (ap_listeners->next != ap_listeners) { + /* more than one socket */ + memcpy(&main_fds, &listenfds, sizeof(fd_set)); + srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, NULL); + + if (srv < 0 && errno != EINTR) { + /* Single Unix documents select as returning errnos + * EBADF, EINTR, and EINVAL... and in none of those + * cases does it make sense to continue. In fact + * on Linux 2.0.x we seem to end up with EFAULT + * occasionally, and we'd loop forever due to it. + */ + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)"); + clean_child_exit(1); + } + + if (srv <= 0) + continue; + + lr = find_ready_listener(&main_fds); + if (lr == NULL) + continue; + sd = lr->fd; + } + else { + /* only one socket, just pretend we did the other stuff */ + sd = ap_listeners->fd; + } + + /* if we accept() something we don't want to die, so we have to + * defer the exit + */ + deferred_die = 0; + usr1_just_die = 0; + for (;;) { + clen = sizeof(sa_client); + csd = ap_accept(sd, &sa_client, &clen); + if (csd >= 0 || errno != EINTR) + break; + if (deferred_die) { + /* we didn't get a socket, and we were told to die */ + clean_child_exit(0); + } + } + + if (csd >= 0) + break; /* We have a socket ready for reading */ + else { + + /* Our old behaviour here was to continue after accept() + * errors. But this leads us into lots of troubles + * because most of the errors are quite fatal. For + * example, EMFILE can be caused by slow descriptor + * leaks (say in a 3rd party module, or libc). It's + * foolish for us to continue after an EMFILE. We also + * seem to tickle kernel bugs on some platforms which + * lead to never-ending loops here. So it seems best + * to just exit in most cases. + */ + switch (errno) { +#ifdef EPROTO + /* EPROTO on certain older kernels really means + * ECONNABORTED, so we need to ignore it for them. + * See discussion in new-httpd archives nh.9701 + * search for EPROTO. + * + * Also see nh.9603, search for EPROTO: + * There is potentially a bug in Solaris 2.x x<6, + * and other boxes that implement tcp sockets in + * userland (i.e. on top of STREAMS). On these + * systems, EPROTO can actually result in a fatal + * loop. See PR#981 for example. It's hard to + * handle both uses of EPROTO. + */ + case EPROTO: +#endif +#ifdef ECONNABORTED + case ECONNABORTED: +#endif + /* Linux generates the rest of these, other tcp + * stacks (i.e. bsd) tend to hide them behind + * getsockopt() interfaces. They occur when + * the net goes sour or the client disconnects + * after the three-way handshake has been done + * in the kernel but before userland has picked + * up the socket. + */ +#ifdef ECONNRESET + case ECONNRESET: +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: +#endif +#ifdef ENETUNREACH + case ENETUNREACH: +#endif + break; +#ifdef ENETDOWN + case ENETDOWN: + /* + * When the network layer has been shut down, there + * is not much use in simply exiting: the parent + * would simply re-create us (and we'd fail again). + * Use the CHILDFATAL code to tear the server down. + * @@@ Martin's idea for possible improvement: + * A different approach would be to define + * a new APEXIT_NETDOWN exit code, the reception + * of which would make the parent shutdown all + * children, then idle-loop until it detected that + * the network is up again, and restart the children. + * Ben Hyde noted that temporary ENETDOWN situations + * occur in mobile IP. + */ + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "accept: giving up."); + clean_child_exit(APEXIT_CHILDFATAL); +#endif /*ENETDOWN*/ + +#ifdef TPF + case EINACT: + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "offload device inactive"); + clean_child_exit(APEXIT_CHILDFATAL); + break; + default: + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "select/accept error (%u)", errno); + clean_child_exit(APEXIT_CHILDFATAL); +#else + default: + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "accept: (client socket)"); + clean_child_exit(1); +#endif + } + } + + /* go around again, safe to die */ + usr1_just_die = 1; + if (deferred_die) { + /* ok maybe not, see ya later */ + clean_child_exit(0); + } + /* or maybe we missed a signal, you never know on systems + * without reliable signals + */ + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->global.running_generation != ap_my_generation) { + clean_child_exit(0); + } + } + + SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ + +#ifdef TPF + if (csd == 0) /* 0 is invalid socket for TPF */ + continue; +#endif + + /* We've got a socket, let's at least process one request off the + * socket before we accept a graceful restart request. + */ + signal(SIGUSR1, SIG_IGN); + + ap_note_cleanups_for_fd(ptrans, csd); + + /* protect various fd_sets */ +#ifdef CHECK_FD_SETSIZE + if (csd >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "[csd] filedescriptor (%u) larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", csd, FD_SETSIZE); + continue; + } +#endif + + /* + * We now have a connection, so set it up with the appropriate + * socket options, file descriptors, and read/write buffers. + */ + + clen = sizeof(sa_server); + if (getsockname(csd, &sa_server, &clen) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "getsockname"); + continue; + } + + sock_disable_nagle(csd); + + (void) ap_update_child_status(my_child_num, SERVER_BUSY_READ, + (request_rec *) NULL); + + conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET); + +#ifdef B_SFIO + (void) sfdisc(conn_io->sf_in, SF_POPDISC); + sfdisc(conn_io->sf_in, bsfio_new(conn_io->pool, conn_io)); + sfsetbuf(conn_io->sf_in, NULL, 0); + + (void) sfdisc(conn_io->sf_out, SF_POPDISC); + sfdisc(conn_io->sf_out, bsfio_new(conn_io->pool, conn_io)); + sfsetbuf(conn_io->sf_out, NULL, 0); +#endif + + dupped_csd = csd; +#if defined(NEED_DUPPED_CSD) + if ((dupped_csd = dup(csd)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "dup: couldn't duplicate csd"); + dupped_csd = csd; /* Oh well... */ + } + ap_note_cleanups_for_fd(ptrans, dupped_csd); + + /* protect various fd_sets */ +#ifdef CHECK_FD_SETSIZE + if (dupped_csd >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "[dupped_csd] filedescriptor (%u) larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", dupped_csd, FD_SETSIZE); + continue; + } +#endif +#endif + ap_bpushfd(conn_io, csd, dupped_csd); + + current_conn = new_connection(ptrans, server_conf, conn_io, + (struct sockaddr_in *) &sa_client, + (struct sockaddr_in *) &sa_server, + my_child_num); + + /* + * Read and process each request found on our connection + * until no requests are left or we decide to close. + */ + + while ((r = ap_read_request(current_conn)) != NULL) { + + /* read_request_line has already done a + * signal (SIGUSR1, SIG_IGN); + */ + + (void) ap_update_child_status(my_child_num, SERVER_BUSY_WRITE, r); + + /* process the request if it was read without error */ + + if (r->status == HTTP_OK) + ap_process_request(r); + + if(ap_extended_status) + increment_counts(my_child_num, r); + + if (!current_conn->keepalive || current_conn->aborted) + break; + + ap_destroy_pool(r->pool); + (void) ap_update_child_status(my_child_num, SERVER_BUSY_KEEPALIVE, + (request_rec *) NULL); + + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->global.running_generation != ap_my_generation) { + ap_bclose(conn_io); + clean_child_exit(0); + } + + /* In case we get a graceful restart while we're blocked + * waiting for the request. + * + * XXX: This isn't perfect, we might actually read the + * request and then just die without saying anything to + * the client. This can be fixed by using deferred_die + * but you have to teach buff.c about it so that it can handle + * the EINTR properly. + * + * In practice though browsers (have to) expect keepalive + * connections to close before receiving a response because + * of network latencies and server timeouts. + */ + usr1_just_die = 1; + signal(SIGUSR1, usr1_handler); + } + + /* + * Close the connection, being careful to send out whatever is still + * in our buffers. If possible, try to avoid a hard close until the + * client has ACKed our FIN and/or has stopped sending us data. + */ + +#ifdef NO_LINGCLOSE + ap_bclose(conn_io); /* just close it */ +#else + if (r && r->connection + && !r->connection->aborted + && r->connection->client + && (r->connection->client->fd >= 0)) { + + lingering_close(r); + } + else { + ap_bsetflag(conn_io, B_EOUT, 1); + ap_bclose(conn_io); + } +#endif + } +} + +#ifdef TPF +static void reset_tpf_listeners(APACHE_TPF_INPUT *input_parms) +{ + int count; + listen_rec *lr; + + count = 0; + listenmaxfd = -1; + FD_ZERO(&listenfds); + lr = ap_listeners; + + for(;;) { + lr->fd = input_parms->listeners[count]; + if(lr->fd >= 0) { + FD_SET(lr->fd, &listenfds); + if(lr->fd > listenmaxfd) + listenmaxfd = lr->fd; + } + if(lr->next == NULL) + break; + lr = lr->next; + count++; + } + lr->next = ap_listeners; + head_listener = ap_listeners; + close_unused_listeners(); +} + +#endif /* TPF */ + +static int make_child(server_rec *s, int slot, time_t now) +{ + int pid; + + if (slot + 1 > max_daemons_limit) { + max_daemons_limit = slot + 1; + } + + if (one_process) { + signal(SIGHUP, just_die); + signal(SIGINT, just_die); +#ifdef SIGQUIT + signal(SIGQUIT, SIG_DFL); +#endif + signal(SIGTERM, just_die); + child_main(slot); + } + + /* avoid starvation */ + head_listener = head_listener->next; + + Explain1("Starting new child in slot %d", slot); + (void) ap_update_child_status(slot, SERVER_STARTING, (request_rec *) NULL); + + +#ifdef _OSD_POSIX + /* BS2000 requires a "special" version of fork() before a setuid() call */ + if ((pid = os_fork(ap_user_name)) == -1) { +#elif defined(TPF) + if ((pid = os_fork(s, slot)) == -1) { +#else + if ((pid = fork()) == -1) { +#endif + ap_log_error(APLOG_MARK, APLOG_ERR, s, "fork: Unable to fork new process"); + + /* fork didn't succeed. Fix the scoreboard or else + * it will say SERVER_STARTING forever and ever + */ + (void) ap_update_child_status(slot, SERVER_DEAD, (request_rec *) NULL); + + /* In case system resources are maxxed out, we don't want + Apache running away with the CPU trying to fork over and + over and over again. */ + sleep(10); + + return -1; + } + + if (!pid) { +#ifdef AIX_BIND_PROCESSOR +/* by default AIX binds to a single processor + * this bit unbinds children which will then bind to another cpu + */ +#include + int status = bindprocessor(BINDPROCESS, (int)getpid(), + PROCESSOR_CLASS_ANY); + if (status != OK) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf, + "processor unbind failed %d", status); + } +#endif + RAISE_SIGSTOP(MAKE_CHILD); + MONCONTROL(1); + /* Disable the restart signal handlers and enable the just_die stuff. + * Note that since restart() just notes that a restart has been + * requested there's no race condition here. + */ + signal(SIGHUP, just_die); + signal(SIGUSR1, just_die); + signal(SIGTERM, just_die); + child_main(slot); + } + +#ifdef OPTIMIZE_TIMEOUTS + ap_scoreboard_image->parent[slot].last_rtime = now; +#endif + ap_scoreboard_image->parent[slot].pid = pid; +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[slot]), 0); + force_write(scoreboard_fd, &ap_scoreboard_image->parent[slot], + sizeof(parent_score)); +#endif + + return 0; +} + + +/* start up a bunch of children */ +static void startup_children(int number_to_start) +{ + int i; + time_t now = time(0); + + for (i = 0; number_to_start && i < ap_daemons_limit; ++i) { + if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) { + continue; + } + if (make_child(server_conf, i, now) < 0) { + break; + } + --number_to_start; + } +} + + +/* + * idle_spawn_rate is the number of children that will be spawned on the + * next maintenance cycle if there aren't enough idle servers. It is + * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by + * without the need to spawn. + */ +static int idle_spawn_rate = 1; +#ifndef MAX_SPAWN_RATE +#define MAX_SPAWN_RATE (32) +#endif +static int hold_off_on_exponential_spawning; + +static void perform_idle_server_maintenance(void) +{ + int i; + int to_kill; + int idle_count; + short_score *ss; + time_t now = time(0); + int free_length; + int free_slots[MAX_SPAWN_RATE]; + int last_non_dead; + int total_non_dead; + + /* initialize the free_list */ + free_length = 0; + + to_kill = -1; + idle_count = 0; + last_non_dead = -1; + total_non_dead = 0; + + ap_sync_scoreboard_image(); + for (i = 0; i < ap_daemons_limit; ++i) { + int status; + + if (i >= max_daemons_limit && free_length == idle_spawn_rate) + break; + ss = &ap_scoreboard_image->servers[i]; + status = ss->status; + if (status == SERVER_DEAD) { + /* try to keep children numbers as low as possible */ + if (free_length < idle_spawn_rate) { + free_slots[free_length] = i; + ++free_length; + } + } + else { + /* We consider a starting server as idle because we started it + * at least a cycle ago, and if it still hasn't finished starting + * then we're just going to swamp things worse by forking more. + * So we hopefully won't need to fork more if we count it. + * This depends on the ordering of SERVER_READY and SERVER_STARTING. + */ + if (status <= SERVER_READY) { + ++ idle_count; + /* always kill the highest numbered child if we have to... + * no really well thought out reason ... other than observing + * the server behaviour under linux where lower numbered children + * tend to service more hits (and hence are more likely to have + * their data in cpu caches). + */ + to_kill = i; + } + + ++total_non_dead; + last_non_dead = i; +#ifdef OPTIMIZE_TIMEOUTS + if (ss->timeout_len) { + /* if it's a live server, with a live timeout then + * start checking its timeout */ + parent_score *ps = &ap_scoreboard_image->parent[i]; + if (ss->cur_vtime != ps->last_vtime) { + /* it has made progress, so update its last_rtime, + * last_vtime */ + ps->last_rtime = now; + ps->last_vtime = ss->cur_vtime; + } + else if (ps->last_rtime + ss->timeout_len < now) { + /* no progress, and the timeout length has been exceeded */ + ss->timeout_len = 0; + kill(ps->pid, SIGALRM); + } + } +#endif + } + } + max_daemons_limit = last_non_dead + 1; + if (idle_count > ap_daemons_max_free) { + /* kill off one child... we use SIGUSR1 because that'll cause it to + * shut down gracefully, in case it happened to pick up a request + * while we were counting + */ + kill(ap_scoreboard_image->parent[to_kill].pid, SIGUSR1); + idle_spawn_rate = 1; + } + else if (idle_count < ap_daemons_min_free) { + /* terminate the free list */ + if (free_length == 0) { + /* only report this condition once */ + static int reported = 0; + + if (!reported) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "server reached MaxClients setting, consider" + " raising the MaxClients setting"); + reported = 1; + } + idle_spawn_rate = 1; + } + else { + if (idle_spawn_rate >= 8) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "server seems busy, (you may need " + "to increase StartServers, or Min/MaxSpareServers), " + "spawning %d children, there are %d idle, and " + "%d total children", idle_spawn_rate, + idle_count, total_non_dead); + } + for (i = 0; i < free_length; ++i) { +#ifdef TPF + if(make_child(server_conf, free_slots[i], now) == -1) { + if(free_length == 1) { + shutdown_pending = 1; + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "No active child processes: shutting down"); + } + } +#else + make_child(server_conf, free_slots[i], now); +#endif /* TPF */ + } + /* the next time around we want to spawn twice as many if this + * wasn't good enough, but not if we've just done a graceful + */ + if (hold_off_on_exponential_spawning) { + --hold_off_on_exponential_spawning; + } + else if (idle_spawn_rate < MAX_SPAWN_RATE) { + idle_spawn_rate *= 2; + } + } + } + else { + idle_spawn_rate = 1; + } +} + + +static void process_child_status(int pid, ap_wait_t status) +{ + /* Child died... if it died due to a fatal error, + * we should simply bail out. + */ + if ((WIFEXITED(status)) && + WEXITSTATUS(status) == APEXIT_CHILDFATAL) { + ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server_conf, + "Child %d returned a Fatal error... \n" + "Apache is exiting!", + pid); + exit(APEXIT_CHILDFATAL); + } + if (WIFSIGNALED(status)) { + switch (WTERMSIG(status)) { + case SIGTERM: + case SIGHUP: + case SIGUSR1: + case SIGKILL: + break; + default: +#ifdef SYS_SIGLIST +#ifdef WCOREDUMP + if (WCOREDUMP(status)) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %s (%d), " + "possible coredump in %s", + pid, (WTERMSIG(status) >= NumSIG) ? "" : + SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status), + ap_coredump_dir); + } + else { +#endif + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %s (%d)", pid, + SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status)); +#ifdef WCOREDUMP + } +#endif +#else + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %d", + pid, WTERMSIG(status)); +#endif + } + } +} + + +/***************************************************************** + * Executive routines. + */ + +#ifndef STANDALONE_MAIN +#define STANDALONE_MAIN standalone_main + +static void standalone_main(int argc, char **argv) +{ + int remaining_children_to_start; + +#ifdef OS2 + printf("%s \n", ap_get_server_version()); +#endif + + ap_standalone = 1; + + is_graceful = 0; + + if (!one_process) { + detach(); + } + else { + MONCONTROL(1); + } + + my_pid = getpid(); + + do { + copy_listeners(pconf); + if (!is_graceful) { + ap_restart_time = time(NULL); + } +#ifdef SCOREBOARD_FILE + else if (scoreboard_fd != -1) { + ap_kill_cleanup(pconf, NULL, cleanup_scoreboard_file); + ap_kill_cleanups_for_fd(pconf, scoreboard_fd); + } +#endif + ap_clear_pool(pconf); + ptrans = ap_make_sub_pool(pconf); + + server_conf = ap_read_config(pconf, ptrans, ap_server_confname); + setup_listeners(pconf); + ap_clear_pool(plog); + ap_open_logs(server_conf, plog); + ap_log_pid(pconf, ap_pid_fname); + ap_set_version(); /* create our server_version string */ + ap_init_modules(pconf, server_conf); + version_locked++; /* no more changes to server_version */ + SAFE_ACCEPT(accept_mutex_init(pconf)); + if (!is_graceful) { + reinit_scoreboard(pconf); + } +#ifdef SCOREBOARD_FILE + else { + ap_scoreboard_fname = ap_server_root_relative(pconf, ap_scoreboard_fname); + ap_note_cleanups_for_fd(pconf, scoreboard_fd); + } +#endif + + set_signals(); + + if (ap_daemons_max_free < ap_daemons_min_free + 1) /* Don't thrash... */ + ap_daemons_max_free = ap_daemons_min_free + 1; + + /* If we're doing a graceful_restart then we're going to see a lot + * of children exiting immediately when we get into the main loop + * below (because we just sent them SIGUSR1). This happens pretty + * rapidly... and for each one that exits we'll start a new one until + * we reach at least daemons_min_free. But we may be permitted to + * start more than that, so we'll just keep track of how many we're + * supposed to start up without the 1 second penalty between each fork. + */ + remaining_children_to_start = ap_daemons_to_start; + if (remaining_children_to_start > ap_daemons_limit) { + remaining_children_to_start = ap_daemons_limit; + } + if (!is_graceful) { + startup_children(remaining_children_to_start); + remaining_children_to_start = 0; + } + else { + /* give the system some time to recover before kicking into + * exponential mode */ + hold_off_on_exponential_spawning = 10; + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "%s configured -- resuming normal operations", + ap_get_server_version()); + if (ap_suexec_enabled) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "suEXEC mechanism enabled (wrapper: %s)", SUEXEC_BIN); + } + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "Server built: %s", ap_get_server_built()); + restart_pending = shutdown_pending = 0; + + while (!restart_pending && !shutdown_pending) { + int child_slot; + ap_wait_t status; + int pid = wait_or_timeout(&status); + + /* XXX: if it takes longer than 1 second for all our children + * to start up and get into IDLE state then we may spawn an + * extra child + */ + if (pid >= 0) { + process_child_status(pid, status); + /* non-fatal death... note that it's gone in the scoreboard. */ + ap_sync_scoreboard_image(); + child_slot = find_child_by_pid(pid); + Explain2("Reaping child %d slot %d", pid, child_slot); + if (child_slot >= 0) { + (void) ap_update_child_status(child_slot, SERVER_DEAD, + (request_rec *) NULL); + if (remaining_children_to_start + && child_slot < ap_daemons_limit) { + /* we're still doing a 1-for-1 replacement of dead + * children with new children + */ + make_child(server_conf, child_slot, time(0)); + --remaining_children_to_start; + } +#ifndef NO_OTHER_CHILD + } + else if (reap_other_child(pid, status) == 0) { + /* handled */ +#endif + } + else if (is_graceful) { + /* Great, we've probably just lost a slot in the + * scoreboard. Somehow we don't know about this + * child. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf, + "long lost child came home! (pid %d)", pid); + } + /* Don't perform idle maintenance when a child dies, + * only do it when there's a timeout. Remember only a + * finite number of children can die, and it's pretty + * pathological for a lot to die suddenly. + */ + continue; + } + else if (remaining_children_to_start) { + /* we hit a 1 second timeout in which none of the previous + * generation of children needed to be reaped... so assume + * they're all done, and pick up the slack if any is left. + */ + startup_children(remaining_children_to_start); + remaining_children_to_start = 0; + /* In any event we really shouldn't do the code below because + * few of the servers we just started are in the IDLE state + * yet, so we'd mistakenly create an extra server. + */ + continue; + } + + perform_idle_server_maintenance(); +#ifdef TPF + shutdown_pending = os_check_server(tpf_server_name); + ap_check_signals(); + sleep(1); +#endif /*TPF */ + } + + if (shutdown_pending) { + /* Time to gracefully shut down: + * Kill child processes, tell them to call child_exit, etc... + */ + if (ap_killpg(pgrp, SIGTERM) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGTERM"); + } + reclaim_child_processes(1); /* Start with SIGTERM */ + + /* cleanup pid file on normal shutdown */ + { + const char *pidfile = NULL; + pidfile = ap_server_root_relative (pconf, ap_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + server_conf, + "removed PID file %s (pid=%ld)", + pidfile, (long)getpid()); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "caught SIGTERM, shutting down"); + clean_parent_exit(0); + } + + /* we've been told to restart */ + signal(SIGHUP, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + + if (one_process) { + /* not worth thinking about */ + clean_parent_exit(0); + } + + /* advance to the next generation */ + /* XXX: we really need to make sure this new generation number isn't in + * use by any of the children. + */ + ++ap_my_generation; + ap_scoreboard_image->global.running_generation = ap_my_generation; + update_scoreboard_global(); + + if (is_graceful) { +#ifndef SCOREBOARD_FILE + int i; +#endif + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "SIGUSR1 received. Doing graceful restart"); + + /* kill off the idle ones */ + if (ap_killpg(pgrp, SIGUSR1) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGUSR1"); + } +#ifndef SCOREBOARD_FILE + /* This is mostly for debugging... so that we know what is still + * gracefully dealing with existing request. But we can't really + * do it if we're in a SCOREBOARD_FILE because it'll cause + * corruption too easily. + */ + ap_sync_scoreboard_image(); + for (i = 0; i < ap_daemons_limit; ++i) { + if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) { + ap_scoreboard_image->servers[i].status = SERVER_GRACEFUL; + } + } +#endif + } + else { + /* Kill 'em off */ + if (ap_killpg(pgrp, SIGHUP) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGHUP"); + } + reclaim_child_processes(0); /* Not when just starting up */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "SIGHUP received. Attempting to restart"); + } + } while (restart_pending); + + /*add_common_vars(NULL);*/ +} /* standalone_main */ +#else +/* prototype */ +void STANDALONE_MAIN(int argc, char **argv); +#endif /* STANDALONE_MAIN */ + +extern char *optarg; +extern int optind; + +int REALMAIN(int argc, char *argv[]) +{ + int c; + int sock_in; + int sock_out; + char *s; + +#ifdef SecureWare + if (set_auth_parameters(argc, argv) < 0) + perror("set_auth_parameters"); + if (getluid() < 0) + if (setluid(getuid()) < 0) + perror("setluid"); + if (setreuid(0, 0) < 0) + perror("setreuid"); +#endif + +#ifdef SOCKS + SOCKSinit(argv[0]); +#endif + +#ifdef TPF + APACHE_TPF_INPUT input_parms; + ecbptr()->ebrout = PRIMECRAS; + input_parms = * (APACHE_TPF_INPUT *)(&(ecbptr()->ebw000)); +#endif + + MONCONTROL(0); + + common_init(); + + if ((s = strrchr(argv[0], PATHSEPARATOR)) != NULL) { + ap_server_argv0 = ++s; + } + else { + ap_server_argv0 = argv[0]; + } + + ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root)); + ap_cpystrn(ap_server_confname, SERVER_CONFIG_FILE, sizeof(ap_server_confname)); + + ap_setup_prelinked_modules(); + + while ((c = getopt(argc, argv, + "D:C:c:xXd:f:vVlLR:StTh" +#ifdef DEBUG_SIGSTOP + "Z:" +#endif + )) != -1) { + char **new; + switch (c) { + case 'c': + new = (char **)ap_push_array(ap_server_post_read_config); + *new = ap_pstrdup(pcommands, optarg); + break; + case 'C': + new = (char **)ap_push_array(ap_server_pre_read_config); + *new = ap_pstrdup(pcommands, optarg); + break; + case 'D': + new = (char **)ap_push_array(ap_server_config_defines); + *new = ap_pstrdup(pcommands, optarg); + break; + case 'd': + ap_cpystrn(ap_server_root, optarg, sizeof(ap_server_root)); + break; + case 'f': + ap_cpystrn(ap_server_confname, optarg, sizeof(ap_server_confname)); + break; + case 'v': + ap_set_version(); + printf("Server version: %s\n", ap_get_server_version()); + printf("Server built: %s\n", ap_get_server_built()); + exit(0); + case 'V': + ap_set_version(); + show_compile_settings(); + exit(0); + case 'l': + ap_show_modules(); + exit(0); + case 'L': + ap_show_directives(); + exit(0); + case 'X': + ++one_process; /* Weird debugging mode. */ + break; +#ifdef TPF + case 'x': + os_tpf_child(&input_parms); + set_signals(); + break; +#endif +#ifdef DEBUG_SIGSTOP + case 'Z': + raise_sigstop_flags = atoi(optarg); + break; +#endif +#ifdef SHARED_CORE + case 'R': + /* just ignore this option here, because it has only + * effect when SHARED_CORE is used and then it was + * already handled in the Shared Core Bootstrap + * program. + */ + break; +#endif + case 'S': + ap_dump_settings = 1; + break; + case 't': + ap_configtestonly = 1; + ap_docrootcheck = 1; + break; + case 'T': + ap_configtestonly = 1; + ap_docrootcheck = 0; + break; + case 'h': + usage(argv[0]); + case '?': + usage(argv[0]); + } + } + + ap_suexec_enabled = init_suexec(); + server_conf = ap_read_config(pconf, ptrans, ap_server_confname); + + if (ap_configtestonly) { + fprintf(stderr, "Syntax OK\n"); + exit(0); + } + if (ap_dump_settings) { + exit(0); + } + + child_timeouts = !ap_standalone || one_process; + +#ifndef TPF + if (ap_standalone) { + ap_open_logs(server_conf, plog); + ap_set_version(); + ap_init_modules(pconf, server_conf); + version_locked++; + STANDALONE_MAIN(argc, argv); + } +#else + if (ap_standalone) { + if(!tpf_child) { + memcpy(tpf_server_name, input_parms.inetd_server.servname, INETD_SERVNAME_LENGTH); + tpf_server_name[INETD_SERVNAME_LENGTH+1] = '\0'; + ap_open_logs(server_conf, pconf); + } + ap_set_version(); + ap_init_modules(pconf, server_conf); + version_locked++; + if(tpf_child) { + copy_listeners(pconf); + reset_tpf_listeners(&input_parms); + server_conf->error_log = NULL; +#ifdef SCOREBOARD_FILE + scoreboard_fd = input_parms.scoreboard_fd; + ap_scoreboard_image = &_scoreboard_image; +#else /* must be USE_TPF_SCOREBOARD or USE_SHMGET_SCOREBOARD */ + ap_scoreboard_image = (scoreboard *)input_parms.scoreboard_heap; +#endif + child_main(input_parms.slot); + } + else + STANDALONE_MAIN(argc, argv); + } +#endif + else { + conn_rec *conn; + request_rec *r; + struct sockaddr sa_server, sa_client; + BUFF *cio; + NET_SIZE_T l; + + ap_set_version(); + /* Yes this is called twice. */ + ap_init_modules(pconf, server_conf); + version_locked++; + ap_open_logs(server_conf, plog); + ap_init_modules(pconf, server_conf); + set_group_privs(); + +#ifdef MPE + /* Only try to switch if we're running as MANAGER.SYS */ + if (geteuid() == 1 && ap_user_id > 1) { + GETPRIVMODE(); + if (setuid(ap_user_id) == -1) { + GETUSERMODE(); + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change to uid: %d", ap_user_id); + exit(1); + } + GETUSERMODE(); + } +#else + /* Only try to switch if we're running as root */ + if (!geteuid() && setuid(ap_user_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change to uid: %ld", + (long) ap_user_id); + exit(1); + } +#endif + if (ap_setjmp(jmpbuffer)) { + exit(0); + } + +#ifdef TPF +/* TPF only passes the incoming socket number from the internet daemon + in ebw000 */ + sock_in = * (int*)(&(ecbptr()->ebw000)); + sock_out = * (int*)(&(ecbptr()->ebw000)); +/* TPF also needs a signal set for alarm in inetd mode */ + signal(SIGALRM, alrm_handler); +#elif defined(MPE) +/* HP MPE 5.5 inetd only passes the incoming socket as stdin (fd 0), whereas + HPUX inetd passes the incoming socket as stdin (fd 0) and stdout (fd 1). + Go figure. SR 5003355016 has been submitted to request that the existing + functionality be documented, and then to enhance the functionality to be + like HPUX. */ + sock_in = fileno(stdin); + sock_out = fileno(stdin); +#else + sock_in = fileno(stdin); + sock_out = fileno(stdout); +#endif + + l = sizeof(sa_client); + if ((getpeername(sock_in, &sa_client, &l)) < 0) { +/* get peername will fail if the input isn't a socket */ + perror("getpeername"); + memset(&sa_client, '\0', sizeof(sa_client)); + } + + l = sizeof(sa_server); + if (getsockname(sock_in, &sa_server, &l) < 0) { + perror("getsockname"); + fprintf(stderr, "Error getting local address\n"); + exit(1); + } + server_conf->port = ntohs(((struct sockaddr_in *) &sa_server)->sin_port); + cio = ap_bcreate(ptrans, B_RDWR | B_SOCKET); + cio->fd = sock_out; + cio->fd_in = sock_in; + conn = new_connection(ptrans, server_conf, cio, + (struct sockaddr_in *) &sa_client, + (struct sockaddr_in *) &sa_server, -1); + + while ((r = ap_read_request(conn)) != NULL) { + + if (r->status == HTTP_OK) + ap_process_request(r); + + if (!conn->keepalive || conn->aborted) + break; + + ap_destroy_pool(r->pool); + } + + ap_bclose(cio); + } + exit(0); +} + +#else /* ndef MULTITHREAD */ + + +/********************************************************************** + * Multithreaded implementation + * + * This code is fairly specific to Win32. + * + * The model used to handle requests is a set of threads. One "main" + * thread listens for new requests. When something becomes + * available, it does a select and places the newly available socket + * onto a list of "jobs" (add_job()). Then any one of a fixed number + * of "worker" threads takes the top job off the job list with + * remove_job() and handles that connection to completion. After + * the connection has finished the thread is free to take another + * job from the job list. + * + * In the code, the "main" thread is running within the worker_main() + * function. The first thing this function does is create the + * worker threads, which operate in the child_sub_main() function. The + * main thread then goes into a loop within worker_main() where they + * do a select() on the listening sockets. The select times out once + * per second so that the thread can check for an "exit" signal + * from the parent process (see below). If this signal is set, the + * thread can exit, but only after it has accepted all incoming + * connections already in the listen queue (since Win32 appears + * to through away listened but unaccepted connections when a + * process dies). + * + * Because the main and worker threads exist within a single process + * they are vulnerable to crashes or memory leaks (crashes can also + * be caused within modules, of course). There also needs to be a + * mechanism to perform restarts and shutdowns. This is done by + * creating the main & worker threads within a subprocess. A + * main process (the "parent process") creates one (or more) + * processes to do the work, then the parent sits around waiting + * for the working process to die, in which case it starts a new + * one. The parent process also handles restarts (by creating + * a new working process then signalling the previous working process + * exit ) and shutdowns (by signalling the working process to exit). + * The parent process operates within the master_main() function. This + * process also handles requests from the service manager (NT only). + * + * Signalling between the parent and working process uses a Win32 + * event. Each child has a unique name for the event, which is + * passed to it with the -Z argument when the child is spawned. The + * parent sets (signals) this event to tell the child to die. + * At present all children do a graceful die - they finish all + * current jobs _and_ empty the listen queue before they exit. + * A non-graceful die would need a second event. The -Z argument in + * the child is also used to create the shutdown and restart events, + * since the prefix (apPID) contains the parent process PID. + * + * The code below starts with functions at the lowest level - + * worker threads, and works up to the top level - the main() + * function of the parent process. + * + * The scoreboard (in process memory) contains details of the worker + * threads (within the active working process). There is no shared + * "scoreboard" between processes, since only one is ever active + * at once (or at most, two, when one has been told to shutdown but + * is processes outstanding requests, and a new one has been started). + * This is controlled by a "start_mutex" which ensures only one working + * process is active at once. + **********************************************************************/ + +/* The code protected by #ifdef UNGRACEFUL_RESTARTS/#endif sections + * could implement a sort-of ungraceful restart for Win32. instead of + * graceful restarts. + * + * However it does not work too well because it does not intercept a + * connection already in progress (in child_sub_main()). We'd have to + * get that to poll on the exit event. + */ + +/* + * Definition of jobs, shared by main and worker threads. + */ + +typedef struct joblist_s { + struct joblist_s *next; + int sock; +} joblist; + +/* + * Globals common to main and worker threads. This structure is not + * used by the parent process. + */ + +typedef struct globals_s { +#ifdef UNGRACEFUL_RESTART + HANDLE thread_exit_event; +#else + int exit_now; +#endif + semaphore *jobsemaphore; + joblist *jobhead; + joblist *jobtail; + mutex *jobmutex; + int jobcount; +} globals; + +globals allowed_globals = +{0, NULL, NULL, NULL, NULL, 0}; + +/* + * add_job()/remove_job() - add or remove an accepted socket from the + * list of sockets connected to clients. allowed_globals.jobmutex protects + * against multiple concurrent access to the linked list of jobs. + */ + +void add_job(int sock) +{ + joblist *new_job; + + ap_assert(allowed_globals.jobmutex); + /* TODO: If too many jobs in queue, sleep, check for problems */ + ap_acquire_mutex(allowed_globals.jobmutex); + new_job = (joblist *) malloc(sizeof(joblist)); + if (new_job == NULL) { + fprintf(stderr, "Ouch! Out of memory in add_job()!\n"); + } + new_job->next = NULL; + new_job->sock = sock; + if (allowed_globals.jobtail != NULL) + allowed_globals.jobtail->next = new_job; + allowed_globals.jobtail = new_job; + if (!allowed_globals.jobhead) + allowed_globals.jobhead = new_job; + allowed_globals.jobcount++; + release_semaphore(allowed_globals.jobsemaphore); + ap_release_mutex(allowed_globals.jobmutex); +} + +int remove_job(void) +{ + joblist *job; + int sock; + +#ifdef UNGRACEFUL_RESTART + HANDLE hObjects[2]; + int rv; + + hObjects[0] = allowed_globals.jobsemaphore; + hObjects[1] = allowed_globals.thread_exit_event; + + rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); + ap_assert(rv != WAIT_FAILED); + if (rv == WAIT_OBJECT_0 + 1) { + /* thread_exit_now */ + APD1("thread got exit now event"); + return -1; + } + /* must be semaphore */ +#else + acquire_semaphore(allowed_globals.jobsemaphore); +#endif + ap_assert(allowed_globals.jobmutex); + +#ifdef UNGRACEFUL_RESTART + if (!allowed_globals.jobhead) { +#else + ap_acquire_mutex(allowed_globals.jobmutex); + if (allowed_globals.exit_now && !allowed_globals.jobhead) { +#endif + ap_release_mutex(allowed_globals.jobmutex); + return (-1); + } + job = allowed_globals.jobhead; + ap_assert(job); + allowed_globals.jobhead = job->next; + if (allowed_globals.jobhead == NULL) + allowed_globals.jobtail = NULL; + ap_release_mutex(allowed_globals.jobmutex); + sock = job->sock; + free(job); + return (sock); +} + +/* + * child_sub_main() - this is the main loop for the worker threads + * + * Each thread runs within this function. They wait within remove_job() + * for a job to become available, then handle all the requests on that + * connection until it is closed, then return to remove_job(). + * + * The worker thread will exit when it removes a job which contains + * socket number -1. This provides a graceful thread exit, since + * it will never exit during a connection. + * + * This code in this function is basically equivalent to the child_main() + * from the multi-process (Unix) environment, except that we + * + * - do not call child_init_modules (child init API phase) + * - block in remove_job, and when unblocked we have an already + * accepted socket, instead of blocking on a mutex or select(). + */ + +static void child_sub_main(int child_num) +{ + NET_SIZE_T clen; + struct sockaddr sa_server; + struct sockaddr sa_client; + pool *ptrans; + int requests_this_child = 0; + int csd = -1; + int dupped_csd = -1; + int srv = 0; + + ptrans = ap_make_sub_pool(pconf); + + (void) ap_update_child_status(child_num, SERVER_READY, (request_rec *) NULL); + + /* + * Setup the jump buffers so that we can return here after a timeout. + */ +#if defined(USE_LONGJMP) + setjmp(jmpbuffer); +#else + sigsetjmp(jmpbuffer, 1); +#endif +#ifdef SIGURG + signal(SIGURG, timeout); +#endif + + while (1) { + BUFF *conn_io; + request_rec *r; + + /* + * (Re)initialize this child to a pre-connection state. + */ + + ap_set_callback_and_alarm(NULL, 0); /* Cancel any outstanding alarms */ + timeout_req = NULL; /* No request in progress */ + current_conn = NULL; + + ap_clear_pool(ptrans); + + (void) ap_update_child_status(child_num, SERVER_READY, + (request_rec *) NULL); + + /* Get job from the job list. This will block until a job is ready. + * If -1 is returned then the main thread wants us to exit. + */ + csd = remove_job(); + if (csd == -1) + break; /* time to exit */ + requests_this_child++; + + ap_note_cleanups_for_socket(ptrans, csd); + + /* + * We now have a connection, so set it up with the appropriate + * socket options, file descriptors, and read/write buffers. + */ + + clen = sizeof(sa_server); + if (getsockname(csd, &sa_server, &clen) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "getsockname"); + continue; + } + clen = sizeof(sa_client); + if ((getpeername(csd, &sa_client, &clen)) < 0) { + /* get peername will fail if the input isn't a socket */ + perror("getpeername"); + memset(&sa_client, '\0', sizeof(sa_client)); + } + + sock_disable_nagle(csd); + + (void) ap_update_child_status(child_num, SERVER_BUSY_READ, + (request_rec *) NULL); + + conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET); + dupped_csd = csd; +#if defined(NEED_DUPPED_CSD) + if ((dupped_csd = dup(csd)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "dup: couldn't duplicate csd"); + dupped_csd = csd; /* Oh well... */ + } + ap_note_cleanups_for_socket(ptrans, dupped_csd); +#endif + ap_bpushfd(conn_io, csd, dupped_csd); + + current_conn = new_connection(ptrans, server_conf, conn_io, + (struct sockaddr_in *) &sa_client, + (struct sockaddr_in *) &sa_server, + child_num); + + /* + * Read and process each request found on our connection + * until no requests are left or we decide to close. + */ + + while ((r = ap_read_request(current_conn)) != NULL) { + (void) ap_update_child_status(child_num, SERVER_BUSY_WRITE, r); + + if (r->status == HTTP_OK) + ap_process_request(r); + + if (ap_extended_status) + increment_counts(child_num, r); + + if (!current_conn->keepalive || current_conn->aborted) + break; + + ap_destroy_pool(r->pool); + (void) ap_update_child_status(child_num, SERVER_BUSY_KEEPALIVE, + (request_rec *) NULL); + + ap_sync_scoreboard_image(); + } + + /* + * Close the connection, being careful to send out whatever is still + * in our buffers. If possible, try to avoid a hard close until the + * client has ACKed our FIN and/or has stopped sending us data. + */ + ap_kill_cleanups_for_socket(ptrans, csd); + +#ifdef NO_LINGCLOSE + ap_bclose(conn_io); /* just close it */ +#else + if (r && r->connection + && !r->connection->aborted + && r->connection->client + && (r->connection->client->fd >= 0)) { + + lingering_close(r); + } + else { + ap_bsetflag(conn_io, B_EOUT, 1); + ap_bclose(conn_io); + } +#endif + } + ap_destroy_pool(ptrans); + (void) ap_update_child_status(child_num, SERVER_DEAD, NULL); +} + + +void child_main(int child_num_arg) +{ + /* + * Only reason for this function, is to pass in + * arguments to child_sub_main() on its stack so + * that longjump doesn't try to corrupt its local + * variables and I don't need to make those + * damn variables static/global + */ + child_sub_main(child_num_arg); +} + + + +void cleanup_thread(thread **handles, int *thread_cnt, int thread_to_clean) +{ + int i; + + free_thread(handles[thread_to_clean]); + for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++) + handles[i] = handles[i + 1]; + (*thread_cnt)--; +} +#ifdef WIN32 +/* + * The Win32 call WaitForMultipleObjects will only allow you to wait for + * a maximum of MAXIMUM_WAIT_OBJECTS (current 64). Since the threading + * model in the multithreaded version of apache wants to use this call, + * we are restricted to a maximum of 64 threads. This is a simplistic + * routine that will increase this size. + */ +static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, + DWORD dwSeconds) +{ + time_t tStopTime; + DWORD dwRet = WAIT_TIMEOUT; + DWORD dwIndex=0; + BOOL bFirst = TRUE; + + tStopTime = time(NULL) + dwSeconds; + + do { + if (!bFirst) + Sleep(1000); + else + bFirst = FALSE; + + for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) { + dwRet = WaitForMultipleObjects( + min(MAXIMUM_WAIT_OBJECTS, + nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)), + lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS), + 0, 0); + + if (dwRet != WAIT_TIMEOUT) { + break; + } + } + } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT)); + + return dwRet; +} +#endif +/***************************************************************** + * Executive routines. + */ + +extern void main_control_server(void *); /* in hellop.c */ + +event *exit_event; +mutex *start_mutex; + +#define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is an int */ +char signal_name_prefix[MAX_SIGNAL_NAME]; +char signal_restart_name[MAX_SIGNAL_NAME]; +char signal_shutdown_name[MAX_SIGNAL_NAME]; + +#define MAX_SELECT_ERRORS 100 + +/* + * Initialise the signal names, in the global variables signal_name_prefix, + * signal_restart_name and signal_shutdown_name. + */ + +void setup_signal_names(char *prefix) +{ + ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix); + ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name), + "%s_shutdown", signal_name_prefix); + ap_snprintf(signal_restart_name, sizeof(signal_restart_name), + "%s_restart", signal_name_prefix); + + APD2("signal prefix %s", signal_name_prefix); +} + +static void setup_inherited_listeners(pool *p) +{ + HANDLE pipe; + listen_rec *lr; + int fd; + WSAPROTOCOL_INFO WSAProtocolInfo; + DWORD BytesRead; + + /* Open the pipe to the parent process to receive the inherited socket + * data. The sockets have been set to listening in the parent process. + */ + pipe = GetStdHandle(STD_INPUT_HANDLE); + + /* Setup the listeners */ + listenmaxfd = -1; + FD_ZERO(&listenfds); + lr = ap_listeners; + + FD_ZERO(&listenfds); + + for (;;) { + fd = find_listener(lr); + if (fd < 0) { + if (!ReadFile(pipe, + &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), + &BytesRead, + (LPOVERLAPPED) NULL)){ + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf, + "setup_inherited_listeners: Unable to read socket data from parent"); + exit(1); + } + fd = WSASocket(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &WSAProtocolInfo, + 0, + 0); + if (fd == INVALID_SOCKET) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf, + "setup_inherited_listeners: WSASocket failed to get inherit the socket."); + exit(1); + } + APD2("setup_inherited_listeners: WSASocket() returned socket %d", fd); + } + else { + ap_note_cleanups_for_socket(p, fd); + } + if (fd >= 0) { + FD_SET(fd, &listenfds); + if (fd > listenmaxfd) + listenmaxfd = fd; + } + lr->fd = fd; + if (lr->next == NULL) + break; + lr = lr->next; + } + /* turn the list into a ring */ + lr->next = ap_listeners; + head_listener = ap_listeners; + close_unused_listeners(); + CloseHandle(pipe); + return; +} + +/* + * worker_main() is main loop for the child process. The loop in + * this function becomes the controlling thread for the actually working + * threads (which run in a loop in child_sub_main()). + */ + +void worker_main(void) +{ + int nthreads; + fd_set main_fds; + int srv; + int clen; + int csd; + struct sockaddr_in sa_client; + int total_jobs = 0; + thread **child_handles; + int rv; + time_t end_time; + int i; + struct timeval tv; + int wait_time = 1; + int max_jobs_per_exe; + int max_jobs_after_exit_request; + HANDLE hObjects[2]; + int count_select_errors = 0; + pool *pchild; + + pchild = ap_make_sub_pool(pconf); + + ap_standalone = 1; + sd = -1; + nthreads = ap_threads_per_child; + max_jobs_after_exit_request = ap_excess_requests_per_child; + max_jobs_per_exe = ap_max_requests_per_child; + if (nthreads <= 0) + nthreads = 40; + if (max_jobs_per_exe <= 0) + max_jobs_per_exe = 0; + if (max_jobs_after_exit_request <= 0) + max_jobs_after_exit_request = max_jobs_per_exe / 10; + + if (!one_process) + detach(); + + my_pid = getpid(); + + ++ap_my_generation; + + copy_listeners(pconf); + ap_restart_time = time(NULL); + + reinit_scoreboard(pconf); + + /* + * Wait until we have permission to start accepting connections. + * start_mutex is used to ensure that only one child ever + * goes into the listen/accept loop at once. Also wait on exit_event, + * in case we (this child) is told to die before we get a chance to + * serve any requests. + */ + hObjects[0] = (HANDLE)start_mutex; + hObjects[1] = (HANDLE)exit_event; + rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); + if (rv == WAIT_FAILED) { + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "Waiting for start_mutex or exit_event -- process will exit"); + + ap_destroy_pool(pchild); + cleanup_scoreboard(); + exit(0); + } + if (rv == WAIT_OBJECT_0 + 1) { + /* exit event signalled - exit now */ + ap_destroy_pool(pchild); + cleanup_scoreboard(); + exit(0); + } + /* start_mutex obtained, continue into the select() loop */ + if (one_process) { + setup_listeners(pconf); + } else { + /* Get listeners from the parent process */ + setup_inherited_listeners(pconf); + } + + if (listenmaxfd == -1) { + /* Help, no sockets were made, better log something and exit */ + ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, NULL, + "No sockets were created for listening"); + + signal_parent(0); /* tell parent to die */ + + ap_destroy_pool(pchild); + cleanup_scoreboard(); + exit(0); + } + set_signals(); + + /* + * - Initialize allowed_globals + * - Create the thread table + * - Spawn off threads + * - Create listen socket set (done above) + * - loop { + * wait for request + * create new job + * } while (!time to exit) + * - Close all listeners + * - Wait for all threads to complete + * - Exit + */ + + ap_child_init_modules(pconf, server_conf); + + allowed_globals.jobsemaphore = create_semaphore(0); + allowed_globals.jobmutex = ap_create_mutex(NULL); + + /* spawn off the threads */ + child_handles = (thread *) alloca(nthreads * sizeof(int)); + for (i = 0; i < nthreads; i++) { + child_handles[i] = create_thread((void (*)(void *)) child_main, (void *) i); + } + if (nthreads > max_daemons_limit) { + max_daemons_limit = nthreads; + } + + while (1) { + if (max_jobs_per_exe && (total_jobs > max_jobs_per_exe)) { + /* MaxRequestsPerChild hit... + */ + break; + } + /* Always check for the exit event being signaled. + */ + rv = WaitForSingleObject(exit_event, 0); + ap_assert((rv == WAIT_TIMEOUT) || (rv == WAIT_OBJECT_0)); + if (rv == WAIT_OBJECT_0) { + APD1("child: exit event signalled, exiting"); + break; + } + + tv.tv_sec = wait_time; + tv.tv_usec = 0; + + memcpy(&main_fds, &listenfds, sizeof(fd_set)); + srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv); +#ifdef WIN32 + if (srv == SOCKET_ERROR) { + /* Map the Win32 error into a standard Unix error condition */ + errno = WSAGetLastError(); + srv = -1; + } +#endif /* WIN32 */ + + if (srv < 0) { + /* Error occurred - if EINTR, loop around with problem */ + if (errno != EINTR) { + /* A "real" error occurred, log it and increment the count of + * select errors. This count is used to ensure we don't go into + * a busy loop of continuous errors. + */ + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)"); + count_select_errors++; + if (count_select_errors > MAX_SELECT_ERRORS) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server_conf, + "Too many errors in select loop. Child process exiting."); + break; + } + } + continue; + } + count_select_errors = 0; /* reset count of errors */ + if (srv == 0) { + continue; + } + + { + listen_rec *lr; + + lr = find_ready_listener(&main_fds); + if (lr != NULL) { + sd = lr->fd; + } + } + do { + clen = sizeof(sa_client); + csd = accept(sd, (struct sockaddr *) &sa_client, &clen); +#ifdef WIN32 + if (csd == INVALID_SOCKET) { + csd = -1; + errno = WSAGetLastError(); + } +#endif /* WIN32 */ + } while (csd < 0 && errno == EINTR); + + if (csd < 0) { +#if defined(EPROTO) && defined(ECONNABORTED) + if ((errno != EPROTO) && (errno != ECONNABORTED)) +#elif defined(EPROTO) + if (errno != EPROTO) +#elif defined(ECONNABORTED) + if (errno != ECONNABORTED) +#endif + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "accept: (client socket)"); + } + else { + add_job(csd); + total_jobs++; + } + } + + APD2("process PID %d exiting", my_pid); + + /* Get ready to shutdown and exit */ + allowed_globals.exit_now = 1; + ap_release_mutex(start_mutex); + +#ifdef UNGRACEFUL_RESTART + SetEvent(allowed_globals.thread_exit_event); +#else + for (i = 0; i < nthreads; i++) { + add_job(-1); + } +#endif + + APD2("process PID %d waiting for worker threads to exit", my_pid); + /* Wait for all your children */ + end_time = time(NULL) + 180; + while (nthreads) { + rv = wait_for_many_objects(nthreads, child_handles, + end_time - time(NULL)); + if (rv != WAIT_TIMEOUT) { + rv = rv - WAIT_OBJECT_0; + ap_assert((rv >= 0) && (rv < nthreads)); + cleanup_thread(child_handles, &nthreads, rv); + continue; + } + break; + } + + APD2("process PID %d killing remaining worker threads", my_pid); + for (i = 0; i < nthreads; i++) { + kill_thread(child_handles[i]); + free_thread(child_handles[i]); + } +#ifdef UNGRACEFUL_RESTART + ap_assert(CloseHandle(allowed_globals.thread_exit_event)); +#endif + destroy_semaphore(allowed_globals.jobsemaphore); + ap_destroy_mutex(allowed_globals.jobmutex); + + ap_child_exit_modules(pconf, server_conf); + ap_destroy_pool(pchild); + + cleanup_scoreboard(); + + APD2("process PID %d exited", my_pid); + clean_parent_exit(0); +} /* standalone_main */ + +/* + * Spawn a child Apache process. The child process has the command line arguments from + * argc and argv[], plus a -Z argument giving the name of an event. The child should + * open and poll or wait on this event. When it is signalled, the child should die. + * prefix is a prefix string for the event name. + * + * The child_num argument on entry contains a serial number for this child (used to create + * a unique event name). On exit, this number will have been incremented by one, ready + * for the next call. + * + * On exit, the value pointed to be *ev will contain the event created + * to signal the new child process. + * + * The return value is the handle to the child process if successful, else -1. If -1 is + * returned the error will already have been logged by ap_log_error(). + */ + +/********************************************************************** + * master_main - this is the parent (main) process. We create a + * child process to do the work, then sit around waiting for either + * the child to exit, or a restart or exit signal. If the child dies, + * we just respawn a new one. If we have a shutdown or graceful restart, + * tell the child to die when it is ready. If it is a non-graceful + * restart, force the child to die immediately. + **********************************************************************/ + +#define MAX_PROCESSES 50 /* must be < MAX_WAIT_OBJECTS-1 */ + +static void cleanup_process(HANDLE *handles, HANDLE *events, int position, int *processes) +{ + int i; + int handle = 0; + + CloseHandle(handles[position]); + CloseHandle(events[position]); + + handle = (int)handles[position]; + + for (i = position; i < (*processes)-1; i++) { + handles[i] = handles[i + 1]; + events[i] = events[i + 1]; + } + (*processes)--; + + APD4("cleanup_processes: removed child in slot %d handle %d, max=%d", position, handle, *processes); +} + +static int create_process(pool *p, HANDLE *handles, HANDLE *events, + int *processes, int *child_num, char *kill_event_name, int argc, char **argv) +{ + + int rv, i; + HANDLE kill_event; + char buf[1024]; + char exit_event_name[40]; /* apPID_C# */ + char *pCommand; + + STARTUPINFO si; /* Filled in prior to call to CreateProcess */ + PROCESS_INFORMATION pi; /* filled in on call to CreateProces */ + LPWSAPROTOCOL_INFO lpWSAProtocolInfo; + listen_rec *lr; + DWORD BytesWritten; + HANDLE hPipeRead = NULL; + HANDLE hPipeWrite = NULL; + SECURITY_ATTRIBUTES sa = {0}; + + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + /* Build the command line. Should look something like this: + * C:/apache/bin/apache.exe -Z exit_event -f ap_server_confname + * First, get the path to the executable... + */ + rv = GetModuleFileName(NULL, buf, sizeof(buf)); + if (rv == sizeof(buf)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Path to Apache process too long"); + return -1; + } else if (rv == 0) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: GetModuleFileName() returned NULL for current process."); + return -1; + } + + /* Create the exit event (apPID_C#). Parent signals this event to tell the + * child to exit + */ + ap_snprintf(exit_event_name, sizeof(exit_event_name), "%s_C%d", kill_event_name, ++(*child_num)); + kill_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name); + if (!kill_event) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Could not create exit event for child process"); + return -1; + } + + pCommand = ap_psprintf(p, "\"%s\" -Z %s -f \"%s\"", buf, exit_event_name, ap_server_confname); + + for (i = 1; i < argc; i++) { + pCommand = ap_pstrcat(p, pCommand, " ", argv[i], NULL); + } + + /* Create a pipe to send socket info to the child */ + if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Unable to create pipe to child process.\n"); + return -1; + } + + /* Give the read in of teh pipe (hPipeRead) to the child as stdin. The + * parent will write the socket data to the child on this pipe. + */ + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = hPipeRead; + + if (!CreateProcess(NULL, pCommand, NULL, NULL, + TRUE, /* Inherit handles */ + 0, /* Creation flags */ + NULL, NULL, + &si, &pi)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Not able to create the child process."); + /* + * We must close the handles to the new process and its main thread + * to prevent handle and memory leaks. + */ + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return -1; + } + else { + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf, + "Parent: Created child process %d", pi.dwProcessId); + + /* Assume the child process lives. Update the process and event tables */ + handles[*processes] = pi.hProcess; + events[*processes] = kill_event; + (*processes)++; + + /* We never store the thread's handle, so close it now. */ + CloseHandle(pi.hThread); + + /* Run the chain of open sockets. For each socket, duplicate it + * for the target process then send the WSAPROTOCOL_INFO + * (returned by dup socket) to the child */ + lr = ap_listeners; + while (lr != NULL) { + lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO)); + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf, + "Parent: Duplicating socket %d and sending it to child process %d", lr->fd, pi.dwProcessId); + if (WSADuplicateSocket(lr->fd, + pi.dwProcessId, + lpWSAProtocolInfo) == SOCKET_ERROR) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: WSADuplicateSocket failed for socket %d.", lr->fd ); + return -1; + } + + if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO), + &BytesWritten, + (LPOVERLAPPED) NULL)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Unable to write duplicated socket %d to the child.", lr->fd ); + return -1; + } + + lr = lr->next; + if (lr == ap_listeners) + break; + } + } + CloseHandle(hPipeRead); + CloseHandle(hPipeWrite); + + return 0; +} + +/* To share the semaphores with other processes, we need a NULL ACL + * Code from MS KB Q106387 + */ + +static PSECURITY_ATTRIBUTES GetNullACL() +{ + PSECURITY_DESCRIPTOR pSD; + PSECURITY_ATTRIBUTES sa; + + sa = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES)); + pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, + SECURITY_DESCRIPTOR_MIN_LENGTH); + if (pSD == NULL || sa == NULL) { + return NULL; + } + /* + * We can safely use GetLastError() here without presetting it; + * {Initialize,Set}SecurityDescriptor() have been verified as clearing it + * on successful completion. + */ + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) + || GetLastError()) { + LocalFree( pSD ); + LocalFree( sa ); + return NULL; + } + if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE) + || GetLastError()) { + LocalFree( pSD ); + LocalFree( sa ); + return NULL; + } + sa->nLength = sizeof(sa); + sa->lpSecurityDescriptor = pSD; + sa->bInheritHandle = TRUE; + return sa; +} + + +static void CleanNullACL( void *sa ) { + if( sa ) { + LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor); + LocalFree( sa ); + } +} + +int master_main(int argc, char **argv) +{ + /* returns NULL if invalid (Win95?) */ + PSECURITY_ATTRIBUTES sa = GetNullACL(); + int nchild = ap_daemons_to_start; + int child_num = 0; + int rv, cld; + char signal_prefix_string[100]; + int i; + time_t tmstart; + HANDLE signal_shutdown_event; /* used to signal shutdown to parent */ + HANDLE signal_restart_event; /* used to signal a restart to parent */ + HANDLE process_handles[MAX_PROCESSES]; + HANDLE process_kill_events[MAX_PROCESSES]; + int current_live_processes = 0; /* number of child process we know about */ + int processes_to_create = 0; /* number of child processes to create */ + pool *pparent = NULL; /* pool for the parent process. Cleaned on each restart */ + + nchild = 1; /* only allowed one child process for current generation */ + processes_to_create = nchild; + + is_graceful = 0; + + ap_snprintf(signal_prefix_string, sizeof(signal_prefix_string), + "ap%d", getpid()); + setup_signal_names(signal_prefix_string); + + /* Create shutdown event, apPID_shutdown, where PID is the parent + * Apache process ID. Shutdown is signaled by 'apache -k shutdown'. + */ + signal_shutdown_event = CreateEvent(sa, TRUE, FALSE, signal_shutdown_name); + if (!signal_shutdown_event) { + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, + "master_main: Cannot create shutdown event %s", signal_shutdown_name); + CleanNullACL((void *)sa); + exit(1); + } + + /* Create restart event, apPID_restart, where PID is the parent + * Apache process ID. Restart is signaled by 'apache -k restart'. + */ + signal_restart_event = CreateEvent(sa, TRUE, FALSE, signal_restart_name); + if (!signal_restart_event) { + CloseHandle(signal_shutdown_event); + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, + "master_main: Cannot create restart event %s", signal_restart_name); + CleanNullACL((void *)sa); + exit(1); + } + CleanNullACL((void *)sa); + + /* Create the start mutex, apPID, where PID is the parent Apache process ID. + * Ths start mutex is used during a restart to prevent more than one + * child process from entering the accept loop at once. + */ + start_mutex = ap_create_mutex(signal_prefix_string); + restart_pending = shutdown_pending = 0; + + do { /* restart-pending */ + if (!is_graceful) { + ap_restart_time = time(NULL); + } + copy_listeners(pconf); + ap_clear_pool(pconf); + pparent = ap_make_sub_pool(pconf); + + server_conf = ap_read_config(pconf, pparent, ap_server_confname); + setup_listeners(pconf); + ap_clear_pool(plog); + ap_open_logs(server_conf, plog); + ap_set_version(); + ap_init_modules(pconf, server_conf); + version_locked++; + service_set_status(SERVICE_START_PENDING); + /* Create child processes */ + while (processes_to_create--) { + if (create_process(pconf, process_handles, process_kill_events, + ¤t_live_processes, &child_num, signal_prefix_string, argc, argv) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "master_main: create child process failed. Exiting."); + goto die_now; + } + } + service_set_status(SERVICE_RUNNING); + restart_pending = shutdown_pending = 0; + + /* Wait for either the shutdown or restart events to be signaled */ + process_handles[current_live_processes] = signal_shutdown_event; + process_handles[current_live_processes+1] = signal_restart_event; + rv = WaitForMultipleObjects(current_live_processes+2, (HANDLE *)process_handles, + FALSE, INFINITE); + if (rv == WAIT_FAILED) { + /* Something serious is wrong */ + ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, server_conf, + "master_main: : WaitForMultipeObjects on process handles and apache-signal -- doing shutdown"); + shutdown_pending = 1; + break; + } + if (rv == WAIT_TIMEOUT) { + /* Hey, this cannot happen */ + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT"); + shutdown_pending = 1; + } + + cld = rv - WAIT_OBJECT_0; + APD4("main process: wait finished, cld=%d handle %d (max=%d)", cld, process_handles[cld], current_live_processes); + if (cld == current_live_processes) { + /* apPID_shutdown event signalled, we should exit now */ + shutdown_pending = 1; + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "master_main: Shutdown event signaled. Shutting the server down."); + if (ResetEvent(signal_shutdown_event) == 0) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "ResetEvent(signal_shutdown_event)"); + } + /* Signal each child processes to die */ + for (i = 0; i < current_live_processes; i++) { + APD3("master_main: signalling child %d, handle %d to die", i, process_handles[i]); + if (SetEvent(process_kill_events[i]) == 0) + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "master_main: SetEvent for child process in slot #%d failed", i); + } + break; + } else if (cld == current_live_processes+1) { + /* apPID_restart event signalled, restart the child process */ + int children_to_kill = current_live_processes; + restart_pending = 1; + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "master_main: Restart event signaled. Doing a graceful restart."); + if (ResetEvent(signal_restart_event) == 0) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "master_main: ResetEvent(signal_restart_event) failed."); + } + /* Signal each child process to die */ + for (i = 0; i < children_to_kill; i++) { + APD3("master_main: signalling child #%d handle %d to die", i, process_handles[i]); + if (SetEvent(process_kill_events[i]) == 0) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "master_main: SetEvent for child process in slot #%d failed", i); + /* Remove the process (and event) from the process table */ + cleanup_process(process_handles, process_kill_events, i, ¤t_live_processes); + } + processes_to_create = nchild; + ++ap_my_generation; + continue; + } else { + /* A child process must have exited because of MaxRequestPerChild being hit + * or a fatal error condition (seg fault, etc.). Remove the dead process + * from the process_handles and process_kill_events table and create a new + * child process. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "master_main: Child processed exited (due to MaxRequestsPerChild?). Restarting the child process."); + ap_assert(cld < current_live_processes); + cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes); + APD2("main_process: child in slot %d died", rv); + processes_to_create = 1; + continue; + } + + } while (1); + + /* If we dropped out of the loop we definitly want to die completely. We need to + * make sure we wait for all the child process to exit first. + */ + + APD2("*** main process shutdown, processes=%d ***", current_live_processes); + +die_now: + + tmstart = time(NULL); + while (current_live_processes && ((tmstart+60) > time(NULL))) { + service_set_status(SERVICE_STOP_PENDING); + rv = WaitForMultipleObjects(current_live_processes, (HANDLE *)process_handles, FALSE, 2000); + if (rv == WAIT_TIMEOUT) + continue; + ap_assert(rv != WAIT_FAILED); + cld = rv - WAIT_OBJECT_0; + ap_assert(rv < current_live_processes); + APD4("main_process: child in #%d handle %d died, left=%d", + rv, process_handles[rv], current_live_processes); + cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes); + } + for (i = 0; i < current_live_processes; i++) { + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO, server_conf, + "forcing termination of child #%d (handle %d)", i, process_handles[i]); + TerminateProcess((HANDLE) process_handles[i], 1); + } + + CloseHandle(signal_restart_event); + CloseHandle(signal_shutdown_event); + + /* cleanup pid file on normal shutdown */ + { + const char *pidfile = NULL; + pidfile = ap_server_root_relative (pparent, ap_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + server_conf, + "removed PID file %s (pid=%ld)", + pidfile, (long)getpid()); + } + + if (pparent) { + ap_destroy_pool(pparent); + } + + ap_destroy_mutex(start_mutex); + + service_set_status(SERVICE_STOPPED); + return (0); +} + +/* + * Send signal to a running Apache. On entry signal should contain + * either "shutdown" or "restart" + */ + +int send_signal(pool *p, char *signal) +{ + char prefix[20]; + FILE *fp; + int nread; + char *fname; + int end; + + fname = ap_server_root_relative (p, ap_pid_fname); + + fp = fopen(fname, "r"); + if (!fp) { + printf("Cannot read apache PID file %s\n", fname); + return FALSE; + } + prefix[0] = 'a'; + prefix[1] = 'p'; + + nread = fread(prefix+2, 1, sizeof(prefix)-3, fp); + if (nread == 0) { + fclose(fp); + printf("PID file %s was empty\n", fname); + return FALSE; + } + fclose(fp); + + /* Terminate the prefix string */ + end = 2 + nread - 1; + while (end > 0 && (prefix[end] == '\r' || prefix[end] == '\n')) + end--; + prefix[end + 1] = '\0'; + + setup_signal_names(prefix); + + if (!strcasecmp(signal, "shutdown")) + ap_start_shutdown(); + else if (!strcasecmp(signal, "restart")) + ap_start_restart(1); + else { + printf("Unknown signal name \"%s\". Use either shutdown or restart.\n", + signal); + return FALSE; + } + return TRUE; +} + +void post_parse_init() +{ + ap_set_version(); + ap_init_modules(pconf, server_conf); + ap_suexec_enabled = init_suexec(); + version_locked++; + ap_open_logs(server_conf, plog); + set_group_privs(); +} + +int service_init() +{ + common_init(); + + ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root)); + if (ap_registry_get_service_conf(pconf, ap_server_confname, sizeof(ap_server_confname), + ap_server_argv0)) + return FALSE; + + ap_setup_prelinked_modules(); + server_conf = ap_read_config(pconf, ptrans, ap_server_confname); + ap_log_pid(pconf, ap_pid_fname); + post_parse_init(); + return TRUE; +} + +#ifdef WIN32 +__declspec(dllexport) + int apache_main(int argc, char *argv[]) +#else +int REALMAIN(int argc, char *argv[]) +#endif +{ + int c; + int child = 0; + char *cp; + char *s; + char *service_name = NULL; + int install = 0; + int conf_specified = 0; + char *signal_to_send = NULL; + char cwd[MAX_STRING_LEN]; + + /* Service application + * Configuration file in registry at: + * HKLM\System\CurrentControlSet\Services\[Svc name]\Parameters\ConfPath + */ + if (isProcessService()) { + service_main(master_main, argc, argv); + clean_parent_exit(0); + } + + /* Console application or a child process. */ + + if ((s = strrchr(argv[0], PATHSEPARATOR)) != NULL) { + ap_server_argv0 = ++s; + } + else { + ap_server_argv0 = argv[0]; + } + + common_init(); + ap_setup_prelinked_modules(); + + if(!GetCurrentDirectory(sizeof(cwd),cwd)) { + ap_log_error(APLOG_MARK,APLOG_WIN32ERROR, NULL, + "GetCurrentDirectory() failure"); + return -1; + } + + ap_cpystrn(cwd, ap_os_canonical_filename(pcommands, cwd), sizeof(cwd)); + ap_cpystrn(ap_server_root, cwd, sizeof(ap_server_root)); + + while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVlLZ:iusStThk:n:")) != -1) { + char **new; + switch (c) { + case 'c': + new = (char **)ap_push_array(ap_server_post_read_config); + *new = ap_pstrdup(pcommands, optarg); + break; + case 'C': + new = (char **)ap_push_array(ap_server_pre_read_config); + *new = ap_pstrdup(pcommands, optarg); + break; + case 'D': + new = (char **)ap_push_array(ap_server_config_defines); + *new = ap_pstrdup(pcommands, optarg); + break; +#ifdef WIN32 + case 'Z': + exit_event = open_event(optarg); + APD2("child: opened process event %s", optarg); + cp = strchr(optarg, '_'); + ap_assert(cp); + *cp = 0; + setup_signal_names(optarg); + start_mutex = ap_open_mutex(signal_name_prefix); + ap_assert(start_mutex); + child = 1; + break; + case 'n': + service_name = ap_pstrdup(pcommands, optarg); + if (isValidService(optarg)) { + ap_registry_get_service_conf(pconf, ap_server_confname, sizeof(ap_server_confname), + optarg); + conf_specified = 1; + } + break; + case 'i': + install = 1; + break; + case 'u': + install = -1; + break; + case 'S': + ap_dump_settings = 1; + break; + case 'k': + signal_to_send = optarg; + break; +#endif /* WIN32 */ + case 'd': + optarg = ap_os_canonical_filename(pcommands, optarg); + if (!ap_os_is_path_absolute(optarg)) { + optarg = ap_pstrcat(pcommands, cwd, optarg, NULL); + ap_getparents(optarg); + } + if (optarg[strlen(optarg)-1] != '/') + optarg = ap_pstrcat(pcommands, optarg, "/", NULL); + ap_cpystrn(ap_server_root, + optarg, + sizeof(ap_server_root)); + break; + case 'f': + ap_cpystrn(ap_server_confname, + ap_os_canonical_filename(pcommands, optarg), + sizeof(ap_server_confname)); + conf_specified = 1; + break; + case 'v': + ap_set_version(); + printf("Server version: %s\n", ap_get_server_version()); + printf("Server built: %s\n", ap_get_server_built()); + exit(0); + case 'V': + ap_set_version(); + show_compile_settings(); + exit(0); + case 'l': + ap_show_modules(); + exit(0); + case 'L': + ap_show_directives(); + exit(0); + case 'X': + ++one_process; /* Weird debugging mode. */ + break; + case 't': + ap_configtestonly = 1; + ap_docrootcheck = 1; + break; + case 'T': + ap_configtestonly = 1; + ap_docrootcheck = 0; + break; + case 'h': + usage(ap_server_argv0); + case '?': + usage(ap_server_argv0); + } /* switch */ + } /* while */ + + /* ServerConfFile is found in this order: + * (1) -f or -n + * (2) [-d]/SERVER_CONFIG_FILE + * (3) ./SERVER_CONFIG_FILE + * (4) [Registry: HKLM\Software\[product]\ServerRoot]/SERVER_CONFIG_FILE + * (5) /HTTPD_ROOT/SERVER_CONFIG_FILE + */ + + if (!conf_specified) { + ap_cpystrn(ap_server_confname, SERVER_CONFIG_FILE, sizeof(ap_server_confname)); + if (access(ap_server_root_relative(pcommands, ap_server_confname), 0)) { + ap_registry_get_server_root(pconf, ap_server_root, sizeof(ap_server_root)); + if (!*ap_server_root) + ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root)); + ap_cpystrn(ap_server_root, ap_os_canonical_filename(pcommands, ap_server_root), + sizeof(ap_server_root)); + } + } + + if (!ap_os_is_path_absolute(ap_server_confname)) { + char *full_conf_path; + + full_conf_path = ap_pstrcat(pcommands, ap_server_root, "/", ap_server_confname, NULL); + full_conf_path = ap_os_canonical_filename(pcommands, full_conf_path); + ap_cpystrn(ap_server_confname, full_conf_path, sizeof(ap_server_confname)); + } + ap_getparents(ap_server_confname); + ap_no2slash(ap_server_confname); + +#ifdef WIN32 + if (install) { + if (!service_name) + service_name = ap_pstrdup(pconf, DEFAULTSERVICENAME); + if (install > 0) + InstallService(service_name, ap_server_root_relative(pcommands, ap_server_confname)); + else + RemoveService(service_name); + clean_parent_exit(0); + } + + if (service_name && signal_to_send) { + send_signal_to_service(service_name, signal_to_send); + clean_parent_exit(0); + } + + if (service_name && !conf_specified) { + printf("Unknown service: %s\n", service_name); + clean_parent_exit(0); + } +#endif + server_conf = ap_read_config(pconf, ptrans, ap_server_confname); + + if (ap_configtestonly) { + fprintf(stderr, "%s: Syntax OK\n", ap_server_root_relative(pcommands, ap_server_confname)); + clean_parent_exit(0); + } + + if (ap_dump_settings) { + clean_parent_exit(0); + } + + /* Treat -k start confpath as just -f confpath */ + if (signal_to_send && strcasecmp(signal_to_send, "start")) { + send_signal(pconf, signal_to_send); + clean_parent_exit(0); + } + + if (!child && !ap_dump_settings) { + ap_log_pid(pconf, ap_pid_fname); + } + + post_parse_init(); + +#ifdef OS2 + printf("%s running...\n", ap_get_server_version()); +#endif +#ifdef WIN32 + if (!child) { + printf("%s running...\n", ap_get_server_version()); + } +#endif + if (one_process && !exit_event) + exit_event = create_event(0, 0, NULL); + if (one_process && !start_mutex) + start_mutex = ap_create_mutex(NULL); + /* + * In the future, the main will spawn off a couple + * of children and monitor them. As soon as a child + * exits, it spawns off a new one + */ + if (child || one_process) { + if (!exit_event || !start_mutex) + exit(-1); + worker_main(); + ap_destroy_mutex(start_mutex); + destroy_event(exit_event); + } + else + master_main(argc, argv); + + clean_parent_exit(0); + return 0; /* purely to avoid a warning */ +} + +#endif /* ndef MULTITHREAD */ + +#else /* ndef SHARED_CORE_TIESTATIC */ + +/* +** Standalone Tie Program for Shared Core support +** +** It's purpose is to tie the static libraries and +** the shared core library under link-time and +** passing execution control to the real main function +** in the shared core library under run-time. +*/ + +extern int ap_main(int argc, char *argv[]); + +int main(int argc, char *argv[]) +{ + return ap_main(argc, argv); +} + +#endif /* ndef SHARED_CORE_TIESTATIC */ +#else /* ndef SHARED_CORE_BOOTSTRAP */ + +#ifdef OS2 +/* Shared core loader for OS/2 */ + +int ap_main(int argc, char *argv[]); /* Load time linked from libhttpd.dll */ + +int main(int argc, char *argv[]) +{ + return ap_main(argc, argv); +} + +#else + +/* +** Standalone Bootstrap Program for Shared Core support +** +** It's purpose is to initialise the LD_LIBRARY_PATH +** environment variable therewith the Unix loader is able +** to start the Standalone Tie Program (see above) +** and then replacing itself with this program by +** immediately passing execution to it. +*/ + +#include +#include +#include + +#include "ap_config.h" +#include "httpd.h" + +#if defined(HPUX) || defined(HPUX10) || defined(HPUX11) +#define VARNAME "SHLIB_PATH" +#else +#define VARNAME "LD_LIBRARY_PATH" +#endif + +#ifndef SHARED_CORE_DIR +#define SHARED_CORE_DIR HTTPD_ROOT "/libexec" +#endif + +#ifndef SHARED_CORE_EXECUTABLE_PROGRAM +#define SHARED_CORE_EXECUTABLE_PROGRAM "lib" TARGET ".ep" +#endif + +extern char *optarg; +extern int optind; + +int main(int argc, char *argv[], char *envp[]) +{ + char prog[MAX_STRING_LEN]; + char llp_buf[MAX_STRING_LEN]; + char **llp_slot; + char *llp_existing; + char *llp_dir; + char **envpnew; + int c, i, l; + + /* + * parse argument line, + * but only handle the -L option + */ + llp_dir = SHARED_CORE_DIR; + while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVlLR:SZ:tTh")) != -1) { + switch (c) { + case 'D': + case 'C': + case 'c': + case 'X': + case 'd': + case 'f': + case 'v': + case 'V': + case 'l': + case 'L': + case 'S': + case 'Z': + case 't': + case 'T': + case 'h': + case '?': + break; + case 'R': + llp_dir = strdup(optarg); + break; + } + } + + /* + * create path to SHARED_CORE_EXECUTABLE_PROGRAM + */ + ap_snprintf(prog, sizeof(prog), "%s/%s", llp_dir, SHARED_CORE_EXECUTABLE_PROGRAM); + + /* + * adjust process environment therewith the Unix loader + * is able to start the SHARED_CORE_EXECUTABLE_PROGRAM. + */ + llp_slot = NULL; + llp_existing = NULL; + l = strlen(VARNAME); + for (i = 0; envp[i] != NULL; i++) { + if (strncmp(envp[i], VARNAME "=", l+1) == 0) { + llp_slot = &envp[i]; + llp_existing = strchr(envp[i], '=') + 1; + } + } + if (llp_slot == NULL) { + envpnew = (char **)malloc(sizeof(char *)*(i + 2)); + if (envpnew == NULL) { + fprintf(stderr, "Ouch! Out of memory generating envpnew!\n"); + } + memcpy(envpnew, envp, sizeof(char *)*i); + envp = envpnew; + llp_slot = &envp[i++]; + envp[i] = NULL; + } + if (llp_existing != NULL) + ap_snprintf(llp_buf, sizeof(llp_buf), "%s=%s:%s", VARNAME, llp_dir, llp_existing); + else + ap_snprintf(llp_buf, sizeof(llp_buf), "%s=%s", VARNAME, llp_dir); + *llp_slot = strdup(llp_buf); + + /* + * finally replace our process with + * the SHARED_CORE_EXECUTABLE_PROGRAM + */ + if (execve(prog, argv, envp) == -1) { + fprintf(stderr, + "%s: Unable to exec Shared Core Executable Program `%s'\n", + argv[0], prog); + return 1; + } + else + return 0; +} + +#endif /* def OS2 */ +#endif /* ndef SHARED_CORE_BOOTSTRAP */ + +#ifndef SHARED_CORE_BOOTSTRAP +/* + * Force ap_validate_password() into the image so that modules like + * mod_auth can use it even if they're dynamically loaded. + */ +void suck_in_ap_validate_password(void); +void suck_in_ap_validate_password(void) +{ + ap_validate_password("a", "b"); +} +#endif + +/* force Expat to be linked into the server executable */ +#if defined(USE_EXPAT) && !defined(SHARED_CORE_BOOTSTRAP) +#include "xmlparse.h" +const XML_LChar *suck_in_expat(void); +const XML_LChar *suck_in_expat(void) +{ + return XML_ErrorString(XML_ERROR_NONE); +} +#endif /* USE_EXPAT */ + diff --git a/server/mpm/.cvsignore b/server/mpm/.cvsignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/server/mpm/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/server/mpm/MPM.NAMING b/server/mpm/MPM.NAMING new file mode 100644 index 00000000000..3ce40f02e6a --- /dev/null +++ b/server/mpm/MPM.NAMING @@ -0,0 +1,22 @@ +For right now, the naming of the MPM methods follows +the following example for the 'prefork' method. + + 1. The directory name under modules/mpm is the actual + name. + + eg: modules/mpm/prefork + + 2. The internal module name must be that name, prepended with + mpm_ and suffixed with _module + + eg: mpm_prefork_module + + 3. The Makefile.tmpl must create lib.a + + eg: libprefork.a + +Numbers 1 and 3 follow the normal rules (for example, modules/standard +creates libstandard.a, etc...). + +The MPM_METHOD Rule will use the actual name (eg: prefork) to +pick the correct method module to build. diff --git a/server/mpm/Makefile.in b/server/mpm/Makefile.in new file mode 100644 index 00000000000..7f678e86bfd --- /dev/null +++ b/server/mpm/Makefile.in @@ -0,0 +1,9 @@ + +DEPTH = ../.. +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +SUBDIRS = $(MPM_NAME) + +include $(topsrcdir)/build/rules.mk diff --git a/server/mpm/config.m4 b/server/mpm/config.m4 new file mode 100644 index 00000000000..3c30fd07c0c --- /dev/null +++ b/server/mpm/config.m4 @@ -0,0 +1,56 @@ +AC_MSG_CHECKING(which MPM to use) +AC_ARG_WITH(mpm, +[ --with-mpm=MPM Choose the process model, etc. for Apache to use.], +[ + if test "$withval" != "no" ; then + apache_cv_mpm=$withval + AC_MSG_RESULT($apache_cv_mpm) + else + AC_MSG_ERROR(An MPM must be specified) + fi +],[ + AC_MSG_ERROR(An MPM must be specified) +]) + +APACHE_OUTPUT(modules/mpm/Makefile) +MPM_NAME=$apache_cv_mpm +MPM_DIR=modules/mpm/$MPM_NAME +MPM_LIB=$MPM_DIR/lib${MPM_NAME}.la + +AC_SUBST(MPM_NAME) + +dnl All the unix MPMs use shared memory; save a little duplication +AC_DEFUN(APACHE_MPM_CHECK_SHMEM, [ + AC_CHECK_FUNCS(shmget) + AC_FUNC_MMAP + + AC_MSG_CHECKING(which shared memory mechanism to use) + if test "$ac_cv_func_shmget" = "yes" ; then + AC_DEFINE(USE_SHMGET_SCOREBOARD) + AC_MSG_RESULT(shmget) + elif test "$ac_cv_func_mmap" = "yes" ; then + AC_DEFINE(USE_MMAP_SCOREBOARD) + AC_MSG_RESULT(mmap) + else + AC_MSG_ERROR(No known shared memory system) + fi +]) + +dnl Check for pthreads and attempt to support it +AC_DEFUN(APACHE_MPM_PTHREAD, [ + +dnl XXX - We should be checking for the proper flags to use on a particular +dnl platform. This will cover a couple of them, anyway + CFLAGS="-pthread $CFLAGS" + CXXFLAGS="-pthread $CXXFLAGS" + + AC_CHECK_HEADER(pthread.h, [ ],[ + AC_MSG_ERROR(This MPM requires pthreads. Try --with-mpm=prefork.) + ]) + AC_CHECK_FUNC(pthread_create, [ ],[ + AC_MSG_ERROR(Can't compile pthread code.) + ]) + + dnl User threads libraries need pthread.h included everywhere + AC_DEFINE(PTHREAD_EVERYWHERE) +]) diff --git a/server/mpm/prefork/.cvsignore b/server/mpm/prefork/.cvsignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/server/mpm/prefork/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/server/mpm/prefork/Makefile.in b/server/mpm/prefork/Makefile.in new file mode 100644 index 00000000000..5d086070f2c --- /dev/null +++ b/server/mpm/prefork/Makefile.in @@ -0,0 +1,10 @@ + +DEPTH = ../../.. +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +LTLIBRARY_NAME = libprefork.la +LTLIBRARY_SOURCES = prefork.c + +include $(topsrcdir)/build/ltlib.mk diff --git a/server/mpm/prefork/config.m4 b/server/mpm/prefork/config.m4 new file mode 100644 index 00000000000..b4cfe383576 --- /dev/null +++ b/server/mpm/prefork/config.m4 @@ -0,0 +1,5 @@ +if test "$MPM_NAME" = "prefork" ; then + APACHE_OUTPUT(modules/mpm/$MPM_NAME/Makefile) + + APACHE_MPM_CHECK_SHMEM +fi diff --git a/server/mpm/prefork/mpm_default.h b/server/mpm/prefork/mpm_default.h new file mode 100644 index 00000000000..3ff9ac27f7f --- /dev/null +++ b/server/mpm/prefork/mpm_default.h @@ -0,0 +1,97 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_MPM_DEFAULT_H +#define APACHE_MPM_DEFAULT_H + +/* Number of servers to spawn off by default --- also, if fewer than + * this free when the caretaker checks, it will spawn more. + */ +#ifndef DEFAULT_START_DAEMON +#define DEFAULT_START_DAEMON 5 +#endif + +/* Maximum number of *free* server processes --- more than this, and + * they will die off. + */ + +#ifndef DEFAULT_MAX_FREE_DAEMON +#define DEFAULT_MAX_FREE_DAEMON 10 +#endif + +/* Minimum --- fewer than this, and more will be created */ + +#ifndef DEFAULT_MIN_FREE_DAEMON +#define DEFAULT_MIN_FREE_DAEMON 5 +#endif + +/* Limit on the total --- clients will be locked out if more servers than + * this are needed. It is intended solely to keep the server from crashing + * when things get out of hand. + * + * We keep a hard maximum number of servers, for two reasons --- first off, + * in case something goes seriously wrong, we want to stop the fork bomb + * short of actually crashing the machine we're running on by filling some + * kernel table. Secondly, it keeps the size of the scoreboard file small + * enough that we can read the whole thing without worrying too much about + * the overhead. + */ +#ifndef HARD_SERVER_LIMIT +#define HARD_SERVER_LIMIT 256 +#endif + +#endif /* AP_MPM_DEFAULT_H */ diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c new file mode 100644 index 00000000000..0d7c19e05b3 --- /dev/null +++ b/server/mpm/prefork/prefork.c @@ -0,0 +1,3514 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * httpd.c: simple http daemon for answering WWW file requests + * + * + * 03-21-93 Rob McCool wrote original code (up to NCSA HTTPd 1.3) + * + * 03-06-95 blong + * changed server number for child-alone processes to 0 and changed name + * of processes + * + * 03-10-95 blong + * Added numerous speed hacks proposed by Robert S. Thau (rst@ai.mit.edu) + * including set group before fork, and call gettime before to fork + * to set up libraries. + * + * 04-14-95 rst / rh + * Brandon's code snarfed from NCSA 1.4, but tinkered to work with the + * Apache server, and also to have child processes do accept() directly. + * + * April-July '95 rst + * Extensive rework for Apache. + */ + +/* TODO: this is a cobbled together prefork MPM example... it should mostly + * TODO: behave like apache-1.3... here's a short list of things I think + * TODO: need cleaning up still: + * TODO: - use ralf's mm stuff for the shared mem and mutexes + * TODO: - abstract the Listen stuff, it's going to be common with other MPM + * TODO: - clean up scoreboard stuff when we figure out how to do it in 2.0 + */ + +#define CORE_PRIVATE + +#include "httpd.h" +#include "http_main.h" +#include "http_log.h" +#include "http_config.h" +#include "http_core.h" /* for get_remote_host */ +#include "http_connection.h" +#include "scoreboard_prefork.h" +#include "ap_mpm.h" +#include "unixd.h" +#ifdef USE_SHMGET_SCOREBOARD +#include +#include +#include +#endif + +#ifdef HAVE_BSTRING_H +#include /* for IRIX, FD_SET calls bzero() */ +#endif + +/* config globals */ + +static int ap_max_requests_per_child=0; +static char *ap_pid_fname=NULL; +static char *ap_scoreboard_fname=NULL; +static char *ap_lock_fname; +static char *ap_server_argv0=NULL; +static struct in_addr ap_bind_address; +static int ap_daemons_to_start=0; +static int ap_daemons_min_free=0; +static int ap_daemons_max_free=0; +static int ap_daemons_limit=0; +static time_t ap_restart_time=0; +static int ap_listenbacklog; +static int ap_extended_status = 0; + +/* + * The max child slot ever assigned, preserved across restarts. Necessary + * to deal with MaxClients changes across SIGUSR1 restarts. We use this + * value to optimize routines that have to scan the entire scoreboard. + */ +static int max_daemons_limit = -1; + +/* + * During config time, listeners is treated as a NULL-terminated list. + * child_main previously would start at the beginning of the list each time + * through the loop, so a socket early on in the list could easily starve out + * sockets later on in the list. The solution is to start at the listener + * after the last one processed. But to do that fast/easily in child_main it's + * way more convenient for listeners to be a ring that loops back on itself. + * The routine setup_listeners() is called after config time to both open up + * the sockets and to turn the NULL-terminated list into a ring that loops back + * on itself. + * + * head_listener is used by each child to keep track of what they consider + * to be the "start" of the ring. It is also set by make_child to ensure + * that new children also don't starve any sockets. + * + * Note that listeners != NULL is ensured by read_config(). + */ +static listen_rec *ap_listeners; +static listen_rec *head_listener; + +static char ap_coredump_dir[MAX_STRING_LEN]; + +/* *Non*-shared http_main globals... */ + +static server_rec *server_conf; +static int sd; +static fd_set listenfds; +static int listenmaxfd; + +/* one_process --- debugging mode variable; can be set from the command line + * with the -X flag. If set, this gets you the child_main loop running + * in the process which originally started up (no detach, no make_child), + * which is a pretty nice debugging environment. (You'll get a SIGHUP + * early in standalone_main; just continue through. This is the server + * trying to kill off any child processes which it might have lying + * around --- Apache doesn't keep track of their pids, it just sends + * SIGHUP to the process group, ignoring it in the root process. + * Continue through and you'll be fine.). + */ + +static int one_process = 0; + +#ifdef HAS_OTHER_CHILD +/* used to maintain list of children which aren't part of the scoreboard */ +typedef struct other_child_rec other_child_rec; +struct other_child_rec { + other_child_rec *next; + int pid; + void (*maintenance) (int, void *, ap_wait_t); + void *data; + int write_fd; +}; +static other_child_rec *other_children; +#endif + +static pool *pconf; /* Pool for config stuff */ +static pool *pchild; /* Pool for httpd child stuff */ + +static int my_pid; /* it seems silly to call getpid all the time */ +#ifndef MULTITHREAD +static int my_child_num; +#endif + +#ifdef TPF +int tpf_child = 0; +char tpf_server_name[INETD_SERVNAME_LENGTH+1]; +#endif /* TPF */ + +static scoreboard *ap_scoreboard_image = NULL; + +static int volatile exit_after_unblock = 0; + +#ifdef GPROF +/* + * change directory for gprof to plop the gmon.out file + * configure in httpd.conf: + * GprofDir logs/ -> $ServerRoot/logs/gmon.out + * GprofDir logs/% -> $ServerRoot/logs/gprof.$pid/gmon.out + */ +static void chdir_for_gprof(void) +{ + core_server_config *sconf = + ap_get_module_config(server_conf->module_config, &core_module); + char *dir = sconf->gprof_dir; + + if(dir) { + char buf[512]; + int len = strlen(sconf->gprof_dir) - 1; + if(*(dir + len) == '%') { + dir[len] = '\0'; + ap_snprintf(buf, sizeof(buf), "%sgprof.%d", dir, (int)getpid()); + } + dir = ap_server_root_relative(pconf, buf[0] ? buf : dir); + if(mkdir(dir, 0755) < 0 && errno != EEXIST) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "gprof: error creating directory %s", dir); + } + } + else { + dir = ap_server_root_relative(pconf, "logs"); + } + + chdir(dir); +} +#else +#define chdir_for_gprof() +#endif + +/* a clean exit from a child with proper cleanup */ +static void clean_child_exit(int code) __attribute__ ((noreturn)); +static void clean_child_exit(int code) +{ + if (pchild) { + ap_destroy_pool(pchild); + } + chdir_for_gprof(); + exit(code); +} + +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) || defined(USE_FLOCK_SERIALIZED_ACCEPT) +static void expand_lock_fname(pool *p) +{ + /* XXXX possibly bogus cast */ + ap_lock_fname = ap_psprintf(p, "%s.%lu", + ap_server_root_relative(p, ap_lock_fname), (unsigned long)getpid()); +} +#endif + +#if defined (USE_USLOCK_SERIALIZED_ACCEPT) + +#include + +static ulock_t uslock = NULL; + +#define accept_mutex_child_init(x) + +static void accept_mutex_init(pool *p) +{ + ptrdiff_t old; + usptr_t *us; + + + /* default is 8, allocate enough for all the children plus the parent */ + if ((old = usconfig(CONF_INITUSERS, HARD_SERVER_LIMIT + 1)) == -1) { + perror("usconfig(CONF_INITUSERS)"); + exit(-1); + } + + if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) { + perror("usconfig(CONF_LOCKTYPE)"); + exit(-1); + } + if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) { + perror("usconfig(CONF_ARENATYPE)"); + exit(-1); + } + if ((us = usinit("/dev/zero")) == NULL) { + perror("usinit"); + exit(-1); + } + + if ((uslock = usnewlock(us)) == NULL) { + perror("usnewlock"); + exit(-1); + } +} + +static void accept_mutex_on(void) +{ + switch (ussetlock(uslock)) { + case 1: + /* got lock */ + break; + case 0: + fprintf(stderr, "didn't get lock\n"); + clean_child_exit(APEXIT_CHILDFATAL); + case -1: + perror("ussetlock"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + if (usunsetlock(uslock) == -1) { + perror("usunsetlock"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT) + +/* This code probably only works on Solaris ... but it works really fast + * on Solaris. Note that pthread mutexes are *NOT* released when a task + * dies ... the task has to free it itself. So we block signals and + * try to be nice about releasing the mutex. + */ + +#include + +static pthread_mutex_t *accept_mutex = (void *)(caddr_t) -1; +static int have_accept_mutex; +static sigset_t accept_block_mask; +static sigset_t accept_previous_mask; + +static void accept_mutex_child_cleanup(void *foo) +{ + if (accept_mutex != (void *)(caddr_t)-1 + && have_accept_mutex) { + pthread_mutex_unlock(accept_mutex); + } +} + +static void accept_mutex_child_init(pool *p) +{ + ap_register_cleanup(p, NULL, accept_mutex_child_cleanup, ap_null_cleanup); +} + +static void accept_mutex_cleanup(void *foo) +{ + if (accept_mutex != (void *)(caddr_t)-1 + && munmap((caddr_t) accept_mutex, sizeof(*accept_mutex))) { + perror("munmap"); + } + accept_mutex = (void *)(caddr_t)-1; +} + +static void accept_mutex_init(pool *p) +{ + pthread_mutexattr_t mattr; + int fd; + + fd = open("/dev/zero", O_RDWR); + if (fd == -1) { + perror("open(/dev/zero)"); + exit(APEXIT_INIT); + } + accept_mutex = (pthread_mutex_t *) mmap((caddr_t) 0, sizeof(*accept_mutex), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (accept_mutex == (void *) (caddr_t) - 1) { + perror("mmap"); + exit(APEXIT_INIT); + } + close(fd); + if ((errno = pthread_mutexattr_init(&mattr))) { + perror("pthread_mutexattr_init"); + exit(APEXIT_INIT); + } + if ((errno = pthread_mutexattr_setpshared(&mattr, + PTHREAD_PROCESS_SHARED))) { + perror("pthread_mutexattr_setpshared"); + exit(APEXIT_INIT); + } + if ((errno = pthread_mutex_init(accept_mutex, &mattr))) { + perror("pthread_mutex_init"); + exit(APEXIT_INIT); + } + sigfillset(&accept_block_mask); + sigdelset(&accept_block_mask, SIGHUP); + sigdelset(&accept_block_mask, SIGTERM); + sigdelset(&accept_block_mask, SIGUSR1); + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); +} + +static void accept_mutex_on(void) +{ + int err; + + if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) { + perror("sigprocmask(SIG_BLOCK)"); + clean_child_exit(APEXIT_CHILDFATAL); + } + if ((err = pthread_mutex_lock(accept_mutex))) { + errno = err; + perror("pthread_mutex_lock"); + clean_child_exit(APEXIT_CHILDFATAL); + } + have_accept_mutex = 1; +} + +static void accept_mutex_off(void) +{ + int err; + + if ((err = pthread_mutex_unlock(accept_mutex))) { + errno = err; + perror("pthread_mutex_unlock"); + clean_child_exit(APEXIT_CHILDFATAL); + } + /* There is a slight race condition right here... if we were to die right + * now, we'd do another pthread_mutex_unlock. Now, doing that would let + * another process into the mutex. pthread mutexes are designed to be + * fast, as such they don't have protection for things like testing if the + * thread owning a mutex is actually unlocking it (or even any way of + * testing who owns the mutex). + * + * If we were to unset have_accept_mutex prior to releasing the mutex + * then the race could result in the server unable to serve hits. Doing + * it this way means that the server can continue, but an additional + * child might be in the critical section ... at least it's still serving + * hits. + */ + have_accept_mutex = 0; + if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) { + perror("sigprocmask(SIG_SETMASK)"); + clean_child_exit(1); + } +} + +#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT) + +#include +#include +#include + +#ifdef NEED_UNION_SEMUN +/* it makes no sense, but this isn't defined on solaris */ +union semun { + long val; + struct semid_ds *buf; + ushort *array; +}; + +#endif + +static int sem_id = -1; +static struct sembuf op_on; +static struct sembuf op_off; + +/* We get a random semaphore ... the lame sysv semaphore interface + * means we have to be sure to clean this up or else we'll leak + * semaphores. + */ +static void accept_mutex_cleanup(void *foo) +{ + union semun ick; + + if (sem_id < 0) + return; + /* this is ignored anyhow */ + ick.val = 0; + semctl(sem_id, 0, IPC_RMID, ick); +} + +#define accept_mutex_child_init(x) + +static void accept_mutex_init(pool *p) +{ + union semun ick; + struct semid_ds buf; + + /* acquire the semaphore */ + sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600); + if (sem_id < 0) { + perror("semget"); + exit(APEXIT_INIT); + } + ick.val = 1; + if (semctl(sem_id, 0, SETVAL, ick) < 0) { + perror("semctl(SETVAL)"); + exit(APEXIT_INIT); + } + if (!getuid()) { + /* restrict it to use only by the appropriate user_id ... not that this + * stops CGIs from acquiring it and dinking around with it. + */ + buf.sem_perm.uid = unixd_config.user_id; + buf.sem_perm.gid = unixd_config.group_id; + buf.sem_perm.mode = 0600; + ick.buf = &buf; + if (semctl(sem_id, 0, IPC_SET, ick) < 0) { + perror("semctl(IPC_SET)"); + exit(APEXIT_INIT); + } + } + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); + + /* pre-initialize these */ + op_on.sem_num = 0; + op_on.sem_op = -1; + op_on.sem_flg = SEM_UNDO; + op_off.sem_num = 0; + op_off.sem_op = 1; + op_off.sem_flg = SEM_UNDO; +} + +static void accept_mutex_on(void) +{ + while (semop(sem_id, &op_on, 1) < 0) { + if (errno != EINTR) { + perror("accept_mutex_on"); + clean_child_exit(APEXIT_CHILDFATAL); + } + } +} + +static void accept_mutex_off(void) +{ + while (semop(sem_id, &op_off, 1) < 0) { + if (errno != EINTR) { + perror("accept_mutex_off"); + clean_child_exit(APEXIT_CHILDFATAL); + } + } +} + +#elif defined(USE_FCNTL_SERIALIZED_ACCEPT) +static struct flock lock_it; +static struct flock unlock_it; + +static int lock_fd = -1; + +#define accept_mutex_child_init(x) + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +static void accept_mutex_init(pool *p) +{ + + lock_it.l_whence = SEEK_SET; /* from current point */ + lock_it.l_start = 0; /* -"- */ + lock_it.l_len = 0; /* until end of file */ + lock_it.l_type = F_WRLCK; /* set exclusive/write lock */ + lock_it.l_pid = 0; /* pid not actually interesting */ + unlock_it.l_whence = SEEK_SET; /* from current point */ + unlock_it.l_start = 0; /* -"- */ + unlock_it.l_len = 0; /* until end of file */ + unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */ + unlock_it.l_pid = 0; /* pid not actually interesting */ + + expand_lock_fname(p); + lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0644); + if (lock_fd == -1) { + perror("open"); + fprintf(stderr, "Cannot open lock file: %s\n", ap_lock_fname); + exit(APEXIT_INIT); + } + unlink(ap_lock_fname); +} + +static void accept_mutex_on(void) +{ + int ret; + + while ((ret = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) { + /* nop */ + } + + if (ret < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "fcntl: F_SETLKW: Error getting accept lock, exiting! " + "Perhaps you need to use the LockFile directive to place " + "your lock file on a local disk!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + int ret; + + while ((ret = fcntl(lock_fd, F_SETLKW, &unlock_it)) < 0 && errno == EINTR) { + /* nop */ + } + if (ret < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "fcntl: F_SETLKW: Error freeing accept lock, exiting! " + "Perhaps you need to use the LockFile directive to place " + "your lock file on a local disk!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined(USE_FLOCK_SERIALIZED_ACCEPT) + +static int lock_fd = -1; + +static void accept_mutex_cleanup(void *foo) +{ + unlink(ap_lock_fname); +} + +/* + * Initialize mutex lock. + * Done by each child at it's birth + */ +static void accept_mutex_child_init(pool *p) +{ + + lock_fd = ap_popenf(p, ap_lock_fname, O_WRONLY, 0600); + if (lock_fd == -1) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "Child cannot open lock file: %s", ap_lock_fname); + clean_child_exit(APEXIT_CHILDINIT); + } +} + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +static void accept_mutex_init(pool *p) +{ + expand_lock_fname(p); + unlink(ap_lock_fname); + lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0600); + if (lock_fd == -1) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "Parent cannot open lock file: %s", ap_lock_fname); + exit(APEXIT_INIT); + } + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); +} + +static void accept_mutex_on(void) +{ + int ret; + + while ((ret = flock(lock_fd, LOCK_EX)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "flock: LOCK_EX: Error getting accept lock. Exiting!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + if (flock(lock_fd, LOCK_UN) < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "flock: LOCK_UN: Error freeing accept lock. Exiting!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined(USE_OS2SEM_SERIALIZED_ACCEPT) + +static HMTX lock_sem = -1; + +static void accept_mutex_cleanup(void *foo) +{ + DosReleaseMutexSem(lock_sem); + DosCloseMutexSem(lock_sem); +} + +/* + * Initialize mutex lock. + * Done by each child at it's birth + */ +static void accept_mutex_child_init(pool *p) +{ + int rc = DosOpenMutexSem(NULL, &lock_sem); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "Child cannot open lock semaphore, rc=%d", rc); + clean_child_exit(APEXIT_CHILDINIT); + } +} + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +static void accept_mutex_init(pool *p) +{ + int rc = DosCreateMutexSem(NULL, &lock_sem, DC_SEM_SHARED, FALSE); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "Parent cannot create lock semaphore, rc=%d", rc); + exit(APEXIT_INIT); + } + + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); +} + +static void accept_mutex_on(void) +{ + int rc = DosRequestMutexSem(lock_sem, SEM_INDEFINITE_WAIT); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "OS2SEM: Error %d getting accept lock. Exiting!", rc); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + int rc = DosReleaseMutexSem(lock_sem); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "OS2SEM: Error %d freeing accept lock. Exiting!", rc); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined(USE_TPF_CORE_SERIALIZED_ACCEPT) + +static int tpf_core_held; + +static void accept_mutex_cleanup(void *foo) +{ + if(tpf_core_held) + coruc(RESOURCE_KEY); +} + +#define accept_mutex_init(x) + +static void accept_mutex_child_init(pool *p) +{ + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); + tpf_core_held = 0; +} + +static void accept_mutex_on(void) +{ + corhc(RESOURCE_KEY); + tpf_core_held = 1; + ap_check_signals(); +} + +static void accept_mutex_off(void) +{ + coruc(RESOURCE_KEY); + tpf_core_held = 0; + ap_check_signals(); +} + +#else +/* Default --- no serialization. Other methods *could* go here, + * as #elifs... + */ +#if !defined(MULTITHREAD) +/* Multithreaded systems don't complete between processes for + * the sockets. */ +#define NO_SERIALIZED_ACCEPT +#define accept_mutex_child_init(x) +#define accept_mutex_init(x) +#define accept_mutex_on() +#define accept_mutex_off() +#endif +#endif + +/* On some architectures it's safe to do unserialized accept()s in the single + * Listen case. But it's never safe to do it in the case where there's + * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT + * when it's safe in the single Listen case. + */ +#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT +#define SAFE_ACCEPT(stmt) do {if(ap_listeners->next != ap_listeners) {stmt;}} while(0) +#else +#define SAFE_ACCEPT(stmt) do {stmt;} while(0) +#endif + + +/***************************************************************** + * dealing with other children + */ + +#ifdef HAS_OTHER_CHILD +API_EXPORT(void) ap_register_other_child(int pid, + void (*maintenance) (int reason, void *, ap_wait_t status), + void *data, int write_fd) +{ + other_child_rec *ocr; + + ocr = ap_palloc(pconf, sizeof(*ocr)); + ocr->pid = pid; + ocr->maintenance = maintenance; + ocr->data = data; + ocr->write_fd = write_fd; + ocr->next = other_children; + other_children = ocr; +} + +/* note that since this can be called by a maintenance function while we're + * scanning the other_children list, all scanners should protect themself + * by loading ocr->next before calling any maintenance function. + */ +API_EXPORT(void) ap_unregister_other_child(void *data) +{ + other_child_rec **pocr, *nocr; + + for (pocr = &other_children; *pocr; pocr = &(*pocr)->next) { + if ((*pocr)->data == data) { + nocr = (*pocr)->next; + (*(*pocr)->maintenance) (OC_REASON_UNREGISTER, (*pocr)->data, -1); + *pocr = nocr; + /* XXX: um, well we've just wasted some space in pconf ? */ + return; + } + } +} + +/* test to ensure that the write_fds are all still writable, otherwise + * invoke the maintenance functions as appropriate */ +static void probe_writable_fds(void) +{ + fd_set writable_fds; + int fd_max; + other_child_rec *ocr, *nocr; + struct timeval tv; + int rc; + + if (other_children == NULL) + return; + + fd_max = 0; + FD_ZERO(&writable_fds); + do { + for (ocr = other_children; ocr; ocr = ocr->next) { + if (ocr->write_fd == -1) + continue; + FD_SET(ocr->write_fd, &writable_fds); + if (ocr->write_fd > fd_max) { + fd_max = ocr->write_fd; + } + } + if (fd_max == 0) + return; + + tv.tv_sec = 0; + tv.tv_usec = 0; + rc = ap_select(fd_max + 1, NULL, &writable_fds, NULL, &tv); + } while (rc == -1 && errno == EINTR); + + if (rc == -1) { + /* XXX: uhh this could be really bad, we could have a bad file + * descriptor due to a bug in one of the maintenance routines */ + ap_log_unixerr("probe_writable_fds", "select", + "could not probe writable fds", server_conf); + return; + } + if (rc == 0) + return; + + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->write_fd == -1) + continue; + if (FD_ISSET(ocr->write_fd, &writable_fds)) + continue; + (*ocr->maintenance) (OC_REASON_UNWRITABLE, ocr->data, -1); + } +} + +/* possibly reap an other_child, return 0 if yes, -1 if not */ +static int reap_other_child(int pid, ap_wait_t status) +{ + other_child_rec *ocr, *nocr; + + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->pid != pid) + continue; + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status); + return 0; + } + return -1; +} +#endif + +/***************************************************************** + * + * Dealing with the scoreboard... a lot of these variables are global + * only to avoid getting clobbered by the longjmp() that happens when + * a hard timeout expires... + * + * We begin with routines which deal with the file itself... + */ + +#if defined(USE_OS2_SCOREBOARD) + +/* The next two routines are used to access shared memory under OS/2. */ +/* This requires EMX v09c to be installed. */ + +caddr_t create_shared_heap(const char *name, size_t size) +{ + ULONG rc; + void *mem; + Heap_t h; + + rc = DosAllocSharedMem(&mem, name, size, + PAG_COMMIT | PAG_READ | PAG_WRITE); + if (rc != 0) + return NULL; + h = _ucreate(mem, size, !_BLOCK_CLEAN, _HEAP_REGULAR | _HEAP_SHARED, + NULL, NULL); + if (h == NULL) + DosFreeMem(mem); + return (caddr_t) h; +} + +caddr_t get_shared_heap(const char *Name) +{ + + PVOID BaseAddress; /* Pointer to the base address of + the shared memory object */ + ULONG AttributeFlags; /* Flags describing characteristics + of the shared memory object */ + APIRET rc; /* Return code */ + + /* Request read and write access to */ + /* the shared memory object */ + AttributeFlags = PAG_WRITE | PAG_READ; + + rc = DosGetNamedSharedMem(&BaseAddress, Name, AttributeFlags); + + if (rc != 0) { + printf("DosGetNamedSharedMem error: return code = %ld", rc); + return 0; + } + + return BaseAddress; +} + +static void setup_shared_mem(pool *p) +{ + caddr_t m; + + int rc; + + m = (caddr_t) create_shared_heap("\\SHAREMEM\\SCOREBOARD", SCOREBOARD_SIZE); + if (m == 0) { + fprintf(stderr, "%s: Could not create OS/2 Shared memory pool.\n", + ap_server_argv0); + exit(APEXIT_INIT); + } + + rc = _uopen((Heap_t) m); + if (rc != 0) { + fprintf(stderr, + "%s: Could not uopen() newly created OS/2 Shared memory pool.\n", + ap_server_argv0); + } + ap_scoreboard_image = (scoreboard *) m; + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ + caddr_t m; + int rc; + + m = (caddr_t) get_shared_heap("\\SHAREMEM\\SCOREBOARD"); + if (m == 0) { + fprintf(stderr, "%s: Could not find existing OS/2 Shared memory pool.\n", + ap_server_argv0); + exit(APEXIT_INIT); + } + + rc = _uopen((Heap_t) m); + ap_scoreboard_image = (scoreboard *) m; +} + +#elif defined(USE_POSIX_SCOREBOARD) +#include +/* + * POSIX 1003.4 style + * + * Note 1: + * As of version 4.23A, shared memory in QNX must reside under /dev/shmem, + * where no subdirectories allowed. + * + * POSIX shm_open() and shm_unlink() will take care about this issue, + * but to avoid confusion, I suggest to redefine scoreboard file name + * in httpd.conf to cut "logs/" from it. With default setup actual name + * will be "/dev/shmem/logs.apache_status". + * + * If something went wrong and Apache did not unlinked this object upon + * exit, you can remove it manually, using "rm -f" command. + * + * Note 2: + * in QNX defines MAP_ANON, but current implementation + * does NOT support BSD style anonymous mapping. So, the order of + * conditional compilation is important: + * this #ifdef section must be ABOVE the next one (BSD style). + * + * I tested this stuff and it works fine for me, but if it provides + * trouble for you, just comment out USE_MMAP_SCOREBOARD in QNX section + * of ap_config.h + * + * June 5, 1997, + * Igor N. Kovalenko -- infoh@mail.wplus.net + */ + +static void cleanup_shared_mem(void *d) +{ + shm_unlink(ap_scoreboard_fname); +} + +static void setup_shared_mem(pool *p) +{ + char buf[512]; + caddr_t m; + int fd; + + fd = shm_open(ap_scoreboard_fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + ap_snprintf(buf, sizeof(buf), "%s: could not open(create) scoreboard", + ap_server_argv0); + perror(buf); + exit(APEXIT_INIT); + } + if (ltrunc(fd, (off_t) SCOREBOARD_SIZE, SEEK_SET) == -1) { + ap_snprintf(buf, sizeof(buf), "%s: could not ltrunc scoreboard", + ap_server_argv0); + perror(buf); + shm_unlink(ap_scoreboard_fname); + exit(APEXIT_INIT); + } + if ((m = (caddr_t) mmap((caddr_t) 0, + (size_t) SCOREBOARD_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, (off_t) 0)) == (caddr_t) - 1) { + ap_snprintf(buf, sizeof(buf), "%s: cannot mmap scoreboard", + ap_server_argv0); + perror(buf); + shm_unlink(ap_scoreboard_fname); + exit(APEXIT_INIT); + } + close(fd); + ap_register_cleanup(p, NULL, cleanup_shared_mem, ap_null_cleanup); + ap_scoreboard_image = (scoreboard *) m; + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ +} + +#elif defined(USE_MMAP_SCOREBOARD) + +static void setup_shared_mem(pool *p) +{ + caddr_t m; + +#if defined(MAP_ANON) +/* BSD style */ +#ifdef CONVEXOS11 + /* + * 9-Aug-97 - Jeff Venters (venters@convex.hp.com) + * ConvexOS maps address space as follows: + * 0x00000000 - 0x7fffffff : Kernel + * 0x80000000 - 0xffffffff : User + * Start mmapped area 1GB above start of text. + * + * Also, the length requires a pointer as the actual length is + * returned (rounded up to a page boundary). + */ + { + unsigned len = SCOREBOARD_SIZE; + + m = mmap((caddr_t) 0xC0000000, &len, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, NOFD, 0); + } +#elif defined(MAP_TMPFILE) + { + char mfile[] = "/tmp/apache_shmem_XXXX"; + int fd = mkstemp(mfile); + if (fd == -1) { + perror("open"); + fprintf(stderr, "%s: Could not open %s\n", ap_server_argv0, mfile); + exit(APEXIT_INIT); + } + m = mmap((caddr_t) 0, SCOREBOARD_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m == (caddr_t) - 1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap %s\n", ap_server_argv0, mfile); + exit(APEXIT_INIT); + } + close(fd); + unlink(mfile); + } +#else + m = mmap((caddr_t) 0, SCOREBOARD_SIZE, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); +#endif + if (m == (caddr_t) - 1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0); + exit(APEXIT_INIT); + } +#else +/* Sun style */ + int fd; + + fd = open("/dev/zero", O_RDWR); + if (fd == -1) { + perror("open"); + fprintf(stderr, "%s: Could not open /dev/zero\n", ap_server_argv0); + exit(APEXIT_INIT); + } + m = mmap((caddr_t) 0, SCOREBOARD_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m == (caddr_t) - 1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap /dev/zero\n", ap_server_argv0); + exit(APEXIT_INIT); + } + close(fd); +#endif + ap_scoreboard_image = (scoreboard *) m; + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ +} + +#elif defined(USE_SHMGET_SCOREBOARD) +static key_t shmkey = IPC_PRIVATE; +static int shmid = -1; + +static void setup_shared_mem(pool *p) +{ + struct shmid_ds shmbuf; +#ifdef MOVEBREAK + char *obrk; +#endif + + if ((shmid = shmget(shmkey, SCOREBOARD_SIZE, IPC_CREAT | SHM_R | SHM_W)) == -1) { +#ifdef LINUX + if (errno == ENOSYS) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "Your kernel was built without CONFIG_SYSVIPC\n" + "%s: Please consult the Apache FAQ for details", + ap_server_argv0); + } +#endif + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "could not call shmget"); + exit(APEXIT_INIT); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "created shared memory segment #%d", shmid); + +#ifdef MOVEBREAK + /* + * Some SysV systems place the shared segment WAY too close + * to the dynamic memory break point (sbrk(0)). This severely + * limits the use of malloc/sbrk in the program since sbrk will + * refuse to move past that point. + * + * To get around this, we move the break point "way up there", + * attach the segment and then move break back down. Ugly + */ + if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "sbrk() could not move break"); + } +#endif + +#define BADSHMAT ((scoreboard *)(-1)) + if ((ap_scoreboard_image = (scoreboard *) shmat(shmid, 0, 0)) == BADSHMAT) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, "shmat error"); + /* + * We exit below, after we try to remove the segment + */ + } + else { /* only worry about permissions if we attached the segment */ + if (shmctl(shmid, IPC_STAT, &shmbuf) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "shmctl() could not stat segment #%d", shmid); + } + else { + shmbuf.shm_perm.uid = unixd_config.user_id; + shmbuf.shm_perm.gid = unixd_config.group_id; + if (shmctl(shmid, IPC_SET, &shmbuf) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "shmctl() could not set segment #%d", shmid); + } + } + } + /* + * We must avoid leaving segments in the kernel's + * (small) tables. + */ + if (shmctl(shmid, IPC_RMID, NULL) != 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "shmctl: IPC_RMID: could not remove shared memory segment #%d", + shmid); + } + if (ap_scoreboard_image == BADSHMAT) /* now bailout */ + exit(APEXIT_INIT); + +#ifdef MOVEBREAK + if (obrk == (char *) -1) + return; /* nothing else to do */ + if (sbrk(-(MOVEBREAK)) == (char *) -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "sbrk() could not move break back"); + } +#endif + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ +} + +#elif defined(USE_TPF_SCOREBOARD) + +static void cleanup_scoreboard_heap() +{ + int rv; + rv = rsysc(ap_scoreboard_image, SCOREBOARD_FRAMES, SCOREBOARD_NAME); + if(rv == RSYSC_ERROR) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "rsysc() could not release scoreboard system heap"); + } +} + +static void setup_shared_mem(pool *p) +{ + cinfc(CINFC_WRITE, CINFC_CMMCTK2); + ap_scoreboard_image = (scoreboard *) gsysc(SCOREBOARD_FRAMES, SCOREBOARD_NAME); + + if (!ap_scoreboard_image) { + fprintf(stderr, "httpd: Could not create scoreboard system heap storage.\n"); + exit(APEXIT_INIT); + } + + ap_register_cleanup(p, NULL, cleanup_scoreboard_heap, ap_null_cleanup); + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ + cinfc(CINFC_WRITE, CINFC_CMMCTK2); +} + +#else +#define SCOREBOARD_FILE +static scoreboard _scoreboard_image; +static int scoreboard_fd = -1; + +/* XXX: things are seriously screwed if we ever have to do a partial + * read or write ... we could get a corrupted scoreboard + */ +static int force_write(int fd, void *buffer, int bufsz) +{ + int rv, orig_sz = bufsz; + + do { + rv = write(fd, buffer, bufsz); + if (rv > 0) { + buffer = (char *) buffer + rv; + bufsz -= rv; + } + } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR)); + + return rv < 0 ? rv : orig_sz - bufsz; +} + +static int force_read(int fd, void *buffer, int bufsz) +{ + int rv, orig_sz = bufsz; + + do { + rv = read(fd, buffer, bufsz); + if (rv > 0) { + buffer = (char *) buffer + rv; + bufsz -= rv; + } + } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR)); + + return rv < 0 ? rv : orig_sz - bufsz; +} + +static void cleanup_scoreboard_file(void *foo) +{ + unlink(ap_scoreboard_fname); +} + +void reopen_scoreboard(pool *p) +{ + if (scoreboard_fd != -1) + ap_pclosef(p, scoreboard_fd); + +#ifdef TPF + ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname); +#endif /* TPF */ + scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0666); + if (scoreboard_fd == -1) { + perror(ap_scoreboard_fname); + fprintf(stderr, "Cannot open scoreboard file:\n"); + clean_child_exit(1); + } +} +#endif + +/* Called by parent process */ +static void reinit_scoreboard(pool *p) +{ + int running_gen = 0; + if (ap_scoreboard_image) + running_gen = ap_scoreboard_image->global.running_generation; + +#ifndef SCOREBOARD_FILE + if (ap_scoreboard_image == NULL) { + setup_shared_mem(p); + } + memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); + ap_scoreboard_image->global.running_generation = running_gen; +#else + ap_scoreboard_image = &_scoreboard_image; + ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname); + + scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0644); + if (scoreboard_fd == -1) { + perror(ap_scoreboard_fname); + fprintf(stderr, "Cannot open scoreboard file:\n"); + exit(APEXIT_INIT); + } + ap_register_cleanup(p, NULL, cleanup_scoreboard_file, ap_null_cleanup); + + memset((char *) ap_scoreboard_image, 0, sizeof(*ap_scoreboard_image)); + ap_scoreboard_image->global.running_generation = running_gen; + force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image)); +#endif +} + +/* Routines called to deal with the scoreboard image + * --- note that we do *not* need write locks, since update_child_status + * only updates a *single* record in place, and only one process writes to + * a given scoreboard slot at a time (either the child process owning that + * slot, or the parent, noting that the child has died). + * + * As a final note --- setting the score entry to getpid() is always safe, + * since when the parent is writing an entry, it's only noting SERVER_DEAD + * anyway. + */ + +ap_inline void ap_sync_scoreboard_image(void) +{ +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, 0L, 0); + force_read(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image)); +#endif +} + +API_EXPORT(int) ap_exists_scoreboard_image(void) +{ + return (ap_scoreboard_image ? 1 : 0); +} + +static ap_inline void put_scoreboard_info(int child_num, + short_score *new_score_rec) +{ +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, (long) child_num * sizeof(short_score), 0); + force_write(scoreboard_fd, new_score_rec, sizeof(short_score)); +#endif +} + +int ap_update_child_status(int child_num, int status, request_rec *r) +{ + int old_status; + short_score *ss; + + if (child_num < 0) + return -1; + + ap_check_signals(); + + ap_sync_scoreboard_image(); + ss = &ap_scoreboard_image->servers[child_num]; + old_status = ss->status; + ss->status = status; + + if (ap_extended_status) { + if (status == SERVER_READY || status == SERVER_DEAD) { + /* + * Reset individual counters + */ + if (status == SERVER_DEAD) { + ss->my_access_count = 0L; + ss->my_bytes_served = 0L; + } + ss->conn_count = (unsigned short) 0; + ss->conn_bytes = (unsigned long) 0; + } + if (r) { + conn_rec *c = r->connection; + ap_cpystrn(ss->client, ap_get_remote_host(c, r->per_dir_config, + REMOTE_NOLOOKUP), sizeof(ss->client)); + if (r->the_request == NULL) { + ap_cpystrn(ss->request, "NULL", sizeof(ss->request)); + } else if (r->parsed_uri.password == NULL) { + ap_cpystrn(ss->request, r->the_request, sizeof(ss->request)); + } else { + /* Don't reveal the password in the server-status view */ + ap_cpystrn(ss->request, ap_pstrcat(r->pool, r->method, " ", + ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD), + r->assbackwards ? NULL : " ", r->protocol, NULL), + sizeof(ss->request)); + } + ss->vhostrec = r->server; + } + } + if (status == SERVER_STARTING && r == NULL) { + /* clean up the slot's vhostrec pointer (maybe re-used) + * and mark the slot as belonging to a new generation. + */ + ss->vhostrec = NULL; + ap_scoreboard_image->parent[child_num].generation = ap_my_generation; +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[child_num]), 0); + force_write(scoreboard_fd, &ap_scoreboard_image->parent[child_num], + sizeof(parent_score)); +#endif + } + put_scoreboard_info(child_num, ss); + + return old_status; +} + +static void update_scoreboard_global(void) +{ +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, + (char *) &ap_scoreboard_image->global -(char *) ap_scoreboard_image, 0); + force_write(scoreboard_fd, &ap_scoreboard_image->global, + sizeof ap_scoreboard_image->global); +#endif +} + +void ap_time_process_request(int child_num, int status) +{ + short_score *ss; +#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES) + struct tms tms_blk; +#endif + + if (child_num < 0) + return; + + ap_sync_scoreboard_image(); + ss = &ap_scoreboard_image->servers[child_num]; + + if (status == START_PREQUEST) { +#if defined(NO_GETTIMEOFDAY) +#ifndef NO_TIMES + if ((ss->start_time = times(&tms_blk)) == -1) +#endif /* NO_TIMES */ + ss->start_time = (clock_t) 0; +#else + if (gettimeofday(&ss->start_time, (struct timezone *) 0) < 0) + ss->start_time.tv_sec = + ss->start_time.tv_usec = 0L; +#endif + } + else if (status == STOP_PREQUEST) { +#if defined(NO_GETTIMEOFDAY) +#ifndef NO_TIMES + if ((ss->stop_time = times(&tms_blk)) == -1) +#endif + ss->stop_time = ss->start_time = (clock_t) 0; +#else + if (gettimeofday(&ss->stop_time, (struct timezone *) 0) < 0) + ss->stop_time.tv_sec = + ss->stop_time.tv_usec = + ss->start_time.tv_sec = + ss->start_time.tv_usec = 0L; +#endif + + } + + put_scoreboard_info(child_num, ss); +} + +static void increment_counts(int child_num, request_rec *r) +{ + long int bs = 0; + short_score *ss; + + ap_sync_scoreboard_image(); + ss = &ap_scoreboard_image->servers[child_num]; + + if (r->sent_bodyct) + ap_bgetopt(r->connection->client, BO_BYTECT, &bs); + +#ifndef NO_TIMES + times(&ss->times); +#endif + ss->access_count++; + ss->my_access_count++; + ss->conn_count++; + ss->bytes_served += (unsigned long) bs; + ss->my_bytes_served += (unsigned long) bs; + ss->conn_bytes += (unsigned long) bs; + + put_scoreboard_info(child_num, ss); +} + +static int find_child_by_pid(int pid) +{ + int i; + + for (i = 0; i < max_daemons_limit; ++i) + if (ap_scoreboard_image->parent[i].pid == pid) + return i; + + return -1; +} + +static void reclaim_child_processes(int terminate) +{ +#ifndef MULTITHREAD + int i, status; + long int waittime = 1024 * 16; /* in usecs */ + struct timeval tv; + int waitret, tries; + int not_dead_yet; +#ifdef HAS_OTHER_CHILD + other_child_rec *ocr, *nocr; +#endif + + ap_sync_scoreboard_image(); + + for (tries = terminate ? 4 : 1; tries <= 9; ++tries) { + /* don't want to hold up progress any more than + * necessary, but we need to allow children a few moments to exit. + * Set delay with an exponential backoff. + */ + tv.tv_sec = waittime / 1000000; + tv.tv_usec = waittime % 1000000; + waittime = waittime * 4; + ap_select(0, NULL, NULL, NULL, &tv); + + /* now see who is done */ + not_dead_yet = 0; + for (i = 0; i < max_daemons_limit; ++i) { + int pid = ap_scoreboard_image->parent[i].pid; + + if (pid == my_pid || pid == 0) + continue; + + waitret = waitpid(pid, &status, WNOHANG); + if (waitret == pid || waitret == -1) { + ap_scoreboard_image->parent[i].pid = 0; + continue; + } + ++not_dead_yet; + switch (tries) { + case 1: /* 16ms */ + case 2: /* 82ms */ + break; + case 3: /* 344ms */ + /* perhaps it missed the SIGHUP, lets try again */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, + server_conf, + "child process %d did not exit, sending another SIGHUP", + pid); + kill(pid, SIGHUP); + waittime = 1024 * 16; + break; + case 4: /* 16ms */ + case 5: /* 82ms */ + case 6: /* 344ms */ + break; + case 7: /* 1.4sec */ + /* ok, now it's being annoying */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, + server_conf, + "child process %d still did not exit, sending a SIGTERM", + pid); + kill(pid, SIGTERM); + break; + case 8: /* 6 sec */ + /* die child scum */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "child process %d still did not exit, sending a SIGKILL", + pid); + kill(pid, SIGKILL); + break; + case 9: /* 14 sec */ + /* gave it our best shot, but alas... If this really + * is a child we are trying to kill and it really hasn't + * exited, we will likely fail to bind to the port + * after the restart. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "could not make child process %d exit, " + "attempting to continue anyway", pid); + break; + } + } +#ifdef HAS_OTHER_CHILD + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->pid == -1) + continue; + + waitret = waitpid(ocr->pid, &status, WNOHANG); + if (waitret == ocr->pid) { + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status); + } + else if (waitret == 0) { + (*ocr->maintenance) (OC_REASON_RESTART, ocr->data, -1); + ++not_dead_yet; + } + else if (waitret == -1) { + /* uh what the heck? they didn't call unregister? */ + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_LOST, ocr->data, -1); + } + } +#endif + if (!not_dead_yet) { + /* nothing left to wait for */ + break; + } + } +#endif /* ndef MULTITHREAD */ +} + + +#if defined(NEED_WAITPID) +/* + Systems without a real waitpid sometimes lose a child's exit while waiting + for another. Search through the scoreboard for missing children. + */ +int reap_children(ap_wait_t *status) +{ + int n, pid; + + for (n = 0; n < max_daemons_limit; ++n) { + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->servers[n].status != SERVER_DEAD && + kill((pid = ap_scoreboard_image->parent[n].pid), 0) == -1) { + ap_update_child_status(n, SERVER_DEAD, NULL); + /* just mark it as having a successful exit status */ + bzero((char *) status, sizeof(ap_wait_t)); + return(pid); + } + } + return 0; +} +#endif + +/* Finally, this routine is used by the caretaker process to wait for + * a while... + */ + +/* number of calls to wait_or_timeout between writable probes */ +#ifndef INTERVAL_OF_WRITABLE_PROBES +#define INTERVAL_OF_WRITABLE_PROBES 10 +#endif +static int wait_or_timeout_counter; + +static int wait_or_timeout(ap_wait_t *status) +{ + struct timeval tv; + int ret; + + ++wait_or_timeout_counter; + if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) { + wait_or_timeout_counter = 0; +#ifdef HAS_OTHER_CHILD + probe_writable_fds(); +#endif + } + ret = waitpid(-1, status, WNOHANG); + if (ret == -1 && errno == EINTR) { + return -1; + } + if (ret > 0) { + return ret; + } +#ifdef NEED_WAITPID + if ((ret = reap_children(status)) > 0) { + return ret; + } +#endif + tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000; + tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000; + ap_select(0, NULL, NULL, NULL, &tv); + return -1; +} + + +#if defined(NSIG) +#define NumSIG NSIG +#elif defined(_NSIG) +#define NumSIG _NSIG +#elif defined(__NSIG) +#define NumSIG __NSIG +#else +#define NumSIG 32 /* for 1998's unixes, this is still a good assumption */ +#endif + +#ifdef SYS_SIGLIST /* platform has sys_siglist[] */ +#define INIT_SIGLIST() /*nothing*/ +#else /* platform has no sys_siglist[], define our own */ +#define SYS_SIGLIST ap_sys_siglist +#define INIT_SIGLIST() siglist_init(); + +const char *ap_sys_siglist[NumSIG]; + +static void siglist_init(void) +{ + int sig; + + ap_sys_siglist[0] = "Signal 0"; +#ifdef SIGHUP + ap_sys_siglist[SIGHUP] = "Hangup"; +#endif +#ifdef SIGINT + ap_sys_siglist[SIGINT] = "Interrupt"; +#endif +#ifdef SIGQUIT + ap_sys_siglist[SIGQUIT] = "Quit"; +#endif +#ifdef SIGILL + ap_sys_siglist[SIGILL] = "Illegal instruction"; +#endif +#ifdef SIGTRAP + ap_sys_siglist[SIGTRAP] = "Trace/BPT trap"; +#endif +#ifdef SIGIOT + ap_sys_siglist[SIGIOT] = "IOT instruction"; +#endif +#ifdef SIGABRT + ap_sys_siglist[SIGABRT] = "Abort"; +#endif +#ifdef SIGEMT + ap_sys_siglist[SIGEMT] = "Emulator trap"; +#endif +#ifdef SIGFPE + ap_sys_siglist[SIGFPE] = "Arithmetic exception"; +#endif +#ifdef SIGKILL + ap_sys_siglist[SIGKILL] = "Killed"; +#endif +#ifdef SIGBUS + ap_sys_siglist[SIGBUS] = "Bus error"; +#endif +#ifdef SIGSEGV + ap_sys_siglist[SIGSEGV] = "Segmentation fault"; +#endif +#ifdef SIGSYS + ap_sys_siglist[SIGSYS] = "Bad system call"; +#endif +#ifdef SIGPIPE + ap_sys_siglist[SIGPIPE] = "Broken pipe"; +#endif +#ifdef SIGALRM + ap_sys_siglist[SIGALRM] = "Alarm clock"; +#endif +#ifdef SIGTERM + ap_sys_siglist[SIGTERM] = "Terminated"; +#endif +#ifdef SIGUSR1 + ap_sys_siglist[SIGUSR1] = "User defined signal 1"; +#endif +#ifdef SIGUSR2 + ap_sys_siglist[SIGUSR2] = "User defined signal 2"; +#endif +#ifdef SIGCLD + ap_sys_siglist[SIGCLD] = "Child status change"; +#endif +#ifdef SIGCHLD + ap_sys_siglist[SIGCHLD] = "Child status change"; +#endif +#ifdef SIGPWR + ap_sys_siglist[SIGPWR] = "Power-fail restart"; +#endif +#ifdef SIGWINCH + ap_sys_siglist[SIGWINCH] = "Window changed"; +#endif +#ifdef SIGURG + ap_sys_siglist[SIGURG] = "urgent socket condition"; +#endif +#ifdef SIGPOLL + ap_sys_siglist[SIGPOLL] = "Pollable event occurred"; +#endif +#ifdef SIGIO + ap_sys_siglist[SIGIO] = "socket I/O possible"; +#endif +#ifdef SIGSTOP + ap_sys_siglist[SIGSTOP] = "Stopped (signal)"; +#endif +#ifdef SIGTSTP + ap_sys_siglist[SIGTSTP] = "Stopped"; +#endif +#ifdef SIGCONT + ap_sys_siglist[SIGCONT] = "Continued"; +#endif +#ifdef SIGTTIN + ap_sys_siglist[SIGTTIN] = "Stopped (tty input)"; +#endif +#ifdef SIGTTOU + ap_sys_siglist[SIGTTOU] = "Stopped (tty output)"; +#endif +#ifdef SIGVTALRM + ap_sys_siglist[SIGVTALRM] = "virtual timer expired"; +#endif +#ifdef SIGPROF + ap_sys_siglist[SIGPROF] = "profiling timer expired"; +#endif +#ifdef SIGXCPU + ap_sys_siglist[SIGXCPU] = "exceeded cpu limit"; +#endif +#ifdef SIGXFSZ + ap_sys_siglist[SIGXFSZ] = "exceeded file size limit"; +#endif + for (sig=0; sig < sizeof(ap_sys_siglist)/sizeof(ap_sys_siglist[0]); ++sig) + if (ap_sys_siglist[sig] == NULL) + ap_sys_siglist[sig] = ""; +} +#endif /* platform has sys_siglist[] */ + + +/* handle all varieties of core dumping signals */ +static void sig_coredump(int sig) +{ + chdir(ap_coredump_dir); + signal(sig, SIG_DFL); + kill(getpid(), sig); + /* At this point we've got sig blocked, because we're still inside + * the signal handler. When we leave the signal handler it will + * be unblocked, and we'll take the signal... and coredump or whatever + * is appropriate for this particular Unix. In addition the parent + * will see the real signal we received -- whereas if we called + * abort() here, the parent would only see SIGABRT. + */ +} + +/***************************************************************** + * Connection structures and accounting... + */ + +static void just_die(int sig) +{ + clean_child_exit(0); +} + +static int volatile deferred_die; +static int volatile usr1_just_die; + +static void usr1_handler(int sig) +{ + if (usr1_just_die) { + just_die(sig); + } + deferred_die = 1; +} + +/* volatile just in case */ +static int volatile shutdown_pending; +static int volatile restart_pending; +static int volatile is_graceful; +ap_generation_t volatile ap_my_generation=0; + +static void sig_term(int sig) +{ + if (shutdown_pending == 1) { + /* Um, is this _probably_ not an error, if the user has + * tried to do a shutdown twice quickly, so we won't + * worry about reporting it. + */ + return; + } + shutdown_pending = 1; +} + +static void restart(int sig) +{ + if (restart_pending == 1) { + /* Probably not an error - don't bother reporting it */ + return; + } + restart_pending = 1; + is_graceful = sig == SIGUSR1; +} + +static void set_signals(void) +{ +#ifndef NO_USE_SIGACTION + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + if (!one_process) { + sa.sa_handler = sig_coredump; +#if defined(SA_ONESHOT) + sa.sa_flags = SA_ONESHOT; +#elif defined(SA_RESETHAND) + sa.sa_flags = SA_RESETHAND; +#endif + if (sigaction(SIGSEGV, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGSEGV)"); +#ifdef SIGBUS + if (sigaction(SIGBUS, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGBUS)"); +#endif +#ifdef SIGABORT + if (sigaction(SIGABORT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABORT)"); +#endif +#ifdef SIGABRT + if (sigaction(SIGABRT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABRT)"); +#endif +#ifdef SIGILL + if (sigaction(SIGILL, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGILL)"); +#endif + sa.sa_flags = 0; + } + sa.sa_handler = sig_term; + if (sigaction(SIGTERM, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGTERM)"); +#ifdef SIGINT + if (sigaction(SIGINT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGINT)"); +#endif +#ifdef SIGXCPU + sa.sa_handler = SIG_DFL; + if (sigaction(SIGXCPU, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXCPU)"); +#endif +#ifdef SIGXFSZ + sa.sa_handler = SIG_DFL; + if (sigaction(SIGXFSZ, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXFSZ)"); +#endif +#ifdef SIGPIPE + sa.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGPIPE)"); +#endif + + /* we want to ignore HUPs and USR1 while we're busy processing one */ + sigaddset(&sa.sa_mask, SIGHUP); + sigaddset(&sa.sa_mask, SIGUSR1); + sa.sa_handler = restart; + if (sigaction(SIGHUP, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGHUP)"); + if (sigaction(SIGUSR1, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGUSR1)"); +#else + if (!one_process) { + signal(SIGSEGV, sig_coredump); +#ifdef SIGBUS + signal(SIGBUS, sig_coredump); +#endif /* SIGBUS */ +#ifdef SIGABORT + signal(SIGABORT, sig_coredump); +#endif /* SIGABORT */ +#ifdef SIGABRT + signal(SIGABRT, sig_coredump); +#endif /* SIGABRT */ +#ifdef SIGILL + signal(SIGILL, sig_coredump); +#endif /* SIGILL */ +#ifdef SIGXCPU + signal(SIGXCPU, SIG_DFL); +#endif /* SIGXCPU */ +#ifdef SIGXFSZ + signal(SIGXFSZ, SIG_DFL); +#endif /* SIGXFSZ */ + } + + signal(SIGTERM, sig_term); +#ifdef SIGHUP + signal(SIGHUP, restart); +#endif /* SIGHUP */ +#ifdef SIGUSR1 + signal(SIGUSR1, restart); +#endif /* SIGUSR1 */ +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif /* SIGPIPE */ + +#endif +} + +/***************************************************************** + * Connection structures and accounting... + */ + + +static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout, + const struct sockaddr_in *remaddr, + const struct sockaddr_in *saddr, + int child_num) +{ + conn_rec *conn = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec)); + + /* Got a connection structure, so initialize what fields we can + * (the rest are zeroed out by pcalloc). + */ + + conn->child_num = child_num; + + conn->pool = p; + conn->local_addr = *saddr; + conn->base_server = server; + conn->client = inout; + + conn->remote_addr = *remaddr; + conn->remote_ip = ap_pstrdup(conn->pool, + inet_ntoa(conn->remote_addr.sin_addr)); + + return conn; +} + +#if defined(TCP_NODELAY) && !defined(MPE) && !defined(TPF) +static void sock_disable_nagle(int s) +{ + /* The Nagle algorithm says that we should delay sending partial + * packets in hopes of getting more data. We don't want to do + * this; we are not telnet. There are bad interactions between + * persistent connections and Nagle's algorithm that have very severe + * performance penalties. (Failing to disable Nagle is not much of a + * problem with simple HTTP.) + * + * In spite of these problems, failure here is not a shooting offense. + */ + int just_say_no = 1; + + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no, + sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "setsockopt: (TCP_NODELAY)"); + } +} + +#else +#define sock_disable_nagle(s) /* NOOP */ +#endif + + +static int make_sock(pool *p, const struct sockaddr_in *server) +{ + int s; + int one = 1; + char addr[512]; + + if (server->sin_addr.s_addr != htonl(INADDR_ANY)) + ap_snprintf(addr, sizeof(addr), "address %s port %d", + inet_ntoa(server->sin_addr), ntohs(server->sin_port)); + else + ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port)); + + /* note that because we're about to slack we don't use psocket */ + if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: failed to get a socket for %s", addr); + exit(1); + } + + /* Solaris (probably versions 2.4, 2.5, and 2.5.1 with various levels + * of tcp patches) has some really weird bugs where if you dup the + * socket now it breaks things across SIGHUP restarts. It'll either + * be unable to bind, or it won't respond. + */ +#if defined (SOLARIS2) && SOLARIS2 < 260 +#define WORKAROUND_SOLARIS_BUG +#endif + + /* PR#1282 Unixware 1.x appears to have the same problem as solaris */ +#if defined (UW) && UW < 200 +#define WORKAROUND_SOLARIS_BUG +#endif + + /* PR#1973 NCR SVR4 systems appear to have the same problem */ +#if defined (MPRAS) +#define WORKAROUND_SOLARIS_BUG +#endif + +#ifndef WORKAROUND_SOLARIS_BUG + s = ap_slack(s, AP_SLACK_HIGH); + + ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ +#ifdef TPF + os_note_additional_cleanups(p, s); +#endif /* TPF */ +#endif + +#ifndef MPE +/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */ +#ifndef _OSD_POSIX + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr); + close(s); + return -1; + } +#endif /*_OSD_POSIX*/ + one = 1; +#ifdef SO_KEEPALIVE + if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr); + close(s); + return -1; + } +#endif +#endif + + sock_disable_nagle(s); + + /* + * To send data over high bandwidth-delay connections at full + * speed we must force the TCP window to open wide enough to keep the + * pipe full. The default window size on many systems + * is only 4kB. Cross-country WAN connections of 100ms + * at 1Mb/s are not impossible for well connected sites. + * If we assume 100ms cross-country latency, + * a 4kB buffer limits throughput to 40kB/s. + * + * To avoid this problem I've added the SendBufferSize directive + * to allow the web master to configure send buffer size. + * + * The trade-off of larger buffers is that more kernel memory + * is consumed. YMMV, know your customers and your network! + * + * -John Heidemann 25-Oct-96 + * + * If no size is specified, use the kernel default. + */ +#ifndef BEOS /* BeOS does not support SO_SNDBUF */ + if (server_conf->send_buffer_size) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, + (char *) &server_conf->send_buffer_size, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "make_sock: failed to set SendBufferSize for %s, " + "using default", addr); + /* not a fatal error */ + } + } +#endif + +#ifdef MPE +/* MPE requires CAP=PM and GETPRIVMODE to bind to ports less than 1024 */ + if (ntohs(server->sin_port) < 1024) + GETPRIVMODE(); +#endif + if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: could not bind to %s", addr); +#ifdef MPE + if (ntohs(server->sin_port) < 1024) + GETUSERMODE(); +#endif + close(s); + exit(1); + } +#ifdef MPE + if (ntohs(server->sin_port) < 1024) + GETUSERMODE(); +#endif + + if (listen(s, ap_listenbacklog) == -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "make_sock: unable to listen for connections on %s", addr); + close(s); + exit(1); + } + +#ifdef WORKAROUND_SOLARIS_BUG + s = ap_slack(s, AP_SLACK_HIGH); + + ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ +#endif + +#ifdef CHECK_FD_SETSIZE + /* protect various fd_sets */ + if (s >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "make_sock: problem listening on %s, filedescriptor (%u) " + "larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", addr, s, FD_SETSIZE); + close(s); + return -1; + } +#endif + + return s; +} + + +/* + * During a restart we keep track of the old listeners here, so that we + * can re-use the sockets. We have to do this because we won't be able + * to re-open the sockets ("Address already in use"). + * + * Unlike the listeners ring, old_listeners is a NULL terminated list. + * + * copy_listeners() makes the copy, find_listener() finds an old listener + * and close_unused_listener() cleans up whatever wasn't used. + */ +static listen_rec *old_listeners; + +/* unfortunately copy_listeners may be called before listeners is a ring */ +static void copy_listeners(pool *p) +{ + listen_rec *lr; + + ap_assert(old_listeners == NULL); + if (ap_listeners == NULL) { + return; + } + lr = ap_listeners; + do { + listen_rec *nr = malloc(sizeof *nr); + if (nr == NULL) { + fprintf(stderr, "Ouch! malloc failed in copy_listeners()\n"); + exit(1); + } + *nr = *lr; + ap_kill_cleanups_for_socket(p, nr->fd); + nr->next = old_listeners; + ap_assert(!nr->used); + old_listeners = nr; + lr = lr->next; + } while (lr && lr != ap_listeners); +} + + +static int find_listener(listen_rec *lr) +{ + listen_rec *or; + + for (or = old_listeners; or; or = or->next) { + if (!memcmp(&or->local_addr, &lr->local_addr, sizeof(or->local_addr))) { + or->used = 1; + return or->fd; + } + } + return -1; +} + + +static void close_unused_listeners(void) +{ + listen_rec *or, *next; + + for (or = old_listeners; or; or = next) { + next = or->next; + if (!or->used) + closesocket(or->fd); + free(or); + } + old_listeners = NULL; +} + + +/* open sockets, and turn the listeners list into a singly linked ring */ +static void setup_listeners(pool *p) +{ + listen_rec *lr; + int fd; + + listenmaxfd = -1; + FD_ZERO(&listenfds); + lr = ap_listeners; + for (;;) { + fd = find_listener(lr); + if (fd < 0) { + fd = make_sock(p, &lr->local_addr); + } + else { + ap_note_cleanups_for_socket(p, fd); + } + if (fd >= 0) { + FD_SET(fd, &listenfds); + if (fd > listenmaxfd) + listenmaxfd = fd; + } + lr->fd = fd; + if (lr->next == NULL) + break; + lr = lr->next; + } + /* turn the list into a ring */ + lr->next = ap_listeners; + head_listener = ap_listeners; + close_unused_listeners(); + +#ifdef NO_SERIALIZED_ACCEPT + /* warn them about the starvation problem if they're using multiple + * sockets + */ + if (ap_listeners->next != ap_listeners) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, NULL, + "You cannot use multiple Listens safely on your system, " + "proceeding anyway. See src/PORTING, search for " + "SERIALIZED_ACCEPT."); + } +#endif +} + + +/* + * Find a listener which is ready for accept(). This advances the + * head_listener global. + */ +static ap_inline listen_rec *find_ready_listener(fd_set * main_fds) +{ + listen_rec *lr; + + lr = head_listener; + do { + if (FD_ISSET(lr->fd, main_fds)) { + head_listener = lr->next; + return (lr); + } + lr = lr->next; + } while (lr != head_listener); + return NULL; +} + + +/***************************************************************** + * Child process main loop. + * The following vars are static to avoid getting clobbered by longjmp(); + * they are really private to child_main. + */ + +static int srv; +static int csd; +static int requests_this_child; +static fd_set main_fds; + +API_EXPORT(void) ap_child_terminate(request_rec *r) +{ + r->connection->keepalive = 0; + requests_this_child = ap_max_requests_per_child = 1; +} + +int ap_graceful_stop_signalled(void) +{ + ap_sync_scoreboard_image(); + if (deferred_die || + ap_scoreboard_image->global.running_generation != ap_my_generation) { + return 1; + } + return 0; +} + +static void child_main(int child_num_arg) +{ + NET_SIZE_T clen; + struct sockaddr sa_server; + struct sockaddr sa_client; + listen_rec *lr; + pool *ptrans; + conn_rec *current_conn; + + my_pid = getpid(); + csd = -1; + my_child_num = child_num_arg; + requests_this_child = 0; + + /* Get a sub pool for global allocations in this child, so that + * we can have cleanups occur when the child exits. + */ + pchild = ap_make_sub_pool(pconf); + + ptrans = ap_make_sub_pool(pchild); + + /* needs to be done before we switch UIDs so we have permissions */ + reopen_scoreboard(pchild); + SAFE_ACCEPT(accept_mutex_child_init(pchild)); + + if (unixd_setup_child()) { + clean_child_exit(APEXIT_CHILDFATAL); + } + + ap_child_init_hook(pchild, server_conf); + + (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL); + + signal(SIGHUP, just_die); + signal(SIGTERM, just_die); + +#ifdef OS2 +/* Stop Ctrl-C/Ctrl-Break signals going to child processes */ + { + unsigned long ulTimes; + DosSetSignalExceptionFocus(0, &ulTimes); + } +#endif + + while (!ap_graceful_stop_signalled()) { + BUFF *conn_io; + + /* Prepare to receive a SIGUSR1 due to graceful restart so that + * we can exit cleanly. + */ + usr1_just_die = 1; + signal(SIGUSR1, usr1_handler); + + /* + * (Re)initialize this child to a pre-connection state. + */ + + current_conn = NULL; + + ap_clear_pool(ptrans); + + if ((ap_max_requests_per_child > 0 + && requests_this_child++ >= ap_max_requests_per_child)) { + clean_child_exit(0); + } + + (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL); + + /* + * Wait for an acceptable connection to arrive. + */ + + /* Lock around "accept", if necessary */ + SAFE_ACCEPT(accept_mutex_on()); + + for (;;) { + if (ap_listeners->next != ap_listeners) { + /* more than one socket */ + memcpy(&main_fds, &listenfds, sizeof(fd_set)); + srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, NULL); + + if (srv < 0 && errno != EINTR) { + /* Single Unix documents select as returning errnos + * EBADF, EINTR, and EINVAL... and in none of those + * cases does it make sense to continue. In fact + * on Linux 2.0.x we seem to end up with EFAULT + * occasionally, and we'd loop forever due to it. + */ + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)"); + clean_child_exit(1); + } + + if (srv <= 0) + continue; + + lr = find_ready_listener(&main_fds); + if (lr == NULL) + continue; + sd = lr->fd; + } + else { + /* only one socket, just pretend we did the other stuff */ + sd = ap_listeners->fd; + } + + /* if we accept() something we don't want to die, so we have to + * defer the exit + */ + usr1_just_die = 0; + for (;;) { + if (deferred_die) { + /* we didn't get a socket, and we were told to die */ + clean_child_exit(0); + } + clen = sizeof(sa_client); + csd = ap_accept(sd, &sa_client, &clen); + if (csd >= 0 || errno != EINTR) + break; + } + + if (csd >= 0) + break; /* We have a socket ready for reading */ + else { + + /* Our old behaviour here was to continue after accept() + * errors. But this leads us into lots of troubles + * because most of the errors are quite fatal. For + * example, EMFILE can be caused by slow descriptor + * leaks (say in a 3rd party module, or libc). It's + * foolish for us to continue after an EMFILE. We also + * seem to tickle kernel bugs on some platforms which + * lead to never-ending loops here. So it seems best + * to just exit in most cases. + */ + switch (errno) { +#ifdef EPROTO + /* EPROTO on certain older kernels really means + * ECONNABORTED, so we need to ignore it for them. + * See discussion in new-httpd archives nh.9701 + * search for EPROTO. + * + * Also see nh.9603, search for EPROTO: + * There is potentially a bug in Solaris 2.x x<6, + * and other boxes that implement tcp sockets in + * userland (i.e. on top of STREAMS). On these + * systems, EPROTO can actually result in a fatal + * loop. See PR#981 for example. It's hard to + * handle both uses of EPROTO. + */ + case EPROTO: +#endif +#ifdef ECONNABORTED + case ECONNABORTED: +#endif + /* Linux generates the rest of these, other tcp + * stacks (i.e. bsd) tend to hide them behind + * getsockopt() interfaces. They occur when + * the net goes sour or the client disconnects + * after the three-way handshake has been done + * in the kernel but before userland has picked + * up the socket. + */ +#ifdef ECONNRESET + case ECONNRESET: +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: +#endif +#ifdef ENETUNREACH + case ENETUNREACH: +#endif + break; +#ifdef TPF + case EINACT: + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "offload device inactive"); + clean_child_exit(APEXIT_CHILDFATAL); + break; + default: + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "select/accept error (%u)", errno); + clean_child_exit(APEXIT_CHILDFATAL); +#else + default: + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "accept: (client socket)"); + clean_child_exit(1); +#endif + } + } + + if (ap_graceful_stop_signalled()) { + clean_child_exit(0); + } + usr1_just_die = 1; + } + + SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ + +#ifdef TPF + if (csd == 0) /* 0 is invalid socket for TPF */ + continue; +#endif + + /* We've got a socket, let's at least process one request off the + * socket before we accept a graceful restart request. We set + * the signal to ignore because we don't want to disturb any + * third party code. + */ + signal(SIGUSR1, SIG_IGN); + + ap_note_cleanups_for_fd(ptrans, csd); + + /* protect various fd_sets */ +#ifdef CHECK_FD_SETSIZE + if (csd >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "[csd] filedescriptor (%u) larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", csd, FD_SETSIZE); + continue; + } +#endif + + /* + * We now have a connection, so set it up with the appropriate + * socket options, file descriptors, and read/write buffers. + */ + + clen = sizeof(sa_server); + if (getsockname(csd, &sa_server, &clen) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "getsockname"); + continue; + } + + sock_disable_nagle(csd); + + (void) ap_update_child_status(my_child_num, SERVER_BUSY_READ, + (request_rec *) NULL); + + conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET); + +#ifdef B_SFIO + (void) sfdisc(conn_io->sf_in, SF_POPDISC); + sfdisc(conn_io->sf_in, bsfio_new(conn_io->pool, conn_io)); + sfsetbuf(conn_io->sf_in, NULL, 0); + + (void) sfdisc(conn_io->sf_out, SF_POPDISC); + sfdisc(conn_io->sf_out, bsfio_new(conn_io->pool, conn_io)); + sfsetbuf(conn_io->sf_out, NULL, 0); +#endif + + ap_bpushfd(conn_io, csd); + + current_conn = new_connection(ptrans, server_conf, conn_io, + (struct sockaddr_in *) &sa_client, + (struct sockaddr_in *) &sa_server, + my_child_num); + + ap_process_connection(current_conn); + } +} + +#ifdef TPF +static void reset_tpf_listeners(APACHE_TPF_INPUT *input_parms) +{ + int count; + listen_rec *lr; + + count = 0; + listenmaxfd = -1; + FD_ZERO(&listenfds); + lr = ap_listeners; + + for(;;) { + lr->fd = input_parms->listeners[count]; + if(lr->fd >= 0) { + FD_SET(lr->fd, &listenfds); + if(lr->fd > listenmaxfd) + listenmaxfd = lr->fd; + } + if(lr->next == NULL) + break; + lr = lr->next; + count++; + } + lr->next = ap_listeners; + head_listener = ap_listeners; + close_unused_listeners(); +} + +#endif /* TPF */ + +static int make_child(server_rec *s, int slot, time_t now) +{ + int pid; + + if (slot + 1 > max_daemons_limit) { + max_daemons_limit = slot + 1; + } + + if (one_process) { + signal(SIGHUP, just_die); + signal(SIGINT, just_die); +#ifdef SIGQUIT + signal(SIGQUIT, SIG_DFL); +#endif + signal(SIGTERM, just_die); + child_main(slot); + } + + /* avoid starvation */ + head_listener = head_listener->next; + + (void) ap_update_child_status(slot, SERVER_STARTING, (request_rec *) NULL); + + +#ifdef _OSD_POSIX + /* BS2000 requires a "special" version of fork() before a setuid() call */ + if ((pid = os_fork(unixd_config.user_name)) == -1) { +#elif defined(TPF) + if ((pid = os_fork(s, slot)) == -1) { +#else + if ((pid = fork()) == -1) { +#endif + ap_log_error(APLOG_MARK, APLOG_ERR, s, "fork: Unable to fork new process"); + + /* fork didn't succeed. Fix the scoreboard or else + * it will say SERVER_STARTING forever and ever + */ + (void) ap_update_child_status(slot, SERVER_DEAD, (request_rec *) NULL); + + /* In case system resources are maxxed out, we don't want + Apache running away with the CPU trying to fork over and + over and over again. */ + sleep(10); + + return -1; + } + + if (!pid) { +#ifdef AIX_BIND_PROCESSOR +/* by default AIX binds to a single processor + * this bit unbinds children which will then bind to another cpu + */ +#include + int status = bindprocessor(BINDPROCESS, (int)getpid(), + PROCESSOR_CLASS_ANY); + if (status != OK) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf, + "processor unbind failed %d", status); + } +#endif + RAISE_SIGSTOP(MAKE_CHILD); + /* Disable the restart signal handlers and enable the just_die stuff. + * Note that since restart() just notes that a restart has been + * requested there's no race condition here. + */ + signal(SIGHUP, just_die); + signal(SIGUSR1, just_die); + signal(SIGTERM, just_die); + child_main(slot); + } + + ap_scoreboard_image->parent[slot].pid = pid; +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[slot]), 0); + force_write(scoreboard_fd, &ap_scoreboard_image->parent[slot], + sizeof(parent_score)); +#endif + + return 0; +} + + +/* start up a bunch of children */ +static void startup_children(int number_to_start) +{ + int i; + time_t now = time(0); + + for (i = 0; number_to_start && i < ap_daemons_limit; ++i) { + if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) { + continue; + } + if (make_child(server_conf, i, now) < 0) { + break; + } + --number_to_start; + } +} + + +/* + * idle_spawn_rate is the number of children that will be spawned on the + * next maintenance cycle if there aren't enough idle servers. It is + * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by + * without the need to spawn. + */ +static int idle_spawn_rate = 1; +#ifndef MAX_SPAWN_RATE +#define MAX_SPAWN_RATE (32) +#endif +static int hold_off_on_exponential_spawning; + +static void perform_idle_server_maintenance(void) +{ + int i; + int to_kill; + int idle_count; + short_score *ss; + time_t now = time(0); + int free_length; + int free_slots[MAX_SPAWN_RATE]; + int last_non_dead; + int total_non_dead; + + /* initialize the free_list */ + free_length = 0; + + to_kill = -1; + idle_count = 0; + last_non_dead = -1; + total_non_dead = 0; + + ap_sync_scoreboard_image(); + for (i = 0; i < ap_daemons_limit; ++i) { + int status; + + if (i >= max_daemons_limit && free_length == idle_spawn_rate) + break; + ss = &ap_scoreboard_image->servers[i]; + status = ss->status; + if (status == SERVER_DEAD) { + /* try to keep children numbers as low as possible */ + if (free_length < idle_spawn_rate) { + free_slots[free_length] = i; + ++free_length; + } + } + else { + /* We consider a starting server as idle because we started it + * at least a cycle ago, and if it still hasn't finished starting + * then we're just going to swamp things worse by forking more. + * So we hopefully won't need to fork more if we count it. + * This depends on the ordering of SERVER_READY and SERVER_STARTING. + */ + if (status <= SERVER_READY) { + ++ idle_count; + /* always kill the highest numbered child if we have to... + * no really well thought out reason ... other than observing + * the server behaviour under linux where lower numbered children + * tend to service more hits (and hence are more likely to have + * their data in cpu caches). + */ + to_kill = i; + } + + ++total_non_dead; + last_non_dead = i; + } + } + max_daemons_limit = last_non_dead + 1; + if (idle_count > ap_daemons_max_free) { + /* kill off one child... we use SIGUSR1 because that'll cause it to + * shut down gracefully, in case it happened to pick up a request + * while we were counting + */ + kill(ap_scoreboard_image->parent[to_kill].pid, SIGUSR1); + idle_spawn_rate = 1; + } + else if (idle_count < ap_daemons_min_free) { + /* terminate the free list */ + if (free_length == 0) { + /* only report this condition once */ + static int reported = 0; + + if (!reported) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "server reached MaxClients setting, consider" + " raising the MaxClients setting"); + reported = 1; + } + idle_spawn_rate = 1; + } + else { + if (idle_spawn_rate >= 8) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "server seems busy, (you may need " + "to increase StartServers, or Min/MaxSpareServers), " + "spawning %d children, there are %d idle, and " + "%d total children", idle_spawn_rate, + idle_count, total_non_dead); + } + for (i = 0; i < free_length; ++i) { +#ifdef TPF + if(make_child(server_conf, free_slots[i], now) == -1) { + if(free_length == 1) { + shutdown_pending = 1; + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "No active child processes: shutting down"); + } + } +#else + make_child(server_conf, free_slots[i], now); +#endif /* TPF */ + } + /* the next time around we want to spawn twice as many if this + * wasn't good enough, but not if we've just done a graceful + */ + if (hold_off_on_exponential_spawning) { + --hold_off_on_exponential_spawning; + } + else if (idle_spawn_rate < MAX_SPAWN_RATE) { + idle_spawn_rate *= 2; + } + } + } + else { + idle_spawn_rate = 1; + } +} + + +static void process_child_status(int pid, ap_wait_t status) +{ + /* Child died... if it died due to a fatal error, + * we should simply bail out. + */ + if ((WIFEXITED(status)) && + WEXITSTATUS(status) == APEXIT_CHILDFATAL) { + ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server_conf, + "Child %d returned a Fatal error... \n" + "Apache is exiting!", + pid); + exit(APEXIT_CHILDFATAL); + } + if (WIFSIGNALED(status)) { + switch (WTERMSIG(status)) { + case SIGTERM: + case SIGHUP: + case SIGUSR1: + case SIGKILL: + break; + default: +#ifdef SYS_SIGLIST +#ifdef WCOREDUMP + if (WCOREDUMP(status)) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %s (%d), " + "possible coredump in %s", + pid, (WTERMSIG(status) >= NumSIG) ? "" : + SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status), + ap_coredump_dir); + } + else { +#endif + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %s (%d)", pid, + SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status)); +#ifdef WCOREDUMP + } +#endif +#else + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %d", + pid, WTERMSIG(status)); +#endif + } + } +} + + +/***************************************************************** + * Executive routines. + */ + +int ap_mpm_run(pool *_pconf, pool *plog, server_rec *s) +{ + int remaining_children_to_start; + + pconf = _pconf; + + server_conf = s; + + ap_log_pid(pconf, ap_pid_fname); + setup_listeners(pconf); + + SAFE_ACCEPT(accept_mutex_init(pconf)); + if (!is_graceful) { + reinit_scoreboard(pconf); + } +#ifdef SCOREBOARD_FILE + else { + ap_scoreboard_fname = ap_server_root_relative(pconf, ap_scoreboard_fname); + ap_note_cleanups_for_fd(pconf, scoreboard_fd); + } +#endif + + set_signals(); + + if (ap_daemons_max_free < ap_daemons_min_free + 1) /* Don't thrash... */ + ap_daemons_max_free = ap_daemons_min_free + 1; + + /* If we're doing a graceful_restart then we're going to see a lot + * of children exiting immediately when we get into the main loop + * below (because we just sent them SIGUSR1). This happens pretty + * rapidly... and for each one that exits we'll start a new one until + * we reach at least daemons_min_free. But we may be permitted to + * start more than that, so we'll just keep track of how many we're + * supposed to start up without the 1 second penalty between each fork. + */ + remaining_children_to_start = ap_daemons_to_start; + if (remaining_children_to_start > ap_daemons_limit) { + remaining_children_to_start = ap_daemons_limit; + } + if (!is_graceful) { + startup_children(remaining_children_to_start); + remaining_children_to_start = 0; + } + else { + /* give the system some time to recover before kicking into + * exponential mode */ + hold_off_on_exponential_spawning = 10; + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "%s configured -- resuming normal operations", + ap_get_server_version()); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "Server built: %s", ap_get_server_built()); + restart_pending = shutdown_pending = 0; + + while (!restart_pending && !shutdown_pending) { + int child_slot; + ap_wait_t status; + int pid = wait_or_timeout(&status); + + /* XXX: if it takes longer than 1 second for all our children + * to start up and get into IDLE state then we may spawn an + * extra child + */ + if (pid >= 0) { + process_child_status(pid, status); + /* non-fatal death... note that it's gone in the scoreboard. */ + ap_sync_scoreboard_image(); + child_slot = find_child_by_pid(pid); + if (child_slot >= 0) { + (void) ap_update_child_status(child_slot, SERVER_DEAD, + (request_rec *) NULL); + if (remaining_children_to_start + && child_slot < ap_daemons_limit) { + /* we're still doing a 1-for-1 replacement of dead + * children with new children + */ + make_child(server_conf, child_slot, time(0)); + --remaining_children_to_start; + } +#ifdef HAS_OTHER_CHILD + } + else if (reap_other_child(pid, status) == 0) { + /* handled */ +#endif + } + else if (is_graceful) { + /* Great, we've probably just lost a slot in the + * scoreboard. Somehow we don't know about this + * child. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf, + "long lost child came home! (pid %d)", pid); + } + /* Don't perform idle maintenance when a child dies, + * only do it when there's a timeout. Remember only a + * finite number of children can die, and it's pretty + * pathological for a lot to die suddenly. + */ + continue; + } + else if (remaining_children_to_start) { + /* we hit a 1 second timeout in which none of the previous + * generation of children needed to be reaped... so assume + * they're all done, and pick up the slack if any is left. + */ + startup_children(remaining_children_to_start); + remaining_children_to_start = 0; + /* In any event we really shouldn't do the code below because + * few of the servers we just started are in the IDLE state + * yet, so we'd mistakenly create an extra server. + */ + continue; + } + + perform_idle_server_maintenance(); +#ifdef TPF + shutdown_pending = os_check_server(tpf_server_name); + ap_check_signals(); + sleep(1); +#endif /*TPF */ + } + + if (shutdown_pending) { + /* Time to gracefully shut down: + * Kill child processes, tell them to call child_exit, etc... + */ + if (ap_killpg(getpgrp(), SIGTERM) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGTERM"); + } + reclaim_child_processes(1); /* Start with SIGTERM */ + + /* cleanup pid file on normal shutdown */ + { + const char *pidfile = NULL; + pidfile = ap_server_root_relative (pconf, ap_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + server_conf, + "removed PID file %s (pid=%ld)", + pidfile, (long)getpid()); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "caught SIGTERM, shutting down"); + return 1; + } + + /* we've been told to restart */ + signal(SIGHUP, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + + if (one_process) { + /* not worth thinking about */ + return 1; + } + + /* advance to the next generation */ + /* XXX: we really need to make sure this new generation number isn't in + * use by any of the children. + */ + ++ap_my_generation; + ap_scoreboard_image->global.running_generation = ap_my_generation; + update_scoreboard_global(); + + if (is_graceful) { +#ifndef SCOREBOARD_FILE + int i; +#endif + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "SIGUSR1 received. Doing graceful restart"); + + /* kill off the idle ones */ + if (ap_killpg(getpgrp(), SIGUSR1) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGUSR1"); + } +#ifndef SCOREBOARD_FILE + /* This is mostly for debugging... so that we know what is still + * gracefully dealing with existing request. But we can't really + * do it if we're in a SCOREBOARD_FILE because it'll cause + * corruption too easily. + */ + ap_sync_scoreboard_image(); + for (i = 0; i < ap_daemons_limit; ++i) { + if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) { + ap_scoreboard_image->servers[i].status = SERVER_GRACEFUL; + } + } +#endif + } + else { + /* Kill 'em off */ + if (ap_killpg(getpgrp(), SIGHUP) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGHUP"); + } + reclaim_child_processes(0); /* Not when just starting up */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "SIGHUP received. Attempting to restart"); + } + + /* must copy now before pconf is cleared */ + copy_listeners(pconf); + if (!is_graceful) { + ap_restart_time = time(NULL); + } + + return 0; +} + +static void prefork_pre_command_line(pool *pcommands) +{ + INIT_SIGLIST(); +#ifdef AUX3 + (void) set42sig(); +#endif + /* TODO: set one_process properly */ one_process = 0; +} + +static void prefork_pre_config(pool *pconf, pool *plog, pool *ptemp) +{ + static int restart_num = 0; + + one_process = ap_exists_config_define("ONE_PROCESS"); + + /* sigh, want this only the second time around */ + if (restart_num++ == 1) { + is_graceful = 0; + + if (!one_process) { + unixd_detach(); + } + + my_pid = getpid(); + } + + unixd_pre_config(); + ap_daemons_to_start = DEFAULT_START_DAEMON; + ap_daemons_min_free = DEFAULT_MIN_FREE_DAEMON; + ap_daemons_max_free = DEFAULT_MAX_FREE_DAEMON; + ap_daemons_limit = HARD_SERVER_LIMIT; + ap_pid_fname = DEFAULT_PIDLOG; + ap_scoreboard_fname = DEFAULT_SCOREBOARD; + ap_lock_fname = DEFAULT_LOCKFILE; + ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; + /* ZZZ Initialize the Network Address here. */ + ap_bind_address.s_addr = htonl(INADDR_ANY); + ap_listeners = NULL; + ap_listenbacklog = DEFAULT_LISTENBACKLOG; + ap_extended_status = 0; + + ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); +} + +static void prefork_post_config(pool *pconf, pool *plog, pool *ptemp, server_rec *s) +{ + if (ap_listeners == NULL) { + /* allocate a default listener */ + listen_rec *new; + + new = ap_pcalloc(pconf, sizeof(listen_rec)); + new->local_addr.sin_family = AF_INET; + new->local_addr.sin_addr = ap_bind_address; + new->local_addr.sin_port = htons(s->port ? s->port : DEFAULT_HTTP_PORT); + new->fd = -1; + new->next = NULL; + ap_listeners = new; + } +} + +static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (cmd->server->is_virtual) { + return "PidFile directive not allowed in "; + } + ap_pid_fname = arg; + return NULL; +} + +static const char *set_scoreboard(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_scoreboard_fname = arg; + return NULL; +} + +static const char *set_lockfile(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_lock_fname = arg; + return NULL; +} + +static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_to_start = atoi(arg); + return NULL; +} + +static const char *set_min_free_servers(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_min_free = atoi(arg); + if (ap_daemons_min_free <= 0) { + fprintf(stderr, "WARNING: detected MinSpareServers set to non-positive.\n"); + fprintf(stderr, "Resetting to 1 to avoid almost certain Apache failure.\n"); + fprintf(stderr, "Please read the documentation.\n"); + ap_daemons_min_free = 1; + } + + return NULL; +} + +static const char *set_max_free_servers(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_max_free = atoi(arg); + return NULL; +} + +static const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_limit = atoi(arg); + if (ap_daemons_limit > HARD_SERVER_LIMIT) { + fprintf(stderr, "WARNING: MaxClients of %d exceeds compile time limit " + "of %d servers,\n", ap_daemons_limit, HARD_SERVER_LIMIT); + fprintf(stderr, " lowering MaxClients to %d. To increase, please " + "see the\n", HARD_SERVER_LIMIT); + fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n"); + ap_daemons_limit = HARD_SERVER_LIMIT; + } + else if (ap_daemons_limit < 1) { + fprintf(stderr, "WARNING: Require MaxClients > 0, setting to 1\n"); + ap_daemons_limit = 1; + } + return NULL; +} + +static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_max_requests_per_child = atoi(arg); + + return NULL; +} + +static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg) +{ + struct stat finfo; + const char *fname; + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + fname = ap_server_root_relative(cmd->pool, arg); + /* ZZZ change this to the AP func FileInfo*/ + if ((stat(fname, &finfo) == -1) || !S_ISDIR(finfo.st_mode)) { + return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", fname, + " does not exist or is not a directory", NULL); + } + ap_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir)); + return NULL; +} + +static const char *set_listenbacklog(cmd_parms *cmd, void *dummy, char *arg) +{ + int b; + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + b = atoi(arg); + if (b < 1) { + return "ListenBacklog must be > 0"; + } + ap_listenbacklog = b; + return NULL; +} + +static const char *set_listener(cmd_parms *cmd, void *dummy, char *ips) +{ + listen_rec *new; + char *ports; + unsigned short port; + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ports = strchr(ips, ':'); + if (ports != NULL) { + if (ports == ips) { + return "Missing IP address"; + } + else if (ports[1] == '\0') { + return "Address must end in :"; + } + *(ports++) = '\0'; + } + else { + ports = ips; + } + + new=ap_pcalloc(cmd->pool, sizeof(listen_rec)); + /* ZZZ let's set this using the AP funcs. */ + new->local_addr.sin_family = AF_INET; + if (ports == ips) { /* no address */ + /* ZZZ Initialize the Network Address */ + new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + } + else { + new->local_addr.sin_addr.s_addr = ap_get_virthost_addr(ips, NULL); + } + port = atoi(ports); + if (!port) { + return "Port must be numeric"; + } + /* ZZZ change to AP funcs.*/ + new->local_addr.sin_port = htons(port); + new->fd = -1; /*ZZZ change to NULL */ + new->used = 0; + new->next = ap_listeners; + ap_listeners = new; + return NULL; +} + +/* there are no threads in the prefork model, so the mutexes are + nops. */ +/* TODO: make these #defines to eliminate the function call */ + +struct ap_thread_mutex { + int dummy; +}; + +API_EXPORT(ap_thread_mutex *) ap_thread_mutex_new(void) +{ + return malloc(sizeof(ap_thread_mutex)); +} + +API_EXPORT(void) ap_thread_mutex_lock(ap_thread_mutex *mtx) +{ +} + +API_EXPORT(void) ap_thread_mutex_unlock(ap_thread_mutex *mtx) +{ +} + +API_EXPORT(void) ap_thread_mutex_destroy(ap_thread_mutex *mtx) +{ + free(mtx); +} + + +static const command_rec prefork_cmds[] = { +UNIX_DAEMON_COMMANDS +{ "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1, + "A file for logging the server process ID"}, +{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1, + "A file for Apache to maintain runtime process management information"}, +{ "LockFile", set_lockfile, NULL, RSRC_CONF, TAKE1, + "The lockfile used when Apache needs to lock the accept() call"}, +{ "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1, + "Number of child processes launched at server startup" }, +{ "MinSpareServers", set_min_free_servers, NULL, RSRC_CONF, TAKE1, + "Minimum number of idle children, to handle request spikes" }, +{ "MaxSpareServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, + "Maximum number of idle children" }, +{ "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1, + "Maximum number of children alive at the same time" }, +{ "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1, + "Maximum number of requests a particular child serves before dying." }, +{ "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1, + "The location of the directory Apache changes to before dumping core" }, +{ "ListenBacklog", set_listenbacklog, NULL, RSRC_CONF, TAKE1, + "Maximum length of the queue of pending connections, as used by listen(2)" }, +{ "Listen", set_listener, NULL, RSRC_CONF, TAKE1, + "A port number or a numeric IP address and a port number"}, +{ NULL } +}; + +module MODULE_VAR_EXPORT mpm_prefork_module = { + STANDARD20_MODULE_STUFF, + prefork_pre_command_line, /* pre_command_line */ + prefork_pre_config, /* pre_config */ + prefork_post_config, /* post_config */ + NULL, /* open_logs */ + NULL, /* child_init */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + prefork_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* translate_handler */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* pre-run fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL /* post_read_request */ +}; diff --git a/server/mpm/winnt/mpm_default.h b/server/mpm/winnt/mpm_default.h new file mode 100644 index 00000000000..5ac2fe40aa4 --- /dev/null +++ b/server/mpm/winnt/mpm_default.h @@ -0,0 +1,114 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_MPM_DEFAULT_H +#define APACHE_MPM_DEFAULT_H + +/* Number of threads to spawn off by default --- also, if fewer than + * this free when the caretaker checks, it will spawn more. + */ +#ifndef DEFAULT_START_THREAD +#define DEFAULT_START_THREAD 5 +#endif + +/* Maximum number of *free* server threads --- more than this, and + * they will die off. +#ifndef DEFAULT_MAX_SPARE_THREAD +#define DEFAULT_MAX_SPARE_THREAD 10 +#endif +*/ + +/* Minimum --- fewer than this, and more will be created */ +/* +#ifndef DEFAULT_MIN_SPARE_THREAD +#define DEFAULT_MIN_SPARE_THREAD 5 +#endif +*/ + +/* Limit on the threads per process. Clients will be locked out if more than + * this * HARD_SERVER_LIMIT are needed. + * + * We keep this for one reason it keeps the size of the scoreboard file small + * enough that we can read the whole thing without worrying too much about + * the overhead. + */ +#ifndef HARD_THREAD_LIMIT +#define HARD_THREAD_LIMIT 64 +#endif + +/* Number of servers to spawn off by default + */ +#ifndef DEFAULT_NUM_DAEMON +#define DEFAULT_NUM_DAEMON 1 +#endif + +/* Limit on the total --- clients will be locked out if more servers than + * this are needed. It is intended solely to keep the server from crashing + * when things get out of hand. + * + * We keep a hard maximum number of servers, for two reasons --- first off, + * in case something goes seriously wrong, we want to stop the fork bomb + * short of actually crashing the machine we're running on by filling some + * kernel table. Secondly, it keeps the size of the scoreboard file small + * enough that we can read the whole thing without worrying too much about + * the overhead. + */ +#ifndef HARD_SERVER_LIMIT +#define HARD_SERVER_LIMIT 8 +#endif + +#endif /* AP_MPM_DEFAULT_H */ diff --git a/server/mpm/winnt/mpm_winnt.c b/server/mpm/winnt/mpm_winnt.c new file mode 100644 index 00000000000..9ab533d2895 --- /dev/null +++ b/server/mpm/winnt/mpm_winnt.c @@ -0,0 +1,1683 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#define CORE_PRIVATE + +#include "httpd.h" +#include "http_main.h" +#include "http_log.h" +#include "http_config.h" /* for read_config */ +#include "http_core.h" /* for get_remote_host */ +#include "http_connection.h" +#include "ap_mpm.h" +#include "ap_config.h" +#include "ap_listen.h" +#include "multithread.h" +#include "../os/win32/getopt.h" +#include "mpm_default.h" +/* + * Actual definitions of WINNT MPM specific config globals + */ +int ap_max_requests_per_child=0; +int ap_daemons_to_start=0; +static char *mpm_pid_fname=NULL; +static int ap_threads_per_child = 0; +//static int ap_max_threads_per_child = 0; +static int workers_may_exit = 0; +static int max_requests_per_child = 0; +static int requests_this_child; +static int num_listenfds = 0; +static struct pollfd *listenfds; +static pool *pconf; /* Pool for config stuff */ + +static char ap_coredump_dir[MAX_STRING_LEN]; + + +static server_rec *server_conf; +/*static int sd; ZZZ why is the global...? Only seems to be needed on Win32*/ + +/* one_process --- debugging mode variable; can be set from the command line + * with the -X flag. If set, this gets you the child_main loop running + * in the process which originally started up (no detach, no make_child), + * which is a pretty nice debugging environment. (You'll get a SIGHUP + * early in standalone_main; just continue through. This is the server + * trying to kill off any child processes which it might have lying + * around --- Apache doesn't keep track of their pids, it just sends + * SIGHUP to the process group, ignoring it in the root process. + * Continue through and you'll be fine.). + */ + +static int one_process = 0; + +#ifdef DEBUG_SIGSTOP +int raise_sigstop_flags; +#endif + + +/* *Non*-shared winnt.c globals... */ +event *exit_event; +mutex *start_mutex; +int my_pid; +int parent_pid; +int listenmaxfd; + +/* a clean exit from a child with proper cleanup + static void clean_child_exit(int code) __attribute__ ((noreturn)); +void clean_child_exit(int code) +{ + if (pchild) { + ap_destroy_pool(pchild); + } + exit(code); +} +*/ +/***************************************************************** + * Connection structures and accounting... + */ + +/* volatile just in case */ +static int volatile shutdown_pending; +static int volatile restart_pending; +static int volatile is_graceful; + +/* + * ap_start_shutdown() and ap_start_restart(), below, are a first stab at + * functions to initiate shutdown or restart without relying on signals. + * Previously this was initiated in sig_term() and restart() signal handlers, + * but we want to be able to start a shutdown/restart from other sources -- + * e.g. on Win32, from the service manager. Now the service manager can + * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that + * these functions can also be called by the child processes, since global + * variables are no longer used to pass on the required action to the parent. + * + * These should only be called from the parent process itself, since the + * parent process will use the shutdown_pending and restart_pending variables + * to determine whether to shutdown or restart. The child process should + * call signal_parent() directly to tell the parent to die -- this will + * cause neither of those variable to be set, which the parent will + * assume means something serious is wrong (which it will be, for the + * child to force an exit) and so do an exit anyway. + */ + +void ap_start_shutdown(void) +{ + if (shutdown_pending == 1) { + /* Um, is this _probably_ not an error, if the user has + * tried to do a shutdown twice quickly, so we won't + * worry about reporting it. + */ + return; + } + shutdown_pending = 1; +} + +/* do a graceful restart if graceful == 1 */ +void ap_start_restart(int graceful) +{ + + if (restart_pending == 1) { + /* Probably not an error - don't bother reporting it */ + return; + } + restart_pending = 1; + is_graceful = graceful; +} + +static void sig_term(int sig) +{ + ap_start_shutdown(); +} + +static void restart(int sig) +{ + ap_start_restart(1); +} + +static ap_listen_rec *old_listeners; +static ap_listen_rec *head_listener; +static int setup_listeners(pool *pconf, server_rec *s) +{ + ap_listen_rec *lr; + int num_listeners = 0; + + if (ap_listen_open(pconf, s->port)) { + return 0; + } + for (lr = ap_listeners; lr; lr = lr->next) { + num_listeners++; + } + return num_listeners; +} + +static int find_listener(ap_listen_rec *lr) +{ + ap_listen_rec *or; + + for (or = old_listeners; or; or = or->next) { + if (!memcmp(&or->local_addr, &lr->local_addr, sizeof(or->local_addr))) { +// or->used = 1; + return or->fd; + } + } + return -1; +} + + +static void close_unused_listeners(void) +{ + ap_listen_rec *or, *next; + + for (or = old_listeners; or; or = next) { + next = or->next; +// if (!or->used) + closesocket(or->fd); + free(or); + } + old_listeners = NULL; +} + +/* + * Find a listener which is ready for accept(). This advances the + * head_listener global. + */ +static ap_inline ap_listen_rec *find_ready_listener(fd_set * main_fds) +//static ap_listen_rec *find_ready_listener(fd_set * main_fds) +{ + ap_listen_rec *lr; + + lr = head_listener; + do { + if (FD_ISSET(lr->fd, main_fds)) { + head_listener = lr->next; + return (lr); + } + lr = lr->next; + } while (lr != head_listener); + return NULL; +} + +/* + * Signalling Apache on NT. + * + * Under Unix, Apache can be told to shutdown or restart by sending various + * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so + * we use "events" instead. The parent apache process goes into a loop + * where it waits forever for a set of events. Two of those events are + * called + * + * apPID_shutdown + * apPID_restart + * + * (where PID is the PID of the apache parent process). When one of these + * is signalled, the Apache parent performs the appropriate action. The events + * can become signalled through internal Apache methods (e.g. if the child + * finds a fatal error and needs to kill its parent), via the service + * control manager (the control thread will signal the shutdown event when + * requested to stop the Apache service), from the -k Apache command line, + * or from any external program which finds the Apache PID from the + * httpd.pid file. + * + * The signal_parent() function, below, is used to signal one of these events. + * It can be called by any child or parent process, since it does not + * rely on global variables. + * + * On entry, type gives the event to signal. 0 means shutdown, 1 means + * graceful restart. + */ + +static void signal_parent(int type) +{ + HANDLE e; + char *signal_name; + extern char signal_shutdown_name[]; + extern char signal_restart_name[]; + + /* after updating the shutdown_pending or restart flags, we need + * to wake up the parent process so it can see the changes. The + * parent will normally be waiting for either a child process + * to die, or for a signal on the "spache-signal" event. So set the + * "apache-signal" event here. + */ + + if (one_process) { + return; + } + + switch(type) { + case 0: signal_name = signal_shutdown_name; break; + case 1: signal_name = signal_restart_name; break; + default: return; + } + +// APD2("signal_parent signalling event \"%s\"", signal_name); + + e = OpenEvent(EVENT_ALL_ACCESS, FALSE, signal_name); + if (!e) { + /* Um, problem, can't signal the parent, which means we can't + * signal ourselves to die. Ignore for now... + */ + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, + "OpenEvent on %s event", signal_name); + return; + } + if (SetEvent(e) == 0) { + /* Same problem as above */ + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, + "SetEvent on %s event", signal_name); + CloseHandle(e); + return; + } + CloseHandle(e); +} + + +/***************************************************************** + * Here follows a long bunch of generic server bookkeeping stuff... + */ + +static void sock_disable_nagle(int s) /* ZZZ abstract */ +{ + /* The Nagle algorithm says that we should delay sending partial + * packets in hopes of getting more data. We don't want to do + * this; we are not telnet. There are bad interactions between + * persistent connections and Nagle's algorithm that have very severe + * performance penalties. (Failing to disable Nagle is not much of a + * problem with simple HTTP.) + * + * In spite of these problems, failure here is not a shooting offense. + */ + int just_say_no = 1; + + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no, + sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "setsockopt: (TCP_NODELAY)"); + } +} + + +/********************************************************************** + * Multithreaded implementation + * + * This code is fairly specific to Win32. + * + * The model used to handle requests is a set of threads. One "main" + * thread listens for new requests. When something becomes + * available, it does a select and places the newly available socket + * onto a list of "jobs" (add_job()). Then any one of a fixed number + * of "worker" threads takes the top job off the job list with + * remove_job() and handles that connection to completion. After + * the connection has finished the thread is free to take another + * job from the job list. + * + * In the code, the "main" thread is running within the worker_main() + * function. The first thing this function does is create the + * worker threads, which operate in the child_sub_main() function. The + * main thread then goes into a loop within worker_main() where they + * do a select() on the listening sockets. The select times out once + * per second so that the thread can check for an "exit" signal + * from the parent process (see below). If this signal is set, the + * thread can exit, but only after it has accepted all incoming + * connections already in the listen queue (since Win32 appears + * to through away listened but unaccepted connections when a + * process dies). + * + * Because the main and worker threads exist within a single process + * they are vulnerable to crashes or memory leaks (crashes can also + * be caused within modules, of course). There also needs to be a + * mechanism to perform restarts and shutdowns. This is done by + * creating the main & worker threads within a subprocess. A + * main process (the "parent process") creates one (or more) + * processes to do the work, then the parent sits around waiting + * for the working process to die, in which case it starts a new + * one. The parent process also handles restarts (by creating + * a new working process then signalling the previous working process + * exit ) and shutdowns (by signalling the working process to exit). + * The parent process operates within the master_main() function. This + * process also handles requests from the service manager (NT only). + * + * Signalling between the parent and working process uses a Win32 + * event. Each child has a unique name for the event, which is + * passed to it with the -Z argument when the child is spawned. The + * parent sets (signals) this event to tell the child to die. + * At present all children do a graceful die - they finish all + * current jobs _and_ empty the listen queue before they exit. + * A non-graceful die would need a second event. The -Z argument in + * the child is also used to create the shutdown and restart events, + * since the prefix (apPID) contains the parent process PID. + * + * The code below starts with functions at the lowest level - + * worker threads, and works up to the top level - the main() + * function of the parent process. + * + * The scoreboard (in process memory) contains details of the worker + * threads (within the active working process). There is no shared + * "scoreboard" between processes, since only one is ever active + * at once (or at most, two, when one has been told to shutdown but + * is processes outstanding requests, and a new one has been started). + * This is controlled by a "start_mutex" which ensures only one working + * process is active at once. + **********************************************************************/ + +/* The code protected by #ifdef UNGRACEFUL_RESTARTS/#endif sections + * could implement a sort-of ungraceful restart for Win32. instead of + * graceful restarts. + * + * However it does not work too well because it does not intercept a + * connection already in progress (in child_sub_main()). We'd have to + * get that to poll on the exit event. + */ + +/* + * Definition of jobs, shared by main and worker threads. + */ + +typedef struct joblist_s { + struct joblist_s *next; + int sock; +} joblist; + +/* + * Globals common to main and worker threads. This structure is not + * used by the parent process. + */ + +typedef struct globals_s { +#ifdef UNGRACEFUL_RESTART + HANDLE thread_exit_event; +#else + int exit_now; +#endif + semaphore *jobsemaphore; + joblist *jobhead; + joblist *jobtail; + mutex *jobmutex; + int jobcount; +} globals; + +globals allowed_globals = +{0, NULL, NULL, NULL, NULL, 0}; + +/* + * add_job()/remove_job() - add or remove an accepted socket from the + * list of sockets connected to clients. allowed_globals.jobmutex protects + * against multiple concurrent access to the linked list of jobs. + */ + +void add_job(int sock) +{ + joblist *new_job; + + ap_assert(allowed_globals.jobmutex); + /* TODO: If too many jobs in queue, sleep, check for problems */ + ap_acquire_mutex(allowed_globals.jobmutex); + new_job = (joblist *) malloc(sizeof(joblist)); + if (new_job == NULL) { + fprintf(stderr, "Ouch! Out of memory in add_job()!\n"); + } + new_job->next = NULL; + new_job->sock = sock; + if (allowed_globals.jobtail != NULL) + allowed_globals.jobtail->next = new_job; + allowed_globals.jobtail = new_job; + if (!allowed_globals.jobhead) + allowed_globals.jobhead = new_job; + allowed_globals.jobcount++; + release_semaphore(allowed_globals.jobsemaphore); + ap_release_mutex(allowed_globals.jobmutex); +} + +int remove_job(void) +{ + joblist *job; + int sock; + +#ifdef UNGRACEFUL_RESTART + HANDLE hObjects[2]; + int rv; + + hObjects[0] = allowed_globals.jobsemaphore; + hObjects[1] = allowed_globals.thread_exit_event; + + rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); + ap_assert(rv != WAIT_FAILED); + if (rv == WAIT_OBJECT_0 + 1) { + /* thread_exit_now */ +// APD1("thread got exit now event"); + return -1; + } + /* must be semaphore */ +#else + acquire_semaphore(allowed_globals.jobsemaphore); +#endif + ap_assert(allowed_globals.jobmutex); + +#ifdef UNGRACEFUL_RESTART + if (!allowed_globals.jobhead) { +#else + ap_acquire_mutex(allowed_globals.jobmutex); + if (allowed_globals.exit_now && !allowed_globals.jobhead) { +#endif + ap_release_mutex(allowed_globals.jobmutex); + return (-1); + } + job = allowed_globals.jobhead; + ap_assert(job); + allowed_globals.jobhead = job->next; + if (allowed_globals.jobhead == NULL) + allowed_globals.jobtail = NULL; + ap_release_mutex(allowed_globals.jobmutex); + sock = job->sock; + free(job); + return (sock); +} + +/* + * child_sub_main() - this is the main loop for the worker threads + * + * Each thread runs within this function. They wait within remove_job() + * for a job to become available, then handle all the requests on that + * connection until it is closed, then return to remove_job(). + * + * The worker thread will exit when it removes a job which contains + * socket number -1. This provides a graceful thread exit, since + * it will never exit during a connection. + * + * This code in this function is basically equivalent to the child_main() + * from the multi-process (Unix) environment, except that we + * + * - do not call child_init_modules (child init API phase) + * - block in remove_job, and when unblocked we have an already + * accepted socket, instead of blocking on a mutex or select(). + */ + +static void child_sub_main(int child_num) +{ + NET_SIZE_T clen; + struct sockaddr sa_server; + struct sockaddr sa_client; + pool *ptrans; + int requests_this_child = 0; + int csd = -1; + int dupped_csd = -1; + int srv = 0; + + /* Note: current_conn used to be a defined at file scope as follows... Since the signal code is + not being used in WIN32, make the variable local */ + // static APACHE_TLS conn_rec *volatile current_conn; + conn_rec *current_conn; + + ptrans = ap_make_sub_pool(pconf); + +#if 0 + /* ZZZ scoreboard */ + (void) ap_update_child_status(child_num, SERVER_READY, (request_rec *) NULL); +#endif + + /* + * Setup the jump buffers so that we can return here after a timeout. + */ +#if 0 /* ZZZ */ +#if defined(USE_LONGJMP) + setjmp(jmpbuffer); +#else + sigsetjmp(jmpbuffer, 1); +#endif +#ifdef SIGURG + signal(SIGURG, timeout); +#endif +#endif + + while (1) { + BUFF *conn_io; + request_rec *r; + + /* + * (Re)initialize this child to a pre-connection state. + */ +#if 0 /* ZZZ Alarms... */ + ap_set_callback_and_alarm(NULL, 0); /* Cancel any outstanding alarms */ +#endif +#if 0 /* ZZZ what is this? It's not thread safe! */ + timeout_req = NULL; /* No request in progress */ +#endif + current_conn = NULL; + + ap_clear_pool(ptrans); + +#if 0 + /* ZZZ scoreboard */ + (void) ap_update_child_status(child_num, SERVER_READY, + (request_rec *) NULL); +#endif + + /* Get job from the job list. This will block until a job is ready. + * If -1 is returned then the main thread wants us to exit. + */ + csd = remove_job(); + if (csd == -1) + break; /* time to exit */ + requests_this_child++; + + ap_note_cleanups_for_socket(ptrans, csd); + + /* + * We now have a connection, so set it up with the appropriate + * socket options, file descriptors, and read/write buffers. + */ + + clen = sizeof(sa_server); + if (getsockname(csd, &sa_server, &clen) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "getsockname"); + continue; + } + clen = sizeof(sa_client); + if ((getpeername(csd, &sa_client, &clen)) < 0) { + /* get peername will fail if the input isn't a socket */ + perror("getpeername"); + memset(&sa_client, '\0', sizeof(sa_client)); + } + + sock_disable_nagle(csd); +#if 0 + /* ZZZ scoreboard */ + (void) ap_update_child_status(child_num, SERVER_BUSY_READ, + (request_rec *) NULL); +#endif + +/* ZZZ . This will break CGIs since they need to know whether a fd is a socket or + a file handle... Fix with APR +*/ + conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET); +// conn_io = ap_bcreate(ptrans, B_RDWR); + dupped_csd = csd; +#if defined(NEED_DUPPED_CSD) + if ((dupped_csd = dup(csd)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "dup: couldn't duplicate csd"); + dupped_csd = csd; /* Oh well... */ + } + ap_note_cleanups_for_socket(ptrans, dupped_csd); +#endif + ap_bpushfd(conn_io, csd); /* ap_bpushfd(conn_io, csd, dupped_csd); why did we drop duped fd? */ + + current_conn = ap_new_connection(ptrans, server_conf, conn_io, + (struct sockaddr_in *) &sa_client, + (struct sockaddr_in *) &sa_server, + child_num, 0); /* Set my_thread_num to 0 for now */ + + ap_process_connection(current_conn); + } +} + + +void child_main(int child_num_arg) +{ + /* + * Only reason for this function, is to pass in + * arguments to child_sub_main() on its stack so + * that longjump doesn't try to corrupt its local + * variables and I don't need to make those + * damn variables static/global + */ + child_sub_main(child_num_arg); +} + + + +void cleanup_thread(thread **handles, int *thread_cnt, int thread_to_clean) +{ + int i; + + free_thread(handles[thread_to_clean]); + for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++) + handles[i] = handles[i + 1]; + (*thread_cnt)--; +} + +/* + * The Win32 call WaitForMultipleObjects will only allow you to wait for + * a maximum of MAXIMUM_WAIT_OBJECTS (current 64). Since the threading + * model in the multithreaded version of apache wants to use this call, + * we are restricted to a maximum of 64 threads. This is a simplistic + * routine that will increase this size. + */ +static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, + DWORD dwSeconds) +{ + time_t tStopTime; + DWORD dwRet = WAIT_TIMEOUT; + DWORD dwIndex=0; + BOOL bFirst = TRUE; + + tStopTime = time(NULL) + dwSeconds; + + do { + if (!bFirst) + Sleep(1000); + else + bFirst = FALSE; + + for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) { + dwRet = WaitForMultipleObjects( + min(MAXIMUM_WAIT_OBJECTS, + nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)), + lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS), + 0, 0); + + if (dwRet != WAIT_TIMEOUT) { + break; + } + } + } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT)); + + return dwRet; +} + +//extern void main_control_server(void *); /* in hellop.c */ + +#define MAX_SELECT_ERRORS 100 + +/* + * Initialise the signal names, in the global variables signal_name_prefix, + * signal_restart_name and signal_shutdown_name. + */ + +#define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is an int */ +char signal_name_prefix[MAX_SIGNAL_NAME]; +char signal_restart_name[MAX_SIGNAL_NAME]; +char signal_shutdown_name[MAX_SIGNAL_NAME]; +static void setup_signal_names(char *prefix) +{ + ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix); + ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name), + "%s_shutdown", signal_name_prefix); + ap_snprintf(signal_restart_name, sizeof(signal_restart_name), + "%s_restart", signal_name_prefix); +} + +static void setup_inherited_listeners(pool *p) +{ + HANDLE pipe; + ap_listen_rec *lr; + int fd; + WSAPROTOCOL_INFO WSAProtocolInfo; + DWORD BytesRead; + + /* Open the pipe to the parent process to receive the inherited socket + * data. The sockets have been set to listening in the parent process. + */ + pipe = GetStdHandle(STD_INPUT_HANDLE); + + /* Setup the listeners */ + listenmaxfd = -1; + FD_ZERO(&listenfds); + lr = ap_listeners; + + FD_ZERO(&listenfds); + + for (;;) { + fd = find_listener(lr); + if (fd < 0) { + if (!ReadFile(pipe, + &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), + &BytesRead, + (LPOVERLAPPED) NULL)){ + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf, + "setup_inherited_listeners: Unable to read socket data from parent"); + exit(1); + } + fd = WSASocket(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &WSAProtocolInfo, + 0, + 0); + if (fd == INVALID_SOCKET) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf, + "setup_inherited_listeners: WSASocket failed to get inherit the socket."); + exit(1); + } +// APD2("setup_inherited_listeners: WSASocket() returned socket %d", fd); + } + else { + ap_note_cleanups_for_socket(p, fd); + } + if (fd >= 0) { + FD_SET(fd, &listenfds); + if (fd > listenmaxfd) + listenmaxfd = fd; + } + lr->fd = fd; + if (lr->next == NULL) + break; + lr = lr->next; + } + /* turn the list into a ring */ + lr->next = ap_listeners; + head_listener = ap_listeners; + close_unused_listeners(); + CloseHandle(pipe); + return; +} + +/* + * worker_main() is main loop for the child process. The loop in + * this function becomes the controlling thread for the actually working + * threads (which run in a loop in child_sub_main()). + */ + +void worker_main(void) +{ + int nthreads; + fd_set main_fds; + int srv; + int clen; + int csd; + int sd = -1; // made this local and not global + struct sockaddr_in sa_client; + int total_jobs = 0; + thread **child_handles; + int rv; + time_t end_time; + int i; + struct timeval tv; + int wait_time = 1; + HANDLE hObjects[2]; + int count_select_errors = 0; + pool *pchild; + + pchild = ap_make_sub_pool(pconf); + +// ap_standalone = 1; +// sd = -1; ?? this variable is global in 1.3.x! + nthreads = ap_threads_per_child; + + if (nthreads <= 0) /* maybe this is not needed... Should be checked in config... */ + nthreads = 1; + + my_pid = getpid(); + + + ap_restart_time = time(NULL); + +// reinit_scoreboard(pconf); + + /* + * Wait until we have permission to start accepting connections. + * start_mutex is used to ensure that only one child ever + * goes into the listen/accept loop at once. Also wait on exit_event, + * in case we (this child) is told to die before we get a chance to + * serve any requests. + */ + hObjects[0] = (HANDLE)start_mutex; + hObjects[1] = (HANDLE)exit_event; + rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); + if (rv == WAIT_FAILED) { + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "Waiting for start_mutex or exit_event -- process will exit"); + + ap_destroy_pool(pchild); +// cleanup_scoreboard(); + exit(0); + } + if (rv == WAIT_OBJECT_0 + 1) { + /* exit event signalled - exit now */ + ap_destroy_pool(pchild); +// cleanup_scoreboard(); + exit(0); + } + /* start_mutex obtained, continue into the select() loop */ + if (one_process) { + setup_listeners(pconf, server_conf); + } else { + /* Get listeners from the parent process */ + setup_inherited_listeners(pconf); + } + + if (listenmaxfd == -1) { + /* Help, no sockets were made, better log something and exit */ + ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, NULL, + "No sockets were created for listening"); + + signal_parent(0); /* tell parent to die */ + + ap_destroy_pool(pchild); +// cleanup_scoreboard(); + exit(0); + } +// set_signals(); + +// ap_child_init_modules(pconf, server_conf); + + allowed_globals.jobsemaphore = create_semaphore(0); + allowed_globals.jobmutex = ap_create_mutex(NULL); + + /* spawn off the threads */ + child_handles = (thread *) alloca(nthreads * sizeof(int)); + for (i = 0; i < nthreads; i++) { + child_handles[i] = create_thread((void (*)(void *)) child_main, (void *) i); + } + + while (1) { + if (ap_max_requests_per_child && (total_jobs > ap_max_requests_per_child)) { + /* MaxRequestsPerChild hit... + */ + break; + } + /* Always check for the exit event being signaled. + */ + rv = WaitForSingleObject(exit_event, 0); + ap_assert((rv == WAIT_TIMEOUT) || (rv == WAIT_OBJECT_0)); + if (rv == WAIT_OBJECT_0) { +// APD1("child: exit event signalled, exiting"); + break; + } + + tv.tv_sec = wait_time; + tv.tv_usec = 0; + + memcpy(&main_fds, &listenfds, sizeof(fd_set)); + srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv); +#ifdef WIN32 + if (srv == SOCKET_ERROR) { + /* Map the Win32 error into a standard Unix error condition */ + errno = WSAGetLastError(); + srv = -1; + } +#endif /* WIN32 */ + + if (srv < 0) { + /* Error occurred - if EINTR, loop around with problem */ + if (errno != EINTR) { + /* A "real" error occurred, log it and increment the count of + * select errors. This count is used to ensure we don't go into + * a busy loop of continuous errors. + */ + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)"); + count_select_errors++; + if (count_select_errors > MAX_SELECT_ERRORS) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server_conf, + "Too many errors in select loop. Child process exiting."); + break; + } + } + continue; + } + count_select_errors = 0; /* reset count of errors */ + if (srv == 0) { + continue; + } + + { + ap_listen_rec *lr; + + lr = find_ready_listener(&main_fds); + if (lr != NULL) { + sd = lr->fd; + } + } + do { + clen = sizeof(sa_client); + csd = accept(sd, (struct sockaddr *) &sa_client, &clen); +#ifdef WIN32 + if (csd == INVALID_SOCKET) { + csd = -1; + errno = WSAGetLastError(); + } +#endif /* WIN32 */ + } while (csd < 0 && errno == EINTR); + + if (csd < 0) { +#if defined(EPROTO) && defined(ECONNABORTED) + if ((errno != EPROTO) && (errno != ECONNABORTED)) +#elif defined(EPROTO) + if (errno != EPROTO) +#elif defined(ECONNABORTED) + if (errno != ECONNABORTED) +#endif + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "accept: (client socket)"); + } + else { + add_job(csd); + total_jobs++; + } + } + +// APD2("process PID %d exiting", my_pid); + + /* Get ready to shutdown and exit */ + allowed_globals.exit_now = 1; + ap_release_mutex(start_mutex); + +#ifdef UNGRACEFUL_RESTART + SetEvent(allowed_globals.thread_exit_event); +#else + for (i = 0; i < nthreads; i++) { + add_job(-1); + } +#endif + +// APD2("process PID %d waiting for worker threads to exit", my_pid); + /* Wait for all your children */ + end_time = time(NULL) + 180; + while (nthreads) { + rv = wait_for_many_objects(nthreads, child_handles, + end_time - time(NULL)); + if (rv != WAIT_TIMEOUT) { + rv = rv - WAIT_OBJECT_0; + ap_assert((rv >= 0) && (rv < nthreads)); + cleanup_thread(child_handles, &nthreads, rv); + continue; + } + break; + } + +// APD2("process PID %d killing remaining worker threads", my_pid); + for (i = 0; i < nthreads; i++) { + kill_thread(child_handles[i]); + free_thread(child_handles[i]); + } +#ifdef UNGRACEFUL_RESTART + ap_assert(CloseHandle(allowed_globals.thread_exit_event)); +#endif + destroy_semaphore(allowed_globals.jobsemaphore); + ap_destroy_mutex(allowed_globals.jobmutex); + +// ap_child_exit_modules(pconf, server_conf); + ap_destroy_pool(pchild); + +// cleanup_scoreboard(); + +// APD2("process PID %d exited", my_pid); +// clean_parent_exit(0); +} /* standalone_main */ + +/* + * Spawn a child Apache process. The child process has the command line arguments from + * argc and argv[], plus a -Z argument giving the name of an event. The child should + * open and poll or wait on this event. When it is signalled, the child should die. + * prefix is a prefix string for the event name. + * + * The child_num argument on entry contains a serial number for this child (used to create + * a unique event name). On exit, this number will have been incremented by one, ready + * for the next call. + * + * On exit, the value pointed to be *ev will contain the event created + * to signal the new child process. + * + * The return value is the handle to the child process if successful, else -1. If -1 is + * returned the error will already have been logged by ap_log_error(). + */ + +/********************************************************************** + * master_main - this is the parent (main) process. We create a + * child process to do the work, then sit around waiting for either + * the child to exit, or a restart or exit signal. If the child dies, + * we just respawn a new one. If we have a shutdown or graceful restart, + * tell the child to die when it is ready. If it is a non-graceful + * restart, force the child to die immediately. + **********************************************************************/ + +#define MAX_PROCESSES 50 /* must be < MAX_WAIT_OBJECTS-1 */ + +static void cleanup_process(HANDLE *handles, HANDLE *events, int position, int *processes) +{ + int i; + int handle = 0; + + CloseHandle(handles[position]); + CloseHandle(events[position]); + + handle = (int)handles[position]; + + for (i = position; i < (*processes)-1; i++) { + handles[i] = handles[i + 1]; + events[i] = events[i + 1]; + } + (*processes)--; + +// APD4("cleanup_processes: removed child in slot %d handle %d, max=%d", position, handle, *processes); +} + +static int create_process(pool *p, HANDLE *handles, HANDLE *events, + int *processes, int *child_num, char *kill_event_name) +{ + + int rv, i; + HANDLE kill_event; + char buf[1024]; + char exit_event_name[40]; /* apPID_C# */ + char *pCommand; + char *pEnvBlock; + + STARTUPINFO si; /* Filled in prior to call to CreateProcess */ + PROCESS_INFORMATION pi; /* filled in on call to CreateProces */ + LPWSAPROTOCOL_INFO lpWSAProtocolInfo; + ap_listen_rec *lr; + DWORD BytesWritten; + HANDLE hPipeRead = NULL; + HANDLE hPipeWrite = NULL; + SECURITY_ATTRIBUTES sa = {0}; + + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + /* Build the command line. Should look something like this: + * C:/apache/bin/apache.exe -f ap_server_confname + * First, get the path to the executable... + */ + rv = GetModuleFileName(NULL, buf, sizeof(buf)); + if (rv == sizeof(buf)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Path to Apache process too long"); + return -1; + } else if (rv == 0) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: GetModuleFileName() returned NULL for current process."); + return -1; + } + + /* Create the exit event (apPID_C#). Parent signals this event to tell the + * child to exit + */ + ap_snprintf(exit_event_name, sizeof(exit_event_name), "%s_C%d", kill_event_name, ++(*child_num)); + kill_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name); + if (!kill_event) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Could not create exit event for child process"); + return -1; + } + +// pCommand = ap_psprintf(p, "\"%s\" -f \"%s\"", buf, ap_server_confname); + pCommand = ap_psprintf(p, "\"%s\" -f \"%s\"", buf, SERVER_CONFIG_FILE); + + /* Create a pipe to send socket info to the child */ + if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Unable to create pipe to child process.\n"); + return -1; + } + + pEnvBlock = ap_psprintf(p, "AP_PARENT_PID=%d\0AP_CHILD_NUM=%d\0\0",parent_pid,*child_num); + + /* Give the read in of the pipe (hPipeRead) to the child as stdin. The + * parent will write the socket data to the child on this pipe. + */ + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = hPipeRead; + + if (!CreateProcess(NULL, pCommand, NULL, NULL, + TRUE, /* Inherit handles */ + 0, /* Creation flags */ + pEnvBlock, /* Environment block */ + NULL, + &si, &pi)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Not able to create the child process."); + /* + * We must close the handles to the new process and its main thread + * to prevent handle and memory leaks. + */ + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return -1; + } + else { + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf, + "Parent: Created child process %d", pi.dwProcessId); + + /* Assume the child process lives. Update the process and event tables */ + handles[*processes] = pi.hProcess; + events[*processes] = kill_event; + (*processes)++; + + /* We never store the thread's handle, so close it now. */ + CloseHandle(pi.hThread); + + /* Run the chain of open sockets. For each socket, duplicate it + * for the target process then send the WSAPROTOCOL_INFO + * (returned by dup socket) to the child */ + lr = ap_listeners; + while (lr != NULL) { + lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO)); + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf, + "Parent: Duplicating socket %d and sending it to child process %d", lr->fd, pi.dwProcessId); + if (WSADuplicateSocket(lr->fd, + pi.dwProcessId, + lpWSAProtocolInfo) == SOCKET_ERROR) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: WSADuplicateSocket failed for socket %d.", lr->fd ); + return -1; + } + + if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO), + &BytesWritten, + (LPOVERLAPPED) NULL)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Unable to write duplicated socket %d to the child.", lr->fd ); + return -1; + } + + lr = lr->next; + if (lr == ap_listeners) + break; + } + } + CloseHandle(hPipeRead); + CloseHandle(hPipeWrite); + + return 0; +} + +/* To share the semaphores with other processes, we need a NULL ACL + * Code from MS KB Q106387 + */ + +static PSECURITY_ATTRIBUTES GetNullACL() +{ + PSECURITY_DESCRIPTOR pSD; + PSECURITY_ATTRIBUTES sa; + + sa = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES)); + pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, + SECURITY_DESCRIPTOR_MIN_LENGTH); + if (pSD == NULL || sa == NULL) { + return NULL; + } + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) + || GetLastError()) { + LocalFree( pSD ); + LocalFree( sa ); + return NULL; + } + if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE) + || GetLastError()) { + LocalFree( pSD ); + LocalFree( sa ); + return NULL; + } + sa->nLength = sizeof(sa); + sa->lpSecurityDescriptor = pSD; + sa->bInheritHandle = TRUE; + return sa; +} + + +static void CleanNullACL( void *sa ) { + if( sa ) { + LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor); + LocalFree( sa ); + } +} + +static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_event) +{ + int remaining_children_to_start; + + int child_num = 0; + int rv, cld; + HANDLE process_handles[MAX_PROCESSES]; + HANDLE process_kill_events[MAX_PROCESSES]; + int current_live_processes = 0; /* number of child process we know about */ + int processes_to_create = 0; /* number of child processes to create */ + pool *pparent = NULL; /* pool for the parent process. Cleaned on each restart */ + + restart_pending = shutdown_pending = 0; + + remaining_children_to_start = ap_daemons_to_start; + + /* Create child process + * Should only be one in this version of Apache for WIN32 + */ + while (remaining_children_to_start--) { + if (create_process(pconf, process_handles, process_kill_events, + ¤t_live_processes, &child_num, signal_prefix_string) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "master_main: create child process failed. Exiting."); + shutdown_pending = 1; + goto die_now; + } + } + + /* service_set_status(SERVICE_RUNNING);*/ + restart_pending = shutdown_pending = 0; + + /* Wait for shutdown or restart events or for child death */ + process_handles[current_live_processes] = shutdown_event; + process_handles[current_live_processes+1] = restart_event; + rv = WaitForMultipleObjects(current_live_processes+2, (HANDLE *)process_handles, + FALSE, INFINITE); + if (rv == WAIT_FAILED) { + /* Something serious is wrong */ + ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, server_conf, + "master_main: : WaitForMultipeObjects on process handles and apache-signal -- doing shutdown"); + shutdown_pending = 1; + break; + } + if (rv == WAIT_TIMEOUT) { + /* Hey, this cannot happen */ + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT"); + shutdown_pending = 1; + } + + cld = rv - WAIT_OBJECT_0; +// APD4("main process: wait finished, cld=%d handle %d (max=%d)", cld, process_handles[cld], current_live_processes); + if (cld == current_live_processes) { + /* shutdown_event signalled */ + shutdown_pending = 1; + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, s, + "master_main: Shutdown event signaled. Shutting the server down."); + if (ResetEvent(shutdown_event) == 0) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, s, + "ResetEvent(shutdown_event)"); + } + /* Signal each child processes to die */ + for (i = 0; i < current_live_processes; i++) { +// APD3("master_main: signalling child %d, handle %d to die", i, process_handles[i]); + if (SetEvent(process_kill_events[i]) == 0) + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "master_main: SetEvent for child process in slot #%d failed", i); + } + break; + } else if (cld == current_live_processes+1) { + /* restart_event signalled */ + int children_to_kill = current_live_processes; + restart_pending = 1; + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, s, + "master_main: Restart event signaled. Doing a graceful restart."); + if (ResetEvent(restart_event) == 0) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, s, + "master_main: ResetEvent(restart_event) failed."); + } + /* Signal each child process to die */ + for (i = 0; i < children_to_kill; i++) { +// APD3("master_main: signalling child #%d handle %d to die", i, process_handles[i]); + if (SetEvent(process_kill_events[i]) == 0) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, s, + "master_main: SetEvent for child process in slot #%d failed", i); + /* Remove the process (and event) from the process table */ + cleanup_process(process_handles, process_kill_events, i, ¤t_live_processes); + } + processes_to_create = ap_daemons_to_start; + } else { + /* A child process must have exited because of MaxRequestPerChild being hit + * or a fatal error condition (seg fault, etc.). Remove the dead process + * from the process_handles and process_kill_events table and create a new + * child process. + * TODO: Consider restarting the child immediately without looping through http_main + * This will become necesasary if we ever support multiple children. + * One option, create a parent thread which waits on child death and restarts it. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "master_main: Child processed exited (due to MaxRequestsPerChild?). Restarting the child process."); + ap_assert(cld < current_live_processes); + cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes); +// APD2("main_process: child in slot %d died", rv); + remaining_children_to_start = 1; + continue; + } + + /* If we dropped out of the loop we definitly want to die completely. We need to + * make sure we wait for all the child process to exit first. + */ +die_now: + if (shutdown_pending) { + tmstart = time(NULL); + while (current_live_processes && ((tmstart+60) > time(NULL))) { + rv = WaitForMultipleObjects(current_live_processes, (HANDLE *)process_handles, FALSE, 2000); + if (rv == WAIT_TIMEOUT) + continue; + ap_assert(rv != WAIT_FAILED); + cld = rv - WAIT_OBJECT_0; + ap_assert(rv < current_live_processes); +// APD4("main_process: child in #%d handle %d died, left=%d", +// rv, process_handles[rv], current_live_processes); + cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes); + } + for (i = 0; i < current_live_processes; i++) { + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO, server_conf, + "forcing termination of child #%d (handle %d)", i, process_handles[i]); + TerminateProcess((HANDLE) process_handles[i], 1); + } + + } + + CloseHandle(restart_event); + CloseHandle(shutdown_event); + + if (pparent) { + ap_destroy_pool(pparent); + } + + return (0); +} + +/* + * winnt_pre_config() + * Gets called twice at startup. + */ +static void winnt_pre_config(pool *pconf, pool *plog, pool *ptemp) +{ + char *pid; + one_process = !!getenv("ONE_PROCESS"); + + /* Track parent/child pids... */ + pid = getenv("AP_PARENT_PID"); + if (pid) { + /* AP_PARENT_PID is only valid in the child */ + parent_pid = atoi(pid); + my_pid = getpid(); + } + else { + /* This is the parent... */ + parent_pid = my_pid = getpid(); + ap_log_pid(pconf, mpm_pid_fname); + } + + ap_listen_pre_config(); + ap_daemons_to_start = DEFAULT_NUM_DAEMON + ap_threads_per_child = DEFAULT_START_THREAD; + mpm_pid_fname = DEFAULT_PIDLOG; + max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; + + ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); +} + +/* +Need to register this hook if we want it... +*/ +static void winnt_post_config(pool *pconf, pool *plog, pool *ptemp, server_rec* server_conf) +{ + server_conf = server_conf; +} + +API_EXPORT(int) ap_mpm_run(pool *_pconf, pool *plog, server_rec *s ) +{ + + int child_num; + char* exit_event_name; + +// char signal_prefix_string[100]; + int i; + time_t tmstart; + HANDLE shutdown_event; /* used to signal shutdown to parent */ + HANDLE restart_event; /* used to signal a restart to parent */ + + pconf = _pconf; + server_conf = s; + + if ((parent_pid != my_pid) || one_process) { + /* Child process */ + child_num = atoi(getenv("AP_CHILD_NUM")); + exit_event_name = ap_psprintf(pconf, "ap_%d_C%d", parent_pid, child_num); + exit_event = open_event(exit_event_name); + setup_signal_names(ap_psprintf(pconf,"ap_%d", parent_pid)); + start_mutex = ap_open_mutex(signal_name_prefix); + ap_assert(start_mutex); + + worker_main(); + + + destroy_event(exit_event); + } + else { + /* Parent process */ + static int restart = 0; + PSECURITY_ATTRIBUTES sa = GetNullACL(); /* returns NULL if invalid (Win95?) */ + ap_clear_pool(plog); + ap_open_logs(server_conf, plog); + + if (!restart) { + /* service_set_status(SERVICE_START_PENDING);*/ + + setup_signal_names(ap_psprintf(pconf,"ap_%d", parent_pid)); + + /* Create shutdown event, apPID_shutdown, where PID is the parent + * Apache process ID. Shutdown is signaled by 'apache -k shutdown'. + */ + shutdown_event = CreateEvent(sa, TRUE, FALSE, signal_shutdown_name); + if (!shutdown_event) { + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, s, + "master_main: Cannot create shutdown event %s", signal_shutdown_name); + CleanNullACL((void *)sa); + exit(1); + } + + /* Create restart event, apPID_restart, where PID is the parent + * Apache process ID. Restart is signaled by 'apache -k restart'. + */ + restart_event = CreateEvent(sa, TRUE, FALSE, signal_restart_name); + if (!restart_event) { + CloseHandle(shutdown_event); + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, s, + "master_main: Cannot create restart event %s", signal_restart_name); + CleanNullACL((void *)sa); + exit(1); + } + CleanNullACL((void *)sa); + + /* Create the start mutex, apPID, where PID is the parent Apache process ID. + * Ths start mutex is used during a restart to prevent more than one + * child process from entering the accept loop at once. + */ + start_mutex = ap_create_mutex(signal_prefix_string); + /* TOTD: Add some code to detect failure */ + } + + /* Go to work... */ + restart = master_main(server_conf, shutdown_event, restart_event); + + if (!restart) { + const char *pidfile = NULL; + /* Shutting down. Clean up... */ + pidfile = ap_server_root_relative (pconf, mpm_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + server_conf, + "removed PID file %s (pid=%ld)", + pidfile, (long)getpid()); + ap_destroy_mutex(start_mutex); + + /* service_set_status(SERVICE_STOPPED); */ + } + + return !restart; +} + +static void winnt_hooks(void) +{ +// INIT_SIGLIST() + one_process = 0; + /* Configuration hooks implemented by http_config.c ... */ + ap_hook_pre_config(winnt_pre_config, NULL, NULL, HOOK_MIDDLE); + /* + ap_hook_post_config()...); + ap_hook_open_logs(xxx,NULL,NULL,HOOK_MIDDLE); + */ + +/* + ap_hook_translate_name(xxx,NULL,NULL,HOOK_REALLY_LAST); + ap_hook_process_connection(xxx,NULL,NULL, + HOOK_REALLY_LAST); + ap_hook_http_method(xxx,NULL,NULL,HOOK_REALLY_LAST); + ap_hook_default_port(xxx,NULL,NULL,HOOK_REALLY_LAST); + + /* FIXME: I suspect we can eliminate the need for these - Ben */ + ap_hook_type_checker(xxx,NULL,NULL,HOOK_REALLY_LAST); +*/ +} + + +/* + * Command processors + */ +static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (cmd->server->is_virtual) { + return "PidFile directive not allowed in "; + } + mpm_pid_fname = arg; + return NULL; +} + +static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_threads_per_child = atoi(arg); + if (ap_threads_per_child > HARD_THREAD_LIMIT) { + fprintf(stderr, "WARNING: ThreadsPerChild of %d exceeds compile time" + "limit of %d threads,\n", ap_threads_per_child, + HARD_THREAD_LIMIT); + fprintf(stderr, " lowering ThreadsPerChild to %d. To increase, please" + "see the\n", HARD_THREAD_LIMIT); + fprintf(stderr, " HARD_THREAD_LIMIT define in src/include/httpd.h.\n"); + } + else if (ap_threads_per_child < 1) { + fprintf(stderr, "WARNING: Require ThreadsPerChild > 0, setting to 1\n"); + ap_threads_per_child = 1; + } + return NULL; +} + + +static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + max_requests_per_child = atoi(arg); + + return NULL; +} + +static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg) +{ + struct stat finfo; + const char *fname; + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + fname = ap_server_root_relative(cmd->pool, arg); + /* ZZZ change this to the AP func FileInfo*/ + if ((stat(fname, &finfo) == -1) || !S_ISDIR(finfo.st_mode)) { + return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", fname, + " does not exist or is not a directory", NULL); + } + ap_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir)); + return NULL; +} + +static const command_rec winnt_cmds[] = { +LISTEN_COMMANDS +{ "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1, + "A file for logging the server process ID"}, +//{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1, +// "A file for Apache to maintain runtime process management information"}, +{ "ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF, TAKE1, + "Number of threads each child creates" }, +{ "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1, + "Maximum number of requests a particular child serves before dying." }, +{ "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1, + "The location of the directory Apache changes to before dumping core" }, +{ NULL } +}; + +module MODULE_VAR_EXPORT mpm_winnt_module = { + STANDARD20_MODULE_STUFF, + NULL, /* child_init */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + winnt_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* check auth */ + NULL, /* check access */ + winnt_hooks /* register_hooks */ +}; + +/* force Expat to be linked into the server executable */ +#if defined(USE_EXPAT) && !defined(SHARED_CORE_BOOTSTRAP) +#include "xmlparse.h" +const XML_LChar *suck_in_expat(void); +const XML_LChar *suck_in_expat(void) +{ + return XML_ErrorString(XML_ERROR_NONE); +} +#endif /* USE_EXPAT */ diff --git a/server/mpm/winnt/mpm_winnt.h b/server/mpm/winnt/mpm_winnt.h new file mode 100644 index 00000000000..1d987d71bb1 --- /dev/null +++ b/server/mpm/winnt/mpm_winnt.h @@ -0,0 +1,67 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_MPM_WINNT_H +#define APACHE_MPM_WINNT_H + +extern int ap_threads_per_child; +extern int ap_max_requests_per_child; +extern void clean_child_exit(int); +extern int ap_extended_status; +extern void clean_child_exit(int); + +#endif /* APACHE_MPM_WINNT_H */ diff --git a/server/mpm/winnt/service.c b/server/mpm/winnt/service.c new file mode 100644 index 00000000000..8108ae1ad93 --- /dev/null +++ b/server/mpm/winnt/service.c @@ -0,0 +1,431 @@ +#ifdef WIN32 + +#include +#include +#include +#include +#include + +#include "httpd.h" +#include "http_conf_globals.h" +#include "http_log.h" +#include "http_main.h" +#include "multithread.h" +#include "service.h" +#include "registry.h" + +static struct +{ + int (*main_fn)(int, char **); + event *stop_event; + int connected; + SERVICE_STATUS_HANDLE hServiceStatus; + char *name; + int exit_status; + SERVICE_STATUS ssStatus; + FILE *logFile; +} globdat; + +static void WINAPI service_main_fn(DWORD, LPTSTR *); +static void WINAPI service_ctrl(DWORD ctrlCode); +static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint); +static int ap_start_service(SC_HANDLE); +static int ap_stop_service(SC_HANDLE); + +int service_main(int (*main_fn)(int, char **), int argc, char **argv ) +{ + SERVICE_TABLE_ENTRY dispatchTable[] = + { + { "", service_main_fn }, + { NULL, NULL } + }; + + globdat.main_fn = main_fn; + globdat.stop_event = create_event(0, 0, "apache-signal"); + globdat.connected = 1; + + if(!StartServiceCtrlDispatcher(dispatchTable)) + { + /* This is a genuine failure of the SCM. */ + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "Error starting service control dispatcher"); + return(globdat.exit_status); + } + else + { + return(globdat.exit_status); + } +} + +void service_cd() +{ + /* change to the drive with the executable */ + char buf[300]; + GetModuleFileName(NULL, buf, 300); + buf[2] = 0; + chdir(buf); +} + +void __stdcall service_main_fn(DWORD argc, LPTSTR *argv) +{ + ap_server_argv0 = globdat.name = argv[0]; + + if(!(globdat.hServiceStatus = RegisterServiceCtrlHandler( globdat.name, service_ctrl))) + { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "Failure registering service handler"); + return; + } + + ReportStatusToSCMgr( + SERVICE_START_PENDING, // service state + NO_ERROR, // exit code + 3000); // wait hint + + service_cd(); + if( service_init() ) + /* Arguments are ok except for \! */ + globdat.exit_status = (*globdat.main_fn)( argc, argv ); + + ReportStatusToSCMgr(SERVICE_STOPPED, NO_ERROR, 0); + + return; +} + +void service_set_status(int status) +{ + ReportStatusToSCMgr(status, NO_ERROR, 3000); +} + + + +// +// FUNCTION: service_ctrl +// +// PURPOSE: This function is called by the SCM whenever +// ControlService() is called on this service. +// +// PARAMETERS: +// dwCtrlCode - type of control requested +// +// RETURN VALUE: +// none +// +// COMMENTS: +// +VOID WINAPI service_ctrl(DWORD dwCtrlCode) +{ + int state; + + + state = globdat.ssStatus.dwCurrentState; + switch(dwCtrlCode) + { + // Stop the service. + // + case SERVICE_CONTROL_STOP: + state = SERVICE_STOP_PENDING; + ap_start_shutdown(); + break; + + // Update the service status. + // + case SERVICE_CONTROL_INTERROGATE: + break; + + // invalid control code + // + default: + break; + + } + + ReportStatusToSCMgr(state, NO_ERROR, 0); +} + + +int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint) +{ + static int firstTime = 1; + static int checkPoint = 1; + int rv; + + if(firstTime) + { + firstTime = 0; + globdat.ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + globdat.ssStatus.dwServiceSpecificExitCode = 0; + globdat.ssStatus.dwCheckPoint = 1; + } + + if(globdat.connected) + { + if (currentState == SERVICE_START_PENDING) + globdat.ssStatus.dwControlsAccepted = 0; + else + globdat.ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + globdat.ssStatus.dwCurrentState = currentState; + globdat.ssStatus.dwWin32ExitCode = exitCode; + if(waitHint) + globdat.ssStatus.dwWaitHint = waitHint; + + if ( ( currentState == SERVICE_RUNNING ) || + ( currentState == SERVICE_STOPPED ) ) + { + globdat.ssStatus.dwWaitHint = 0; + globdat.ssStatus.dwCheckPoint = 0; + } + else + globdat.ssStatus.dwCheckPoint = ++checkPoint; + + rv = SetServiceStatus(globdat.hServiceStatus, &globdat.ssStatus); + + } + return(1); +} + +void InstallService(char *service_name, char *conf) +{ + SC_HANDLE schService; + SC_HANDLE schSCManager; + + TCHAR szPath[512]; + TCHAR szQuotedPath[512]; + + printf("Installing the %s service to use %s\n", service_name, conf); + + if (GetModuleFileName( NULL, szPath, 512 ) == 0) + { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "GetModuleFileName failed"); + return; + } + + ap_snprintf(szQuotedPath, 512, "\"%s\"", szPath); + + schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + ); + if (!schSCManager) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenSCManager failed"); + } + else { + schService = CreateService( + schSCManager, // SCManager database + service_name, // name of service + service_name, // name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_AUTO_START, // start type + SERVICE_ERROR_NORMAL, // error control type + szQuotedPath, // service's binary + NULL, // no load ordering group + NULL, // no tag identifier + NULL, // dependencies + NULL, // LocalSystem account + NULL); // no password + + if (schService) { + CloseServiceHandle(schService); + + /* Now store the server_root in the registry */ + if(!ap_registry_set_service_conf(conf, service_name)) + printf("The %s service has been installed successfully.\n", service_name ); + } + else { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "CreateService failed"); + } + + CloseServiceHandle(schSCManager); + } +} + + +void RemoveService(char *service_name) +{ + SC_HANDLE schService; + SC_HANDLE schSCManager; + + printf("Removing the %s service\n", service_name); + + schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + ); + if (!schSCManager) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenSCManager failed"); + } + else { + schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS); + + if (schService == NULL) { + /* Could not open the service */ + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenService failed"); + } + else { + /* try to stop the service */ + ap_stop_service(schService); + + // now remove the service + if (DeleteService(schService) == 0) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "DeleteService failed"); + else + printf("The %s service has been removed successfully.\n", service_name ); + CloseServiceHandle(schService); + } + /* SCM removes registry parameters */ + CloseServiceHandle(schSCManager); + } + +} + +/* A hack to determine if we're running as a service without waiting for + * the SCM to fail; if AllocConsole succeeds, we're a service. + */ + +BOOL isProcessService() { + if( !AllocConsole() ) + return FALSE; + FreeConsole(); + return TRUE; +} + +/* Determine is service_name is a valid service + */ + +BOOL isValidService(char *service_name) { + SC_HANDLE schSCM, schSVC; + int Err; + + if (!(schSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenSCManager failed"); + return FALSE; + } + + if ((schSVC = OpenService(schSCM, service_name, SERVICE_ALL_ACCESS))) { + CloseServiceHandle(schSVC); + CloseServiceHandle(schSCM); + return TRUE; + } + + Err = GetLastError(); + if (Err != ERROR_SERVICE_DOES_NOT_EXIST && Err != ERROR_INVALID_NAME) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenService failed"); + + return FALSE; +} + +int send_signal_to_service(char *service_name, char *sig) { + SC_HANDLE schService; + SC_HANDLE schSCManager; + int success = FALSE; + + enum { start, restart, stop, unknown } action; + static char *param[] = { "start", "restart", "shutdown" }; + static char *participle[] = { "starting", "restarting", "stopping" }; + static char *past[] = { "started", "restarted", "stopped" }; + + for (action = start; action < unknown; action++) + if (!strcasecmp(sig, param[action])) + break; + + if (action == unknown) { + printf("signal must be start, restart, or shutdown\n"); + return FALSE; + } + + schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + ); + if (!schSCManager) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenSCManager failed"); + } + else { + schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS); + + if (schService == NULL) { + /* Could not open the service */ + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenService failed"); + } + else { + if (!QueryServiceStatus(schService, &globdat.ssStatus)) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "QueryService failed"); + else { + if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED && action == stop) + printf("The %s service is not started.\n", service_name); + else if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING && action == start) + printf("The %s service has already been started.\n", service_name); + else { + printf("The %s service is %s.\n", service_name, participle[action]); + + if (action == stop || action == restart) + success = ap_stop_service(schService); + if (action == start || action == restart) + success = ap_start_service(schService); + + if( success ) + printf("The %s service has %s.\n", service_name, past[action]); + else + printf("Failed to %s the %s service.\n", sig, service_name ); + } + + CloseServiceHandle(schService); + } + } + /* SCM removes registry parameters */ + CloseServiceHandle(schSCManager); + } + return success; +} + +int ap_stop_service(SC_HANDLE schService) +{ + if (ControlService(schService, SERVICE_CONTROL_STOP, &globdat.ssStatus)) { + Sleep(1000); + while (QueryServiceStatus(schService, &globdat.ssStatus)) { + if (globdat.ssStatus.dwCurrentState == SERVICE_STOP_PENDING) + Sleep(1000); + else + break; + } + } + if (QueryServiceStatus(schService, &globdat.ssStatus)) + if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) + return TRUE; + return FALSE; +} + +int ap_start_service(SC_HANDLE schService) { + if (StartService(schService, 0, NULL)) { + Sleep(1000); + while(QueryServiceStatus(schService, &globdat.ssStatus)) { + if(globdat.ssStatus.dwCurrentState == SERVICE_START_PENDING) + Sleep(1000); + else + break; + } + } + if (QueryServiceStatus(schService, &globdat.ssStatus)) + if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING) + return TRUE; + return FALSE; +} + +#endif /* WIN32 */ + diff --git a/server/util.c b/server/util.c new file mode 100644 index 00000000000..b358501c077 --- /dev/null +++ b/server/util.c @@ -0,0 +1,2145 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * util.c: string utility things + * + * 3/21/93 Rob McCool + * 1995-96 Many changes by the Apache Group + * + */ + +/* Debugging aid: + * #define DEBUG to trace all cfg_open*()/cfg_closefile() calls + * #define DEBUG_CFG_LINES to trace every line read from the config files + */ + +#include "httpd.h" +#include "http_conf_globals.h" /* for user_id & group_id */ +#include "http_log.h" +#if defined(SUNOS4) +/* stdio.h has been read in ap_config.h already. Add missing prototypes here: */ +extern int fgetc(FILE *); +extern char *fgets(char *s, int, FILE*); +extern int fclose(FILE *); +#endif + +/* A bunch of functions in util.c scan strings looking for certain characters. + * To make that more efficient we encode a lookup table. The test_char_table + * is generated automatically by gen_test_char.c. + */ +#include "test_char.h" + +/* we assume the folks using this ensure 0 <= c < 256... which means + * you need a cast to (unsigned char) first, you can't just plug a + * char in here and get it to work, because if char is signed then it + * will first be sign extended. + */ +#define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f)) + +void ap_util_init(void) +{ + /* nothing to do... previously there was run-time initialization of + * test_char_table here + */ +} + + +API_VAR_EXPORT const char ap_month_snames[12][4] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +API_VAR_EXPORT const char ap_day_snames[7][4] = +{ + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +API_EXPORT(char *) ap_get_time() +{ + time_t t; + char *time_string; + + t = time(NULL); + time_string = ctime(&t); + time_string[strlen(time_string) - 1] = '\0'; + return (time_string); +} + +/* + * Examine a field value (such as a media-/content-type) string and return + * it sans any parameters; e.g., strip off any ';charset=foo' and the like. + */ +API_EXPORT(char *) ap_field_noparam(pool *p, const char *intype) +{ + const char *semi; + + semi = strchr(intype, ';'); + if (semi == NULL) { + return ap_pstrdup(p, intype); + } + else { + while ((semi > intype) && ap_isspace(semi[-1])) { + semi--; + } + return ap_pstrndup(p, intype, semi - intype); + } +} + +API_EXPORT(char *) ap_ht_time(pool *p, time_t t, const char *fmt, int gmt) +{ + char ts[MAX_STRING_LEN]; + char tf[MAX_STRING_LEN]; + struct tm *tms; + + tms = (gmt ? gmtime(&t) : localtime(&t)); + if(gmt) { + /* Convert %Z to "GMT" and %z to "+0000"; + * on hosts that do not have a time zone string in struct tm, + * strftime must assume its argument is local time. + */ + const char *f; + char *strp; + for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f) + ; f++, strp++) { + if (*f != '%') continue; + switch (f[1]) { + case '%': + *++strp = *++f; + break; + case 'Z': + *strp++ = 'G'; + *strp++ = 'M'; + *strp = 'T'; + f++; + break; + case 'z': /* common extension */ + *strp++ = '+'; + *strp++ = '0'; + *strp++ = '0'; + *strp++ = '0'; + *strp = '0'; + f++; + break; + } + } + *strp = '\0'; + fmt = tf; + } + + /* check return code? */ + strftime(ts, MAX_STRING_LEN, fmt, tms); + ts[MAX_STRING_LEN - 1] = '\0'; + return ap_pstrdup(p, ts); +} + +API_EXPORT(char *) ap_gm_timestr_822(pool *p, time_t sec) +{ + struct tm *tms; + + tms = gmtime(&sec); + + /* RFC date format; as strftime '%a, %d %b %Y %T GMT' */ + return ap_psprintf(p, + "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[tms->tm_wday], + tms->tm_mday, ap_month_snames[tms->tm_mon], tms->tm_year + 1900, + tms->tm_hour, tms->tm_min, tms->tm_sec); +} + +/* What a pain in the ass. */ +#if defined(HAVE_GMTOFF) +API_EXPORT(struct tm *) ap_get_gmtoff(int *tz) +{ + time_t tt = time(NULL); + struct tm *t; + + t = localtime(&tt); + *tz = (int) (t->tm_gmtoff / 60); + return t; +} +#else +API_EXPORT(struct tm *) ap_get_gmtoff(int *tz) +{ + time_t tt = time(NULL); + struct tm gmt; + struct tm *t; + int days, hours, minutes; + + /* Assume we are never more than 24 hours away. */ + gmt = *gmtime(&tt); /* remember gmtime/localtime return ptr to static */ + t = localtime(&tt); /* buffer... so be careful */ + days = t->tm_yday - gmt.tm_yday; + hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24) + + t->tm_hour - gmt.tm_hour); + minutes = hours * 60 + t->tm_min - gmt.tm_min; + *tz = minutes; + return t; +} +#endif + +/* Roy owes Rob beer. */ +/* Rob owes Roy dinner. */ + +/* These legacy comments would make a lot more sense if Roy hadn't + * replaced the old later_than() routine with util_date.c. + * + * Well, okay, they still wouldn't make any sense. + */ + +/* Match = 0, NoMatch = 1, Abort = -1 + * Based loosely on sections of wildmat.c by Rich Salz + * Hmmm... shouldn't this really go component by component? + */ +API_EXPORT(int) ap_strcmp_match(const char *str, const char *exp) +{ + int x, y; + + for (x = 0, y = 0; exp[y]; ++y, ++x) { + if ((!str[x]) && (exp[y] != '*')) + return -1; + if (exp[y] == '*') { + while (exp[++y] == '*'); + if (!exp[y]) + return 0; + while (str[x]) { + int ret; + if ((ret = ap_strcmp_match(&str[x++], &exp[y])) != 1) + return ret; + } + return -1; + } + else if ((exp[y] != '?') && (str[x] != exp[y])) + return 1; + } + return (str[x] != '\0'); +} + +API_EXPORT(int) ap_strcasecmp_match(const char *str, const char *exp) +{ + int x, y; + + for (x = 0, y = 0; exp[y]; ++y, ++x) { + if ((!str[x]) && (exp[y] != '*')) + return -1; + if (exp[y] == '*') { + while (exp[++y] == '*'); + if (!exp[y]) + return 0; + while (str[x]) { + int ret; + if ((ret = ap_strcasecmp_match(&str[x++], &exp[y])) != 1) + return ret; + } + return -1; + } + else if ((exp[y] != '?') && (ap_tolower(str[x]) != ap_tolower(exp[y]))) + return 1; + } + return (str[x] != '\0'); +} + +API_EXPORT(int) ap_is_matchexp(const char *str) +{ + register int x; + + for (x = 0; str[x]; x++) + if ((str[x] == '*') || (str[x] == '?')) + return 1; + return 0; +} + +/* + * Apache stub function for the regex libraries regexec() to make sure the + * whole regex(3) API is available through the Apache (exported) namespace. + * This is especially important for the DSO situations of modules. + * DO NOT MAKE A MACRO OUT OF THIS FUNCTION! + */ +API_EXPORT(int) ap_regexec(const regex_t *preg, const char *string, + size_t nmatch, regmatch_t pmatch[], int eflags) +{ + return regexec(preg, string, nmatch, pmatch, eflags); +} + +API_EXPORT(size_t) ap_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) +{ + return regerror(errcode, preg, errbuf, errbuf_size); +} + + +/* This function substitutes for $0-$9, filling in regular expression + * submatches. Pass it the same nmatch and pmatch arguments that you + * passed ap_regexec(). pmatch should not be greater than the maximum number + * of subexpressions - i.e. one more than the re_nsub member of regex_t. + * + * input should be the string with the $-expressions, source should be the + * string that was matched against. + * + * It returns the substituted string, or NULL on error. + * + * Parts of this code are based on Henry Spencer's regsub(), from his + * AT&T V8 regexp package. + */ + +API_EXPORT(char *) ap_pregsub(pool *p, const char *input, const char *source, + size_t nmatch, regmatch_t pmatch[]) +{ + const char *src = input; + char *dest, *dst; + char c; + size_t no; + int len; + + if (!source) + return NULL; + if (!nmatch) + return ap_pstrdup(p, src); + + /* First pass, find the size */ + + len = 0; + + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '$' && ap_isdigit(*src)) + no = *src++ - '0'; + else + no = 10; + + if (no > 9) { /* Ordinary character. */ + if (c == '\\' && (*src == '$' || *src == '&')) + c = *src++; + len++; + } + else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { + len += pmatch[no].rm_eo - pmatch[no].rm_so; + } + + } + + dest = dst = ap_pcalloc(p, len + 1); + + /* Now actually fill in the string */ + + src = input; + + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '$' && ap_isdigit(*src)) + no = *src++ - '0'; + else + no = 10; + + if (no > 9) { /* Ordinary character. */ + if (c == '\\' && (*src == '$' || *src == '&')) + c = *src++; + *dst++ = c; + } + else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { + len = pmatch[no].rm_eo - pmatch[no].rm_so; + memcpy(dst, source + pmatch[no].rm_so, len); + dst += len; + } + + } + *dst = '\0'; + + return dest; +} + +/* + * Parse .. so we don't compromise security + */ +API_EXPORT(void) ap_getparents(char *name) +{ + int l, w; + + /* Four paseses, as per RFC 1808 */ + /* a) remove ./ path segments */ + + for (l = 0, w = 0; name[l] != '\0';) { + if (name[l] == '.' && name[l + 1] == '/' && (l == 0 || name[l - 1] == '/')) + l += 2; + else + name[w++] = name[l++]; + } + + /* b) remove trailing . path, segment */ + if (w == 1 && name[0] == '.') + w--; + else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/') + w--; + name[w] = '\0'; + + /* c) remove all xx/../ segments. (including leading ../ and /../) */ + l = 0; + + while (name[l] != '\0') { + if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' && + (l == 0 || name[l - 1] == '/')) { + register int m = l + 3, n; + + l = l - 2; + if (l >= 0) { + while (l >= 0 && name[l] != '/') + l--; + l++; + } + else + l = 0; + n = l; + while ((name[n] = name[m])) + (++n, ++m); + } + else + ++l; + } + + /* d) remove trailing xx/.. segment. */ + if (l == 2 && name[0] == '.' && name[1] == '.') + name[0] = '\0'; + else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' && name[l - 3] == '/') { + l = l - 4; + if (l >= 0) { + while (l >= 0 && name[l] != '/') + l--; + l++; + } + else + l = 0; + name[l] = '\0'; + } +} + +API_EXPORT(void) ap_no2slash(char *name) +{ + char *d, *s; + + s = d = name; + +#ifdef WIN32 + /* Check for UNC names. Leave leading two slashes. */ + if (s[0] == '/' && s[1] == '/') + *d++ = *s++; +#endif + + while (*s) { + if ((*d++ = *s) == '/') { + do { + ++s; + } while (*s == '/'); + } + else { + ++s; + } + } + *d = '\0'; +} + + +/* + * copy at most n leading directories of s into d + * d should be at least as large as s plus 1 extra byte + * assumes n > 0 + * the return value is the ever useful pointer to the trailing \0 of d + * + * examples: + * /a/b, 1 ==> / + * /a/b, 2 ==> /a/ + * /a/b, 3 ==> /a/b/ + * /a/b, 4 ==> /a/b/ + */ +API_EXPORT(char *) ap_make_dirstr_prefix(char *d, const char *s, int n) +{ + for (;;) { + *d = *s; + if (*d == '\0') { + *d = '/'; + break; + } + if (*d == '/' && (--n) == 0) + break; + ++d; + ++s; + } + *++d = 0; + return (d); +} + + +/* + * return the parent directory name including trailing / of the file s + */ +API_EXPORT(char *) ap_make_dirstr_parent(pool *p, const char *s) +{ + char *last_slash = strrchr(s, '/'); + char *d; + int l; + + if (last_slash == NULL) { + /* XXX: well this is really broken if this happens */ + return (ap_pstrdup(p, "/")); + } + l = (last_slash - s) + 1; + d = ap_palloc(p, l + 1); + memcpy(d, s, l); + d[l] = 0; + return (d); +} + + +/* + * This function is deprecated. Use one of the preceeding two functions + * which are faster. + */ +API_EXPORT(char *) ap_make_dirstr(pool *p, const char *s, int n) +{ + register int x, f; + char *res; + + for (x = 0, f = 0; s[x]; x++) { + if (s[x] == '/') + if ((++f) == n) { + res = ap_palloc(p, x + 2); + memcpy(res, s, x); + res[x] = '/'; + res[x + 1] = '\0'; + return res; + } + } + + if (s[strlen(s) - 1] == '/') + return ap_pstrdup(p, s); + else + return ap_pstrcat(p, s, "/", NULL); +} + +API_EXPORT(int) ap_count_dirs(const char *path) +{ + register int x, n; + + for (x = 0, n = 0; path[x]; x++) + if (path[x] == '/') + n++; + return n; +} + + +API_EXPORT(void) ap_chdir_file(const char *file) +{ + const char *x; + char buf[HUGE_STRING_LEN]; + + x = strrchr(file, '/'); + if (x == NULL) { + chdir(file); + } + else if (x - file < sizeof(buf) - 1) { + memcpy(buf, file, x - file); + buf[x - file] = '\0'; + chdir(buf); + } + /* XXX: well, this is a silly function, no method of reporting an + * error... ah well. */ +} + +API_EXPORT(char *) ap_getword_nc(pool *atrans, char **line, char stop) +{ + return ap_getword(atrans, (const char **) line, stop); +} + +API_EXPORT(char *) ap_getword(pool *atrans, const char **line, char stop) +{ + char *pos = strchr(*line, stop); + char *res; + + if (!pos) { + res = ap_pstrdup(atrans, *line); + *line += strlen(*line); + return res; + } + + res = ap_pstrndup(atrans, *line, pos - *line); + + while (*pos == stop) { + ++pos; + } + + *line = pos; + + return res; +} + +API_EXPORT(char *) ap_getword_white_nc(pool *atrans, char **line) +{ + return ap_getword_white(atrans, (const char **) line); +} + +API_EXPORT(char *) ap_getword_white(pool *atrans, const char **line) +{ + int pos = -1, x; + char *res; + + for (x = 0; (*line)[x]; x++) { + if (ap_isspace((*line)[x])) { + pos = x; + break; + } + } + + if (pos == -1) { + res = ap_pstrdup(atrans, *line); + *line += strlen(*line); + return res; + } + + res = ap_palloc(atrans, pos + 1); + ap_cpystrn(res, *line, pos + 1); + + while (ap_isspace((*line)[pos])) + ++pos; + + *line += pos; + + return res; +} + +API_EXPORT(char *) ap_getword_nulls_nc(pool *atrans, char **line, char stop) +{ + return ap_getword_nulls(atrans, (const char **) line, stop); +} + +API_EXPORT(char *) ap_getword_nulls(pool *atrans, const char **line, char stop) +{ + char *pos = strchr(*line, stop); + char *res; + + if (!pos) { + res = ap_pstrdup(atrans, *line); + *line += strlen(*line); + return res; + } + + res = ap_pstrndup(atrans, *line, pos - *line); + + ++pos; + + *line = pos; + + return res; +} + +/* Get a word, (new) config-file style --- quoted strings and backslashes + * all honored + */ + +static char *substring_conf(pool *p, const char *start, int len, char quote) +{ + char *result = ap_palloc(p, len + 2); + char *resp = result; + int i; + + for (i = 0; i < len; ++i) { + if (start[i] == '\\' && (start[i + 1] == '\\' + || (quote && start[i + 1] == quote))) + *resp++ = start[++i]; + else + *resp++ = start[i]; + } + + *resp++ = '\0'; + return result; +} + +API_EXPORT(char *) ap_getword_conf_nc(pool *p, char **line) +{ + return ap_getword_conf(p, (const char **) line); +} + +API_EXPORT(char *) ap_getword_conf(pool *p, const char **line) +{ + const char *str = *line, *strend; + char *res; + char quote; + + while (*str && ap_isspace(*str)) + ++str; + + if (!*str) { + *line = str; + return ""; + } + + if ((quote = *str) == '"' || quote == '\'') { + strend = str + 1; + while (*strend && *strend != quote) { + if (*strend == '\\' && strend[1] && strend[1] == quote) + strend += 2; + else + ++strend; + } + res = substring_conf(p, str + 1, strend - str - 1, quote); + + if (*strend == quote) + ++strend; + } + else { + strend = str; + while (*strend && !ap_isspace(*strend)) + ++strend; + + res = substring_conf(p, str, strend - str, 0); + } + + while (*strend && ap_isspace(*strend)) + ++strend; + *line = strend; + return res; +} + +API_EXPORT(int) ap_cfg_closefile(configfile_t *cfp) +{ +#ifdef DEBUG + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, + "Done with config file %s", cfp->name); +#endif + return (cfp->close == NULL) ? 0 : cfp->close(cfp->param); +} + +/* Common structure that holds the file and pool for ap_pcfg_openfile */ +typedef struct { + struct pool *pool; + FILE *file; +} poolfile_t; + +static int cfg_close(void *param) +{ + poolfile_t *cfp = (poolfile_t *) param; + return (ap_pfclose(cfp->pool, cfp->file)); +} + +static int cfg_getch(void *param) +{ + poolfile_t *cfp = (poolfile_t *) param; + return (fgetc(cfp->file)); +} + +static void *cfg_getstr(void *buf, size_t bufsiz, void *param) +{ + poolfile_t *cfp = (poolfile_t *) param; + return (fgets(buf, bufsiz, cfp->file)); +} + +/* Open a configfile_t as FILE, return open configfile_t struct pointer */ +API_EXPORT(configfile_t *) ap_pcfg_openfile(pool *p, const char *name) +{ + configfile_t *new_cfg; + poolfile_t *new_pfile; + FILE *file; + struct stat stbuf; + int saved_errno; + + if (name == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL, + "Internal error: pcfg_openfile() called with NULL filename"); + return NULL; + } + + if (!ap_os_is_filename_valid(name)) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL, + "Access to config file %s denied: not a valid filename", + name); + errno = EACCES; + return NULL; + } + + file = ap_pfopen(p, name, "r"); +#ifdef DEBUG + saved_errno = errno; + ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, NULL, + "Opening config file %s (%s)", + name, (file == NULL) ? strerror(errno) : "successful"); + errno = saved_errno; +#endif + if (file == NULL) + return NULL; + + if (fstat(fileno(file), &stbuf) == 0 && + !S_ISREG(stbuf.st_mode) && +#if defined(WIN32) || defined(OS2) + !(strcasecmp(name, "nul") == 0 || + (strlen(name) >= 4 && + strcasecmp(name + strlen(name) - 4, "/nul") == 0))) { +#else + strcmp(name, "/dev/null") != 0) { +#endif /* WIN32 || OS2 */ + saved_errno = errno; + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL, + "Access to file %s denied by server: not a regular file", + name); + ap_pfclose(p, file); + errno = saved_errno; + return NULL; + } + + new_cfg = ap_palloc(p, sizeof(*new_cfg)); + new_pfile = ap_palloc(p, sizeof(*new_pfile)); + new_pfile->file = file; + new_pfile->pool = p; + new_cfg->param = new_pfile; + new_cfg->name = ap_pstrdup(p, name); + new_cfg->getch = (int (*)(void *)) cfg_getch; + new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr; + new_cfg->close = (int (*)(void *)) cfg_close; + new_cfg->line_number = 0; + return new_cfg; +} + + +/* Allocate a configfile_t handle with user defined functions and params */ +API_EXPORT(configfile_t *) ap_pcfg_open_custom(pool *p, const char *descr, + void *param, + int(*getch)(void *param), + void *(*getstr) (void *buf, size_t bufsiz, void *param), + int(*close_func)(void *param)) +{ + configfile_t *new_cfg = ap_palloc(p, sizeof(*new_cfg)); +#ifdef DEBUG + ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, NULL, "Opening config handler %s", descr); +#endif + new_cfg->param = param; + new_cfg->name = descr; + new_cfg->getch = getch; + new_cfg->getstr = getstr; + new_cfg->close = close_func; + new_cfg->line_number = 0; + return new_cfg; +} + + +/* Read one character from a configfile_t */ +API_EXPORT(int) ap_cfg_getc(configfile_t *cfp) +{ + register int ch = cfp->getch(cfp->param); + if (ch == LF) + ++cfp->line_number; + return ch; +} + + +/* Read one line from open configfile_t, strip LF, increase line number */ +/* If custom handler does not define a getstr() function, read char by char */ +API_EXPORT(int) ap_cfg_getline(char *buf, size_t bufsize, configfile_t *cfp) +{ + /* If a "get string" function is defined, use it */ + if (cfp->getstr != NULL) { + char *src, *dst; + char *cp; + char *cbuf = buf; + size_t cbufsize = bufsize; + + while (1) { + ++cfp->line_number; + if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL) + return 1; + + /* + * check for line continuation, + * i.e. match [^\\]\\[\r]\n only + */ + cp = cbuf; + while (cp < cbuf+cbufsize && *cp != '\0') + cp++; + if (cp > cbuf && cp[-1] == LF) { + cp--; + if (cp > cbuf && cp[-1] == CR) + cp--; + if (cp > cbuf && cp[-1] == '\\') { + cp--; + if (!(cp > cbuf && cp[-1] == '\\')) { + /* + * line continuation requested - + * then remove backslash and continue + */ + cbufsize -= (cp-cbuf); + cbuf = cp; + continue; + } + else { + /* + * no real continuation because escaped - + * then just remove escape character + */ + for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++) + cp[0] = cp[1]; + } + } + } + break; + } + + /* + * Leading and trailing white space is eliminated completely + */ + src = buf; + while (ap_isspace(*src)) + ++src; + /* blast trailing whitespace */ + dst = &src[strlen(src)]; + while (--dst >= src && ap_isspace(*dst)) + *dst = '\0'; + /* Zap leading whitespace by shifting */ + if (src != buf) + for (dst = buf; (*dst++ = *src++) != '\0'; ) + ; + +#ifdef DEBUG_CFG_LINES + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Read config: %s", buf); +#endif + return 0; + } else { + /* No "get string" function defined; read character by character */ + register int c; + register size_t i = 0; + + buf[0] = '\0'; + /* skip leading whitespace */ + do { + c = cfp->getch(cfp->param); + } while (c == '\t' || c == ' '); + + if (c == EOF) + return 1; + + if(bufsize < 2) { + /* too small, assume caller is crazy */ + return 1; + } + + while (1) { + if ((c == '\t') || (c == ' ')) { + buf[i++] = ' '; + while ((c == '\t') || (c == ' ')) + c = cfp->getch(cfp->param); + } + if (c == CR) { + /* silently ignore CR (_assume_ that a LF follows) */ + c = cfp->getch(cfp->param); + } + if (c == LF) { + /* increase line number and return on LF */ + ++cfp->line_number; + } + if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) { + /* + * check for line continuation + */ + if (i > 0 && buf[i-1] == '\\') { + i--; + if (!(i > 0 && buf[i-1] == '\\')) { + /* line is continued */ + c = cfp->getch(cfp->param); + continue; + } + /* else nothing needs be done because + * then the backslash is escaped and + * we just strip to a single one + */ + } + /* blast trailing whitespace */ + while (i > 0 && ap_isspace(buf[i - 1])) + --i; + buf[i] = '\0'; +#ifdef DEBUG_CFG_LINES + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Read config: %s", buf); +#endif + return 0; + } + buf[i] = c; + ++i; + c = cfp->getch(cfp->param); + } + } +} + +/* Size an HTTP header field list item, as separated by a comma. + * The return value is a pointer to the beginning of the non-empty list item + * within the original string (or NULL if there is none) and the address + * of field is shifted to the next non-comma, non-whitespace character. + * len is the length of the item excluding any beginning whitespace. + */ +API_EXPORT(const char *) ap_size_list_item(const char **field, int *len) +{ + const unsigned char *ptr = (const unsigned char *)*field; + const unsigned char *token; + int in_qpair, in_qstr, in_com; + + /* Find first non-comma, non-whitespace byte */ + + while (*ptr == ',' || ap_isspace(*ptr)) + ++ptr; + + token = ptr; + + /* Find the end of this item, skipping over dead bits */ + + for (in_qpair = in_qstr = in_com = 0; + *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); + ++ptr) { + + if (in_qpair) { + in_qpair = 0; + } + else { + switch (*ptr) { + case '\\': in_qpair = 1; /* quoted-pair */ + break; + case '"' : if (!in_com) /* quoted string delim */ + in_qstr = !in_qstr; + break; + case '(' : if (!in_qstr) /* comment (may nest) */ + ++in_com; + break; + case ')' : if (in_com) /* end comment */ + --in_com; + break; + default : break; + } + } + } + + if ((*len = (ptr - token)) == 0) { + *field = (const char *)ptr; + return NULL; + } + + /* Advance field pointer to the next non-comma, non-white byte */ + + while (*ptr == ',' || ap_isspace(*ptr)) + ++ptr; + + *field = (const char *)ptr; + return (const char *)token; +} + +/* Retrieve an HTTP header field list item, as separated by a comma, + * while stripping insignificant whitespace and lowercasing anything not in + * a quoted string or comment. The return value is a new string containing + * the converted list item (or NULL if none) and the address pointed to by + * field is shifted to the next non-comma, non-whitespace. + */ +API_EXPORT(char *) ap_get_list_item(pool *p, const char **field) +{ + const char *tok_start; + const unsigned char *ptr; + unsigned char *pos; + char *token; + int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0; + + /* Find the beginning and maximum length of the list item so that + * we can allocate a buffer for the new string and reset the field. + */ + if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) { + return NULL; + } + token = ap_palloc(p, tok_len + 1); + + /* Scan the token again, but this time copy only the good bytes. + * We skip extra whitespace and any whitespace around a '=', '/', + * or ';' and lowercase normal characters not within a comment, + * quoted-string or quoted-pair. + */ + for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token; + *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); + ++ptr) { + + if (in_qpair) { + in_qpair = 0; + *pos++ = *ptr; + } + else { + switch (*ptr) { + case '\\': in_qpair = 1; + if (addspace == 1) + *pos++ = ' '; + *pos++ = *ptr; + addspace = 0; + break; + case '"' : if (!in_com) + in_qstr = !in_qstr; + if (addspace == 1) + *pos++ = ' '; + *pos++ = *ptr; + addspace = 0; + break; + case '(' : if (!in_qstr) + ++in_com; + if (addspace == 1) + *pos++ = ' '; + *pos++ = *ptr; + addspace = 0; + break; + case ')' : if (in_com) + --in_com; + *pos++ = *ptr; + addspace = 0; + break; + case ' ' : + case '\t': if (addspace) + break; + if (in_com || in_qstr) + *pos++ = *ptr; + else + addspace = 1; + break; + case '=' : + case '/' : + case ';' : if (!(in_com || in_qstr)) + addspace = -1; + *pos++ = *ptr; + break; + default : if (addspace == 1) + *pos++ = ' '; + *pos++ = (in_com || in_qstr) ? *ptr + : ap_tolower(*ptr); + addspace = 0; + break; + } + } + } + *pos = '\0'; + + return token; +} + +/* Find an item in canonical form (lowercase, no extra spaces) within + * an HTTP field value list. Returns 1 if found, 0 if not found. + * This would be much more efficient if we stored header fields as + * an array of list items as they are received instead of a plain string. + */ +API_EXPORT(int) ap_find_list_item(pool *p, const char *line, const char *tok) +{ + const unsigned char *pos; + const unsigned char *ptr = (const unsigned char *)line; + int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0; + + if (!line || !tok) + return 0; + + do { /* loop for each item in line's list */ + + /* Find first non-comma, non-whitespace byte */ + + while (*ptr == ',' || ap_isspace(*ptr)) + ++ptr; + + if (*ptr) + good = 1; /* until proven otherwise for this item */ + else + break; /* no items left and nothing good found */ + + /* We skip extra whitespace and any whitespace around a '=', '/', + * or ';' and lowercase normal characters not within a comment, + * quoted-string or quoted-pair. + */ + for (pos = (const unsigned char *)tok; + *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); + ++ptr) { + + if (in_qpair) { + in_qpair = 0; + if (good) + good = (*pos++ == *ptr); + } + else { + switch (*ptr) { + case '\\': in_qpair = 1; + if (addspace == 1) + good = good && (*pos++ == ' '); + good = good && (*pos++ == *ptr); + addspace = 0; + break; + case '"' : if (!in_com) + in_qstr = !in_qstr; + if (addspace == 1) + good = good && (*pos++ == ' '); + good = good && (*pos++ == *ptr); + addspace = 0; + break; + case '(' : if (!in_qstr) + ++in_com; + if (addspace == 1) + good = good && (*pos++ == ' '); + good = good && (*pos++ == *ptr); + addspace = 0; + break; + case ')' : if (in_com) + --in_com; + good = good && (*pos++ == *ptr); + addspace = 0; + break; + case ' ' : + case '\t': if (addspace || !good) + break; + if (in_com || in_qstr) + good = (*pos++ == *ptr); + else + addspace = 1; + break; + case '=' : + case '/' : + case ';' : if (!(in_com || in_qstr)) + addspace = -1; + good = good && (*pos++ == *ptr); + break; + default : if (!good) + break; + if (addspace == 1) + good = (*pos++ == ' '); + if (in_com || in_qstr) + good = good && (*pos++ == *ptr); + else + good = good && (*pos++ == ap_tolower(*ptr)); + addspace = 0; + break; + } + } + } + if (good && *pos) + good = 0; /* not good if only a prefix was matched */ + + } while (*ptr && !good); + + return good; +} + + +/* Retrieve a token, spacing over it and returning a pointer to + * the first non-white byte afterwards. Note that these tokens + * are delimited by semis and commas; and can also be delimited + * by whitespace at the caller's option. + */ + +API_EXPORT(char *) ap_get_token(pool *p, const char **accept_line, int accept_white) +{ + const char *ptr = *accept_line; + const char *tok_start; + char *token; + int tok_len; + + /* Find first non-white byte */ + + while (*ptr && ap_isspace(*ptr)) + ++ptr; + + tok_start = ptr; + + /* find token end, skipping over quoted strings. + * (comments are already gone). + */ + + while (*ptr && (accept_white || !ap_isspace(*ptr)) + && *ptr != ';' && *ptr != ',') { + if (*ptr++ == '"') + while (*ptr) + if (*ptr++ == '"') + break; + } + + tok_len = ptr - tok_start; + token = ap_pstrndup(p, tok_start, tok_len); + + /* Advance accept_line pointer to the next non-white byte */ + + while (*ptr && ap_isspace(*ptr)) + ++ptr; + + *accept_line = ptr; + return token; +} + + +/* find http tokens, see the definition of token from RFC2068 */ +API_EXPORT(int) ap_find_token(pool *p, const char *line, const char *tok) +{ + const unsigned char *start_token; + const unsigned char *s; + + if (!line) + return 0; + + s = (const unsigned char *)line; + for (;;) { + /* find start of token, skip all stop characters, note NUL + * isn't a token stop, so we don't need to test for it + */ + while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) { + ++s; + } + if (!*s) { + return 0; + } + start_token = s; + /* find end of the token */ + while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) { + ++s; + } + if (!strncasecmp((const char *)start_token, (const char *)tok, s - start_token)) { + return 1; + } + if (!*s) { + return 0; + } + } +} + + +API_EXPORT(int) ap_find_last_token(pool *p, const char *line, const char *tok) +{ + int llen, tlen, lidx; + + if (!line) + return 0; + + llen = strlen(line); + tlen = strlen(tok); + lidx = llen - tlen; + + if ((lidx < 0) || + ((lidx > 0) && !(ap_isspace(line[lidx - 1]) || line[lidx - 1] == ','))) + return 0; + + return (strncasecmp(&line[lidx], tok, tlen) == 0); +} + +API_EXPORT(char *) ap_escape_shell_cmd(pool *p, const char *str) +{ + char *cmd; + unsigned char *d; + const unsigned char *s; + + cmd = ap_palloc(p, 2 * strlen(str) + 1); /* Be safe */ + d = (unsigned char *)cmd; + s = (const unsigned char *)str; + for (; *s; ++s) { + +#if defined(OS2) || defined(WIN32) + /* Don't allow '&' in parameters under OS/2. */ + /* This can be used to send commands to the shell. */ + if (*s == '&') { + *d++ = ' '; + continue; + } +#endif + + if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) { + *d++ = '\\'; + } + *d++ = *s; + } + *d = '\0'; + + return cmd; +} + +static char x2c(const char *what) +{ + register char digit; + +#ifndef CHARSET_EBCDIC + digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + digit *= 16; + digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); +#else /*CHARSET_EBCDIC*/ + char xstr[5]; + xstr[0]='0'; + xstr[1]='x'; + xstr[2]=what[0]; + xstr[3]=what[1]; + xstr[4]='\0'; + digit = os_toebcdic[0xFF & strtol(xstr, NULL, 16)]; +#endif /*CHARSET_EBCDIC*/ + return (digit); +} + +/* + * Unescapes a URL. + * Returns 0 on success, non-zero on error + * Failure is due to + * bad % escape returns BAD_REQUEST + * + * decoding %00 -> \0 + * decoding %2f -> / (a special character) + * returns NOT_FOUND + */ +API_EXPORT(int) ap_unescape_url(char *url) +{ + register int x, y, badesc, badpath; + + badesc = 0; + badpath = 0; + for (x = 0, y = 0; url[y]; ++x, ++y) { + if (url[y] != '%') + url[x] = url[y]; + else { + if (!ap_isxdigit(url[y + 1]) || !ap_isxdigit(url[y + 2])) { + badesc = 1; + url[x] = '%'; + } + else { + url[x] = x2c(&url[y + 1]); + y += 2; + if (url[x] == '/' || url[x] == '\0') + badpath = 1; + } + } + } + url[x] = '\0'; + if (badesc) + return BAD_REQUEST; + else if (badpath) + return NOT_FOUND; + else + return OK; +} + +API_EXPORT(char *) ap_construct_server(pool *p, const char *hostname, + unsigned port, const request_rec *r) +{ + if (ap_is_default_port(port, r)) + return ap_pstrdup(p, hostname); + else { + return ap_psprintf(p, "%s:%u", hostname, port); + } +} + +/* c2x takes an unsigned, and expects the caller has guaranteed that + * 0 <= what < 256... which usually means that you have to cast to + * unsigned char first, because (unsigned)(char)(x) fist goes through + * signed extension to an int before the unsigned cast. + * + * The reason for this assumption is to assist gcc code generation -- + * the unsigned char -> unsigned extension is already done earlier in + * both uses of this code, so there's no need to waste time doing it + * again. + */ +static const char c2x_table[] = "0123456789abcdef"; + +static ap_inline unsigned char *c2x(unsigned what, unsigned char *where) +{ + *where++ = '%'; + *where++ = c2x_table[what >> 4]; + *where++ = c2x_table[what & 0xf]; + return where; +} + +/* + * escape_path_segment() escapes a path segment, as defined in RFC 1808. This + * routine is (should be) OS independent. + * + * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all + * cases if a ':' occurs before the first '/' in the URL, the URL should be + * prefixed with "./" (or the ':' escaped). In the case of Unix, this means + * leaving '/' alone, but otherwise doing what escape_path_segment() does. For + * efficiency reasons, we don't use escape_path_segment(), which is provided for + * reference. Again, RFC 1808 is where this stuff is defined. + * + * If partial is set, os_escape_path() assumes that the path will be appended to + * something with a '/' in it (and thus does not prefix "./"). + */ + +API_EXPORT(char *) ap_escape_path_segment(pool *p, const char *segment) +{ + char *copy = ap_palloc(p, 3 * strlen(segment) + 1); + const unsigned char *s = (const unsigned char *)segment; + unsigned char *d = (unsigned char *)copy; + unsigned c; + + while ((c = *s)) { + if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) { + d = c2x(c, d); + } + else { + *d++ = c; + } + ++s; + } + *d = '\0'; + return copy; +} + +API_EXPORT(char *) ap_os_escape_path(pool *p, const char *path, int partial) +{ + char *copy = ap_palloc(p, 3 * strlen(path) + 3); + const unsigned char *s = (const unsigned char *)path; + unsigned char *d = (unsigned char *)copy; + unsigned c; + + if (!partial) { + char *colon = strchr(path, ':'); + char *slash = strchr(path, '/'); + + if (colon && (!slash || colon < slash)) { + *d++ = '.'; + *d++ = '/'; + } + } + while ((c = *s)) { + if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) { + d = c2x(c, d); + } + else { + *d++ = c; + } + ++s; + } + *d = '\0'; + return copy; +} + +/* ap_escape_uri is now a macro for os_escape_path */ + +API_EXPORT(char *) ap_escape_html(pool *p, const char *s) +{ + int i, j; + char *x; + + /* first, count the number of extra characters */ + for (i = 0, j = 0; s[i] != '\0'; i++) + if (s[i] == '<' || s[i] == '>') + j += 3; + else if (s[i] == '&') + j += 4; + + if (j == 0) + return ap_pstrndup(p, s, i); + + x = ap_palloc(p, i + j + 1); + for (i = 0, j = 0; s[i] != '\0'; i++, j++) + if (s[i] == '<') { + memcpy(&x[j], "<", 4); + j += 3; + } + else if (s[i] == '>') { + memcpy(&x[j], ">", 4); + j += 3; + } + else if (s[i] == '&') { + memcpy(&x[j], "&", 5); + j += 4; + } + else + x[j] = s[i]; + + x[j] = '\0'; + return x; +} + +API_EXPORT(int) ap_is_directory(const char *path) +{ + struct stat finfo; + + if (stat(path, &finfo) == -1) + return 0; /* in error condition, just return no */ + + return (S_ISDIR(finfo.st_mode)); +} + +API_EXPORT(char *) ap_make_full_path(pool *a, const char *src1, + const char *src2) +{ + register int x; + + x = strlen(src1); + if (x == 0) + return ap_pstrcat(a, "/", src2, NULL); + + if (src1[x - 1] != '/') + return ap_pstrcat(a, src1, "/", src2, NULL); + else + return ap_pstrcat(a, src1, src2, NULL); +} + +/* + * Check for an absoluteURI syntax (see section 3.2 in RFC2068). + */ +API_EXPORT(int) ap_is_url(const char *u) +{ + register int x; + + for (x = 0; u[x] != ':'; x++) { + if ((!u[x]) || + ((!ap_isalpha(u[x])) && (!ap_isdigit(u[x])) && + (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) { + return 0; + } + } + + return (x ? 1 : 0); /* If the first character is ':', it's broken, too */ +} + +API_EXPORT(int) ap_can_exec(const struct stat *finfo) +{ +#ifdef MULTIPLE_GROUPS + int cnt; +#endif +#if defined(OS2) || defined(WIN32) + /* OS/2 dosen't have Users and Groups */ + return 1; +#else + if (ap_user_id == finfo->st_uid) + if (finfo->st_mode & S_IXUSR) + return 1; + if (ap_group_id == finfo->st_gid) + if (finfo->st_mode & S_IXGRP) + return 1; +#ifdef MULTIPLE_GROUPS + for (cnt = 0; cnt < NGROUPS_MAX; cnt++) { + if (group_id_list[cnt] == finfo->st_gid) + if (finfo->st_mode & S_IXGRP) + return 1; + } +#endif + return (finfo->st_mode & S_IXOTH); +#endif +} + +#ifdef NEED_STRDUP +char *strdup(const char *str) +{ + char *sdup; + + if (!(sdup = (char *) malloc(strlen(str) + 1))) { + fprintf(stderr, "Ouch! Out of memory in our strdup()!\n"); + return NULL; + } + sdup = strcpy(sdup, str); + + return sdup; +} +#endif + +/* The following two routines were donated for SVR4 by Andreas Vogel */ +#ifdef NEED_STRCASECMP +int strcasecmp(const char *a, const char *b) +{ + const char *p = a; + const char *q = b; + for (p = a, q = b; *p && *q; p++, q++) { + int diff = ap_tolower(*p) - ap_tolower(*q); + if (diff) + return diff; + } + if (*p) + return 1; /* p was longer than q */ + if (*q) + return -1; /* p was shorter than q */ + return 0; /* Exact match */ +} + +#endif + +#ifdef NEED_STRNCASECMP +int strncasecmp(const char *a, const char *b, int n) +{ + const char *p = a; + const char *q = b; + + for (p = a, q = b; /*NOTHING */ ; p++, q++) { + int diff; + if (p == a + n) + return 0; /* Match up to n characters */ + if (!(*p && *q)) + return *p - *q; + diff = ap_tolower(*p) - ap_tolower(*q); + if (diff) + return diff; + } + /*NOTREACHED */ +} +#endif + +/* The following routine was donated for UTS21 by dwd@bell-labs.com */ +#ifdef NEED_STRSTR +char *strstr(char *s1, char *s2) +{ + char *p1, *p2; + if (*s2 == '\0') { + /* an empty s2 */ + return(s1); + } + while((s1 = strchr(s1, *s2)) != NULL) { + /* found first character of s2, see if the rest matches */ + p1 = s1; + p2 = s2; + while (*++p1 == *++p2) { + if (*p1 == '\0') { + /* both strings ended together */ + return(s1); + } + } + if (*p2 == '\0') { + /* second string ended, a match */ + break; + } + /* didn't find a match here, try starting at next character in s1 */ + s1++; + } + return(s1); +} +#endif + +#ifdef NEED_INITGROUPS +int initgroups(const char *name, gid_t basegid) +{ +#if defined(QNX) || defined(MPE) || defined(BEOS) || defined(_OSD_POSIX) || defined(TPF) || defined(__TANDEM) +/* QNX, MPE and BeOS do not appear to support supplementary groups. */ + return 0; +#else /* ndef QNX */ + gid_t groups[NGROUPS_MAX]; + struct group *g; + int index = 0; + + setgrent(); + + groups[index++] = basegid; + + while (index < NGROUPS_MAX && ((g = getgrent()) != NULL)) + if (g->gr_gid != basegid) { + char **names; + + for (names = g->gr_mem; *names != NULL; ++names) + if (!strcmp(*names, name)) + groups[index++] = g->gr_gid; + } + + endgrent(); + + return setgroups(index, groups); +#endif /* def QNX */ +} +#endif /* def NEED_INITGROUPS */ + +#ifdef NEED_WAITPID +/* From ikluft@amdahl.com + * this is not ideal but it works for SVR3 variants + * Modified by dwd@bell-labs.com to call wait3 instead of wait because + * apache started to use the WNOHANG option. + */ +int waitpid(pid_t pid, int *statusp, int options) +{ + int tmp_pid; + if (kill(pid, 0) == -1) { + errno = ECHILD; + return -1; + } + while (((tmp_pid = wait3(statusp, options, 0)) != pid) && + (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1)) + ; + return tmp_pid; +} +#endif + +API_EXPORT(int) ap_ind(const char *s, char c) +{ + register int x; + + for (x = 0; s[x]; x++) + if (s[x] == c) + return x; + + return -1; +} + +API_EXPORT(int) ap_rind(const char *s, char c) +{ + register int x; + + for (x = strlen(s) - 1; x != -1; x--) + if (s[x] == c) + return x; + + return -1; +} + +API_EXPORT(void) ap_str_tolower(char *str) +{ + while (*str) { + *str = ap_tolower(*str); + ++str; + } +} + +API_EXPORT(uid_t) ap_uname2id(const char *name) +{ +#ifdef WIN32 + return (1); +#else + struct passwd *ent; + + if (name[0] == '#') + return (atoi(&name[1])); + + if (!(ent = getpwnam(name))) { + fprintf(stderr, "%s: bad user name %s\n", ap_server_argv0, name); + exit(1); + } + return (ent->pw_uid); +#endif +} + +API_EXPORT(gid_t) ap_gname2id(const char *name) +{ +#ifdef WIN32 + return (1); +#else + struct group *ent; + + if (name[0] == '#') + return (atoi(&name[1])); + + if (!(ent = getgrnam(name))) { + fprintf(stderr, "%s: bad group name %s\n", ap_server_argv0, name); + exit(1); + } + return (ent->gr_gid); +#endif +} + + +/* + * Parses a host of the form

[:port] + * :port is permitted if 'port' is not NULL + */ +unsigned long ap_get_virthost_addr(char *w, unsigned short *ports) +{ + struct hostent *hep; + unsigned long my_addr; + char *p; + + p = strchr(w, ':'); + if (ports != NULL) { + *ports = 0; + if (p != NULL && strcmp(p + 1, "*") != 0) + *ports = atoi(p + 1); + } + + if (p != NULL) + *p = '\0'; + if (strcmp(w, "*") == 0) { + if (p != NULL) + *p = ':'; + return htonl(INADDR_ANY); + } + + my_addr = ap_inet_addr((char *)w); + if (my_addr != INADDR_NONE) { + if (p != NULL) + *p = ':'; + return my_addr; + } + + hep = gethostbyname(w); + + if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { + fprintf(stderr, "Cannot resolve host name %s --- exiting!\n", w); + exit(1); + } + + if (hep->h_addr_list[1]) { + fprintf(stderr, "Host %s has multiple addresses ---\n", w); + fprintf(stderr, "you must choose one explicitly for use as\n"); + fprintf(stderr, "a virtual host. Exiting!!!\n"); + exit(1); + } + + if (p != NULL) + *p = ':'; + + return ((struct in_addr *) (hep->h_addr))->s_addr; +} + + +static char *find_fqdn(pool *a, struct hostent *p) +{ + int x; + + if (!strchr(p->h_name, '.')) { + for (x = 0; p->h_aliases[x]; ++x) { + if (strchr(p->h_aliases[x], '.') && + (!strncasecmp(p->h_aliases[x], p->h_name, strlen(p->h_name)))) + return ap_pstrdup(a, p->h_aliases[x]); + } + return NULL; + } + return ap_pstrdup(a, (void *) p->h_name); +} + +char *ap_get_local_host(pool *a) +{ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + char str[MAXHOSTNAMELEN + 1]; + char *server_hostname; + struct hostent *p; + +#ifdef BEOS /* BeOS returns zero as an error for gethostname */ + if (gethostname(str, sizeof(str) - 1) == 0) { +#else + if (gethostname(str, sizeof(str) - 1) != 0) { +#endif /* BeOS */ + perror("Unable to gethostname"); + exit(1); + } + str[MAXHOSTNAMELEN] = '\0'; + if ((!(p = gethostbyname(str))) || (!(server_hostname = find_fqdn(a, p)))) { + fprintf(stderr, "%s: cannot determine local host name.\n", + ap_server_argv0); + fprintf(stderr, "Use the ServerName directive to set it manually.\n"); + exit(1); + } + + return server_hostname; +} + +/* simple 'pool' alloc()ing glue to ap_base64.c + */ +API_EXPORT(char *) ap_pbase64decode(pool *p, const char *bufcoded) +{ + char *decoded; + int l; + + decoded = (char *) ap_palloc(p, 1 + ap_base64decode_len(bufcoded)); + l = ap_base64decode(decoded, bufcoded); + decoded[l] = '\0'; /* make binary sequence into string */ + + return decoded; +} + +API_EXPORT(char *) ap_pbase64encode(pool *p, char *string) +{ + char *encoded; + int l = strlen(string); + + encoded = (char *) ap_palloc(p, 1 + ap_base64encode_len(l)); + l = ap_base64encode(encoded, string, l); + encoded[l] = '\0'; /* make binary sequence into string */ + + return encoded; +} + +/* deprecated names for the above two functions, here for compatibility + */ +API_EXPORT(char *) ap_uudecode(pool *p, const char *bufcoded) +{ + return ap_pbase64decode(p, bufcoded); +} + +API_EXPORT(char *) ap_uuencode(pool *p, char *string) +{ + return ap_pbase64encode(p, string); +} + +#ifdef OS2 +void os2pathname(char *path) +{ + char newpath[MAX_STRING_LEN]; + int loop; + int offset; + + offset = 0; + for (loop = 0; loop < (strlen(path) + 1) && loop < sizeof(newpath) - 1; loop++) { + if (path[loop] == '/') { + newpath[offset] = '\\'; + /* + offset = offset + 1; + newpath[offset] = '\\'; + */ + } + else + newpath[offset] = path[loop]; + offset = offset + 1; + }; + /* Debugging code */ + /* fprintf(stderr, "%s \n", newpath); */ + + strcpy(path, newpath); +}; + +/* quotes in the string are doubled up. + * Used to escape quotes in args passed to OS/2's cmd.exe + */ +char *ap_double_quotes(pool *p, char *str) +{ + int num_quotes = 0; + int len = 0; + char *quote_doubled_str, *dest; + + while (str[len]) { + num_quotes += str[len++] == '\"'; + } + + quote_doubled_str = ap_palloc(p, len + num_quotes + 1); + dest = quote_doubled_str; + + while (*str) { + if (*str == '\"') + *(dest++) = '\"'; + *(dest++) = *(str++); + } + + *dest = 0; + return quote_doubled_str; +} +#endif + + +#ifdef NEED_STRERROR +char * + strerror(int err) +{ + + char *p; + extern char *const sys_errlist[]; + + p = sys_errlist[err]; + return (p); +} +#endif + +#if defined(NEED_DIFFTIME) +double difftime(time_t time1, time_t time0) +{ + return (time1 - time0); +} +#endif + +/* we want to downcase the type/subtype for comparison purposes + * but nothing else because ;parameter=foo values are case sensitive. + * XXX: in truth we want to downcase parameter names... but really, + * apache has never handled parameters and such correctly. You + * also need to compress spaces and such to be able to compare + * properly. -djg + */ +API_EXPORT(void) ap_content_type_tolower(char *str) +{ + char *semi; + + semi = strchr(str, ';'); + if (semi) { + *semi = '\0'; + } + while (*str) { + *str = ap_tolower(*str); + ++str; + } + if (semi) { + *semi = ';'; + } +} + +/* + * Given a string, replace any bare " with \" . + */ +API_EXPORT(char *) ap_escape_quotes (pool *p, const char *instring) +{ + int newlen = 0; + const char *inchr = instring; + char *outchr, *outstring; + + /* + * Look through the input string, jogging the length of the output + * string up by an extra byte each time we find an unescaped ". + */ + while (*inchr != '\0') { + newlen++; + if (*inchr == '"') { + newlen++; + } + /* + * If we find a slosh, and it's not the last byte in the string, + * it's escaping something - advance past both bytes. + */ + if ((*inchr == '\\') && (inchr[1] != '\0')) { + inchr++; + newlen++; + } + inchr++; + } + outstring = ap_palloc(p, newlen + 1); + inchr = instring; + outchr = outstring; + /* + * Now copy the input string to the output string, inserting a slosh + * in front of every " that doesn't already have one. + */ + while (*inchr != '\0') { + if ((*inchr == '\\') && (inchr[1] != '\0')) { + *outchr++ = *inchr++; + *outchr++ = *inchr++; + } + if (*inchr == '"') { + *outchr++ = '\\'; + } + if (*inchr != '\0') { + *outchr++ = *inchr++; + } + } + *outchr = '\0'; + return outstring; +} diff --git a/server/util_cfgtree.c b/server/util_cfgtree.c new file mode 100644 index 00000000000..cc82e1c1854 --- /dev/null +++ b/server/util_cfgtree.c @@ -0,0 +1,89 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +#define CORE_PRIVATE +#include "util_cfgtree.h" +#include + +ap_directive_t *conf_tree; + +ap_directive_t *ap_add_node(ap_directive_t **parent, ap_directive_t *current, + ap_directive_t *toadd, int child) +{ + if (current == NULL) { + /* we just started a new parent */ + if (*parent == NULL) { + /* no parent, no current: root of tree */ + conf_tree = toadd; + } + else { + (*parent)->first_child = toadd; + } + return toadd; + } + current->next = toadd; + toadd->parent = *parent; + if (child) { + /* switch parents, navigate into child */ + *parent = toadd; + return NULL; + } + return conf_tree; +} + + diff --git a/server/util_md5.c b/server/util_md5.c new file mode 100644 index 00000000000..368ddf63b23 --- /dev/null +++ b/server/util_md5.c @@ -0,0 +1,229 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/************************************************************************ + * NCSA HTTPd Server + * Software Development Group + * National Center for Supercomputing Applications + * University of Illinois at Urbana-Champaign + * 605 E. Springfield, Champaign, IL 61820 + * httpd@ncsa.uiuc.edu + * + * Copyright (C) 1995, Board of Trustees of the University of Illinois + * + ************************************************************************ + * + * md5.c: NCSA HTTPd code which uses the md5c.c RSA Code + * + * Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc. + * Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon + * University (see Copyright below). + * Portions of Content-MD5 code Copyright (C) 1991 Bell Communications + * Research, Inc. (Bellcore) (see Copyright below). + * Portions extracted from mpack, John G. Myers - jgm+@cmu.edu + * Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk) + * + */ + + + +/* md5.c --Module Interface to MD5. */ +/* Jeff Hostetler, Spyglass, Inc., 1994. */ + +#include "httpd.h" +#include "util_md5.h" + +API_EXPORT(char *) ap_md5_binary(pool *p, const unsigned char *buf, int length) +{ + const char *hex = "0123456789abcdef"; + AP_MD5_CTX my_md5; + unsigned char hash[16]; + char *r, result[33]; + int i; + + /* + * Take the MD5 hash of the string argument. + */ + + ap_MD5Init(&my_md5); + ap_MD5Update(&my_md5, buf, (unsigned int)length); + ap_MD5Final(hash, &my_md5); + + for (i = 0, r = result; i < 16; i++) { + *r++ = hex[hash[i] >> 4]; + *r++ = hex[hash[i] & 0xF]; + } + *r = '\0'; + + return ap_pstrdup(p, result); +} + +API_EXPORT(char *) ap_md5(pool *p, const unsigned char *string) +{ + return ap_md5_binary(p, string, (int) strlen((char *)string)); +} + +/* these portions extracted from mpack, John G. Myers - jgm+@cmu.edu */ + +/* (C) Copyright 1993,1994 by Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of Carnegie + * Mellon University not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. Carnegie Mellon University makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore) + * + * Permission to use, copy, modify, and distribute this material + * for any purpose and without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies, and that the name of Bellcore not be + * used in advertising or publicity pertaining to this + * material without the specific, prior written permission + * of an authorized representative of Bellcore. BELLCORE + * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY + * OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS", + * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. + */ + +static char basis_64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +API_EXPORT(char *) ap_md5contextTo64(pool *a, AP_MD5_CTX * context) +{ + unsigned char digest[18]; + char *encodedDigest; + int i; + char *p; + + encodedDigest = (char *) ap_pcalloc(a, 25 * sizeof(char)); + + ap_MD5Final(digest, context); + digest[sizeof(digest) - 1] = digest[sizeof(digest) - 2] = 0; + + p = encodedDigest; + for (i = 0; i < sizeof(digest); i += 3) { + *p++ = basis_64[digest[i] >> 2]; + *p++ = basis_64[((digest[i] & 0x3) << 4) | ((int) (digest[i + 1] & 0xF0) >> 4)]; + *p++ = basis_64[((digest[i + 1] & 0xF) << 2) | ((int) (digest[i + 2] & 0xC0) >> 6)]; + *p++ = basis_64[digest[i + 2] & 0x3F]; + } + *p-- = '\0'; + *p-- = '='; + *p-- = '='; + return encodedDigest; +} + +#ifdef CHARSET_EBCDIC + +API_EXPORT(char *) ap_md5digest(pool *p, FILE *infile, int convert) +{ + AP_MD5_CTX context; + unsigned char buf[1000]; + long length = 0; + int nbytes; + + ap_MD5Init(&context); + while ((nbytes = fread(buf, 1, sizeof(buf), infile))) { + length += nbytes; + if (!convert) { + ascii2ebcdic(buf, buf, nbytes); + } + ap_MD5Update(&context, buf, nbytes); + } + rewind(infile); + return ap_md5contextTo64(p, &context); +} + +#else + +API_EXPORT(char *) ap_md5digest(pool *p, FILE *infile) +{ + AP_MD5_CTX context; + unsigned char buf[1000]; + long length = 0; + unsigned int nbytes; + + ap_MD5Init(&context); + while ((nbytes = fread(buf, 1, sizeof(buf), infile))) { + length += nbytes; + ap_MD5Update(&context, buf, nbytes); + } + rewind(infile); + return ap_md5contextTo64(p, &context); +} + +#endif /* CHARSET_EBCDIC */ diff --git a/server/util_script.c b/server/util_script.c new file mode 100644 index 00000000000..467dd2964c1 --- /dev/null +++ b/server/util_script.c @@ -0,0 +1,1138 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_conf_globals.h" +#include "http_main.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_core.h" /* For document_root. Sigh... */ +#include "http_request.h" /* for sub_req_lookup_uri() */ +#include "util_script.h" +#include "util_date.h" /* For parseHTTPdate() */ + +#ifdef OS2 +#define INCL_DOS +#include +#endif + +/* + * Various utility functions which are common to a whole lot of + * script-type extensions mechanisms, and might as well be gathered + * in one place (if only to avoid creating inter-module dependancies + * where there don't have to be). + */ + +#define MALFORMED_MESSAGE "malformed header from script. Bad header=" +#define MALFORMED_HEADER_LENGTH_TO_SHOW 30 + +/* If a request includes query info in the URL (stuff after "?"), and + * the query info does not contain "=" (indicative of a FORM submission), + * then 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. + * + * XXXX: note that the WIN32 code uses one of the suexec strings + * to pass an interpreter name. Remember this if changing the way they + * are handled in create_argv. + * + */ +static char **create_argv(pool *p, char *path, char *user, char *group, + char *av0, const char *args) +{ + int x, numwords; + char **av; + char *w; + int idx = 0; + + /* 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 **) ap_palloc(p, (numwords + 5) * sizeof(char *)); + + if (path) { + av[idx++] = path; + } + if (user) { + av[idx++] = user; + } + if (group) { + av[idx++] = group; + } + + av[idx++] = 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; +} + + +static char *http2env(pool *a, char *w) +{ + char *res = ap_pstrcat(a, "HTTP_", w, NULL); + char *cp = res; + + while (*++cp) { + if (!ap_isalnum(*cp) && *cp != '_') { + *cp = '_'; + } + else { + *cp = ap_toupper(*cp); + } + } + + return res; +} + +API_EXPORT(char **) ap_create_environment(pool *p, table *t) +{ + array_header *env_arr = ap_table_elts(t); + table_entry *elts = (table_entry *) env_arr->elts; + char **env = (char **) ap_palloc(p, (env_arr->nelts + 2) * sizeof(char *)); + int i, j; + char *tz; + char *whack; + + j = 0; + if (!ap_table_get(t, "TZ")) { + tz = getenv("TZ"); + if (tz != NULL) { + env[j++] = ap_pstrcat(p, "TZ=", tz, NULL); + } + } + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) { + continue; + } + env[j] = ap_pstrcat(p, elts[i].key, "=", elts[i].val, NULL); + whack = env[j]; + if (ap_isdigit(*whack)) { + *whack++ = '_'; + } + while (*whack != '=') { + if (!ap_isalnum(*whack) && *whack != '_') { + *whack = '_'; + } + ++whack; + } + ++j; + } + + env[j] = NULL; + return env; +} + +API_EXPORT(void) ap_add_common_vars(request_rec *r) +{ + table *e; + server_rec *s = r->server; + conn_rec *c = r->connection; + const char *rem_logname; + char *env_path; +#ifdef WIN32 + char *env_temp; +#endif + const char *host; + array_header *hdrs_arr = ap_table_elts(r->headers_in); + table_entry *hdrs = (table_entry *) hdrs_arr->elts; + int i; + + /* use a temporary table which we'll overlap onto + * r->subprocess_env later + */ + e = ap_make_table(r->pool, 25 + hdrs_arr->nelts); + + /* First, add environment vars from headers... this is as per + * CGI specs, though other sorts of scripting interfaces see + * the same vars... + */ + + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) { + continue; + } + + /* A few headers are special cased --- Authorization to prevent + * rogue scripts from capturing passwords; content-type and -length + * for no particular reason. + */ + + if (!strcasecmp(hdrs[i].key, "Content-type")) { + ap_table_addn(e, "CONTENT_TYPE", hdrs[i].val); + } + else if (!strcasecmp(hdrs[i].key, "Content-length")) { + ap_table_addn(e, "CONTENT_LENGTH", hdrs[i].val); + } + /* + * You really don't want to disable this check, since it leaves you + * wide open to CGIs stealing passwords and people viewing them + * in the environment with "ps -e". But, if you must... + */ +#ifndef SECURITY_HOLE_PASS_AUTHORIZATION + else if (!strcasecmp(hdrs[i].key, "Authorization") + || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) { + continue; + } +#endif + else { + ap_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val); + } + } + + if (!(env_path = getenv("PATH"))) { + env_path = DEFAULT_PATH; + } + +#ifdef WIN32 + if (env_temp = getenv("SystemRoot")) { + ap_table_addn(e, "SystemRoot", env_temp); + } + if (env_temp = getenv("COMSPEC")) { + ap_table_addn(e, "COMSPEC", env_temp); + } + if (env_temp = getenv("WINDIR")) { + ap_table_addn(e, "WINDIR", env_temp); + } +#endif + + ap_table_addn(e, "PATH", env_path); + ap_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r)); + ap_table_addn(e, "SERVER_SOFTWARE", ap_get_server_version()); + ap_table_addn(e, "SERVER_NAME", ap_get_server_name(r)); + ap_table_addn(e, "SERVER_ADDR", r->connection->local_ip); /* Apache */ + ap_table_addn(e, "SERVER_PORT", + ap_psprintf(r->pool, "%u", ap_get_server_port(r))); + host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST); + if (host) { + ap_table_addn(e, "REMOTE_HOST", host); + } + ap_table_addn(e, "REMOTE_ADDR", c->remote_ip); + ap_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r)); /* Apache */ + ap_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */ + ap_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */ + + ap_table_addn(e, "REMOTE_PORT", + ap_psprintf(r->pool, "%d", ntohs(c->remote_addr.sin_port))); + + if (c->user) { + ap_table_addn(e, "REMOTE_USER", c->user); + } + if (c->ap_auth_type) { + ap_table_addn(e, "AUTH_TYPE", c->ap_auth_type); + } + rem_logname = ap_get_remote_logname(r); + if (rem_logname) { + ap_table_addn(e, "REMOTE_IDENT", ap_pstrdup(r->pool, rem_logname)); + } + + /* Apache custom error responses. If we have redirected set two new vars */ + + if (r->prev) { + if (r->prev->args) { + ap_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args); + } + if (r->prev->uri) { + ap_table_addn(e, "REDIRECT_URL", r->prev->uri); + } + } + + ap_overlap_tables(r->subprocess_env, e, AP_OVERLAP_TABLES_SET); +} + +/* This "cute" little function comes about because the path info on + * filenames and URLs aren't always the same. So we take the two, + * and find as much of the two that match as possible. + */ + +API_EXPORT(int) ap_find_path_info(const char *uri, const char *path_info) +{ + int lu = strlen(uri); + int lp = strlen(path_info); + + while (lu-- && lp-- && uri[lu] == path_info[lp]); + + if (lu == -1) { + lu = 0; + } + + while (uri[lu] != '\0' && uri[lu] != '/') { + lu++; + } + return lu; +} + +/* Obtain the Request-URI from the original request-line, returning + * a new string from the request pool containing the URI or "". + */ +static char *original_uri(request_rec *r) +{ + char *first, *last; + + if (r->the_request == NULL) { + return (char *) ap_pcalloc(r->pool, 1); + } + + first = r->the_request; /* use the request-line */ + + while (*first && !ap_isspace(*first)) { + ++first; /* skip over the method */ + } + while (ap_isspace(*first)) { + ++first; /* and the space(s) */ + } + + last = first; + while (*last && !ap_isspace(*last)) { + ++last; /* end at next whitespace */ + } + + return ap_pstrndup(r->pool, first, last - first); +} + +API_EXPORT(void) ap_add_cgi_vars(request_rec *r) +{ + table *e = r->subprocess_env; + + ap_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1"); + ap_table_setn(e, "SERVER_PROTOCOL", r->protocol); + ap_table_setn(e, "REQUEST_METHOD", r->method); + ap_table_setn(e, "QUERY_STRING", r->args ? r->args : ""); + ap_table_setn(e, "REQUEST_URI", original_uri(r)); + + /* Note that the code below special-cases scripts run from includes, + * because it "knows" that the sub_request has been hacked to have the + * args and path_info of the original request, and not any that may have + * come with the script URI in the include command. Ugh. + */ + + if (!strcmp(r->protocol, "INCLUDED")) { + ap_table_setn(e, "SCRIPT_NAME", r->uri); + if (r->path_info && *r->path_info) { + ap_table_setn(e, "PATH_INFO", r->path_info); + } + } + else if (!r->path_info || !*r->path_info) { + ap_table_setn(e, "SCRIPT_NAME", r->uri); + } + else { + int path_info_start = ap_find_path_info(r->uri, r->path_info); + + ap_table_setn(e, "SCRIPT_NAME", + ap_pstrndup(r->pool, r->uri, path_info_start)); + + ap_table_setn(e, "PATH_INFO", r->path_info); + } + + if (r->path_info && r->path_info[0]) { + /* + * To get PATH_TRANSLATED, treat PATH_INFO as a URI path. + * Need to re-escape it for this, since the entire URI was + * un-escaped before we determined where the PATH_INFO began. + */ + request_rec *pa_req; + + pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r); + + if (pa_req->filename) { +#ifdef WIN32 + char buffer[HUGE_STRING_LEN]; +#endif + char *pt = ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info, + NULL); +#ifdef WIN32 + /* We need to make this a real Windows path name */ + GetFullPathName(pt, HUGE_STRING_LEN, buffer, NULL); + ap_table_setn(e, "PATH_TRANSLATED", ap_pstrdup(r->pool, buffer)); +#else + ap_table_setn(e, "PATH_TRANSLATED", pt); +#endif + } + ap_destroy_sub_req(pa_req); + } +} + + +static int set_cookie_doo_doo(void *v, const char *key, const char *val) +{ + ap_table_addn(v, key, val); + return 1; +} + +API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, + int (*getsfunc) (char *, int, void *), + void *getsfunc_data) +{ + char x[MAX_STRING_LEN]; + char *w, *l; + int p; + int cgi_status = HTTP_OK; + table *merge; + table *cookie_table; + + if (buffer) { + *buffer = '\0'; + } + w = buffer ? buffer : x; + + ap_hard_timeout("read script header", r); + + /* temporary place to hold headers to merge in later */ + merge = ap_make_table(r->pool, 10); + + /* The HTTP specification says that it is legal to merge duplicate + * headers into one. Some browsers that support Cookies don't like + * merged headers and prefer that each Set-Cookie header is sent + * separately. Lets humour those browsers by not merging. + * Oh what a pain it is. + */ + cookie_table = ap_make_table(r->pool, 2); + ap_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL); + + while (1) { + + if ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data) == 0) { + ap_kill_timeout(r); + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Premature end of script headers: %s", r->filename); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* Delete terminal (CR?)LF */ + + p = strlen(w); + if (p > 0 && w[p - 1] == '\n') { + if (p > 1 && w[p - 2] == '\015') { + w[p - 2] = '\0'; + } + else { + w[p - 1] = '\0'; + } + } + + /* + * If we've finished reading the headers, check to make sure any + * HTTP/1.1 conditions are met. If so, we're done; normal processing + * will handle the script's output. If not, just return the error. + * The appropriate thing to do would be to send the script process a + * SIGPIPE to let it know we're ignoring it, close the channel to the + * script process, and *then* return the failed-to-meet-condition + * error. Otherwise we'd be waiting for the script to finish + * blithering before telling the client the output was no good. + * However, we don't have the information to do that, so we have to + * leave it to an upper layer. + */ + if (w[0] == '\0') { + int cond_status = OK; + + ap_kill_timeout(r); + if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) { + cond_status = ap_meets_conditions(r); + } + ap_overlap_tables(r->err_headers_out, merge, + AP_OVERLAP_TABLES_MERGE); + if (!ap_is_empty_table(cookie_table)) { + /* the cookies have already been copied to the cookie_table */ + ap_table_unset(r->err_headers_out, "Set-Cookie"); + r->err_headers_out = ap_overlay_tables(r->pool, + r->err_headers_out, cookie_table); + } + return cond_status; + } + + /* if we see a bogus header don't ignore it. Shout and scream */ + +#ifdef CHARSET_EBCDIC + /* Chances are that we received an ASCII header text instead of + * the expected EBCDIC header lines. Try to auto-detect: + */ + if (!(l = strchr(w, ':'))) { + int maybeASCII = 0, maybeEBCDIC = 0; + char *cp; + + for (cp = w; *cp != '\0'; ++cp) { + if (isprint(*cp) && !isprint(os_toebcdic[*cp])) + ++maybeEBCDIC; + if (!isprint(*cp) && isprint(os_toebcdic[*cp])) + ++maybeASCII; + } + if (maybeASCII > maybeEBCDIC) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server, + "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)", r->filename); + ascii2ebcdic(w, w, cp - w); + } + } +#endif + if (!(l = strchr(w, ':'))) { + char malformed[(sizeof MALFORMED_MESSAGE) + 1 + + MALFORMED_HEADER_LENGTH_TO_SHOW]; + + strcpy(malformed, MALFORMED_MESSAGE); + strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW); + + if (!buffer) { + /* Soak up all the script output - may save an outright kill */ + while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) { + continue; + } + } + + ap_kill_timeout(r); + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "%s: %s", malformed, r->filename); + return HTTP_INTERNAL_SERVER_ERROR; + } + + *l++ = '\0'; + while (*l && ap_isspace(*l)) { + ++l; + } + + if (!strcasecmp(w, "Content-type")) { + char *tmp; + + /* Nuke trailing whitespace */ + + char *endp = l + strlen(l) - 1; + while (endp > l && ap_isspace(*endp)) { + *endp-- = '\0'; + } + + tmp = ap_pstrdup(r->pool, l); + ap_content_type_tolower(tmp); + r->content_type = tmp; + } + /* + * If the script returned a specific status, that's what + * we'll use - otherwise we assume 200 OK. + */ + else if (!strcasecmp(w, "Status")) { + r->status = cgi_status = atoi(l); + r->status_line = ap_pstrdup(r->pool, l); + } + else if (!strcasecmp(w, "Location")) { + ap_table_set(r->headers_out, w, l); + } + else if (!strcasecmp(w, "Content-Length")) { + ap_table_set(r->headers_out, w, l); + } + else if (!strcasecmp(w, "Transfer-Encoding")) { + ap_table_set(r->headers_out, w, l); + } + /* + * If the script gave us a Last-Modified header, we can't just + * pass it on blindly because of restrictions on future values. + */ + else if (!strcasecmp(w, "Last-Modified")) { + time_t mtime = ap_parseHTTPdate(l); + + ap_update_mtime(r, mtime); + ap_set_last_modified(r); + } + else if (!strcasecmp(w, "Set-Cookie")) { + ap_table_add(cookie_table, w, l); + } + else { + ap_table_add(merge, w, l); + } + } +} + +static int getsfunc_FILE(char *buf, int len, void *f) +{ + return fgets(buf, len, (FILE *) f) != NULL; +} + +API_EXPORT(int) ap_scan_script_header_err(request_rec *r, FILE *f, + char *buffer) +{ + return ap_scan_script_header_err_core(r, buffer, getsfunc_FILE, f); +} + +static int getsfunc_BUFF(char *w, int len, void *fb) +{ + return ap_bgets(w, len, (BUFF *) fb) > 0; +} + +API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *fb, + char *buffer) +{ + return ap_scan_script_header_err_core(r, buffer, getsfunc_BUFF, fb); +} + + +API_EXPORT(void) ap_send_size(size_t size, request_rec *r) +{ + /* XXX: this -1 thing is a gross hack */ + if (size == (size_t)-1) { + ap_rputs(" -", r); + } + else if (!size) { + ap_rputs(" 0k", r); + } + else if (size < 1024) { + ap_rputs(" 1k", r); + } + else if (size < 1048576) { + ap_rprintf(r, "%4dk", (size + 512) / 1024); + } + else if (size < 103809024) { + ap_rprintf(r, "%4.1fM", size / 1048576.0); + } + else { + ap_rprintf(r, "%4dM", (size + 524288) / 1048576); + } +} + +#if defined(OS2) || defined(WIN32) +static char **create_argv_cmd(pool *p, char *av0, const char *args, char *path) +{ + register int x, n; + char **av; + char *w; + + for (x = 0, n = 2; args[x]; x++) { + if (args[x] == '+') { + ++n; + } + } + + /* Add extra strings to array. */ + n = n + 2; + + av = (char **) ap_palloc(p, (n + 1) * sizeof(char *)); + av[0] = av0; + + /* Now insert the extra strings we made room for above. */ + av[1] = strdup("/C"); + av[2] = strdup(path); + + for (x = (1 + 2); x < n; x++) { + w = ap_getword(p, &args, '+'); + ap_unescape_url(w); + av[x] = ap_escape_shell_cmd(p, w); + } + av[n] = NULL; + return av; +} +#endif + + +API_EXPORT(int) ap_call_exec(request_rec *r, child_info *pinfo, char *argv0, + char **env, int shellcmd) +{ + int pid = 0; +#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ + defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS) + + core_dir_config *conf; + conf = (core_dir_config *) ap_get_module_config(r->per_dir_config, + &core_module); + +#endif + +#if !defined(WIN32) && !defined(OS2) + /* the fd on r->server->error_log is closed, but we need somewhere to + * put the error messages from the log_* functions. So, we use stderr, + * since that is better than allowing errors to go unnoticed. Don't do + * this on Win32, though, since we haven't fork()'d. + */ + r->server->error_log = stderr; +#endif + +#ifdef RLIMIT_CPU + if (conf->limit_cpu != NULL) { + if ((setrlimit(RLIMIT_CPU, conf->limit_cpu)) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "setrlimit: failed to set CPU usage limit"); + } + } +#endif +#ifdef RLIMIT_NPROC + if (conf->limit_nproc != NULL) { + if ((setrlimit(RLIMIT_NPROC, conf->limit_nproc)) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "setrlimit: failed to set process limit"); + } + } +#endif +#if defined(RLIMIT_AS) + if (conf->limit_mem != NULL) { + if ((setrlimit(RLIMIT_AS, conf->limit_mem)) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "setrlimit(RLIMIT_AS): failed to set memory " + "usage limit"); + } + } +#elif defined(RLIMIT_DATA) + if (conf->limit_mem != NULL) { + if ((setrlimit(RLIMIT_DATA, conf->limit_mem)) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "setrlimit(RLIMIT_DATA): failed to set memory " + "usage limit"); + } + } +#elif defined(RLIMIT_VMEM) + if (conf->limit_mem != NULL) { + if ((setrlimit(RLIMIT_VMEM, conf->limit_mem)) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "setrlimit(RLIMIT_VMEM): failed to set memory " + "usage limit"); + } + } +#endif + +#ifdef OS2 + { + /* Additions by Alec Kloss, to allow exec'ing of scripts under OS/2 */ + int is_script = 0; + char interpreter[2048]; /* hope it's enough for the interpreter path */ + char error_object[260]; + FILE *program; + char *cmdline = r->filename, *cmdline_pos; + int cmdlen; + char *args = "", *args_end; + ULONG rc; + RESULTCODES rescodes; + int env_len, e; + char *env_block, *env_block_pos; + + if (r->args && r->args[0] && !strchr(r->args, '=')) + args = r->args; + + program = fopen(r->filename, "rt"); + + if (!program) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "fopen(%s) failed", + r->filename); + return (pid); + } + + fgets(interpreter, sizeof(interpreter), program); + fclose(program); + + if (!strncmp(interpreter, "#!", 2)) { + is_script = 1; + interpreter[strlen(interpreter) - 1] = '\0'; + if (interpreter[2] != '/' && interpreter[2] != '\\' && interpreter[3] != ':') { + char buffer[300]; + if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) { + strcpy(interpreter+2, buffer); + } else { + strcat(interpreter, ".exe"); + if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) { + strcpy(interpreter+2, buffer); + } + } + } + } + + if (is_script) { + cmdline = ap_pstrcat(r->pool, interpreter+2, " ", r->filename, NULL); + } + else if (strstr(strupr(r->filename), ".CMD") > 0) { + /* Special case to allow use of REXX commands as scripts. */ + os2pathname(r->filename); + cmdline = ap_pstrcat(r->pool, SHELL_PATH, " /C ", r->filename, NULL); + } + else { + cmdline = r->filename; + } + + args = ap_pstrdup(r->pool, args); + ap_unescape_url(args); + args = ap_double_quotes(r->pool, args); + args_end = args + strlen(args); + + if (args_end - args > 4000) { /* cmd.exe won't handle lines longer than 4k */ + args_end = args + 4000; + *args_end = 0; + } + + /* +4 = 1 space between progname and args, 2 for double null at end, 2 for possible quote on first arg */ + cmdlen = strlen(cmdline) + strlen(args) + 4; + cmdline_pos = cmdline; + + while (*cmdline_pos) { + cmdlen += 2 * (*cmdline_pos == '+'); /* Allow space for each arg to be quoted */ + cmdline_pos++; + } + + cmdline = ap_pstrndup(r->pool, cmdline, cmdlen); + cmdline_pos = cmdline + strlen(cmdline); + + while (args < args_end) { + char *arg; + + arg = ap_getword_nc(r->pool, &args, '+'); + + if (strpbrk(arg, "&|<> ")) + arg = ap_pstrcat(r->pool, "\"", arg, "\"", NULL); + + *(cmdline_pos++) = ' '; + strcpy(cmdline_pos, arg); + cmdline_pos += strlen(cmdline_pos); + } + + *(++cmdline_pos) = 0; /* Add required second terminator */ + args = strchr(cmdline, ' '); + + if (args) { + *args = 0; + args++; + } + + /* Create environment block from list of envariables */ + for (env_len=1, e=0; env[e]; e++) + env_len += strlen(env[e]) + 1; + + env_block = ap_palloc(r->pool, env_len); + env_block_pos = env_block; + + for (e=0; env[e]; e++) { + strcpy(env_block_pos, env[e]); + env_block_pos += strlen(env_block_pos) + 1; + } + + *env_block_pos = 0; /* environment block is terminated by a double null */ + + rc = DosExecPgm(error_object, sizeof(error_object), EXEC_ASYNC, cmdline, env_block, &rescodes, cmdline); + + if (rc) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "DosExecPgm(%s %s) failed, %s - %s", + cmdline, args ? args : "", ap_os_error_message(rc), error_object ); + return -1; + } + + return rescodes.codeTerminate; + } +#elif defined(WIN32) + { + /* Adapted from Alec Kloss' work for OS/2 */ + char *interpreter = NULL; + char *arguments = NULL; + char *ext = NULL; + char *exename = NULL; + char *s = NULL; + char *quoted_filename; + char *pCommand; + char *pEnvBlock, *pNext; + + int i; + int iEnvBlockLen; + + file_type_e fileType; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + + pid = -1; + + if (!shellcmd) { + + fileType = ap_get_win32_interpreter(r, &interpreter); + + if (fileType == eFileTypeUNKNOWN) { + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, + "%s is not executable; ensure interpreted scripts have " + "\"#!\" first line", + r->filename); + return (pid); + } + + /* + * Look at the arguments... + */ + arguments = ""; + if ((r->args) && (r->args[0]) && !strchr(r->args, '=')) { + /* If we are in this leg, there are some other arguments + * that we must include in the execution of the CGI. + * Because CreateProcess is the way it is, we have to + * create a command line like format for the execution + * of the CGI. This means we need to create on long + * string with the executable and arguments. + * + * The arguments string comes in the request structure, + * and each argument is separated by a '+'. We'll replace + * these pluses with spaces. + */ + + int iStringSize = 0; + int x; + + /* + * Duplicate the request structure string so we don't change it. + */ + arguments = ap_pstrdup(r->pool, r->args); + + /* + * Change the '+' to ' ' + */ + for (x=0; arguments[x]; x++) { + if ('+' == arguments[x]) { + arguments[x] = ' '; + } + } + + /* + * We need to unescape any characters that are + * in the arguments list. + */ + ap_unescape_url(arguments); + arguments = ap_escape_shell_cmd(r->pool, arguments); + } + + /* + * We have the interpreter (if there is one) and we have + * the arguments (if there are any). + * Build the command string to pass to CreateProcess. + */ + quoted_filename = ap_pstrcat(r->pool, "\"", r->filename, "\"", NULL); + if (interpreter && *interpreter) { + pCommand = ap_pstrcat(r->pool, interpreter, " ", + quoted_filename, " ", arguments, NULL); + } + else { + pCommand = ap_pstrcat(r->pool, quoted_filename, " ", arguments, NULL); + } + + } else { + + char *shell_cmd = "CMD.EXE /C "; + OSVERSIONINFO osver; + osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + /* + * Use CMD.EXE for NT, COMMAND.COM for WIN95 + */ + if (GetVersionEx(&osver)) { + if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT) { + shell_cmd = "COMMAND.COM /C "; + } + } + pCommand = ap_pstrcat(r->pool, shell_cmd, argv0, NULL); + } + + /* + * Make child process use hPipeOutputWrite as standard out, + * and make sure it does not show on screen. + */ + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = pinfo->hPipeInputRead; + si.hStdOutput = pinfo->hPipeOutputWrite; + si.hStdError = pinfo->hPipeErrorWrite; + + /* + * Win32's CreateProcess call requires that the environment + * be passed in an environment block, a null terminated block of + * null terminated strings. + */ + i = 0; + iEnvBlockLen = 1; + while (env[i]) { + iEnvBlockLen += strlen(env[i]) + 1; + i++; + } + + pEnvBlock = (char *)ap_pcalloc(r->pool,iEnvBlockLen); + + i = 0; + pNext = pEnvBlock; + while (env[i]) { + strcpy(pNext, env[i]); + pNext = pNext + strlen(pNext) + 1; + i++; + } + + if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, DETACHED_PROCESS, pEnvBlock, + ap_make_dirstr_parent(r->pool, r->filename), + &si, &pi)) { + if (fileType == eFileTypeEXE16) { + /* Hack to get 16-bit CGI's working. It works for all the + * standard modules shipped with Apache. pi.dwProcessId is 0 + * for 16-bit CGIs and all the Unix specific code that calls + * ap_call_exec interprets this as a failure case. And we can't + * use -1 either because it is mapped to 0 by the caller. + */ + pid = -2; + } + else { + pid = pi.dwProcessId; + /* + * We must close the handles to the new process and its main thread + * to prevent handle and memory leaks. + */ + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + } + return (pid); + } + +#else + if (ap_suexec_enabled + && ((r->server->server_uid != ap_user_id) + || (r->server->server_gid != ap_group_id) + || (!strncmp("/~", r->uri, 2)))) { + + char *execuser, *grpname; + struct passwd *pw; + struct group *gr; + + if (!strncmp("/~", r->uri, 2)) { + gid_t user_gid; + char *username = ap_pstrdup(r->pool, r->uri + 2); + char *pos = strchr(username, '/'); + + if (pos) { + *pos = '\0'; + } + + if ((pw = getpwnam(username)) == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "getpwnam: invalid username %s", username); + return (pid); + } + execuser = ap_pstrcat(r->pool, "~", pw->pw_name, NULL); + user_gid = pw->pw_gid; + + if ((gr = getgrgid(user_gid)) == NULL) { + if ((grpname = ap_palloc(r->pool, 16)) == NULL) { + return (pid); + } + else { + ap_snprintf(grpname, 16, "%ld", (long) user_gid); + } + } + else { + grpname = gr->gr_name; + } + } + else { + if ((pw = getpwuid(r->server->server_uid)) == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "getpwuid: invalid userid %ld", + (long) r->server->server_uid); + return (pid); + } + execuser = ap_pstrdup(r->pool, pw->pw_name); + + if ((gr = getgrgid(r->server->server_gid)) == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "getgrgid: invalid groupid %ld", + (long) r->server->server_gid); + return (pid); + } + grpname = gr->gr_name; + } + + if (shellcmd) { + execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, + NULL, env); + } + + else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { + execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, + NULL, env); + } + + else { + execve(SUEXEC_BIN, + create_argv(r->pool, SUEXEC_BIN, execuser, grpname, + argv0, r->args), + env); + } + } + else { + if (shellcmd) { + execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env); + } + + else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { + execle(r->filename, argv0, NULL, env); + } + + else { + execve(r->filename, + create_argv(r->pool, NULL, NULL, NULL, argv0, r->args), + env); + } + } + return (pid); +#endif +} diff --git a/server/vhost.c b/server/vhost.c new file mode 100644 index 00000000000..793048f9471 --- /dev/null +++ b/server/vhost.c @@ -0,0 +1,917 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_vhost.c: functions pertaining to virtual host addresses + * (configuration and run-time) + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_conf_globals.h" +#include "http_log.h" +#include "http_vhost.h" +#include "http_protocol.h" + +/* + * After all the definitions there's an explanation of how it's all put + * together. + */ + +/* meta-list of name-vhosts. Each server_rec can be in possibly multiple + * lists of name-vhosts. + */ +typedef struct name_chain name_chain; +struct name_chain { + name_chain *next; + server_addr_rec *sar; /* the record causing it to be in + * this chain (needed for port comparisons) */ + server_rec *server; /* the server to use on a match */ +}; + +/* meta-list of ip addresses. Each server_rec can be in possibly multiple + * hash chains since it can have multiple ips. + */ +typedef struct ipaddr_chain ipaddr_chain; +struct ipaddr_chain { + ipaddr_chain *next; + server_addr_rec *sar; /* the record causing it to be in + * this chain (need for both ip addr and port + * comparisons) */ + server_rec *server; /* the server to use if this matches */ + name_chain *names; /* if non-NULL then a list of name-vhosts + * sharing this address */ +}; + +/* This defines the size of the hash table used for hashing ip addresses + * of virtual hosts. It must be a power of two. + */ +#ifndef IPHASH_TABLE_SIZE +#define IPHASH_TABLE_SIZE 256 +#endif + +/* A (n) bucket hash table, each entry has a pointer to a server rec and + * a pointer to the other entries in that bucket. Each individual address, + * even for virtualhosts with multiple addresses, has an entry in this hash + * table. There are extra buckets for _default_, and name-vhost entries. + * + * Note that after config time this is constant, so it is thread-safe. + */ +static ipaddr_chain *iphash_table[IPHASH_TABLE_SIZE]; + +/* dump out statistics about the hash function */ +/* #define IPHASH_STATISTICS */ + +/* list of the _default_ servers */ +static ipaddr_chain *default_list; + +/* list of the NameVirtualHost addresses */ +static server_addr_rec *name_vhost_list; +static server_addr_rec **name_vhost_list_tail; + +/* + * How it's used: + * + * The ip address determines which chain in iphash_table is interesting, then + * a comparison is done down that chain to find the first ipaddr_chain whose + * sar matches the address:port pair. + * + * If that ipaddr_chain has names == NULL then you're done, it's an ip-vhost. + * + * Otherwise it's a name-vhost list, and the default is the server in the + * ipaddr_chain record. We tuck away the ipaddr_chain record in the + * conn_rec field vhost_lookup_data. Later on after the headers we get a + * second chance, and we use the name_chain to figure out what name-vhost + * matches the headers. + * + * If there was no ip address match in the iphash_table then do a lookup + * in the default_list. + * + * How it's put together ... well you should be able to figure that out + * from how it's used. Or something like that. + */ + + +/* called at the beginning of the config */ +void ap_init_vhost_config(pool *p) +{ + memset(iphash_table, 0, sizeof(iphash_table)); + default_list = NULL; + name_vhost_list = NULL; + name_vhost_list_tail = &name_vhost_list; +} + + +/* + * Parses a host of the form
[:port] + * paddr is used to create a list in the order of input + * **paddr is the ->next pointer of the last entry (or s->addrs) + * *paddr is the variable used to keep track of **paddr between calls + * port is the default port to assume + */ +static const char *get_addresses(pool *p, char *w, server_addr_rec ***paddr, + unsigned port) +{ + struct hostent *hep; + unsigned long my_addr; + server_addr_rec *sar; + char *t; + int i, is_an_ip_addr; + + if (*w == 0) + return NULL; + + t = strchr(w, ':'); + if (t) { + if (strcmp(t + 1, "*") == 0) { + port = 0; + } + else if ((i = atoi(t + 1))) { + port = i; + } + else { + return ":port must be numeric"; + } + *t = 0; + } + + is_an_ip_addr = 0; + if (strcmp(w, "*") == 0) { + my_addr = htonl(INADDR_ANY); + is_an_ip_addr = 1; + } + else if (strcasecmp(w, "_default_") == 0 + || strcmp(w, "255.255.255.255") == 0) { + my_addr = DEFAULT_VHOST_ADDR; + is_an_ip_addr = 1; + } + else if ((my_addr = ap_inet_addr(w)) != INADDR_NONE) { + is_an_ip_addr = 1; + } + if (is_an_ip_addr) { + sar = ap_pcalloc(p, sizeof(server_addr_rec)); + **paddr = sar; + *paddr = &sar->next; + sar->host_addr.s_addr = my_addr; + sar->host_port = port; + sar->virthost = ap_pstrdup(p, w); + if (t != NULL) + *t = ':'; + return NULL; + } + + hep = gethostbyname(w); + + if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, + "Cannot resolve host name %s --- ignoring!", w); + if (t != NULL) + *t = ':'; + return NULL; + } + + for (i = 0; hep->h_addr_list[i]; ++i) { + sar = ap_pcalloc(p, sizeof(server_addr_rec)); + **paddr = sar; + *paddr = &sar->next; + sar->host_addr = *(struct in_addr *) hep->h_addr_list[i]; + sar->host_port = port; + sar->virthost = ap_pstrdup(p, w); + } + + if (t != NULL) + *t = ':'; + return NULL; +} + + +/* parse the addresses */ +const char *ap_parse_vhost_addrs(pool *p, const char *hostname, server_rec *s) +{ + server_addr_rec **addrs; + const char *err; + + /* start the list of addreses */ + addrs = &s->addrs; + while (hostname[0]) { + err = get_addresses(p, ap_getword_conf(p, &hostname), &addrs, s->port); + if (err) { + *addrs = NULL; + return err; + } + } + /* terminate the list */ + *addrs = NULL; + if (s->addrs) { + if (s->addrs->host_port) { + /* override the default port which is inherited from main_server */ + s->port = s->addrs->host_port; + } + } + return NULL; +} + + +const char *ap_set_name_virtual_host (cmd_parms *cmd, void *dummy, char *arg) +{ + /* use whatever port the main server has at this point */ + return get_addresses(cmd->pool, arg, &name_vhost_list_tail, + cmd->server->port); +} + + +/* hash table statistics, keep this in here for the beta period so + * we can find out if the hash function is ok + */ +#ifdef IPHASH_STATISTICS +static int iphash_compare(const void *a, const void *b) +{ + return (*(const int *) b - *(const int *) a); +} + + +static void dump_iphash_statistics(server_rec *main_s) +{ + unsigned count[IPHASH_TABLE_SIZE]; + int i; + ipaddr_chain *src; + unsigned total; + char buf[HUGE_STRING_LEN]; + char *p; + + total = 0; + for (i = 0; i < IPHASH_TABLE_SIZE; ++i) { + count[i] = 0; + for (src = iphash_table[i]; src; src = src->next) { + ++count[i]; + if (i < IPHASH_TABLE_SIZE) { + /* don't count the slop buckets in the total */ + ++total; + } + } + } + qsort(count, IPHASH_TABLE_SIZE, sizeof(count[0]), iphash_compare); + p = buf + ap_snprintf(buf, sizeof(buf), + "iphash: total hashed = %u, avg chain = %u, " + "chain lengths (count x len):", + total, total / IPHASH_TABLE_SIZE); + total = 1; + for (i = 1; i < IPHASH_TABLE_SIZE; ++i) { + if (count[i - 1] != count[i]) { + p += ap_snprintf(p, sizeof(buf) - (p - buf), " %ux%u", + total, count[i - 1]); + total = 1; + } + else { + ++total; + } + } + p += ap_snprintf(p, sizeof(buf) - (p - buf), " %ux%u", + total, count[IPHASH_TABLE_SIZE - 1]); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, main_s, buf); +} +#endif + + +/* This hashing function is designed to get good distribution in the cases + * where the server is handling entire "networks" of servers. i.e. a + * whack of /24s. This is probably the most common configuration for + * ISPs with large virtual servers. + * + * NOTE: This function is symmetric (i.e. collapses all 4 octets + * into one), so machine byte order (big/little endianness) does not matter. + * + * Hash function provided by David Hankins. + */ +static ap_inline unsigned hash_inaddr(unsigned key) +{ + key ^= (key >> 16); + return ((key >> 8) ^ key) % IPHASH_TABLE_SIZE; +} + + + +static ipaddr_chain *new_ipaddr_chain(pool *p, + server_rec *s, server_addr_rec *sar) +{ + ipaddr_chain *new; + + new = ap_palloc(p, sizeof(*new)); + new->names = NULL; + new->server = s; + new->sar = sar; + new->next = NULL; + return new; +} + + +static name_chain *new_name_chain(pool *p, server_rec *s, server_addr_rec *sar) +{ + name_chain *new; + + new = ap_palloc(p, sizeof(*new)); + new->server = s; + new->sar = sar; + new->next = NULL; + return new; +} + + +static ap_inline ipaddr_chain *find_ipaddr(struct in_addr *server_ip, + unsigned port) +{ + unsigned bucket; + ipaddr_chain *trav; + unsigned addr; + + /* scan the hash table for an exact match first */ + addr = server_ip->s_addr; + bucket = hash_inaddr(addr); + for (trav = iphash_table[bucket]; trav; trav = trav->next) { + server_addr_rec *sar = trav->sar; + if ((sar->host_addr.s_addr == addr) + && (sar->host_port == 0 || sar->host_port == port + || port == 0)) { + return trav; + } + } + return NULL; +} + + +static ipaddr_chain *find_default_server(unsigned port) +{ + server_addr_rec *sar; + ipaddr_chain *trav; + + for (trav = default_list; trav; trav = trav->next) { + sar = trav->sar; + if (sar->host_port == 0 || sar->host_port == port) { + /* match! */ + return trav; + } + } + return NULL; +} + + +static void dump_vhost_config(FILE *f) +{ + int i; + ipaddr_chain *ic; + name_chain *nc; + char buf[MAX_STRING_LEN]; + + fprintf(f, "VirtualHost configuration:\n"); + for (i = 0; i < IPHASH_TABLE_SIZE; ++i) { + for (ic = iphash_table[i]; ic; ic = ic->next) { + if (ic->sar->host_port == 0) { + ap_snprintf(buf, sizeof(buf), "%pA:*", &ic->sar->host_addr); + } + else { + ap_snprintf(buf, sizeof(buf), "%pA:%u", &ic->sar->host_addr, + ic->sar->host_port); + } + if (ic->names == NULL) { + fprintf(f, "%-22s %s (%s:%u)\n", buf, + ic->server->server_hostname, ic->server->defn_name, + ic->server->defn_line_number); + continue; + } + fprintf(f, "%-22s is a NameVirtualHost\n" + "%22s default server %s (%s:%u)\n", + buf, "", ic->server->server_hostname, + ic->server->defn_name, ic->server->defn_line_number); + for (nc = ic->names; nc; nc = nc->next) { + if (nc->sar->host_port) { + fprintf(f, "%22s port %u ", "", nc->sar->host_port); + } + else { + fprintf(f, "%22s port * ", ""); + } + fprintf(f, "namevhost %s (%s:%u)\n", + nc->server->server_hostname, + nc->server->defn_name, + nc->server->defn_line_number); + } + } + } + if (default_list) { + fprintf(f, "_default_ servers:\n"); + for (ic = default_list; ic; ic = ic->next) { + if (ic->sar->host_port == 0) { + fprintf(f, "port * "); + } + else { + fprintf(f, "port %u ", ic->sar->host_port); + } + fprintf(f, "server %s (%s:%u)\n", + ic->server->server_hostname, ic->server->defn_name, + ic->server->defn_line_number); + } + } +} + +/* compile the tables and such we need to do the run-time vhost lookups */ +void ap_fini_vhost_config(pool *p, server_rec *main_s) +{ + server_addr_rec *sar; + int has_default_vhost_addr; + server_rec *s; + int i; + ipaddr_chain **iphash_table_tail[IPHASH_TABLE_SIZE]; + + /* terminate the name_vhost list */ + *name_vhost_list_tail = NULL; + + /* Main host first */ + s = main_s; + + if (!s->server_hostname) { + s->server_hostname = ap_get_local_host(p); + } + + /* initialize the tails */ + for (i = 0; i < IPHASH_TABLE_SIZE; ++i) { + iphash_table_tail[i] = &iphash_table[i]; + } + + /* The first things to go into the hash table are the NameVirtualHosts + * Since name_vhost_list is in the same order that the directives + * occured in the config file, we'll copy it in that order. + */ + for (sar = name_vhost_list; sar; sar = sar->next) { + unsigned bucket = hash_inaddr(sar->host_addr.s_addr); + ipaddr_chain *new = new_ipaddr_chain(p, NULL, sar); + + *iphash_table_tail[bucket] = new; + iphash_table_tail[bucket] = &new->next; + + /* Notice that what we've done is insert an ipaddr_chain with + * both server and names NULL. Remember that. + */ + } + + /* The next things to go into the hash table are the virtual hosts + * themselves. They're listed off of main_s->next in the reverse + * order they occured in the config file, so we insert them at + * the iphash_table_tail but don't advance the tail. + */ + + for (s = main_s->next; s; s = s->next) { + has_default_vhost_addr = 0; + for (sar = s->addrs; sar; sar = sar->next) { + ipaddr_chain *ic; + + if (sar->host_addr.s_addr == DEFAULT_VHOST_ADDR + || sar->host_addr.s_addr == INADDR_ANY) { + /* add it to default bucket for each appropriate sar + * since we need to do a port test + */ + ipaddr_chain *other; + + other = find_default_server(sar->host_port); + if (other && other->sar->host_port != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s, + "_default_ VirtualHost overlap on port %u," + " the first has precedence", sar->host_port); + } + has_default_vhost_addr = 1; + ic = new_ipaddr_chain(p, s, sar); + ic->next = default_list; + default_list = ic; + } + else { + /* see if it matches something we've already got */ + ic = find_ipaddr(&sar->host_addr, sar->host_port); + + /* the first time we encounter a NameVirtualHost address + * ic->server will be NULL, on subsequent encounters + * ic->names will be non-NULL. + */ + if (ic && (ic->names || ic->server == NULL)) { + name_chain *nc = new_name_chain(p, s, sar); + nc->next = ic->names; + ic->names = nc; + ic->server = s; + if (sar->host_port != ic->sar->host_port) { + /* one of the two is a * port, the other isn't */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, main_s, + "VirtualHost %s:%u -- mixing * " + "ports and non-* ports with " + "a NameVirtualHost address is not supported," + " proceeding with undefined results", + sar->virthost, sar->host_port); + } + } + else if (ic) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s, + "VirtualHost %s:%u overlaps with " + "VirtualHost %s:%u, the first has precedence, " + "perhaps you need a NameVirtualHost directive", + sar->virthost, sar->host_port, + ic->sar->virthost, ic->sar->host_port); + ic->sar = sar; + ic->server = s; + } + else { + unsigned bucket = hash_inaddr(sar->host_addr.s_addr); + + ic = new_ipaddr_chain(p, s, sar); + ic->next = *iphash_table_tail[bucket]; + *iphash_table_tail[bucket] = ic; + } + } + } + + /* Ok now we want to set up a server_hostname if the user was + * silly enough to forget one. + * XXX: This is silly we should just crash and burn. + */ + if (!s->server_hostname) { + if (has_default_vhost_addr) { + s->server_hostname = main_s->server_hostname; + } + else if (!s->addrs) { + /* what else can we do? at this point this vhost has + no configured name, probably because they used + DNS in the VirtualHost statement. It's disabled + anyhow by the host matching code. -djg */ + s->server_hostname = + ap_pstrdup(p, "bogus_host_without_forward_dns"); + } + else { + struct hostent *h; + + if ((h = gethostbyaddr((char *) &(s->addrs->host_addr), + sizeof(struct in_addr), AF_INET))) { + s->server_hostname = ap_pstrdup(p, (char *) h->h_name); + } + else { + /* again, what can we do? They didn't specify a + ServerName, and their DNS isn't working. -djg */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, main_s, + "Failed to resolve server name " + "for %s (check DNS) -- or specify an explicit " + "ServerName", + inet_ntoa(s->addrs->host_addr)); + s->server_hostname = + ap_pstrdup(p, "bogus_host_without_reverse_dns"); + } + } + } + } + + /* now go through and delete any NameVirtualHosts that didn't have any + * hosts associated with them. Lamers. + */ + for (i = 0; i < IPHASH_TABLE_SIZE; ++i) { + ipaddr_chain **pic = &iphash_table[i]; + + while (*pic) { + ipaddr_chain *ic = *pic; + + if (ic->server == NULL) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s, + "NameVirtualHost %s:%u has no VirtualHosts", + ic->sar->virthost, ic->sar->host_port); + *pic = ic->next; + } + else if (ic->names == NULL) { + /* if server != NULL and names == NULL then we're done + * looking at NameVirtualHosts + */ + break; + } + else { + pic = &ic->next; + } + } + } + +#ifdef IPHASH_STATISTICS + dump_iphash_statistics(main_s); +#endif + if (ap_dump_settings) { + dump_vhost_config(stderr); + } +} + + +/***************************************************************************** + * run-time vhost matching functions + */ + +/* Remove :port and optionally a single trailing . from the hostname, this + * canonicalizes it somewhat. + */ +static void fix_hostname(request_rec *r) +{ + const char *hostname = r->hostname; + char *host = ap_getword(r->pool, &hostname, ':'); /* get rid of port */ + size_t l; + + /* trim a trailing . */ + l = strlen(host); + if (l > 0 && host[l-1] == '.') { + host[l-1] = '\0'; + } + + r->hostname = host; +} + + +/* return 1 if host matches ServerName or ServerAliases */ +static int matches_aliases(server_rec *s, const char *host) +{ + int i; + array_header *names; + + /* match ServerName */ + if (!strcasecmp(host, s->server_hostname)) { + return 1; + } + + /* search all the aliases from ServerAlias directive */ + names = s->names; + if (names) { + char **name = (char **) names->elts; + for (i = 0; i < names->nelts; ++i) { + if(!name[i]) continue; + if (!strcasecmp(host, name[i])) + return 1; + } + } + names = s->wild_names; + if (names) { + char **name = (char **) names->elts; + for (i = 0; i < names->nelts; ++i) { + if(!name[i]) continue; + if (!ap_strcasecmp_match(host, name[i])) + return 1; + } + } + return 0; +} + + +/* Suppose a request came in on the same socket as this r, and included + * a header "Host: host:port", would it map to r->server? It's more + * than just that though. When we do the normal matches for each request + * we don't even bother considering Host: etc on non-namevirtualhosts, + * we just call it a match. But here we require the host:port to match + * the ServerName and/or ServerAliases. + */ +API_EXPORT(int) ap_matches_request_vhost(request_rec *r, const char *host, + unsigned port) +{ + server_rec *s; + server_addr_rec *sar; + + s = r->server; + + /* search all the values */ + /* XXX: If this is a NameVirtualHost then we may not be doing the Right Thing + * consider: + * + * NameVirtualHost 10.1.1.1 + * + * ServerName v1 + * + * + * ServerName v2 + * + * + * Suppose r->server is v2, and we're asked to match "10.1.1.1". We'll say + * "yup it's v2", when really it isn't... if a request came in for 10.1.1.1 + * it would really go to v1. + */ + for (sar = s->addrs; sar; sar = sar->next) { + if ((sar->host_port == 0 || port == sar->host_port) + && !strcasecmp(host, sar->virthost)) { + return 1; + } + } + + /* the Port has to match now, because the rest don't have ports associated + * with them. */ + if (port != s->port) { + return 0; + } + + return matches_aliases(s, host); +} + + +static void check_hostalias(request_rec *r) +{ + /* + * Even if the request has a Host: header containing a port we ignore + * that port. We always use the physical port of the socket. There + * are a few reasons for this: + * + * - the default of 80 or 443 for SSL is easier to handle this way + * - there is less of a possibility of a security problem + * - it simplifies the data structure + * - the client may have no idea that a proxy somewhere along the way + * translated the request to another ip:port + * - except for the addresses from the VirtualHost line, none of the other + * names we'll match have ports associated with them + */ + const char *host = r->hostname; + unsigned port = ntohs(r->connection->local_addr.sin_port); + server_rec *s; + server_rec *last_s; + name_chain *src; + + last_s = NULL; + + /* Recall that the name_chain is a list of server_addr_recs, some of + * whose ports may not match. Also each server may appear more than + * once in the chain -- specifically, it will appear once for each + * address from its VirtualHost line which matched. We only want to + * do the full ServerName/ServerAlias comparisons once for each + * server, fortunately we know that all the VirtualHost addresses for + * a single server are adjacent to each other. + */ + + for (src = r->connection->vhost_lookup_data; src; src = src->next) { + server_addr_rec *sar; + + /* We only consider addresses on the name_chain which have a matching + * port + */ + sar = src->sar; + if (sar->host_port != 0 && port != sar->host_port) { + continue; + } + + s = src->server; + + /* does it match the virthost from the sar? */ + if (!strcasecmp(host, sar->virthost)) { + goto found; + } + + if (s == last_s) { + /* we've already done ServerName and ServerAlias checks for this + * vhost + */ + continue; + } + last_s = s; + + if (matches_aliases(s, host)) { + goto found; + } + } + return; + +found: + /* s is the first matching server, we're done */ + r->server = r->connection->server = s; +} + + +static void check_serverpath(request_rec *r) +{ + server_rec *s; + server_rec *last_s; + name_chain *src; + unsigned port = ntohs(r->connection->local_addr.sin_port); + + /* + * This is in conjunction with the ServerPath code in http_core, so we + * get the right host attached to a non- Host-sending request. + * + * See the comment in check_hostalias about how each vhost can be + * listed multiple times. + */ + + last_s = NULL; + for (src = r->connection->vhost_lookup_data; src; src = src->next) { + /* We only consider addresses on the name_chain which have a matching + * port + */ + if (src->sar->host_port != 0 && port != src->sar->host_port) { + continue; + } + + s = src->server; + if (s == last_s) { + continue; + } + last_s = s; + + if (s->path && !strncmp(r->uri, s->path, s->pathlen) && + (s->path[s->pathlen - 1] == '/' || + r->uri[s->pathlen] == '/' || + r->uri[s->pathlen] == '\0')) { + r->server = r->connection->server = s; + return; + } + } +} + + +void ap_update_vhost_from_headers(request_rec *r) +{ + /* must set this for HTTP/1.1 support */ + if (r->hostname || (r->hostname = ap_table_get(r->headers_in, "Host"))) { + fix_hostname(r); + } + /* check if we tucked away a name_chain */ + if (r->connection->vhost_lookup_data) { + if (r->hostname) + check_hostalias(r); + else + check_serverpath(r); + } +} + + +/* Called for a new connection which has a known local_addr. Note that the + * new connection is assumed to have conn->server == main server. + */ +void ap_update_vhost_given_ip(conn_rec *conn) +{ + ipaddr_chain *trav; + unsigned port = ntohs(conn->local_addr.sin_port); + + /* scan the hash table for an exact match first */ + trav = find_ipaddr(&conn->local_addr.sin_addr, port); + if (trav) { + /* save the name_chain for later in case this is a name-vhost */ + conn->vhost_lookup_data = trav->names; + conn->server = trav->server; + return; + } + + /* There's certainly no name-vhosts with this address, they would have + * been matched above. + */ + conn->vhost_lookup_data = NULL; + + /* maybe there's a default server matching this port */ + trav = find_default_server(port); + if (trav) { + conn->server = trav->server; + } + + /* otherwise we're stuck with just the main server */ +} diff --git a/srclib/.cvsignore b/srclib/.cvsignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/srclib/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/srclib/Makefile.in b/srclib/Makefile.in new file mode 100644 index 00000000000..5d725f2af18 --- /dev/null +++ b/srclib/Makefile.in @@ -0,0 +1,9 @@ + +DEPTH = .. +topsrcdir = @topsrcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +SUBDIRS = apr + +include $(topsrcdir)/build/rules.mk diff --git a/srclib/pcre/AUTHORS b/srclib/pcre/AUTHORS new file mode 100644 index 00000000000..bfe1b5d8a46 --- /dev/null +++ b/srclib/pcre/AUTHORS @@ -0,0 +1,6 @@ +Written by: Philip Hazel + +University of Cambridge Computing Service, +Cambridge, England. Phone: +44 1223 334714. + +Copyright (c) 1997-2000 University of Cambridge diff --git a/srclib/pcre/COPYING b/srclib/pcre/COPYING new file mode 100644 index 00000000000..f305033c16c --- /dev/null +++ b/srclib/pcre/COPYING @@ -0,0 +1,32 @@ +PCRE LICENCE +------------ + +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +Written by: Philip Hazel + +University of Cambridge Computing Service, +Cambridge, England. Phone: +44 1223 334714. + +Copyright (c) 1997-2000 University of Cambridge + +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. + +End diff --git a/srclib/pcre/ChangeLog b/srclib/pcre/ChangeLog new file mode 100644 index 00000000000..c594ab50d88 --- /dev/null +++ b/srclib/pcre/ChangeLog @@ -0,0 +1,576 @@ +ChangeLog for PCRE +------------------ + + +Version 3.1 09-Feb-00 +--------------------- + +The only change in this release is the fixing of some bugs in Makefile.in for +the "install" target: + +(1) It was failing to install pcreposix.h. + +(2) It was overwriting the pcre.3 man page with the pcreposix.3 man page. + + +Version 3.0 01-Feb-00 +--------------------- + +1. Add support for the /+ modifier to perltest (to output $` like it does in +pcretest). + +2. Add support for the /g modifier to perltest. + +3. Fix pcretest so that it behaves even more like Perl for /g when the pattern +matches null strings. + +4. Fix perltest so that it doesn't do unwanted things when fed an empty +pattern. Perl treats empty patterns specially - it reuses the most recent +pattern, which is not what we want. Replace // by /(?#)/ in order to avoid this +effect. + +5. The POSIX interface was broken in that it was just handing over the POSIX +captured string vector to pcre_exec(), but (since release 2.00) PCRE has +required a bigger vector, with some working space on the end. This means that +the POSIX wrapper now has to get and free some memory, and copy the results. + +6. Added some simple autoconf support, placing the test data and the +documentation in separate directories, re-organizing some of the +information files, and making it build pcre-config (a GNU standard). Also added +libtool support for building PCRE as a shared library, which is now the +default. + +7. Got rid of the leading zero in the definition of PCRE_MINOR because 08 and +09 are not valid octal constants. Single digits will be used for minor values +less than 10. + +8. Defined REG_EXTENDED and REG_NOSUB as zero in the POSIX header, so that +existing programs that set these in the POSIX interface can use PCRE without +modification. + +9. Added a new function, pcre_fullinfo() with an extensible interface. It can +return all that pcre_info() returns, plus additional data. The pcre_info() +function is retained for compatibility, but is considered to be obsolete. + +10. Added experimental recursion feature (?R) to handle one common case that +Perl 5.6 will be able to do with (?p{...}). + +11. Added support for POSIX character classes like [:alpha:], which Perl is +adopting. + + +Version 2.08 31-Aug-99 +---------------------- + +1. When startoffset was not zero and the pattern began with ".*", PCRE was not +trying to match at the startoffset position, but instead was moving forward to +the next newline as if a previous match had failed. + +2. pcretest was not making use of PCRE_NOTEMPTY when repeating for /g and /G, +and could get into a loop if a null string was matched other than at the start +of the subject. + +3. Added definitions of PCRE_MAJOR and PCRE_MINOR to pcre.h so the version can +be distinguished at compile time, and for completeness also added PCRE_DATE. + +5. Added Paul Sokolovsky's minor changes to make it easy to compile a Win32 DLL +in GnuWin32 environments. + + +Version 2.07 29-Jul-99 +---------------------- + +1. The documentation is now supplied in plain text form and HTML as well as in +the form of man page sources. + +2. C++ compilers don't like assigning (void *) values to other pointer types. +In particular this affects malloc(). Although there is no problem in Standard +C, I've put in casts to keep C++ compilers happy. + +3. Typo on pcretest.c; a cast of (unsigned char *) in the POSIX regexec() call +should be (const char *). + +4. If NOPOSIX is defined, pcretest.c compiles without POSIX support. This may +be useful for non-Unix systems who don't want to bother with the POSIX stuff. +However, I haven't made this a standard facility. The documentation doesn't +mention it, and the Makefile doesn't support it. + +5. The Makefile now contains an "install" target, with editable destinations at +the top of the file. The pcretest program is not installed. + +6. pgrep -V now gives the PCRE version number and date. + +7. Fixed bug: a zero repetition after a literal string (e.g. /abcde{0}/) was +causing the entire string to be ignored, instead of just the last character. + +8. If a pattern like /"([^\\"]+|\\.)*"/ is applied in the normal way to a +non-matching string, it can take a very, very long time, even for strings of +quite modest length, because of the nested recursion. PCRE now does better in +some of these cases. It does this by remembering the last required literal +character in the pattern, and pre-searching the subject to ensure it is present +before running the real match. In other words, it applies a heuristic to detect +some types of certain failure quickly, and in the above example, if presented +with a string that has no trailing " it gives "no match" very quickly. + +9. A new runtime option PCRE_NOTEMPTY causes null string matches to be ignored; +other alternatives are tried instead. + + +Version 2.06 09-Jun-99 +---------------------- + +1. Change pcretest's output for amount of store used to show just the code +space, because the remainder (the data block) varies in size between 32-bit and +64-bit systems. + +2. Added an extra argument to pcre_exec() to supply an offset in the subject to +start matching at. This allows lookbehinds to work when searching for multiple +occurrences in a string. + +3. Added additional options to pcretest for testing multiple occurrences: + + /+ outputs the rest of the string that follows a match + /g loops for multiple occurrences, using the new startoffset argument + /G loops for multiple occurrences by passing an incremented pointer + +4. PCRE wasn't doing the "first character" optimization for patterns starting +with \b or \B, though it was doing it for other lookbehind assertions. That is, +it wasn't noticing that a match for a pattern such as /\bxyz/ has to start with +the letter 'x'. On long subject strings, this gives a significant speed-up. + + +Version 2.05 21-Apr-99 +---------------------- + +1. Changed the type of magic_number from int to long int so that it works +properly on 16-bit systems. + +2. Fixed a bug which caused patterns starting with .* not to work correctly +when the subject string contained newline characters. PCRE was assuming +anchoring for such patterns in all cases, which is not correct because .* will +not pass a newline unless PCRE_DOTALL is set. It now assumes anchoring only if +DOTALL is set at top level; otherwise it knows that patterns starting with .* +must be retried after every newline in the subject. + + +Version 2.04 18-Feb-99 +---------------------- + +1. For parenthesized subpatterns with repeats whose minimum was zero, the +computation of the store needed to hold the pattern was incorrect (too large). +If such patterns were nested a few deep, this could multiply and become a real +problem. + +2. Added /M option to pcretest to show the memory requirement of a specific +pattern. Made -m a synonym of -s (which does this globally) for compatibility. + +3. Subpatterns of the form (regex){n,m} (i.e. limited maximum) were being +compiled in such a way that the backtracking after subsequent failure was +pessimal. Something like (a){0,3} was compiled as (a)?(a)?(a)? instead of +((a)((a)(a)?)?)? with disastrous performance if the maximum was of any size. + + +Version 2.03 02-Feb-99 +---------------------- + +1. Fixed typo and small mistake in man page. + +2. Added 4th condition (GPL supersedes if conflict) and created separate +LICENCE file containing the conditions. + +3. Updated pcretest so that patterns such as /abc\/def/ work like they do in +Perl, that is the internal \ allows the delimiter to be included in the +pattern. Locked out the use of \ as a delimiter. If \ immediately follows +the final delimiter, add \ to the end of the pattern (to test the error). + +4. Added the convenience functions for extracting substrings after a successful +match. Updated pcretest to make it able to test these functions. + + +Version 2.02 14-Jan-99 +---------------------- + +1. Initialized the working variables associated with each extraction so that +their saving and restoring doesn't refer to uninitialized store. + +2. Put dummy code into study.c in order to trick the optimizer of the IBM C +compiler for OS/2 into generating correct code. Apparently IBM isn't going to +fix the problem. + +3. Pcretest: the timing code wasn't using LOOPREPEAT for timing execution +calls, and wasn't printing the correct value for compiling calls. Increased the +default value of LOOPREPEAT, and the number of significant figures in the +times. + +4. Changed "/bin/rm" in the Makefile to "-rm" so it works on Windows NT. + +5. Renamed "deftables" as "dftables" to get it down to 8 characters, to avoid +a building problem on Windows NT with a FAT file system. + + +Version 2.01 21-Oct-98 +---------------------- + +1. Changed the API for pcre_compile() to allow for the provision of a pointer +to character tables built by pcre_maketables() in the current locale. If NULL +is passed, the default tables are used. + + +Version 2.00 24-Sep-98 +---------------------- + +1. Since the (>?) facility is in Perl 5.005, don't require PCRE_EXTRA to enable +it any more. + +2. Allow quantification of (?>) groups, and make it work correctly. + +3. The first character computation wasn't working for (?>) groups. + +4. Correct the implementation of \Z (it is permitted to match on the \n at the +end of the subject) and add 5.005's \z, which really does match only at the +very end of the subject. + +5. Remove the \X "cut" facility; Perl doesn't have it, and (?> is neater. + +6. Remove the ability to specify CASELESS, MULTILINE, DOTALL, and +DOLLAR_END_ONLY at runtime, to make it possible to implement the Perl 5.005 +localized options. All options to pcre_study() were also removed. + +7. Add other new features from 5.005: + + $(?<= positive lookbehind + $(?a*))*/ (a PCRE_EXTRA facility). + + +Version 1.00 18-Nov-97 +---------------------- + +1. Added compile-time macros to support systems such as SunOS4 which don't have +memmove() or strerror() but have other things that can be used instead. + +2. Arranged that "make clean" removes the executables. + + +Version 0.99 27-Oct-97 +---------------------- + +1. Fixed bug in code for optimizing classes with only one character. It was +initializing a 32-byte map regardless, which could cause it to run off the end +of the memory it had got. + +2. Added, conditional on PCRE_EXTRA, the proposed (?>REGEX) construction. + + +Version 0.98 22-Oct-97 +---------------------- + +1. Fixed bug in code for handling temporary memory usage when there are more +back references than supplied space in the ovector. This could cause segfaults. + + +Version 0.97 21-Oct-97 +---------------------- + +1. Added the \X "cut" facility, conditional on PCRE_EXTRA. + +2. Optimized negated single characters not to use a bit map. + +3. Brought error texts together as macro definitions; clarified some of them; +fixed one that was wrong - it said "range out of order" when it meant "invalid +escape sequence". + +4. Changed some char * arguments to const char *. + +5. Added PCRE_NOTBOL and PCRE_NOTEOL (from POSIX). + +6. Added the POSIX-style API wrapper in pcreposix.a and testing facilities in +pcretest. + + +Version 0.96 16-Oct-97 +---------------------- + +1. Added a simple "pgrep" utility to the distribution. + +2. Fixed an incompatibility with Perl: "{" is now treated as a normal character +unless it appears in one of the precise forms "{ddd}", "{ddd,}", or "{ddd,ddd}" +where "ddd" means "one or more decimal digits". + +3. Fixed serious bug. If a pattern had a back reference, but the call to +pcre_exec() didn't supply a large enough ovector to record the related +identifying subpattern, the match always failed. PCRE now remembers the number +of the largest back reference, and gets some temporary memory in which to save +the offsets during matching if necessary, in order to ensure that +backreferences always work. + +4. Increased the compatibility with Perl in a number of ways: + + (a) . no longer matches \n by default; an option PCRE_DOTALL is provided + to request this handling. The option can be set at compile or exec time. + + (b) $ matches before a terminating newline by default; an option + PCRE_DOLLAR_ENDONLY is provided to override this (but not in multiline + mode). The option can be set at compile or exec time. + + (c) The handling of \ followed by a digit other than 0 is now supposed to be + the same as Perl's. If the decimal number it represents is less than 10 + or there aren't that many previous left capturing parentheses, an octal + escape is read. Inside a character class, it's always an octal escape, + even if it is a single digit. + + (d) An escaped but undefined alphabetic character is taken as a literal, + unless PCRE_EXTRA is set. Currently this just reserves the remaining + escapes. + + (e) {0} is now permitted. (The previous item is removed from the compiled + pattern). + +5. Changed all the names of code files so that the basic parts are no longer +than 10 characters, and abolished the teeny "globals.c" file. + +6. Changed the handling of character classes; they are now done with a 32-byte +bit map always. + +7. Added the -d and /D options to pcretest to make it possible to look at the +internals of compilation without having to recompile pcre. + + +Version 0.95 23-Sep-97 +---------------------- + +1. Fixed bug in pre-pass concerning escaped "normal" characters such as \x5c or +\x20 at the start of a run of normal characters. These were being treated as +real characters, instead of the source characters being re-checked. + + +Version 0.94 18-Sep-97 +---------------------- + +1. The functions are now thread-safe, with the caveat that the global variables +containing pointers to malloc() and free() or alternative functions are the +same for all threads. + +2. Get pcre_study() to generate a bitmap of initial characters for non- +anchored patterns when this is possible, and use it if passed to pcre_exec(). + + +Version 0.93 15-Sep-97 +---------------------- + +1. /(b)|(:+)/ was computing an incorrect first character. + +2. Add pcre_study() to the API and the passing of pcre_extra to pcre_exec(), +but not actually doing anything yet. + +3. Treat "-" characters in classes that cannot be part of ranges as literals, +as Perl does (e.g. [-az] or [az-]). + +4. Set the anchored flag if a branch starts with .* or .*? because that tests +all possible positions. + +5. Split up into different modules to avoid including unneeded functions in a +compiled binary. However, compile and exec are still in one module. The "study" +function is split off. + +6. The character tables are now in a separate module whose source is generated +by an auxiliary program - but can then be edited by hand if required. There are +now no calls to isalnum(), isspace(), isdigit(), isxdigit(), tolower() or +toupper() in the code. + +7. Turn the malloc/free funtions variables into pcre_malloc and pcre_free and +make them global. Abolish the function for setting them, as the caller can now +set them directly. + + +Version 0.92 11-Sep-97 +---------------------- + +1. A repeat with a fixed maximum and a minimum of 1 for an ordinary character +(e.g. /a{1,3}/) was broken (I mis-optimized it). + +2. Caseless matching was not working in character classes if the characters in +the pattern were in upper case. + +3. Make ranges like [W-c] work in the same way as Perl for caseless matching. + +4. Make PCRE_ANCHORED public and accept as a compile option. + +5. Add an options word to pcre_exec() and accept PCRE_ANCHORED and +PCRE_CASELESS at run time. Add escapes \A and \I to pcretest to cause it to +pass them. + +6. Give an error if bad option bits passed at compile or run time. + +7. Add PCRE_MULTILINE at compile and exec time, and (?m) as well. Add \M to +pcretest to cause it to pass that flag. + +8. Add pcre_info(), to get the number of identifying subpatterns, the stored +options, and the first character, if set. + +9. Recognize C+ or C{n,m} where n >= 1 as providing a fixed starting character. + + +Version 0.91 10-Sep-97 +---------------------- + +1. PCRE was failing to diagnose unlimited repeats of subpatterns that could +match the empty string as in /(a*)*/. It was looping and ultimately crashing. + +2. PCRE was looping on encountering an indefinitely repeated back reference to +a subpattern that had matched an empty string, e.g. /(a|)\1*/. It now does what +Perl does - treats the match as successful. + +**** diff --git a/srclib/pcre/INSTALL b/srclib/pcre/INSTALL new file mode 100644 index 00000000000..d63a78fef9b --- /dev/null +++ b/srclib/pcre/INSTALL @@ -0,0 +1,185 @@ +Basic Installation +================== + + These are generic installation instructions that apply to systems that +can run the `configure' shell script - Unix systems and any that imitate +it. They are not specific to PCRE. There are PCRE-specific instructions +for non-Unix systems in the file NON-UNIX. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/srclib/pcre/LICENCE b/srclib/pcre/LICENCE new file mode 100644 index 00000000000..f305033c16c --- /dev/null +++ b/srclib/pcre/LICENCE @@ -0,0 +1,32 @@ +PCRE LICENCE +------------ + +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +Written by: Philip Hazel + +University of Cambridge Computing Service, +Cambridge, England. Phone: +44 1223 334714. + +Copyright (c) 1997-2000 University of Cambridge + +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. + +End diff --git a/srclib/pcre/Makefile.in b/srclib/pcre/Makefile.in new file mode 100644 index 00000000000..b8374247778 --- /dev/null +++ b/srclib/pcre/Makefile.in @@ -0,0 +1,215 @@ + +# Makefile.in for PCRE (Perl-Compatible Regular Expression) library. + +#---------------------------------------------------------------------------# +# To build mingw32 DLL uncomment the next two lines. This addition for # +# mingw32 was contributed by . I (Philip # +# Hazel) don't know anything about it! There are some additional targets at # +# the bottom of this Makefile. # +#---------------------------------------------------------------------------# +# +# include dll.mk +# DLL_LDFLAGS=-s + + +#---------------------------------------------------------------------------# +# The next few lines are modified by "configure" to insert data that it is # +# given in its arguments, or which it finds out for itself. # +#---------------------------------------------------------------------------# + +# BINDIR is the directory in which the pgrep command is installed. +# INCDIR is the directory in which the public header file pcre.h is installed. +# LIBDIR is the directory in which the libraries are installed. +# MANDIR is the directory in which the man pages are installed. +# The pcretest program, as it is a test program, does not get installed +# anywhere. + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +BINDIR = @bindir@ +LIBDIR = @libdir@ +INCDIR = @includedir@ +MANDIR = @mandir@ + +CC = @CC@ +CFLAGS = @CFLAGS@ +RANLIB = @RANLIB@ + +# LIBTOOL defaults to "", which cuts out the building of shared libraries. +# If "configure" is called with --enable-shared-libraries, then LIBTOOL is +# set to "libtool", which causes shared libraries to be built, and LIBSUFFIX +# is set to "la" instead of "a", which causes the shared libraries to be +# installed. + +LIBTOOL = @LIBTOOL@ +LIBSUFFIX = @LIBSUFFIX@ + +# These are the version numbers for the shared libraries + +PCRELIBVERSION = @PCRE_LIB_VERSION@ +PCREPOSIXLIBVERSION = @PCRE_POSIXLIB_VERSION@ + + +#---------------------------------------------------------------------------# +# A copy of install-sh is in this distribution and is used by default. # +#---------------------------------------------------------------------------# + +INSTALL = ./install-sh -c +INSTALL_DATA = ${INSTALL} -m 644 + + +#---------------------------------------------------------------------------# +# For almost all systems, the command to create a library is "ar cq", but # +# there is at least one where it is different, to make this configurable. # +# However, I haven't got round to learning how to make "configure" find # +# this out for itself. It is necessary to use a command such as # +# "make AR='ar -rc'" if you need to vary this. # +#---------------------------------------------------------------------------# + +AR = ar cq + + +############################################################################## + + +OBJ = maketables.o get.o study.o pcre.o +LOBJ = maketables.lo get.lo study.lo pcre.lo + +all: libtool libpcre.$(LIBSUFFIX) libpcreposix.$(LIBSUFFIX) pcretest pgrep + +libtool: config.guess config.sub ltconfig ltmain.sh + @if test "$(LIBTOOL)" = "libtool"; then \ + echo '--- Building libtool ---'; \ + ./ltconfig ./ltmain.sh; \ + echo '--- Built libtool ---'; fi + +pgrep: libpcre.$(LIBSUFFIX) pgrep.o + @echo ' ' + @echo '--- Building pgrep utility' + @echo ' ' + $(LIBTOOL) $(CC) $(CFLAGS) -o pgrep pgrep.o libpcre.$(LIBSUFFIX) + +pcretest: libpcre.$(LIBSUFFIX) libpcreposix.$(LIBSUFFIX) pcretest.o + @echo ' ' + @echo '--- Building pcretest testing program' + @echo ' ' + $(LIBTOOL) $(PURIFY) $(CC) $(CFLAGS) -o pcretest pcretest.o \ + libpcre.$(LIBSUFFIX) libpcreposix.$(LIBSUFFIX) + +libpcre.a: $(OBJ) + @echo ' ' + @echo '--- Building static library: libpcre' + @echo ' ' + -rm -f libpcre.a + $(AR) libpcre.a $(OBJ) + $(RANLIB) libpcre.a + +libpcre.la: $(OBJ) + @echo ' ' + @echo '--- Building shared library: libpcre' + @echo ' ' + -rm -f libpcre.la + libtool $(CC) -version-info '$(PCRELIBVERSION)' -o libpcre.la -rpath $(LIBDIR) $(LOBJ) + +libpcreposix.a: pcreposix.o + @echo ' ' + @echo '--- Building static library: libpcreposix' + @echo ' ' + -rm -f libpcreposix.a + $(AR) libpcreposix.a pcreposix.o + $(RANLIB) libpcreposix.a + +libpcreposix.la: pcreposix.o + @echo ' ' + @echo '--- Building shared library: libpcreposix' + @echo ' ' + -rm -f libpcreposix.la + libtool $(CC) -version-info '$(PCREPOSIXLIBVERSION)' -o libpcreposix.la -rpath $(LIBDIR) pcreposix.lo + +pcre.o: chartables.c pcre.c pcre.h internal.h config.h Makefile + $(LIBTOOL) $(CC) -c $(CFLAGS) pcre.c + +pcreposix.o: pcreposix.c pcreposix.h internal.h pcre.h config.h Makefile + $(LIBTOOL) $(CC) -c $(CFLAGS) pcreposix.c + +maketables.o: maketables.c pcre.h internal.h config.h Makefile + $(LIBTOOL) $(CC) -c $(CFLAGS) maketables.c + +get.o: get.c pcre.h internal.h config.h Makefile + $(LIBTOOL) $(CC) -c $(CFLAGS) get.c + +study.o: study.c pcre.h internal.h config.h Makefile + $(LIBTOOL) $(CC) -c $(CFLAGS) study.c + +pcretest.o: pcretest.c pcre.h config.h Makefile + $(CC) -c $(CFLAGS) pcretest.c + +pgrep.o: pgrep.c pcre.h Makefile config.h + $(CC) -c $(CFLAGS) pgrep.c + +# An auxiliary program makes the default character table source + +chartables.c: dftables + ./dftables >chartables.c + +dftables: dftables.c maketables.c pcre.h internal.h config.h Makefile + $(CC) -o dftables $(CFLAGS) dftables.c + +install: all + $(LIBTOOL) $(INSTALL_DATA) libpcre.$(LIBSUFFIX) $(LIBDIR)/libpcre.$(LIBSUFFIX) + $(LIBTOOL) $(INSTALL_DATA) libpcreposix.$(LIBSUFFIX) $(LIBDIR)/libpcreposix.$(LIBSUFFIX) + $(INSTALL_DATA) pcre.h $(INCDIR)/pcre.h + $(INSTALL_DATA) pcreposix.h $(INCDIR)/pcreposix.h + $(INSTALL_DATA) doc/pcre.3 $(MANDIR)/man3/pcre.3 + $(INSTALL_DATA) doc/pcreposix.3 $(MANDIR)/man3/pcreposix.3 + $(INSTALL_DATA) doc/pgrep.1 $(MANDIR)/man1/pgrep.1 + @if test "$(LIBTOOL)" = "libtool"; then \ + echo ' '; \ + echo '--- Rebuilding pgrep to use installed shared library ---'; \ + echo $(CC) $(CFLAGS) -o pgrep pgrep.o -L$(LIBDIR) -lpcre; \ + $(CC) $(CFLAGS) -o pgrep pgrep.o -L$(LIBDIR) -lpcre; \ + echo '--- Rebuilding pcretest to use installed shared library ---'; \ + echo $(CC) $(CFLAGS) -o pcretest pcretest.o -L$(LIBDIR) -lpcre -lpcreposix; \ + $(CC) $(CFLAGS) -o pcretest pcretest.o -L$(LIBDIR) -lpcre -lpcreposix; \ + fi + $(INSTALL) pgrep $(BINDIR)/pgrep + $(INSTALL) pcre-config $(BINDIR)/pcre-config + +# We deliberately omit dftables and chartables.c from 'make clean'; once made +# chartables.c shouldn't change, and if people have edited the tables by hand, +# you don't want to throw them away. + +clean:; -rm -rf *.o *.lo *.a *.la .libs pcretest pgrep testtry + +# But "make distclean" should get back to a virgin distribution + +distclean: clean + -rm -f chartables.c libtool pcre-config pcre.h \ + Makefile config.h config.status config.log config.cache + +check: runtest + +runtest: all + ./RunTest + +######## MINGW32 ############### MINGW32 ############### MINGW32 ############# + +# This addition for mingw32 was contributed by Paul Sokolovsky +# . I (PH) don't know anything about it! + +dll: _dll libpcre.dll.a pgrep_d pcretest_d + +_dll: + $(MAKE) CFLAGS=-DSTATIC pcre.dll + +pcre.dll: $(OBJ) pcreposix.o pcre.def +libpcre.dll.a: pcre.def + +pgrep_d: libpcre.dll.a pgrep.o + $(CC) $(CFLAGS) -L. -o pgrep pgrep.o -lpcre.dll + +pcretest_d: libpcre.dll.a pcretest.o + $(PURIFY) $(CC) $(CFLAGS) -L. -o pcretest pcretest.o -lpcre.dll + +# End diff --git a/srclib/pcre/NEWS b/srclib/pcre/NEWS new file mode 100644 index 00000000000..4c80bd6833f --- /dev/null +++ b/srclib/pcre/NEWS @@ -0,0 +1,46 @@ +News about PCRE releases +------------------------ + +Release 3.0 01-Feb-00 +--------------------- + +1. A "configure" script is now used to configure PCRE for Unix systems. It +builds a Makefile, a config.h file, and the pcre-config script. + +2. PCRE is built as a shared library by default. + +3. There is support for POSIX classes such as [:alpha:]. + +5. There is an experimental recursion feature. + +---------------------------------------------------------------------------- + IMPORTANT FOR THOSE UPGRADING FROM VERSIONS BEFORE 2.00 + +Please note that there has been a change in the API such that a larger +ovector is required at matching time, to provide some additional workspace. +The new man page has details. This change was necessary in order to support +some of the new functionality in Perl 5.005. + + IMPORTANT FOR THOSE UPGRADING FROM VERSION 2.00 + +Another (I hope this is the last!) change has been made to the API for the +pcre_compile() function. An additional argument has been added to make it +possible to pass over a pointer to character tables built in the current +locale by pcre_maketables(). To use the default tables, this new arguement +should be passed as NULL. + + IMPORTANT FOR THOSE UPGRADING FROM VERSION 2.05 + +Yet another (and again I hope this really is the last) change has been made +to the API for the pcre_exec() function. An additional argument has been +added to make it possible to start the match other than at the start of the +subject string. This is important if there are lookbehinds. The new man +page has the details, but you just want to convert existing programs, all +you need to do is to stick in a new fifth argument to pcre_exec(), with a +value of zero. For example, change + + pcre_exec(pattern, extra, subject, length, options, ovec, ovecsize) +to + pcre_exec(pattern, extra, subject, length, 0, options, ovec, ovecsize) + +**** diff --git a/srclib/pcre/NON-UNIX-USE b/srclib/pcre/NON-UNIX-USE new file mode 100644 index 00000000000..09a743245ba --- /dev/null +++ b/srclib/pcre/NON-UNIX-USE @@ -0,0 +1,50 @@ +Compiling PCRE on non-Unix systems +---------------------------------- + +If you want to compile PCRE for a non-Unix system, note that it consists +entirely of code written in Standard C, and so should compile successfully +on any machine with a Standard C compiler and library, using normal compiling +commands to do the following: + +(1) Copy or rename the file config.in as config.h, and change the macros that +define HAVE_STRERROR and HAVE_MEMMOVE to define them as 1 rather than 0. +Unfortunately, because of the way Unix autoconf works, the default setting has +to be 0. + +(2) Copy or rename the file pcre.in as pcre.h, and change the macro definitions +for PCRE_MAJOR, PCRE_MINOR, and PCRE_DATE near its start to the values set in +configure.in. + +(3) Compile dftables.c as a stand-alone program, and then run it with +the standard output sent to chartables.c. This generates a set of standard +character tables. + +(4) Compile maketables.c, get.c, study.c and pcre.c and link them all +together into an object library in whichever form your system keeps such +libraries. This is the pcre library (chartables.c gets included by means of an +#include directive). + +(5) Similarly, compile pcreposix.c and link it as the pcreposix library. + +(6) Compile the test program pcretest.c. This needs the functions in the +pcre and pcreposix libraries when linking. + +(7) Run pcretest on the testinput files in the testdata directory, and check +that the output matches the corresponding testoutput files. You must use the +-i option when checking testinput2. + +If you have a system without "configure" but where you can use a Makefile, edit +Makefile.in to create Makefile, substituting suitable values for the variables +at the head of the file. + +Some help in building a Win32 DLL of PCRE in GnuWin32 environments was +contributed by Paul.Sokolovsky@technologist.com. These environments are +Mingw32 (http://www.xraylith.wisc.edu/~khan/software/gnu-win32/) and +CygWin (http://sourceware.cygnus.com/cygwin/). Paul comments: + + For CygWin, set CFLAGS=-mno-cygwin, and do 'make dll'. You'll get + pcre.dll (containing pcreposix also), libpcre.dll.a, and dynamically + linked pgrep and pcretest. If you have /bin/sh, run RunTest (three + main test go ok, locale not supported). + +**** diff --git a/srclib/pcre/README b/srclib/pcre/README new file mode 100644 index 00000000000..90aaf4d6a65 --- /dev/null +++ b/srclib/pcre/README @@ -0,0 +1,239 @@ +README file for PCRE (Perl-compatible regular expression library) +----------------------------------------------------------------- + +The latest release of PCRE is always available from + + ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-xxx.tar.gz + +Please read the NEWS file if you are upgrading from a previous release. + + +Building PCRE on a Unix system +------------------------------ + +To build PCRE on a Unix system, run the "configure" command in the PCRE +distribution directory. This is a standard GNU "autoconf" configuration script, +for which generic instructions are supplied in INSTALL. On many systems just +running "./configure" is sufficient, but the usual methods of changing standard +defaults are available. For example + +CFLAGS='-O2 -Wall' ./configure --prefix=/opt/local + +specifies that the C compiler should be run with the flags '-O2 -Wall' instead +of the default, and that "make install" should install PCRE under /opt/local +instead of the default /usr/local. The "configure" script builds thre files: + +. Makefile is built by copying Makefile.in and making substitutions. +. config.h is built by copying config.in and making substitutions. +. pcre-config is built by copying pcre-config.in and making substitutions. + +Once "configure" has run, you can run "make". It builds two libraries called +libpcre and libpcreposix, a test program called pcretest, and the pgrep +command. You can use "make install" to copy these, and the public header file +pcre.h, to appropriate live directories on your system, in the normal way. + +Running "make install" also installs the command pcre-config, which can be used +to recall information about the PCRE configuration and installation. For +example, + + pcre-config --version + +prints the version number, and + + pcre-config --libs + +outputs information about where the library is installed. This command can be +included in makefiles for programs that use PCRE, saving the programmer from +having to remember too many details. + + +Shared libraries on Unix systems +-------------------------------- + +The default distribution builds PCRE as two shared libraries. This support is +new and experimental and may not work on all systems. It relies on the +"libtool" scripts - these are distributed with PCRE. It should build a +"libtool" script and use this to compile and link shared libraries, which are +placed in a subdirectory called .libs. The programs pcretest and pgrep are +built to use these uninstalled libraries by means of wrapper scripts. When you +use "make install" to install shared libraries, pgrep and pcretest are +automatically re-built to use the newly installed libraries. However, only +pgrep is installed, as pcretest is really just a test program. + +To build PCRE using static libraries you must use --disable-shared when +configuring it. For example + +./configure --prefix=/usr/gnu --disable-shared + +Then run "make" in the usual way. + + +Building on non-Unix systems +---------------------------- + +For a non-Unix system, read the comments in the file NON-UNIX-USE. PCRE has +been compiled on Windows systems and on Macintoshes, but I don't know the +details because I don't use those systems. It should be straightforward to +build PCRE on any system that has a Standard C compiler, because it uses only +Standard C functions. + + +Testing PCRE +------------ + +To test PCRE on a Unix system, run the RunTest script in the pcre directory. +(This can also be run by "make runtest" or "make check".) For other systems, +see the instruction in NON-UNIX-USE. + +The script runs the pcretest test program (which is documented in +doc/pcretest.txt) on each of the testinput files (in the testdata directory) in +turn, and compares the output with the contents of the corresponding testoutput +file. A file called testtry is used to hold the output from pcretest. To run +pcretest on just one of the test files, give its number as an argument to +RunTest, for example: + + RunTest 3 + +The first and third test files can also be fed directly into the perltest +script to check that Perl gives the same results. The third file requires the +additional features of release 5.005, which is why it is kept separate from the +main test input, which needs only Perl 5.004. In the long run, when 5.005 is +widespread, these two test files may get amalgamated. + +The second set of tests check pcre_info(), pcre_study(), pcre_copy_substring(), +pcre_get_substring(), pcre_get_substring_list(), error detection and run-time +flags that are specific to PCRE, as well as the POSIX wrapper API. + +The fourth set of tests checks pcre_maketables(), the facility for building a +set of character tables for a specific locale and using them instead of the +default tables. The tests make use of the "fr" (French) locale. Before running +the test, the script checks for the presence of this locale by running the +"locale" command. If that command fails, or if it doesn't include "fr" in the +list of available locales, the fourth test cannot be run, and a comment is +output to say why. If running this test produces instances of the error + + ** Failed to set locale "fr" + +in the comparison output, it means that locale is not available on your system, +despite being listed by "locale". This does not mean that PCRE is broken. + +PCRE has its own native API, but a set of "wrapper" functions that are based on +the POSIX API are also supplied in the library libpcreposix.a. Note that this +just provides a POSIX calling interface to PCRE: the regular expressions +themselves still follow Perl syntax and semantics. The header file +for the POSIX-style functions is called pcreposix.h. The official POSIX name is +regex.h, but I didn't want to risk possible problems with existing files of +that name by distributing it that way. To use it with an existing program that +uses the POSIX API, it will have to be renamed or pointed at by a link. + + +Character tables +---------------- + +PCRE uses four tables for manipulating and identifying characters. The final +argument of the pcre_compile() function is a pointer to a block of memory +containing the concatenated tables. A call to pcre_maketables() can be used to +generate a set of tables in the current locale. If the final argument for +pcre_compile() is passed as NULL, a set of default tables that is built into +the binary is used. + +The source file called chartables.c contains the default set of tables. This is +not supplied in the distribution, but is built by the program dftables +(compiled from dftables.c), which uses the ANSI C character handling functions +such as isalnum(), isalpha(), isupper(), islower(), etc. to build the table +sources. This means that the default C locale which is set for your system will +control the contents of these default tables. You can change the default tables +by editing chartables.c and then re-building PCRE. If you do this, you should +probably also edit Makefile to ensure that the file doesn't ever get +re-generated. + +The first two 256-byte tables provide lower casing and case flipping functions, +respectively. The next table consists of three 32-byte bit maps which identify +digits, "word" characters, and white space, respectively. These are used when +building 32-byte bit maps that represent character classes. + +The final 256-byte table has bits indicating various character types, as +follows: + + 1 white space character + 2 letter + 4 decimal digit + 8 hexadecimal digit + 16 alphanumeric or '_' + 128 regular expression metacharacter or binary zero + +You should not alter the set of characters that contain the 128 bit, as that +will cause PCRE to malfunction. + + +Manifest +-------- + +The distribution should contain the following files: + +(A) The actual source files of the PCRE library functions and their + headers: + + dftables.c auxiliary program for building chartables.c + get.c ) + maketables.c ) + study.c ) source of + pcre.c ) the functions + pcreposix.c ) + pcre.in "source" for the header for the external API; pcre.h + is built from this by "configure" + pcreposix.h header for the external POSIX wrapper API + internal.h header for internal use + config.in template for config.h, which is built by configure + +(B) Auxiliary files: + + AUTHORS information about the author of PCRE + ChangeLog log of changes to the code + INSTALL generic installation instructions + LICENCE conditions for the use of PCRE + COPYING the same, using GNU's standard name + Makefile.in template for Unix Makefile, which is built by configure + NEWS important changes in this release + NON-UNIX-USE notes on building PCRE on non-Unix systems + README this file + RunTest a Unix shell script for running tests + config.guess ) files used by libtool, + config.sub ) used only when building a shared library + configure a configuring shell script (built by autoconf) + configure.in the autoconf input used to build configure + doc/Tech.Notes notes on the encoding + doc/pcre.3 man page source for the PCRE functions + doc/pcre.html HTML version + doc/pcre.txt plain text version + doc/pcreposix.3 man page source for the POSIX wrapper API + doc/pcreposix.html HTML version + doc/pcreposix.txt plain text version + doc/pcretest.txt documentation of test program + doc/perltest.txt documentation of Perl test program + doc/pgrep.1 man page source for the pgrep utility + doc/pgrep.html HTML version + doc/pgrep.txt plain text version + install-sh a shell script for installing files + ltconfig ) files used to build "libtool", + ltmain.sh ) used only when building a shared library + pcretest.c test program + perltest Perl test program + pgrep.c source of a grep utility that uses PCRE + pcre-config.in source of script which retains PCRE information + testdata/testinput1 test data, compatible with Perl 5.004 and 5.005 + testdata/testinput2 test data for error messages and non-Perl things + testdata/testinput3 test data, compatible with Perl 5.005 + testdata/testinput4 test data for locale-specific tests + testdata/testoutput1 test results corresponding to testinput1 + testdata/testoutput2 test results corresponding to testinput2 + testdata/testoutput3 test results corresponding to testinput3 + testdata/testoutput4 test results corresponding to testinput4 + +(C) Auxiliary files for Win32 DLL + + dll.mk + pcre.def + +Philip Hazel +February 2000 diff --git a/srclib/pcre/RunTest b/srclib/pcre/RunTest new file mode 100755 index 00000000000..85eeb6245e3 --- /dev/null +++ b/srclib/pcre/RunTest @@ -0,0 +1,94 @@ +#! /bin/sh + +# Run PCRE tests + +cf=diff + +# Select which tests to run; if no selection, run all + +do1=no +do2=no +do3=no +do4=no + +while [ $# -gt 0 ] ; do + case $1 in + 1) do1=yes;; + 2) do2=yes;; + 3) do3=yes;; + 4) do4=yes;; + *) echo "Unknown test number $1"; exit 1;; + esac + shift +done + +if [ $do1 = no -a $do2 = no -a $do3 = no -a $do4 = no ] ; then + do1=yes + do2=yes + do3=yes + do4=yes +fi + +# Primary test, Perl-compatible + +if [ $do1 = yes ] ; then + echo "Testing main functionality (Perl compatible)" + ./pcretest testdata/testinput1 testtry + if [ $? = 0 ] ; then + $cf testtry testdata/testoutput1 + if [ $? != 0 ] ; then exit 1; fi + else exit 1 + fi +fi + +# PCRE tests that are not Perl-compatible - API & error tests, mostly + +if [ $do2 = yes ] ; then + echo "Testing API and error handling (not Perl compatible)" + ./pcretest -i testdata/testinput2 testtry + if [ $? = 0 ] ; then + $cf testtry testdata/testoutput2 + if [ $? != 0 ] ; then exit 1; fi + else exit 1 + fi +fi + +# Additional Perl-compatible tests for Perl 5.005's new features + +if [ $do3 = yes ] ; then + echo "Testing Perl 5.005 features (Perl 5.005 compatible)" + ./pcretest testdata/testinput3 testtry + if [ $? = 0 ] ; then + $cf testtry testdata/testoutput3 + if [ $? != 0 ] ; then exit 1; fi + else exit 1 + fi +fi + +if [ $do1 = yes -a $do2 = yes -a $do3 = yes ] ; then + echo "The three main tests all ran OK" + echo " " +fi + +# Locale-specific tests, provided the "fr" locale is available + +if [ $do4 = yes ] ; then + locale -a | grep '^fr$' >/dev/null + if [ $? -eq 0 ] ; then + echo "Testing locale-specific features (using 'fr' locale)" + ./pcretest testdata/testinput4 testtry + if [ $? = 0 ] ; then + $cf testtry testdata/testoutput4 + if [ $? != 0 ] ; then exit 1; fi + echo "Locale test ran OK" + echo " " + else exit 1 + fi + else + echo "Cannot test locale-specific features - 'fr' locale not found," + echo "or the \"locale\" command is not available to check for it." + echo " " + fi +fi + +# End diff --git a/srclib/pcre/config.guess b/srclib/pcre/config.guess new file mode 100644 index 00000000000..e1b58717080 --- /dev/null +++ b/srclib/pcre/config.guess @@ -0,0 +1,1121 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999 +# Free Software Foundation, Inc. +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Written by Per Bothner . +# The master version of this file is at the FSF in /home/gd/gnu/lib. +# Please send patches to . +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit system type (host/target name). +# +# Only a few systems have been added to this list; please add others +# (but try to keep the structure clean). +# + +# Use $HOST_CC if defined. $CC may point to a cross-compiler +if test x"$CC_FOR_BUILD" = x; then + if test x"$HOST_CC" != x; then + CC_FOR_BUILD="$HOST_CC" + else + if test x"$CC" != x; then + CC_FOR_BUILD="$CC" + else + CC_FOR_BUILD=cc + fi + fi +fi + + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 8/24/94.) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +dummy=dummy-$$ +trap 'rm -f $dummy.c $dummy.o $dummy; exit 1' 1 2 15 + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + cat <$dummy.s + .globl main + .ent main +main: + .frame \$30,0,\$26,0 + .prologue 0 + .long 0x47e03d80 # implver $0 + lda \$2,259 + .long 0x47e20c21 # amask $2,$1 + srl \$1,8,\$2 + sll \$2,2,\$2 + sll \$0,3,\$0 + addl \$1,\$0,\$0 + addl \$2,\$0,\$0 + ret \$31,(\$26),1 + .end main +EOF + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + ./$dummy + case "$?" in + 7) + UNAME_MACHINE="alpha" + ;; + 15) + UNAME_MACHINE="alphaev5" + ;; + 14) + UNAME_MACHINE="alphaev56" + ;; + 10) + UNAME_MACHINE="alphapca56" + ;; + 16) + UNAME_MACHINE="alphaev6" + ;; + esac + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-cbm-sysv4 + exit 0;; + amiga:NetBSD:*:*) + echo m68k-cbm-netbsd${UNAME_RELEASE} + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + arc64:OpenBSD:*:*) + echo mips64el-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hkmips:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + arm32:NetBSD:*:*) + echo arm-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + SR2?01:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + atari*:NetBSD:*:*) + echo m68k-atari-netbsd${UNAME_RELEASE} + exit 0 ;; + atari*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + sun3*:NetBSD:*:*) + echo m68k-sun-netbsd${UNAME_RELEASE} + exit 0 ;; + sun3*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:NetBSD:*:*) + echo m68k-apple-netbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + macppc:NetBSD:*:*) + echo powerpc-apple-netbsd${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy \ + && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i?86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:4) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` + if /usr/sbin/lsattr -EHl ${IBM_CPU_ID} | grep POWER >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=4.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + sed 's/^ //' << EOF >$dummy.c + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy` + rm -f $dummy.c $dummy + esac + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i?86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + hppa*:OpenBSD:*:*) + echo hppa-unknown-openbsd + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} + exit 0 ;; + CRAY*T3E:*:*:*) + echo alpha-cray-unicosmk${UNAME_RELEASE} + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F300:UNIX_System_V:*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + F301:UNIX_System_V:*:*) + echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'` + exit 0 ;; + hp3[0-9][05]:NetBSD:*:*) + echo m68k-hp-netbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + i?86:BSD/386:*:* | i?86:BSD/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + if test -x /usr/bin/objformat; then + if test "elf" = "`/usr/bin/objformat`"; then + echo ${UNAME_MACHINE}-unknown-freebsdelf`echo ${UNAME_RELEASE}|sed -e 's/[-_].*//'` + exit 0 + fi + fi + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:NetBSD:*:*) + echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*//'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i386-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + *:Linux:*:*) + + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + ld_help_string=`cd /; ld --help 2>&1` + ld_supported_emulations=`echo $ld_help_string \ + | sed -ne '/supported emulations:/!d + s/[ ][ ]*/ /g + s/.*supported emulations: *// + s/ .*// + p'` + case "$ld_supported_emulations" in + *ia64) + echo "${UNAME_MACHINE}-unknown-linux" + exit 0 + ;; + i?86linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 + ;; + i?86coff) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 + ;; + sparclinux) + echo "${UNAME_MACHINE}-unknown-linux-gnuaout" + exit 0 + ;; + armlinux) + echo "${UNAME_MACHINE}-unknown-linux-gnuaout" + exit 0 + ;; + elf32arm*) + echo "${UNAME_MACHINE}-unknown-linux-gnu" + exit 0 + ;; + armelf_linux*) + echo "${UNAME_MACHINE}-unknown-linux-gnu" + exit 0 + ;; + m68klinux) + echo "${UNAME_MACHINE}-unknown-linux-gnuaout" + exit 0 + ;; + elf32ppc) + # Determine Lib Version + cat >$dummy.c < +#if defined(__GLIBC__) +extern char __libc_version[]; +extern char __libc_release[]; +#endif +main(argc, argv) + int argc; + char *argv[]; +{ +#if defined(__GLIBC__) + printf("%s %s\n", __libc_version, __libc_release); +#else + printf("unkown\n"); +#endif + return 0; +} +EOF + LIBC="" + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null + if test "$?" = 0 ; then + ./$dummy | grep 1\.99 > /dev/null + if test "$?" = 0 ; then + LIBC="libc1" + fi + fi + rm -f $dummy.c $dummy + echo powerpc-unknown-linux-gnu${LIBC} + exit 0 + ;; + esac + + if test "${UNAME_MACHINE}" = "alpha" ; then + sed 's/^ //' <$dummy.s + .globl main + .ent main + main: + .frame \$30,0,\$26,0 + .prologue 0 + .long 0x47e03d80 # implver $0 + lda \$2,259 + .long 0x47e20c21 # amask $2,$1 + srl \$1,8,\$2 + sll \$2,2,\$2 + sll \$0,3,\$0 + addl \$1,\$0,\$0 + addl \$2,\$0,\$0 + ret \$31,(\$26),1 + .end main +EOF + LIBC="" + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + ./$dummy + case "$?" in + 7) + UNAME_MACHINE="alpha" + ;; + 15) + UNAME_MACHINE="alphaev5" + ;; + 14) + UNAME_MACHINE="alphaev56" + ;; + 10) + UNAME_MACHINE="alphapca56" + ;; + 16) + UNAME_MACHINE="alphaev6" + ;; + esac + + objdump --private-headers $dummy | \ + grep ld.so.1 > /dev/null + if test "$?" = 0 ; then + LIBC="libc1" + fi + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ; exit 0 + elif test "${UNAME_MACHINE}" = "mips" ; then + cat >$dummy.c </dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + else + # Either a pre-BFD a.out linker (linux-gnuoldld) + # or one that does not give us useful --help. + # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout. + # If ld does not provide *any* "supported emulations:" + # that means it is gnuoldld. + echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations:" + test $? != 0 && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0 + + case "${UNAME_MACHINE}" in + i?86) + VENDOR=pc; + ;; + *) + VENDOR=unknown; + ;; + esac + # Determine whether the default compiler is a.out or elf + cat >$dummy.c < +#ifdef __cplusplus + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __ELF__ +# ifdef __GLIBC__ +# if __GLIBC__ >= 2 + printf ("%s-${VENDOR}-linux-gnu\n", argv[1]); +# else + printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); +# endif +# else + printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); +# endif +#else + printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]); +#endif + return 0; +} +EOF + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + fi ;; +# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions +# are messed up and put the nodename in both sysname and nodename. + i?86:DYNIX/ptx:4*:*) + echo i386-sequent-sysv4 + exit 0 ;; + i?86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i?86:*:5:7*) + # Fixed at (any) Pentium or better + UNAME_MACHINE=i586 + if [ ${UNAME_SYSTEM} = "UnixWare" ] ; then + echo ${UNAME_MACHINE}-sco-sysv${UNAME_RELEASE}uw${UNAME_VERSION} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE} + fi + exit 0 ;; + i?86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + pc:*:*:*) + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i?86:LynxOS:2.*:* | i?86:LynxOS:3.[01]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:*:6*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:QNX:*:4*) + echo i386-qnx-qnx${UNAME_VERSION} + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +#if !defined (ultrix) + printf ("vax-dec-bsd\n"); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm $dummy.c $dummy && exit 0 +rm -f $dummy.c $dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +#echo '(Unable to guess system type)' 1>&2 + +exit 1 diff --git a/srclib/pcre/config.in b/srclib/pcre/config.in new file mode 100644 index 00000000000..7631d468cd3 --- /dev/null +++ b/srclib/pcre/config.in @@ -0,0 +1,28 @@ + +/* On Unix systems config.in is converted by configure into config.h. PCRE is +written in Standard C, but there are a few non-standard things it can cope +with, allowing it to run on SunOS4 and other "close to standard" systems. + +On a non-Unix system you should just copy this file into config.h and change +the definitions of HAVE_STRERROR and HAVE_MEMMOVE to 1. Unfortunately, because +of the way autoconf works, these cannot be made the defaults. */ + +/* Define to empty if the keyword does not work. */ + +#undef const + +/* Define to `unsigned' if doesn't define size_t. */ + +#undef size_t + +/* The following two definitions are mainly for the benefit of SunOS4, which +doesn't have the strerror() or memmove() functions that should be present in +all Standard C libraries. The macros should normally be defined with the value +1 for other systems, but unfortunately we can't make this the default because +"configure" files generated by autoconf will only change 0 to 1; they won't +change 1 to 0 if the functions are not found. */ + +#define HAVE_STRERROR 0 +#define HAVE_MEMMOVE 0 + +/* End */ diff --git a/srclib/pcre/config.sub b/srclib/pcre/config.sub new file mode 100644 index 00000000000..28426bb8fa0 --- /dev/null +++ b/srclib/pcre/config.sub @@ -0,0 +1,1232 @@ +#! /bin/sh +# Configuration validation subroutine script, version 1.1. +# Copyright (C) 1991, 92-97, 1998, 1999 Free Software Foundation, Inc. +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +if [ x$1 = x ] +then + echo Configuration name missing. 1>&2 + echo "Usage: $0 CPU-MFR-OPSYS" 1>&2 + echo "or $0 ALIAS" 1>&2 + echo where ALIAS is a recognized configuration type. 1>&2 + exit 1 +fi + +# First pass through any local machine types. +case $1 in + *local*) + echo $1 + exit 0 + ;; + *) + ;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + linux-gnu*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \ + | arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \ + | 580 | i960 | h8300 \ + | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \ + | alpha | alphaev[4-7] | alphaev56 | alphapca5[67] \ + | we32k | ns16k | clipper | i370 | sh | powerpc | powerpcle \ + | 1750a | dsp16xx | pdp11 | mips16 | mips64 | mipsel | mips64el \ + | mips64orion | mips64orionel | mipstx39 | mipstx39el \ + | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \ + | mips64vr5000 | miprs64vr5000el | mcore \ + | sparc | sparclet | sparclite | sparc64 | sparcv9 | v850 | c4x \ + | thumb | d10v | fr30) + basic_machine=$basic_machine-unknown + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | h8500 | w65 | pj | pjl) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i[34567]86) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + # FIXME: clean up the formatting here. + vax-* | tahoe-* | i[34567]86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \ + | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \ + | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \ + | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \ + | xmp-* | ymp-* \ + | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* | hppa2.0n-* \ + | alpha-* | alphaev[4-7]-* | alphaev56-* | alphapca5[67]-* \ + | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \ + | clipper-* | orion-* \ + | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \ + | sparc64-* | sparcv9-* | sparc86x-* | mips16-* | mips64-* | mipsel-* \ + | mips64el-* | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \ + | mipstx39-* | mipstx39el-* | mcore-* \ + | f301-* | armv*-* | t3e-* \ + | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \ + | thumb-* | v850-* | d30v-* | tic30-* | c30-* | fr30-* ) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-cbm + ;; + amigaos | amigados) + basic_machine=m68k-cbm + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-cbm + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [ctj]90-cray) + basic_machine=c90-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i[34567]86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i[34567]86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i[34567]86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i[34567]86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + i386-go32 | go32) + basic_machine=i386-unknown + os=-go32 + ;; + i386-mingw32 | mingw32) + basic_machine=i386-unknown + os=-mingw32 + ;; + i386-qnx | qnx) + basic_machine=i386-qnx + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mipsel*-linux*) + basic_machine=mipsel-unknown + os=-linux-gnu + ;; + mips*-linux*) + basic_machine=mips-unknown + os=-linux-gnu + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + msdos) + basic_machine=i386-unknown + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + np1) + basic_machine=np1-gould + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexen) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexen-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=rs6000-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=t3e-cray + os=-unicos + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + mips) + if [ x$os = x-linux-gnu ]; then + basic_machine=mips-unknown + else + basic_machine=mips-mips + fi + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sparc | sparcv9) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + c4x*) + basic_machine=c4x-none + os=-coff + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -rhapsody* | -opened* | -openstep* | -oskit*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -qnx) + os=-qnx4 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -*MiNT) + os=-mint + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f301-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -*MiNT) + vendor=atari + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os diff --git a/srclib/pcre/configure.in b/srclib/pcre/configure.in new file mode 100644 index 00000000000..a947f19d2dc --- /dev/null +++ b/srclib/pcre/configure.in @@ -0,0 +1,75 @@ +dnl Process this file with autoconf to produce a configure script. + +dnl This is required at the start; the name is the name of a file +dnl it should be seeing, to verify it is in the same directory. + +AC_INIT(dftables.c) + +dnl Arrange to build config.h from config.in. Note that pcre.h is +dnl built differently, as it is just a "substitution" file. +dnl Manual says this macro should come right after AC_INIT. +AC_CONFIG_HEADER(config.h:config.in) + +dnl Provide the current PCRE version information. Do not use numbers +dnl with leading zeros for the minor version, as they end up in a C +dnl macro, and may be treated as octal constants. Stick to single +dnl digits for minor numbers less than 10. There are unlikely to be +dnl that many releases anyway. + +PCRE_MAJOR=3 +PCRE_MINOR=1 +PCRE_DATE=09-Feb-2000 +PCRE_VERSION=${PCRE_MAJOR}.${PCRE_MINOR} + +dnl Provide versioning information for libtool shared libraries that +dnl are built by default on Unix systems. + +PCRE_LIB_VERSION=0:0:0 +PCRE_POSIXLIB_VERSION=0:0:0 + +dnl Checks for programs. + +AC_PROG_CC +AC_PROG_RANLIB + +dnl Checks for header files. + +AC_HEADER_STDC +AC_CHECK_HEADERS(limits.h) + +dnl Checks for typedefs, structures, and compiler characteristics. + +AC_C_CONST +AC_TYPE_SIZE_T + +dnl Checks for library functions. + +AC_CHECK_FUNCS(memmove strerror) + +dnl Handle --enable-shared-libraries + +LIBTOOL=libtool +LIBSUFFIX=la +AC_ARG_ENABLE(shared, +[ --disable-shared build PCRE as a static library], +if test "$enableval" = "no"; then + LIBTOOL= + LIBSUFFIX=a +fi +) + +dnl "Export" these variables + +AC_SUBST(HAVE_MEMMOVE) +AC_SUBST(HAVE_STRERROR) +AC_SUBST(LIBTOOL) +AC_SUBST(LIBSUFFIX) +AC_SUBST(PCRE_MAJOR) +AC_SUBST(PCRE_MINOR) +AC_SUBST(PCRE_DATE) +AC_SUBST(PCRE_VERSION) +AC_SUBST(PCRE_LIB_VERSION) +AC_SUBST(PCRE_POSIXLIB_VERSION) + +dnl This must be last; it determines what files are written +AC_OUTPUT(Makefile pcre.h:pcre.in pcre-config,[chmod a+x pcre-config]) diff --git a/srclib/pcre/dftables.c b/srclib/pcre/dftables.c new file mode 100644 index 00000000000..d572dfd3e6f --- /dev/null +++ b/srclib/pcre/dftables.c @@ -0,0 +1,148 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +Written by: Philip Hazel + + Copyright (c) 1997-2000 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- + +See the file Tech.Notes for some information on the internals. +*/ + + +/* This is a support program to generate the file chartables.c, containing +character tables of various kinds. They are built according to the default C +locale and used as the default tables by PCRE. Now that pcre_maketables is +a function visible to the outside world, we make use of its code from here in +order to be consistent. */ + +#include +#include +#include + +#include "internal.h" + +#define DFTABLES /* maketables.c notices this */ +#include "maketables.c" + + +int main(void) +{ +int i; +unsigned const char *tables = pcre_maketables(); + +printf( + "/*************************************************\n" + "* Perl-Compatible Regular Expressions *\n" + "*************************************************/\n\n" + "/* This file is automatically written by the dftables auxiliary \n" + "program. If you edit it by hand, you might like to edit the Makefile to \n" + "prevent its ever being regenerated.\n\n" + "This file is #included in the compilation of pcre.c to build the default\n" + "character tables which are used when no tables are passed to the compile\n" + "function. */\n\n" + "static unsigned char pcre_default_tables[] = {\n\n" + "/* This table is a lower casing table. */\n\n"); + +printf(" "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) printf("\n "); + printf("%3d", *tables++); + if (i != 255) printf(","); + } +printf(",\n\n"); + +printf("/* This table is a case flipping table. */\n\n"); + +printf(" "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) printf("\n "); + printf("%3d", *tables++); + if (i != 255) printf(","); + } +printf(",\n\n"); + +printf( + "/* This table contains bit maps for various character classes.\n" + "Each map is 32 bytes long and the bits run from the least\n" + "significant end of each byte. The classes that have their own\n" + "maps are: space, xdigit, digit, upper, lower, word, graph\n" + "print, punct, and cntrl. Other classes are built from combinations. */\n\n"); + +printf(" "); +for (i = 0; i < cbit_length; i++) + { + if ((i & 7) == 0 && i != 0) + { + if ((i & 31) == 0) printf("\n"); + printf("\n "); + } + printf("0x%02x", *tables++); + if (i != cbit_length - 1) printf(","); + } +printf(",\n\n"); + +printf( + "/* This table identifies various classes of character by individual bits:\n" + " 0x%02x white space character\n" + " 0x%02x letter\n" + " 0x%02x decimal digit\n" + " 0x%02x hexadecimal digit\n" + " 0x%02x alphanumeric or '_'\n" + " 0x%02x regular expression metacharacter or binary zero\n*/\n\n", + ctype_space, ctype_letter, ctype_digit, ctype_xdigit, ctype_word, + ctype_meta); + +printf(" "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) + { + printf(" /* "); + if (isprint(i-8)) printf(" %c -", i-8); + else printf("%3d-", i-8); + if (isprint(i-1)) printf(" %c ", i-1); + else printf("%3d", i-1); + printf(" */\n "); + } + printf("0x%02x", *tables++); + if (i != 255) printf(","); + } + +printf("};/* "); +if (isprint(i-8)) printf(" %c -", i-8); + else printf("%3d-", i-8); +if (isprint(i-1)) printf(" %c ", i-1); + else printf("%3d", i-1); +printf(" */\n\n/* End of chartables.c */\n"); + +return 0; +} + +/* End of dftables.c */ diff --git a/srclib/pcre/dll.mk b/srclib/pcre/dll.mk new file mode 100644 index 00000000000..d8b728e57e4 --- /dev/null +++ b/srclib/pcre/dll.mk @@ -0,0 +1,60 @@ +# dll.mk - auxilary Makefile to easy build dll's for mingw32 target +# ver. 0.6 of 1999-03-25 +# +# Homepage of this makefile - http://www.is.lg.ua/~paul/devel/ +# Homepage of original mingw32 project - +# http://www.fu.is.saga-u.ac.jp/~colin/gcc.html +# +# How to use: +# This makefile can: +# 1. Create automatical .def file from list of objects +# 2. Create .dll from objects and .def file, either automatical, or your +# hand-written (maybe) file, which must have same basename as dll +# WARNING! There MUST be object, which name match dll's name. Make sux. +# 3. Create import library from .def (as for .dll, only its name required, +# not dll itself) +# By convention implibs for dll have .dll.a suffix, e.g. libstuff.dll.a +# Why not just libstuff.a? 'Cos that's name for static lib, ok? +# Process divided into 3 phases because: +# 1. Pre-existent .def possible +# 2. Generating implib is enough time-consuming +# +# Variables: +# DLL_LDLIBS - libs for linking dll +# DLL_LDFLAGS - flags for linking dll +# +# By using $(DLL_SUFFIX) instead of 'dll', e.g. stuff.$(DLL_SUFFIX) +# you may help porting makefiles to other platforms +# +# Put this file in your make's include path (e.g. main include dir, for +# more information see include section in make doc). Put in the beginning +# of your own Makefile line "include dll.mk". Specify dependences, e.g.: +# +# Do all stuff in one step +# libstuff.dll.a: $(OBJECTS) stuff.def +# stuff.def: $(OBJECTS) +# +# Steps separated, pre-provided .def, link with user32 +# +# DLL_LDLIBS=-luser32 +# stuff.dll: $(OBJECTS) +# libstuff.dll.a: $(OBJECTS) + + +DLLWRAP=dllwrap +DLLTOOL=dlltool + +DLL_SUFFIX=dll + +.SUFFIXES: .o .$(DLL_SUFFIX) + +_%.def: %.o + $(DLLTOOL) --export-all --output-def $@ $^ + +%.$(DLL_SUFFIX): %.o + $(DLLWRAP) --dllname $(notdir $@) --driver-name $(CC) --def $*.def -o $@ $(filter %.o,$^) $(DLL_LDFLAGS) $(DLL_LDLIBS) + +lib%.$(DLL_SUFFIX).a:%.def + $(DLLTOOL) --dllname $(notdir $*.dll) --def $< --output-lib $@ + +# End diff --git a/srclib/pcre/doc/Tech.Notes b/srclib/pcre/doc/Tech.Notes new file mode 100644 index 00000000000..03904db3cca --- /dev/null +++ b/srclib/pcre/doc/Tech.Notes @@ -0,0 +1,242 @@ +Technical Notes about PCRE +-------------------------- + +Many years ago I implemented some regular expression functions to an algorithm +suggested by Martin Richards. These were not Unix-like in form, and were quite +restricted in what they could do by comparison with Perl. The interesting part +about the algorithm was that the amount of space required to hold the compiled +form of an expression was known in advance. The code to apply an expression did +not operate by backtracking, as the Henry Spencer and Perl code does, but +instead checked all possibilities simultaneously by keeping a list of current +states and checking all of them as it advanced through the subject string. (In +the terminology of Jeffrey Friedl's book, it was a "DFA algorithm".) When the +pattern was all used up, all remaining states were possible matches, and the +one matching the longest subset of the subject string was chosen. This did not +necessarily maximize the individual wild portions of the pattern, as is +expected in Unix and Perl-style regular expressions. + +By contrast, the code originally written by Henry Spencer and subsequently +heavily modified for Perl actually compiles the expression twice: once in a +dummy mode in order to find out how much store will be needed, and then for +real. The execution function operates by backtracking and maximizing (or, +optionally, minimizing in Perl) the amount of the subject that matches +individual wild portions of the pattern. This is an "NFA algorithm" in Friedl's +terminology. + +For the set of functions that forms PCRE (which are unrelated to those +mentioned above), I tried at first to invent an algorithm that used an amount +of store bounded by a multiple of the number of characters in the pattern, to +save on compiling time. However, because of the greater complexity in Perl +regular expressions, I couldn't do this. In any case, a first pass through the +pattern is needed, in order to find internal flag settings like (?i) at top +level. So PCRE works by running a very degenerate first pass to calculate a +maximum store size, and then a second pass to do the real compile - which may +use a bit less than the predicted amount of store. The idea is that this is +going to turn out faster because the first pass is degenerate and the second +pass can just store stuff straight into the vector. It does make the compiling +functions bigger, of course, but they have got quite big anyway to handle all +the Perl stuff. + +The compiled form of a pattern is a vector of bytes, containing items of +variable length. The first byte in an item is an opcode, and the length of the +item is either implicit in the opcode or contained in the data bytes which +follow it. A list of all the opcodes follows: + +Opcodes with no following data +------------------------------ + +These items are all just one byte long + + OP_END end of pattern + OP_ANY match any character + OP_SOD match start of data: \A + OP_CIRC ^ (start of data, or after \n in multiline) + OP_NOT_WORD_BOUNDARY \W + OP_WORD_BOUNDARY \w + OP_NOT_DIGIT \D + OP_DIGIT \d + OP_NOT_WHITESPACE \S + OP_WHITESPACE \s + OP_NOT_WORDCHAR \W + OP_WORDCHAR \w + OP_EODN match end of data or \n at end: \Z + OP_EOD match end of data: \z + OP_DOLL $ (end of data, or before \n in multiline) + OP_RECURSE match the pattern recursively + + +Repeating single characters +--------------------------- + +The common repeats (*, +, ?) when applied to a single character appear as +two-byte items using the following opcodes: + + OP_STAR + OP_MINSTAR + OP_PLUS + OP_MINPLUS + OP_QUERY + OP_MINQUERY + +Those with "MIN" in their name are the minimizing versions. Each is followed by +the character that is to be repeated. Other repeats make use of + + OP_UPTO + OP_MINUPTO + OP_EXACT + +which are followed by a two-byte count (most significant first) and the +repeated character. OP_UPTO matches from 0 to the given number. A repeat with a +non-zero minimum and a fixed maximum is coded as an OP_EXACT followed by an +OP_UPTO (or OP_MINUPTO). + + +Repeating character types +------------------------- + +Repeats of things like \d are done exactly as for single characters, except +that instead of a character, the opcode for the type is stored in the data +byte. The opcodes are: + + OP_TYPESTAR + OP_TYPEMINSTAR + OP_TYPEPLUS + OP_TYPEMINPLUS + OP_TYPEQUERY + OP_TYPEMINQUERY + OP_TYPEUPTO + OP_TYPEMINUPTO + OP_TYPEEXACT + + +Matching a character string +--------------------------- + +The OP_CHARS opcode is followed by a one-byte count and then that number of +characters. If there are more than 255 characters in sequence, successive +instances of OP_CHARS are used. + + +Character classes +----------------- + +OP_CLASS is used for a character class, provided there are at least two +characters in the class. If there is only one character, OP_CHARS is used for a +positive class, and OP_NOT for a negative one (that is, for something like +[^a]). Another set of repeating opcodes (OP_NOTSTAR etc.) are used for a +repeated, negated, single-character class. The normal ones (OP_STAR etc.) are +used for a repeated positive single-character class. + +OP_CLASS is followed by a 32-byte bit map containing a 1 bit for every +character that is acceptable. The bits are counted from the least significant +end of each byte. + + +Back references +--------------- + +OP_REF is followed by a single byte containing the reference number. + + +Repeating character classes and back references +----------------------------------------------- + +Single-character classes are handled specially (see above). This applies to +OP_CLASS and OP_REF. In both cases, the repeat information follows the base +item. The matching code looks at the following opcode to see if it is one of + + OP_CRSTAR + OP_CRMINSTAR + OP_CRPLUS + OP_CRMINPLUS + OP_CRQUERY + OP_CRMINQUERY + OP_CRRANGE + OP_CRMINRANGE + +All but the last two are just single-byte items. The others are followed by +four bytes of data, comprising the minimum and maximum repeat counts. + + +Brackets and alternation +------------------------ + +A pair of non-capturing (round) brackets is wrapped round each expression at +compile time, so alternation always happens in the context of brackets. +Non-capturing brackets use the opcode OP_BRA, while capturing brackets use +OP_BRA+1, OP_BRA+2, etc. [Note for North Americans: "bracket" to some English +speakers, including myself, can be round, square, curly, or pointy. Hence this +usage.] + +A bracket opcode is followed by two bytes which give the offset to the next +alternative OP_ALT or, if there aren't any branches, to the matching KET +opcode. Each OP_ALT is followed by two bytes giving the offset to the next one, +or to the KET opcode. + +OP_KET is used for subpatterns that do not repeat indefinitely, while +OP_KETRMIN and OP_KETRMAX are used for indefinite repetitions, minimally or +maximally respectively. All three are followed by two bytes giving (as a +positive number) the offset back to the matching BRA opcode. + +If a subpattern is quantified such that it is permitted to match zero times, it +is preceded by one of OP_BRAZERO or OP_BRAMINZERO. These are single-byte +opcodes which tell the matcher that skipping this subpattern entirely is a +valid branch. + +A subpattern with an indefinite maximum repetition is replicated in the +compiled data its minimum number of times (or once with a BRAZERO if the +minimum is zero), with the final copy terminating with a KETRMIN or KETRMAX as +appropriate. + +A subpattern with a bounded maximum repetition is replicated in a nested +fashion up to the maximum number of times, with BRAZERO or BRAMINZERO before +each replication after the minimum, so that, for example, (abc){2,5} is +compiled as (abc)(abc)((abc)((abc)(abc)?)?)?. The 200-bracket limit does not +apply to these internally generated brackets. + + +Assertions +---------- + +Forward assertions are just like other subpatterns, but starting with one of +the opcodes OP_ASSERT or OP_ASSERT_NOT. Backward assertions use the opcodes +OP_ASSERTBACK and OP_ASSERTBACK_NOT, and the first opcode inside the assertion +is OP_REVERSE, followed by a two byte count of the number of characters to move +back the pointer in the subject string. A separate count is present in each +alternative of a lookbehind assertion, allowing them to have different fixed +lengths. + + +Once-only subpatterns +--------------------- + +These are also just like other subpatterns, but they start with the opcode +OP_ONCE. + + +Conditional subpatterns +----------------------- + +These are like other subpatterns, but they start with the opcode OP_COND. If +the condition is a back reference, this is stored at the start of the +subpattern using the opcode OP_CREF followed by one byte containing the +reference number. Otherwise, a conditional subpattern will always start with +one of the assertions. + + +Changing options +---------------- + +If any of the /i, /m, or /s options are changed within a parenthesized group, +an OP_OPT opcode is compiled, followed by one byte containing the new settings +of these flags. If there are several alternatives in a group, there is an +occurrence of OP_OPT at the start of all those following the first options +change, to set appropriate options for the start of the alternative. +Immediately after the end of the group there is another such item to reset the +flags to their previous values. Other changes of flag within the pattern can be +handled entirely at compile time, and so do not cause anything to be put into +the compiled data. + + +Philip Hazel +February 2000 diff --git a/srclib/pcre/doc/pcre.3 b/srclib/pcre/doc/pcre.3 new file mode 100644 index 00000000000..bd435e9ecde --- /dev/null +++ b/srclib/pcre/doc/pcre.3 @@ -0,0 +1,1702 @@ +.TH PCRE 3 +.SH NAME +pcre - Perl-compatible regular expressions. +.SH SYNOPSIS +.B #include +.PP +.SM +.br +.B pcre *pcre_compile(const char *\fIpattern\fR, int \fIoptions\fR, +.ti +5n +.B const char **\fIerrptr\fR, int *\fIerroffset\fR, +.ti +5n +.B const unsigned char *\fItableptr\fR); +.PP +.br +.B pcre_extra *pcre_study(const pcre *\fIcode\fR, int \fIoptions\fR, +.ti +5n +.B const char **\fIerrptr\fR); +.PP +.br +.B int pcre_exec(const pcre *\fIcode\fR, "const pcre_extra *\fIextra\fR," +.ti +5n +.B "const char *\fIsubject\fR," int \fIlength\fR, int \fIstartoffset\fR, +.ti +5n +.B int \fIoptions\fR, int *\fIovector\fR, int \fIovecsize\fR); +.PP +.br +.B int pcre_copy_substring(const char *\fIsubject\fR, int *\fIovector\fR, +.ti +5n +.B int \fIstringcount\fR, int \fIstringnumber\fR, char *\fIbuffer\fR, +.ti +5n +.B int \fIbuffersize\fR); +.PP +.br +.B int pcre_get_substring(const char *\fIsubject\fR, int *\fIovector\fR, +.ti +5n +.B int \fIstringcount\fR, int \fIstringnumber\fR, +.ti +5n +.B const char **\fIstringptr\fR); +.PP +.br +.B int pcre_get_substring_list(const char *\fIsubject\fR, +.ti +5n +.B int *\fIovector\fR, int \fIstringcount\fR, "const char ***\fIlistptr\fR);" +.PP +.br +.B const unsigned char *pcre_maketables(void); +.PP +.br +.B int pcre_fullinfo(const pcre *\fIcode\fR, "const pcre_extra *\fIextra\fR," +.ti +5n +.B int \fIwhat\fR, void *\fIwhere\fR); +.PP +.br +.B int pcre_info(const pcre *\fIcode\fR, int *\fIoptptr\fR, int +.B *\fIfirstcharptr\fR); +.PP +.br +.B char *pcre_version(void); +.PP +.br +.B void *(*pcre_malloc)(size_t); +.PP +.br +.B void (*pcre_free)(void *); + + + +.SH DESCRIPTION +The PCRE library is a set of functions that implement regular expression +pattern matching using the same syntax and semantics as Perl 5, with just a few +differences (see below). The current implementation corresponds to Perl 5.005, +with some additional features from the Perl development release. + +PCRE has its own native API, which is described in this document. There is also +a set of wrapper functions that correspond to the POSIX regular expression API. +These are described in the \fBpcreposix\fR documentation. + +The native API function prototypes are defined in the header file \fBpcre.h\fR, +and on Unix systems the library itself is called \fBlibpcre.a\fR, so can be +accessed by adding \fB-lpcre\fR to the command for linking an application which +calls it. The header file defines the macros PCRE_MAJOR and PCRE_MINOR to +contain the major and minor release numbers for the library. Applications can +use these to include support for different releases. + +The functions \fBpcre_compile()\fR, \fBpcre_study()\fR, and \fBpcre_exec()\fR +are used for compiling and matching regular expressions, while +\fBpcre_copy_substring()\fR, \fBpcre_get_substring()\fR, and +\fBpcre_get_substring_list()\fR are convenience functions for extracting +captured substrings from a matched subject string. The function +\fBpcre_maketables()\fR is used (optionally) to build a set of character tables +in the current locale for passing to \fBpcre_compile()\fR. + +The function \fBpcre_fullinfo()\fR is used to find out information about a +compiled pattern; \fBpcre_info()\fR is an obsolete version which returns only +some of the available information, but is retained for backwards compatibility. +The function \fBpcre_version()\fR returns a pointer to a string containing the +version of PCRE and its date of release. + +The global variables \fBpcre_malloc\fR and \fBpcre_free\fR initially contain +the entry points of the standard \fBmalloc()\fR and \fBfree()\fR functions +respectively. PCRE calls the memory management functions via these variables, +so a calling program can replace them if it wishes to intercept the calls. This +should be done before calling any PCRE functions. + + +.SH MULTI-THREADING +The PCRE functions can be used in multi-threading applications, with the +proviso that the memory management functions pointed to by \fBpcre_malloc\fR +and \fBpcre_free\fR are shared by all threads. + +The compiled form of a regular expression is not altered during matching, so +the same compiled pattern can safely be used by several threads at once. + + +.SH COMPILING A PATTERN +The function \fBpcre_compile()\fR is called to compile a pattern into an +internal form. The pattern is a C string terminated by a binary zero, and +is passed in the argument \fIpattern\fR. A pointer to a single block of memory +that is obtained via \fBpcre_malloc\fR is returned. This contains the +compiled code and related data. The \fBpcre\fR type is defined for this for +convenience, but in fact \fBpcre\fR is just a typedef for \fBvoid\fR, since the +contents of the block are not externally defined. It is up to the caller to +free the memory when it is no longer required. +.PP +The size of a compiled pattern is roughly proportional to the length of the +pattern string, except that each character class (other than those containing +just a single character, negated or not) requires 33 bytes, and repeat +quantifiers with a minimum greater than one or a bounded maximum cause the +relevant portions of the compiled pattern to be replicated. +.PP +The \fIoptions\fR argument contains independent bits that affect the +compilation. It should be zero if no options are required. Some of the options, +in particular, those that are compatible with Perl, can also be set and unset +from within the pattern (see the detailed description of regular expressions +below). For these options, the contents of the \fIoptions\fR argument specifies +their initial settings at the start of compilation and execution. The +PCRE_ANCHORED option can be set at the time of matching as well as at compile +time. +.PP +If \fIerrptr\fR is NULL, \fBpcre_compile()\fR returns NULL immediately. +Otherwise, if compilation of a pattern fails, \fBpcre_compile()\fR returns +NULL, and sets the variable pointed to by \fIerrptr\fR to point to a textual +error message. The offset from the start of the pattern to the character where +the error was discovered is placed in the variable pointed to by +\fIerroffset\fR, which must not be NULL. If it is, an immediate error is given. +.PP +If the final argument, \fItableptr\fR, is NULL, PCRE uses a default set of +character tables which are built when it is compiled, using the default C +locale. Otherwise, \fItableptr\fR must be the result of a call to +\fBpcre_maketables()\fR. See the section on locale support below. +.PP +The following option bits are defined in the header file: + + PCRE_ANCHORED + +If this bit is set, the pattern is forced to be "anchored", that is, it is +constrained to match only at the start of the string which is being searched +(the "subject string"). This effect can also be achieved by appropriate +constructs in the pattern itself, which is the only way to do it in Perl. + + PCRE_CASELESS + +If this bit is set, letters in the pattern match both upper and lower case +letters. It is equivalent to Perl's /i option. + + PCRE_DOLLAR_ENDONLY + +If this bit is set, a dollar metacharacter in the pattern matches only at the +end of the subject string. Without this option, a dollar also matches +immediately before the final character if it is a newline (but not before any +other newlines). The PCRE_DOLLAR_ENDONLY option is ignored if PCRE_MULTILINE is +set. There is no equivalent to this option in Perl. + + PCRE_DOTALL + +If this bit is set, a dot metacharater in the pattern matches all characters, +including newlines. Without it, newlines are excluded. This option is +equivalent to Perl's /s option. A negative class such as [^a] always matches a +newline character, independent of the setting of this option. + + PCRE_EXTENDED + +If this bit is set, whitespace data characters in the pattern are totally +ignored except when escaped or inside a character class, and characters between +an unescaped # outside a character class and the next newline character, +inclusive, are also ignored. This is equivalent to Perl's /x option, and makes +it possible to include comments inside complicated patterns. Note, however, +that this applies only to data characters. Whitespace characters may never +appear within special character sequences in a pattern, for example within the +sequence (?( which introduces a conditional subpattern. + + PCRE_EXTRA + +This option was invented in order to turn on additional functionality of PCRE +that is incompatible with Perl, but it is currently of very little use. When +set, any backslash in a pattern that is followed by a letter that has no +special meaning causes an error, thus reserving these combinations for future +expansion. By default, as in Perl, a backslash followed by a letter with no +special meaning is treated as a literal. There are at present no other features +controlled by this option. It can also be set by a (?X) option setting within a +pattern. + + PCRE_MULTILINE + +By default, PCRE treats the subject string as consisting of a single "line" of +characters (even if it actually contains several newlines). The "start of line" +metacharacter (^) matches only at the start of the string, while the "end of +line" metacharacter ($) matches only at the end of the string, or before a +terminating newline (unless PCRE_DOLLAR_ENDONLY is set). This is the same as +Perl. + +When PCRE_MULTILINE it is set, the "start of line" and "end of line" constructs +match immediately following or immediately before any newline in the subject +string, respectively, as well as at the very start and end. This is equivalent +to Perl's /m option. If there are no "\\n" characters in a subject string, or +no occurrences of ^ or $ in a pattern, setting PCRE_MULTILINE has no +effect. + + PCRE_UNGREEDY + +This option inverts the "greediness" of the quantifiers so that they are not +greedy by default, but become greedy if followed by "?". It is not compatible +with Perl. It can also be set by a (?U) option setting within the pattern. + + +.SH STUDYING A PATTERN +When a pattern is going to be used several times, it is worth spending more +time analyzing it in order to speed up the time taken for matching. The +function \fBpcre_study()\fR takes a pointer to a compiled pattern as its first +argument, and returns a pointer to a \fBpcre_extra\fR block (another \fBvoid\fR +typedef) containing additional information about the pattern; this can be +passed to \fBpcre_exec()\fR. If no additional information is available, NULL +is returned. + +The second argument contains option bits. At present, no options are defined +for \fBpcre_study()\fR, and this argument should always be zero. + +The third argument for \fBpcre_study()\fR is a pointer to an error message. If +studying succeeds (even if no data is returned), the variable it points to is +set to NULL. Otherwise it points to a textual error message. + +At present, studying a pattern is useful only for non-anchored patterns that do +not have a single fixed starting character. A bitmap of possible starting +characters is created. + + +.SH LOCALE SUPPORT +PCRE handles caseless matching, and determines whether characters are letters, +digits, or whatever, by reference to a set of tables. The library contains a +default set of tables which is created in the default C locale when PCRE is +compiled. This is used when the final argument of \fBpcre_compile()\fR is NULL, +and is sufficient for many applications. + +An alternative set of tables can, however, be supplied. Such tables are built +by calling the \fBpcre_maketables()\fR function, which has no arguments, in the +relevant locale. The result can then be passed to \fBpcre_compile()\fR as often +as necessary. For example, to build and use tables that are appropriate for the +French locale (where accented characters with codes greater than 128 are +treated as letters), the following code could be used: + + setlocale(LC_CTYPE, "fr"); + tables = pcre_maketables(); + re = pcre_compile(..., tables); + +The tables are built in memory that is obtained via \fBpcre_malloc\fR. The +pointer that is passed to \fBpcre_compile\fR is saved with the compiled +pattern, and the same tables are used via this pointer by \fBpcre_study()\fR +and \fBpcre_exec()\fR. Thus for any single pattern, compilation, studying and +matching all happen in the same locale, but different patterns can be compiled +in different locales. It is the caller's responsibility to ensure that the +memory containing the tables remains available for as long as it is needed. + + +.SH INFORMATION ABOUT A PATTERN +The \fBpcre_fullinfo()\fR function returns information about a compiled +pattern. It replaces the obsolete \fBpcre_info()\fR function, which is +nevertheless retained for backwards compability (and is documented below). + +The first argument for \fBpcre_fullinfo()\fR is a pointer to the compiled +pattern. The second argument is the result of \fBpcre_study()\fR, or NULL if +the pattern was not studied. The third argument specifies which piece of +information is required, while the fourth argument is a pointer to a variable +to receive the data. The yield of the function is zero for success, or one of +the following negative numbers: + + PCRE_ERROR_NULL the argument \fIcode\fR was NULL + the argument \fIwhere\fR was NULL + PCRE_ERROR_BADMAGIC the "magic number" was not found + PCRE_ERROR_BADOPTION the value of \fIwhat\fR was invalid + +The possible values for the third argument are defined in \fBpcre.h\fR, and are +as follows: + + PCRE_INFO_OPTIONS + +Return a copy of the options with which the pattern was compiled. The fourth +argument should point to au \fBunsigned long int\fR variable. These option bits +are those specified in the call to \fBpcre_compile()\fR, modified by any +top-level option settings within the pattern itself, and with the PCRE_ANCHORED +bit forcibly set if the form of the pattern implies that it can match only at +the start of a subject string. + + PCRE_INFO_SIZE + +Return the size of the compiled pattern, that is, the value that was passed as +the argument to \fBpcre_malloc()\fR when PCRE was getting memory in which to +place the compiled data. The fourth argument should point to a \fBsize_t\fR +variable. + + PCRE_INFO_CAPTURECOUNT + +Return the number of capturing subpatterns in the pattern. The fourth argument +should point to an \fbint\fR variable. + + PCRE_INFO_BACKREFMAX + +Return the number of the highest back reference in the pattern. The fourth +argument should point to an \fBint\fR variable. Zero is returned if there are +no back references. + + PCRE_INFO_FIRSTCHAR + +Return information about the first character of any matched string, for a +non-anchored pattern. If there is a fixed first character, e.g. from a pattern +such as (cat|cow|coyote), then it is returned in the integer pointed to by +\fIwhere\fR. Otherwise, if either + +(a) the pattern was compiled with the PCRE_MULTILINE option, and every branch +starts with "^", or + +(b) every branch of the pattern starts with ".*" and PCRE_DOTALL is not set +(if it were set, the pattern would be anchored), + +then -1 is returned, indicating that the pattern matches only at the +start of a subject string or after any "\\n" within the string. Otherwise -2 is +returned. For anchored patterns, -2 is returned. + + PCRE_INFO_FIRSTTABLE + +If the pattern was studied, and this resulted in the construction of a 256-bit +table indicating a fixed set of characters for the first character in any +matching string, a pointer to the table is returned. Otherwise NULL is +returned. The fourth argument should point to an \fBunsigned char *\fR +variable. + + PCRE_INFO_LASTLITERAL + +For a non-anchored pattern, return the value of the rightmost literal character +which must exist in any matched string, other than at its start. The fourth +argument should point to an \fBint\fR variable. If there is no such character, +or if the pattern is anchored, -1 is returned. For example, for the pattern +/a\\d+z\\d+/ the returned value is 'z'. + +The \fBpcre_info()\fR function is now obsolete because its interface is too +restrictive to return all the available data about a compiled pattern. New +programs should use \fBpcre_fullinfo()\fR instead. The yield of +\fBpcre_info()\fR is the number of capturing subpatterns, or one of the +following negative numbers: + + PCRE_ERROR_NULL the argument \fIcode\fR was NULL + PCRE_ERROR_BADMAGIC the "magic number" was not found + +If the \fIoptptr\fR argument is not NULL, a copy of the options with which the +pattern was compiled is placed in the integer it points to (see +PCRE_INFO_OPTIONS above). + +If the pattern is not anchored and the \fIfirstcharptr\fR argument is not NULL, +it is used to pass back information about the first character of any matched +string (see PCRE_INFO_FIRSTCHAR above). + + +.SH MATCHING A PATTERN +The function \fBpcre_exec()\fR is called to match a subject string against a +pre-compiled pattern, which is passed in the \fIcode\fR argument. If the +pattern has been studied, the result of the study should be passed in the +\fIextra\fR argument. Otherwise this must be NULL. + +The PCRE_ANCHORED option can be passed in the \fIoptions\fR argument, whose +unused bits must be zero. However, if a pattern was compiled with +PCRE_ANCHORED, or turned out to be anchored by virtue of its contents, it +cannot be made unachored at matching time. + +There are also three further options that can be set only at matching time: + + PCRE_NOTBOL + +The first character of the string is not the beginning of a line, so the +circumflex metacharacter should not match before it. Setting this without +PCRE_MULTILINE (at compile time) causes circumflex never to match. + + PCRE_NOTEOL + +The end of the string is not the end of a line, so the dollar metacharacter +should not match it nor (except in multiline mode) a newline immediately before +it. Setting this without PCRE_MULTILINE (at compile time) causes dollar never +to match. + + PCRE_NOTEMPTY + +An empty string is not considered to be a valid match if this option is set. If +there are alternatives in the pattern, they are tried. If all the alternatives +match the empty string, the entire match fails. For example, if the pattern + + a?b? + +is applied to a string not beginning with "a" or "b", it matches the empty +string at the start of the subject. With PCRE_NOTEMPTY set, this match is not +valid, so PCRE searches further into the string for occurrences of "a" or "b". + +Perl has no direct equivalent of PCRE_NOTEMPTY, but it does make a special case +of a pattern match of the empty string within its \fBsplit()\fR function, and +when using the /g modifier. It is possible to emulate Perl's behaviour after +matching a null string by first trying the match again at the same offset with +PCRE_NOTEMPTY set, and then if that fails by advancing the starting offset (see +below) and trying an ordinary match again. + +The subject string is passed as a pointer in \fIsubject\fR, a length in +\fIlength\fR, and a starting offset in \fIstartoffset\fR. Unlike the pattern +string, it may contain binary zero characters. When the starting offset is +zero, the search for a match starts at the beginning of the subject, and this +is by far the most common case. + +A non-zero starting offset is useful when searching for another match in the +same subject by calling \fBpcre_exec()\fR again after a previous success. +Setting \fIstartoffset\fR differs from just passing over a shortened string and +setting PCRE_NOTBOL in the case of a pattern that begins with any kind of +lookbehind. For example, consider the pattern + + \\Biss\\B + +which finds occurrences of "iss" in the middle of words. (\\B matches only if +the current position in the subject is not a word boundary.) When applied to +the string "Mississipi" the first call to \fBpcre_exec()\fR finds the first +occurrence. If \fBpcre_exec()\fR is called again with just the remainder of the +subject, namely "issipi", it does not match, because \\B is always false at the +start of the subject, which is deemed to be a word boundary. However, if +\fBpcre_exec()\fR is passed the entire string again, but with \fIstartoffset\fR +set to 4, it finds the second occurrence of "iss" because it is able to look +behind the starting point to discover that it is preceded by a letter. + +If a non-zero starting offset is passed when the pattern is anchored, one +attempt to match at the given offset is tried. This can only succeed if the +pattern does not require the match to be at the start of the subject. + +In general, a pattern matches a certain portion of the subject, and in +addition, further substrings from the subject may be picked out by parts of the +pattern. Following the usage in Jeffrey Friedl's book, this is called +"capturing" in what follows, and the phrase "capturing subpattern" is used for +a fragment of a pattern that picks out a substring. PCRE supports several other +kinds of parenthesized subpattern that do not cause substrings to be captured. + +Captured substrings are returned to the caller via a vector of integer offsets +whose address is passed in \fIovector\fR. The number of elements in the vector +is passed in \fIovecsize\fR. The first two-thirds of the vector is used to pass +back captured substrings, each substring using a pair of integers. The +remaining third of the vector is used as workspace by \fBpcre_exec()\fR while +matching capturing subpatterns, and is not available for passing back +information. The length passed in \fIovecsize\fR should always be a multiple of +three. If it is not, it is rounded down. + +When a match has been successful, information about captured substrings is +returned in pairs of integers, starting at the beginning of \fIovector\fR, and +continuing up to two-thirds of its length at the most. The first element of a +pair is set to the offset of the first character in a substring, and the second +is set to the offset of the first character after the end of a substring. The +first pair, \fIovector[0]\fR and \fIovector[1]\fR, identify the portion of the +subject string matched by the entire pattern. The next pair is used for the +first capturing subpattern, and so on. The value returned by \fBpcre_exec()\fR +is the number of pairs that have been set. If there are no capturing +subpatterns, the return value from a successful match is 1, indicating that +just the first pair of offsets has been set. + +Some convenience functions are provided for extracting the captured substrings +as separate strings. These are described in the following section. + +It is possible for an capturing subpattern number \fIn+1\fR to match some +part of the subject when subpattern \fIn\fR has not been used at all. For +example, if the string "abc" is matched against the pattern (a|(z))(bc) +subpatterns 1 and 3 are matched, but 2 is not. When this happens, both offset +values corresponding to the unused subpattern are set to -1. + +If a capturing subpattern is matched repeatedly, it is the last portion of the +string that it matched that gets returned. + +If the vector is too small to hold all the captured substrings, it is used as +far as possible (up to two-thirds of its length), and the function returns a +value of zero. In particular, if the substring offsets are not of interest, +\fBpcre_exec()\fR may be called with \fIovector\fR passed as NULL and +\fIovecsize\fR as zero. However, if the pattern contains back references and +the \fIovector\fR isn't big enough to remember the related substrings, PCRE has +to get additional memory for use during matching. Thus it is usually advisable +to supply an \fIovector\fR. + +Note that \fBpcre_info()\fR can be used to find out how many capturing +subpatterns there are in a compiled pattern. The smallest size for +\fIovector\fR that will allow for \fIn\fR captured substrings in addition to +the offsets of the substring matched by the whole pattern is (\fIn\fR+1)*3. + +If \fBpcre_exec()\fR fails, it returns a negative number. The following are +defined in the header file: + + PCRE_ERROR_NOMATCH (-1) + +The subject string did not match the pattern. + + PCRE_ERROR_NULL (-2) + +Either \fIcode\fR or \fIsubject\fR was passed as NULL, or \fIovector\fR was +NULL and \fIovecsize\fR was not zero. + + PCRE_ERROR_BADOPTION (-3) + +An unrecognized bit was set in the \fIoptions\fR argument. + + PCRE_ERROR_BADMAGIC (-4) + +PCRE stores a 4-byte "magic number" at the start of the compiled code, to catch +the case when it is passed a junk pointer. This is the error it gives when the +magic number isn't present. + + PCRE_ERROR_UNKNOWN_NODE (-5) + +While running the pattern match, an unknown item was encountered in the +compiled pattern. This error could be caused by a bug in PCRE or by overwriting +of the compiled pattern. + + PCRE_ERROR_NOMEMORY (-6) + +If a pattern contains back references, but the \fIovector\fR that is passed to +\fBpcre_exec()\fR is not big enough to remember the referenced substrings, PCRE +gets a block of memory at the start of matching to use for this purpose. If the +call via \fBpcre_malloc()\fR fails, this error is given. The memory is freed at +the end of matching. + + +.SH EXTRACTING CAPTURED SUBSTRINGS +Captured substrings can be accessed directly by using the offsets returned by +\fBpcre_exec()\fR in \fIovector\fR. For convenience, the functions +\fBpcre_copy_substring()\fR, \fBpcre_get_substring()\fR, and +\fBpcre_get_substring_list()\fR are provided for extracting captured substrings +as new, separate, zero-terminated strings. A substring that contains a binary +zero is correctly extracted and has a further zero added on the end, but the +result does not, of course, function as a C string. + +The first three arguments are the same for all three functions: \fIsubject\fR +is the subject string which has just been successfully matched, \fIovector\fR +is a pointer to the vector of integer offsets that was passed to +\fBpcre_exec()\fR, and \fIstringcount\fR is the number of substrings that +were captured by the match, including the substring that matched the entire +regular expression. This is the value returned by \fBpcre_exec\fR if it +is greater than zero. If \fBpcre_exec()\fR returned zero, indicating that it +ran out of space in \fIovector\fR, then the value passed as +\fIstringcount\fR should be the size of the vector divided by three. + +The functions \fBpcre_copy_substring()\fR and \fBpcre_get_substring()\fR +extract a single substring, whose number is given as \fIstringnumber\fR. A +value of zero extracts the substring that matched the entire pattern, while +higher values extract the captured substrings. For \fBpcre_copy_substring()\fR, +the string is placed in \fIbuffer\fR, whose length is given by +\fIbuffersize\fR, while for \fBpcre_get_substring()\fR a new block of store is +obtained via \fBpcre_malloc\fR, and its address is returned via +\fIstringptr\fR. The yield of the function is the length of the string, not +including the terminating zero, or one of + + PCRE_ERROR_NOMEMORY (-6) + +The buffer was too small for \fBpcre_copy_substring()\fR, or the attempt to get +memory failed for \fBpcre_get_substring()\fR. + + PCRE_ERROR_NOSUBSTRING (-7) + +There is no substring whose number is \fIstringnumber\fR. + +The \fBpcre_get_substring_list()\fR function extracts all available substrings +and builds a list of pointers to them. All this is done in a single block of +memory which is obtained via \fBpcre_malloc\fR. The address of the memory block +is returned via \fIlistptr\fR, which is also the start of the list of string +pointers. The end of the list is marked by a NULL pointer. The yield of the +function is zero if all went well, or + + PCRE_ERROR_NOMEMORY (-6) + +if the attempt to get the memory block failed. + +When any of these functions encounter a substring that is unset, which can +happen when capturing subpattern number \fIn+1\fR matches some part of the +subject, but subpattern \fIn\fR has not been used at all, they return an empty +string. This can be distinguished from a genuine zero-length substring by +inspecting the appropriate offset in \fIovector\fR, which is negative for unset +substrings. + + + +.SH LIMITATIONS +There are some size limitations in PCRE but it is hoped that they will never in +practice be relevant. +The maximum length of a compiled pattern is 65539 (sic) bytes. +All values in repeating quantifiers must be less than 65536. +The maximum number of capturing subpatterns is 99. +The maximum number of all parenthesized subpatterns, including capturing +subpatterns, assertions, and other types of subpattern, is 200. + +The maximum length of a subject string is the largest positive number that an +integer variable can hold. However, PCRE uses recursion to handle subpatterns +and indefinite repetition. This means that the available stack space may limit +the size of a subject string that can be processed by certain patterns. + + +.SH DIFFERENCES FROM PERL +The differences described here are with respect to Perl 5.005. + +1. By default, a whitespace character is any character that the C library +function \fBisspace()\fR recognizes, though it is possible to compile PCRE with +alternative character type tables. Normally \fBisspace()\fR matches space, +formfeed, newline, carriage return, horizontal tab, and vertical tab. Perl 5 +no longer includes vertical tab in its set of whitespace characters. The \\v +escape that was in the Perl documentation for a long time was never in fact +recognized. However, the character itself was treated as whitespace at least +up to 5.002. In 5.004 and 5.005 it does not match \\s. + +2. PCRE does not allow repeat quantifiers on lookahead assertions. Perl permits +them, but they do not mean what you might think. For example, (?!a){3} does +not assert that the next three characters are not "a". It just asserts that the +next character is not "a" three times. + +3. Capturing subpatterns that occur inside negative lookahead assertions are +counted, but their entries in the offsets vector are never set. Perl sets its +numerical variables from any such patterns that are matched before the +assertion fails to match something (thereby succeeding), but only if the +negative lookahead assertion contains just one branch. + +4. Though binary zero characters are supported in the subject string, they are +not allowed in a pattern string because it is passed as a normal C string, +terminated by zero. The escape sequence "\\0" can be used in the pattern to +represent a binary zero. + +5. The following Perl escape sequences are not supported: \\l, \\u, \\L, \\U, +\\E, \\Q. In fact these are implemented by Perl's general string-handling and +are not part of its pattern matching engine. + +6. The Perl \\G assertion is not supported as it is not relevant to single +pattern matches. + +7. Fairly obviously, PCRE does not support the (?{code}) and (?p{code}) +constructions. However, there is some experimental support for recursive +patterns using the non-Perl item (?R). + +8. There are at the time of writing some oddities in Perl 5.005_02 concerned +with the settings of captured strings when part of a pattern is repeated. For +example, matching "aba" against the pattern /^(a(b)?)+$/ sets $2 to the value +"b", but matching "aabbaa" against /^(aa(bb)?)+$/ leaves $2 unset. However, if +the pattern is changed to /^(aa(b(b))?)+$/ then $2 (and $3) get set. + +In Perl 5.004 $2 is set in both cases, and that is also true of PCRE. If in the +future Perl changes to a consistent state that is different, PCRE may change to +follow. + +9. Another as yet unresolved discrepancy is that in Perl 5.005_02 the pattern +/^(a)?(?(1)a|b)+$/ matches the string "a", whereas in PCRE it does not. +However, in both Perl and PCRE /^(a)?a/ matched against "a" leaves $1 unset. + +10. PCRE provides some extensions to the Perl regular expression facilities: + +(a) Although lookbehind assertions must match fixed length strings, each +alternative branch of a lookbehind assertion can match a different length of +string. Perl 5.005 requires them all to have the same length. + +(b) If PCRE_DOLLAR_ENDONLY is set and PCRE_MULTILINE is not set, the $ meta- +character matches only at the very end of the string. + +(c) If PCRE_EXTRA is set, a backslash followed by a letter with no special +meaning is faulted. + +(d) If PCRE_UNGREEDY is set, the greediness of the repetition quantifiers is +inverted, that is, by default they are not greedy, but if followed by a +question mark they are. + +(e) PCRE_ANCHORED can be used to force a pattern to be tried only at the start +of the subject. + +(f) The PCRE_NOTBOL, PCRE_NOTEOL, and PCRE_NOTEMPTY options for +\fBpcre_exec()\fR have no Perl equivalents. + +(g) The (?R) construct allows for recursive pattern matching (Perl 5.6 can do +this using the (?p{code}) construct, which PCRE cannot of course support.) + + +.SH REGULAR EXPRESSION DETAILS +The syntax and semantics of the regular expressions supported by PCRE are +described below. Regular expressions are also described in the Perl +documentation and in a number of other books, some of which have copious +examples. Jeffrey Friedl's "Mastering Regular Expressions", published by +O'Reilly (ISBN 1-56592-257), covers them in great detail. The description +here is intended as reference documentation. + +A regular expression is a pattern that is matched against a subject string from +left to right. Most characters stand for themselves in a pattern, and match the +corresponding characters in the subject. As a trivial example, the pattern + + The quick brown fox + +matches a portion of a subject string that is identical to itself. The power of +regular expressions comes from the ability to include alternatives and +repetitions in the pattern. These are encoded in the pattern by the use of +\fImeta-characters\fR, which do not stand for themselves but instead are +interpreted in some special way. + +There are two different sets of meta-characters: those that are recognized +anywhere in the pattern except within square brackets, and those that are +recognized in square brackets. Outside square brackets, the meta-characters are +as follows: + + \\ general escape character with several uses + ^ assert start of subject (or line, in multiline mode) + $ assert end of subject (or line, in multiline mode) + . match any character except newline (by default) + [ start character class definition + | start of alternative branch + ( start subpattern + ) end subpattern + ? extends the meaning of ( + also 0 or 1 quantifier + also quantifier minimizer + * 0 or more quantifier + + 1 or more quantifier + { start min/max quantifier + +Part of a pattern that is in square brackets is called a "character class". In +a character class the only meta-characters are: + + \\ general escape character + ^ negate the class, but only if the first character + - indicates character range + ] terminates the character class + +The following sections describe the use of each of the meta-characters. + + +.SH BACKSLASH +The backslash character has several uses. Firstly, if it is followed by a +non-alphameric character, it takes away any special meaning that character may +have. This use of backslash as an escape character applies both inside and +outside character classes. + +For example, if you want to match a "*" character, you write "\\*" in the +pattern. This applies whether or not the following character would otherwise be +interpreted as a meta-character, so it is always safe to precede a +non-alphameric with "\\" to specify that it stands for itself. In particular, +if you want to match a backslash, you write "\\\\". + +If a pattern is compiled with the PCRE_EXTENDED option, whitespace in the +pattern (other than in a character class) and characters between a "#" outside +a character class and the next newline character are ignored. An escaping +backslash can be used to include a whitespace or "#" character as part of the +pattern. + +A second use of backslash provides a way of encoding non-printing characters +in patterns in a visible manner. There is no restriction on the appearance of +non-printing characters, apart from the binary zero that terminates a pattern, +but when a pattern is being prepared by text editing, it is usually easier to +use one of the following escape sequences than the binary character it +represents: + + \\a alarm, that is, the BEL character (hex 07) + \\cx "control-x", where x is any character + \\e escape (hex 1B) + \\f formfeed (hex 0C) + \\n newline (hex 0A) + \\r carriage return (hex 0D) + \\t tab (hex 09) + \\xhh character with hex code hh + \\ddd character with octal code ddd, or backreference + +The precise effect of "\\cx" is as follows: if "x" is a lower case letter, it +is converted to upper case. Then bit 6 of the character (hex 40) is inverted. +Thus "\\cz" becomes hex 1A, but "\\c{" becomes hex 3B, while "\\c;" becomes hex +7B. + +After "\\x", up to two hexadecimal digits are read (letters can be in upper or +lower case). + +After "\\0" up to two further octal digits are read. In both cases, if there +are fewer than two digits, just those that are present are used. Thus the +sequence "\\0\\x\\07" specifies two binary zeros followed by a BEL character. +Make sure you supply two digits after the initial zero if the character that +follows is itself an octal digit. + +The handling of a backslash followed by a digit other than 0 is complicated. +Outside a character class, PCRE reads it and any following digits as a decimal +number. If the number is less than 10, or if there have been at least that many +previous capturing left parentheses in the expression, the entire sequence is +taken as a \fIback reference\fR. A description of how this works is given +later, following the discussion of parenthesized subpatterns. + +Inside a character class, or if the decimal number is greater than 9 and there +have not been that many capturing subpatterns, PCRE re-reads up to three octal +digits following the backslash, and generates a single byte from the least +significant 8 bits of the value. Any subsequent digits stand for themselves. +For example: + + \\040 is another way of writing a space + \\40 is the same, provided there are fewer than 40 + previous capturing subpatterns + \\7 is always a back reference + \\11 might be a back reference, or another way of + writing a tab + \\011 is always a tab + \\0113 is a tab followed by the character "3" + \\113 is the character with octal code 113 (since there + can be no more than 99 back references) + \\377 is a byte consisting entirely of 1 bits + \\81 is either a back reference, or a binary zero + followed by the two characters "8" and "1" + +Note that octal values of 100 or greater must not be introduced by a leading +zero, because no more than three octal digits are ever read. + +All the sequences that define a single byte value can be used both inside and +outside character classes. In addition, inside a character class, the sequence +"\\b" is interpreted as the backspace character (hex 08). Outside a character +class it has a different meaning (see below). + +The third use of backslash is for specifying generic character types: + + \\d any decimal digit + \\D any character that is not a decimal digit + \\s any whitespace character + \\S any character that is not a whitespace character + \\w any "word" character + \\W any "non-word" character + +Each pair of escape sequences partitions the complete set of characters into +two disjoint sets. Any given character matches one, and only one, of each pair. + +A "word" character is any letter or digit or the underscore character, that is, +any character which can be part of a Perl "word". The definition of letters and +digits is controlled by PCRE's character tables, and may vary if locale- +specific matching is taking place (see "Locale support" above). For example, in +the "fr" (French) locale, some character codes greater than 128 are used for +accented letters, and these are matched by \\w. + +These character type sequences can appear both inside and outside character +classes. They each match one character of the appropriate type. If the current +matching point is at the end of the subject string, all of them fail, since +there is no character to match. + +The fourth use of backslash is for certain simple assertions. An assertion +specifies a condition that has to be met at a particular point in a match, +without consuming any characters from the subject string. The use of +subpatterns for more complicated assertions is described below. The backslashed +assertions are + + \\b word boundary + \\B not a word boundary + \\A start of subject (independent of multiline mode) + \\Z end of subject or newline at end (independent of multiline mode) + \\z end of subject (independent of multiline mode) + +These assertions may not appear in character classes (but note that "\\b" has a +different meaning, namely the backspace character, inside a character class). + +A word boundary is a position in the subject string where the current character +and the previous character do not both match \\w or \\W (i.e. one matches +\\w and the other matches \\W), or the start or end of the string if the +first or last character matches \\w, respectively. + +The \\A, \\Z, and \\z assertions differ from the traditional circumflex and +dollar (described below) in that they only ever match at the very start and end +of the subject string, whatever options are set. They are not affected by the +PCRE_NOTBOL or PCRE_NOTEOL options. If the \fIstartoffset\fR argument of +\fBpcre_exec()\fR is non-zero, \\A can never match. The difference between \\Z +and \\z is that \\Z matches before a newline that is the last character of the +string as well as at the end of the string, whereas \\z matches only at the +end. + + +.SH CIRCUMFLEX AND DOLLAR +Outside a character class, in the default matching mode, the circumflex +character is an assertion which is true only if the current matching point is +at the start of the subject string. If the \fIstartoffset\fR argument of +\fBpcre_exec()\fR is non-zero, circumflex can never match. Inside a character +class, circumflex has an entirely different meaning (see below). + +Circumflex need not be the first character of the pattern if a number of +alternatives are involved, but it should be the first thing in each alternative +in which it appears if the pattern is ever to match that branch. If all +possible alternatives start with a circumflex, that is, if the pattern is +constrained to match only at the start of the subject, it is said to be an +"anchored" pattern. (There are also other constructs that can cause a pattern +to be anchored.) + +A dollar character is an assertion which is true only if the current matching +point is at the end of the subject string, or immediately before a newline +character that is the last character in the string (by default). Dollar need +not be the last character of the pattern if a number of alternatives are +involved, but it should be the last item in any branch in which it appears. +Dollar has no special meaning in a character class. + +The meaning of dollar can be changed so that it matches only at the very end of +the string, by setting the PCRE_DOLLAR_ENDONLY option at compile or matching +time. This does not affect the \\Z assertion. + +The meanings of the circumflex and dollar characters are changed if the +PCRE_MULTILINE option is set. When this is the case, they match immediately +after and immediately before an internal "\\n" character, respectively, in +addition to matching at the start and end of the subject string. For example, +the pattern /^abc$/ matches the subject string "def\\nabc" in multiline mode, +but not otherwise. Consequently, patterns that are anchored in single line mode +because all branches start with "^" are not anchored in multiline mode, and a +match for circumflex is possible when the \fIstartoffset\fR argument of +\fBpcre_exec()\fR is non-zero. The PCRE_DOLLAR_ENDONLY option is ignored if +PCRE_MULTILINE is set. + +Note that the sequences \\A, \\Z, and \\z can be used to match the start and +end of the subject in both modes, and if all branches of a pattern start with +\\A is it always anchored, whether PCRE_MULTILINE is set or not. + + +.SH FULL STOP (PERIOD, DOT) +Outside a character class, a dot in the pattern matches any one character in +the subject, including a non-printing character, but not (by default) newline. +If the PCRE_DOTALL option is set, then dots match newlines as well. The +handling of dot is entirely independent of the handling of circumflex and +dollar, the only relationship being that they both involve newline characters. +Dot has no special meaning in a character class. + + +.SH SQUARE BRACKETS +An opening square bracket introduces a character class, terminated by a closing +square bracket. A closing square bracket on its own is not special. If a +closing square bracket is required as a member of the class, it should be the +first data character in the class (after an initial circumflex, if present) or +escaped with a backslash. + +A character class matches a single character in the subject; the character must +be in the set of characters defined by the class, unless the first character in +the class is a circumflex, in which case the subject character must not be in +the set defined by the class. If a circumflex is actually required as a member +of the class, ensure it is not the first character, or escape it with a +backslash. + +For example, the character class [aeiou] matches any lower case vowel, while +[^aeiou] matches any character that is not a lower case vowel. Note that a +circumflex is just a convenient notation for specifying the characters which +are in the class by enumerating those that are not. It is not an assertion: it +still consumes a character from the subject string, and fails if the current +pointer is at the end of the string. + +When caseless matching is set, any letters in a class represent both their +upper case and lower case versions, so for example, a caseless [aeiou] matches +"A" as well as "a", and a caseless [^aeiou] does not match "A", whereas a +caseful version would. + +The newline character is never treated in any special way in character classes, +whatever the setting of the PCRE_DOTALL or PCRE_MULTILINE options is. A class +such as [^a] will always match a newline. + +The minus (hyphen) character can be used to specify a range of characters in a +character class. For example, [d-m] matches any letter between d and m, +inclusive. If a minus character is required in a class, it must be escaped with +a backslash or appear in a position where it cannot be interpreted as +indicating a range, typically as the first or last character in the class. + +It is not possible to have the literal character "]" as the end character of a +range. A pattern such as [W-]46] is interpreted as a class of two characters +("W" and "-") followed by a literal string "46]", so it would match "W46]" or +"-46]". However, if the "]" is escaped with a backslash it is interpreted as +the end of range, so [W-\\]46] is interpreted as a single class containing a +range followed by two separate characters. The octal or hexadecimal +representation of "]" can also be used to end a range. + +Ranges operate in ASCII collating sequence. They can also be used for +characters specified numerically, for example [\\000-\\037]. If a range that +includes letters is used when caseless matching is set, it matches the letters +in either case. For example, [W-c] is equivalent to [][\\^_`wxyzabc], matched +caselessly, and if character tables for the "fr" locale are in use, +[\\xc8-\\xcb] matches accented E characters in both cases. + +The character types \\d, \\D, \\s, \\S, \\w, and \\W may also appear in a +character class, and add the characters that they match to the class. For +example, [\\dABCDEF] matches any hexadecimal digit. A circumflex can +conveniently be used with the upper case character types to specify a more +restricted set of characters than the matching lower case type. For example, +the class [^\\W_] matches any letter or digit, but not underscore. + +All non-alphameric characters other than \\, -, ^ (at the start) and the +terminating ] are non-special in character classes, but it does no harm if they +are escaped. + + +.SH POSIX CHARACTER CLASSES +Perl 5.6 (not yet released at the time of writing) is going to support the +POSIX notation for character classes, which uses names enclosed by [: and :] +within the enclosing square brackets. PCRE supports this notation. For example, + + [01[:alpha:]%] + +matches "0", "1", any alphabetic character, or "%". The supported class names +are + + alnum letters and digits + alpha letters + ascii character codes 0 - 127 + cntrl control characters + digit decimal digits (same as \\d) + graph printing characters, excluding space + lower lower case letters + print printing characters, including space + punct printing characters, excluding letters and digits + space white space (same as \\s) + upper upper case letters + word "word" characters (same as \\w) + xdigit hexadecimal digits + +The names "ascii" and "word" are Perl extensions. Another Perl extension is +negation, which is indicated by a ^ character after the colon. For example, + + [12[:^digit:]] + +matches "1", "2", or any non-digit. PCRE (and Perl) also recogize the POSIX +syntax [.ch.] and [=ch=] where "ch" is a "collating element", but these are not +supported, and an error is given if they are encountered. + + +.SH VERTICAL BAR +Vertical bar characters are used to separate alternative patterns. For example, +the pattern + + gilbert|sullivan + +matches either "gilbert" or "sullivan". Any number of alternatives may appear, +and an empty alternative is permitted (matching the empty string). +The matching process tries each alternative in turn, from left to right, +and the first one that succeeds is used. If the alternatives are within a +subpattern (defined below), "succeeds" means matching the rest of the main +pattern as well as the alternative in the subpattern. + + +.SH INTERNAL OPTION SETTING +The settings of PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL, and PCRE_EXTENDED +can be changed from within the pattern by a sequence of Perl option letters +enclosed between "(?" and ")". The option letters are + + i for PCRE_CASELESS + m for PCRE_MULTILINE + s for PCRE_DOTALL + x for PCRE_EXTENDED + +For example, (?im) sets caseless, multiline matching. It is also possible to +unset these options by preceding the letter with a hyphen, and a combined +setting and unsetting such as (?im-sx), which sets PCRE_CASELESS and +PCRE_MULTILINE while unsetting PCRE_DOTALL and PCRE_EXTENDED, is also +permitted. If a letter appears both before and after the hyphen, the option is +unset. + +The scope of these option changes depends on where in the pattern the setting +occurs. For settings that are outside any subpattern (defined below), the +effect is the same as if the options were set or unset at the start of +matching. The following patterns all behave in exactly the same way: + + (?i)abc + a(?i)bc + ab(?i)c + abc(?i) + +which in turn is the same as compiling the pattern abc with PCRE_CASELESS set. +In other words, such "top level" settings apply to the whole pattern (unless +there are other changes inside subpatterns). If there is more than one setting +of the same option at top level, the rightmost setting is used. + +If an option change occurs inside a subpattern, the effect is different. This +is a change of behaviour in Perl 5.005. An option change inside a subpattern +affects only that part of the subpattern that follows it, so + + (a(?i)b)c + +matches abc and aBc and no other strings (assuming PCRE_CASELESS is not used). +By this means, options can be made to have different settings in different +parts of the pattern. Any changes made in one alternative do carry on +into subsequent branches within the same subpattern. For example, + + (a(?i)b|c) + +matches "ab", "aB", "c", and "C", even though when matching "C" the first +branch is abandoned before the option setting. This is because the effects of +option settings happen at compile time. There would be some very weird +behaviour otherwise. + +The PCRE-specific options PCRE_UNGREEDY and PCRE_EXTRA can be changed in the +same way as the Perl-compatible options by using the characters U and X +respectively. The (?X) flag setting is special in that it must always occur +earlier in the pattern than any of the additional features it turns on, even +when it is at top level. It is best put at the start. + + +.SH SUBPATTERNS +Subpatterns are delimited by parentheses (round brackets), which can be nested. +Marking part of a pattern as a subpattern does two things: + +1. It localizes a set of alternatives. For example, the pattern + + cat(aract|erpillar|) + +matches one of the words "cat", "cataract", or "caterpillar". Without the +parentheses, it would match "cataract", "erpillar" or the empty string. + +2. It sets up the subpattern as a capturing subpattern (as defined above). +When the whole pattern matches, that portion of the subject string that matched +the subpattern is passed back to the caller via the \fIovector\fR argument of +\fBpcre_exec()\fR. Opening parentheses are counted from left to right (starting +from 1) to obtain the numbers of the capturing subpatterns. + +For example, if the string "the red king" is matched against the pattern + + the ((red|white) (king|queen)) + +the captured substrings are "red king", "red", and "king", and are numbered 1, +2, and 3. + +The fact that plain parentheses fulfil two functions is not always helpful. +There are often times when a grouping subpattern is required without a +capturing requirement. If an opening parenthesis is followed by "?:", the +subpattern does not do any capturing, and is not counted when computing the +number of any subsequent capturing subpatterns. For example, if the string "the +white queen" is matched against the pattern + + the ((?:red|white) (king|queen)) + +the captured substrings are "white queen" and "queen", and are numbered 1 and +2. The maximum number of captured substrings is 99, and the maximum number of +all subpatterns, both capturing and non-capturing, is 200. + +As a convenient shorthand, if any option settings are required at the start of +a non-capturing subpattern, the option letters may appear between the "?" and +the ":". Thus the two patterns + + (?i:saturday|sunday) + (?:(?i)saturday|sunday) + +match exactly the same set of strings. Because alternative branches are tried +from left to right, and options are not reset until the end of the subpattern +is reached, an option setting in one branch does affect subsequent branches, so +the above patterns match "SUNDAY" as well as "Saturday". + + +.SH REPETITION +Repetition is specified by quantifiers, which can follow any of the following +items: + + a single character, possibly escaped + the . metacharacter + a character class + a back reference (see next section) + a parenthesized subpattern (unless it is an assertion - see below) + +The general repetition quantifier specifies a minimum and maximum number of +permitted matches, by giving the two numbers in curly brackets (braces), +separated by a comma. The numbers must be less than 65536, and the first must +be less than or equal to the second. For example: + + z{2,4} + +matches "zz", "zzz", or "zzzz". A closing brace on its own is not a special +character. If the second number is omitted, but the comma is present, there is +no upper limit; if the second number and the comma are both omitted, the +quantifier specifies an exact number of required matches. Thus + + [aeiou]{3,} + +matches at least 3 successive vowels, but may match many more, while + + \\d{8} + +matches exactly 8 digits. An opening curly bracket that appears in a position +where a quantifier is not allowed, or one that does not match the syntax of a +quantifier, is taken as a literal character. For example, {,6} is not a +quantifier, but a literal string of four characters. + +The quantifier {0} is permitted, causing the expression to behave as if the +previous item and the quantifier were not present. + +For convenience (and historical compatibility) the three most common +quantifiers have single-character abbreviations: + + * is equivalent to {0,} + + is equivalent to {1,} + ? is equivalent to {0,1} + +It is possible to construct infinite loops by following a subpattern that can +match no characters with a quantifier that has no upper limit, for example: + + (a?)* + +Earlier versions of Perl and PCRE used to give an error at compile time for +such patterns. However, because there are cases where this can be useful, such +patterns are now accepted, but if any repetition of the subpattern does in fact +match no characters, the loop is forcibly broken. + +By default, the quantifiers are "greedy", that is, they match as much as +possible (up to the maximum number of permitted times), without causing the +rest of the pattern to fail. The classic example of where this gives problems +is in trying to match comments in C programs. These appear between the +sequences /* and */ and within the sequence, individual * and / characters may +appear. An attempt to match C comments by applying the pattern + + /\\*.*\\*/ + +to the string + + /* first command */ not comment /* second comment */ + +fails, because it matches the entire string due to the greediness of the .* +item. + +However, if a quantifier is followed by a question mark, then it ceases to be +greedy, and instead matches the minimum number of times possible, so the +pattern + + /\\*.*?\\*/ + +does the right thing with the C comments. The meaning of the various +quantifiers is not otherwise changed, just the preferred number of matches. +Do not confuse this use of question mark with its use as a quantifier in its +own right. Because it has two uses, it can sometimes appear doubled, as in + + \\d??\\d + +which matches one digit by preference, but can match two if that is the only +way the rest of the pattern matches. + +If the PCRE_UNGREEDY option is set (an option which is not available in Perl) +then the quantifiers are not greedy by default, but individual ones can be made +greedy by following them with a question mark. In other words, it inverts the +default behaviour. + +When a parenthesized subpattern is quantified with a minimum repeat count that +is greater than 1 or with a limited maximum, more store is required for the +compiled pattern, in proportion to the size of the minimum or maximum. + +If a pattern starts with .* or .{0,} and the PCRE_DOTALL option (equivalent +to Perl's /s) is set, thus allowing the . to match newlines, then the pattern +is implicitly anchored, because whatever follows will be tried against every +character position in the subject string, so there is no point in retrying the +overall match at any position after the first. PCRE treats such a pattern as +though it were preceded by \\A. In cases where it is known that the subject +string contains no newlines, it is worth setting PCRE_DOTALL when the pattern +begins with .* in order to obtain this optimization, or alternatively using ^ +to indicate anchoring explicitly. + +When a capturing subpattern is repeated, the value captured is the substring +that matched the final iteration. For example, after + + (tweedle[dume]{3}\\s*)+ + +has matched "tweedledum tweedledee" the value of the captured substring is +"tweedledee". However, if there are nested capturing subpatterns, the +corresponding captured values may have been set in previous iterations. For +example, after + + /(a|(b))+/ + +matches "aba" the value of the second captured substring is "b". + + +.SH BACK REFERENCES +Outside a character class, a backslash followed by a digit greater than 0 (and +possibly further digits) is a back reference to a capturing subpattern earlier +(i.e. to its left) in the pattern, provided there have been that many previous +capturing left parentheses. + +However, if the decimal number following the backslash is less than 10, it is +always taken as a back reference, and causes an error only if there are not +that many capturing left parentheses in the entire pattern. In other words, the +parentheses that are referenced need not be to the left of the reference for +numbers less than 10. See the section entitled "Backslash" above for further +details of the handling of digits following a backslash. + +A back reference matches whatever actually matched the capturing subpattern in +the current subject string, rather than anything matching the subpattern +itself. So the pattern + + (sens|respons)e and \\1ibility + +matches "sense and sensibility" and "response and responsibility", but not +"sense and responsibility". If caseful matching is in force at the time of the +back reference, then the case of letters is relevant. For example, + + ((?i)rah)\\s+\\1 + +matches "rah rah" and "RAH RAH", but not "RAH rah", even though the original +capturing subpattern is matched caselessly. + +There may be more than one back reference to the same subpattern. If a +subpattern has not actually been used in a particular match, then any back +references to it always fail. For example, the pattern + + (a|(bc))\\2 + +always fails if it starts to match "a" rather than "bc". Because there may be +up to 99 back references, all digits following the backslash are taken +as part of a potential back reference number. If the pattern continues with a +digit character, then some delimiter must be used to terminate the back +reference. If the PCRE_EXTENDED option is set, this can be whitespace. +Otherwise an empty comment can be used. + +A back reference that occurs inside the parentheses to which it refers fails +when the subpattern is first used, so, for example, (a\\1) never matches. +However, such references can be useful inside repeated subpatterns. For +example, the pattern + + (a|b\\1)+ + +matches any number of "a"s and also "aba", "ababaa" etc. At each iteration of +the subpattern, the back reference matches the character string corresponding +to the previous iteration. In order for this to work, the pattern must be such +that the first iteration does not need to match the back reference. This can be +done using alternation, as in the example above, or by a quantifier with a +minimum of zero. + + +.SH ASSERTIONS +An assertion is a test on the characters following or preceding the current +matching point that does not actually consume any characters. The simple +assertions coded as \\b, \\B, \\A, \\Z, \\z, ^ and $ are described above. More +complicated assertions are coded as subpatterns. There are two kinds: those +that look ahead of the current position in the subject string, and those that +look behind it. + +An assertion subpattern is matched in the normal way, except that it does not +cause the current matching position to be changed. Lookahead assertions start +with (?= for positive assertions and (?! for negative assertions. For example, + + \\w+(?=;) + +matches a word followed by a semicolon, but does not include the semicolon in +the match, and + + foo(?!bar) + +matches any occurrence of "foo" that is not followed by "bar". Note that the +apparently similar pattern + + (?!foo)bar + +does not find an occurrence of "bar" that is preceded by something other than +"foo"; it finds any occurrence of "bar" whatsoever, because the assertion +(?!foo) is always true when the next three characters are "bar". A +lookbehind assertion is needed to achieve this effect. + +Lookbehind assertions start with (?<= for positive assertions and (? as in this example: + + (?>\\d+)bar + +This kind of parenthesis "locks up" the part of the pattern it contains once +it has matched, and a failure further into the pattern is prevented from +backtracking into it. Backtracking past it to previous items, however, works as +normal. + +An alternative description is that a subpattern of this type matches the string +of characters that an identical standalone pattern would match, if anchored at +the current point in the subject string. + +Once-only subpatterns are not capturing subpatterns. Simple cases such as the +above example can be thought of as a maximizing repeat that must swallow +everything it can. So, while both \\d+ and \\d+? are prepared to adjust the +number of digits they match in order to make the rest of the pattern match, +(?>\\d+) can only match an entire sequence of digits. + +This construction can of course contain arbitrarily complicated subpatterns, +and it can be nested. + +Once-only subpatterns can be used in conjunction with lookbehind assertions to +specify efficient matching at the end of the subject string. Consider a simple +pattern such as + + abcd$ + +when applied to a long string which does not match. Because matching proceeds +from left to right, PCRE will look for each "a" in the subject and then see if +what follows matches the rest of the pattern. If the pattern is specified as + + ^.*abcd$ + +then the initial .* matches the entire string at first, but when this fails +(because there is no following "a"), it backtracks to match all but the last +character, then all but the last two characters, and so on. Once again the +search for "a" covers the entire string, from right to left, so we are no +better off. However, if the pattern is written as + + ^(?>.*)(?<=abcd) + +then there can be no backtracking for the .* item; it can match only the entire +string. The subsequent lookbehind assertion does a single test on the last four +characters. If it fails, the match fails immediately. For long strings, this +approach makes a significant difference to the processing time. + +When a pattern contains an unlimited repeat inside a subpattern that can itself +be repeated an unlimited number of times, the use of a once-only subpattern is +the only way to avoid some failing matches taking a very long time indeed. +The pattern + + (\\D+|<\\d+>)*[!?] + +matches an unlimited number of substrings that either consist of non-digits, or +digits enclosed in <>, followed by either ! or ?. When it matches, it runs +quickly. However, if it is applied to + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +it takes a long time before reporting failure. This is because the string can +be divided between the two repeats in a large number of ways, and all have to +be tried. (The example used [!?] rather than a single character at the end, +because both PCRE and Perl have an optimization that allows for fast failure +when a single character is used. They remember the last single character that +is required for a match, and fail early if it is not present in the string.) +If the pattern is changed to + + ((?>\\D+)|<\\d+>)*[!?] + +sequences of non-digits cannot be broken, and failure happens quickly. + + +.SH CONDITIONAL SUBPATTERNS +It is possible to cause the matching process to obey a subpattern +conditionally or to choose between two alternative subpatterns, depending on +the result of an assertion, or whether a previous capturing subpattern matched +or not. The two possible forms of conditional subpattern are + + (?(condition)yes-pattern) + (?(condition)yes-pattern|no-pattern) + +If the condition is satisfied, the yes-pattern is used; otherwise the +no-pattern (if present) is used. If there are more than two alternatives in the +subpattern, a compile-time error occurs. + +There are two kinds of condition. If the text between the parentheses consists +of a sequence of digits, then the condition is satisfied if the capturing +subpattern of that number has previously matched. Consider the following +pattern, which contains non-significant white space to make it more readable +(assume the PCRE_EXTENDED option) and to divide it into three parts for ease +of discussion: + + ( \\( )? [^()]+ (?(1) \\) ) + +The first part matches an optional opening parenthesis, and if that +character is present, sets it as the first captured substring. The second part +matches one or more characters that are not parentheses. The third part is a +conditional subpattern that tests whether the first set of parentheses matched +or not. If they did, that is, if subject started with an opening parenthesis, +the condition is true, and so the yes-pattern is executed and a closing +parenthesis is required. Otherwise, since no-pattern is not present, the +subpattern matches nothing. In other words, this pattern matches a sequence of +non-parentheses, optionally enclosed in parentheses. + +If the condition is not a sequence of digits, it must be an assertion. This may +be a positive or negative lookahead or lookbehind assertion. Consider this +pattern, again containing non-significant white space, and with the two +alternatives on the second line: + + (?(?=[^a-z]*[a-z]) + \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) + +The condition is a positive lookahead assertion that matches an optional +sequence of non-letters followed by a letter. In other words, it tests for the +presence of at least one letter in the subject. If a letter is found, the +subject is matched against the first alternative; otherwise it is matched +against the second. This pattern matches strings in one of the two forms +dd-aaa-dd or dd-dd-dd, where aaa are letters and dd are digits. + + +.SH COMMENTS +The sequence (?# marks the start of a comment which continues up to the next +closing parenthesis. Nested parentheses are not permitted. The characters +that make up a comment play no part in the pattern matching at all. + +If the PCRE_EXTENDED option is set, an unescaped # character outside a +character class introduces a comment that continues up to the next newline +character in the pattern. + + +.SH RECURSIVE PATTERNS +Consider the problem of matching a string in parentheses, allowing for +unlimited nested parentheses. Without the use of recursion, the best that can +be done is to use a pattern that matches up to some fixed depth of nesting. It +is not possible to handle an arbitrary nesting depth. Perl 5.6 has provided an +experimental facility that allows regular expressions to recurse (amongst other +things). It does this by interpolating Perl code in the expression at run time, +and the code can refer to the expression itself. A Perl pattern to solve the +parentheses problem can be created like this: + + $re = qr{\\( (?: (?>[^()]+) | (?p{$re}) )* \\)}x; + +The (?p{...}) item interpolates Perl code at run time, and in this case refers +recursively to the pattern in which it appears. Obviously, PCRE cannot support +the interpolation of Perl code. Instead, the special item (?R) is provided for +the specific case of recursion. This PCRE pattern solves the parentheses +problem (assume the PCRE_EXTENDED option is set so that white space is +ignored): + + \\( ( (?>[^()]+) | (?R) )* \\) + +First it matches an opening parenthesis. Then it matches any number of +substrings which can either be a sequence of non-parentheses, or a recursive +match of the pattern itself (i.e. a correctly parenthesized substring). Finally +there is a closing parenthesis. + +This particular example pattern contains nested unlimited repeats, and so the +use of a once-only subpattern for matching strings of non-parentheses is +important when applying the pattern to strings that do not match. For example, +when it is applied to + + (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa() + +it yields "no match" quickly. However, if a once-only subpattern is not used, +the match runs for a very long time indeed because there are so many different +ways the + and * repeats can carve up the subject, and all have to be tested +before failure can be reported. + +The values set for any capturing subpatterns are those from the outermost level +of the recursion at which the subpattern value is set. If the pattern above is +matched against + + (ab(cd)ef) + +the value for the capturing parentheses is "ef", which is the last value taken +on at the top level. If additional parentheses are added, giving + + \\( ( ( (?>[^()]+) | (?R) )* ) \\) + ^ ^ + ^ ^ +then the string they capture is "ab(cd)ef", the contents of the top level +parentheses. If there are more than 15 capturing parentheses in a pattern, PCRE +has to obtain extra memory to store data during a recursion, which it does by +using \fBpcre_malloc\fR, freeing it via \fBpcre_free\fR afterwards. If no +memory can be obtained, it saves data for the first 15 capturing parentheses +only, as there is no way to give an out-of-memory error from within a +recursion. + + +.SH PERFORMANCE +Certain items that may appear in patterns are more efficient than others. It is +more efficient to use a character class like [aeiou] than a set of alternatives +such as (a|e|i|o|u). In general, the simplest construction that provides the +required behaviour is usually the most efficient. Jeffrey Friedl's book +contains a lot of discussion about optimizing regular expressions for efficient +performance. + +When a pattern begins with .* and the PCRE_DOTALL option is set, the pattern is +implicitly anchored by PCRE, since it can match only at the start of a subject +string. However, if PCRE_DOTALL is not set, PCRE cannot make this optimization, +because the . metacharacter does not then match a newline, and if the subject +string contains newlines, the pattern may match from the character immediately +following one of them instead of from the very start. For example, the pattern + + (.*) second + +matches the subject "first\\nand second" (where \\n stands for a newline +character) with the first captured substring being "and". In order to do this, +PCRE has to retry the match starting after every newline in the subject. + +If you are using such a pattern with subject strings that do not contain +newlines, the best performance is obtained by setting PCRE_DOTALL, or starting +the pattern with ^.* to indicate explicit anchoring. That saves PCRE from +having to scan along the subject looking for a newline to restart at. + +Beware of patterns that contain nested indefinite repeats. These can take a +long time to run when applied to a string that does not match. Consider the +pattern fragment + + (a+)* + +This can match "aaaa" in 33 different ways, and this number increases very +rapidly as the string gets longer. (The * repeat can match 0, 1, 2, 3, or 4 +times, and for each of those cases other than 0, the + repeats can match +different numbers of times.) When the remainder of the pattern is such that the +entire match is going to fail, PCRE has in principle to try every possible +variation, and this can take an extremely long time. + +An optimization catches some of the more simple cases such as + + (a+)*b + +where a literal character follows. Before embarking on the standard matching +procedure, PCRE checks that there is a "b" later in the subject string, and if +there is not, it fails the match immediately. However, when there is no +following literal this optimization cannot be used. You can see the difference +by comparing the behaviour of + + (a+)*\\d + +with the pattern above. The former gives a failure almost instantly when +applied to a whole line of "a" characters, whereas the latter takes an +appreciable time with strings longer than about 20 characters. + +.SH AUTHOR +Philip Hazel +.br +University Computing Service, +.br +New Museums Site, +.br +Cambridge CB2 3QG, England. +.br +Phone: +44 1223 334714 + +Last updated: 27 January 2000 +.br +Copyright (c) 1997-2000 University of Cambridge. diff --git a/srclib/pcre/doc/pcre.html b/srclib/pcre/doc/pcre.html new file mode 100644 index 00000000000..2ce289007de --- /dev/null +++ b/srclib/pcre/doc/pcre.html @@ -0,0 +1,2259 @@ + + +pcre specification + + +

pcre specification

+This HTML document has been generated automatically from the original man page. +If there is any nonsense in it, please consult the man page in case the +conversion went wrong. + +
  • NAME +

    +pcre - Perl-compatible regular expressions. +

    +
  • SYNOPSIS +

    +#include <pcre.h> +

    +

    +pcre *pcre_compile(const char *pattern, int options, +const char **errptr, int *erroffset, +const unsigned char *tableptr); +

    +

    +pcre_extra *pcre_study(const pcre *code, int options, +const char **errptr); +

    +

    +int pcre_exec(const pcre *code, const pcre_extra *extra, +const char *subject, int length, int startoffset, +int options, int *ovector, int ovecsize); +

    +

    +int pcre_copy_substring(const char *subject, int *ovector, +int stringcount, int stringnumber, char *buffer, +int buffersize); +

    +

    +int pcre_get_substring(const char *subject, int *ovector, +int stringcount, int stringnumber, +const char **stringptr); +

    +

    +int pcre_get_substring_list(const char *subject, +int *ovector, int stringcount, const char ***listptr); +

    +

    +const unsigned char *pcre_maketables(void); +

    +

    +int pcre_fullinfo(const pcre *code, const pcre_extra *extra, +int what, void *where); +

    +

    +int pcre_info(const pcre *code, int *optptr, int +*firstcharptr); +

    +

    +char *pcre_version(void); +

    +

    +void *(*pcre_malloc)(size_t); +

    +

    +void (*pcre_free)(void *); +

    +
  • DESCRIPTION +

    +The PCRE library is a set of functions that implement regular expression +pattern matching using the same syntax and semantics as Perl 5, with just a few +differences (see below). The current implementation corresponds to Perl 5.005, +with some additional features from the Perl development release. +

    +

    +PCRE has its own native API, which is described in this document. There is also +a set of wrapper functions that correspond to the POSIX regular expression API. +These are described in the pcreposix documentation. +

    +

    +The native API function prototypes are defined in the header file pcre.h, +and on Unix systems the library itself is called libpcre.a, so can be +accessed by adding -lpcre to the command for linking an application which +calls it. The header file defines the macros PCRE_MAJOR and PCRE_MINOR to +contain the major and minor release numbers for the library. Applications can +use these to include support for different releases. +

    +

    +The functions pcre_compile(), pcre_study(), and pcre_exec() +are used for compiling and matching regular expressions, while +pcre_copy_substring(), pcre_get_substring(), and +pcre_get_substring_list() are convenience functions for extracting +captured substrings from a matched subject string. The function +pcre_maketables() is used (optionally) to build a set of character tables +in the current locale for passing to pcre_compile(). +

    +

    +The function pcre_fullinfo() is used to find out information about a +compiled pattern; pcre_info() is an obsolete version which returns only +some of the available information, but is retained for backwards compatibility. +The function pcre_version() returns a pointer to a string containing the +version of PCRE and its date of release. +

    +

    +The global variables pcre_malloc and pcre_free initially contain +the entry points of the standard malloc() and free() functions +respectively. PCRE calls the memory management functions via these variables, +so a calling program can replace them if it wishes to intercept the calls. This +should be done before calling any PCRE functions. +

    +
  • MULTI-THREADING +

    +The PCRE functions can be used in multi-threading applications, with the +proviso that the memory management functions pointed to by pcre_malloc +and pcre_free are shared by all threads. +

    +

    +The compiled form of a regular expression is not altered during matching, so +the same compiled pattern can safely be used by several threads at once. +

    +
  • COMPILING A PATTERN +

    +The function pcre_compile() is called to compile a pattern into an +internal form. The pattern is a C string terminated by a binary zero, and +is passed in the argument pattern. A pointer to a single block of memory +that is obtained via pcre_malloc is returned. This contains the +compiled code and related data. The pcre type is defined for this for +convenience, but in fact pcre is just a typedef for void, since the +contents of the block are not externally defined. It is up to the caller to +free the memory when it is no longer required. +

    +

    +The size of a compiled pattern is roughly proportional to the length of the +pattern string, except that each character class (other than those containing +just a single character, negated or not) requires 33 bytes, and repeat +quantifiers with a minimum greater than one or a bounded maximum cause the +relevant portions of the compiled pattern to be replicated. +

    +

    +The options argument contains independent bits that affect the +compilation. It should be zero if no options are required. Some of the options, +in particular, those that are compatible with Perl, can also be set and unset +from within the pattern (see the detailed description of regular expressions +below). For these options, the contents of the options argument specifies +their initial settings at the start of compilation and execution. The +PCRE_ANCHORED option can be set at the time of matching as well as at compile +time. +

    +

    +If errptr is NULL, pcre_compile() returns NULL immediately. +Otherwise, if compilation of a pattern fails, pcre_compile() returns +NULL, and sets the variable pointed to by errptr to point to a textual +error message. The offset from the start of the pattern to the character where +the error was discovered is placed in the variable pointed to by +erroffset, which must not be NULL. If it is, an immediate error is given. +

    +

    +If the final argument, tableptr, is NULL, PCRE uses a default set of +character tables which are built when it is compiled, using the default C +locale. Otherwise, tableptr must be the result of a call to +pcre_maketables(). See the section on locale support below. +

    +

    +The following option bits are defined in the header file: +

    +

    +

    +  PCRE_ANCHORED
    +
    +

    +

    +If this bit is set, the pattern is forced to be "anchored", that is, it is +constrained to match only at the start of the string which is being searched +(the "subject string"). This effect can also be achieved by appropriate +constructs in the pattern itself, which is the only way to do it in Perl. +

    +

    +

    +  PCRE_CASELESS
    +
    +

    +

    +If this bit is set, letters in the pattern match both upper and lower case +letters. It is equivalent to Perl's /i option. +

    +

    +

    +  PCRE_DOLLAR_ENDONLY
    +
    +

    +

    +If this bit is set, a dollar metacharacter in the pattern matches only at the +end of the subject string. Without this option, a dollar also matches +immediately before the final character if it is a newline (but not before any +other newlines). The PCRE_DOLLAR_ENDONLY option is ignored if PCRE_MULTILINE is +set. There is no equivalent to this option in Perl. +

    +

    +

    +  PCRE_DOTALL
    +
    +

    +

    +If this bit is set, a dot metacharater in the pattern matches all characters, +including newlines. Without it, newlines are excluded. This option is +equivalent to Perl's /s option. A negative class such as [^a] always matches a +newline character, independent of the setting of this option. +

    +

    +

    +  PCRE_EXTENDED
    +
    +

    +

    +If this bit is set, whitespace data characters in the pattern are totally +ignored except when escaped or inside a character class, and characters between +an unescaped # outside a character class and the next newline character, +inclusive, are also ignored. This is equivalent to Perl's /x option, and makes +it possible to include comments inside complicated patterns. Note, however, +that this applies only to data characters. Whitespace characters may never +appear within special character sequences in a pattern, for example within the +sequence (?( which introduces a conditional subpattern. +

    +

    +

    +  PCRE_EXTRA
    +
    +

    +

    +This option was invented in order to turn on additional functionality of PCRE +that is incompatible with Perl, but it is currently of very little use. When +set, any backslash in a pattern that is followed by a letter that has no +special meaning causes an error, thus reserving these combinations for future +expansion. By default, as in Perl, a backslash followed by a letter with no +special meaning is treated as a literal. There are at present no other features +controlled by this option. It can also be set by a (?X) option setting within a +pattern. +

    +

    +

    +  PCRE_MULTILINE
    +
    +

    +

    +By default, PCRE treats the subject string as consisting of a single "line" of +characters (even if it actually contains several newlines). The "start of line" +metacharacter (^) matches only at the start of the string, while the "end of +line" metacharacter ($) matches only at the end of the string, or before a +terminating newline (unless PCRE_DOLLAR_ENDONLY is set). This is the same as +Perl. +

    +

    +When PCRE_MULTILINE it is set, the "start of line" and "end of line" constructs +match immediately following or immediately before any newline in the subject +string, respectively, as well as at the very start and end. This is equivalent +to Perl's /m option. If there are no "\n" characters in a subject string, or +no occurrences of ^ or $ in a pattern, setting PCRE_MULTILINE has no +effect. +

    +

    +

    +  PCRE_UNGREEDY
    +
    +

    +

    +This option inverts the "greediness" of the quantifiers so that they are not +greedy by default, but become greedy if followed by "?". It is not compatible +with Perl. It can also be set by a (?U) option setting within the pattern. +

    +
  • STUDYING A PATTERN +

    +When a pattern is going to be used several times, it is worth spending more +time analyzing it in order to speed up the time taken for matching. The +function pcre_study() takes a pointer to a compiled pattern as its first +argument, and returns a pointer to a pcre_extra block (another void +typedef) containing additional information about the pattern; this can be +passed to pcre_exec(). If no additional information is available, NULL +is returned. +

    +

    +The second argument contains option bits. At present, no options are defined +for pcre_study(), and this argument should always be zero. +

    +

    +The third argument for pcre_study() is a pointer to an error message. If +studying succeeds (even if no data is returned), the variable it points to is +set to NULL. Otherwise it points to a textual error message. +

    +

    +At present, studying a pattern is useful only for non-anchored patterns that do +not have a single fixed starting character. A bitmap of possible starting +characters is created. +

    +
  • LOCALE SUPPORT +

    +PCRE handles caseless matching, and determines whether characters are letters, +digits, or whatever, by reference to a set of tables. The library contains a +default set of tables which is created in the default C locale when PCRE is +compiled. This is used when the final argument of pcre_compile() is NULL, +and is sufficient for many applications. +

    +

    +An alternative set of tables can, however, be supplied. Such tables are built +by calling the pcre_maketables() function, which has no arguments, in the +relevant locale. The result can then be passed to pcre_compile() as often +as necessary. For example, to build and use tables that are appropriate for the +French locale (where accented characters with codes greater than 128 are +treated as letters), the following code could be used: +

    +

    +

    +  setlocale(LC_CTYPE, "fr");
    +  tables = pcre_maketables();
    +  re = pcre_compile(..., tables);
    +
    +

    +

    +The tables are built in memory that is obtained via pcre_malloc. The +pointer that is passed to pcre_compile is saved with the compiled +pattern, and the same tables are used via this pointer by pcre_study() +and pcre_exec(). Thus for any single pattern, compilation, studying and +matching all happen in the same locale, but different patterns can be compiled +in different locales. It is the caller's responsibility to ensure that the +memory containing the tables remains available for as long as it is needed. +

    +
  • INFORMATION ABOUT A PATTERN +

    +The pcre_fullinfo() function returns information about a compiled +pattern. It replaces the obsolete pcre_info() function, which is +nevertheless retained for backwards compability (and is documented below). +

    +

    +The first argument for pcre_fullinfo() is a pointer to the compiled +pattern. The second argument is the result of pcre_study(), or NULL if +the pattern was not studied. The third argument specifies which piece of +information is required, while the fourth argument is a pointer to a variable +to receive the data. The yield of the function is zero for success, or one of +the following negative numbers: +

    +

    +

    +  PCRE_ERROR_NULL       the argument code was NULL
    +                        the argument where was NULL
    +  PCRE_ERROR_BADMAGIC   the "magic number" was not found
    +  PCRE_ERROR_BADOPTION  the value of what was invalid
    +
    +

    +

    +The possible values for the third argument are defined in pcre.h, and are +as follows: +

    +

    +

    +  PCRE_INFO_OPTIONS
    +
    +

    +

    +Return a copy of the options with which the pattern was compiled. The fourth +argument should point to au unsigned long int variable. These option bits +are those specified in the call to pcre_compile(), modified by any +top-level option settings within the pattern itself, and with the PCRE_ANCHORED +bit forcibly set if the form of the pattern implies that it can match only at +the start of a subject string. +

    +

    +

    +  PCRE_INFO_SIZE
    +
    +

    +

    +Return the size of the compiled pattern, that is, the value that was passed as +the argument to pcre_malloc() when PCRE was getting memory in which to +place the compiled data. The fourth argument should point to a size_t +variable. +

    +

    +

    +  PCRE_INFO_CAPTURECOUNT
    +
    +

    +

    +Return the number of capturing subpatterns in the pattern. The fourth argument +should point to an \fbint\fR variable. +

    +

    +

    +  PCRE_INFO_BACKREFMAX
    +
    +

    +

    +Return the number of the highest back reference in the pattern. The fourth +argument should point to an int variable. Zero is returned if there are +no back references. +

    +

    +

    +  PCRE_INFO_FIRSTCHAR
    +
    +

    +

    +Return information about the first character of any matched string, for a +non-anchored pattern. If there is a fixed first character, e.g. from a pattern +such as (cat|cow|coyote), then it is returned in the integer pointed to by +where. Otherwise, if either +

    +

    +(a) the pattern was compiled with the PCRE_MULTILINE option, and every branch +starts with "^", or +

    +

    +(b) every branch of the pattern starts with ".*" and PCRE_DOTALL is not set +(if it were set, the pattern would be anchored), +

    +

    +then -1 is returned, indicating that the pattern matches only at the +start of a subject string or after any "\n" within the string. Otherwise -2 is +returned. For anchored patterns, -2 is returned. +

    +

    +

    +  PCRE_INFO_FIRSTTABLE
    +
    +

    +

    +If the pattern was studied, and this resulted in the construction of a 256-bit +table indicating a fixed set of characters for the first character in any +matching string, a pointer to the table is returned. Otherwise NULL is +returned. The fourth argument should point to an unsigned char * +variable. +

    +

    +

    +  PCRE_INFO_LASTLITERAL
    +
    +

    +

    +For a non-anchored pattern, return the value of the rightmost literal character +which must exist in any matched string, other than at its start. The fourth +argument should point to an int variable. If there is no such character, +or if the pattern is anchored, -1 is returned. For example, for the pattern +/a\d+z\d+/ the returned value is 'z'. +

    +

    +The pcre_info() function is now obsolete because its interface is too +restrictive to return all the available data about a compiled pattern. New +programs should use pcre_fullinfo() instead. The yield of +pcre_info() is the number of capturing subpatterns, or one of the +following negative numbers: +

    +

    +

    +  PCRE_ERROR_NULL       the argument code was NULL
    +  PCRE_ERROR_BADMAGIC   the "magic number" was not found
    +
    +

    +

    +If the optptr argument is not NULL, a copy of the options with which the +pattern was compiled is placed in the integer it points to (see +PCRE_INFO_OPTIONS above). +

    +

    +If the pattern is not anchored and the firstcharptr argument is not NULL, +it is used to pass back information about the first character of any matched +string (see PCRE_INFO_FIRSTCHAR above). +

    +
  • MATCHING A PATTERN +

    +The function pcre_exec() is called to match a subject string against a +pre-compiled pattern, which is passed in the code argument. If the +pattern has been studied, the result of the study should be passed in the +extra argument. Otherwise this must be NULL. +

    +

    +The PCRE_ANCHORED option can be passed in the options argument, whose +unused bits must be zero. However, if a pattern was compiled with +PCRE_ANCHORED, or turned out to be anchored by virtue of its contents, it +cannot be made unachored at matching time. +

    +

    +There are also three further options that can be set only at matching time: +

    +

    +

    +  PCRE_NOTBOL
    +
    +

    +

    +The first character of the string is not the beginning of a line, so the +circumflex metacharacter should not match before it. Setting this without +PCRE_MULTILINE (at compile time) causes circumflex never to match. +

    +

    +

    +  PCRE_NOTEOL
    +
    +

    +

    +The end of the string is not the end of a line, so the dollar metacharacter +should not match it nor (except in multiline mode) a newline immediately before +it. Setting this without PCRE_MULTILINE (at compile time) causes dollar never +to match. +

    +

    +

    +  PCRE_NOTEMPTY
    +
    +

    +

    +An empty string is not considered to be a valid match if this option is set. If +there are alternatives in the pattern, they are tried. If all the alternatives +match the empty string, the entire match fails. For example, if the pattern +

    +

    +

    +  a?b?
    +
    +

    +

    +is applied to a string not beginning with "a" or "b", it matches the empty +string at the start of the subject. With PCRE_NOTEMPTY set, this match is not +valid, so PCRE searches further into the string for occurrences of "a" or "b". +

    +

    +Perl has no direct equivalent of PCRE_NOTEMPTY, but it does make a special case +of a pattern match of the empty string within its split() function, and +when using the /g modifier. It is possible to emulate Perl's behaviour after +matching a null string by first trying the match again at the same offset with +PCRE_NOTEMPTY set, and then if that fails by advancing the starting offset (see +below) and trying an ordinary match again. +

    +

    +The subject string is passed as a pointer in subject, a length in +length, and a starting offset in startoffset. Unlike the pattern +string, it may contain binary zero characters. When the starting offset is +zero, the search for a match starts at the beginning of the subject, and this +is by far the most common case. +

    +

    +A non-zero starting offset is useful when searching for another match in the +same subject by calling pcre_exec() again after a previous success. +Setting startoffset differs from just passing over a shortened string and +setting PCRE_NOTBOL in the case of a pattern that begins with any kind of +lookbehind. For example, consider the pattern +

    +

    +

    +  \Biss\B
    +
    +

    +

    +which finds occurrences of "iss" in the middle of words. (\B matches only if +the current position in the subject is not a word boundary.) When applied to +the string "Mississipi" the first call to pcre_exec() finds the first +occurrence. If pcre_exec() is called again with just the remainder of the +subject, namely "issipi", it does not match, because \B is always false at the +start of the subject, which is deemed to be a word boundary. However, if +pcre_exec() is passed the entire string again, but with startoffset +set to 4, it finds the second occurrence of "iss" because it is able to look +behind the starting point to discover that it is preceded by a letter. +

    +

    +If a non-zero starting offset is passed when the pattern is anchored, one +attempt to match at the given offset is tried. This can only succeed if the +pattern does not require the match to be at the start of the subject. +

    +

    +In general, a pattern matches a certain portion of the subject, and in +addition, further substrings from the subject may be picked out by parts of the +pattern. Following the usage in Jeffrey Friedl's book, this is called +"capturing" in what follows, and the phrase "capturing subpattern" is used for +a fragment of a pattern that picks out a substring. PCRE supports several other +kinds of parenthesized subpattern that do not cause substrings to be captured. +

    +

    +Captured substrings are returned to the caller via a vector of integer offsets +whose address is passed in ovector. The number of elements in the vector +is passed in ovecsize. The first two-thirds of the vector is used to pass +back captured substrings, each substring using a pair of integers. The +remaining third of the vector is used as workspace by pcre_exec() while +matching capturing subpatterns, and is not available for passing back +information. The length passed in ovecsize should always be a multiple of +three. If it is not, it is rounded down. +

    +

    +When a match has been successful, information about captured substrings is +returned in pairs of integers, starting at the beginning of ovector, and +continuing up to two-thirds of its length at the most. The first element of a +pair is set to the offset of the first character in a substring, and the second +is set to the offset of the first character after the end of a substring. The +first pair, ovector[0] and ovector[1], identify the portion of the +subject string matched by the entire pattern. The next pair is used for the +first capturing subpattern, and so on. The value returned by pcre_exec() +is the number of pairs that have been set. If there are no capturing +subpatterns, the return value from a successful match is 1, indicating that +just the first pair of offsets has been set. +

    +

    +Some convenience functions are provided for extracting the captured substrings +as separate strings. These are described in the following section. +

    +

    +It is possible for an capturing subpattern number n+1 to match some +part of the subject when subpattern n has not been used at all. For +example, if the string "abc" is matched against the pattern (a|(z))(bc) +subpatterns 1 and 3 are matched, but 2 is not. When this happens, both offset +values corresponding to the unused subpattern are set to -1. +

    +

    +If a capturing subpattern is matched repeatedly, it is the last portion of the +string that it matched that gets returned. +

    +

    +If the vector is too small to hold all the captured substrings, it is used as +far as possible (up to two-thirds of its length), and the function returns a +value of zero. In particular, if the substring offsets are not of interest, +pcre_exec() may be called with ovector passed as NULL and +ovecsize as zero. However, if the pattern contains back references and +the ovector isn't big enough to remember the related substrings, PCRE has +to get additional memory for use during matching. Thus it is usually advisable +to supply an ovector. +

    +

    +Note that pcre_info() can be used to find out how many capturing +subpatterns there are in a compiled pattern. The smallest size for +ovector that will allow for n captured substrings in addition to +the offsets of the substring matched by the whole pattern is (n+1)*3. +

    +

    +If pcre_exec() fails, it returns a negative number. The following are +defined in the header file: +

    +

    +

    +  PCRE_ERROR_NOMATCH        (-1)
    +
    +

    +

    +The subject string did not match the pattern. +

    +

    +

    +  PCRE_ERROR_NULL           (-2)
    +
    +

    +

    +Either code or subject was passed as NULL, or ovector was +NULL and ovecsize was not zero. +

    +

    +

    +  PCRE_ERROR_BADOPTION      (-3)
    +
    +

    +

    +An unrecognized bit was set in the options argument. +

    +

    +

    +  PCRE_ERROR_BADMAGIC       (-4)
    +
    +

    +

    +PCRE stores a 4-byte "magic number" at the start of the compiled code, to catch +the case when it is passed a junk pointer. This is the error it gives when the +magic number isn't present. +

    +

    +

    +  PCRE_ERROR_UNKNOWN_NODE   (-5)
    +
    +

    +

    +While running the pattern match, an unknown item was encountered in the +compiled pattern. This error could be caused by a bug in PCRE or by overwriting +of the compiled pattern. +

    +

    +

    +  PCRE_ERROR_NOMEMORY       (-6)
    +
    +

    +

    +If a pattern contains back references, but the ovector that is passed to +pcre_exec() is not big enough to remember the referenced substrings, PCRE +gets a block of memory at the start of matching to use for this purpose. If the +call via pcre_malloc() fails, this error is given. The memory is freed at +the end of matching. +

    +
  • EXTRACTING CAPTURED SUBSTRINGS +

    +Captured substrings can be accessed directly by using the offsets returned by +pcre_exec() in ovector. For convenience, the functions +pcre_copy_substring(), pcre_get_substring(), and +pcre_get_substring_list() are provided for extracting captured substrings +as new, separate, zero-terminated strings. A substring that contains a binary +zero is correctly extracted and has a further zero added on the end, but the +result does not, of course, function as a C string. +

    +

    +The first three arguments are the same for all three functions: subject +is the subject string which has just been successfully matched, ovector +is a pointer to the vector of integer offsets that was passed to +pcre_exec(), and stringcount is the number of substrings that +were captured by the match, including the substring that matched the entire +regular expression. This is the value returned by pcre_exec if it +is greater than zero. If pcre_exec() returned zero, indicating that it +ran out of space in ovector, then the value passed as +stringcount should be the size of the vector divided by three. +

    +

    +The functions pcre_copy_substring() and pcre_get_substring() +extract a single substring, whose number is given as stringnumber. A +value of zero extracts the substring that matched the entire pattern, while +higher values extract the captured substrings. For pcre_copy_substring(), +the string is placed in buffer, whose length is given by +buffersize, while for pcre_get_substring() a new block of store is +obtained via pcre_malloc, and its address is returned via +stringptr. The yield of the function is the length of the string, not +including the terminating zero, or one of +

    +

    +

    +  PCRE_ERROR_NOMEMORY       (-6)
    +
    +

    +

    +The buffer was too small for pcre_copy_substring(), or the attempt to get +memory failed for pcre_get_substring(). +

    +

    +

    +  PCRE_ERROR_NOSUBSTRING    (-7)
    +
    +

    +

    +There is no substring whose number is stringnumber. +

    +

    +The pcre_get_substring_list() function extracts all available substrings +and builds a list of pointers to them. All this is done in a single block of +memory which is obtained via pcre_malloc. The address of the memory block +is returned via listptr, which is also the start of the list of string +pointers. The end of the list is marked by a NULL pointer. The yield of the +function is zero if all went well, or +

    +

    +

    +  PCRE_ERROR_NOMEMORY       (-6)
    +
    +

    +

    +if the attempt to get the memory block failed. +

    +

    +When any of these functions encounter a substring that is unset, which can +happen when capturing subpattern number n+1 matches some part of the +subject, but subpattern n has not been used at all, they return an empty +string. This can be distinguished from a genuine zero-length substring by +inspecting the appropriate offset in ovector, which is negative for unset +substrings. +

    +
  • LIMITATIONS +

    +There are some size limitations in PCRE but it is hoped that they will never in +practice be relevant. +The maximum length of a compiled pattern is 65539 (sic) bytes. +All values in repeating quantifiers must be less than 65536. +The maximum number of capturing subpatterns is 99. +The maximum number of all parenthesized subpatterns, including capturing +subpatterns, assertions, and other types of subpattern, is 200. +

    +

    +The maximum length of a subject string is the largest positive number that an +integer variable can hold. However, PCRE uses recursion to handle subpatterns +and indefinite repetition. This means that the available stack space may limit +the size of a subject string that can be processed by certain patterns. +

    +
  • DIFFERENCES FROM PERL +

    +The differences described here are with respect to Perl 5.005. +

    +

    +1. By default, a whitespace character is any character that the C library +function isspace() recognizes, though it is possible to compile PCRE with +alternative character type tables. Normally isspace() matches space, +formfeed, newline, carriage return, horizontal tab, and vertical tab. Perl 5 +no longer includes vertical tab in its set of whitespace characters. The \v +escape that was in the Perl documentation for a long time was never in fact +recognized. However, the character itself was treated as whitespace at least +up to 5.002. In 5.004 and 5.005 it does not match \s. +

    +

    +2. PCRE does not allow repeat quantifiers on lookahead assertions. Perl permits +them, but they do not mean what you might think. For example, (?!a){3} does +not assert that the next three characters are not "a". It just asserts that the +next character is not "a" three times. +

    +

    +3. Capturing subpatterns that occur inside negative lookahead assertions are +counted, but their entries in the offsets vector are never set. Perl sets its +numerical variables from any such patterns that are matched before the +assertion fails to match something (thereby succeeding), but only if the +negative lookahead assertion contains just one branch. +

    +

    +4. Though binary zero characters are supported in the subject string, they are +not allowed in a pattern string because it is passed as a normal C string, +terminated by zero. The escape sequence "\0" can be used in the pattern to +represent a binary zero. +

    +

    +5. The following Perl escape sequences are not supported: \l, \u, \L, \U, +\E, \Q. In fact these are implemented by Perl's general string-handling and +are not part of its pattern matching engine. +

    +

    +6. The Perl \G assertion is not supported as it is not relevant to single +pattern matches. +

    +

    +7. Fairly obviously, PCRE does not support the (?{code}) and (?p{code}) +constructions. However, there is some experimental support for recursive +patterns using the non-Perl item (?R). +

    +

    +8. There are at the time of writing some oddities in Perl 5.005_02 concerned +with the settings of captured strings when part of a pattern is repeated. For +example, matching "aba" against the pattern /^(a(b)?)+$/ sets $2 to the value +"b", but matching "aabbaa" against /^(aa(bb)?)+$/ leaves $2 unset. However, if +the pattern is changed to /^(aa(b(b))?)+$/ then $2 (and $3) get set. +

    +

    +In Perl 5.004 $2 is set in both cases, and that is also true of PCRE. If in the +future Perl changes to a consistent state that is different, PCRE may change to +follow. +

    +

    +9. Another as yet unresolved discrepancy is that in Perl 5.005_02 the pattern +/^(a)?(?(1)a|b)+$/ matches the string "a", whereas in PCRE it does not. +However, in both Perl and PCRE /^(a)?a/ matched against "a" leaves $1 unset. +

    +

    +10. PCRE provides some extensions to the Perl regular expression facilities: +

    +

    +(a) Although lookbehind assertions must match fixed length strings, each +alternative branch of a lookbehind assertion can match a different length of +string. Perl 5.005 requires them all to have the same length. +

    +

    +(b) If PCRE_DOLLAR_ENDONLY is set and PCRE_MULTILINE is not set, the $ meta- +character matches only at the very end of the string. +

    +

    +(c) If PCRE_EXTRA is set, a backslash followed by a letter with no special +meaning is faulted. +

    +

    +(d) If PCRE_UNGREEDY is set, the greediness of the repetition quantifiers is +inverted, that is, by default they are not greedy, but if followed by a +question mark they are. +

    +

    +(e) PCRE_ANCHORED can be used to force a pattern to be tried only at the start +of the subject. +

    +

    +(f) The PCRE_NOTBOL, PCRE_NOTEOL, and PCRE_NOTEMPTY options for +pcre_exec() have no Perl equivalents. +

    +

    +(g) The (?R) construct allows for recursive pattern matching (Perl 5.6 can do +this using the (?p{code}) construct, which PCRE cannot of course support.) +

    +
  • REGULAR EXPRESSION DETAILS +

    +The syntax and semantics of the regular expressions supported by PCRE are +described below. Regular expressions are also described in the Perl +documentation and in a number of other books, some of which have copious +examples. Jeffrey Friedl's "Mastering Regular Expressions", published by +O'Reilly (ISBN 1-56592-257), covers them in great detail. The description +here is intended as reference documentation. +

    +

    +A regular expression is a pattern that is matched against a subject string from +left to right. Most characters stand for themselves in a pattern, and match the +corresponding characters in the subject. As a trivial example, the pattern +

    +

    +

    +  The quick brown fox
    +
    +

    +

    +matches a portion of a subject string that is identical to itself. The power of +regular expressions comes from the ability to include alternatives and +repetitions in the pattern. These are encoded in the pattern by the use of +meta-characters, which do not stand for themselves but instead are +interpreted in some special way. +

    +

    +There are two different sets of meta-characters: those that are recognized +anywhere in the pattern except within square brackets, and those that are +recognized in square brackets. Outside square brackets, the meta-characters are +as follows: +

    +

    +

    +  \      general escape character with several uses
    +  ^      assert start of subject (or line, in multiline mode)
    +  $      assert end of subject (or line, in multiline mode)
    +  .      match any character except newline (by default)
    +  [      start character class definition
    +  |      start of alternative branch
    +  (      start subpattern
    +  )      end subpattern
    +  ?      extends the meaning of (
    +         also 0 or 1 quantifier
    +         also quantifier minimizer
    +  *      0 or more quantifier
    +  +      1 or more quantifier
    +  {      start min/max quantifier
    +
    +

    +

    +Part of a pattern that is in square brackets is called a "character class". In +a character class the only meta-characters are: +

    +

    +

    +  \      general escape character
    +  ^      negate the class, but only if the first character
    +  -      indicates character range
    +  ]      terminates the character class
    +
    +

    +

    +The following sections describe the use of each of the meta-characters. +

    +
  • BACKSLASH +

    +The backslash character has several uses. Firstly, if it is followed by a +non-alphameric character, it takes away any special meaning that character may +have. This use of backslash as an escape character applies both inside and +outside character classes. +

    +

    +For example, if you want to match a "*" character, you write "\*" in the +pattern. This applies whether or not the following character would otherwise be +interpreted as a meta-character, so it is always safe to precede a +non-alphameric with "\" to specify that it stands for itself. In particular, +if you want to match a backslash, you write "\\". +

    +

    +If a pattern is compiled with the PCRE_EXTENDED option, whitespace in the +pattern (other than in a character class) and characters between a "#" outside +a character class and the next newline character are ignored. An escaping +backslash can be used to include a whitespace or "#" character as part of the +pattern. +

    +

    +A second use of backslash provides a way of encoding non-printing characters +in patterns in a visible manner. There is no restriction on the appearance of +non-printing characters, apart from the binary zero that terminates a pattern, +but when a pattern is being prepared by text editing, it is usually easier to +use one of the following escape sequences than the binary character it +represents: +

    +

    +

    +  \a     alarm, that is, the BEL character (hex 07)
    +  \cx    "control-x", where x is any character
    +  \e     escape (hex 1B)
    +  \f     formfeed (hex 0C)
    +  \n     newline (hex 0A)
    +  \r     carriage return (hex 0D)
    +  \t     tab (hex 09)
    +  \xhh   character with hex code hh
    +  \ddd   character with octal code ddd, or backreference
    +
    +

    +

    +The precise effect of "\cx" is as follows: if "x" is a lower case letter, it +is converted to upper case. Then bit 6 of the character (hex 40) is inverted. +Thus "\cz" becomes hex 1A, but "\c{" becomes hex 3B, while "\c;" becomes hex +7B. +

    +

    +After "\x", up to two hexadecimal digits are read (letters can be in upper or +lower case). +

    +

    +After "\0" up to two further octal digits are read. In both cases, if there +are fewer than two digits, just those that are present are used. Thus the +sequence "\0\x\07" specifies two binary zeros followed by a BEL character. +Make sure you supply two digits after the initial zero if the character that +follows is itself an octal digit. +

    +

    +The handling of a backslash followed by a digit other than 0 is complicated. +Outside a character class, PCRE reads it and any following digits as a decimal +number. If the number is less than 10, or if there have been at least that many +previous capturing left parentheses in the expression, the entire sequence is +taken as a back reference. A description of how this works is given +later, following the discussion of parenthesized subpatterns. +

    +

    +Inside a character class, or if the decimal number is greater than 9 and there +have not been that many capturing subpatterns, PCRE re-reads up to three octal +digits following the backslash, and generates a single byte from the least +significant 8 bits of the value. Any subsequent digits stand for themselves. +For example: +

    +

    +

    +  \040   is another way of writing a space
    +  \40    is the same, provided there are fewer than 40
    +            previous capturing subpatterns
    +  \7     is always a back reference
    +  \11    might be a back reference, or another way of
    +            writing a tab
    +  \011   is always a tab
    +  \0113  is a tab followed by the character "3"
    +  \113   is the character with octal code 113 (since there
    +            can be no more than 99 back references)
    +  \377   is a byte consisting entirely of 1 bits
    +  \81    is either a back reference, or a binary zero
    +            followed by the two characters "8" and "1"
    +
    +

    +

    +Note that octal values of 100 or greater must not be introduced by a leading +zero, because no more than three octal digits are ever read. +

    +

    +All the sequences that define a single byte value can be used both inside and +outside character classes. In addition, inside a character class, the sequence +"\b" is interpreted as the backspace character (hex 08). Outside a character +class it has a different meaning (see below). +

    +

    +The third use of backslash is for specifying generic character types: +

    +

    +

    +  \d     any decimal digit
    +  \D     any character that is not a decimal digit
    +  \s     any whitespace character
    +  \S     any character that is not a whitespace character
    +  \w     any "word" character
    +  \W     any "non-word" character
    +
    +

    +

    +Each pair of escape sequences partitions the complete set of characters into +two disjoint sets. Any given character matches one, and only one, of each pair. +

    +

    +A "word" character is any letter or digit or the underscore character, that is, +any character which can be part of a Perl "word". The definition of letters and +digits is controlled by PCRE's character tables, and may vary if locale- +specific matching is taking place (see "Locale support" above). For example, in +the "fr" (French) locale, some character codes greater than 128 are used for +accented letters, and these are matched by \w. +

    +

    +These character type sequences can appear both inside and outside character +classes. They each match one character of the appropriate type. If the current +matching point is at the end of the subject string, all of them fail, since +there is no character to match. +

    +

    +The fourth use of backslash is for certain simple assertions. An assertion +specifies a condition that has to be met at a particular point in a match, +without consuming any characters from the subject string. The use of +subpatterns for more complicated assertions is described below. The backslashed +assertions are +

    +

    +

    +  \b     word boundary
    +  \B     not a word boundary
    +  \A     start of subject (independent of multiline mode)
    +  \Z     end of subject or newline at end (independent of multiline mode)
    +  \z     end of subject (independent of multiline mode)
    +
    +

    +

    +These assertions may not appear in character classes (but note that "\b" has a +different meaning, namely the backspace character, inside a character class). +

    +

    +A word boundary is a position in the subject string where the current character +and the previous character do not both match \w or \W (i.e. one matches +\w and the other matches \W), or the start or end of the string if the +first or last character matches \w, respectively. +

    +

    +The \A, \Z, and \z assertions differ from the traditional circumflex and +dollar (described below) in that they only ever match at the very start and end +of the subject string, whatever options are set. They are not affected by the +PCRE_NOTBOL or PCRE_NOTEOL options. If the startoffset argument of +pcre_exec() is non-zero, \A can never match. The difference between \Z +and \z is that \Z matches before a newline that is the last character of the +string as well as at the end of the string, whereas \z matches only at the +end. +

    +
  • CIRCUMFLEX AND DOLLAR +

    +Outside a character class, in the default matching mode, the circumflex +character is an assertion which is true only if the current matching point is +at the start of the subject string. If the startoffset argument of +pcre_exec() is non-zero, circumflex can never match. Inside a character +class, circumflex has an entirely different meaning (see below). +

    +

    +Circumflex need not be the first character of the pattern if a number of +alternatives are involved, but it should be the first thing in each alternative +in which it appears if the pattern is ever to match that branch. If all +possible alternatives start with a circumflex, that is, if the pattern is +constrained to match only at the start of the subject, it is said to be an +"anchored" pattern. (There are also other constructs that can cause a pattern +to be anchored.) +

    +

    +A dollar character is an assertion which is true only if the current matching +point is at the end of the subject string, or immediately before a newline +character that is the last character in the string (by default). Dollar need +not be the last character of the pattern if a number of alternatives are +involved, but it should be the last item in any branch in which it appears. +Dollar has no special meaning in a character class. +

    +

    +The meaning of dollar can be changed so that it matches only at the very end of +the string, by setting the PCRE_DOLLAR_ENDONLY option at compile or matching +time. This does not affect the \Z assertion. +

    +

    +The meanings of the circumflex and dollar characters are changed if the +PCRE_MULTILINE option is set. When this is the case, they match immediately +after and immediately before an internal "\n" character, respectively, in +addition to matching at the start and end of the subject string. For example, +the pattern /^abc$/ matches the subject string "def\nabc" in multiline mode, +but not otherwise. Consequently, patterns that are anchored in single line mode +because all branches start with "^" are not anchored in multiline mode, and a +match for circumflex is possible when the startoffset argument of +pcre_exec() is non-zero. The PCRE_DOLLAR_ENDONLY option is ignored if +PCRE_MULTILINE is set. +

    +

    +Note that the sequences \A, \Z, and \z can be used to match the start and +end of the subject in both modes, and if all branches of a pattern start with +\A is it always anchored, whether PCRE_MULTILINE is set or not. +

    +
  • FULL STOP (PERIOD, DOT) +

    +Outside a character class, a dot in the pattern matches any one character in +the subject, including a non-printing character, but not (by default) newline. +If the PCRE_DOTALL option is set, then dots match newlines as well. The +handling of dot is entirely independent of the handling of circumflex and +dollar, the only relationship being that they both involve newline characters. +Dot has no special meaning in a character class. +

    +
  • SQUARE BRACKETS +

    +An opening square bracket introduces a character class, terminated by a closing +square bracket. A closing square bracket on its own is not special. If a +closing square bracket is required as a member of the class, it should be the +first data character in the class (after an initial circumflex, if present) or +escaped with a backslash. +

    +

    +A character class matches a single character in the subject; the character must +be in the set of characters defined by the class, unless the first character in +the class is a circumflex, in which case the subject character must not be in +the set defined by the class. If a circumflex is actually required as a member +of the class, ensure it is not the first character, or escape it with a +backslash. +

    +

    +For example, the character class [aeiou] matches any lower case vowel, while +[^aeiou] matches any character that is not a lower case vowel. Note that a +circumflex is just a convenient notation for specifying the characters which +are in the class by enumerating those that are not. It is not an assertion: it +still consumes a character from the subject string, and fails if the current +pointer is at the end of the string. +

    +

    +When caseless matching is set, any letters in a class represent both their +upper case and lower case versions, so for example, a caseless [aeiou] matches +"A" as well as "a", and a caseless [^aeiou] does not match "A", whereas a +caseful version would. +

    +

    +The newline character is never treated in any special way in character classes, +whatever the setting of the PCRE_DOTALL or PCRE_MULTILINE options is. A class +such as [^a] will always match a newline. +

    +

    +The minus (hyphen) character can be used to specify a range of characters in a +character class. For example, [d-m] matches any letter between d and m, +inclusive. If a minus character is required in a class, it must be escaped with +a backslash or appear in a position where it cannot be interpreted as +indicating a range, typically as the first or last character in the class. +

    +

    +It is not possible to have the literal character "]" as the end character of a +range. A pattern such as [W-]46] is interpreted as a class of two characters +("W" and "-") followed by a literal string "46]", so it would match "W46]" or +"-46]". However, if the "]" is escaped with a backslash it is interpreted as +the end of range, so [W-\]46] is interpreted as a single class containing a +range followed by two separate characters. The octal or hexadecimal +representation of "]" can also be used to end a range. +

    +

    +Ranges operate in ASCII collating sequence. They can also be used for +characters specified numerically, for example [\000-\037]. If a range that +includes letters is used when caseless matching is set, it matches the letters +in either case. For example, [W-c] is equivalent to [][\^_`wxyzabc], matched +caselessly, and if character tables for the "fr" locale are in use, +[\xc8-\xcb] matches accented E characters in both cases. +

    +

    +The character types \d, \D, \s, \S, \w, and \W may also appear in a +character class, and add the characters that they match to the class. For +example, [\dABCDEF] matches any hexadecimal digit. A circumflex can +conveniently be used with the upper case character types to specify a more +restricted set of characters than the matching lower case type. For example, +the class [^\W_] matches any letter or digit, but not underscore. +

    +

    +All non-alphameric characters other than \, -, ^ (at the start) and the +terminating ] are non-special in character classes, but it does no harm if they +are escaped. +

    +
  • POSIX CHARACTER CLASSES +

    +Perl 5.6 (not yet released at the time of writing) is going to support the +POSIX notation for character classes, which uses names enclosed by [: and :] +within the enclosing square brackets. PCRE supports this notation. For example, +

    +

    +

    +  [01[:alpha:]%]
    +
    +

    +

    +matches "0", "1", any alphabetic character, or "%". The supported class names +are +

    +

    +

    +  alnum    letters and digits
    +  alpha    letters
    +  ascii    character codes 0 - 127
    +  cntrl    control characters
    +  digit    decimal digits (same as \d)
    +  graph    printing characters, excluding space
    +  lower    lower case letters
    +  print    printing characters, including space
    +  punct    printing characters, excluding letters and digits
    +  space    white space (same as \s)
    +  upper    upper case letters
    +  word     "word" characters (same as \w)
    +  xdigit   hexadecimal digits
    +
    +

    +

    +The names "ascii" and "word" are Perl extensions. Another Perl extension is +negation, which is indicated by a ^ character after the colon. For example, +

    +

    +

    +  [12[:^digit:]]
    +
    +

    +

    +matches "1", "2", or any non-digit. PCRE (and Perl) also recogize the POSIX +syntax [.ch.] and [=ch=] where "ch" is a "collating element", but these are not +supported, and an error is given if they are encountered. +

    +
  • VERTICAL BAR +

    +Vertical bar characters are used to separate alternative patterns. For example, +the pattern +

    +

    +

    +  gilbert|sullivan
    +
    +

    +

    +matches either "gilbert" or "sullivan". Any number of alternatives may appear, +and an empty alternative is permitted (matching the empty string). +The matching process tries each alternative in turn, from left to right, +and the first one that succeeds is used. If the alternatives are within a +subpattern (defined below), "succeeds" means matching the rest of the main +pattern as well as the alternative in the subpattern. +

    +
  • INTERNAL OPTION SETTING +

    +The settings of PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL, and PCRE_EXTENDED +can be changed from within the pattern by a sequence of Perl option letters +enclosed between "(?" and ")". The option letters are +

    +

    +

    +  i  for PCRE_CASELESS
    +  m  for PCRE_MULTILINE
    +  s  for PCRE_DOTALL
    +  x  for PCRE_EXTENDED
    +
    +

    +

    +For example, (?im) sets caseless, multiline matching. It is also possible to +unset these options by preceding the letter with a hyphen, and a combined +setting and unsetting such as (?im-sx), which sets PCRE_CASELESS and +PCRE_MULTILINE while unsetting PCRE_DOTALL and PCRE_EXTENDED, is also +permitted. If a letter appears both before and after the hyphen, the option is +unset. +

    +

    +The scope of these option changes depends on where in the pattern the setting +occurs. For settings that are outside any subpattern (defined below), the +effect is the same as if the options were set or unset at the start of +matching. The following patterns all behave in exactly the same way: +

    +

    +

    +  (?i)abc
    +  a(?i)bc
    +  ab(?i)c
    +  abc(?i)
    +
    +

    +

    +which in turn is the same as compiling the pattern abc with PCRE_CASELESS set. +In other words, such "top level" settings apply to the whole pattern (unless +there are other changes inside subpatterns). If there is more than one setting +of the same option at top level, the rightmost setting is used. +

    +

    +If an option change occurs inside a subpattern, the effect is different. This +is a change of behaviour in Perl 5.005. An option change inside a subpattern +affects only that part of the subpattern that follows it, so +

    +

    +

    +  (a(?i)b)c
    +
    +

    +

    +matches abc and aBc and no other strings (assuming PCRE_CASELESS is not used). +By this means, options can be made to have different settings in different +parts of the pattern. Any changes made in one alternative do carry on +into subsequent branches within the same subpattern. For example, +

    +

    +

    +  (a(?i)b|c)
    +
    +

    +

    +matches "ab", "aB", "c", and "C", even though when matching "C" the first +branch is abandoned before the option setting. This is because the effects of +option settings happen at compile time. There would be some very weird +behaviour otherwise. +

    +

    +The PCRE-specific options PCRE_UNGREEDY and PCRE_EXTRA can be changed in the +same way as the Perl-compatible options by using the characters U and X +respectively. The (?X) flag setting is special in that it must always occur +earlier in the pattern than any of the additional features it turns on, even +when it is at top level. It is best put at the start. +

    +
  • SUBPATTERNS +

    +Subpatterns are delimited by parentheses (round brackets), which can be nested. +Marking part of a pattern as a subpattern does two things: +

    +

    +1. It localizes a set of alternatives. For example, the pattern +

    +

    +

    +  cat(aract|erpillar|)
    +
    +

    +

    +matches one of the words "cat", "cataract", or "caterpillar". Without the +parentheses, it would match "cataract", "erpillar" or the empty string. +

    +

    +2. It sets up the subpattern as a capturing subpattern (as defined above). +When the whole pattern matches, that portion of the subject string that matched +the subpattern is passed back to the caller via the ovector argument of +pcre_exec(). Opening parentheses are counted from left to right (starting +from 1) to obtain the numbers of the capturing subpatterns. +

    +

    +For example, if the string "the red king" is matched against the pattern +

    +

    +

    +  the ((red|white) (king|queen))
    +
    +

    +

    +the captured substrings are "red king", "red", and "king", and are numbered 1, +2, and 3. +

    +

    +The fact that plain parentheses fulfil two functions is not always helpful. +There are often times when a grouping subpattern is required without a +capturing requirement. If an opening parenthesis is followed by "?:", the +subpattern does not do any capturing, and is not counted when computing the +number of any subsequent capturing subpatterns. For example, if the string "the +white queen" is matched against the pattern +

    +

    +

    +  the ((?:red|white) (king|queen))
    +
    +

    +

    +the captured substrings are "white queen" and "queen", and are numbered 1 and +2. The maximum number of captured substrings is 99, and the maximum number of +all subpatterns, both capturing and non-capturing, is 200. +

    +

    +As a convenient shorthand, if any option settings are required at the start of +a non-capturing subpattern, the option letters may appear between the "?" and +the ":". Thus the two patterns +

    +

    +

    +  (?i:saturday|sunday)
    +  (?:(?i)saturday|sunday)
    +
    +

    +

    +match exactly the same set of strings. Because alternative branches are tried +from left to right, and options are not reset until the end of the subpattern +is reached, an option setting in one branch does affect subsequent branches, so +the above patterns match "SUNDAY" as well as "Saturday". +

    +
  • REPETITION +

    +Repetition is specified by quantifiers, which can follow any of the following +items: +

    +

    +

    +  a single character, possibly escaped
    +  the . metacharacter
    +  a character class
    +  a back reference (see next section)
    +  a parenthesized subpattern (unless it is an assertion - see below)
    +
    +

    +

    +The general repetition quantifier specifies a minimum and maximum number of +permitted matches, by giving the two numbers in curly brackets (braces), +separated by a comma. The numbers must be less than 65536, and the first must +be less than or equal to the second. For example: +

    +

    +

    +  z{2,4}
    +
    +

    +

    +matches "zz", "zzz", or "zzzz". A closing brace on its own is not a special +character. If the second number is omitted, but the comma is present, there is +no upper limit; if the second number and the comma are both omitted, the +quantifier specifies an exact number of required matches. Thus +

    +

    +

    +  [aeiou]{3,}
    +
    +

    +

    +matches at least 3 successive vowels, but may match many more, while +

    +

    +

    +  \d{8}
    +
    +

    +

    +matches exactly 8 digits. An opening curly bracket that appears in a position +where a quantifier is not allowed, or one that does not match the syntax of a +quantifier, is taken as a literal character. For example, {,6} is not a +quantifier, but a literal string of four characters. +

    +

    +The quantifier {0} is permitted, causing the expression to behave as if the +previous item and the quantifier were not present. +

    +

    +For convenience (and historical compatibility) the three most common +quantifiers have single-character abbreviations: +

    +

    +

    +  *    is equivalent to {0,}
    +  +    is equivalent to {1,}
    +  ?    is equivalent to {0,1}
    +
    +

    +

    +It is possible to construct infinite loops by following a subpattern that can +match no characters with a quantifier that has no upper limit, for example: +

    +

    +

    +  (a?)*
    +
    +

    +

    +Earlier versions of Perl and PCRE used to give an error at compile time for +such patterns. However, because there are cases where this can be useful, such +patterns are now accepted, but if any repetition of the subpattern does in fact +match no characters, the loop is forcibly broken. +

    +

    +By default, the quantifiers are "greedy", that is, they match as much as +possible (up to the maximum number of permitted times), without causing the +rest of the pattern to fail. The classic example of where this gives problems +is in trying to match comments in C programs. These appear between the +sequences /* and */ and within the sequence, individual * and / characters may +appear. An attempt to match C comments by applying the pattern +

    +

    +

    +  /\*.*\*/
    +
    +

    +

    +to the string +

    +

    +

    +  /* first command */  not comment  /* second comment */
    +
    +

    +

    +fails, because it matches the entire string due to the greediness of the .* +item. +

    +

    +However, if a quantifier is followed by a question mark, then it ceases to be +greedy, and instead matches the minimum number of times possible, so the +pattern +

    +

    +

    +  /\*.*?\*/
    +
    +

    +

    +does the right thing with the C comments. The meaning of the various +quantifiers is not otherwise changed, just the preferred number of matches. +Do not confuse this use of question mark with its use as a quantifier in its +own right. Because it has two uses, it can sometimes appear doubled, as in +

    +

    +

    +  \d??\d
    +
    +

    +

    +which matches one digit by preference, but can match two if that is the only +way the rest of the pattern matches. +

    +

    +If the PCRE_UNGREEDY option is set (an option which is not available in Perl) +then the quantifiers are not greedy by default, but individual ones can be made +greedy by following them with a question mark. In other words, it inverts the +default behaviour. +

    +

    +When a parenthesized subpattern is quantified with a minimum repeat count that +is greater than 1 or with a limited maximum, more store is required for the +compiled pattern, in proportion to the size of the minimum or maximum. +

    +

    +If a pattern starts with .* or .{0,} and the PCRE_DOTALL option (equivalent +to Perl's /s) is set, thus allowing the . to match newlines, then the pattern +is implicitly anchored, because whatever follows will be tried against every +character position in the subject string, so there is no point in retrying the +overall match at any position after the first. PCRE treats such a pattern as +though it were preceded by \A. In cases where it is known that the subject +string contains no newlines, it is worth setting PCRE_DOTALL when the pattern +begins with .* in order to obtain this optimization, or alternatively using ^ +to indicate anchoring explicitly. +

    +

    +When a capturing subpattern is repeated, the value captured is the substring +that matched the final iteration. For example, after +

    +

    +

    +  (tweedle[dume]{3}\s*)+
    +
    +

    +

    +has matched "tweedledum tweedledee" the value of the captured substring is +"tweedledee". However, if there are nested capturing subpatterns, the +corresponding captured values may have been set in previous iterations. For +example, after +

    +

    +

    +  /(a|(b))+/
    +
    +

    +

    +matches "aba" the value of the second captured substring is "b". +

    +
  • BACK REFERENCES +

    +Outside a character class, a backslash followed by a digit greater than 0 (and +possibly further digits) is a back reference to a capturing subpattern earlier +(i.e. to its left) in the pattern, provided there have been that many previous +capturing left parentheses. +

    +

    +However, if the decimal number following the backslash is less than 10, it is +always taken as a back reference, and causes an error only if there are not +that many capturing left parentheses in the entire pattern. In other words, the +parentheses that are referenced need not be to the left of the reference for +numbers less than 10. See the section entitled "Backslash" above for further +details of the handling of digits following a backslash. +

    +

    +A back reference matches whatever actually matched the capturing subpattern in +the current subject string, rather than anything matching the subpattern +itself. So the pattern +

    +

    +

    +  (sens|respons)e and \1ibility
    +
    +

    +

    +matches "sense and sensibility" and "response and responsibility", but not +"sense and responsibility". If caseful matching is in force at the time of the +back reference, then the case of letters is relevant. For example, +

    +

    +

    +  ((?i)rah)\s+\1
    +
    +

    +

    +matches "rah rah" and "RAH RAH", but not "RAH rah", even though the original +capturing subpattern is matched caselessly. +

    +

    +There may be more than one back reference to the same subpattern. If a +subpattern has not actually been used in a particular match, then any back +references to it always fail. For example, the pattern +

    +

    +

    +  (a|(bc))\2
    +
    +

    +

    +always fails if it starts to match "a" rather than "bc". Because there may be +up to 99 back references, all digits following the backslash are taken +as part of a potential back reference number. If the pattern continues with a +digit character, then some delimiter must be used to terminate the back +reference. If the PCRE_EXTENDED option is set, this can be whitespace. +Otherwise an empty comment can be used. +

    +

    +A back reference that occurs inside the parentheses to which it refers fails +when the subpattern is first used, so, for example, (a\1) never matches. +However, such references can be useful inside repeated subpatterns. For +example, the pattern +

    +

    +

    +  (a|b\1)+
    +
    +

    +

    +matches any number of "a"s and also "aba", "ababaa" etc. At each iteration of +the subpattern, the back reference matches the character string corresponding +to the previous iteration. In order for this to work, the pattern must be such +that the first iteration does not need to match the back reference. This can be +done using alternation, as in the example above, or by a quantifier with a +minimum of zero. +

    +
  • ASSERTIONS +

    +An assertion is a test on the characters following or preceding the current +matching point that does not actually consume any characters. The simple +assertions coded as \b, \B, \A, \Z, \z, ^ and $ are described above. More +complicated assertions are coded as subpatterns. There are two kinds: those +that look ahead of the current position in the subject string, and those that +look behind it. +

    +

    +An assertion subpattern is matched in the normal way, except that it does not +cause the current matching position to be changed. Lookahead assertions start +with (?= for positive assertions and (?! for negative assertions. For example, +

    +

    +

    +  \w+(?=;)
    +
    +

    +

    +matches a word followed by a semicolon, but does not include the semicolon in +the match, and +

    +

    +

    +  foo(?!bar)
    +
    +

    +

    +matches any occurrence of "foo" that is not followed by "bar". Note that the +apparently similar pattern +

    +

    +

    +  (?!foo)bar
    +
    +

    +

    +does not find an occurrence of "bar" that is preceded by something other than +"foo"; it finds any occurrence of "bar" whatsoever, because the assertion +(?!foo) is always true when the next three characters are "bar". A +lookbehind assertion is needed to achieve this effect. +

    +

    +Lookbehind assertions start with (?<= for positive assertions and (?<! for +negative assertions. For example, +

    +

    +

    +  (?<!foo)bar
    +
    +

    +

    +does find an occurrence of "bar" that is not preceded by "foo". The contents of +a lookbehind assertion are restricted such that all the strings it matches must +have a fixed length. However, if there are several alternatives, they do not +all have to have the same fixed length. Thus +

    +

    +

    +  (?<=bullock|donkey)
    +
    +

    +

    +is permitted, but +

    +

    +

    +  (?<!dogs?|cats?)
    +
    +

    +

    +causes an error at compile time. Branches that match different length strings +are permitted only at the top level of a lookbehind assertion. This is an +extension compared with Perl 5.005, which requires all branches to match the +same length of string. An assertion such as +

    +

    +

    +  (?<=ab(c|de))
    +
    +

    +

    +is not permitted, because its single top-level branch can match two different +lengths, but it is acceptable if rewritten to use two top-level branches: +

    +

    +

    +  (?<=abc|abde)
    +
    +

    +

    +The implementation of lookbehind assertions is, for each alternative, to +temporarily move the current position back by the fixed width and then try to +match. If there are insufficient characters before the current position, the +match is deemed to fail. Lookbehinds in conjunction with once-only subpatterns +can be particularly useful for matching at the ends of strings; an example is +given at the end of the section on once-only subpatterns. +

    +

    +Several assertions (of any sort) may occur in succession. For example, +

    +

    +

    +  (?<=\d{3})(?<!999)foo
    +
    +

    +

    +matches "foo" preceded by three digits that are not "999". Notice that each of +the assertions is applied independently at the same point in the subject +string. First there is a check that the previous three characters are all +digits, then there is a check that the same three characters are not "999". +This pattern does not match "foo" preceded by six characters, the first +of which are digits and the last three of which are not "999". For example, it +doesn't match "123abcfoo". A pattern to do that is +

    +

    +

    +  (?<=\d{3}...)(?<!999)foo
    +
    +

    +

    +This time the first assertion looks at the preceding six characters, checking +that the first three are digits, and then the second assertion checks that the +preceding three characters are not "999". +

    +

    +Assertions can be nested in any combination. For example, +

    +

    +

    +  (?<=(?<!foo)bar)baz
    +
    +

    +

    +matches an occurrence of "baz" that is preceded by "bar" which in turn is not +preceded by "foo", while +

    +

    +

    +  (?<=\d{3}(?!999)...)foo
    +
    +

    +

    +is another pattern which matches "foo" preceded by three digits and any three +characters that are not "999". +

    +

    +Assertion subpatterns are not capturing subpatterns, and may not be repeated, +because it makes no sense to assert the same thing several times. If any kind +of assertion contains capturing subpatterns within it, these are counted for +the purposes of numbering the capturing subpatterns in the whole pattern. +However, substring capturing is carried out only for positive assertions, +because it does not make sense for negative assertions. +

    +

    +Assertions count towards the maximum of 200 parenthesized subpatterns. +

    +
  • ONCE-ONLY SUBPATTERNS +

    +With both maximizing and minimizing repetition, failure of what follows +normally causes the repeated item to be re-evaluated to see if a different +number of repeats allows the rest of the pattern to match. Sometimes it is +useful to prevent this, either to change the nature of the match, or to cause +it fail earlier than it otherwise might, when the author of the pattern knows +there is no point in carrying on. +

    +

    +Consider, for example, the pattern \d+foo when applied to the subject line +

    +

    +

    +  123456bar
    +
    +

    +

    +After matching all 6 digits and then failing to match "foo", the normal +action of the matcher is to try again with only 5 digits matching the \d+ +item, and then with 4, and so on, before ultimately failing. Once-only +subpatterns provide the means for specifying that once a portion of the pattern +has matched, it is not to be re-evaluated in this way, so the matcher would +give up immediately on failing to match "foo" the first time. The notation is +another kind of special parenthesis, starting with (?> as in this example: +

    +

    +

    +  (?>\d+)bar
    +
    +

    +

    +This kind of parenthesis "locks up" the part of the pattern it contains once +it has matched, and a failure further into the pattern is prevented from +backtracking into it. Backtracking past it to previous items, however, works as +normal. +

    +

    +An alternative description is that a subpattern of this type matches the string +of characters that an identical standalone pattern would match, if anchored at +the current point in the subject string. +

    +

    +Once-only subpatterns are not capturing subpatterns. Simple cases such as the +above example can be thought of as a maximizing repeat that must swallow +everything it can. So, while both \d+ and \d+? are prepared to adjust the +number of digits they match in order to make the rest of the pattern match, +(?>\d+) can only match an entire sequence of digits. +

    +

    +This construction can of course contain arbitrarily complicated subpatterns, +and it can be nested. +

    +

    +Once-only subpatterns can be used in conjunction with lookbehind assertions to +specify efficient matching at the end of the subject string. Consider a simple +pattern such as +

    +

    +

    +  abcd$
    +
    +

    +

    +when applied to a long string which does not match. Because matching proceeds +from left to right, PCRE will look for each "a" in the subject and then see if +what follows matches the rest of the pattern. If the pattern is specified as +

    +

    +

    +  ^.*abcd$
    +
    +

    +

    +then the initial .* matches the entire string at first, but when this fails +(because there is no following "a"), it backtracks to match all but the last +character, then all but the last two characters, and so on. Once again the +search for "a" covers the entire string, from right to left, so we are no +better off. However, if the pattern is written as +

    +

    +

    +  ^(?>.*)(?<=abcd)
    +
    +

    +

    +then there can be no backtracking for the .* item; it can match only the entire +string. The subsequent lookbehind assertion does a single test on the last four +characters. If it fails, the match fails immediately. For long strings, this +approach makes a significant difference to the processing time. +

    +

    +When a pattern contains an unlimited repeat inside a subpattern that can itself +be repeated an unlimited number of times, the use of a once-only subpattern is +the only way to avoid some failing matches taking a very long time indeed. +The pattern +

    +

    +

    +  (\D+|<\d+>)*[!?]
    +
    +

    +

    +matches an unlimited number of substrings that either consist of non-digits, or +digits enclosed in <>, followed by either ! or ?. When it matches, it runs +quickly. However, if it is applied to +

    +

    +

    +  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    +
    +

    +

    +it takes a long time before reporting failure. This is because the string can +be divided between the two repeats in a large number of ways, and all have to +be tried. (The example used [!?] rather than a single character at the end, +because both PCRE and Perl have an optimization that allows for fast failure +when a single character is used. They remember the last single character that +is required for a match, and fail early if it is not present in the string.) +If the pattern is changed to +

    +

    +

    +  ((?>\D+)|<\d+>)*[!?]
    +
    +

    +

    +sequences of non-digits cannot be broken, and failure happens quickly. +

    +
  • CONDITIONAL SUBPATTERNS +

    +It is possible to cause the matching process to obey a subpattern +conditionally or to choose between two alternative subpatterns, depending on +the result of an assertion, or whether a previous capturing subpattern matched +or not. The two possible forms of conditional subpattern are +

    +

    +

    +  (?(condition)yes-pattern)
    +  (?(condition)yes-pattern|no-pattern)
    +
    +

    +

    +If the condition is satisfied, the yes-pattern is used; otherwise the +no-pattern (if present) is used. If there are more than two alternatives in the +subpattern, a compile-time error occurs. +

    +

    +There are two kinds of condition. If the text between the parentheses consists +of a sequence of digits, then the condition is satisfied if the capturing +subpattern of that number has previously matched. Consider the following +pattern, which contains non-significant white space to make it more readable +(assume the PCRE_EXTENDED option) and to divide it into three parts for ease +of discussion: +

    +

    +

    +  ( \( )?    [^()]+    (?(1) \) )
    +
    +

    +

    +The first part matches an optional opening parenthesis, and if that +character is present, sets it as the first captured substring. The second part +matches one or more characters that are not parentheses. The third part is a +conditional subpattern that tests whether the first set of parentheses matched +or not. If they did, that is, if subject started with an opening parenthesis, +the condition is true, and so the yes-pattern is executed and a closing +parenthesis is required. Otherwise, since no-pattern is not present, the +subpattern matches nothing. In other words, this pattern matches a sequence of +non-parentheses, optionally enclosed in parentheses. +

    +

    +If the condition is not a sequence of digits, it must be an assertion. This may +be a positive or negative lookahead or lookbehind assertion. Consider this +pattern, again containing non-significant white space, and with the two +alternatives on the second line: +

    +

    +

    +  (?(?=[^a-z]*[a-z])
    +  \d{2}-[a-z]{3}-\d{2}  |  \d{2}-\d{2}-\d{2} )
    +
    +

    +

    +The condition is a positive lookahead assertion that matches an optional +sequence of non-letters followed by a letter. In other words, it tests for the +presence of at least one letter in the subject. If a letter is found, the +subject is matched against the first alternative; otherwise it is matched +against the second. This pattern matches strings in one of the two forms +dd-aaa-dd or dd-dd-dd, where aaa are letters and dd are digits. +

    +
  • COMMENTS +

    +The sequence (?# marks the start of a comment which continues up to the next +closing parenthesis. Nested parentheses are not permitted. The characters +that make up a comment play no part in the pattern matching at all. +

    +

    +If the PCRE_EXTENDED option is set, an unescaped # character outside a +character class introduces a comment that continues up to the next newline +character in the pattern. +

    +
  • RECURSIVE PATTERNS +

    +Consider the problem of matching a string in parentheses, allowing for +unlimited nested parentheses. Without the use of recursion, the best that can +be done is to use a pattern that matches up to some fixed depth of nesting. It +is not possible to handle an arbitrary nesting depth. Perl 5.6 has provided an +experimental facility that allows regular expressions to recurse (amongst other +things). It does this by interpolating Perl code in the expression at run time, +and the code can refer to the expression itself. A Perl pattern to solve the +parentheses problem can be created like this: +

    +

    +

    +  $re = qr{\( (?: (?>[^()]+) | (?p{$re}) )* \)}x;
    +
    +

    +

    +The (?p{...}) item interpolates Perl code at run time, and in this case refers +recursively to the pattern in which it appears. Obviously, PCRE cannot support +the interpolation of Perl code. Instead, the special item (?R) is provided for +the specific case of recursion. This PCRE pattern solves the parentheses +problem (assume the PCRE_EXTENDED option is set so that white space is +ignored): +

    +

    +

    +  \( ( (?>[^()]+) | (?R) )* \)
    +
    +

    +

    +First it matches an opening parenthesis. Then it matches any number of +substrings which can either be a sequence of non-parentheses, or a recursive +match of the pattern itself (i.e. a correctly parenthesized substring). Finally +there is a closing parenthesis. +

    +

    +This particular example pattern contains nested unlimited repeats, and so the +use of a once-only subpattern for matching strings of non-parentheses is +important when applying the pattern to strings that do not match. For example, +when it is applied to +

    +

    +

    +  (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()
    +
    +

    +

    +it yields "no match" quickly. However, if a once-only subpattern is not used, +the match runs for a very long time indeed because there are so many different +ways the + and * repeats can carve up the subject, and all have to be tested +before failure can be reported. +

    +

    +The values set for any capturing subpatterns are those from the outermost level +of the recursion at which the subpattern value is set. If the pattern above is +matched against +

    +

    +

    +  (ab(cd)ef)
    +
    +

    +

    +the value for the capturing parentheses is "ef", which is the last value taken +on at the top level. If additional parentheses are added, giving +

    +

    +

    +  \( ( ( (?>[^()]+) | (?R) )* ) \)
    +     ^                        ^
    +     ^                        ^
    +
    +then the string they capture is "ab(cd)ef", the contents of the top level +parentheses. If there are more than 15 capturing parentheses in a pattern, PCRE +has to obtain extra memory to store data during a recursion, which it does by +using pcre_malloc, freeing it via pcre_free afterwards. If no +memory can be obtained, it saves data for the first 15 capturing parentheses +only, as there is no way to give an out-of-memory error from within a +recursion. +

    +
  • PERFORMANCE +

    +Certain items that may appear in patterns are more efficient than others. It is +more efficient to use a character class like [aeiou] than a set of alternatives +such as (a|e|i|o|u). In general, the simplest construction that provides the +required behaviour is usually the most efficient. Jeffrey Friedl's book +contains a lot of discussion about optimizing regular expressions for efficient +performance. +

    +

    +When a pattern begins with .* and the PCRE_DOTALL option is set, the pattern is +implicitly anchored by PCRE, since it can match only at the start of a subject +string. However, if PCRE_DOTALL is not set, PCRE cannot make this optimization, +because the . metacharacter does not then match a newline, and if the subject +string contains newlines, the pattern may match from the character immediately +following one of them instead of from the very start. For example, the pattern +

    +

    +

    +  (.*) second
    +
    +

    +

    +matches the subject "first\nand second" (where \n stands for a newline +character) with the first captured substring being "and". In order to do this, +PCRE has to retry the match starting after every newline in the subject. +

    +

    +If you are using such a pattern with subject strings that do not contain +newlines, the best performance is obtained by setting PCRE_DOTALL, or starting +the pattern with ^.* to indicate explicit anchoring. That saves PCRE from +having to scan along the subject looking for a newline to restart at. +

    +

    +Beware of patterns that contain nested indefinite repeats. These can take a +long time to run when applied to a string that does not match. Consider the +pattern fragment +

    +

    +

    +  (a+)*
    +
    +

    +

    +This can match "aaaa" in 33 different ways, and this number increases very +rapidly as the string gets longer. (The * repeat can match 0, 1, 2, 3, or 4 +times, and for each of those cases other than 0, the + repeats can match +different numbers of times.) When the remainder of the pattern is such that the +entire match is going to fail, PCRE has in principle to try every possible +variation, and this can take an extremely long time. +

    +

    +An optimization catches some of the more simple cases such as +

    +

    +

    +  (a+)*b
    +
    +

    +

    +where a literal character follows. Before embarking on the standard matching +procedure, PCRE checks that there is a "b" later in the subject string, and if +there is not, it fails the match immediately. However, when there is no +following literal this optimization cannot be used. You can see the difference +by comparing the behaviour of +

    +

    +

    +  (a+)*\d
    +
    +

    +

    +with the pattern above. The former gives a failure almost instantly when +applied to a whole line of "a" characters, whereas the latter takes an +appreciable time with strings longer than about 20 characters. +

    +
  • AUTHOR +

    +Philip Hazel <ph10@cam.ac.uk> +
    +University Computing Service, +
    +New Museums Site, +
    +Cambridge CB2 3QG, England. +
    +Phone: +44 1223 334714 +

    +

    +Last updated: 27 January 2000 +
    +Copyright (c) 1997-2000 University of Cambridge. diff --git a/srclib/pcre/doc/pcre.txt b/srclib/pcre/doc/pcre.txt new file mode 100644 index 00000000000..f28ee99e8bb --- /dev/null +++ b/srclib/pcre/doc/pcre.txt @@ -0,0 +1,1978 @@ +NAME + pcre - Perl-compatible regular expressions. + + + +SYNOPSIS + #include + + pcre *pcre_compile(const char *pattern, int options, + const char **errptr, int *erroffset, + const unsigned char *tableptr); + + pcre_extra *pcre_study(const pcre *code, int options, + const char **errptr); + + int pcre_exec(const pcre *code, const pcre_extra *extra, + const char *subject, int length, int startoffset, + int options, int *ovector, int ovecsize); + + int pcre_copy_substring(const char *subject, int *ovector, + int stringcount, int stringnumber, char *buffer, + int buffersize); + + int pcre_get_substring(const char *subject, int *ovector, + int stringcount, int stringnumber, + const char **stringptr); + + int pcre_get_substring_list(const char *subject, + int *ovector, int stringcount, const char ***listptr); + + const unsigned char *pcre_maketables(void); + + int pcre_fullinfo(const pcre *code, const pcre_extra *extra, + int what, void *where); + + int pcre_info(const pcre *code, int *optptr, *firstcharptr); + + char *pcre_version(void); + + void *(*pcre_malloc)(size_t); + + void (*pcre_free)(void *); + + + + +DESCRIPTION + The PCRE library is a set of functions that implement regu- + lar expression pattern matching using the same syntax and + semantics as Perl 5, with just a few differences (see + below). The current implementation corresponds to Perl + 5.005, with some additional features from the Perl develop- + ment release. + + PCRE has its own native API, which is described in this + document. There is also a set of wrapper functions that + correspond to the POSIX regular expression API. These are + described in the pcreposix documentation. + + The native API function prototypes are defined in the header + file pcre.h, and on Unix systems the library itself is + called libpcre.a, so can be accessed by adding -lpcre to the + command for linking an application which calls it. The + header file defines the macros PCRE_MAJOR and PCRE_MINOR to + contain the major and minor release numbers for the library. + Applications can use these to include support for different + releases. + + The functions pcre_compile(), pcre_study(), and pcre_exec() + are used for compiling and matching regular expressions, + while pcre_copy_substring(), pcre_get_substring(), and + pcre_get_substring_list() are convenience functions for + extracting captured substrings from a matched subject + string. The function pcre_maketables() is used (optionally) + to build a set of character tables in the current locale for + passing to pcre_compile(). + + The function pcre_fullinfo() is used to find out information + about a compiled pattern; pcre_info() is an obsolete version + which returns only some of the available information, but is + retained for backwards compatibility. The function + pcre_version() returns a pointer to a string containing the + version of PCRE and its date of release. + + The global variables pcre_malloc and pcre_free initially + contain the entry points of the standard malloc() and free() + functions respectively. PCRE calls the memory management + functions via these variables, so a calling program can + replace them if it wishes to intercept the calls. This + should be done before calling any PCRE functions. + + + +MULTI-THREADING + The PCRE functions can be used in multi-threading applica- + tions, with the proviso that the memory management functions + pointed to by pcre_malloc and pcre_free are shared by all + threads. + + The compiled form of a regular expression is not altered + during matching, so the same compiled pattern can safely be + used by several threads at once. + + + + +COMPILING A PATTERN + The function pcre_compile() is called to compile a pattern + into an internal form. The pattern is a C string terminated + by a binary zero, and is passed in the argument pattern. A + pointer to a single block of memory that is obtained via + pcre_malloc is returned. This contains the compiled code and + related data. The pcre type is defined for this for conveni- + ence, but in fact pcre is just a typedef for void, since the + contents of the block are not externally defined. It is up + to the caller to free the memory when it is no longer + required. + + The size of a compiled pattern is roughly proportional to + the length of the pattern string, except that each character + class (other than those containing just a single character, + negated or not) requires 33 bytes, and repeat quantifiers + with a minimum greater than one or a bounded maximum cause + the relevant portions of the compiled pattern to be repli- + cated. + + The options argument contains independent bits that affect + the compilation. It should be zero if no options are + required. Some of the options, in particular, those that are + compatible with Perl, can also be set and unset from within + the pattern (see the detailed description of regular expres- + sions below). For these options, the contents of the options + argument specifies their initial settings at the start of + compilation and execution. The PCRE_ANCHORED option can be + set at the time of matching as well as at compile time. + + If errptr is NULL, pcre_compile() returns NULL immediately. + Otherwise, if compilation of a pattern fails, pcre_compile() + returns NULL, and sets the variable pointed to by errptr to + point to a textual error message. The offset from the start + of the pattern to the character where the error was + discovered is placed in the variable pointed to by + erroffset, which must not be NULL. If it is, an immediate + error is given. + + If the final argument, tableptr, is NULL, PCRE uses a + default set of character tables which are built when it is + compiled, using the default C locale. Otherwise, tableptr + must be the result of a call to pcre_maketables(). See the + section on locale support below. + + The following option bits are defined in the header file: + + PCRE_ANCHORED + + If this bit is set, the pattern is forced to be "anchored", + that is, it is constrained to match only at the start of the + string which is being searched (the "subject string"). This + effect can also be achieved by appropriate constructs in the + pattern itself, which is the only way to do it in Perl. + + PCRE_CASELESS + + If this bit is set, letters in the pattern match both upper + and lower case letters. It is equivalent to Perl's /i + option. + + PCRE_DOLLAR_ENDONLY + + If this bit is set, a dollar metacharacter in the pattern + matches only at the end of the subject string. Without this + option, a dollar also matches immediately before the final + character if it is a newline (but not before any other new- + lines). The PCRE_DOLLAR_ENDONLY option is ignored if + PCRE_MULTILINE is set. There is no equivalent to this option + in Perl. + + PCRE_DOTALL + + If this bit is set, a dot metacharater in the pattern + matches all characters, including newlines. Without it, new- + lines are excluded. This option is equivalent to Perl's /s + option. A negative class such as [^a] always matches a new- + line character, independent of the setting of this option. + + PCRE_EXTENDED + + If this bit is set, whitespace data characters in the pat- + tern are totally ignored except when escaped or inside a + character class, and characters between an unescaped # out- + side a character class and the next newline character, + inclusive, are also ignored. This is equivalent to Perl's /x + option, and makes it possible to include comments inside + complicated patterns. Note, however, that this applies only + to data characters. Whitespace characters may never appear + within special character sequences in a pattern, for example + within the sequence (?( which introduces a conditional sub- + pattern. + + PCRE_EXTRA + + This option was invented in order to turn on additional + functionality of PCRE that is incompatible with Perl, but it + is currently of very little use. When set, any backslash in + a pattern that is followed by a letter that has no special + meaning causes an error, thus reserving these combinations + for future expansion. By default, as in Perl, a backslash + followed by a letter with no special meaning is treated as a + literal. There are at present no other features controlled + by this option. It can also be set by a (?X) option setting + within a pattern. + + PCRE_MULTILINE + + By default, PCRE treats the subject string as consisting of + a single "line" of characters (even if it actually contains + several newlines). The "start of line" metacharacter (^) + matches only at the start of the string, while the "end of + line" metacharacter ($) matches only at the end of the + string, or before a terminating newline (unless + PCRE_DOLLAR_ENDONLY is set). This is the same as Perl. + + When PCRE_MULTILINE it is set, the "start of line" and "end + of line" constructs match immediately following or immedi- + ately before any newline in the subject string, respec- + tively, as well as at the very start and end. This is + equivalent to Perl's /m option. If there are no "\n" charac- + ters in a subject string, or no occurrences of ^ or $ in a + pattern, setting PCRE_MULTILINE has no effect. + + PCRE_UNGREEDY + + This option inverts the "greediness" of the quantifiers so + that they are not greedy by default, but become greedy if + followed by "?". It is not compatible with Perl. It can also + be set by a (?U) option setting within the pattern. + + + +STUDYING A PATTERN + When a pattern is going to be used several times, it is + worth spending more time analyzing it in order to speed up + the time taken for matching. The function pcre_study() takes + a pointer to a compiled pattern as its first argument, and + returns a pointer to a pcre_extra block (another void + typedef) containing additional information about the pat- + tern; this can be passed to pcre_exec(). If no additional + information is available, NULL is returned. + + The second argument contains option bits. At present, no + options are defined for pcre_study(), and this argument + should always be zero. + + The third argument for pcre_study() is a pointer to an error + message. If studying succeeds (even if no data is returned), + the variable it points to is set to NULL. Otherwise it + points to a textual error message. + + At present, studying a pattern is useful only for non- + anchored patterns that do not have a single fixed starting + character. A bitmap of possible starting characters is + created. + + + +LOCALE SUPPORT + PCRE handles caseless matching, and determines whether char- + acters are letters, digits, or whatever, by reference to a + set of tables. The library contains a default set of tables + which is created in the default C locale when PCRE is com- + piled. This is used when the final argument of + pcre_compile() is NULL, and is sufficient for many applica- + tions. + + An alternative set of tables can, however, be supplied. Such + tables are built by calling the pcre_maketables() function, + which has no arguments, in the relevant locale. The result + can then be passed to pcre_compile() as often as necessary. + For example, to build and use tables that are appropriate + for the French locale (where accented characters with codes + greater than 128 are treated as letters), the following code + could be used: + + setlocale(LC_CTYPE, "fr"); + tables = pcre_maketables(); + re = pcre_compile(..., tables); + + The tables are built in memory that is obtained via + pcre_malloc. The pointer that is passed to pcre_compile is + saved with the compiled pattern, and the same tables are + used via this pointer by pcre_study() and pcre_exec(). Thus + for any single pattern, compilation, studying and matching + all happen in the same locale, but different patterns can be + compiled in different locales. It is the caller's responsi- + bility to ensure that the memory containing the tables + remains available for as long as it is needed. + + + +INFORMATION ABOUT A PATTERN + The pcre_fullinfo() function returns information about a + compiled pattern. It replaces the obsolete pcre_info() func- + tion, which is nevertheless retained for backwards compabil- + ity (and is documented below). + + The first argument for pcre_fullinfo() is a pointer to the + compiled pattern. The second argument is the result of + pcre_study(), or NULL if the pattern was not studied. The + third argument specifies which piece of information is + required, while the fourth argument is a pointer to a vari- + able to receive the data. The yield of the function is zero + for success, or one of the following negative numbers: + + PCRE_ERROR_NULL the argument code was NULL + the argument where was NULL + PCRE_ERROR_BADMAGIC the "magic number" was not found + PCRE_ERROR_BADOPTION the value of what was invalid + + The possible values for the third argument are defined in + pcre.h, and are as follows: + + PCRE_INFO_OPTIONS + + Return a copy of the options with which the pattern was com- + piled. The fourth argument should point to au unsigned long + int variable. These option bits are those specified in the + call to pcre_compile(), modified by any top-level option + settings within the pattern itself, and with the + PCRE_ANCHORED bit forcibly set if the form of the pattern + implies that it can match only at the start of a subject + string. + + PCRE_INFO_SIZE + + Return the size of the compiled pattern, that is, the value + that was passed as the argument to pcre_malloc() when PCRE + was getting memory in which to place the compiled data. The + fourth argument should point to a size_t variable. + + PCRE_INFO_CAPTURECOUNT + + Return the number of capturing subpatterns in the pattern. + The fourth argument should point to an int variable. + + PCRE_INFO_BACKREFMAX + + Return the number of the highest back reference in the pat- + tern. The fourth argument should point to an int variable. + Zero is returned if there are no back references. + + PCRE_INFO_FIRSTCHAR + + Return information about the first character of any matched + string, for a non-anchored pattern. If there is a fixed + first character, e.g. from a pattern such as + (cat|cow|coyote), then it is returned in the integer pointed + to by where. Otherwise, if either + + (a) the pattern was compiled with the PCRE_MULTILINE option, + and every branch starts with "^", or + + (b) every branch of the pattern starts with ".*" and + PCRE_DOTALL is not set (if it were set, the pattern would be + anchored), + + then -1 is returned, indicating that the pattern matches + only at the start of a subject string or after any "\n" + within the string. Otherwise -2 is returned. For anchored + patterns, -2 is returned. + + PCRE_INFO_FIRSTTABLE + + If the pattern was studied, and this resulted in the con- + struction of a 256-bit table indicating a fixed set of char- + acters for the first character in any matching string, a + pointer to the table is returned. Otherwise NULL is + returned. The fourth argument should point to an unsigned + char * variable. + + PCRE_INFO_LASTLITERAL + + For a non-anchored pattern, return the value of the right- + most literal character which must exist in any matched + string, other than at its start. The fourth argument should + point to an int variable. If there is no such character, or + if the pattern is anchored, -1 is returned. For example, for + the pattern /a\d+z\d+/ the returned value is 'z'. + + The pcre_info() function is now obsolete because its inter- + face is too restrictive to return all the available data + about a compiled pattern. New programs should use + pcre_fullinfo() instead. The yield of pcre_info() is the + number of capturing subpatterns, or one of the following + negative numbers: + + PCRE_ERROR_NULL the argument code was NULL + PCRE_ERROR_BADMAGIC the "magic number" was not found + + If the optptr argument is not NULL, a copy of the options + with which the pattern was compiled is placed in the integer + it points to (see PCRE_INFO_OPTIONS above). + + If the pattern is not anchored and the firstcharptr argument + is not NULL, it is used to pass back information about the + first character of any matched string (see + PCRE_INFO_FIRSTCHAR above). + + + +MATCHING A PATTERN + The function pcre_exec() is called to match a subject string + against a pre-compiled pattern, which is passed in the code + argument. If the pattern has been studied, the result of the + study should be passed in the extra argument. Otherwise this + must be NULL. + + The PCRE_ANCHORED option can be passed in the options argu- + ment, whose unused bits must be zero. However, if a pattern + was compiled with PCRE_ANCHORED, or turned out to be + anchored by virtue of its contents, it cannot be made + unachored at matching time. + + There are also three further options that can be set only at + matching time: + + PCRE_NOTBOL + + The first character of the string is not the beginning of a + line, so the circumflex metacharacter should not match + before it. Setting this without PCRE_MULTILINE (at compile + time) causes circumflex never to match. + + PCRE_NOTEOL + + The end of the string is not the end of a line, so the dol- + lar metacharacter should not match it nor (except in multi- + line mode) a newline immediately before it. Setting this + without PCRE_MULTILINE (at compile time) causes dollar never + to match. + + PCRE_NOTEMPTY + + An empty string is not considered to be a valid match if + this option is set. If there are alternatives in the pat- + tern, they are tried. If all the alternatives match the + empty string, the entire match fails. For example, if the + pattern + + a?b? + + is applied to a string not beginning with "a" or "b", it + matches the empty string at the start of the subject. With + PCRE_NOTEMPTY set, this match is not valid, so PCRE searches + further into the string for occurrences of "a" or "b". + + Perl has no direct equivalent of PCRE_NOTEMPTY, but it does + make a special case of a pattern match of the empty string + within its split() function, and when using the /g modifier. + It is possible to emulate Perl's behaviour after matching a + null string by first trying the match again at the same + offset with PCRE_NOTEMPTY set, and then if that fails by + advancing the starting offset (see below) and trying an + ordinary match again. + + The subject string is passed as a pointer in subject, a + length in length, and a starting offset in startoffset. + Unlike the pattern string, it may contain binary zero char- + acters. When the starting offset is zero, the search for a + match starts at the beginning of the subject, and this is by + far the most common case. + + A non-zero starting offset is useful when searching for + another match in the same subject by calling pcre_exec() + again after a previous success. Setting startoffset differs + from just passing over a shortened string and setting + PCRE_NOTBOL in the case of a pattern that begins with any + kind of lookbehind. For example, consider the pattern + + \Biss\B + + which finds occurrences of "iss" in the middle of words. (\B + matches only if the current position in the subject is not a + word boundary.) When applied to the string "Mississipi" the + first call to pcre_exec() finds the first occurrence. If + pcre_exec() is called again with just the remainder of the + subject, namely "issipi", it does not match, because \B is + always false at the start of the subject, which is deemed to + be a word boundary. However, if pcre_exec() is passed the + entire string again, but with startoffset set to 4, it finds + the second occurrence of "iss" because it is able to look + behind the starting point to discover that it is preceded by + a letter. + + If a non-zero starting offset is passed when the pattern is + anchored, one attempt to match at the given offset is tried. + This can only succeed if the pattern does not require the + match to be at the start of the subject. + + In general, a pattern matches a certain portion of the sub- + ject, and in addition, further substrings from the subject + may be picked out by parts of the pattern. Following the + usage in Jeffrey Friedl's book, this is called "capturing" + in what follows, and the phrase "capturing subpattern" is + used for a fragment of a pattern that picks out a substring. + PCRE supports several other kinds of parenthesized subpat- + tern that do not cause substrings to be captured. + + Captured substrings are returned to the caller via a vector + of integer offsets whose address is passed in ovector. The + number of elements in the vector is passed in ovecsize. The + first two-thirds of the vector is used to pass back captured + substrings, each substring using a pair of integers. The + remaining third of the vector is used as workspace by + pcre_exec() while matching capturing subpatterns, and is not + available for passing back information. The length passed in + ovecsize should always be a multiple of three. If it is not, + it is rounded down. + + When a match has been successful, information about captured + substrings is returned in pairs of integers, starting at the + beginning of ovector, and continuing up to two-thirds of its + length at the most. The first element of a pair is set to + the offset of the first character in a substring, and the + second is set to the offset of the first character after the + end of a substring. The first pair, ovector[0] and ovec- + tor[1], identify the portion of the subject string matched + by the entire pattern. The next pair is used for the first + capturing subpattern, and so on. The value returned by + pcre_exec() is the number of pairs that have been set. If + there are no capturing subpatterns, the return value from a + successful match is 1, indicating that just the first pair + of offsets has been set. + + Some convenience functions are provided for extracting the + captured substrings as separate strings. These are described + in the following section. + + It is possible for an capturing subpattern number n+1 to + match some part of the subject when subpattern n has not + been used at all. For example, if the string "abc" is + matched against the pattern (a|(z))(bc) subpatterns 1 and 3 + are matched, but 2 is not. When this happens, both offset + values corresponding to the unused subpattern are set to -1. + + If a capturing subpattern is matched repeatedly, it is the + last portion of the string that it matched that gets + returned. + + If the vector is too small to hold all the captured sub- + strings, it is used as far as possible (up to two-thirds of + its length), and the function returns a value of zero. In + particular, if the substring offsets are not of interest, + pcre_exec() may be called with ovector passed as NULL and + ovecsize as zero. However, if the pattern contains back + references and the ovector isn't big enough to remember the + related substrings, PCRE has to get additional memory for + use during matching. Thus it is usually advisable to supply + an ovector. + + Note that pcre_info() can be used to find out how many cap- + turing subpatterns there are in a compiled pattern. The + smallest size for ovector that will allow for n captured + substrings in addition to the offsets of the substring + matched by the whole pattern is (n+1)*3. + + If pcre_exec() fails, it returns a negative number. The fol- + lowing are defined in the header file: + + PCRE_ERROR_NOMATCH (-1) + + The subject string did not match the pattern. + + PCRE_ERROR_NULL (-2) + + Either code or subject was passed as NULL, or ovector was + NULL and ovecsize was not zero. + + PCRE_ERROR_BADOPTION (-3) + + An unrecognized bit was set in the options argument. + + PCRE_ERROR_BADMAGIC (-4) + + PCRE stores a 4-byte "magic number" at the start of the com- + piled code, to catch the case when it is passed a junk + pointer. This is the error it gives when the magic number + isn't present. + + PCRE_ERROR_UNKNOWN_NODE (-5) + + While running the pattern match, an unknown item was encoun- + tered in the compiled pattern. This error could be caused by + a bug in PCRE or by overwriting of the compiled pattern. + + PCRE_ERROR_NOMEMORY (-6) + + If a pattern contains back references, but the ovector that + is passed to pcre_exec() is not big enough to remember the + referenced substrings, PCRE gets a block of memory at the + start of matching to use for this purpose. If the call via + pcre_malloc() fails, this error is given. The memory is + freed at the end of matching. + + + +EXTRACTING CAPTURED SUBSTRINGS + Captured substrings can be accessed directly by using the + offsets returned by pcre_exec() in ovector. For convenience, + the functions pcre_copy_substring(), pcre_get_substring(), + and pcre_get_substring_list() are provided for extracting + captured substrings as new, separate, zero-terminated + strings. A substring that contains a binary zero is + correctly extracted and has a further zero added on the end, + but the result does not, of course, function as a C string. + + The first three arguments are the same for all three func- + tions: subject is the subject string which has just been + successfully matched, ovector is a pointer to the vector of + integer offsets that was passed to pcre_exec(), and + stringcount is the number of substrings that were captured + by the match, including the substring that matched the + entire regular expression. This is the value returned by + pcre_exec if it is greater than zero. If pcre_exec() + returned zero, indicating that it ran out of space in ovec- + tor, then the value passed as stringcount should be the size + of the vector divided by three. + + The functions pcre_copy_substring() and pcre_get_substring() + extract a single substring, whose number is given as string- + number. A value of zero extracts the substring that matched + the entire pattern, while higher values extract the captured + substrings. For pcre_copy_substring(), the string is placed + in buffer, whose length is given by buffersize, while for + pcre_get_substring() a new block of store is obtained via + pcre_malloc, and its address is returned via stringptr. The + yield of the function is the length of the string, not + including the terminating zero, or one of + + PCRE_ERROR_NOMEMORY (-6) + + The buffer was too small for pcre_copy_substring(), or the + attempt to get memory failed for pcre_get_substring(). + + PCRE_ERROR_NOSUBSTRING (-7) + + There is no substring whose number is stringnumber. + + The pcre_get_substring_list() function extracts all avail- + able substrings and builds a list of pointers to them. All + this is done in a single block of memory which is obtained + via pcre_malloc. The address of the memory block is returned + via listptr, which is also the start of the list of string + pointers. The end of the list is marked by a NULL pointer. + The yield of the function is zero if all went well, or + + PCRE_ERROR_NOMEMORY (-6) + + if the attempt to get the memory block failed. + + When any of these functions encounter a substring that is + unset, which can happen when capturing subpattern number n+1 + matches some part of the subject, but subpattern n has not + been used at all, they return an empty string. This can be + distinguished from a genuine zero-length substring by + inspecting the appropriate offset in ovector, which is nega- + tive for unset substrings. + + + + +LIMITATIONS + There are some size limitations in PCRE but it is hoped that + they will never in practice be relevant. The maximum length + of a compiled pattern is 65539 (sic) bytes. All values in + repeating quantifiers must be less than 65536. The maximum + number of capturing subpatterns is 99. The maximum number + of all parenthesized subpatterns, including capturing sub- + patterns, assertions, and other types of subpattern, is 200. + + The maximum length of a subject string is the largest posi- + tive number that an integer variable can hold. However, PCRE + uses recursion to handle subpatterns and indefinite repeti- + tion. This means that the available stack space may limit + the size of a subject string that can be processed by cer- + tain patterns. + + + +DIFFERENCES FROM PERL + The differences described here are with respect to Perl + 5.005. + + 1. By default, a whitespace character is any character that + the C library function isspace() recognizes, though it is + possible to compile PCRE with alternative character type + tables. Normally isspace() matches space, formfeed, newline, + carriage return, horizontal tab, and vertical tab. Perl 5 no + longer includes vertical tab in its set of whitespace char- + acters. The \v escape that was in the Perl documentation for + a long time was never in fact recognized. However, the char- + acter itself was treated as whitespace at least up to 5.002. + In 5.004 and 5.005 it does not match \s. + + 2. PCRE does not allow repeat quantifiers on lookahead + assertions. Perl permits them, but they do not mean what you + might think. For example, (?!a){3} does not assert that the + next three characters are not "a". It just asserts that the + next character is not "a" three times. + + 3. Capturing subpatterns that occur inside negative looka- + head assertions are counted, but their entries in the + offsets vector are never set. Perl sets its numerical vari- + ables from any such patterns that are matched before the + assertion fails to match something (thereby succeeding), but + only if the negative lookahead assertion contains just one + branch. + + 4. Though binary zero characters are supported in the sub- + ject string, they are not allowed in a pattern string + because it is passed as a normal C string, terminated by + zero. The escape sequence "\0" can be used in the pattern to + represent a binary zero. + + 5. The following Perl escape sequences are not supported: + \l, \u, \L, \U, \E, \Q. In fact these are implemented by + Perl's general string-handling and are not part of its pat- + tern matching engine. + + 6. The Perl \G assertion is not supported as it is not + relevant to single pattern matches. + + 7. Fairly obviously, PCRE does not support the (?{code}) and + (?p{code}) constructions. However, there is some experimen- + tal support for recursive patterns using the non-Perl item + (?R). + 8. There are at the time of writing some oddities in Perl + 5.005_02 concerned with the settings of captured strings + when part of a pattern is repeated. For example, matching + "aba" against the pattern /^(a(b)?)+$/ sets $2 to the value + "b", but matching "aabbaa" against /^(aa(bb)?)+$/ leaves $2 + unset. However, if the pattern is changed to + /^(aa(b(b))?)+$/ then $2 (and $3) get set. + + In Perl 5.004 $2 is set in both cases, and that is also true + of PCRE. If in the future Perl changes to a consistent state + that is different, PCRE may change to follow. + + 9. Another as yet unresolved discrepancy is that in Perl + 5.005_02 the pattern /^(a)?(?(1)a|b)+$/ matches the string + "a", whereas in PCRE it does not. However, in both Perl and + PCRE /^(a)?a/ matched against "a" leaves $1 unset. + + 10. PCRE provides some extensions to the Perl regular + expression facilities: + + (a) Although lookbehind assertions must match fixed length + strings, each alternative branch of a lookbehind assertion + can match a different length of string. Perl 5.005 requires + them all to have the same length. + + (b) If PCRE_DOLLAR_ENDONLY is set and PCRE_MULTILINE is not + set, the $ meta- character matches only at the very end of + the string. + + (c) If PCRE_EXTRA is set, a backslash followed by a letter + with no special meaning is faulted. + + (d) If PCRE_UNGREEDY is set, the greediness of the repeti- + tion quantifiers is inverted, that is, by default they are + not greedy, but if followed by a question mark they are. + + (e) PCRE_ANCHORED can be used to force a pattern to be tried + only at the start of the subject. + + (f) The PCRE_NOTBOL, PCRE_NOTEOL, and PCRE_NOTEMPTY options + for pcre_exec() have no Perl equivalents. + + (g) The (?R) construct allows for recursive pattern matching + (Perl 5.6 can do this using the (?p{code}) construct, which + PCRE cannot of course support.) + + + +REGULAR EXPRESSION DETAILS + The syntax and semantics of the regular expressions sup- + ported by PCRE are described below. Regular expressions are + also described in the Perl documentation and in a number of + + other books, some of which have copious examples. Jeffrey + Friedl's "Mastering Regular Expressions", published by + O'Reilly (ISBN 1-56592-257), covers them in great detail. + The description here is intended as reference documentation. + + A regular expression is a pattern that is matched against a + subject string from left to right. Most characters stand for + themselves in a pattern, and match the corresponding charac- + ters in the subject. As a trivial example, the pattern + + The quick brown fox + + matches a portion of a subject string that is identical to + itself. The power of regular expressions comes from the + ability to include alternatives and repetitions in the pat- + tern. These are encoded in the pattern by the use of meta- + characters, which do not stand for themselves but instead + are interpreted in some special way. + + There are two different sets of meta-characters: those that + are recognized anywhere in the pattern except within square + brackets, and those that are recognized in square brackets. + Outside square brackets, the meta-characters are as follows: + + \ general escape character with several uses + ^ assert start of subject (or line, in multiline + mode) + $ assert end of subject (or line, in multiline mode) + . match any character except newline (by default) + [ start character class definition + | start of alternative branch + ( start subpattern + ) end subpattern + ? extends the meaning of ( + also 0 or 1 quantifier + also quantifier minimizer + * 0 or more quantifier + + 1 or more quantifier + { start min/max quantifier + + Part of a pattern that is in square brackets is called a + "character class". In a character class the only meta- + characters are: + + \ general escape character + ^ negate the class, but only if the first character + - indicates character range + ] terminates the character class + + The following sections describe the use of each of the + meta-characters. + + + +BACKSLASH + The backslash character has several uses. Firstly, if it is + followed by a non-alphameric character, it takes away any + special meaning that character may have. This use of + backslash as an escape character applies both inside and + outside character classes. + + For example, if you want to match a "*" character, you write + "\*" in the pattern. This applies whether or not the follow- + ing character would otherwise be interpreted as a meta- + character, so it is always safe to precede a non-alphameric + with "\" to specify that it stands for itself. In particu- + lar, if you want to match a backslash, you write "\\". + + If a pattern is compiled with the PCRE_EXTENDED option, whi- + tespace in the pattern (other than in a character class) and + characters between a "#" outside a character class and the + next newline character are ignored. An escaping backslash + can be used to include a whitespace or "#" character as part + of the pattern. + + A second use of backslash provides a way of encoding non- + printing characters in patterns in a visible manner. There + is no restriction on the appearance of non-printing charac- + ters, apart from the binary zero that terminates a pattern, + but when a pattern is being prepared by text editing, it is + usually easier to use one of the following escape sequences + than the binary character it represents: + + \a alarm, that is, the BEL character (hex 07) + \cx "control-x", where x is any character + \e escape (hex 1B) + \f formfeed (hex 0C) + \n newline (hex 0A) + \r carriage return (hex 0D) + \t tab (hex 09) + \xhh character with hex code hh + \ddd character with octal code ddd, or backreference + + The precise effect of "\cx" is as follows: if "x" is a lower + case letter, it is converted to upper case. Then bit 6 of + the character (hex 40) is inverted. Thus "\cz" becomes hex + 1A, but "\c{" becomes hex 3B, while "\c;" becomes hex 7B. + + After "\x", up to two hexadecimal digits are read (letters + can be in upper or lower case). + + After "\0" up to two further octal digits are read. In both + cases, if there are fewer than two digits, just those that + are present are used. Thus the sequence "\0\x\07" specifies + two binary zeros followed by a BEL character. Make sure you + supply two digits after the initial zero if the character + that follows is itself an octal digit. + + The handling of a backslash followed by a digit other than 0 + is complicated. Outside a character class, PCRE reads it + and any following digits as a decimal number. If the number + is less than 10, or if there have been at least that many + previous capturing left parentheses in the expression, the + entire sequence is taken as a back reference. A description + of how this works is given later, following the discussion + of parenthesized subpatterns. + + Inside a character class, or if the decimal number is + greater than 9 and there have not been that many capturing + subpatterns, PCRE re-reads up to three octal digits follow- + ing the backslash, and generates a single byte from the + least significant 8 bits of the value. Any subsequent digits + stand for themselves. For example: + + \040 is another way of writing a space + \40 is the same, provided there are fewer than 40 + previous capturing subpatterns + \7 is always a back reference + \11 might be a back reference, or another way of + writing a tab + \011 is always a tab + \0113 is a tab followed by the character "3" + \113 is the character with octal code 113 (since there + can be no more than 99 back references) + \377 is a byte consisting entirely of 1 bits + \81 is either a back reference, or a binary zero + followed by the two characters "8" and "1" + + Note that octal values of 100 or greater must not be intro- + duced by a leading zero, because no more than three octal + digits are ever read. + + All the sequences that define a single byte value can be + used both inside and outside character classes. In addition, + inside a character class, the sequence "\b" is interpreted + as the backspace character (hex 08). Outside a character + class it has a different meaning (see below). + + The third use of backslash is for specifying generic charac- + ter types: + + \d any decimal digit + \D any character that is not a decimal digit + \s any whitespace character + \S any character that is not a whitespace character + \w any "word" character + \W any "non-word" character + + Each pair of escape sequences partitions the complete set of + characters into two disjoint sets. Any given character + matches one, and only one, of each pair. + + A "word" character is any letter or digit or the underscore + character, that is, any character which can be part of a + Perl "word". The definition of letters and digits is con- + trolled by PCRE's character tables, and may vary if locale- + specific matching is taking place (see "Locale support" + above). For example, in the "fr" (French) locale, some char- + acter codes greater than 128 are used for accented letters, + and these are matched by \w. + + These character type sequences can appear both inside and + outside character classes. They each match one character of + the appropriate type. If the current matching point is at + the end of the subject string, all of them fail, since there + is no character to match. + + The fourth use of backslash is for certain simple asser- + tions. An assertion specifies a condition that has to be met + at a particular point in a match, without consuming any + characters from the subject string. The use of subpatterns + for more complicated assertions is described below. The + backslashed assertions are + + \b word boundary + \B not a word boundary + \A start of subject (independent of multiline mode) + \Z end of subject or newline at end (independent of + multiline mode) + \z end of subject (independent of multiline mode) + + These assertions may not appear in character classes (but + note that "\b" has a different meaning, namely the backspace + character, inside a character class). + + A word boundary is a position in the subject string where + the current character and the previous character do not both + match \w or \W (i.e. one matches \w and the other matches + \W), or the start or end of the string if the first or last + character matches \w, respectively. + + The \A, \Z, and \z assertions differ from the traditional + circumflex and dollar (described below) in that they only + ever match at the very start and end of the subject string, + whatever options are set. They are not affected by the + PCRE_NOTBOL or PCRE_NOTEOL options. If the startoffset argu- + ment of pcre_exec() is non-zero, \A can never match. The + difference between \Z and \z is that \Z matches before a + newline that is the last character of the string as well as + at the end of the string, whereas \z matches only at the + end. + + + +CIRCUMFLEX AND DOLLAR + Outside a character class, in the default matching mode, the + circumflex character is an assertion which is true only if + the current matching point is at the start of the subject + string. If the startoffset argument of pcre_exec() is non- + zero, circumflex can never match. Inside a character class, + circumflex has an entirely different meaning (see below). + + Circumflex need not be the first character of the pattern if + a number of alternatives are involved, but it should be the + first thing in each alternative in which it appears if the + pattern is ever to match that branch. If all possible alter- + natives start with a circumflex, that is, if the pattern is + constrained to match only at the start of the subject, it is + said to be an "anchored" pattern. (There are also other con- + structs that can cause a pattern to be anchored.) + + A dollar character is an assertion which is true only if the + current matching point is at the end of the subject string, + or immediately before a newline character that is the last + character in the string (by default). Dollar need not be the + last character of the pattern if a number of alternatives + are involved, but it should be the last item in any branch + in which it appears. Dollar has no special meaning in a + character class. + + The meaning of dollar can be changed so that it matches only + at the very end of the string, by setting the + PCRE_DOLLAR_ENDONLY option at compile or matching time. This + does not affect the \Z assertion. + + The meanings of the circumflex and dollar characters are + changed if the PCRE_MULTILINE option is set. When this is + the case, they match immediately after and immediately + before an internal "\n" character, respectively, in addition + to matching at the start and end of the subject string. For + example, the pattern /^abc$/ matches the subject string + "def\nabc" in multiline mode, but not otherwise. Conse- + quently, patterns that are anchored in single line mode + because all branches start with "^" are not anchored in mul- + tiline mode, and a match for circumflex is possible when the + startoffset argument of pcre_exec() is non-zero. The + PCRE_DOLLAR_ENDONLY option is ignored if PCRE_MULTILINE is + set. + + Note that the sequences \A, \Z, and \z can be used to match + the start and end of the subject in both modes, and if all + branches of a pattern start with \A is it always anchored, + whether PCRE_MULTILINE is set or not. + + + +FULL STOP (PERIOD, DOT) + Outside a character class, a dot in the pattern matches any + one character in the subject, including a non-printing char- + acter, but not (by default) newline. If the PCRE_DOTALL + option is set, then dots match newlines as well. The han- + dling of dot is entirely independent of the handling of cir- + cumflex and dollar, the only relationship being that they + both involve newline characters. Dot has no special meaning + in a character class. + + + +SQUARE BRACKETS + An opening square bracket introduces a character class, ter- + minated by a closing square bracket. A closing square + bracket on its own is not special. If a closing square + bracket is required as a member of the class, it should be + the first data character in the class (after an initial cir- + cumflex, if present) or escaped with a backslash. + + A character class matches a single character in the subject; + the character must be in the set of characters defined by + the class, unless the first character in the class is a cir- + cumflex, in which case the subject character must not be in + the set defined by the class. If a circumflex is actually + required as a member of the class, ensure it is not the + first character, or escape it with a backslash. + + For example, the character class [aeiou] matches any lower + case vowel, while [^aeiou] matches any character that is not + a lower case vowel. Note that a circumflex is just a con- + venient notation for specifying the characters which are in + the class by enumerating those that are not. It is not an + assertion: it still consumes a character from the subject + string, and fails if the current pointer is at the end of + the string. + + When caseless matching is set, any letters in a class + represent both their upper case and lower case versions, so + for example, a caseless [aeiou] matches "A" as well as "a", + and a caseless [^aeiou] does not match "A", whereas a case- + ful version would. + + The newline character is never treated in any special way in + character classes, whatever the setting of the PCRE_DOTALL + or PCRE_MULTILINE options is. A class such as [^a] will + always match a newline. + + The minus (hyphen) character can be used to specify a range + of characters in a character class. For example, [d-m] + matches any letter between d and m, inclusive. If a minus + character is required in a class, it must be escaped with a + backslash or appear in a position where it cannot be inter- + preted as indicating a range, typically as the first or last + character in the class. + + It is not possible to have the literal character "]" as the + end character of a range. A pattern such as [W-]46] is + interpreted as a class of two characters ("W" and "-") fol- + lowed by a literal string "46]", so it would match "W46]" or + "-46]". However, if the "]" is escaped with a backslash it + is interpreted as the end of range, so [W-\]46] is inter- + preted as a single class containing a range followed by two + separate characters. The octal or hexadecimal representation + of "]" can also be used to end a range. + + Ranges operate in ASCII collating sequence. They can also be + used for characters specified numerically, for example + [\000-\037]. If a range that includes letters is used when + caseless matching is set, it matches the letters in either + case. For example, [W-c] is equivalent to [][\^_`wxyzabc], + matched caselessly, and if character tables for the "fr" + locale are in use, [\xc8-\xcb] matches accented E characters + in both cases. + + The character types \d, \D, \s, \S, \w, and \W may also + appear in a character class, and add the characters that + they match to the class. For example, [\dABCDEF] matches any + hexadecimal digit. A circumflex can conveniently be used + with the upper case character types to specify a more res- + tricted set of characters than the matching lower case type. + For example, the class [^\W_] matches any letter or digit, + but not underscore. + + All non-alphameric characters other than \, -, ^ (at the + start) and the terminating ] are non-special in character + classes, but it does no harm if they are escaped. + + + +POSIX CHARACTER CLASSES + Perl 5.6 (not yet released at the time of writing) is going + to support the POSIX notation for character classes, which + uses names enclosed by [: and :] within the enclosing + square brackets. PCRE supports this notation. For example, + + [01[:alpha:]%] + + matches "0", "1", any alphabetic character, or "%". The sup- + ported class names are + + alnum letters and digits + alpha letters + ascii character codes 0 - 127 + cntrl control characters + digit decimal digits (same as \d) + graph printing characters, excluding space + lower lower case letters + print printing characters, including space + punct printing characters, excluding letters and digits + space white space (same as \s) + upper upper case letters + word "word" characters (same as \w) + xdigit hexadecimal digits + + The names "ascii" and "word" are Perl extensions. Another + Perl extension is negation, which is indicated by a ^ char- + acter after the colon. For example, + + [12[:^digit:]] + + matches "1", "2", or any non-digit. PCRE (and Perl) also + recogize the POSIX syntax [.ch.] and [=ch=] where "ch" is a + "collating element", but these are not supported, and an + error is given if they are encountered. + + + +VERTICAL BAR + Vertical bar characters are used to separate alternative + patterns. For example, the pattern + + gilbert|sullivan + + matches either "gilbert" or "sullivan". Any number of alter- + natives may appear, and an empty alternative is permitted + (matching the empty string). The matching process tries + each alternative in turn, from left to right, and the first + one that succeeds is used. If the alternatives are within a + subpattern (defined below), "succeeds" means matching the + rest of the main pattern as well as the alternative in the + subpattern. + + + +INTERNAL OPTION SETTING + The settings of PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL, + and PCRE_EXTENDED can be changed from within the pattern by + a sequence of Perl option letters enclosed between "(?" and + ")". The option letters are + + i for PCRE_CASELESS + m for PCRE_MULTILINE + s for PCRE_DOTALL + x for PCRE_EXTENDED + + For example, (?im) sets caseless, multiline matching. It is + also possible to unset these options by preceding the letter + with a hyphen, and a combined setting and unsetting such as + (?im-sx), which sets PCRE_CASELESS and PCRE_MULTILINE while + unsetting PCRE_DOTALL and PCRE_EXTENDED, is also permitted. + If a letter appears both before and after the hyphen, the + option is unset. + + The scope of these option changes depends on where in the + pattern the setting occurs. For settings that are outside + any subpattern (defined below), the effect is the same as if + the options were set or unset at the start of matching. The + following patterns all behave in exactly the same way: + + (?i)abc + a(?i)bc + ab(?i)c + abc(?i) + + which in turn is the same as compiling the pattern abc with + PCRE_CASELESS set. In other words, such "top level" set- + tings apply to the whole pattern (unless there are other + changes inside subpatterns). If there is more than one set- + ting of the same option at top level, the rightmost setting + is used. + + If an option change occurs inside a subpattern, the effect + is different. This is a change of behaviour in Perl 5.005. + An option change inside a subpattern affects only that part + of the subpattern that follows it, so + + (a(?i)b)c + + matches abc and aBc and no other strings (assuming + PCRE_CASELESS is not used). By this means, options can be + made to have different settings in different parts of the + pattern. Any changes made in one alternative do carry on + into subsequent branches within the same subpattern. For + example, + + (a(?i)b|c) + + matches "ab", "aB", "c", and "C", even though when matching + "C" the first branch is abandoned before the option setting. + This is because the effects of option settings happen at + compile time. There would be some very weird behaviour oth- + erwise. + + The PCRE-specific options PCRE_UNGREEDY and PCRE_EXTRA can + be changed in the same way as the Perl-compatible options by + using the characters U and X respectively. The (?X) flag + setting is special in that it must always occur earlier in + the pattern than any of the additional features it turns on, + even when it is at top level. It is best put at the start. + + + +SUBPATTERNS + Subpatterns are delimited by parentheses (round brackets), + which can be nested. Marking part of a pattern as a subpat- + tern does two things: + + 1. It localizes a set of alternatives. For example, the pat- + tern + + cat(aract|erpillar|) + + matches one of the words "cat", "cataract", or "caterpil- + lar". Without the parentheses, it would match "cataract", + "erpillar" or the empty string. + + 2. It sets up the subpattern as a capturing subpattern (as + defined above). When the whole pattern matches, that por- + tion of the subject string that matched the subpattern is + passed back to the caller via the ovector argument of + pcre_exec(). Opening parentheses are counted from left to + right (starting from 1) to obtain the numbers of the captur- + ing subpatterns. + + For example, if the string "the red king" is matched against + the pattern + + the ((red|white) (king|queen)) + + the captured substrings are "red king", "red", and "king", + and are numbered 1, 2, and 3. + + The fact that plain parentheses fulfil two functions is not + always helpful. There are often times when a grouping sub- + pattern is required without a capturing requirement. If an + opening parenthesis is followed by "?:", the subpattern does + not do any capturing, and is not counted when computing the + number of any subsequent capturing subpatterns. For example, + if the string "the white queen" is matched against the pat- + tern + + the ((?:red|white) (king|queen)) + + the captured substrings are "white queen" and "queen", and + are numbered 1 and 2. The maximum number of captured sub- + strings is 99, and the maximum number of all subpatterns, + both capturing and non-capturing, is 200. + + As a convenient shorthand, if any option settings are + required at the start of a non-capturing subpattern, the + option letters may appear between the "?" and the ":". Thus + the two patterns + + (?i:saturday|sunday) + (?:(?i)saturday|sunday) + + match exactly the same set of strings. Because alternative + branches are tried from left to right, and options are not + reset until the end of the subpattern is reached, an option + setting in one branch does affect subsequent branches, so + the above patterns match "SUNDAY" as well as "Saturday". + + + +REPETITION + Repetition is specified by quantifiers, which can follow any + of the following items: + + a single character, possibly escaped + the . metacharacter + a character class + a back reference (see next section) + a parenthesized subpattern (unless it is an assertion - + see below) + + The general repetition quantifier specifies a minimum and + maximum number of permitted matches, by giving the two + numbers in curly brackets (braces), separated by a comma. + The numbers must be less than 65536, and the first must be + less than or equal to the second. For example: + + z{2,4} + + matches "zz", "zzz", or "zzzz". A closing brace on its own + is not a special character. If the second number is omitted, + but the comma is present, there is no upper limit; if the + second number and the comma are both omitted, the quantifier + specifies an exact number of required matches. Thus + + [aeiou]{3,} + + matches at least 3 successive vowels, but may match many + more, while + + \d{8} + + matches exactly 8 digits. An opening curly bracket that + appears in a position where a quantifier is not allowed, or + one that does not match the syntax of a quantifier, is taken + as a literal character. For example, {,6} is not a quantif- + ier, but a literal string of four characters. + + The quantifier {0} is permitted, causing the expression to + behave as if the previous item and the quantifier were not + present. + + For convenience (and historical compatibility) the three + most common quantifiers have single-character abbreviations: + + * is equivalent to {0,} + + is equivalent to {1,} + ? is equivalent to {0,1} + + It is possible to construct infinite loops by following a + subpattern that can match no characters with a quantifier + that has no upper limit, for example: + + (a?)* + + Earlier versions of Perl and PCRE used to give an error at + compile time for such patterns. However, because there are + cases where this can be useful, such patterns are now + accepted, but if any repetition of the subpattern does in + fact match no characters, the loop is forcibly broken. + + By default, the quantifiers are "greedy", that is, they + match as much as possible (up to the maximum number of per- + mitted times), without causing the rest of the pattern to + fail. The classic example of where this gives problems is in + trying to match comments in C programs. These appear between + the sequences /* and */ and within the sequence, individual + * and / characters may appear. An attempt to match C com- + ments by applying the pattern + + /\*.*\*/ + + to the string + + /* first command */ not comment /* second comment */ + + fails, because it matches the entire string due to the + greediness of the .* item. + + However, if a quantifier is followed by a question mark, + then it ceases to be greedy, and instead matches the minimum + number of times possible, so the pattern + + /\*.*?\*/ + + does the right thing with the C comments. The meaning of the + various quantifiers is not otherwise changed, just the pre- + ferred number of matches. Do not confuse this use of ques- + tion mark with its use as a quantifier in its own right. + Because it has two uses, it can sometimes appear doubled, as + in + + \d??\d + + which matches one digit by preference, but can match two if + that is the only way the rest of the pattern matches. + + If the PCRE_UNGREEDY option is set (an option which is not + available in Perl) then the quantifiers are not greedy by + default, but individual ones can be made greedy by following + them with a question mark. In other words, it inverts the + default behaviour. + + When a parenthesized subpattern is quantified with a minimum + repeat count that is greater than 1 or with a limited max- + imum, more store is required for the compiled pattern, in + proportion to the size of the minimum or maximum. + + If a pattern starts with .* or .{0,} and the PCRE_DOTALL + option (equivalent to Perl's /s) is set, thus allowing the . + to match newlines, then the pattern is implicitly anchored, + because whatever follows will be tried against every charac- + ter position in the subject string, so there is no point in + retrying the overall match at any position after the first. + PCRE treats such a pattern as though it were preceded by \A. + In cases where it is known that the subject string contains + no newlines, it is worth setting PCRE_DOTALL when the pat- + tern begins with .* in order to obtain this optimization, or + alternatively using ^ to indicate anchoring explicitly. + + When a capturing subpattern is repeated, the value captured + is the substring that matched the final iteration. For exam- + ple, after + + (tweedle[dume]{3}\s*)+ + + has matched "tweedledum tweedledee" the value of the cap- + tured substring is "tweedledee". However, if there are + nested capturing subpatterns, the corresponding captured + values may have been set in previous iterations. For exam- + ple, after + + /(a|(b))+/ + + matches "aba" the value of the second captured substring is + "b". + + + +BACK REFERENCES + Outside a character class, a backslash followed by a digit + greater than 0 (and possibly further digits) is a back + reference to a capturing subpattern earlier (i.e. to its + left) in the pattern, provided there have been that many + previous capturing left parentheses. + + However, if the decimal number following the backslash is + less than 10, it is always taken as a back reference, and + causes an error only if there are not that many capturing + left parentheses in the entire pattern. In other words, the + parentheses that are referenced need not be to the left of + the reference for numbers less than 10. See the section + entitled "Backslash" above for further details of the han- + dling of digits following a backslash. + + A back reference matches whatever actually matched the cap- + turing subpattern in the current subject string, rather than + anything matching the subpattern itself. So the pattern + + (sens|respons)e and \1ibility + + matches "sense and sensibility" and "response and responsi- + bility", but not "sense and responsibility". If caseful + matching is in force at the time of the back reference, then + the case of letters is relevant. For example, + + ((?i)rah)\s+\1 + + matches "rah rah" and "RAH RAH", but not "RAH rah", even + though the original capturing subpattern is matched case- + lessly. + + There may be more than one back reference to the same sub- + pattern. If a subpattern has not actually been used in a + particular match, then any back references to it always + fail. For example, the pattern + + (a|(bc))\2 + + always fails if it starts to match "a" rather than "bc". + Because there may be up to 99 back references, all digits + following the backslash are taken as part of a potential + back reference number. If the pattern continues with a digit + character, then some delimiter must be used to terminate the + back reference. If the PCRE_EXTENDED option is set, this can + be whitespace. Otherwise an empty comment can be used. + + A back reference that occurs inside the parentheses to which + it refers fails when the subpattern is first used, so, for + example, (a\1) never matches. However, such references can + be useful inside repeated subpatterns. For example, the + pattern + + (a|b\1)+ + + matches any number of "a"s and also "aba", "ababaa" etc. At + each iteration of the subpattern, the back reference matches + the character string corresponding to the previous itera- + tion. In order for this to work, the pattern must be such + that the first iteration does not need to match the back + reference. This can be done using alternation, as in the + example above, or by a quantifier with a minimum of zero. + + + +ASSERTIONS + An assertion is a test on the characters following or + preceding the current matching point that does not actually + consume any characters. The simple assertions coded as \b, + \B, \A, \Z, \z, ^ and $ are described above. More compli- + cated assertions are coded as subpatterns. There are two + kinds: those that look ahead of the current position in the + subject string, and those that look behind it. + + An assertion subpattern is matched in the normal way, except + that it does not cause the current matching position to be + changed. Lookahead assertions start with (?= for positive + assertions and (?! for negative assertions. For example, + + \w+(?=;) + + matches a word followed by a semicolon, but does not include + the semicolon in the match, and + + foo(?!bar) + + matches any occurrence of "foo" that is not followed by + "bar". Note that the apparently similar pattern + + (?!foo)bar + + does not find an occurrence of "bar" that is preceded by + something other than "foo"; it finds any occurrence of "bar" + whatsoever, because the assertion (?!foo) is always true + when the next three characters are "bar". A lookbehind + assertion is needed to achieve this effect. + + Lookbehind assertions start with (?<= for positive asser- + tions and (? as in this example: + + (?>\d+)bar + + This kind of parenthesis "locks up" the part of the pattern + it contains once it has matched, and a failure further into + the pattern is prevented from backtracking into it. Back- + tracking past it to previous items, however, works as nor- + mal. + + An alternative description is that a subpattern of this type + matches the string of characters that an identical stan- + dalone pattern would match, if anchored at the current point + in the subject string. + + Once-only subpatterns are not capturing subpatterns. Simple + cases such as the above example can be thought of as a max- + imizing repeat that must swallow everything it can. So, + while both \d+ and \d+? are prepared to adjust the number of + digits they match in order to make the rest of the pattern + match, (?>\d+) can only match an entire sequence of digits. + + This construction can of course contain arbitrarily compli- + cated subpatterns, and it can be nested. + + Once-only subpatterns can be used in conjunction with look- + behind assertions to specify efficient matching at the end + of the subject string. Consider a simple pattern such as + + abcd$ + + when applied to a long string which does not match. Because + matching proceeds from left to right, PCRE will look for + each "a" in the subject and then see if what follows matches + the rest of the pattern. If the pattern is specified as + + ^.*abcd$ + + then the initial .* matches the entire string at first, but + when this fails (because there is no following "a"), it + backtracks to match all but the last character, then all but + the last two characters, and so on. Once again the search + for "a" covers the entire string, from right to left, so we + are no better off. However, if the pattern is written as + + ^(?>.*)(?<=abcd) + + then there can be no backtracking for the .* item; it can + match only the entire string. The subsequent lookbehind + assertion does a single test on the last four characters. If + it fails, the match fails immediately. For long strings, + this approach makes a significant difference to the process- + ing time. + + When a pattern contains an unlimited repeat inside a subpat- + tern that can itself be repeated an unlimited number of + times, the use of a once-only subpattern is the only way to + avoid some failing matches taking a very long time indeed. + The pattern + + (\D+|<\d+>)*[!?] + + matches an unlimited number of substrings that either con- + sist of non-digits, or digits enclosed in <>, followed by + either ! or ?. When it matches, it runs quickly. However, if + it is applied to + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + it takes a long time before reporting failure. This is + because the string can be divided between the two repeats in + a large number of ways, and all have to be tried. (The exam- + ple used [!?] rather than a single character at the end, + because both PCRE and Perl have an optimization that allows + for fast failure when a single character is used. They + remember the last single character that is required for a + match, and fail early if it is not present in the string.) + If the pattern is changed to + + ((?>\D+)|<\d+>)*[!?] + + sequences of non-digits cannot be broken, and failure hap- + pens quickly. + + + +CONDITIONAL SUBPATTERNS + It is possible to cause the matching process to obey a sub- + pattern conditionally or to choose between two alternative + subpatterns, depending on the result of an assertion, or + whether a previous capturing subpattern matched or not. The + two possible forms of conditional subpattern are + + (?(condition)yes-pattern) + (?(condition)yes-pattern|no-pattern) + + If the condition is satisfied, the yes-pattern is used; oth- + erwise the no-pattern (if present) is used. If there are + more than two alternatives in the subpattern, a compile-time + error occurs. + + There are two kinds of condition. If the text between the + parentheses consists of a sequence of digits, then the + condition is satisfied if the capturing subpattern of that + number has previously matched. Consider the following pat- + tern, which contains non-significant white space to make it + more readable (assume the PCRE_EXTENDED option) and to + divide it into three parts for ease of discussion: + + ( \( )? [^()]+ (?(1) \) ) + + The first part matches an optional opening parenthesis, and + if that character is present, sets it as the first captured + substring. The second part matches one or more characters + that are not parentheses. The third part is a conditional + subpattern that tests whether the first set of parentheses + matched or not. If they did, that is, if subject started + with an opening parenthesis, the condition is true, and so + the yes-pattern is executed and a closing parenthesis is + required. Otherwise, since no-pattern is not present, the + subpattern matches nothing. In other words, this pattern + matches a sequence of non-parentheses, optionally enclosed + in parentheses. + + If the condition is not a sequence of digits, it must be an + assertion. This may be a positive or negative lookahead or + lookbehind assertion. Consider this pattern, again contain- + ing non-significant white space, and with the two alterna- + tives on the second line: + + (?(?=[^a-z]*[a-z]) + \d{2}-[a-z]{3}-\d{2} | \d{2}-\d{2}-\d{2} ) + + The condition is a positive lookahead assertion that matches + an optional sequence of non-letters followed by a letter. In + other words, it tests for the presence of at least one + letter in the subject. If a letter is found, the subject is + matched against the first alternative; otherwise it is + matched against the second. This pattern matches strings in + one of the two forms dd-aaa-dd or dd-dd-dd, where aaa are + letters and dd are digits. + + + +COMMENTS + The sequence (?# marks the start of a comment which contin- + ues up to the next closing parenthesis. Nested parentheses + are not permitted. The characters that make up a comment + play no part in the pattern matching at all. + + If the PCRE_EXTENDED option is set, an unescaped # character + outside a character class introduces a comment that contin- + ues up to the next newline character in the pattern. + + + +RECURSIVE PATTERNS + Consider the problem of matching a string in parentheses, + allowing for unlimited nested parentheses. Without the use + of recursion, the best that can be done is to use a pattern + that matches up to some fixed depth of nesting. It is not + possible to handle an arbitrary nesting depth. Perl 5.6 has + provided an experimental facility that allows regular + expressions to recurse (amongst other things). It does this + by interpolating Perl code in the expression at run time, + and the code can refer to the expression itself. A Perl pat- + tern to solve the parentheses problem can be created like + this: + + $re = qr{\( (?: (?>[^()]+) | (?p{$re}) )* \)}x; + + The (?p{...}) item interpolates Perl code at run time, and + in this case refers recursively to the pattern in which it + appears. Obviously, PCRE cannot support the interpolation of + Perl code. Instead, the special item (?R) is provided for + the specific case of recursion. This PCRE pattern solves the + parentheses problem (assume the PCRE_EXTENDED option is set + so that white space is ignored): + + \( ( (?>[^()]+) | (?R) )* \) + + First it matches an opening parenthesis. Then it matches any + number of substrings which can either be a sequence of non- + parentheses, or a recursive match of the pattern itself + (i.e. a correctly parenthesized substring). Finally there is + a closing parenthesis. + + This particular example pattern contains nested unlimited + repeats, and so the use of a once-only subpattern for match- + ing strings of non-parentheses is important when applying + the pattern to strings that do not match. For example, when + it is applied to + + (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa() + + it yields "no match" quickly. However, if a once-only sub- + pattern is not used, the match runs for a very long time + indeed because there are so many different ways the + and * + repeats can carve up the subject, and all have to be tested + before failure can be reported. + + The values set for any capturing subpatterns are those from + the outermost level of the recursion at which the subpattern + value is set. If the pattern above is matched against + + (ab(cd)ef) + + the value for the capturing parentheses is "ef", which is + the last value taken on at the top level. If additional + parentheses are added, giving + + \( ( ( (?>[^()]+) | (?R) )* ) \) + ^ ^ + ^ ^ then the string they capture + is "ab(cd)ef", the contents of the top level parentheses. If + there are more than 15 capturing parentheses in a pattern, + PCRE has to obtain extra memory to store data during a + recursion, which it does by using pcre_malloc, freeing it + via pcre_free afterwards. If no memory can be obtained, it + saves data for the first 15 capturing parentheses only, as + there is no way to give an out-of-memory error from within a + recursion. + + + +PERFORMANCE + Certain items that may appear in patterns are more efficient + than others. It is more efficient to use a character class + like [aeiou] than a set of alternatives such as (a|e|i|o|u). + In general, the simplest construction that provides the + required behaviour is usually the most efficient. Jeffrey + Friedl's book contains a lot of discussion about optimizing + regular expressions for efficient performance. + + When a pattern begins with .* and the PCRE_DOTALL option is + set, the pattern is implicitly anchored by PCRE, since it + can match only at the start of a subject string. However, if + PCRE_DOTALL is not set, PCRE cannot make this optimization, + because the . metacharacter does not then match a newline, + and if the subject string contains newlines, the pattern may + match from the character immediately following one of them + instead of from the very start. For example, the pattern + + (.*) second + + matches the subject "first\nand second" (where \n stands for + a newline character) with the first captured substring being + "and". In order to do this, PCRE has to retry the match + starting after every newline in the subject. + + If you are using such a pattern with subject strings that do + not contain newlines, the best performance is obtained by + setting PCRE_DOTALL, or starting the pattern with ^.* to + indicate explicit anchoring. That saves PCRE from having to + scan along the subject looking for a newline to restart at. + + Beware of patterns that contain nested indefinite repeats. + These can take a long time to run when applied to a string + that does not match. Consider the pattern fragment + + (a+)* + + This can match "aaaa" in 33 different ways, and this number + increases very rapidly as the string gets longer. (The * + repeat can match 0, 1, 2, 3, or 4 times, and for each of + those cases other than 0, the + repeats can match different + numbers of times.) When the remainder of the pattern is such + that the entire match is going to fail, PCRE has in princi- + ple to try every possible variation, and this can take an + extremely long time. + + An optimization catches some of the more simple cases such + as + + (a+)*b + + where a literal character follows. Before embarking on the + standard matching procedure, PCRE checks that there is a "b" + later in the subject string, and if there is not, it fails + the match immediately. However, when there is no following + literal this optimization cannot be used. You can see the + difference by comparing the behaviour of + + (a+)*\d + + with the pattern above. The former gives a failure almost + instantly when applied to a whole line of "a" characters, + whereas the latter takes an appreciable time with strings + longer than about 20 characters. + + + +AUTHOR + Philip Hazel + University Computing Service, + New Museums Site, + Cambridge CB2 3QG, England. + Phone: +44 1223 334714 + + Last updated: 27 January 2000 + Copyright (c) 1997-2000 University of Cambridge. diff --git a/srclib/pcre/doc/pcreposix.3 b/srclib/pcre/doc/pcreposix.3 new file mode 100644 index 00000000000..1be5d9ac6fd --- /dev/null +++ b/srclib/pcre/doc/pcreposix.3 @@ -0,0 +1,141 @@ +.TH PCRE 3 +.SH NAME +pcreposix - POSIX API for Perl-compatible regular expressions. +.SH SYNOPSIS +.B #include +.PP +.SM +.br +.B int regcomp(regex_t *\fIpreg\fR, const char *\fIpattern\fR, +.ti +5n +.B int \fIcflags\fR); +.PP +.br +.B int regexec(regex_t *\fIpreg\fR, const char *\fIstring\fR, +.ti +5n +.B size_t \fInmatch\fR, regmatch_t \fIpmatch\fR[], int \fIeflags\fR); +.PP +.br +.B size_t regerror(int \fIerrcode\fR, const regex_t *\fIpreg\fR, +.ti +5n +.B char *\fIerrbuf\fR, size_t \fIerrbuf_size\fR); +.PP +.br +.B void regfree(regex_t *\fIpreg\fR); + + +.SH DESCRIPTION +This set of functions provides a POSIX-style API to the PCRE regular expression +package. See the \fBpcre\fR documentation for a description of the native API, +which contains additional functionality. + +The functions described here are just wrapper functions that ultimately call +the native API. Their prototypes are defined in the \fBpcreposix.h\fR header +file, and on Unix systems the library itself is called \fBpcreposix.a\fR, so +can be accessed by adding \fB-lpcreposix\fR to the command for linking an +application which uses them. Because the POSIX functions call the native ones, +it is also necessary to add \fR-lpcre\fR. + +I have implemented only those option bits that can be reasonably mapped to PCRE +native options. In addition, the options REG_EXTENDED and REG_NOSUB are defined +with the value zero. They have no effect, but since programs that are written +to the POSIX interface often use them, this makes it easier to slot in PCRE as +a replacement library. Other POSIX options are not even defined. + +When PCRE is called via these functions, it is only the API that is POSIX-like +in style. The syntax and semantics of the regular expressions themselves are +still those of Perl, subject to the setting of various PCRE options, as +described below. + +The header for these functions is supplied as \fBpcreposix.h\fR to avoid any +potential clash with other POSIX libraries. It can, of course, be renamed or +aliased as \fBregex.h\fR, which is the "correct" name. It provides two +structure types, \fIregex_t\fR for compiled internal forms, and +\fIregmatch_t\fR for returning captured substrings. It also defines some +constants whose names start with "REG_"; these are used for setting options and +identifying error codes. + + +.SH COMPILING A PATTERN + +The function \fBregcomp()\fR is called to compile a pattern into an +internal form. The pattern is a C string terminated by a binary zero, and +is passed in the argument \fIpattern\fR. The \fIpreg\fR argument is a pointer +to a regex_t structure which is used as a base for storing information about +the compiled expression. + +The argument \fIcflags\fR is either zero, or contains one or more of the bits +defined by the following macros: + + REG_ICASE + +The PCRE_CASELESS option is set when the expression is passed for compilation +to the native function. + + REG_NEWLINE + +The PCRE_MULTILINE option is set when the expression is passed for compilation +to the native function. + +The yield of \fBregcomp()\fR is zero on success, and non-zero otherwise. The +\fIpreg\fR structure is filled in on success, and one member of the structure +is publicized: \fIre_nsub\fR contains the number of capturing subpatterns in +the regular expression. Various error codes are defined in the header file. + + +.SH MATCHING A PATTERN +The function \fBregexec()\fR is called to match a pre-compiled pattern +\fIpreg\fR against a given \fIstring\fR, which is terminated by a zero byte, +subject to the options in \fIeflags\fR. These can be: + + REG_NOTBOL + +The PCRE_NOTBOL option is set when calling the underlying PCRE matching +function. + + REG_NOTEOL + +The PCRE_NOTEOL option is set when calling the underlying PCRE matching +function. + +The portion of the string that was matched, and also any captured substrings, +are returned via the \fIpmatch\fR argument, which points to an array of +\fInmatch\fR structures of type \fIregmatch_t\fR, containing the members +\fIrm_so\fR and \fIrm_eo\fR. These contain the offset to the first character of +each substring and the offset to the first character after the end of each +substring, respectively. The 0th element of the vector relates to the entire +portion of \fIstring\fR that was matched; subsequent elements relate to the +capturing subpatterns of the regular expression. Unused entries in the array +have both structure members set to -1. + +A successful match yields a zero return; various error codes are defined in the +header file, of which REG_NOMATCH is the "expected" failure code. + + +.SH ERROR MESSAGES +The \fBregerror()\fR function maps a non-zero errorcode from either +\fBregcomp\fR or \fBregexec\fR to a printable message. If \fIpreg\fR is not +NULL, the error should have arisen from the use of that structure. A message +terminated by a binary zero is placed in \fIerrbuf\fR. The length of the +message, including the zero, is limited to \fIerrbuf_size\fR. The yield of the +function is the size of buffer needed to hold the whole message. + + +.SH STORAGE +Compiling a regular expression causes memory to be allocated and associated +with the \fIpreg\fR structure. The function \fBregfree()\fR frees all such +memory, after which \fIpreg\fR may no longer be used as a compiled expression. + + +.SH AUTHOR +Philip Hazel +.br +University Computing Service, +.br +New Museums Site, +.br +Cambridge CB2 3QG, England. +.br +Phone: +44 1223 334714 + +Copyright (c) 1997-1999 University of Cambridge. diff --git a/srclib/pcre/doc/pcreposix.html b/srclib/pcre/doc/pcreposix.html new file mode 100644 index 00000000000..121d90f8672 --- /dev/null +++ b/srclib/pcre/doc/pcreposix.html @@ -0,0 +1,182 @@ + + +pcreposix specification + + +

    pcreposix specification

    +This HTML document has been generated automatically from the original man page. +If there is any nonsense in it, please consult the man page in case the +conversion went wrong. + +
  • NAME +

    +pcreposix - POSIX API for Perl-compatible regular expressions. +

    +
  • SYNOPSIS +

    +#include <pcreposix.h> +

    +

    +int regcomp(regex_t *preg, const char *pattern, +int cflags); +

    +

    +int regexec(regex_t *preg, const char *string, +size_t nmatch, regmatch_t pmatch[], int eflags); +

    +

    +size_t regerror(int errcode, const regex_t *preg, +char *errbuf, size_t errbuf_size); +

    +

    +void regfree(regex_t *preg); +

    +
  • DESCRIPTION +

    +This set of functions provides a POSIX-style API to the PCRE regular expression +package. See the pcre documentation for a description of the native API, +which contains additional functionality. +

    +

    +The functions described here are just wrapper functions that ultimately call +the native API. Their prototypes are defined in the pcreposix.h header +file, and on Unix systems the library itself is called pcreposix.a, so +can be accessed by adding -lpcreposix to the command for linking an +application which uses them. Because the POSIX functions call the native ones, +it is also necessary to add \fR-lpcre\fR. +

    +

    +I have implemented only those option bits that can be reasonably mapped to PCRE +native options. In addition, the options REG_EXTENDED and REG_NOSUB are defined +with the value zero. They have no effect, but since programs that are written +to the POSIX interface often use them, this makes it easier to slot in PCRE as +a replacement library. Other POSIX options are not even defined. +

    +

    +When PCRE is called via these functions, it is only the API that is POSIX-like +in style. The syntax and semantics of the regular expressions themselves are +still those of Perl, subject to the setting of various PCRE options, as +described below. +

    +

    +The header for these functions is supplied as pcreposix.h to avoid any +potential clash with other POSIX libraries. It can, of course, be renamed or +aliased as regex.h, which is the "correct" name. It provides two +structure types, regex_t for compiled internal forms, and +regmatch_t for returning captured substrings. It also defines some +constants whose names start with "REG_"; these are used for setting options and +identifying error codes. +

    +
  • COMPILING A PATTERN +

    +The function regcomp() is called to compile a pattern into an +internal form. The pattern is a C string terminated by a binary zero, and +is passed in the argument pattern. The preg argument is a pointer +to a regex_t structure which is used as a base for storing information about +the compiled expression. +

    +

    +The argument cflags is either zero, or contains one or more of the bits +defined by the following macros: +

    +

    +

    +  REG_ICASE
    +
    +

    +

    +The PCRE_CASELESS option is set when the expression is passed for compilation +to the native function. +

    +

    +

    +  REG_NEWLINE
    +
    +

    +

    +The PCRE_MULTILINE option is set when the expression is passed for compilation +to the native function. +

    +

    +The yield of regcomp() is zero on success, and non-zero otherwise. The +preg structure is filled in on success, and one member of the structure +is publicized: re_nsub contains the number of capturing subpatterns in +the regular expression. Various error codes are defined in the header file. +

    +
  • MATCHING A PATTERN +

    +The function regexec() is called to match a pre-compiled pattern +preg against a given string, which is terminated by a zero byte, +subject to the options in eflags. These can be: +

    +

    +

    +  REG_NOTBOL
    +
    +

    +

    +The PCRE_NOTBOL option is set when calling the underlying PCRE matching +function. +

    +

    +

    +  REG_NOTEOL
    +
    +

    +

    +The PCRE_NOTEOL option is set when calling the underlying PCRE matching +function. +

    +

    +The portion of the string that was matched, and also any captured substrings, +are returned via the pmatch argument, which points to an array of +nmatch structures of type regmatch_t, containing the members +rm_so and rm_eo. These contain the offset to the first character of +each substring and the offset to the first character after the end of each +substring, respectively. The 0th element of the vector relates to the entire +portion of string that was matched; subsequent elements relate to the +capturing subpatterns of the regular expression. Unused entries in the array +have both structure members set to -1. +

    +

    +A successful match yields a zero return; various error codes are defined in the +header file, of which REG_NOMATCH is the "expected" failure code. +

    +
  • ERROR MESSAGES +

    +The regerror() function maps a non-zero errorcode from either +regcomp or regexec to a printable message. If preg is not +NULL, the error should have arisen from the use of that structure. A message +terminated by a binary zero is placed in errbuf. The length of the +message, including the zero, is limited to errbuf_size. The yield of the +function is the size of buffer needed to hold the whole message. +

    +
  • STORAGE +

    +Compiling a regular expression causes memory to be allocated and associated +with the preg structure. The function regfree() frees all such +memory, after which preg may no longer be used as a compiled expression. +

    +
  • AUTHOR +

    +Philip Hazel <ph10@cam.ac.uk> +
    +University Computing Service, +
    +New Museums Site, +
    +Cambridge CB2 3QG, England. +
    +Phone: +44 1223 334714 +

    +

    +Copyright (c) 1997-1999 University of Cambridge. diff --git a/srclib/pcre/doc/pcreposix.txt b/srclib/pcre/doc/pcreposix.txt new file mode 100644 index 00000000000..4a7036f3406 --- /dev/null +++ b/srclib/pcre/doc/pcreposix.txt @@ -0,0 +1,150 @@ +NAME + pcreposix - POSIX API for Perl-compatible regular expres- + sions. + + + +SYNOPSIS + #include + + int regcomp(regex_t *preg, const char *pattern, + int cflags); + + int regexec(regex_t *preg, const char *string, + size_t nmatch, regmatch_t pmatch[], int eflags); + + size_t regerror(int errcode, const regex_t *preg, + char *errbuf, size_t errbuf_size); + + void regfree(regex_t *preg); + + + +DESCRIPTION + This set of functions provides a POSIX-style API to the PCRE + regular expression package. See the pcre documentation for a + description of the native API, which contains additional + functionality. + + The functions described here are just wrapper functions that + ultimately call the native API. Their prototypes are defined + in the pcreposix.h header file, and on Unix systems the + library itself is called pcreposix.a, so can be accessed by + adding -lpcreposix to the command for linking an application + which uses them. Because the POSIX functions call the native + ones, it is also necessary to add -lpcre. + + I have implemented only those option bits that can be rea- + sonably mapped to PCRE native options. In addition, the + options REG_EXTENDED and REG_NOSUB are defined with the + value zero. They have no effect, but since programs that are + written to the POSIX interface often use them, this makes it + easier to slot in PCRE as a replacement library. Other POSIX + options are not even defined. + + When PCRE is called via these functions, it is only the API + that is POSIX-like in style. The syntax and semantics of the + regular expressions themselves are still those of Perl, sub- + ject to the setting of various PCRE options, as described + below. + + The header for these functions is supplied as pcreposix.h to + avoid any potential clash with other POSIX libraries. It + can, of course, be renamed or aliased as regex.h, which is + the "correct" name. It provides two structure types, regex_t + for compiled internal forms, and regmatch_t for returning + captured substrings. It also defines some constants whose + names start with "REG_"; these are used for setting options + and identifying error codes. + + + +COMPILING A PATTERN + The function regcomp() is called to compile a pattern into + an internal form. The pattern is a C string terminated by a + binary zero, and is passed in the argument pattern. The preg + argument is a pointer to a regex_t structure which is used + as a base for storing information about the compiled expres- + sion. + + The argument cflags is either zero, or contains one or more + of the bits defined by the following macros: + + REG_ICASE + + The PCRE_CASELESS option is set when the expression is + passed for compilation to the native function. + + REG_NEWLINE + + The PCRE_MULTILINE option is set when the expression is + passed for compilation to the native function. + + The yield of regcomp() is zero on success, and non-zero oth- + erwise. The preg structure is filled in on success, and one + member of the structure is publicized: re_nsub contains the + number of capturing subpatterns in the regular expression. + Various error codes are defined in the header file. + + + +MATCHING A PATTERN + The function regexec() is called to match a pre-compiled + pattern preg against a given string, which is terminated by + a zero byte, subject to the options in eflags. These can be: + + REG_NOTBOL + + The PCRE_NOTBOL option is set when calling the underlying + PCRE matching function. + + REG_NOTEOL + + The PCRE_NOTEOL option is set when calling the underlying + PCRE matching function. + + The portion of the string that was matched, and also any + captured substrings, are returned via the pmatch argument, + which points to an array of nmatch structures of type + regmatch_t, containing the members rm_so and rm_eo. These + contain the offset to the first character of each substring + and the offset to the first character after the end of each + substring, respectively. The 0th element of the vector + relates to the entire portion of string that was matched; + subsequent elements relate to the capturing subpatterns of + the regular expression. Unused entries in the array have + both structure members set to -1. + + A successful match yields a zero return; various error codes + are defined in the header file, of which REG_NOMATCH is the + "expected" failure code. + + + +ERROR MESSAGES + The regerror() function maps a non-zero errorcode from + either regcomp or regexec to a printable message. If preg is + not NULL, the error should have arisen from the use of that + structure. A message terminated by a binary zero is placed + in errbuf. The length of the message, including the zero, is + limited to errbuf_size. The yield of the function is the + size of buffer needed to hold the whole message. + + + +STORAGE + Compiling a regular expression causes memory to be allocated + and associated with the preg structure. The function reg- + free() frees all such memory, after which preg may no longer + be used as a compiled expression. + + + +AUTHOR + Philip Hazel + University Computing Service, + New Museums Site, + Cambridge CB2 3QG, England. + Phone: +44 1223 334714 + + Copyright (c) 1997-1999 University of Cambridge. diff --git a/srclib/pcre/doc/pcretest.txt b/srclib/pcre/doc/pcretest.txt new file mode 100644 index 00000000000..831fdac9876 --- /dev/null +++ b/srclib/pcre/doc/pcretest.txt @@ -0,0 +1,216 @@ +The pcretest program +-------------------- + +This program is intended for testing PCRE, but it can also be used for +experimenting with regular expressions. + +If it is given two filename arguments, it reads from the first and writes to +the second. If it is given only one filename argument, it reads from that file +and writes to stdout. Otherwise, it reads from stdin and writes to stdout, and +prompts for each line of input, using "re>" to prompt for regular expressions, +and "data>" to prompt for data lines. + +The program handles any number of sets of input on a single input file. Each +set starts with a regular expression, and continues with any number of data +lines to be matched against the pattern. An empty line signals the end of the +data lines, at which point a new regular expression is read. The regular +expressions are given enclosed in any non-alphameric delimiters other than +backslash, for example + + /(a|bc)x+yz/ + +White space before the initial delimiter is ignored. A regular expression may +be continued over several input lines, in which case the newline characters are +included within it. See the test input files in the testdata directory for many +examples. It is possible to include the delimiter within the pattern by +escaping it, for example + + /abc\/def/ + +If you do so, the escape and the delimiter form part of the pattern, but since +delimiters are always non-alphameric, this does not affect its interpretation. +If the terminating delimiter is immediately followed by a backslash, for +example, + + /abc/\ + +then a backslash is added to the end of the pattern. This is done to provide a +way of testing the error condition that arises if a pattern finishes with a +backslash, because + + /abc\/ + +is interpreted as the first line of a pattern that starts with "abc/", causing +pcretest to read the next line as a continuation of the regular expression. + +The pattern may be followed by i, m, s, or x to set the PCRE_CASELESS, +PCRE_MULTILINE, PCRE_DOTALL, or PCRE_EXTENDED options, respectively. For +example: + + /caseless/i + +These modifier letters have the same effect as they do in Perl. There are +others which set PCRE options that do not correspond to anything in Perl: /A, +/E, and /X set PCRE_ANCHORED, PCRE_DOLLAR_ENDONLY, and PCRE_EXTRA respectively. + +Searching for all possible matches within each subject string can be requested +by the /g or /G modifier. After finding a match, PCRE is called again to search +the remainder of the subject string. The difference between /g and /G is that +the former uses the startoffset argument to pcre_exec() to start searching at +a new point within the entire string (which is in effect what Perl does), +whereas the latter passes over a shortened substring. This makes a difference +to the matching process if the pattern begins with a lookbehind assertion +(including \b or \B). + +If any call to pcre_exec() in a /g or /G sequence matches an empty string, the +next call is done with the PCRE_NOTEMPTY flag set so that it cannot match an +empty string again at the same point. If however, this second match fails, the +start offset is advanced by one, and the match is retried. This imitates the +way Perl handles such cases when using the /g modifier or the split() function. + +There are a number of other modifiers for controlling the way pcretest +operates. + +The /+ modifier requests that as well as outputting the substring that matched +the entire pattern, pcretest should in addition output the remainder of the +subject string. This is useful for tests where the subject contains multiple +copies of the same substring. + +The /L modifier must be followed directly by the name of a locale, for example, + + /pattern/Lfr + +For this reason, it must be the last modifier letter. The given locale is set, +pcre_maketables() is called to build a set of character tables for the locale, +and this is then passed to pcre_compile() when compiling the regular +expression. Without an /L modifier, NULL is passed as the tables pointer; that +is, /L applies only to the expression on which it appears. + +The /I modifier requests that pcretest output information about the compiled +expression (whether it is anchored, has a fixed first character, and so on). It +does this by calling pcre_fullinfo() after compiling an expression, and +outputting the information it gets back. If the pattern is studied, the results +of that are also output. + +The /D modifier is a PCRE debugging feature, which also assumes /I. It causes +the internal form of compiled regular expressions to be output after +compilation. + +The /S modifier causes pcre_study() to be called after the expression has been +compiled, and the results used when the expression is matched. + +The /M modifier causes the size of memory block used to hold the compiled +pattern to be output. + +Finally, the /P modifier causes pcretest to call PCRE via the POSIX wrapper API +rather than its native API. When this is done, all other modifiers except /i, +/m, and /+ are ignored. REG_ICASE is set if /i is present, and REG_NEWLINE is +set if /m is present. The wrapper functions force PCRE_DOLLAR_ENDONLY always, +and PCRE_DOTALL unless REG_NEWLINE is set. + +Before each data line is passed to pcre_exec(), leading and trailing whitespace +is removed, and it is then scanned for \ escapes. The following are recognized: + + \a alarm (= BEL) + \b backspace + \e escape + \f formfeed + \n newline + \r carriage return + \t tab + \v vertical tab + \nnn octal character (up to 3 octal digits) + \xhh hexadecimal character (up to 2 hex digits) + + \A pass the PCRE_ANCHORED option to pcre_exec() + \B pass the PCRE_NOTBOL option to pcre_exec() + \Cdd call pcre_copy_substring() for substring dd after a successful match + (any decimal number less than 32) + \Gdd call pcre_get_substring() for substring dd after a successful match + (any decimal number less than 32) + \L call pcre_get_substringlist() after a successful match + \N pass the PCRE_NOTEMPTY option to pcre_exec() + \Odd set the size of the output vector passed to pcre_exec() to dd + (any number of decimal digits) + \Z pass the PCRE_NOTEOL option to pcre_exec() + +A backslash followed by anything else just escapes the anything else. If the +very last character is a backslash, it is ignored. This gives a way of passing +an empty line as data, since a real empty line terminates the data input. + +If /P was present on the regex, causing the POSIX wrapper API to be used, only +\B, and \Z have any effect, causing REG_NOTBOL and REG_NOTEOL to be passed to +regexec() respectively. + +When a match succeeds, pcretest outputs the list of captured substrings that +pcre_exec() returns, starting with number 0 for the string that matched the +whole pattern. Here is an example of an interactive pcretest run. + + $ pcretest + PCRE version 2.06 08-Jun-1999 + + re> /^abc(\d+)/ + data> abc123 + 0: abc123 + 1: 123 + data> xyz + No match + +If the strings contain any non-printing characters, they are output as \0x +escapes. If the pattern has the /+ modifier, then the output for substring 0 is +followed by the the rest of the subject string, identified by "0+" like this: + + re> /cat/+ + data> cataract + 0: cat + 0+ aract + +If the pattern has the /g or /G modifier, the results of successive matching +attempts are output in sequence, like this: + + re> /\Bi(\w\w)/g + data> Mississippi + 0: iss + 1: ss + 0: iss + 1: ss + 0: ipp + 1: pp + +"No match" is output only if the first match attempt fails. + +If any of \C, \G, or \L are present in a data line that is successfully +matched, the substrings extracted by the convenience functions are output with +C, G, or L after the string number instead of a colon. This is in addition to +the normal full list. The string length (that is, the return from the +extraction function) is given in parentheses after each string for \C and \G. + +Note that while patterns can be continued over several lines (a plain ">" +prompt is used for continuations), data lines may not. However newlines can be +included in data by means of the \n escape. + +If the -p option is given to pcretest, it is equivalent to adding /P to each +regular expression: the POSIX wrapper API is used to call PCRE. None of the +following flags has any effect in this case. + +If the option -d is given to pcretest, it is equivalent to adding /D to each +regular expression: the internal form is output after compilation. + +If the option -i is given to pcretest, it is equivalent to adding /I to each +regular expression: information about the compiled pattern is given after +compilation. + +If the option -m is given to pcretest, it outputs the size of each compiled +pattern after it has been compiled. It is equivalent to adding /M to each +regular expression. For compatibility with earlier versions of pcretest, -s is +a synonym for -m. + +If the -t option is given, each compile, study, and match is run 20000 times +while being timed, and the resulting time per compile or match is output in +milliseconds. Do not set -t with -s, because you will then get the size output +20000 times and the timing will be distorted. If you want to change the number +of repetitions used for timing, edit the definition of LOOPREPEAT at the top of +pcretest.c + +Philip Hazel +January 2000 diff --git a/srclib/pcre/doc/perltest.txt b/srclib/pcre/doc/perltest.txt new file mode 100644 index 00000000000..6c38ebe19ff --- /dev/null +++ b/srclib/pcre/doc/perltest.txt @@ -0,0 +1,23 @@ +The perltest program +-------------------- + +The perltest program tests Perl's regular expressions; it has the same +specification as pcretest, and so can be given identical input, except that +input patterns can be followed only by Perl's lower case modifiers and /+ (as +used by pcretest), which is recognized and handled by the program. + +The data lines are processed as Perl double-quoted strings, so if they contain +" \ $ or @ characters, these have to be escaped. For this reason, all such +characters in testinput1 and testinput3 are escaped so that they can be used +for perltest as well as for pcretest, and the special upper case modifiers such +as /A that pcretest recognizes are not used in these files. The output should +be identical, apart from the initial identifying banner. + +The testinput2 and testinput4 files are not suitable for feeding to perltest, +since they do make use of the special upper case modifiers and escapes that +pcretest uses to test some features of PCRE. The first of these files also +contains malformed regular expressions, in order to check that PCRE diagnoses +them correctly. + +Philip Hazel +January 2000 diff --git a/srclib/pcre/doc/pgrep.1 b/srclib/pcre/doc/pgrep.1 new file mode 100644 index 00000000000..d9e9b575e01 --- /dev/null +++ b/srclib/pcre/doc/pgrep.1 @@ -0,0 +1,76 @@ +.TH PGREP 1 +.SH NAME +pgrep - a grep with Perl-compatible regular expressions. +.SH SYNOPSIS +.B pgrep [-Vchilnsvx] pattern [file] ... + + +.SH DESCRIPTION +\fBpgrep\fR searches files for character patterns, in the same way as other +grep commands do, but it uses the PCRE regular expression library to support +patterns that are compatible with the regular expressions of Perl 5. See +\fBpcre(3)\fR for a full description of syntax and semantics. + +If no files are specified, \fBpgrep\fR reads the standard input. By default, +each line that matches the pattern is copied to the standard output, and if +there is more than one file, the file name is printed before each line of +output. However, there are options that can change how \fBpgrep\fR behaves. + +Lines are limited to BUFSIZ characters. BUFSIZ is defined in \fB\fR. +The newline character is removed from the end of each line before it is matched +against the pattern. + + +.SH OPTIONS +.TP 10 +\fB-V\fR +Write the version number of the PCRE library being used to the standard error +stream. +.TP +\fB-c\fR +Do not print individual lines; instead just print a count of the number of +lines that would otherwise have been printed. If several files are given, a +count is printed for each of them. +.TP +\fB-h\fR +Suppress printing of filenames when searching multiple files. +.TP +\fB-i\fR +Ignore upper/lower case distinctions during comparisons. +.TP +\fB-l\fR +Instead of printing lines from the files, just print the names of the files +containing lines that would have been printed. Each file name is printed +once, on a separate line. +.TP +\fB-n\fR +Precede each line by its line number in the file. +.TP +\fB-s\fR +Work silently, that is, display nothing except error messages. +The exit status indicates whether any matches were found. +.TP +\fB-v\fR +Invert the sense of the match, so that lines which do \fInot\fR match the +pattern are now the ones that are found. +.TP +\fB-x\fR +Force the pattern to be anchored (it must start matching at the beginning of +the line) and in addition, require it to match the entire line. This is +equivalent to having ^ and $ characters at the start and end of each +alternative branch in the regular expression. + + +.SH SEE ALSO +\fBpcre(3)\fR, Perl 5 documentation + + +.SH DIAGNOSTICS +Exit status is 0 if any matches were found, 1 if no matches were found, and 2 +for syntax errors or inacessible files (even if matches were found). + + +.SH AUTHOR +Philip Hazel +.br +Copyright (c) 1997-1999 University of Cambridge. diff --git a/srclib/pcre/doc/pgrep.html b/srclib/pcre/doc/pgrep.html new file mode 100644 index 00000000000..54efed6785f --- /dev/null +++ b/srclib/pcre/doc/pgrep.html @@ -0,0 +1,105 @@ + + +pgrep specification + + +

    pgrep specification

    +This HTML document has been generated automatically from the original man page. +If there is any nonsense in it, please consult the man page in case the +conversion went wrong. + +
  • NAME +

    +pgrep - a grep with Perl-compatible regular expressions. +

    +
  • SYNOPSIS +

    +pgrep [-Vchilnsvx] pattern [file] ... +

    +
  • DESCRIPTION +

    +pgrep searches files for character patterns, in the same way as other +grep commands do, but it uses the PCRE regular expression library to support +patterns that are compatible with the regular expressions of Perl 5. See +pcre(3) for a full description of syntax and semantics. +

    +

    +If no files are specified, pgrep reads the standard input. By default, +each line that matches the pattern is copied to the standard output, and if +there is more than one file, the file name is printed before each line of +output. However, there are options that can change how pgrep behaves. +

    +

    +Lines are limited to BUFSIZ characters. BUFSIZ is defined in <stdio.h>. +The newline character is removed from the end of each line before it is matched +against the pattern. +

    +
  • OPTIONS +

    +-V +Write the version number of the PCRE library being used to the standard error +stream. +

    +

    +-c +Do not print individual lines; instead just print a count of the number of +lines that would otherwise have been printed. If several files are given, a +count is printed for each of them. +

    +

    +-h +Suppress printing of filenames when searching multiple files. +

    +

    +-i +Ignore upper/lower case distinctions during comparisons. +

    +

    +-l +Instead of printing lines from the files, just print the names of the files +containing lines that would have been printed. Each file name is printed +once, on a separate line. +

    +

    +-n +Precede each line by its line number in the file. +

    +

    +-s +Work silently, that is, display nothing except error messages. +The exit status indicates whether any matches were found. +

    +

    +-v +Invert the sense of the match, so that lines which do not match the +pattern are now the ones that are found. +

    +

    +-x +Force the pattern to be anchored (it must start matching at the beginning of +the line) and in addition, require it to match the entire line. This is +equivalent to having ^ and $ characters at the start and end of each +alternative branch in the regular expression. +

    +
  • SEE ALSO +

    +pcre(3), Perl 5 documentation +

    +
  • DIAGNOSTICS +

    +Exit status is 0 if any matches were found, 1 if no matches were found, and 2 +for syntax errors or inacessible files (even if matches were found). +

    +
  • AUTHOR +

    +Philip Hazel <ph10@cam.ac.uk> +
    +Copyright (c) 1997-1999 University of Cambridge. diff --git a/srclib/pcre/doc/pgrep.txt b/srclib/pcre/doc/pgrep.txt new file mode 100644 index 00000000000..bcd08c0aaba --- /dev/null +++ b/srclib/pcre/doc/pgrep.txt @@ -0,0 +1,86 @@ +NAME + pgrep - a grep with Perl-compatible regular expressions. + + + +SYNOPSIS + pgrep [-Vchilnsvx] pattern [file] ... + + + +DESCRIPTION + pgrep searches files for character patterns, in the same way + as other grep commands do, but it uses the PCRE regular + expression library to support patterns that are compatible + with the regular expressions of Perl 5. See pcre(3) for a + full description of syntax and semantics. + + If no files are specified, pgrep reads the standard input. + By default, each line that matches the pattern is copied to + the standard output, and if there is more than one file, the + file name is printed before each line of output. However, + there are options that can change how pgrep behaves. + + Lines are limited to BUFSIZ characters. BUFSIZ is defined in + . The newline character is removed from the end of + each line before it is matched against the pattern. + + + +OPTIONS + -V Write the version number of the PCRE library being + used to the standard error stream. + + -c Do not print individual lines; instead just print + a count of the number of lines that would other- + wise have been printed. If several files are + given, a count is printed for each of them. + + -h Suppress printing of filenames when searching mul- + tiple files. + + -i Ignore upper/lower case distinctions during com- + parisons. + + -l Instead of printing lines from the files, just + print the names of the files containing lines that + would have been printed. Each file name is printed + once, on a separate line. + + -n Precede each line by its line number in the file. + + -s Work silently, that is, display nothing except + error messages. The exit status indicates whether + any matches were found. + + -v Invert the sense of the match, so that lines which + do not match the pattern are now the ones that are + found. + + -x Force the pattern to be anchored (it must start + matching at the beginning of the line) and in + addition, require it to match the entire line. + This is equivalent to having ^ and $ characters at + the start and end of each alternative branch in + the regular expression. + + + +SEE ALSO + pcre(3), Perl 5 documentation + + + + + +DIAGNOSTICS + Exit status is 0 if any matches were found, 1 if no matches + were found, and 2 for syntax errors or inacessible files + (even if matches were found). + + + +AUTHOR + Philip Hazel + Copyright (c) 1997-1999 University of Cambridge. + diff --git a/srclib/pcre/get.c b/srclib/pcre/get.c new file mode 100644 index 00000000000..035668e3010 --- /dev/null +++ b/srclib/pcre/get.c @@ -0,0 +1,189 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-1999 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- +*/ + +/* This module contains some convenience functions for extracting substrings +from the subject string after a regex match has succeeded. The original idea +for these functions came from Scott Wimer . */ + + +/* Include the internals header, which itself includes Standard C headers plus +the external pcre header. */ + +#include "internal.h" + + + +/************************************************* +* Copy captured string to given buffer * +*************************************************/ + +/* This function copies a single captured substring into a given buffer. +Note that we use memcpy() rather than strncpy() in case there are binary zeros +in the string. + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringnumber the number of the required substring + buffer where to put the substring + size the size of the buffer + +Returns: if successful: + the length of the copied string, not including the zero + that is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) buffer too small + PCRE_ERROR_NOSUBSTRING (-7) no such captured substring +*/ + +int +pcre_copy_substring(const char *subject, int *ovector, int stringcount, + int stringnumber, char *buffer, int size) +{ +int yield; +if (stringnumber < 0 || stringnumber >= stringcount) + return PCRE_ERROR_NOSUBSTRING; +stringnumber *= 2; +yield = ovector[stringnumber+1] - ovector[stringnumber]; +if (size < yield + 1) return PCRE_ERROR_NOMEMORY; +memcpy(buffer, subject + ovector[stringnumber], yield); +buffer[yield] = 0; +return yield; +} + + + +/************************************************* +* Copy all captured strings to new store * +*************************************************/ + +/* This function gets one chunk of store and builds a list of pointers and all +of the captured substrings in it. A NULL pointer is put on the end of the list. + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + listptr set to point to the list of pointers + +Returns: if successful: 0 + if not successful: + PCRE_ERROR_NOMEMORY (-6) failed to get store +*/ + +int +pcre_get_substring_list(const char *subject, int *ovector, int stringcount, + const char ***listptr) +{ +int i; +int size = sizeof(char *); +int double_count = stringcount * 2; +char **stringlist; +char *p; + +for (i = 0; i < double_count; i += 2) + size += sizeof(char *) + ovector[i+1] - ovector[i] + 1; + +stringlist = (char **)(pcre_malloc)(size); +if (stringlist == NULL) return PCRE_ERROR_NOMEMORY; + +*listptr = (const char **)stringlist; +p = (char *)(stringlist + stringcount + 1); + +for (i = 0; i < double_count; i += 2) + { + int len = ovector[i+1] - ovector[i]; + memcpy(p, subject + ovector[i], len); + *stringlist++ = p; + p += len; + *p++ = 0; + } + +*stringlist = NULL; +return 0; +} + + + +/************************************************* +* Copy captured string to new store * +*************************************************/ + +/* This function copies a single captured substring into a piece of new +store + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringnumber the number of the required substring + stringptr where to put a pointer to the substring + +Returns: if successful: + the length of the string, not including the zero that + is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) failed to get store + PCRE_ERROR_NOSUBSTRING (-7) substring not present +*/ + +int +pcre_get_substring(const char *subject, int *ovector, int stringcount, + int stringnumber, const char **stringptr) +{ +int yield; +char *substring; +if (stringnumber < 0 || stringnumber >= stringcount) + return PCRE_ERROR_NOSUBSTRING; +stringnumber *= 2; +yield = ovector[stringnumber+1] - ovector[stringnumber]; +substring = (char *)(pcre_malloc)(yield + 1); +if (substring == NULL) return PCRE_ERROR_NOMEMORY; +memcpy(substring, subject + ovector[stringnumber], yield); +substring[yield] = 0; +*stringptr = substring; +return yield; +} + +/* End of get.c */ diff --git a/srclib/pcre/internal.h b/srclib/pcre/internal.h new file mode 100644 index 00000000000..91ff3018f1f --- /dev/null +++ b/srclib/pcre/internal.h @@ -0,0 +1,360 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + + +/* This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-2000 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- +*/ + +/* This header contains definitions that are shared between the different +modules, but which are not relevant to the outside. */ + +/* Get the definitions provided by running "configure" */ + +#include "config.h" + +/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(), +define a macro for memmove() if HAVE_MEMMOVE is false. */ + +#if ! HAVE_MEMMOVE +#undef memmove /* some systems may have a macro */ +#define memmove(a, b, c) bcopy(b, a, c) +#endif + +/* Standard C headers plus the external interface definition */ + +#include +#include +#include +#include +#include +#include +#include "pcre.h" + +/* In case there is no definition of offsetof() provided - though any proper +Standard C system should have one. */ + +#ifndef offsetof +#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field)) +#endif + +/* These are the public options that can change during matching. */ + +#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL) + +/* Private options flags start at the most significant end of the four bytes, +but skip the top bit so we can use ints for convenience without getting tangled +with negative values. The public options defined in pcre.h start at the least +significant end. Make sure they don't overlap, though now that we have expanded +to four bytes there is plenty of space. */ + +#define PCRE_FIRSTSET 0x40000000 /* first_char is set */ +#define PCRE_REQCHSET 0x20000000 /* req_char is set */ +#define PCRE_STARTLINE 0x10000000 /* start after \n for multiline */ +#define PCRE_INGROUP 0x08000000 /* compiling inside a group */ +#define PCRE_ICHANGED 0x04000000 /* i option changes within regex */ + +/* Options for the "extra" block produced by pcre_study(). */ + +#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */ + +/* Masks for identifying the public options which are permitted at compile +time, run time or study time, respectively. */ + +#define PUBLIC_OPTIONS \ + (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ + PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY) + +#define PUBLIC_EXEC_OPTIONS \ + (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY) + +#define PUBLIC_STUDY_OPTIONS 0 /* None defined */ + +/* Magic number to provide a small check against being handed junk. */ + +#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ + +/* Miscellaneous definitions */ + +typedef int BOOL; + +#define FALSE 0 +#define TRUE 1 + +/* These are escaped items that aren't just an encoding of a particular data +value such as \n. They must have non-zero values, as check_escape() returns +their negation. Also, they must appear in the same order as in the opcode +definitions below, up to ESC_z. The final one must be ESC_REF as subsequent +values are used for \1, \2, \3, etc. There is a test in the code for an escape +greater than ESC_b and less than ESC_X to detect the types that may be +repeated. If any new escapes are put in-between that don't consume a character, +that code will have to change. */ + +enum { ESC_A = 1, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w, + ESC_Z, ESC_z, ESC_REF }; + +/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets +that extract substrings. Starting from 1 (i.e. after OP_END), the values up to +OP_EOD must correspond in order to the list of escapes immediately above. */ + +enum { + OP_END, /* End of pattern */ + + /* Values corresponding to backslashed metacharacters */ + + OP_SOD, /* Start of data: \A */ + OP_NOT_WORD_BOUNDARY, /* \B */ + OP_WORD_BOUNDARY, /* \b */ + OP_NOT_DIGIT, /* \D */ + OP_DIGIT, /* \d */ + OP_NOT_WHITESPACE, /* \S */ + OP_WHITESPACE, /* \s */ + OP_NOT_WORDCHAR, /* \W */ + OP_WORDCHAR, /* \w */ + OP_EODN, /* End of data or \n at end of data: \Z. */ + OP_EOD, /* End of data: \z */ + + OP_OPT, /* Set runtime options */ + OP_CIRC, /* Start of line - varies with multiline switch */ + OP_DOLL, /* End of line - varies with multiline switch */ + OP_ANY, /* Match any character */ + OP_CHARS, /* Match string of characters */ + OP_NOT, /* Match anything but the following char */ + + OP_STAR, /* The maximizing and minimizing versions of */ + OP_MINSTAR, /* all these opcodes must come in pairs, with */ + OP_PLUS, /* the minimizing one second. */ + OP_MINPLUS, /* This first set applies to single characters */ + OP_QUERY, + OP_MINQUERY, + OP_UPTO, /* From 0 to n matches */ + OP_MINUPTO, + OP_EXACT, /* Exactly n matches */ + + OP_NOTSTAR, /* The maximizing and minimizing versions of */ + OP_NOTMINSTAR, /* all these opcodes must come in pairs, with */ + OP_NOTPLUS, /* the minimizing one second. */ + OP_NOTMINPLUS, /* This first set applies to "not" single characters */ + OP_NOTQUERY, + OP_NOTMINQUERY, + OP_NOTUPTO, /* From 0 to n matches */ + OP_NOTMINUPTO, + OP_NOTEXACT, /* Exactly n matches */ + + OP_TYPESTAR, /* The maximizing and minimizing versions of */ + OP_TYPEMINSTAR, /* all these opcodes must come in pairs, with */ + OP_TYPEPLUS, /* the minimizing one second. These codes must */ + OP_TYPEMINPLUS, /* be in exactly the same order as those above. */ + OP_TYPEQUERY, /* This set applies to character types such as \d */ + OP_TYPEMINQUERY, + OP_TYPEUPTO, /* From 0 to n matches */ + OP_TYPEMINUPTO, + OP_TYPEEXACT, /* Exactly n matches */ + + OP_CRSTAR, /* The maximizing and minimizing versions of */ + OP_CRMINSTAR, /* all these opcodes must come in pairs, with */ + OP_CRPLUS, /* the minimizing one second. These codes must */ + OP_CRMINPLUS, /* be in exactly the same order as those above. */ + OP_CRQUERY, /* These are for character classes and back refs */ + OP_CRMINQUERY, + OP_CRRANGE, /* These are different to the three seta above. */ + OP_CRMINRANGE, + + OP_CLASS, /* Match a character class */ + OP_REF, /* Match a back reference */ + OP_RECURSE, /* Match this pattern recursively */ + + OP_ALT, /* Start of alternation */ + OP_KET, /* End of group that doesn't have an unbounded repeat */ + OP_KETRMAX, /* These two must remain together and in this */ + OP_KETRMIN, /* order. They are for groups the repeat for ever. */ + + /* The assertions must come before ONCE and COND */ + + OP_ASSERT, /* Positive lookahead */ + OP_ASSERT_NOT, /* Negative lookahead */ + OP_ASSERTBACK, /* Positive lookbehind */ + OP_ASSERTBACK_NOT, /* Negative lookbehind */ + OP_REVERSE, /* Move pointer back - used in lookbehind assertions */ + + /* ONCE and COND must come after the assertions, with ONCE first, as there's + a test for >= ONCE for a subpattern that isn't an assertion. */ + + OP_ONCE, /* Once matched, don't back up into the subpattern */ + OP_COND, /* Conditional group */ + OP_CREF, /* Used to hold an extraction string number */ + + OP_BRAZERO, /* These two must remain together and in this */ + OP_BRAMINZERO, /* order. */ + + OP_BRA /* This and greater values are used for brackets that + extract substrings. */ +}; + +/* The highest extraction number. This is limited by the number of opcodes +left after OP_BRA, i.e. 255 - OP_BRA. We actually set it somewhat lower. */ + +#define EXTRACT_MAX 99 + +/* The texts of compile-time error messages are defined as macros here so that +they can be accessed by the POSIX wrapper and converted into error codes. Yes, +I could have used error codes in the first place, but didn't feel like changing +just to accommodate the POSIX wrapper. */ + +#define ERR1 "\\ at end of pattern" +#define ERR2 "\\c at end of pattern" +#define ERR3 "unrecognized character follows \\" +#define ERR4 "numbers out of order in {} quantifier" +#define ERR5 "number too big in {} quantifier" +#define ERR6 "missing terminating ] for character class" +#define ERR7 "invalid escape sequence in character class" +#define ERR8 "range out of order in character class" +#define ERR9 "nothing to repeat" +#define ERR10 "operand of unlimited repeat could match the empty string" +#define ERR11 "internal error: unexpected repeat" +#define ERR12 "unrecognized character after (?" +#define ERR13 "too many capturing parenthesized sub-patterns" +#define ERR14 "missing )" +#define ERR15 "back reference to non-existent subpattern" +#define ERR16 "erroffset passed as NULL" +#define ERR17 "unknown option bit(s) set" +#define ERR18 "missing ) after comment" +#define ERR19 "too many sets of parentheses" +#define ERR20 "regular expression too large" +#define ERR21 "failed to get memory" +#define ERR22 "unmatched parentheses" +#define ERR23 "internal error: code overflow" +#define ERR24 "unrecognized character after (?<" +#define ERR25 "lookbehind assertion is not fixed length" +#define ERR26 "malformed number after (?(" +#define ERR27 "conditional group contains more than two branches" +#define ERR28 "assertion expected after (?(" +#define ERR29 "(?p must be followed by )" +#define ERR30 "unknown POSIX class name" +#define ERR31 "POSIX collating elements are not supported" + +/* All character handling must be done as unsigned characters. Otherwise there +are problems with top-bit-set characters and functions such as isspace(). +However, we leave the interface to the outside world as char *, because that +should make things easier for callers. We define a short type for unsigned char +to save lots of typing. I tried "uchar", but it causes problems on Digital +Unix, where it is defined in sys/types, so use "uschar" instead. */ + +typedef unsigned char uschar; + +/* The real format of the start of the pcre block; the actual code vector +runs on as long as necessary after the end. */ + +typedef struct real_pcre { + unsigned long int magic_number; + size_t size; + const unsigned char *tables; + unsigned long int options; + uschar top_bracket; + uschar top_backref; + uschar first_char; + uschar req_char; + uschar code[1]; +} real_pcre; + +/* The real format of the extra block returned by pcre_study(). */ + +typedef struct real_pcre_extra { + uschar options; + uschar start_bits[32]; +} real_pcre_extra; + + +/* Structure for passing "static" information around between the functions +doing the compiling, so that they are thread-safe. */ + +typedef struct compile_data { + const uschar *lcc; /* Points to lower casing table */ + const uschar *fcc; /* Points to case-flipping table */ + const uschar *cbits; /* Points to character type table */ + const uschar *ctypes; /* Points to table of type maps */ +} compile_data; + +/* Structure for passing "static" information around between the functions +doing the matching, so that they are thread-safe. */ + +typedef struct match_data { + int errorcode; /* As it says */ + int *offset_vector; /* Offset vector */ + int offset_end; /* One past the end */ + int offset_max; /* The maximum usable for return data */ + const uschar *lcc; /* Points to lower casing table */ + const uschar *ctypes; /* Points to table of type maps */ + BOOL offset_overflow; /* Set if too many extractions */ + BOOL notbol; /* NOTBOL flag */ + BOOL noteol; /* NOTEOL flag */ + BOOL endonly; /* Dollar not before final \n */ + BOOL notempty; /* Empty string match not wanted */ + const uschar *start_pattern; /* For use when recursing */ + const uschar *start_subject; /* Start of the subject string */ + const uschar *end_subject; /* End of the subject string */ + const uschar *start_match; /* Start of this match attempt */ + const uschar *end_match_ptr; /* Subject position at end match */ + int end_offset_top; /* Highwater mark at end of match */ +} match_data; + +/* Bit definitions for entries in the pcre_ctypes table. */ + +#define ctype_space 0x01 +#define ctype_letter 0x02 +#define ctype_digit 0x04 +#define ctype_xdigit 0x08 +#define ctype_word 0x10 /* alphameric or '_' */ +#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ + +/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set +of bits for a class map. Some classes are built by combining these tables. */ + +#define cbit_space 0 /* [:space:] or \s */ +#define cbit_xdigit 32 /* [:xdigit:] */ +#define cbit_digit 64 /* [:digit:] or \d */ +#define cbit_upper 96 /* [:upper:] */ +#define cbit_lower 128 /* [:lower:] */ +#define cbit_word 160 /* [:word:] or \w */ +#define cbit_graph 192 /* [:graph:] */ +#define cbit_print 224 /* [:print:] */ +#define cbit_punct 256 /* [:punct:] */ +#define cbit_cntrl 288 /* [:cntrl:] */ +#define cbit_length 320 /* Length of the cbits table */ + +/* Offsets of the various tables from the base tables pointer, and +total length. */ + +#define lcc_offset 0 +#define fcc_offset 256 +#define cbits_offset 512 +#define ctypes_offset (cbits_offset + cbit_length) +#define tables_length (ctypes_offset + 256) + +/* End of internal.h */ diff --git a/srclib/pcre/ltconfig b/srclib/pcre/ltconfig new file mode 100755 index 00000000000..a01334f9212 --- /dev/null +++ b/srclib/pcre/ltconfig @@ -0,0 +1,3078 @@ +#! /bin/sh + +# ltconfig - Create a system-specific libtool. +# Copyright (C) 1996-1999 Free Software Foundation, Inc. +# Originally by Gordon Matzigkeit , 1996 +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# A lot of this script is taken from autoconf-2.10. + +# Check that we are running under the correct shell. +SHELL=${CONFIG_SHELL-/bin/sh} +echo=echo +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then + # Yippee, $echo works! + : +else + # Restart under the correct shell. + exec "$SHELL" "$0" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat </dev/null`} + case X$UNAME in + *-DOS) PATH_SEPARATOR=';' ;; + *) PATH_SEPARATOR=':' ;; + esac +fi + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test "X${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi + +if test "X${echo_test_string+set}" != Xset; then + # find a string as large as possible, as long as the shell can cope with it + for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do + # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... + if (echo_test_string="`eval $cmd`") 2>/dev/null && + echo_test_string="`eval $cmd`" && + (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null; then + break + fi + done +fi + +if test "X`($echo '\t') 2>/dev/null`" != 'X\t' || + test "X`($echo "$echo_test_string") 2>/dev/null`" != X"$echo_test_string"; then + # The Solaris, AIX, and Digital Unix default echo programs unquote + # backslashes. This makes it impossible to quote backslashes using + # echo "$something" | sed 's/\\/\\\\/g' + # + # So, first we look for a working echo in the user's PATH. + + IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}" + for dir in $PATH /usr/ucb; do + if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && + test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && + test "X`($dir/echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then + echo="$dir/echo" + break + fi + done + IFS="$save_ifs" + + if test "X$echo" = Xecho; then + # We didn't find a better echo, so look for alternatives. + if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' && + test "X`(print -r "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then + # This shell has a builtin print -r that does the trick. + echo='print -r' + elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) && + test "X$CONFIG_SHELL" != X/bin/ksh; then + # If we have ksh, try running ltconfig again with it. + ORIGINAL_CONFIG_SHELL="${CONFIG_SHELL-/bin/sh}" + export ORIGINAL_CONFIG_SHELL + CONFIG_SHELL=/bin/ksh + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" --no-reexec ${1+"$@"} + else + # Try using printf. + echo='printf "%s\n"' + if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && + test "X`($echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then + # Cool, printf works + : + elif test "X`("$ORIGINAL_CONFIG_SHELL" "$0" --fallback-echo '\t') 2>/dev/null`" = 'X\t' && + test "X`("$ORIGINAL_CONFIG_SHELL" "$0" --fallback-echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then + CONFIG_SHELL="$ORIGINAL_CONFIG_SHELL" + export CONFIG_SHELL + SHELL="$CONFIG_SHELL" + export SHELL + echo="$CONFIG_SHELL $0 --fallback-echo" + elif test "X`("$CONFIG_SHELL" "$0" --fallback-echo '\t') 2>/dev/null`" = 'X\t' && + test "X`("$CONFIG_SHELL" "$0" --fallback-echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then + echo="$CONFIG_SHELL $0 --fallback-echo" + else + # maybe with a smaller string... + prev=: + + for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do + if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null; then + break + fi + prev="$cmd" + done + + if test "$prev" != 'sed 50q "$0"'; then + echo_test_string=`eval $prev` + export echo_test_string + exec "${ORIGINAL_CONFIG_SHELL}" "$0" ${1+"$@"} + else + # Oops. We lost completely, so just stick with echo. + echo=echo + fi + fi + fi + fi +fi + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='sed -e s/^X//' +sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# The name of this program. +progname=`$echo "X$0" | $Xsed -e 's%^.*/%%'` + +# Constants: +PROGRAM=ltconfig +PACKAGE=libtool +VERSION=1.3.4 +TIMESTAMP=" (1.385.2.196 1999/12/07 21:47:57)" +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +rm="rm -f" + +help="Try \`$progname --help' for more information." + +# Global variables: +default_ofile=libtool +can_build_shared=yes +enable_shared=yes +# All known linkers require a `.a' archive for static linking (except M$VC, +# which needs '.lib'). +enable_static=yes +enable_fast_install=yes +enable_dlopen=unknown +enable_win32_dll=no +ltmain= +silent= +srcdir= +ac_config_guess= +ac_config_sub= +host= +nonopt= +ofile="$default_ofile" +verify_host=yes +with_gcc=no +with_gnu_ld=no +need_locks=yes +ac_ext=c +objext=o +libext=a +exeext= +cache_file= + +old_AR="$AR" +old_CC="$CC" +old_CFLAGS="$CFLAGS" +old_CPPFLAGS="$CPPFLAGS" +old_LDFLAGS="$LDFLAGS" +old_LD="$LD" +old_LN_S="$LN_S" +old_LIBS="$LIBS" +old_NM="$NM" +old_RANLIB="$RANLIB" +old_DLLTOOL="$DLLTOOL" +old_OBJDUMP="$OBJDUMP" +old_AS="$AS" + +# Parse the command line options. +args= +prev= +for option +do + case "$option" in + -*=*) optarg=`echo "$option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + eval "$prev=\$option" + prev= + continue + fi + + case "$option" in + --help) cat <&2 + echo "$help" 1>&2 + exit 1 + ;; + + *) + if test -z "$ltmain"; then + ltmain="$option" + elif test -z "$host"; then +# This generates an unnecessary warning for sparc-sun-solaris4.1.3_U1 +# if test -n "`echo $option| sed 's/[-a-z0-9.]//g'`"; then +# echo "$progname: warning \`$option' is not a valid host type" 1>&2 +# fi + host="$option" + else + echo "$progname: too many arguments" 1>&2 + echo "$help" 1>&2 + exit 1 + fi ;; + esac +done + +if test -z "$ltmain"; then + echo "$progname: you must specify a LTMAIN file" 1>&2 + echo "$help" 1>&2 + exit 1 +fi + +if test ! -f "$ltmain"; then + echo "$progname: \`$ltmain' does not exist" 1>&2 + echo "$help" 1>&2 + exit 1 +fi + +# Quote any args containing shell metacharacters. +ltconfig_args= +for arg +do + case "$arg" in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ltconfig_args="$ltconfig_args '$arg'" ;; + *) ltconfig_args="$ltconfig_args $arg" ;; + esac +done + +# A relevant subset of AC_INIT. + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 5 compiler messages saved in config.log +# 6 checking for... messages and results +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>>./config.log + +# NLS nuisances. +# Only set LANG and LC_ALL to C if already set. +# These must not be set unconditionally because not all systems understand +# e.g. LANG=C (notably SCO). +if test "X${LC_ALL+set}" = Xset; then LC_ALL=C; export LC_ALL; fi +if test "X${LANG+set}" = Xset; then LANG=C; export LANG; fi + +if test -n "$cache_file" && test -r "$cache_file"; then + echo "loading cache $cache_file within ltconfig" + . $cache_file +fi + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + +if test -z "$srcdir"; then + # Assume the source directory is the same one as the path to LTMAIN. + srcdir=`$echo "X$ltmain" | $Xsed -e 's%/[^/]*$%%'` + test "$srcdir" = "$ltmain" && srcdir=. +fi + +trap "$rm conftest*; exit 1" 1 2 15 +if test "$verify_host" = yes; then + # Check for config.guess and config.sub. + ac_aux_dir= + for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/config.guess; then + ac_aux_dir=$ac_dir + break + fi + done + if test -z "$ac_aux_dir"; then + echo "$progname: cannot find config.guess in $srcdir $srcdir/.. $srcdir/../.." 1>&2 + echo "$help" 1>&2 + exit 1 + fi + ac_config_guess=$ac_aux_dir/config.guess + ac_config_sub=$ac_aux_dir/config.sub + + # Make sure we can run config.sub. + if $SHELL $ac_config_sub sun4 >/dev/null 2>&1; then : + else + echo "$progname: cannot run $ac_config_sub" 1>&2 + echo "$help" 1>&2 + exit 1 + fi + + echo $ac_n "checking host system type""... $ac_c" 1>&6 + + host_alias=$host + case "$host_alias" in + "") + if host_alias=`$SHELL $ac_config_guess`; then : + else + echo "$progname: cannot guess host type; you must specify one" 1>&2 + echo "$help" 1>&2 + exit 1 + fi ;; + esac + host=`$SHELL $ac_config_sub $host_alias` + echo "$ac_t$host" 1>&6 + + # Make sure the host verified. + test -z "$host" && exit 1 + +elif test -z "$host"; then + echo "$progname: you must specify a host type if you use \`--no-verify'" 1>&2 + echo "$help" 1>&2 + exit 1 +else + host_alias=$host +fi + +# Transform linux* to *-*-linux-gnu*, to support old configure scripts. +case "$host_os" in +linux-gnu*) ;; +linux*) host=`echo $host | sed 's/^\(.*-.*-linux\)\(.*\)$/\1-gnu\2/'` +esac + +host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +case "$host_os" in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR cru $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +# Set a sane default for `AR'. +test -z "$AR" && AR=ar + +# Set a sane default for `OBJDUMP'. +test -z "$OBJDUMP" && OBJDUMP=objdump + +# If RANLIB is not set, then run the test. +if test "${RANLIB+set}" != "set"; then + result=no + + echo $ac_n "checking for ranlib... $ac_c" 1>&6 + IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}" + for dir in $PATH; do + test -z "$dir" && dir=. + if test -f $dir/ranlib || test -f $dir/ranlib$ac_exeext; then + RANLIB="ranlib" + result="ranlib" + break + fi + done + IFS="$save_ifs" + + echo "$ac_t$result" 1>&6 +fi + +if test -n "$RANLIB"; then + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" + old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds" +fi + +# Set sane defaults for `DLLTOOL', `OBJDUMP', and `AS', used on cygwin. +test -z "$DLLTOOL" && DLLTOOL=dlltool +test -z "$OBJDUMP" && OBJDUMP=objdump +test -z "$AS" && AS=as + +# Check to see if we are using GCC. +if test "$with_gcc" != yes || test -z "$CC"; then + # If CC is not set, then try to find GCC or a usable CC. + if test -z "$CC"; then + echo $ac_n "checking for gcc... $ac_c" 1>&6 + IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}" + for dir in $PATH; do + test -z "$dir" && dir=. + if test -f $dir/gcc || test -f $dir/gcc$ac_exeext; then + CC="gcc" + break + fi + done + IFS="$save_ifs" + + if test -n "$CC"; then + echo "$ac_t$CC" 1>&6 + else + echo "$ac_t"no 1>&6 + fi + fi + + # Not "gcc", so try "cc", rejecting "/usr/ucb/cc". + if test -z "$CC"; then + echo $ac_n "checking for cc... $ac_c" 1>&6 + IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}" + cc_rejected=no + for dir in $PATH; do + test -z "$dir" && dir=. + if test -f $dir/cc || test -f $dir/cc$ac_exeext; then + if test "$dir/cc" = "/usr/ucb/cc"; then + cc_rejected=yes + continue + fi + CC="cc" + break + fi + done + IFS="$save_ifs" + if test $cc_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same name, so the bogon will be chosen + # first if we set CC to just the name; use the full file name. + shift + set dummy "$dir/cc" "$@" + shift + CC="$@" + fi + fi + + if test -n "$CC"; then + echo "$ac_t$CC" 1>&6 + else + echo "$ac_t"no 1>&6 + fi + + if test -z "$CC"; then + echo "$progname: error: no acceptable cc found in \$PATH" 1>&2 + exit 1 + fi + fi + + # Now see if the compiler is really GCC. + with_gcc=no + echo $ac_n "checking whether we are using GNU C... $ac_c" 1>&6 + echo "$progname:581: checking whether we are using GNU C" >&5 + + $rm conftest.c + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + with_gcc=yes + fi + $rm conftest.c + echo "$ac_t$with_gcc" 1>&6 +fi + +# Allow CC to be a program name with arguments. +set dummy $CC +compiler="$2" + +echo $ac_n "checking for object suffix... $ac_c" 1>&6 +$rm conftest* +echo 'int i = 1;' > conftest.c +echo "$progname:603: checking for object suffix" >& 5 +if { (eval echo $progname:604: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; }; then + # Append any warnings to the config.log. + cat conftest.err 1>&5 + + for ac_file in conftest.*; do + case $ac_file in + *.c) ;; + *) objext=`echo $ac_file | sed -e s/conftest.//` ;; + esac + done +else + cat conftest.err 1>&5 + echo "$progname: failed program was:" >&5 + cat conftest.c >&5 +fi +$rm conftest* +echo "$ac_t$objext" 1>&6 + +echo $ac_n "checking for executable suffix... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_exeext'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_exeext="no" + $rm conftest* + echo 'main () { return 0; }' > conftest.c + echo "$progname:629: checking for executable suffix" >& 5 + if { (eval echo $progname:630: \"$ac_link\") 1>&5; (eval $ac_link) 2>conftest.err; }; then + # Append any warnings to the config.log. + cat conftest.err 1>&5 + + for ac_file in conftest.*; do + case $ac_file in + *.c | *.err | *.$objext ) ;; + *) ac_cv_exeext=.`echo $ac_file | sed -e s/conftest.//` ;; + esac + done + else + cat conftest.err 1>&5 + echo "$progname: failed program was:" >&5 + cat conftest.c >&5 + fi + $rm conftest* +fi +if test "X$ac_cv_exeext" = Xno; then + exeext="" +else + exeext="$ac_cv_exeext" +fi +echo "$ac_t$ac_cv_exeext" 1>&6 + +echo $ac_n "checking for $compiler option to produce PIC... $ac_c" 1>&6 +pic_flag= +special_shlib_compile_flags= +wl= +link_static_flag= +no_builtin_flag= + +if test "$with_gcc" = yes; then + wl='-Wl,' + link_static_flag='-static' + + case "$host_os" in + beos* | irix5* | irix6* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + aix*) + # Below there is a dirty hack to force normal static linking with -ldl + # The problem is because libdl dynamically linked with both libc and + # libC (AIX C++ library), which obviously doesn't included in libraries + # list by gcc. This cause undefined symbols with -static flags. + # This hack allows C programs to be linked with "-static -ldl", but + # we not sure about C++ programs. + link_static_flag="$link_static_flag ${wl}-lC" + ;; + cygwin* | mingw* | os2*) + # We can build DLLs from non-PIC. + ;; + amigaos*) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + pic_flag='-m68020 -resident32 -malways-restore-a4' + ;; + sysv4*MP*) + if test -d /usr/nec; then + pic_flag=-Kconform_pic + fi + ;; + *) + pic_flag='-fPIC' + ;; + esac +else + # PORTME Check for PIC flags for the system compiler. + case "$host_os" in + aix3* | aix4*) + # All AIX code is PIC. + link_static_flag='-bnso -bI:/lib/syscalls.exp' + ;; + + hpux9* | hpux10* | hpux11*) + # Is there a better link_static_flag that works with the bundled CC? + wl='-Wl,' + link_static_flag="${wl}-a ${wl}archive" + pic_flag='+Z' + ;; + + irix5* | irix6*) + wl='-Wl,' + link_static_flag='-non_shared' + # PIC (with -KPIC) is the default. + ;; + + cygwin* | mingw* | os2*) + # We can build DLLs from non-PIC. + ;; + + osf3* | osf4* | osf5*) + # All OSF/1 code is PIC. + wl='-Wl,' + link_static_flag='-non_shared' + ;; + + sco3.2v5*) + pic_flag='-Kpic' + link_static_flag='-dn' + special_shlib_compile_flags='-belf' + ;; + + solaris*) + pic_flag='-KPIC' + link_static_flag='-Bstatic' + wl='-Wl,' + ;; + + sunos4*) + pic_flag='-PIC' + link_static_flag='-Bstatic' + wl='-Qoption ld ' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + pic_flag='-KPIC' + link_static_flag='-Bstatic' + wl='-Wl,' + ;; + + uts4*) + pic_flag='-pic' + link_static_flag='-Bstatic' + ;; + sysv4*MP*) + if test -d /usr/nec ;then + pic_flag='-Kconform_pic' + link_static_flag='-Bstatic' + fi + ;; + *) + can_build_shared=no + ;; + esac +fi + +if test -n "$pic_flag"; then + echo "$ac_t$pic_flag" 1>&6 + + # Check to make sure the pic_flag actually works. + echo $ac_n "checking if $compiler PIC flag $pic_flag works... $ac_c" 1>&6 + $rm conftest* + echo "int some_variable = 0;" > conftest.c + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $pic_flag -DPIC" + echo "$progname:776: checking if $compiler PIC flag $pic_flag works" >&5 + if { (eval echo $progname:777: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; } && test -s conftest.$objext; then + # Append any warnings to the config.log. + cat conftest.err 1>&5 + + case "$host_os" in + hpux9* | hpux10* | hpux11*) + # On HP-UX, both CC and GCC only warn that PIC is supported... then they + # create non-PIC objects. So, if there were any warnings, we assume that + # PIC is not supported. + if test -s conftest.err; then + echo "$ac_t"no 1>&6 + can_build_shared=no + pic_flag= + else + echo "$ac_t"yes 1>&6 + pic_flag=" $pic_flag" + fi + ;; + *) + echo "$ac_t"yes 1>&6 + pic_flag=" $pic_flag" + ;; + esac + else + # Append any errors to the config.log. + cat conftest.err 1>&5 + can_build_shared=no + pic_flag= + echo "$ac_t"no 1>&6 + fi + CFLAGS="$save_CFLAGS" + $rm conftest* +else + echo "$ac_t"none 1>&6 +fi + +# Check to see if options -o and -c are simultaneously supported by compiler +echo $ac_n "checking if $compiler supports -c -o file.o... $ac_c" 1>&6 +$rm -r conftest 2>/dev/null +mkdir conftest +cd conftest +$rm conftest* +echo "int some_variable = 0;" > conftest.c +mkdir out +# According to Tom Tromey, Ian Lance Taylor reported there are C compilers +# that will create temporary files in the current directory regardless of +# the output directory. Thus, making CWD read-only will cause this test +# to fail, enabling locking or at least warning the user not to do parallel +# builds. +chmod -w . +save_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -o out/conftest2.o" +echo "$progname:829: checking if $compiler supports -c -o file.o" >&5 +if { (eval echo $progname:830: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.o; then + + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s out/conftest.err; then + echo "$ac_t"no 1>&6 + compiler_c_o=no + else + echo "$ac_t"yes 1>&6 + compiler_c_o=yes + fi +else + # Append any errors to the config.log. + cat out/conftest.err 1>&5 + compiler_c_o=no + echo "$ac_t"no 1>&6 +fi +CFLAGS="$save_CFLAGS" +chmod u+w . +$rm conftest* out/* +rmdir out +cd .. +rmdir conftest +$rm -r conftest 2>/dev/null + +if test x"$compiler_c_o" = x"yes"; then + # Check to see if we can write to a .lo + echo $ac_n "checking if $compiler supports -c -o file.lo... $ac_c" 1>&6 + $rm conftest* + echo "int some_variable = 0;" > conftest.c + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -c -o conftest.lo" + echo "$progname:862: checking if $compiler supports -c -o file.lo" >&5 +if { (eval echo $progname:863: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; } && test -s conftest.lo; then + + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + echo "$ac_t"no 1>&6 + compiler_o_lo=no + else + echo "$ac_t"yes 1>&6 + compiler_o_lo=yes + fi + else + # Append any errors to the config.log. + cat conftest.err 1>&5 + compiler_o_lo=no + echo "$ac_t"no 1>&6 + fi + CFLAGS="$save_CFLAGS" + $rm conftest* +else + compiler_o_lo=no +fi + +# Check to see if we can do hard links to lock some files if needed +hard_links="nottested" +if test "$compiler_c_o" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + echo $ac_n "checking if we can lock with hard links... $ac_c" 1>&6 + hard_links=yes + $rm conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + echo "$ac_t$hard_links" 1>&6 + $rm conftest* + if test "$hard_links" = no; then + echo "*** WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2 + need_locks=warn + fi +else + need_locks=no +fi + +if test "$with_gcc" = yes; then + # Check to see if options -fno-rtti -fno-exceptions are supported by compiler + echo $ac_n "checking if $compiler supports -fno-rtti -fno-exceptions ... $ac_c" 1>&6 + $rm conftest* + echo "int some_variable = 0;" > conftest.c + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fno-rtti -fno-exceptions -c conftest.c" + echo "$progname:914: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 + if { (eval echo $progname:915: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; } && test -s conftest.o; then + + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + echo "$ac_t"no 1>&6 + compiler_rtti_exceptions=no + else + echo "$ac_t"yes 1>&6 + compiler_rtti_exceptions=yes + fi + else + # Append any errors to the config.log. + cat conftest.err 1>&5 + compiler_rtti_exceptions=no + echo "$ac_t"no 1>&6 + fi + CFLAGS="$save_CFLAGS" + $rm conftest* + + if test "$compiler_rtti_exceptions" = "yes"; then + no_builtin_flag=' -fno-builtin -fno-rtti -fno-exceptions' + else + no_builtin_flag=' -fno-builtin' + fi + +fi + +# Check for any special shared library compilation flags. +if test -n "$special_shlib_compile_flags"; then + echo "$progname: warning: \`$CC' requires \`$special_shlib_compile_flags' to build shared libraries" 1>&2 + if echo "$old_CC $old_CFLAGS " | egrep -e "[ ]$special_shlib_compile_flags[ ]" >/dev/null; then : + else + echo "$progname: add \`$special_shlib_compile_flags' to the CC or CFLAGS env variable and reconfigure" 1>&2 + can_build_shared=no + fi +fi + +echo $ac_n "checking if $compiler static flag $link_static_flag works... $ac_c" 1>&6 +$rm conftest* +echo 'main(){return(0);}' > conftest.c +save_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS $link_static_flag" +echo "$progname:958: checking if $compiler static flag $link_static_flag works" >&5 +if { (eval echo $progname:959: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + echo "$ac_t$link_static_flag" 1>&6 +else + echo "$ac_t"none 1>&6 + link_static_flag= +fi +LDFLAGS="$save_LDFLAGS" +$rm conftest* + +if test -z "$LN_S"; then + # Check to see if we can use ln -s, or we need hard links. + echo $ac_n "checking whether ln -s works... $ac_c" 1>&6 + $rm conftest.dat + if ln -s X conftest.dat 2>/dev/null; then + $rm conftest.dat + LN_S="ln -s" + else + LN_S=ln + fi + if test "$LN_S" = "ln -s"; then + echo "$ac_t"yes 1>&6 + else + echo "$ac_t"no 1>&6 + fi +fi + +# Make sure LD is an absolute path. +if test -z "$LD"; then + ac_prog=ld + if test "$with_gcc" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + echo $ac_n "checking for ld used by GCC... $ac_c" 1>&6 + echo "$progname:991: checking for ld used by GCC" >&5 + ac_prog=`($CC -print-prog-name=ld) 2>&5` + case "$ac_prog" in + # Accept absolute paths. + [\\/]* | [A-Za-z]:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the path of ld + ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we are not using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac + elif test "$with_gnu_ld" = yes; then + echo $ac_n "checking for GNU ld... $ac_c" 1>&6 + echo "$progname:1015: checking for GNU ld" >&5 + else + echo $ac_n "checking for non-GNU ld""... $ac_c" 1>&6 + echo "$progname:1018: checking for non-GNU ld" >&5 + fi + + if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + if "$LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then + test "$with_gnu_ld" != no && break + else + test "$with_gnu_ld" != yes && break + fi + fi + done + IFS="$ac_save_ifs" + fi + + if test -n "$LD"; then + echo "$ac_t$LD" 1>&6 + else + echo "$ac_t"no 1>&6 + fi + + if test -z "$LD"; then + echo "$progname: error: no acceptable ld found in \$PATH" 1>&2 + exit 1 + fi +fi + +# Check to see if it really is or is not GNU ld. +echo $ac_n "checking if the linker ($LD) is GNU ld... $ac_c" 1>&6 +# I'd rather use --version here, but apparently some GNU ld's only accept -v. +if $LD -v 2>&1 &5; then + with_gnu_ld=yes +else + with_gnu_ld=no +fi +echo "$ac_t$with_gnu_ld" 1>&6 + +# See if the linker supports building shared libraries. +echo $ac_n "checking whether the linker ($LD) supports shared libraries... $ac_c" 1>&6 + +allow_undefined_flag= +no_undefined_flag= +need_lib_prefix=unknown +need_version=unknown +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +archive_cmds= +archive_expsym_cmds= +old_archive_from_new_cmds= +export_dynamic_flag_spec= +whole_archive_flag_spec= +thread_safe_flag_spec= +hardcode_libdir_flag_spec= +hardcode_libdir_separator= +hardcode_direct=no +hardcode_minus_L=no +hardcode_shlibpath_var=unsupported +runpath_var= +always_export_symbols=no +export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | sed '\''s/.* //'\'' | sort | uniq > $export_symbols' +# include_expsyms should be a list of space-separated symbols to be *always* +# included in the symbol list +include_expsyms= +# exclude_expsyms can be an egrep regular expression of symbols to exclude +# it will be wrapped by ` (' and `)$', so one must not match beginning or +# end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', +# as well as any symbol that contains `d'. +exclude_expsyms="_GLOBAL_OFFSET_TABLE_" +# Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out +# platforms (ab)use it in PIC code, but their linkers get confused if +# the symbol is explicitly referenced. Since portable code cannot +# rely on this symbol name, it's probably fine to never include it in +# preloaded symbol tables. + +case "$host_os" in +cygwin* | mingw*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$with_gcc" != yes; then + with_gnu_ld=no + fi + ;; + +esac + +ld_shlibs=yes +if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # See if GNU ld supports shared libraries. + case "$host_os" in + aix3* | aix4*) + # On AIX, the GNU linker is very broken + ld_shlibs=no + cat <&2 + +*** Warning: the GNU linker, at least up to release 2.9.1, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to modify your PATH +*** so that a non-GNU linker is found, and then restart. + +EOF + ;; + + amigaos*) + archive_cmds='$rm $objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $objdir/a2ixlibrary.data~$AR cru $lib $libobjs~$RANLIB $lib~(cd $objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + + # Samuel A. Falvo II reports + # that the semantics of dynamic libraries on AmigaOS, at least up + # to version 4, is to share data among multiple programs linked + # with the same dynamic library. Since this doesn't match the + # behavior of shared libraries on other platforms, we can use + # them. + ld_shlibs=no + ;; + + beos*) + if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $linkopts ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw*) + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + allow_undefined_flag=unsupported + always_export_symbols=yes + + # Extract the symbol export list from an `--export-all' def file, + # then regenerate the def file from the symbol export list, so that + # the compiled dll only exports the symbol export list. + export_symbols_cmds='test -f $objdir/$soname-ltdll.c || sed -e "/^# \/\* ltdll\.c starts here \*\//,/^# \/\* ltdll.c ends here \*\// { s/^# //; p; }" -e d < $0 > $objdir/$soname-ltdll.c~ + test -f $objdir/$soname-ltdll.$objext || (cd $objdir && $CC -c $soname-ltdll.c)~ + $DLLTOOL --export-all --exclude-symbols DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12 --output-def $objdir/$soname-def $objdir/$soname-ltdll.$objext $libobjs $convenience~ + sed -e "1,/EXPORTS/d" -e "s/ @ [0-9]* ; *//" < $objdir/$soname-def > $export_symbols' + + archive_expsym_cmds='echo EXPORTS > $objdir/$soname-def~ + _lt_hint=1; + for symbol in `cat $export_symbols`; do + echo " \$symbol @ \$_lt_hint ; " >> $objdir/$soname-def; + _lt_hint=`expr 1 + \$_lt_hint`; + done~ + test -f $objdir/$soname-ltdll.c || sed -e "/^# \/\* ltdll\.c starts here \*\//,/^# \/\* ltdll.c ends here \*\// { s/^# //; p; }" -e d < $0 > $objdir/$soname-ltdll.c~ + test -f $objdir/$soname-ltdll.$objext || (cd $objdir && $CC -c $soname-ltdll.c)~ + $CC -Wl,--base-file,$objdir/$soname-base -Wl,--dll -nostartfiles -Wl,-e,__cygwin_dll_entry@12 -o $lib $objdir/$soname-ltdll.$objext $libobjs $deplibs $linkopts~ + $DLLTOOL --as=$AS --dllname $soname --exclude-symbols DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12 --def $objdir/$soname-def --base-file $objdir/$soname-base --output-exp $objdir/$soname-exp~ + $CC -Wl,--base-file,$objdir/$soname-base $objdir/$soname-exp -Wl,--dll -nostartfiles -Wl,-e,__cygwin_dll_entry@12 -o $lib $objdir/$soname-ltdll.$objext $libobjs $deplibs $linkopts~ + $DLLTOOL --as=$AS --dllname $soname --exclude-symbols DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12 --def $objdir/$soname-def --base-file $objdir/$soname-base --output-exp $objdir/$soname-exp~ + $CC $objdir/$soname-exp -Wl,--dll -nostartfiles -Wl,-e,__cygwin_dll_entry@12 -o $lib $objdir/$soname-ltdll.$objext $libobjs $deplibs $linkopts' + + old_archive_from_new_cmds='$DLLTOOL --as=$AS --dllname $soname --def $objdir/$soname-def --output-lib $objdir/$libname.a' + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + archive_cmds='$LD -Bshareable $libobjs $deplibs $linkopts -o $lib' + # can we support soname and/or expsyms with a.out? -oliva + fi + ;; + + solaris* | sysv5*) + if $LD -v 2>&1 | egrep 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +EOF + elif $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linkopts' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test "$ld_shlibs" = yes; then + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec='${wl}--export-dynamic' + case $host_os in + cygwin* | mingw*) + # dlltool doesn't understand --whole-archive et. al. + whole_archive_flag_spec= + ;; + *) + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | egrep 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec= + fi + ;; + esac + fi +else + # PORTME fill in a description of your system's linker (not GNU ld) + case "$host_os" in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $objdir/$soname $libobjs $deplibs $linkopts -bE:$export_symbols -T512 -H512 -bM:SRE~$AR cru $lib $objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$with_gcc" = yes && test -z "$link_static_flag"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix4*) + hardcode_libdir_flag_spec='${wl}-b ${wl}nolibpath ${wl}-b ${wl}libpath:$libdir:/usr/lib:/lib' + hardcode_libdir_separator=':' + if test "$with_gcc" = yes; then + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + hardcode_direct=yes + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + shared_flag='-shared' + else + shared_flag='${wl}-bM:SRE' + hardcode_direct=yes + fi + allow_undefined_flag=' ${wl}-berok' + archive_cmds="\$CC $shared_flag"' -o $objdir/$soname $libobjs $deplibs $linkopts ${wl}-bexpall ${wl}-bnoentry${allow_undefined_flag}' + archive_expsym_cmds="\$CC $shared_flag"' -o $objdir/$soname $libobjs $deplibs $linkopts ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}' + case "$host_os" in aix4.[01]|aix4.[01].*) + # According to Greg Wooledge, -bexpall is only supported from AIX 4.2 on + always_export_symbols=yes ;; + esac + ;; + + amigaos*) + archive_cmds='$rm $objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $objdir/a2ixlibrary.data~$AR cru $lib $libobjs~$RANLIB $lib~(cd $objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + # see comment about different semantics on the GNU ld section + ld_shlibs=no + ;; + + cygwin* | mingw*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $linkopts `echo "$deplibs" | sed -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib /OUT:$oldlib$oldobjs' + fix_srcfile_path='`cygpath -w $srcfile`' + ;; + + freebsd1*) + ld_shlibs=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd*) + archive_cmds='$CC -shared -o $lib $libobjs $deplibs $linkopts' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9* | hpux10* | hpux11*) + case "$host_os" in + hpux9*) archive_cmds='$rm $objdir/$soname~$LD -b +b $install_libdir -o $objdir/$soname $libobjs $deplibs $linkopts~test $objdir/$soname = $lib || mv $objdir/$soname $lib' ;; + *) archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linkopts' ;; + esac + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_minus_L=yes # Not in the search PATH, but as the default + # location of the library. + export_dynamic_flag_spec='${wl}-E' + ;; + + irix5* | irix6*) + if test "$with_gcc" = yes; then + archive_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + else + archive_cmds='$LD -shared $libobjs $deplibs $linkopts -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + fi + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linkopts' # ELF + fi + hardcode_libdir_flag_spec='${wl}-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + openbsd*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $objdir/$libname.def~$echo DATA >> $objdir/$libname.def~$echo " SINGLE NONSHARED" >> $objdir/$libname.def~$echo EXPORTS >> $objdir/$libname.def~emxexp $libobjs >> $objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $linkopts $objdir/$libname.def' + old_archive_from_new_cmds='emximp -o $objdir/$libname.a $objdir/$libname.def' + ;; + + osf3*) + if test "$with_gcc" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $linkopts ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linkopts -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + fi + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # As osf3* with the addition of the -msym flag + if test "$with_gcc" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $linkopts ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linkopts -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + fi + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + sco3.2v5*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ;; + + solaris*) + no_undefined_flag=' -z text' + # $CC -shared without GNU ld will not create a library from C++ + # object files and a static libstdc++, better avoid it by now + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linkopts' + archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linkopts~$rm $lib.exp' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case "$host_os" in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linkopts' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts' + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv5*) + no_undefined_flag=' -z text' + # $CC -shared without GNU ld will not create a library from C++ + # object files and a static libstdc++, better avoid it by now + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linkopts' + archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linkopts~$rm $lib.exp' + hardcode_libdir_flag_spec= + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4.2uw2*) + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linkopts' + hardcode_direct=yes + hardcode_minus_L=no + hardcode_shlibpath_var=no + hardcode_runpath_var=yes + runpath_var=LD_RUN_PATH + ;; + + unixware7*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts' + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac +fi +echo "$ac_t$ld_shlibs" 1>&6 +test "$ld_shlibs" = no && can_build_shared=no + +if test -z "$NM"; then + echo $ac_n "checking for BSD-compatible nm... $ac_c" 1>&6 + case "$NM" in + [\\/]* | [A-Za-z]:[\\/]*) ;; # Let the user override the test with a path. + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}" + for ac_dir in $PATH /usr/ucb /usr/ccs/bin /bin; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/nm || test -f $ac_dir/nm$ac_exeext; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + if ($ac_dir/nm -B /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then + NM="$ac_dir/nm -B" + break + elif ($ac_dir/nm -p /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then + NM="$ac_dir/nm -p" + break + else + NM=${NM="$ac_dir/nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + fi + fi + done + IFS="$ac_save_ifs" + test -z "$NM" && NM=nm + ;; + esac + echo "$ac_t$NM" 1>&6 +fi + +# Check for command to grab the raw symbol name followed by C symbol from nm. +echo $ac_n "checking command to parse $NM output... $ac_c" 1>&6 + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRST]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Transform the above into a raw symbol and a C symbol. +symxfrm='\1 \2\3 \3' + +# Transform an extracted symbol line into a proper C declaration +global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern char \1;/p'" + +# Define system-specific variables. +case "$host_os" in +aix*) + symcode='[BCDT]' + ;; +cygwin* | mingw*) + symcode='[ABCDGISTW]' + ;; +hpux*) # Its linker distinguishes data from code symbols + global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern char \1();/p' -e 's/^. .* \(.*\)$/extern char \1;/p'" + ;; +irix*) + symcode='[BCDEGRST]' + ;; +solaris*) + symcode='[BDT]' + ;; +sysv4) + symcode='[DFNSTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +if $NM -V 2>&1 | egrep '(GNU|with BFD)' > /dev/null; then + symcode='[ABCDGISTW]' +fi + +# Try without a prefix undercore, then with it. +for ac_symprfx in "" "_"; do + + # Write the raw and C identifiers. + global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode\)[ ][ ]*\($ac_symprfx\)$sympat$/$symxfrm/p'" + + # Check to see that the pipe works correctly. + pipe_works=no + $rm conftest* + cat > conftest.c <&5 + if { (eval echo $progname:1636: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; } && test -s conftest.$objext; then + # Now try to grab the symbols. + nlist=conftest.nm + if { echo "$progname:1639: eval \"$NM conftest.$objext | $global_symbol_pipe > $nlist\"" >&5; eval "$NM conftest.$objext | $global_symbol_pipe > $nlist 2>&5"; } && test -s "$nlist"; then + + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if egrep ' nm_test_var$' "$nlist" >/dev/null; then + if egrep ' nm_test_func$' "$nlist" >/dev/null; then + cat < conftest.c +#ifdef __cplusplus +extern "C" { +#endif + +EOF + # Now generate the symbol file. + eval "$global_symbol_to_cdecl"' < "$nlist" >> conftest.c' + + cat <> conftest.c +#if defined (__STDC__) && __STDC__ +# define lt_ptr_t void * +#else +# define lt_ptr_t char * +# define const +#endif + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + lt_ptr_t address; +} +lt_preloaded_symbols[] = +{ +EOF + sed 's/^. \(.*\) \(.*\)$/ {"\2", (lt_ptr_t) \&\2},/' < "$nlist" >> conftest.c + cat <<\EOF >> conftest.c + {0, (lt_ptr_t) 0} +}; + +#ifdef __cplusplus +} +#endif +EOF + # Now try linking the two files. + mv conftest.$objext conftstm.$objext + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="conftstm.$objext" + CFLAGS="$CFLAGS$no_builtin_flag" + if { (eval echo $progname:1691: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + pipe_works=yes + else + echo "$progname: failed program was:" >&5 + cat conftest.c >&5 + fi + LIBS="$save_LIBS" + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.c >&5 + fi + $rm conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + global_symbol_pipe= + fi +done +if test "$pipe_works" = yes; then + echo "${ac_t}ok" 1>&6 +else + echo "${ac_t}failed" 1>&6 +fi + +if test -z "$global_symbol_pipe"; then + global_symbol_to_cdecl= +fi + +# Check hardcoding attributes. +echo $ac_n "checking how to hardcode library paths into programs... $ac_c" 1>&6 +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || \ + test -n "$runpath_var"; then + + # We can hardcode non-existant directories. + if test "$hardcode_direct" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$hardcode_shlibpath_var" != no && + test "$hardcode_minus_L" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +echo "$ac_t$hardcode_action" 1>&6 + + +reload_flag= +reload_cmds='$LD$reload_flag -o $output$reload_objs' +echo $ac_n "checking for $LD option to reload object files... $ac_c" 1>&6 +# PORTME Some linkers may need a different reload flag. +reload_flag='-r' +echo "$ac_t$reload_flag" 1>&6 +test -n "$reload_flag" && reload_flag=" $reload_flag" + +# PORTME Fill in your ld.so characteristics +library_names_spec= +libname_spec='lib$name' +soname_spec= +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +file_magic_cmd= +file_magic_test_file= +deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# `unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [regex]' -- check by looking for files in library path +# which responds to the $file_magic_cmd with a given egrep regex. +# If you have `file' or equivalent on your system and you're not sure +# whether `pass_all' will *always* work, you probably want this one. +echo $ac_n "checking dynamic linker characteristics... $ac_c" 1>&6 +case "$host_os" in +aix3*) + version_type=linux + library_names_spec='${libname}${release}.so$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}.so$major' + ;; + +aix4*) + version_type=linux + # AIX has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + # We preserve .a as extension for shared libraries though AIX4.2 + # and later linker supports .so + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.a' + shlibpath_var=LIBPATH + deplibs_check_method=pass_all + ;; + +amigaos*) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "(cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a)"; (cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a) || exit 1; done' + ;; + +beos*) + library_names_spec='${libname}.so' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + deplibs_check_method=pass_all + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + +bsdi4*) + version_type=linux + need_version=no + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so' + soname_spec='${libname}${release}.so$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + file_magic_cmd=/usr/bin/file + file_magic_test_file=/shlib/libc.so + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + export_dynamic_flag_spec=-rdynamic + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw*) + version_type=windows + need_version=no + need_lib_prefix=no + if test "$with_gcc" = yes; then + library_names_spec='${libname}`echo ${release} | sed -e 's/[.]/-/g'`${versuffix}.dll $libname.a' + else + library_names_spec='${libname}`echo ${release} | sed -e 's/[.]/-/g'`${versuffix}.dll $libname.lib' + fi + dynamic_linker='Win32 ld.exe' + deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' + file_magic_cmd='${OBJDUMP} -f' + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + +freebsd1*) + dynamic_linker=no + ;; + +freebsd*) + objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout` + version_type=freebsd-$objformat + case "$version_type" in + freebsd-elf*) + deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB shared object' + file_magic_cmd=/usr/bin/file + file_magic_test_file=`echo /usr/lib/libc.so*` + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so $libname.so' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + deplibs_check_method=unknown + library_names_spec='${libname}${release}.so$versuffix $libname.so$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case "$host_os" in + freebsd2* | freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + ;; + *) # from 3.2 on + shlibpath_overrides_runpath=no + ;; + esac + ;; + +gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so${major} ${libname}.so' + soname_spec='${libname}${release}.so$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + dynamic_linker="$host_os dld.sl" + version_type=sunos + need_lib_prefix=no + need_version=no + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}.sl$versuffix ${libname}${release}.sl$major $libname.sl' + soname_spec='${libname}${release}.sl$major' + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' + ;; + +irix5* | irix6*) + version_type=irix + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}.so.$major' + library_names_spec='${libname}${release}.so.$versuffix ${libname}${release}.so.$major ${libname}${release}.so $libname.so' + case "$host_os" in + irix5*) + libsuff= shlibsuff= + # this will be overridden with pass_all, but let us keep it just in case + deplibs_check_method="file_magic ELF 32-bit MSB dynamic lib MIPS - version 1" + ;; + *) + case "$LD" in # libtool.m4 will add one of these switches to LD + *-32|*"-32 ") libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 ") libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + file_magic_cmd=/usr/bin/file + file_magic_test_file=`echo /lib${libsuff}/libc.so*` + deplibs_check_method='pass_all' + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux-gnuoldld* | linux-gnuaout* | linux-gnucoff*) + dynamic_linker=no + ;; + +# This must be Linux ELF. +linux-gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so' + soname_spec='${libname}${release}.so$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + file_magic_cmd=/usr/bin/file + file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so` + + if test -f /lib/ld.so.1; then + dynamic_linker='GNU ld.so' + else + # Only the GNU ld.so supports shared libraries on MkLinux. + case "$host_cpu" in + powerpc*) dynamic_linker=no ;; + *) dynamic_linker='Linux ld.so' ;; + esac + fi + ;; + +netbsd*) + version_type=sunos + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major ${libname}${release}.so ${libname}.so' + soname_spec='${libname}${release}.so$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + ;; + +openbsd*) + version_type=sunos + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + need_version=no + fi + library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + ;; + +os2*) + libname_spec='$name' + need_lib_prefix=no + library_names_spec='$libname.dll $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_version=no + soname_spec='${libname}${release}.so' + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so $libname.so' + shlibpath_var=LD_LIBRARY_PATH + # this will be overridden with pass_all, but let us keep it just in case + deplibs_check_method='file_magic COFF format alpha shared library' + file_magic_cmd=/usr/bin/file + file_magic_test_file=/shlib/libc.so + deplibs_check_method='pass_all' + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +sco3.2v5*) + version_type=osf + soname_spec='${libname}${release}.so$major' + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so' + shlibpath_var=LD_LIBRARY_PATH + ;; + +solaris*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so' + soname_spec='${libname}${release}.so$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + deplibs_check_method="file_magic ELF [0-9][0-9]-bit [LM]SB dynamic lib" + file_magic_cmd=/usr/bin/file + file_magic_test_file=/lib/libc.so + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + version_type=linux + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so' + soname_spec='${libname}${release}.so$major' + shlibpath_var=LD_LIBRARY_PATH + case "$host_vendor" in + ncr) + deplibs_check_method='pass_all' + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + file_magic_cmd=/usr/bin/file + file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + esac + ;; + +uts4*) + version_type=linux + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so' + soname_spec='${libname}${release}.so$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +dgux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so' + soname_spec='${libname}${release}.so$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux + library_names_spec='$libname.so.$versuffix $libname.so.$major $libname.so' + soname_spec='$libname.so.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +*) + dynamic_linker=no + ;; +esac +echo "$ac_t$dynamic_linker" 1>&6 +test "$dynamic_linker" = no && can_build_shared=no + +# Report the final consequences. +echo "checking if libtool supports shared libraries... $can_build_shared" 1>&6 + +# Only try to build win32 dlls if AC_LIBTOOL_WIN32_DLL was used in +# configure.in, otherwise build static only libraries. +case "$host_os" in +cygwin* | mingw* | os2*) + if test x$can_build_shared = xyes; then + test x$enable_win32_dll = xno && can_build_shared=no + echo "checking if package supports dlls... $can_build_shared" 1>&6 + fi +;; +esac + +if test -n "$file_magic_test_file" && test -n "$file_magic_cmd"; then + case "$deplibs_check_method" in + "file_magic "*) + file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + egrep "$file_magic_regex" > /dev/null; then + : + else + cat <&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +EOF + fi ;; + esac +fi + +echo $ac_n "checking whether to build shared libraries... $ac_c" 1>&6 +test "$can_build_shared" = "no" && enable_shared=no + +# On AIX, shared libraries and static libraries use the same namespace, and +# are all built from PIC. +case "$host_os" in +aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + +aix4*) + test "$enable_shared" = yes && enable_static=no + ;; +esac + +echo "$ac_t$enable_shared" 1>&6 + +# Make sure either enable_shared or enable_static is yes. +test "$enable_shared" = yes || enable_static=yes + +echo "checking whether to build static libraries... $enable_static" 1>&6 + +if test "$hardcode_action" = relink; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + +echo $ac_n "checking for objdir... $ac_c" 1>&6 +rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + objdir=_libs +fi +rmdir .libs 2>/dev/null +echo "$ac_t$objdir" 1>&6 + +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else +if eval "test \"`echo '$''{'lt_cv_dlopen'+set}'`\" != set"; then + lt_cv_dlopen=no lt_cv_dlopen_libs= +echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6 +echo "$progname:2212: checking for dlopen in -ldl" >&5 +ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for dlopen""... $ac_c" 1>&6 +echo "$progname:2252: checking for dlopen" >&5 +if eval "test \"`echo '$''{'ac_cv_func_dlopen'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_dlopen) || defined (__stub___dlopen) +choke me +#else +dlopen(); +#endif + +; return 0; } +EOF +if { (eval echo $progname:2282: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_dlopen=yes" +else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_dlopen=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_func_'dlopen`\" = yes"; then + echo "$ac_t""yes" 1>&6 + lt_cv_dlopen="dlopen" +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for dld_link in -ldld""... $ac_c" 1>&6 +echo "$progname:2299: checking for dld_link in -ldld" >&5 +ac_lib_var=`echo dld'_'dld_link | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldld $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for shl_load""... $ac_c" 1>&6 +echo "$progname:2339: checking for shl_load" >&5 +if eval "test \"`echo '$''{'ac_cv_func_shl_load'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_shl_load) || defined (__stub___shl_load) +choke me +#else +shl_load(); +#endif + +; return 0; } +EOF +if { (eval echo $progname:2369: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_shl_load=yes" +else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_shl_load=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'shl_load`\" = yes"; then + echo "$ac_t""yes" 1>&6 + lt_cv_dlopen="shl_load" +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for shl_load in -ldld""... $ac_c" 1>&6 +echo "$progname:2387: checking for shl_load in -ldld" >&5 +ac_lib_var=`echo dld'_'shl_load | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldld $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" +else + echo "$ac_t""no" 1>&6 +fi + + +fi + + +fi + + +fi + + +fi + +fi + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + fi + + case "$lt_cv_dlopen" in + dlopen) +for ac_hdr in dlfcn.h; do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "$progname:2452: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int fnord = 0; +EOF +ac_try="$ac_compile >/dev/null 2>conftest.out" +{ (eval echo $progname:2462: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi +done + + if test "x$ac_cv_header_dlfcn_h" = xyes; then + CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + fi + eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + LIBS="$lt_cv_dlopen_libs $LIBS" + + echo $ac_n "checking whether a program can dlopen itself""... $ac_c" 1>&6 +echo "$progname:2490: checking whether a program can dlopen itself" >&5 +if test "${lt_cv_dlopen_self+set}" = set; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + lt_cv_dlopen_self=cross + else + cat > conftest.c < +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LTDL_GLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LTDL_GLOBAL DL_GLOBAL +# else +# define LTDL_GLOBAL 0 +# endif +#endif + +/* We may have to define LTDL_LAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LTDL_LAZY_OR_NOW +# ifdef RTLD_LAZY +# define LTDL_LAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LTDL_LAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LTDL_LAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LTDL_LAZY_OR_NOW DL_NOW +# else +# define LTDL_LAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +fnord() { int i=42;} +main() { void *self, *ptr1, *ptr2; self=dlopen(0,LTDL_GLOBAL|LTDL_LAZY_OR_NOW); + if(self) { ptr1=dlsym(self,"fnord"); ptr2=dlsym(self,"_fnord"); + if(ptr1 || ptr2) { dlclose(self); exit(0); } } exit(1); } + +EOF +if { (eval echo $progname:2544: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + lt_cv_dlopen_self=yes +else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + lt_cv_dlopen_self=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$lt_cv_dlopen_self" 1>&6 + + if test "$lt_cv_dlopen_self" = yes; then + LDFLAGS="$LDFLAGS $link_static_flag" + echo $ac_n "checking whether a statically linked program can dlopen itself""... $ac_c" 1>&6 +echo "$progname:2563: checking whether a statically linked program can dlopen itself" >&5 +if test "${lt_cv_dlopen_self_static+set}" = set; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + lt_cv_dlopen_self_static=cross + else + cat > conftest.c < +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LTDL_GLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LTDL_GLOBAL DL_GLOBAL +# else +# define LTDL_GLOBAL 0 +# endif +#endif + +/* We may have to define LTDL_LAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LTDL_LAZY_OR_NOW +# ifdef RTLD_LAZY +# define LTDL_LAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LTDL_LAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LTDL_LAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LTDL_LAZY_OR_NOW DL_NOW +# else +# define LTDL_LAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +fnord() { int i=42;} +main() { void *self, *ptr1, *ptr2; self=dlopen(0,LTDL_GLOBAL|LTDL_LAZY_OR_NOW); + if(self) { ptr1=dlsym(self,"fnord"); ptr2=dlsym(self,"_fnord"); + if(ptr1 || ptr2) { dlclose(self); exit(0); } } exit(1); } + +EOF +if { (eval echo $progname:2617: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + lt_cv_dlopen_self_static=yes +else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + lt_cv_dlopen_self_static=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$lt_cv_dlopen_self_static" 1>&6 +fi + ;; + esac + + case "$lt_cv_dlopen_self" in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case "$lt_cv_dlopen_self_static" in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + +# Copy echo and quote the copy, instead of the original, because it is +# used later. +ltecho="$echo" +if test "X$ltecho" = "X$CONFIG_SHELL $0 --fallback-echo"; then + ltecho="$CONFIG_SHELL \$0 --fallback-echo" +fi +LTSHELL="$SHELL" + +LTCONFIG_VERSION="$VERSION" + +# Only quote variables if we're using ltmain.sh. +case "$ltmain" in +*.sh) + # Now quote all the things that may contain metacharacters. + for var in ltecho old_CC old_CFLAGS old_CPPFLAGS \ + old_LD old_LDFLAGS old_LIBS \ + old_NM old_RANLIB old_LN_S old_DLLTOOL old_OBJDUMP old_AS \ + AR CC LD LN_S NM LTSHELL LTCONFIG_VERSION \ + reload_flag reload_cmds wl \ + pic_flag link_static_flag no_builtin_flag export_dynamic_flag_spec \ + thread_safe_flag_spec whole_archive_flag_spec libname_spec \ + library_names_spec soname_spec \ + RANLIB old_archive_cmds old_archive_from_new_cmds old_postinstall_cmds \ + old_postuninstall_cmds archive_cmds archive_expsym_cmds postinstall_cmds postuninstall_cmds \ + file_magic_cmd export_symbols_cmds deplibs_check_method allow_undefined_flag no_undefined_flag \ + finish_cmds finish_eval global_symbol_pipe global_symbol_to_cdecl \ + hardcode_libdir_flag_spec hardcode_libdir_separator \ + sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ + compiler_c_o compiler_o_lo need_locks exclude_expsyms include_expsyms; do + + case "$var" in + reload_cmds | old_archive_cmds | old_archive_from_new_cmds | \ + old_postinstall_cmds | old_postuninstall_cmds | \ + export_symbols_cmds | archive_cmds | archive_expsym_cmds | \ + postinstall_cmds | postuninstall_cmds | \ + finish_cmds | sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) + # Double-quote double-evaled strings. + eval "$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" + ;; + *) + eval "$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" + ;; + esac + done + + case "$ltecho" in + *'\$0 --fallback-echo"') + ltecho=`$echo "X$ltecho" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` + ;; + esac + + trap "$rm \"$ofile\"; exit 1" 1 2 15 + echo "creating $ofile" + $rm "$ofile" + cat < "$ofile" +#! $SHELL + +# `$echo "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP) +# NOTE: Changes made to this file will be lost: look at ltconfig or ltmain.sh. +# +# Copyright (C) 1996-1999 Free Software Foundation, Inc. +# Originally by Gordon Matzigkeit , 1996 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="sed -e s/^X//" + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test "X\${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi + +### BEGIN LIBTOOL CONFIG +EOF + cfgfile="$ofile" + ;; + +*) + # Double-quote the variables that need it (for aesthetics). + for var in old_CC old_CFLAGS old_CPPFLAGS \ + old_LD old_LDFLAGS old_LIBS \ + old_NM old_RANLIB old_LN_S old_DLLTOOL old_OBJDUMP old_AS; do + eval "$var=\\\"\$var\\\"" + done + + # Just create a config file. + cfgfile="$ofile.cfg" + trap "$rm \"$cfgfile\"; exit 1" 1 2 15 + echo "creating $cfgfile" + $rm "$cfgfile" + cat < "$cfgfile" +# `$echo "$cfgfile" | sed 's%^.*/%%'` - Libtool configuration file. +# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP) +EOF + ;; +esac + +cat <> "$cfgfile" +# Libtool was configured as follows, on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# +# CC=$old_CC CFLAGS=$old_CFLAGS CPPFLAGS=$old_CPPFLAGS \\ +# LD=$old_LD LDFLAGS=$old_LDFLAGS LIBS=$old_LIBS \\ +# NM=$old_NM RANLIB=$old_RANLIB LN_S=$old_LN_S \\ +# DLLTOOL=$old_DLLTOOL OBJDUMP=$old_OBJDUMP AS=$old_AS \\ +# $0$ltconfig_args +# +# Compiler and other test output produced by $progname, useful for +# debugging $progname, is in ./config.log if it exists. + +# The version of $progname that generated this script. +LTCONFIG_VERSION=$LTCONFIG_VERSION + +# Shell to use when invoking shell scripts. +SHELL=$LTSHELL + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# The host system. +host_alias=$host_alias +host=$host + +# An echo program that does not interpret backslashes. +echo=$ltecho + +# The archiver. +AR=$AR + +# The default C compiler. +CC=$CC + +# The linker used to build libraries. +LD=$LD + +# Whether we need hard or soft links. +LN_S=$LN_S + +# A BSD-compatible nm program. +NM=$NM + +# Used on cygwin: DLL creation program. +DLLTOOL="$DLLTOOL" + +# Used on cygwin: object dumper. +OBJDUMP="$OBJDUMP" + +# Used on cygwin: assembler. +AS="$AS" + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# How to create reloadable object files. +reload_flag=$reload_flag +reload_cmds=$reload_cmds + +# How to pass a linker flag through the compiler. +wl=$wl + +# Object file suffix (normally "o"). +objext="$objext" + +# Old archive suffix (normally "a"). +libext="$libext" + +# Executable file suffix (normally ""). +exeext="$exeext" + +# Additional compiler flags for building library objects. +pic_flag=$pic_flag + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$compiler_c_o + +# Can we write directly to a .lo ? +compiler_o_lo=$compiler_o_lo + +# Must we lock files when doing compilation ? +need_locks=$need_locks + +# Do we need the lib prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Whether dlopen is supported. +dlopen=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Compiler flag to prevent dynamic linking. +link_static_flag=$link_static_flag + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$no_builtin_flag + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$whole_archive_flag_spec + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec=$thread_safe_flag_spec + +# Library versioning type. +version_type=$version_type + +# Format of library name prefix. +libname_spec=$libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec=$library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$soname_spec + +# Commands used to build and install an old-style archive. +RANLIB=$RANLIB +old_archive_cmds=$old_archive_cmds +old_postinstall_cmds=$old_postinstall_cmds +old_postuninstall_cmds=$old_postuninstall_cmds + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$old_archive_from_new_cmds + +# Commands used to build and install a shared archive. +archive_cmds=$archive_cmds +archive_expsym_cmds=$archive_expsym_cmds +postinstall_cmds=$postinstall_cmds +postuninstall_cmds=$postuninstall_cmds + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$deplibs_check_method + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd=$file_magic_cmd + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$allow_undefined_flag + +# Flag that forces no undefined symbols. +no_undefined_flag=$no_undefined_flag + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$finish_cmds + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval=$finish_eval + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$global_symbol_pipe + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl=$global_symbol_to_cdecl + +# This is the shared library runtime path variable. +runpath_var=$runpath_var + +# This is the shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec=$hardcode_libdir_flag_spec + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator=$hardcode_libdir_separator + +# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=$hardcode_direct + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Compile-time system search path for libraries +sys_lib_search_path_spec=$sys_lib_search_path_spec + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec=$sys_lib_dlsearch_path_spec + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path="$fix_srcfile_path" + +# Set to yes if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$include_expsyms + +EOF + +case "$ltmain" in +*.sh) + echo '### END LIBTOOL CONFIG' >> "$ofile" + echo >> "$ofile" + case "$host_os" in + aix3*) + cat <<\EOF >> "$ofile" + +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +EOF + ;; + esac + + # Append the ltmain.sh script. + sed '$q' "$ltmain" >> "$ofile" || (rm -f "$ofile"; exit 1) + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + + chmod +x "$ofile" + ;; + +*) + # Compile the libtool program. + echo "FIXME: would compile $ltmain" + ;; +esac + +test -n "$cache_file" || exit 0 + +# AC_CACHE_SAVE +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +exit 0 + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/srclib/pcre/ltmain.sh b/srclib/pcre/ltmain.sh new file mode 100644 index 00000000000..50515ad0b9e --- /dev/null +++ b/srclib/pcre/ltmain.sh @@ -0,0 +1,4012 @@ +# ltmain.sh - Provide generalized library-building support services. +# NOTE: Changing this file will not affect anything until you rerun ltconfig. +# +# Copyright (C) 1996-1999 Free Software Foundation, Inc. +# Originally by Gordon Matzigkeit , 1996 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Check that we have a working $echo. +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then + # Yippee, $echo works! + : +else + # Restart under the correct shell, and then maybe $echo will work. + exec $SHELL "$0" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat <&2 + echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit 1 +fi + +if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + echo "$modename: not configured to build any kind of library" 1>&2 + echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit 1 +fi + +# Global variables. +mode=$default_mode +nonopt= +prev= +prevopt= +run= +show="$echo" +show_help= +execute_dlfiles= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" + +# Parse our command line options once, thoroughly. +while test $# -gt 0 +do + arg="$1" + shift + + case "$arg" in + -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case "$prev" in + execute_dlfiles) + eval "$prev=\"\$$prev \$arg\"" + ;; + *) + eval "$prev=\$arg" + ;; + esac + + prev= + prevopt= + continue + fi + + # Have we seen a non-optional argument yet? + case "$arg" in + --help) + show_help=yes + ;; + + --version) + echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" + exit 0 + ;; + + --config) + sed -e '1,/^### BEGIN LIBTOOL CONFIG/d' -e '/^### END LIBTOOL CONFIG/,$d' $0 + exit 0 + ;; + + --debug) + echo "$progname: enabling shell trace mode" + set -x + ;; + + --dry-run | -n) + run=: + ;; + + --features) + echo "host: $host" + if test "$build_libtool_libs" = yes; then + echo "enable shared libraries" + else + echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + echo "enable static libraries" + else + echo "disable static libraries" + fi + exit 0 + ;; + + --finish) mode="finish" ;; + + --mode) prevopt="--mode" prev=mode ;; + --mode=*) mode="$optarg" ;; + + --quiet | --silent) + show=: + ;; + + -dlopen) + prevopt="-dlopen" + prev=execute_dlfiles + ;; + + -*) + $echo "$modename: unrecognized option \`$arg'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + + *) + nonopt="$arg" + break + ;; + esac +done + +if test -n "$prevopt"; then + $echo "$modename: option \`$prevopt' requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 +fi + +if test -z "$show_help"; then + + # Infer the operation mode. + if test -z "$mode"; then + case "$nonopt" in + *cc | *++ | gcc* | *-gcc*) + mode=link + for arg + do + case "$arg" in + -c) + mode=compile + break + ;; + esac + done + ;; + *db | *dbx | *strace | *truss) + mode=execute + ;; + *install*|cp|mv) + mode=install + ;; + *rm) + mode=uninstall + ;; + *) + # If we have no mode, but dlfiles were specified, then do execute mode. + test -n "$execute_dlfiles" && mode=execute + + # Just use the default operation mode. + if test -z "$mode"; then + if test -n "$nonopt"; then + $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 + else + $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 + fi + fi + ;; + esac + fi + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$execute_dlfiles" && test "$mode" != execute; then + $echo "$modename: unrecognized option \`-dlopen'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$modename --help --mode=$mode' for more information." + + # These modes are in order of execution frequency so that they run quickly. + case "$mode" in + # libtool compile mode + compile) + modename="$modename: compile" + # Get the compilation command and the source file. + base_compile= + lastarg= + srcfile="$nonopt" + suppress_output= + + user_target=no + for arg + do + # Accept any command-line options. + case "$arg" in + -o) + if test "$user_target" != "no"; then + $echo "$modename: you cannot specify \`-o' more than once" 1>&2 + exit 1 + fi + user_target=next + ;; + + -static) + build_old_libs=yes + continue + ;; + esac + + case "$user_target" in + next) + # The next one is the -o target name + user_target=yes + continue + ;; + yes) + # We got the output file + user_target=set + libobj="$arg" + continue + ;; + esac + + # Accept the current argument as the source file. + lastarg="$srcfile" + srcfile="$arg" + + # Aesthetically quote the previous argument. + + # Backslashify any backslashes, double quotes, and dollar signs. + # These are the only characters that are still specially + # interpreted inside of double-quoted scrings. + lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` + + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly in scan + # sets, so we specify it separately. + case "$lastarg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + lastarg="\"$lastarg\"" + ;; + esac + + # Add the previous argument to base_compile. + if test -z "$base_compile"; then + base_compile="$lastarg" + else + base_compile="$base_compile $lastarg" + fi + done + + case "$user_target" in + set) + ;; + no) + # Get the name of the library object. + libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` + ;; + *) + $echo "$modename: you must specify a target with \`-o'" 1>&2 + exit 1 + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + xform='[cCFSfmso]' + case "$libobj" in + *.ada) xform=ada ;; + *.adb) xform=adb ;; + *.ads) xform=ads ;; + *.asm) xform=asm ;; + *.c++) xform=c++ ;; + *.cc) xform=cc ;; + *.cpp) xform=cpp ;; + *.cxx) xform=cxx ;; + *.f90) xform=f90 ;; + *.for) xform=for ;; + esac + + libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` + + case "$libobj" in + *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; + *) + $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 + exit 1 + ;; + esac + + if test -z "$base_compile"; then + $echo "$modename: you must specify a compilation command" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $libobj" + else + removelist="$libobj" + fi + + $run $rm $removelist + trap "$run $rm $removelist; exit 1" 1 2 15 + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\..*$%%'`.${objext} + lockfile="$output_obj.lock" + removelist="$removelist $output_obj $lockfile" + trap "$run $rm $removelist; exit 1" 1 2 15 + else + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until ln "$0" "$lockfile" 2>/dev/null; do + $show "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + echo "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit 1 + fi + echo $srcfile > "$lockfile" + fi + + if test -n "$fix_srcfile_path"; then + eval srcfile=\"$fix_srcfile_path\" + fi + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + # All platforms use -DPIC, to notify preprocessed assembler code. + command="$base_compile $srcfile $pic_flag -DPIC" + if test "$build_old_libs" = yes; then + lo_libobj="$libobj" + dir=`$echo "X$libobj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$dir" = "X$libobj"; then + dir="$objdir" + else + dir="$dir/$objdir" + fi + libobj="$dir/"`$echo "X$libobj" | $Xsed -e 's%^.*/%%'` + + if test -d "$dir"; then + $show "$rm $libobj" + $run $rm $libobj + else + $show "$mkdir $dir" + $run $mkdir $dir + status=$? + if test $status -ne 0 && test ! -d $dir; then + exit $status + fi + fi + fi + if test "$compiler_o_lo" = yes; then + output_obj="$libobj" + command="$command -o $output_obj" + elif test "$compiler_c_o" = yes; then + output_obj="$obj" + command="$command -o $output_obj" + fi + + $run $rm "$output_obj" + $show "$command" + if $run eval "$command"; then : + else + test -n "$output_obj" && $run $rm $removelist + exit 1 + fi + + if test "$need_locks" = warn && + test x"`cat $lockfile 2>/dev/null`" != x"$srcfile"; then + echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit 1 + fi + + # Just move the object if needed, then go on to compile the next one + if test x"$output_obj" != x"$libobj"; then + $show "$mv $output_obj $libobj" + if $run $mv $output_obj $libobj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # If we have no pic_flag, then copy the object into place and finish. + if test -z "$pic_flag" && test "$build_old_libs" = yes; then + # Rename the .lo from within objdir to obj + if test -f $obj; then + $show $rm $obj + $run $rm $obj + fi + + $show "$mv $libobj $obj" + if $run $mv $libobj $obj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + + xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$obj"; then + xdir="." + else + xdir="$xdir" + fi + baseobj=`$echo "X$obj" | $Xsed -e "s%.*/%%"` + libobj=`$echo "X$baseobj" | $Xsed -e "$o2lo"` + # Now arrange that obj and lo_libobj become the same file + $show "(cd $xdir && $LN_S $baseobj $libobj)" + if $run eval '(cd $xdir && $LN_S $baseobj $libobj)'; then + exit 0 + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Allow error messages only from the first compilation. + suppress_output=' >/dev/null 2>&1' + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + command="$base_compile $srcfile" + if test "$compiler_c_o" = yes; then + command="$command -o $obj" + output_obj="$obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + command="$command$suppress_output" + $run $rm "$output_obj" + $show "$command" + if $run eval "$command"; then : + else + $run $rm $removelist + exit 1 + fi + + if test "$need_locks" = warn && + test x"`cat $lockfile 2>/dev/null`" != x"$srcfile"; then + echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit 1 + fi + + # Just move the object if needed + if test x"$output_obj" != x"$obj"; then + $show "$mv $output_obj $obj" + if $run $mv $output_obj $obj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Create an invalid libtool object if no PIC, so that we do not + # accidentally link it into a program. + if test "$build_libtool_libs" != yes; then + $show "echo timestamp > $libobj" + $run eval "echo timestamp > \$libobj" || exit $? + else + # Move the .lo from within objdir + $show "$mv $libobj $lo_libobj" + if $run $mv $libobj $lo_libobj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + fi + + # Unlock the critical section if it was locked + if test "$need_locks" != no; then + $rm "$lockfile" + fi + + exit 0 + ;; + + # libtool link mode + link) + modename="$modename: link" + case "$host" in + *-*-cygwin* | *-*-mingw* | *-*-os2*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invokation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + + # This is a source program that is used to create dlls on Windows + # Don't remove nor modify the starting and closing comments +# /* ltdll.c starts here */ +# #define WIN32_LEAN_AND_MEAN +# #include +# #undef WIN32_LEAN_AND_MEAN +# #include +# +# #ifndef __CYGWIN__ +# # ifdef __CYGWIN32__ +# # define __CYGWIN__ __CYGWIN32__ +# # endif +# #endif +# +# #ifdef __cplusplus +# extern "C" { +# #endif +# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved); +# #ifdef __cplusplus +# } +# #endif +# +# #ifdef __CYGWIN__ +# #include +# DECLARE_CYGWIN_DLL( DllMain ); +# #endif +# HINSTANCE __hDllInstance_base; +# +# BOOL APIENTRY +# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved) +# { +# __hDllInstance_base = hInst; +# return TRUE; +# } +# /* ltdll.c ends here */ + # This is a source program that is used to create import libraries + # on Windows for dlls which lack them. Don't remove nor modify the + # starting and closing comments +# /* impgen.c starts here */ +# /* Copyright (C) 1999 Free Software Foundation, Inc. +# +# This file is part of GNU libtool. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# */ +# +# #include /* for printf() */ +# #include /* for open(), lseek(), read() */ +# #include /* for O_RDONLY, O_BINARY */ +# #include /* for strdup() */ +# +# static unsigned int +# pe_get16 (fd, offset) +# int fd; +# int offset; +# { +# unsigned char b[2]; +# lseek (fd, offset, SEEK_SET); +# read (fd, b, 2); +# return b[0] + (b[1]<<8); +# } +# +# static unsigned int +# pe_get32 (fd, offset) +# int fd; +# int offset; +# { +# unsigned char b[4]; +# lseek (fd, offset, SEEK_SET); +# read (fd, b, 4); +# return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24); +# } +# +# static unsigned int +# pe_as32 (ptr) +# void *ptr; +# { +# unsigned char *b = ptr; +# return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24); +# } +# +# int +# main (argc, argv) +# int argc; +# char *argv[]; +# { +# int dll; +# unsigned long pe_header_offset, opthdr_ofs, num_entries, i; +# unsigned long export_rva, export_size, nsections, secptr, expptr; +# unsigned long name_rvas, nexp; +# unsigned char *expdata, *erva; +# char *filename, *dll_name; +# +# filename = argv[1]; +# +# dll = open(filename, O_RDONLY|O_BINARY); +# if (!dll) +# return 1; +# +# dll_name = filename; +# +# for (i=0; filename[i]; i++) +# if (filename[i] == '/' || filename[i] == '\\' || filename[i] == ':') +# dll_name = filename + i +1; +# +# pe_header_offset = pe_get32 (dll, 0x3c); +# opthdr_ofs = pe_header_offset + 4 + 20; +# num_entries = pe_get32 (dll, opthdr_ofs + 92); +# +# if (num_entries < 1) /* no exports */ +# return 1; +# +# export_rva = pe_get32 (dll, opthdr_ofs + 96); +# export_size = pe_get32 (dll, opthdr_ofs + 100); +# nsections = pe_get16 (dll, pe_header_offset + 4 +2); +# secptr = (pe_header_offset + 4 + 20 + +# pe_get16 (dll, pe_header_offset + 4 + 16)); +# +# expptr = 0; +# for (i = 0; i < nsections; i++) +# { +# char sname[8]; +# unsigned long secptr1 = secptr + 40 * i; +# unsigned long vaddr = pe_get32 (dll, secptr1 + 12); +# unsigned long vsize = pe_get32 (dll, secptr1 + 16); +# unsigned long fptr = pe_get32 (dll, secptr1 + 20); +# lseek(dll, secptr1, SEEK_SET); +# read(dll, sname, 8); +# if (vaddr <= export_rva && vaddr+vsize > export_rva) +# { +# expptr = fptr + (export_rva - vaddr); +# if (export_rva + export_size > vaddr + vsize) +# export_size = vsize - (export_rva - vaddr); +# break; +# } +# } +# +# expdata = (unsigned char*)malloc(export_size); +# lseek (dll, expptr, SEEK_SET); +# read (dll, expdata, export_size); +# erva = expdata - export_rva; +# +# nexp = pe_as32 (expdata+24); +# name_rvas = pe_as32 (expdata+32); +# +# printf ("EXPORTS\n"); +# for (i = 0; i&2 + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + else + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + fi + build_libtool_libs=no + build_old_libs=yes + prefer_static_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test $# -gt 0; do + arg="$1" + shift + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case "$prev" in + output) + compile_command="$compile_command @OUTPUT@" + finalize_command="$finalize_command @OUTPUT@" + ;; + esac + + case "$prev" in + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + compile_command="$compile_command @SYMFILE@" + finalize_command="$finalize_command @SYMFILE@" + preload=yes + fi + case "$arg" in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + else + dlprefiles="$dlprefiles $arg" + fi + prev= + ;; + esac + ;; + expsyms) + export_symbols="$arg" + if test ! -f "$arg"; then + $echo "$modename: symbol file \`$arg' does not exist" + exit 1 + fi + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case "$arg" in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit 1 + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath="$rpath $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath="$xrpath $arg" ;; + esac + fi + prev= + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi + + prevarg="$arg" + + case "$arg" in + -all-static) + if test -n "$link_static_flag"; then + compile_command="$compile_command $link_static_flag" + finalize_command="$finalize_command $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 + continue + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: not more than one -exported-symbols argument allowed" + exit 1 + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -L*) + dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` + # We need an absolute path. + case "$dir" in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 + $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 + absdir="$dir" + fi + dir="$absdir" + ;; + esac + case " $deplibs " in + *" $arg "*) ;; + *) deplibs="$deplibs $arg";; + esac + case " $lib_search_path " in + *" $dir "*) ;; + *) lib_search_path="$lib_search_path $dir";; + esac + case "$host" in + *-*-cygwin* | *-*-mingw* | *-*-os2*) + dllsearchdir=`cd "$dir" && pwd || echo "$dir"` + case ":$dllsearchpath:" in + ::) dllsearchpath="$dllsearchdir";; + *":$dllsearchdir:"*) ;; + *) dllsearchpath="$dllsearchpath:$dllsearchdir";; + esac + ;; + esac + ;; + + -l*) + if test "$arg" = "-lc"; then + case "$host" in + *-*-cygwin* | *-*-mingw* | *-*-os2* | *-*-beos*) + # These systems don't actually have c library (as such) + continue + ;; + esac + elif test "$arg" = "-lm"; then + case "$host" in + *-*-cygwin* | *-*-beos*) + # These systems don't actually have math library (as such) + continue + ;; + esac + fi + deplibs="$deplibs $arg" + ;; + + -module) + module=yes + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -o) prev=output ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` + # We need an absolute path. + case "$dir" in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit 1 + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + continue + ;; + + -static) + # If we have no pic_flag, then this is the same as -all-static. + if test -z "$pic_flag" && test -n "$link_static_flag"; then + compile_command="$compile_command $link_static_flag" + finalize_command="$finalize_command $link_static_flag" + fi + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + # Some other compiler flag. + -* | +*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case "$arg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + ;; + + *.o | *.obj | *.a | *.lib) + # A standard object. + objs="$objs $arg" + ;; + + *.lo) + # A library object. + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + if test "$build_libtool_libs" = yes && test "$dlopen" = yes; then + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles "`$echo "X$arg" | $Xsed -e "$lo2o"` + prev= + fi + libobjs="$libobjs $arg" + ;; + + *.la) + # A libtool-controlled library. + + dlname= + libdir= + library_names= + old_library= + + # Check to see that this really is a libtool archive. + if (sed -e '2q' $arg | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$arg' is not a valid libtool archive" 1>&2 + exit 1 + fi + + # If the library was installed with an old release of libtool, + # it will not redefine variable installed. + installed=yes + + # Read the .la file + # If there is no directory component, then add one. + case "$arg" in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + + if test -z "$linklib"; then + $echo "$modename: cannot find name of link library for \`$arg'" 1>&2 + exit 1 + fi + + # Find the relevant object directory and library name. + name=`$echo "X$arg" | $Xsed -e 's%^.*/%%' -e 's/\.la$//' -e 's/^lib//'` + + if test "X$installed" = Xyes; then + dir="$libdir" + else + dir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$dir" = "X$arg"; then + dir="$objdir" + else + dir="$dir/$objdir" + fi + fi + + if test -n "$dependency_libs"; then + # Extract -R and -L from dependency_libs + temp_deplibs= + for deplib in $dependency_libs; do + case "$deplib" in + -R*) temp_xrpath=`$echo "X$deplib" | $Xsed -e 's/^-R//'` + case " $rpath $xrpath " in + *" $temp_xrpath "*) ;; + *) xrpath="$xrpath $temp_xrpath";; + esac;; + -L*) case "$compile_command $temp_deplibs " in + *" $deplib "*) ;; + *) temp_deplibs="$temp_deplibs $deplib";; + esac + temp_dir=`$echo "X$deplib" | $Xsed -e 's/^-L//'` + case " $lib_search_path " in + *" $temp_dir "*) ;; + *) lib_search_path="$lib_search_path $temp_dir";; + esac + ;; + *) temp_deplibs="$temp_deplibs $deplib";; + esac + done + dependency_libs="$temp_deplibs" + fi + + if test -z "$libdir"; then + # It is a libtool convenience library, so add in its objects. + convenience="$convenience $dir/$old_library" + old_convenience="$old_convenience $dir/$old_library" + deplibs="$deplibs$dependency_libs" + compile_command="$compile_command $dir/$old_library$dependency_libs" + finalize_command="$finalize_command $dir/$old_library$dependency_libs" + continue + fi + + # This library was specified with -dlopen. + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + if test -z "$dlname" || test "$dlopen" != yes || test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking statically, + # we need to preload. + prev=dlprefiles + else + # We should not create a dependency on this library, but we + # may need any libraries it requires. + compile_command="$compile_command$dependency_libs" + finalize_command="$finalize_command$dependency_libs" + prev= + continue + fi + fi + + # The library was specified with -dlpreopen. + if test "$prev" = dlprefiles; then + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + dlprefiles="$dlprefiles $dir/$old_library" + else + dlprefiles="$dlprefiles $dir/$linklib" + fi + prev= + fi + + if test -n "$library_names" && + { test "$prefer_static_libs" = no || test -z "$old_library"; }; then + link_against_libtool_libs="$link_against_libtool_libs $arg" + if test -n "$shlibpath_var"; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath " in + *" $dir "*) ;; + *) temp_rpath="$temp_rpath $dir" ;; + esac + fi + + # We need an absolute path. + case "$dir" in + [\\/] | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 + $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 + absdir="$dir" + fi + ;; + esac + + # This is the magic to use -rpath. + # Skip directories that are in the system default run-time + # search path, unless they have been requested with -R. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + + lib_linked=yes + case "$hardcode_action" in + immediate | unsupported) + if test "$hardcode_direct" = no; then + compile_command="$compile_command $dir/$linklib" + deplibs="$deplibs $dir/$linklib" + case "$host" in + *-*-cygwin* | *-*-mingw* | *-*-os2*) + dllsearchdir=`cd "$dir" && pwd || echo "$dir"` + if test -n "$dllsearchpath"; then + dllsearchpath="$dllsearchpath:$dllsearchdir" + else + dllsearchpath="$dllsearchdir" + fi + ;; + esac + elif test "$hardcode_minus_L" = no; then + case "$host" in + *-*-sunos*) + compile_shlibpath="$compile_shlibpath$dir:" + ;; + esac + case "$compile_command " in + *" -L$dir "*) ;; + *) compile_command="$compile_command -L$dir";; + esac + compile_command="$compile_command -l$name" + deplibs="$deplibs -L$dir -l$name" + elif test "$hardcode_shlibpath_var" = no; then + case ":$compile_shlibpath:" in + *":$dir:"*) ;; + *) compile_shlibpath="$compile_shlibpath$dir:";; + esac + compile_command="$compile_command -l$name" + deplibs="$deplibs -l$name" + else + lib_linked=no + fi + ;; + + relink) + if test "$hardcode_direct" = yes; then + compile_command="$compile_command $absdir/$linklib" + deplibs="$deplibs $absdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + case "$compile_command " in + *" -L$absdir "*) ;; + *) compile_command="$compile_command -L$absdir";; + esac + compile_command="$compile_command -l$name" + deplibs="$deplibs -L$absdir -l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case ":$compile_shlibpath:" in + *":$absdir:"*) ;; + *) compile_shlibpath="$compile_shlibpath$absdir:";; + esac + compile_command="$compile_command -l$name" + deplibs="$deplibs -l$name" + else + lib_linked=no + fi + ;; + + *) + lib_linked=no + ;; + esac + + if test "$lib_linked" != yes; then + $echo "$modename: configuration error: unsupported hardcode properties" + exit 1 + fi + + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes; then + finalize_command="$finalize_command $libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + case "$finalize_command " in + *" -L$libdir "*) ;; + *) finalize_command="$finalize_command -L$libdir";; + esac + finalize_command="$finalize_command -l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case ":$finalize_shlibpath:" in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:";; + esac + finalize_command="$finalize_command -l$name" + else + # We cannot seem to hardcode it, guess we'll fake it. + case "$finalize_command " in + *" -L$dir "*) ;; + *) finalize_command="$finalize_command -L$libdir";; + esac + finalize_command="$finalize_command -l$name" + fi + else + # Transform directly to old archives if we don't build new libraries. + if test -n "$pic_flag" && test -z "$old_library"; then + $echo "$modename: cannot find static library for \`$arg'" 1>&2 + exit 1 + fi + + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_command="$compile_command $dir/$linklib" + finalize_command="$finalize_command $dir/$linklib" + else + case "$compile_command " in + *" -L$dir "*) ;; + *) compile_command="$compile_command -L$dir";; + esac + compile_command="$compile_command -l$name" + case "$finalize_command " in + *" -L$dir "*) ;; + *) finalize_command="$finalize_command -L$dir";; + esac + finalize_command="$finalize_command -l$name" + fi + fi + + # Add in any libraries that this one depends upon. + compile_command="$compile_command$dependency_libs" + finalize_command="$finalize_command$dependency_libs" + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case "$arg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + ;; + esac + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + done + + if test -n "$prev"; then + $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` + libobjs_save="$libobjs" + + case "$output" in + "") + $echo "$modename: you must specify an output file" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + + *.a | *.lib) + if test -n "$link_against_libtool_libs"; then + $echo "$modename: error: cannot link libtool libraries into archives" 1>&2 + exit 1 + fi + + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for archives" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for archives" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for archives" 1>&2 + fi + + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2 + fi + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + ;; + + *.la) + # Make sure we only generate libraries of the form `libNAME.la'. + case "$outputname" in + lib*) + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + eval libname=\"$libname_spec\" + ;; + *) + if test "$module" = no; then + $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + eval libname=\"$libname_spec\" + else + libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + fi + ;; + esac + + output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` + if test "X$output_objdir" = "X$output"; then + output_objdir="$objdir" + else + output_objdir="$output_objdir/$objdir" + fi + + if test -n "$objs"; then + $echo "$modename: cannot build libtool library \`$output' from non-libtool objects:$objs" 2>&1 + exit 1 + fi + + # How the heck are we supposed to write a wrapper for a shared library? + if test -n "$link_against_libtool_libs"; then + $echo "$modename: error: cannot link shared libraries into libtool libraries" 1>&2 + exit 1 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for libtool libraries" 1>&2 + fi + + set dummy $rpath + if test $# -gt 2; then + $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 + fi + install_libdir="$2" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + libext=al + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + dependency_libs="$deplibs" + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for convenience libraries" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2 + fi + else + + # Parse the version information argument. + IFS="${IFS= }"; save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + IFS="$save_ifs" + + if test -n "$8"; then + $echo "$modename: too many parameters to \`-version-info'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + current="$2" + revision="$3" + age="$4" + + # Check that each of the things are valid numbers. + case "$current" in + 0 | [1-9] | [1-9][0-9]*) ;; + *) + $echo "$modename: CURRENT \`$current' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + case "$revision" in + 0 | [1-9] | [1-9][0-9]*) ;; + *) + $echo "$modename: REVISION \`$revision' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + case "$age" in + 0 | [1-9] | [1-9][0-9]*) ;; + *) + $echo "$modename: AGE \`$age' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + if test $age -gt $current; then + $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case "$version_type" in + none) ;; + + irix) + major=`expr $current - $age + 1` + versuffix="$major.$revision" + verstring="sgi$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test $loop != 0; do + iface=`expr $revision - $loop` + loop=`expr $loop - 1` + verstring="sgi$major.$iface:$verstring" + done + ;; + + linux) + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + ;; + + osf) + major=`expr $current - $age` + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test $loop != 0; do + iface=`expr $current - $loop` + loop=`expr $loop - 1` + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring="$verstring:${current}.0" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current"; + ;; + + windows) + # Like Linux, but with '-' rather than '.', since we only + # want one extension on Windows 95. + major=`expr $current - $age` + versuffix="-$major-$age-$revision" + ;; + + *) + $echo "$modename: unknown library version type \`$version_type'" 1>&2 + echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit 1 + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + verstring="0.0" + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + dependency_libs="$deplibs" + case "$host" in + *-*-cygwin* | *-*-mingw* | *-*-os2* | *-*-beos*) + # these systems don't actually have a c library (as such)! + ;; + *) + # Add libc to deplibs on all other systems. + deplibs="$deplibs -lc" + ;; + esac + fi + + # Create the output directory, or remove our outputs if we need to. + if test -d $output_objdir; then + $show "${rm}r $output_objdir/$outputname $output_objdir/$libname.* $output_objdir/${libname}${release}.*" + $run ${rm}r $output_objdir/$outputname $output_objdir/$libname.* $output_objdir/${libname}${release}.* + else + $show "$mkdir $output_objdir" + $run $mkdir $output_objdir + status=$? + if test $status -ne 0 && test ! -d $output_objdir; then + exit $status + fi + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + oldlibs="$oldlibs $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` + fi + + if test "$build_libtool_libs" = yes; then + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case "$deplibs_check_method" in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behaviour. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $rm conftest.c + cat > conftest.c </dev/null` + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null \ + | grep " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | sed 's/.* -> //'` + case "$potliblink" in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \ + | sed 10q \ + | egrep "$file_magic_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + if test -n "$a_deplib" ; then + droppeddeps=yes + echo + echo "*** Warning: This library needs some functionality provided by $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have." + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + if $echo "X $deplibs" | $Xsed -e 's/ -lc$//' \ + -e 's/ -[LR][^ ]*//g' -e 's/[ ]//g' | + grep . >/dev/null; then + echo + if test "X$deplibs_check_method" = "Xnone"; then + echo "*** Warning: inter-library dependencies are not supported in this platform." + else + echo "*** Warning: inter-library dependencies are not known to be supported." + fi + echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + fi + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + echo + echo "*** Warning: libtool could not satisfy all declared inter-library" + echo "*** dependencies of module $libname. Therefore, libtool will create" + echo "*** a static module, that should work as long as the dlopening" + echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + echo "*** The inter-library dependencies that have been dropped here will be" + echo "*** automatically added whenever a program is linked with this library" + echo "*** or is declared to -dlopen it." + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + # Get the real and link names of the library. + eval library_names=\"$library_names_spec\" + set dummy $library_names + realname="$2" + shift; shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + lib="$output_objdir/$realname" + for link + do + linknames="$linknames $link" + done + + # Ensure that we have .o objects for linkers which dislike .lo + # (e.g. aix) in case we are running --disable-static + for obj in $libobjs; do + xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$obj"; then + xdir="." + else + xdir="$xdir" + fi + baseobj=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` + oldobj=`$echo "X$baseobj" | $Xsed -e "$lo2o"` + if test ! -f $xdir/$oldobj; then + $show "(cd $xdir && ${LN_S} $baseobj $oldobj)" + $run eval '(cd $xdir && ${LN_S} $baseobj $oldobj)' || exit $? + fi + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + eval cmds=\"$export_symbols_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + if test -n "$export_symbols_regex"; then + $show "egrep -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"" + $run eval 'egrep -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + $show "$mv \"${export_symbols}T\" \"$export_symbols\"" + $run eval '$mv "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"' + fi + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${outputname}x" + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "mkdir $gentop" + $run mkdir "$gentop" + status=$? + if test $status -ne 0 && test ! -d "$gentop"; then + exit $status + fi + generated="$generated $gentop" + + for xlib in $convenience; do + # Extract the objects. + case "$xlib" in + [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;; + *) xabs=`pwd`"/$xlib" ;; + esac + xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'` + xdir="$gentop/$xlib" + + $show "${rm}r $xdir" + $run ${rm}r "$xdir" + $show "mkdir $xdir" + $run mkdir "$xdir" + status=$? + if test $status -ne 0 && test ! -d "$xdir"; then + exit $status + fi + $show "(cd $xdir && $AR x $xabs)" + $run eval "(cd \$xdir && $AR x \$xabs)" || exit $? + + libobjs="$libobjs "`find $xdir -name \*.o -print -o -name \*.lo -print | $NL2SP` + done + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + linkopts="$linkopts $flag" + fi + + # Do each of the archive commands. + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval cmds=\"$archive_expsym_cmds\" + else + eval cmds=\"$archive_cmds\" + fi + IFS="${IFS= }"; save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)" + $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $? + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + *.lo | *.o | *.obj) + if test -n "$link_against_libtool_libs"; then + $echo "$modename: error: cannot link libtool libraries into objects" 1>&2 + exit 1 + fi + + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for objects" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for objects" 1>&2 + fi + + case "$output" in + *.lo) + if test -n "$objs"; then + $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 + exit 1 + fi + libobj="$output" + obj=`$echo "X$output" | $Xsed -e "$lo2o"` + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $run $rm $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval reload_conv_objs=\"\$reload_objs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${obj}x" + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "mkdir $gentop" + $run mkdir "$gentop" + status=$? + if test $status -ne 0 && test ! -d "$gentop"; then + exit $status + fi + generated="$generated $gentop" + + for xlib in $convenience; do + # Extract the objects. + case "$xlib" in + [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;; + *) xabs=`pwd`"/$xlib" ;; + esac + xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'` + xdir="$gentop/$xlib" + + $show "${rm}r $xdir" + $run ${rm}r "$xdir" + $show "mkdir $xdir" + $run mkdir "$xdir" + status=$? + if test $status -ne 0 && test ! -d "$xdir"; then + exit $status + fi + $show "(cd $xdir && $AR x $xabs)" + $run eval "(cd \$xdir && $AR x \$xabs)" || exit $? + + reload_conv_objs="$reload_objs "`find $xdir -name \*.o -print -o -name \*.lo -print | $NL2SP` + done + fi + fi + + # Create the old-style object. + reload_objs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" + + output="$obj" + eval cmds=\"$reload_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit 0 + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + $show "echo timestamp > $libobj" + $run eval "echo timestamp > $libobj" || exit $? + exit 0 + fi + + if test -n "$pic_flag"; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + eval cmds=\"$reload_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + else + # Just create a symlink. + $show $rm $libobj + $run $rm $libobj + xdir=`$echo "X$libobj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$libobj"; then + xdir="." + else + xdir="$xdir" + fi + baseobj=`$echo "X$libobj" | $Xsed -e 's%^.*/%%'` + oldobj=`$echo "X$baseobj" | $Xsed -e "$lo2o"` + $show "(cd $xdir && $LN_S $oldobj $baseobj)" + $run eval '(cd $xdir && $LN_S $oldobj $baseobj)' || exit $? + fi + + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit 0 + ;; + + # Anything else should be a program. + *) + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for programs" 1>&2 + fi + + if test "$preload" = yes; then + if test "$dlopen" = unknown && test "$dlopen_self" = unknown && + test "$dlopen_self_static" = unknown; then + $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support." + fi + fi + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$compile_rpath " in + *" $libdir "*) ;; + *) compile_rpath="$compile_rpath $libdir" ;; + esac + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case "$hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator" in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case "$hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator" in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` + if test "X$output_objdir" = "X$output"; then + output_objdir="$objdir" + else + output_objdir="$output_objdir/$objdir" + fi + + # Create the binary in the object directory, then wrap it. + if test ! -d $output_objdir; then + $show "$mkdir $output_objdir" + $run $mkdir $output_objdir + status=$? + if test $status -ne 0 && test ! -d $output_objdir; then + exit $status + fi + fi + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + fi + + dlsyms= + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + dlsyms="${outputname}S.c" + else + $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 + fi + fi + + if test -n "$dlsyms"; then + case "$dlsyms" in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${outputname}.nm" + + $show "$rm $nlist ${nlist}S ${nlist}T" + $run $rm "$nlist" "${nlist}S" "${nlist}T" + + # Parse the name list into a source file. + $show "creating $output_objdir/$dlsyms" + + test -z "$run" && $echo > "$output_objdir/$dlsyms" "\ +/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */ +/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +/* Prevent the only kind of declaration conflicts we can make. */ +#define lt_preloaded_symbols some_other_symbol + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + $show "generating symbol list for \`$output'" + + test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$echo "X$objs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + for arg in $progfiles; do + $show "extracting global C symbols from \`$arg'" + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $run eval 'egrep -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + if test -n "$export_symbols_regex"; then + $run eval 'egrep -e "$export_symbols_regex" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$output.exp" + $run $rm $export_symbols + $run eval "sed -n -e '/^: @PROGRAM@$/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + else + $run eval "sed -e 's/\([][.*^$]\)/\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$output.exp"' + $run eval 'grep -f "$output_objdir/$output.exp" < "$nlist" > "$nlist"T' + $run eval 'mv "$nlist"T "$nlist"' + fi + fi + + for arg in $dlprefiles; do + $show "extracting global C symbols from \`$arg'" + name=`echo "$arg" | sed -e 's%^.*/%%'` + $run eval 'echo ": $name " >> "$nlist"' + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -z "$run"; then + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + egrep -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $mv "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if grep -v "^: " < "$nlist" | sort +2 | uniq > "$nlist"S; then + : + else + grep -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"' + else + echo '/* NONE */' >> "$output_objdir/$dlsyms" + fi + + $echo >> "$output_objdir/$dlsyms" "\ + +#undef lt_preloaded_symbols + +#if defined (__STDC__) && __STDC__ +# define lt_ptr_t void * +#else +# define lt_ptr_t char * +# define const +#endif + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + lt_ptr_t address; +} +lt_preloaded_symbols[] = +{\ +" + + sed -n -e 's/^: \([^ ]*\) $/ {\"\1\", (lt_ptr_t) 0},/p' \ + -e 's/^. \([^ ]*\) \([^ ]*\)$/ {"\2", (lt_ptr_t) \&\2},/p' \ + < "$nlist" >> "$output_objdir/$dlsyms" + + $echo >> "$output_objdir/$dlsyms" "\ + {0, (lt_ptr_t) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + fi + + pic_flag_for_symtable= + case "$host" in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag -DPIC -DFREEBSD_WORKAROUND";; + esac;; + *-*-hpux*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag -DPIC";; + esac + esac + + # Now compile the dynamic symbol file. + $show "(cd $output_objdir && $CC -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")" + $run eval '(cd $output_objdir && $CC -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $? + + # Clean up the generated files. + $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T" + $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T" + + # Transform the symbol file into the correct name. + compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + ;; + *) + $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 + exit 1 + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi + + if test -z "$link_against_libtool_libs" || test "$build_libtool_libs" != yes; then + # Replace the output file specification. + compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + $show "$link_command" + $run eval "$link_command" + status=$? + + # Delete the generated files. + if test -n "$dlsyms"; then + $show "$rm $output_objdir/${outputname}S.${objext}" + $run $rm "$output_objdir/${outputname}S.${objext}" + fi + + exit $status + fi + + if test -n "$shlibpath_var"; then + # We should set the shlibpath_var + rpath= + for dir in $temp_rpath; do + case "$dir" in + [\\/]* | [A-Za-z]:[\\/]*) + # Absolute path. + rpath="$rpath$dir:" + ;; + *) + # Relative path: add a thisdir entry. + rpath="$rpath\$thisdir/$dir:" + ;; + esac + done + temp_rpath="$rpath" + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + rpath="$rpath$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2 + $echo "$modename: \`$output' will be relinked during installation" 1>&2 + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname + + $show "$link_command" + $run eval "$link_command" || exit $? + + # Now create the wrapper script. + $show "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` + fi + + # Quote $echo for shipping. + if test "X$echo" = "X$SHELL $0 --fallback-echo"; then + case "$0" in + [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $0 --fallback-echo";; + *) qecho="$SHELL `pwd`/$0 --fallback-echo";; + esac + qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"` + else + qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` + fi + + # Only actually do things if our run command is non-null. + if test -z "$run"; then + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) output=`echo $output|sed 's,.exe$,,'` ;; + esac + $rm $output + trap "$rm $output; exit 1" 1 2 15 + + $echo > $output "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='sed -e 1s/^X//' +sed_quote_subst='$sed_quote_subst' + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test \"\${CDPATH+set}\" = set; then CDPATH=:; export CDPATH; fi + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variable: + link_against_libtool_libs='$link_against_libtool_libs' +else + # When we are sourced in execute mode, \$file and \$echo are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + echo=\"$qecho\" + file=\"\$0\" + # Make sure echo works. + if test \"X\$1\" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift + elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then + # Yippee, \$echo works! + : + else + # Restart under the correct shell, and then maybe \$echo will work. + exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} + fi + fi\ +" + $echo >> $output "\ + + # Find the directory that this script lives in. + thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | sed -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | sed -n 's/.*-> //p'\` + done + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + echo >> $output "\ + program=lt-'$outputname' + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || \\ + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | sed 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $mkdir \"\$progdir\" + else + $rm \"\$progdir/\$file\" + fi" + + echo >> $output "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if (cd \"\$thisdir\" && eval \$relink_command); then : + else + $rm \"\$progdir/\$file\" + exit 1 + fi + fi + + $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $rm \"\$progdir/\$program\"; + $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $rm \"\$progdir/\$file\" + fi" + else + echo >> $output "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + echo >> $output "\ + + if test -f \"\$progdir/\$program\"; then" + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $echo >> $output "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` + + export $shlibpath_var +" + fi + + # fixup the dll searchpath if we need to. + if test -n "$dllsearchpath"; then + $echo >> $output "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + $echo >> $output "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. +" + case $host in + *-*-cygwin* | *-*-mingw | *-*-os2*) + # win32 systems need to use the prog path for dll + # lookup to work + $echo >> $output "\ + exec \$progdir\\\\\$program \${1+\"\$@\"} +" + ;; + *) + $echo >> $output "\ + # Export the path to the program. + PATH=\"\$progdir:\$PATH\" + export PATH + + exec \$program \${1+\"\$@\"} +" + ;; + esac + $echo >> $output "\ + \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\" + exit 1 + fi + else + # The program doesn't exist. + \$echo \"\$0: error: \$progdir/\$program does not exist\" 1>&2 + \$echo \"This script is just a wrapper for \$program.\" 1>&2 + echo \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" + chmod +x $output + fi + exit 0 + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$objs "`$echo "X$libobjs_save" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP` + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "mkdir $gentop" + $run mkdir "$gentop" + status=$? + if test $status -ne 0 && test ! -d "$gentop"; then + exit $status + fi + generated="$generated $gentop" + + # Add in members from convenience archives. + for xlib in $addlibs; do + # Extract the objects. + case "$xlib" in + [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;; + *) xabs=`pwd`"/$xlib" ;; + esac + xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'` + xdir="$gentop/$xlib" + + $show "${rm}r $xdir" + $run ${rm}r "$xdir" + $show "mkdir $xdir" + $run mkdir "$xdir" + status=$? + if test $status -ne 0 && test ! -d "$xdir"; then + exit $status + fi + $show "(cd $xdir && $AR x $xabs)" + $run eval "(cd \$xdir && $AR x \$xabs)" || exit $? + + oldobjs="$oldobjs "`find $xdir -name \*.${objext} -print -o -name \*.lo -print | $NL2SP` + done + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + eval cmds=\"$old_archive_from_new_cmds\" + else + # Ensure that we have .o objects in place in case we decided + # not to build a shared library, and have fallen back to building + # static libs even though --disable-static was passed! + for oldobj in $oldobjs; do + if test ! -f $oldobj; then + xdir=`$echo "X$oldobj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$oldobj"; then + xdir="." + else + xdir="$xdir" + fi + baseobj=`$echo "X$oldobj" | $Xsed -e 's%^.*/%%'` + obj=`$echo "X$baseobj" | $Xsed -e "$o2lo"` + $show "(cd $xdir && ${LN_S} $obj $baseobj)" + $run eval '(cd $xdir && ${LN_S} $obj $baseobj)' || exit $? + fi + done + + eval cmds=\"$old_archive_cmds\" + fi + IFS="${IFS= }"; save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$generated"; then + $show "${rm}r$generated" + $run ${rm}r$generated + fi + + # Now create the libtool archive. + case "$output" in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + $show "creating $output" + + if test -n "$xrpath"; then + temp_xrpath= + for libdir in $xrpath; do + temp_xrpath="$temp_xrpath -R$libdir" + done + dependency_libs="$temp_xrpath $dependency_libs" + fi + + # Only create the output if not a dry run. + if test -z "$run"; then + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + fi + $rm $output + $echo > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$dlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Directory that this library needs to be installed in: +libdir='$install_libdir'\ +" + done + fi + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" + $run eval "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" || exit $? + ;; + esac + exit 0 + ;; + + # libtool install mode + install) + modename="$modename: install" + + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh; then + # Aesthetically quote it. + arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` + case "$arg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$arg " + arg="$1" + shift + else + install_prog= + arg="$nonopt" + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case "$arg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog$arg" + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + for arg + do + if test -n "$dest"; then + files="$files $dest" + dest="$arg" + continue + fi + + case "$arg" in + -d) isdir=yes ;; + -f) prev="-f" ;; + -g) prev="-g" ;; + -m) prev="-m" ;; + -o) prev="-o" ;; + -s) + stripme=" -s" + continue + ;; + -*) ;; + + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + prev= + else + dest="$arg" + continue + fi + ;; + esac + + # Aesthetically quote the argument. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case "$arg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog $arg" + done + + if test -z "$install_prog"; then + $echo "$modename: you must specify an install program" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test -n "$prev"; then + $echo "$modename: the \`$prev' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test -z "$files"; then + if test -z "$dest"; then + $echo "$modename: no file or destination specified" 1>&2 + else + $echo "$modename: you must specify a destination" 1>&2 + fi + $echo "$help" 1>&2 + exit 1 + fi + + # Strip any trailing slash from the destination. + dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` + test "X$destdir" = "X$dest" && destdir=. + destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` + + # Not a directory, so check to see that there is only one file specified. + set dummy $files + if test $# -gt 2; then + $echo "$modename: \`$dest' is not a directory" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + fi + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case "$file" in + *.lo) ;; + *) + $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case "$file" in + *.a | *.lib) + # Do the static libraries later. + staticlibs="$staticlibs $file" + ;; + + *.la) + # Check to see that this really is a libtool archive. + if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + library_names= + old_library= + # If there is no directory component, then add one. + case "$file" in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) current_libdirs="$current_libdirs $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs="$future_libdirs $libdir" ;; + esac + fi + + dir="`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/" + test "X$dir" = "X$file/" && dir= + dir="$dir$objdir" + + # See the names of the shared library. + set dummy $library_names + if test -n "$2"; then + realname="$2" + shift + shift + + # Install the shared library and build the symlinks. + $show "$install_prog $dir/$realname $destdir/$realname" + $run eval "$install_prog $dir/$realname $destdir/$realname" || exit $? + + if test $# -gt 0; then + # Delete the old symlinks, and create new ones. + for linkname + do + if test "$linkname" != "$realname"; then + $show "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)" + $run eval "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)" + fi + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + eval cmds=\"$postinstall_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Install the pseudo-library for information purposes. + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + instname="$dir/$name"i + $show "$install_prog $instname $destdir/$name" + $run eval "$install_prog $instname $destdir/$name" || exit $? + + # Maybe install the static library, too. + test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case "$destfile" in + *.lo) + staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"` + ;; + *.o | *.obj) + staticdest="$destfile" + destfile= + ;; + *) + $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + esac + + # Install the libtool object if requested. + if test -n "$destfile"; then + $show "$install_prog $file $destfile" + $run eval "$install_prog $file $destfile" || exit $? + fi + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + staticobj=`$echo "X$file" | $Xsed -e "$lo2o"` + + $show "$install_prog $staticobj $staticdest" + $run eval "$install_prog \$staticobj \$staticdest" || exit $? + fi + exit 0 + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # Do a test to see if this is really a libtool program. + if (sed -e '4q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + link_against_libtool_libs= + relink_command= + + # If there is no directory component, then add one. + case "$file" in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Check the variables that should have been set. + if test -z "$link_against_libtool_libs"; then + $echo "$modename: invalid libtool wrapper script \`$file'" 1>&2 + exit 1 + fi + + finalize=yes + for lib in $link_against_libtool_libs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + # If there is no directory component, then add one. + case "$lib" in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + fi + libfile="$libdir/`$echo "X$lib" | $Xsed -e 's%^.*/%%g'`" + if test -n "$libdir" && test ! -f "$libfile"; then + $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 + finalize=no + fi + done + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + if test "$finalize" = yes && test -z "$run"; then + tmpdir="/tmp" + test -n "$TMPDIR" && tmpdir="$TMPDIR" + tmpdir="$tmpdir/libtool-$$" + if $mkdir -p "$tmpdir" && chmod 700 "$tmpdir"; then : + else + $echo "$modename: error: cannot create temporary directory \`$tmpdir'" 1>&2 + continue + fi + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` + + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + ${rm}r "$tmpdir" + continue + fi + file="$outputname" + else + $echo "$modename: warning: cannot relink \`$file'" 1>&2 + fi + else + # Install the binary that we compiled earlier. + file=`$echo "X$file" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + $show "$install_prog$stripme $file $destfile" + $run eval "$install_prog\$stripme \$file \$destfile" || exit $? + test -n "$outputname" && ${rm}r "$tmpdir" + ;; + esac + done + + for file in $staticlibs; do + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + + $show "$install_prog $file $oldlib" + $run eval "$install_prog \$file \$oldlib" || exit $? + + # Do each command in the postinstall commands. + eval cmds=\"$old_postinstall_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$future_libdirs"; then + $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 + fi + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + test -n "$run" && current_libdirs=" -n$current_libdirs" + exec $SHELL $0 --finish$current_libdirs + exit 1 + fi + + exit 0 + ;; + + # libtool finish mode + finish) + modename="$modename: finish" + libdirs="$nonopt" + admincmds= + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for dir + do + libdirs="$libdirs $dir" + done + + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + eval cmds=\"$finish_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || admincmds="$admincmds + $cmd" + done + IFS="$save_ifs" + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $run eval "$cmds" || admincmds="$admincmds + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + test "$show" = : && exit 0 + + echo "----------------------------------------------------------------------" + echo "Libraries have been installed in:" + for libdir in $libdirs; do + echo " $libdir" + done + echo + echo "If you ever happen to want to link against installed libraries" + echo "in a given directory, LIBDIR, you must either use libtool, and" + echo "specify the full pathname of the library, or use \`-LLIBDIR'" + echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + echo " during execution" + fi + if test -n "$runpath_var"; then + echo " - add LIBDIR to the \`$runpath_var' environment variable" + echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + echo " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + echo " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + echo + echo "See any operating system documentation about shared libraries for" + echo "more information, such as the ld(1) and ld.so(8) manual pages." + echo "----------------------------------------------------------------------" + exit 0 + ;; + + # libtool execute mode + execute) + modename="$modename: execute" + + # The first argument is the command name. + cmd="$nonopt" + if test -z "$cmd"; then + $echo "$modename: you must specify a COMMAND" 1>&2 + $echo "$help" + exit 1 + fi + + # Handle -dlopen flags immediately. + for file in $execute_dlfiles; do + if test ! -f "$file"; then + $echo "$modename: \`$file' is not a file" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + dir= + case "$file" in + *.la) + # Check to see that this really is a libtool archive. + if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Read the libtool library. + dlname= + library_names= + + # If there is no directory component, then add one. + case "$file" in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" + continue + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + + if test -f "$dir/$objdir/$dlname"; then + dir="$dir/$objdir" + else + $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 + exit 1 + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + ;; + + *) + $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case "$file" in + -*) ;; + *) + # Do a test to see if this is really a libtool program. + if (sed -e '4q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + # If there is no directory component, then add one. + case "$file" in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` + args="$args \"$file\"" + done + + if test -z "$run"; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved enviroment variables + if test "${save_LC_ALL+set}" = set; then + LC_ALL="$save_LC_ALL"; export LC_ALL + fi + if test "${save_LANG+set}" = set; then + LANG="$save_LANG"; export LANG + fi + + # Now actually exec the command. + eval "exec \$cmd$args" + + $echo "$modename: cannot exec \$cmd$args" + exit 1 + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" + $echo "export $shlibpath_var" + fi + $echo "$cmd$args" + exit 0 + fi + ;; + + # libtool uninstall mode + uninstall) + modename="$modename: uninstall" + rm="$nonopt" + files= + + for arg + do + case "$arg" in + -*) rm="$rm $arg" ;; + *) files="$files $arg" ;; + esac + done + + if test -z "$rm"; then + $echo "$modename: you must specify an RM program" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + for file in $files; do + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + + rmfiles="$file" + + case "$name" in + *.la) + # Possibly a libtool archive, so verify it. + if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + . $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + rmfiles="$rmfiles $dir/$n" + done + test -n "$old_library" && rmfiles="$rmfiles $dir/$old_library" + + $show "$rm $rmfiles" + $run $rm $rmfiles + + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + eval cmds=\"$postuninstall_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" + done + IFS="$save_ifs" + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + eval cmds=\"$old_postuninstall_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" + done + IFS="$save_ifs" + fi + + # FIXME: should reinstall the best remaining shared library. + fi + ;; + + *.lo) + if test "$build_old_libs" = yes; then + oldobj=`$echo "X$name" | $Xsed -e "$lo2o"` + rmfiles="$rmfiles $dir/$oldobj" + fi + $show "$rm $rmfiles" + $run $rm $rmfiles + ;; + + *) + $show "$rm $rmfiles" + $run $rm $rmfiles + ;; + esac + done + exit 0 + ;; + + "") + $echo "$modename: you must specify a MODE" 1>&2 + $echo "$generic_help" 1>&2 + exit 1 + ;; + esac + + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$generic_help" 1>&2 + exit 1 +fi # test -z "$show_help" + +# We need to display help for each of the modes. +case "$mode" in +"") $echo \ +"Usage: $modename [OPTION]... [MODE-ARG]... + +Provide generalized library-building support services. + + --config show all configuration variables + --debug enable verbose shell tracing +-n, --dry-run display commands without modifying any files + --features display basic configuration information and exit + --finish same as \`--mode=finish' + --help display this help message and exit + --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] + --quiet same as \`--silent' + --silent don't print informational messages + --version print version information + +MODE must be one of the following: + + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for +a more detailed description of MODE." + exit 0 + ;; + +compile) + $echo \ +"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -static always build a \`.o' file suitable for static linking + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + +execute) + $echo \ +"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + +finish) + $echo \ +"Usage: $modename [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + +install) + $echo \ +"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + +link) + $echo \ +"Usage: $modename [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -static do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + +uninstall) + $echo \ +"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + +*) + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; +esac + +echo +$echo "Try \`$modename --help' for more information about other modes." + +exit 0 + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/srclib/pcre/maketables.c b/srclib/pcre/maketables.c new file mode 100644 index 00000000000..c0f06c0375b --- /dev/null +++ b/srclib/pcre/maketables.c @@ -0,0 +1,132 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +Written by: Philip Hazel + + Copyright (c) 1997-2000 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- + +See the file Tech.Notes for some information on the internals. +*/ + + +/* This file is compiled on its own as part of the PCRE library. However, +it is also included in the compilation of dftables.c, in which case the macro +DFTABLES is defined. */ + +#ifndef DFTABLES +#include "internal.h" +#endif + + + +/************************************************* +* Create PCRE character tables * +*************************************************/ + +/* This function builds a set of character tables for use by PCRE and returns +a pointer to them. They are build using the ctype functions, and consequently +their contents will depend upon the current locale setting. When compiled as +part of the library, the store is obtained via pcre_malloc(), but when compiled +inside dftables, use malloc(). + +Arguments: none +Returns: pointer to the contiguous block of data +*/ + +unsigned const char * +pcre_maketables(void) +{ +unsigned char *yield, *p; +int i; + +#ifndef DFTABLES +yield = (unsigned char*)(pcre_malloc)(tables_length); +#else +yield = (unsigned char*)malloc(tables_length); +#endif + +if (yield == NULL) return NULL; +p = yield; + +/* First comes the lower casing table */ + +for (i = 0; i < 256; i++) *p++ = tolower(i); + +/* Next the case-flipping table */ + +for (i = 0; i < 256; i++) *p++ = islower(i)? toupper(i) : tolower(i); + +/* Then the character class tables. Don't try to be clever and save effort +on exclusive ones - in some locales things may be different. */ + +memset(p, 0, cbit_length); +for (i = 0; i < 256; i++) + { + if (isdigit(i)) + { + p[cbit_digit + i/8] |= 1 << (i&7); + p[cbit_word + i/8] |= 1 << (i&7); + } + if (isupper(i)) + { + p[cbit_upper + i/8] |= 1 << (i&7); + p[cbit_word + i/8] |= 1 << (i&7); + } + if (islower(i)) + { + p[cbit_lower + i/8] |= 1 << (i&7); + p[cbit_word + i/8] |= 1 << (i&7); + } + if (i == '_') p[cbit_word + i/8] |= 1 << (i&7); + if (isspace(i)) p[cbit_space + i/8] |= 1 << (i&7); + if (isxdigit(i))p[cbit_xdigit + i/8] |= 1 << (i&7); + if (isgraph(i)) p[cbit_graph + i/8] |= 1 << (i&7); + if (isprint(i)) p[cbit_print + i/8] |= 1 << (i&7); + if (ispunct(i)) p[cbit_punct + i/8] |= 1 << (i&7); + if (iscntrl(i)) p[cbit_cntrl + i/8] |= 1 << (i&7); + } +p += cbit_length; + +/* Finally, the character type table */ + +for (i = 0; i < 256; i++) + { + int x = 0; + if (isspace(i)) x += ctype_space; + if (isalpha(i)) x += ctype_letter; + if (isdigit(i)) x += ctype_digit; + if (isxdigit(i)) x += ctype_xdigit; + if (isalnum(i) || i == '_') x += ctype_word; + if (strchr("*+?{^.$|()[", i) != 0) x += ctype_meta; + *p++ = x; + } + +return yield; +} + +/* End of maketables.c */ diff --git a/srclib/pcre/pcre-config.in b/srclib/pcre/pcre-config.in new file mode 100644 index 00000000000..8daded9fe12 --- /dev/null +++ b/srclib/pcre/pcre-config.in @@ -0,0 +1,59 @@ +#!/bin/sh + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +exec_prefix_set=no + +usage="\ +Usage: pcre-config [--prefix] [--exec-prefix] [--version] [--libs] [--libs-posix] [--cflags] [--cflags-posix]" + +if test $# -eq 0; then + echo "${usage}" 1>&2 + exit 1 +fi + +while test $# -gt 0; do + case "$1" in + -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case $1 in + --prefix=*) + prefix=$optarg + if test $exec_prefix_set = no ; then + exec_prefix=$optarg + fi + ;; + --prefix) + echo $prefix + ;; + --exec-prefix=*) + exec_prefix=$optarg + exec_prefix_set=yes + ;; + --exec-prefix) + echo $exec_prefix + ;; + --version) + echo @PCRE_VERSION@ + ;; + --cflags | --cflags-posix) + if test @includedir@ != /usr/include ; then + includes=-I@includedir@ + fi + echo $includes + ;; + --libs-posix) + echo -L@libdir@ -lpcreposix -lpcre + ;; + --libs) + echo -L@libdir@ -lpcre + ;; + *) + echo "${usage}" 1>&2 + exit 1 + ;; + esac + shift +done diff --git a/srclib/pcre/pcre.c b/srclib/pcre/pcre.c new file mode 100644 index 00000000000..e45dee8d965 --- /dev/null +++ b/srclib/pcre/pcre.c @@ -0,0 +1,4769 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-2000 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- +*/ + + +/* Define DEBUG to get debugging output on stdout. */ + +/* #define DEBUG */ + +/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef +inline, and there are *still* stupid compilers about that don't like indented +pre-processor statements. I suppose it's only been 10 years... */ + +#ifdef DEBUG +#define DPRINTF(p) printf p +#else +#define DPRINTF(p) /*nothing*/ +#endif + +/* Include the internals header, which itself includes Standard C headers plus +the external pcre header. */ + +#include "internal.h" + + +/* Allow compilation as C++ source code, should anybody want to do that. */ + +#ifdef __cplusplus +#define class pcre_class +#endif + + +/* Number of items on the nested bracket stacks at compile time. This should +not be set greater than 200. */ + +#define BRASTACK_SIZE 200 + + +/* Min and max values for the common repeats; for the maxima, 0 => infinity */ + +static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; +static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; + +/* Text forms of OP_ values and things, for debugging (not all used) */ + +#ifdef DEBUG +static const char *OP_names[] = { + "End", "\\A", "\\B", "\\b", "\\D", "\\d", + "\\S", "\\s", "\\W", "\\w", "\\Z", "\\z", + "Opt", "^", "$", "Any", "chars", "not", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", + "class", "Ref", "Recurse", + "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", + "AssertB", "AssertB not", "Reverse", "Once", "Cond", "Cref", + "Brazero", "Braminzero", "Bra" +}; +#endif + +/* Table for handling escaped characters in the range '0'-'z'. Positive returns +are simple data values; negative values are for special things like \d and so +on. Zero means further processing is needed (for things like \x), or the escape +is invalid. */ + +static const short int escapes[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ + 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ + '@', -ESC_A, -ESC_B, 0, -ESC_D, 0, 0, 0, /* @ - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, -ESC_S, 0, 0, 0, -ESC_W, /* P - W */ + 0, 0, -ESC_Z, '[', '\\', ']', '^', '_', /* X - _ */ + '`', 7, -ESC_b, 0, -ESC_d, 27, '\f', 0, /* ` - g */ + 0, 0, 0, 0, 0, 0, '\n', 0, /* h - o */ + 0, 0, '\r', -ESC_s, '\t', 0, 0, -ESC_w, /* p - w */ + 0, 0, -ESC_z /* x - z */ +}; + +/* Tables of names of POSIX character classes and their lengths. The list is +terminated by a zero length entry. The first three must be alpha, upper, lower, +as this is assumed for handling case independence. */ + +static const char *posix_names[] = { + "alpha", "lower", "upper", + "alnum", "ascii", "cntrl", "digit", "graph", + "print", "punct", "space", "word", "xdigit" }; + +static const uschar posix_name_lengths[] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; + +/* Table of class bit maps for each POSIX class; up to three may be combined +to form the class. */ + +static const int posix_class_maps[] = { + cbit_lower, cbit_upper, -1, /* alpha */ + cbit_lower, -1, -1, /* lower */ + cbit_upper, -1, -1, /* upper */ + cbit_digit, cbit_lower, cbit_upper, /* alnum */ + cbit_print, cbit_cntrl, -1, /* ascii */ + cbit_cntrl, -1, -1, /* cntrl */ + cbit_digit, -1, -1, /* digit */ + cbit_graph, -1, -1, /* graph */ + cbit_print, -1, -1, /* print */ + cbit_punct, -1, -1, /* punct */ + cbit_space, -1, -1, /* space */ + cbit_word, -1, -1, /* word */ + cbit_xdigit,-1, -1 /* xdigit */ +}; + + +/* Definition to allow mutual recursion */ + +static BOOL + compile_regex(int, int, int *, uschar **, const uschar **, const char **, + BOOL, int, int *, int *, compile_data *); + + + +/************************************************* +* Global variables * +*************************************************/ + +/* PCRE is thread-clean and doesn't use any global variables in the normal +sense. However, it calls memory allocation and free functions via the two +indirections below, which are can be changed by the caller, but are shared +between all threads. */ + +void *(*pcre_malloc)(size_t) = malloc; +void (*pcre_free)(void *) = free; + + + + +/************************************************* +* Default character tables * +*************************************************/ + +/* A default set of character tables is included in the PCRE binary. Its source +is built by the maketables auxiliary program, which uses the default C ctypes +functions, and put in the file chartables.c. These tables are used by PCRE +whenever the caller of pcre_compile() does not provide an alternate set of +tables. */ + +#include "chartables.c" + + + +/************************************************* +* Return version string * +*************************************************/ + +#define STRING(a) # a +#define XSTRING(s) STRING(s) + +const char * +pcre_version(void) +{ +return XSTRING(PCRE_MAJOR) "." XSTRING(PCRE_MINOR) " " XSTRING(PCRE_DATE); +} + + + + +/************************************************* +* (Obsolete) Return info about compiled pattern * +*************************************************/ + +/* This is the original "info" function. It picks potentially useful data out +of the private structure, but its interface was too rigid. It remains for +backwards compatibility. The public options are passed back in an int - though +the re->options field has been expanded to a long int, all the public options +at the low end of it, and so even on 16-bit systems this will still be OK. +Therefore, I haven't changed the API for pcre_info(). + +Arguments: + external_re points to compiled code + optptr where to pass back the options + first_char where to pass back the first character, + or -1 if multiline and all branches start ^, + or -2 otherwise + +Returns: number of capturing subpatterns + or negative values on error +*/ + +int +pcre_info(const pcre *external_re, int *optptr, int *first_char) +{ +const real_pcre *re = (const real_pcre *)external_re; +if (re == NULL) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; +if (optptr != NULL) *optptr = (int)(re->options & PUBLIC_OPTIONS); +if (first_char != NULL) + *first_char = ((re->options & PCRE_FIRSTSET) != 0)? re->first_char : + ((re->options & PCRE_STARTLINE) != 0)? -1 : -2; +return re->top_bracket; +} + + + +/************************************************* +* Return info about compiled pattern * +*************************************************/ + +/* This is a newer "info" function which has an extensible interface so +that additional items can be added compatibly. + +Arguments: + external_re points to compiled code + external_study points to study data, or NULL + what what information is required + where where to put the information + +Returns: 0 if data returned, negative on error +*/ + +int +pcre_fullinfo(const pcre *external_re, const pcre_extra *study_data, int what, + void *where) +{ +const real_pcre *re = (const real_pcre *)external_re; +const real_pcre_extra *study = (const real_pcre_extra *)study_data; + +if (re == NULL || where == NULL) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; + +switch (what) + { + case PCRE_INFO_OPTIONS: + *((unsigned long int *)where) = re->options & PUBLIC_OPTIONS; + break; + + case PCRE_INFO_SIZE: + *((size_t *)where) = re->size; + break; + + case PCRE_INFO_CAPTURECOUNT: + *((int *)where) = re->top_bracket; + break; + + case PCRE_INFO_BACKREFMAX: + *((int *)where) = re->top_backref; + break; + + case PCRE_INFO_FIRSTCHAR: + *((int *)where) = + ((re->options & PCRE_FIRSTSET) != 0)? re->first_char : + ((re->options & PCRE_STARTLINE) != 0)? -1 : -2; + break; + + case PCRE_INFO_FIRSTTABLE: + *((const uschar **)where) = + (study != NULL && (study->options & PCRE_STUDY_MAPPED) != 0)? + study->start_bits : NULL; + break; + + case PCRE_INFO_LASTLITERAL: + *((int *)where) = + ((re->options & PCRE_REQCHSET) != 0)? re->req_char : -1; + break; + + default: return PCRE_ERROR_BADOPTION; + } + +return 0; +} + + + +#ifdef DEBUG +/************************************************* +* Debugging function to print chars * +*************************************************/ + +/* Print a sequence of chars in printable format, stopping at the end of the +subject if the requested. + +Arguments: + p points to characters + length number to print + is_subject TRUE if printing from within md->start_subject + md pointer to matching data block, if is_subject is TRUE + +Returns: nothing +*/ + +static void +pchars(const uschar *p, int length, BOOL is_subject, match_data *md) +{ +int c; +if (is_subject && length > md->end_subject - p) length = md->end_subject - p; +while (length-- > 0) + if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c); +} +#endif + + + + +/************************************************* +* Handle escapes * +*************************************************/ + +/* This function is called when a \ has been encountered. It either returns a +positive value for a simple escape such as \n, or a negative value which +encodes one of the more complicated things such as \d. On entry, ptr is +pointing at the \. On exit, it is on the final character of the escape +sequence. + +Arguments: + ptrptr points to the pattern position pointer + errorptr points to the pointer to the error message + bracount number of previous extracting brackets + options the options bits + isclass TRUE if inside a character class + cd pointer to char tables block + +Returns: zero or positive => a data character + negative => a special escape sequence + on error, errorptr is set +*/ + +static int +check_escape(const uschar **ptrptr, const char **errorptr, int bracount, + int options, BOOL isclass, compile_data *cd) +{ +const uschar *ptr = *ptrptr; +int c, i; + +c = *(++ptr) & 255; /* Ensure > 0 on signed-char systems */ +if (c == 0) *errorptr = ERR1; + +/* Digits or letters may have special meaning; all others are literals. */ + +else if (c < '0' || c > 'z') {} + +/* Do an initial lookup in a table. A non-zero result is something that can be +returned immediately. Otherwise further processing may be required. */ + +else if ((i = escapes[c - '0']) != 0) c = i; + +/* Escapes that need further processing, or are illegal. */ + +else + { + const uschar *oldptr; + switch (c) + { + /* The handling of escape sequences consisting of a string of digits + starting with one that is not zero is not straightforward. By experiment, + the way Perl works seems to be as follows: + + Outside a character class, the digits are read as a decimal number. If the + number is less than 10, or if there are that many previous extracting + left brackets, then it is a back reference. Otherwise, up to three octal + digits are read to form an escaped byte. Thus \123 is likely to be octal + 123 (cf \0123, which is octal 012 followed by the literal 3). If the octal + value is greater than 377, the least significant 8 bits are taken. Inside a + character class, \ followed by a digit is always an octal number. */ + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + + if (!isclass) + { + oldptr = ptr; + c -= '0'; + while ((cd->ctypes[ptr[1]] & ctype_digit) != 0) + c = c * 10 + *(++ptr) - '0'; + if (c < 10 || c <= bracount) + { + c = -(ESC_REF + c); + break; + } + ptr = oldptr; /* Put the pointer back and fall through */ + } + + /* Handle an octal number following \. If the first digit is 8 or 9, Perl + generates a binary zero byte and treats the digit as a following literal. + Thus we have to pull back the pointer by one. */ + + if ((c = *ptr) >= '8') + { + ptr--; + c = 0; + break; + } + + /* \0 always starts an octal number, but we may drop through to here with a + larger first octal digit */ + + case '0': + c -= '0'; + while(i++ < 2 && (cd->ctypes[ptr[1]] & ctype_digit) != 0 && + ptr[1] != '8' && ptr[1] != '9') + c = c * 8 + *(++ptr) - '0'; + break; + + /* Special escapes not starting with a digit are straightforward */ + + case 'x': + c = 0; + while (i++ < 2 && (cd->ctypes[ptr[1]] & ctype_xdigit) != 0) + { + ptr++; + c = c * 16 + cd->lcc[*ptr] - + (((cd->ctypes[*ptr] & ctype_digit) != 0)? '0' : 'W'); + } + break; + + case 'c': + c = *(++ptr); + if (c == 0) + { + *errorptr = ERR2; + return 0; + } + + /* A letter is upper-cased; then the 0x40 bit is flipped */ + + if (c >= 'a' && c <= 'z') c = cd->fcc[c]; + c ^= 0x40; + break; + + /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any + other alphameric following \ is an error if PCRE_EXTRA was set; otherwise, + for Perl compatibility, it is a literal. This code looks a bit odd, but + there used to be some cases other than the default, and there may be again + in future, so I haven't "optimized" it. */ + + default: + if ((options & PCRE_EXTRA) != 0) switch(c) + { + default: + *errorptr = ERR3; + break; + } + break; + } + } + +*ptrptr = ptr; +return c; +} + + + +/************************************************* +* Check for counted repeat * +*************************************************/ + +/* This function is called when a '{' is encountered in a place where it might +start a quantifier. It looks ahead to see if it really is a quantifier or not. +It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} +where the ddds are digits. + +Arguments: + p pointer to the first char after '{' + cd pointer to char tables block + +Returns: TRUE or FALSE +*/ + +static BOOL +is_counted_repeat(const uschar *p, compile_data *cd) +{ +if ((cd->ctypes[*p++] & ctype_digit) == 0) return FALSE; +while ((cd->ctypes[*p] & ctype_digit) != 0) p++; +if (*p == '}') return TRUE; + +if (*p++ != ',') return FALSE; +if (*p == '}') return TRUE; + +if ((cd->ctypes[*p++] & ctype_digit) == 0) return FALSE; +while ((cd->ctypes[*p] & ctype_digit) != 0) p++; +return (*p == '}'); +} + + + +/************************************************* +* Read repeat counts * +*************************************************/ + +/* Read an item of the form {n,m} and return the values. This is called only +after is_counted_repeat() has confirmed that a repeat-count quantifier exists, +so the syntax is guaranteed to be correct, but we need to check the values. + +Arguments: + p pointer to first char after '{' + minp pointer to int for min + maxp pointer to int for max + returned as -1 if no max + errorptr points to pointer to error message + cd pointer to character tables clock + +Returns: pointer to '}' on success; + current ptr on error, with errorptr set +*/ + +static const uschar * +read_repeat_counts(const uschar *p, int *minp, int *maxp, + const char **errorptr, compile_data *cd) +{ +int min = 0; +int max = -1; + +while ((cd->ctypes[*p] & ctype_digit) != 0) min = min * 10 + *p++ - '0'; + +if (*p == '}') max = min; else + { + if (*(++p) != '}') + { + max = 0; + while((cd->ctypes[*p] & ctype_digit) != 0) max = max * 10 + *p++ - '0'; + if (max < min) + { + *errorptr = ERR4; + return p; + } + } + } + +/* Do paranoid checks, then fill in the required variables, and pass back the +pointer to the terminating '}'. */ + +if (min > 65535 || max > 65535) + *errorptr = ERR5; +else + { + *minp = min; + *maxp = max; + } +return p; +} + + + +/************************************************* +* Find the fixed length of a pattern * +*************************************************/ + +/* Scan a pattern and compute the fixed length of subject that will match it, +if the length is fixed. This is needed for dealing with backward assertions. + +Arguments: + code points to the start of the pattern (the bracket) + +Returns: the fixed length, or -1 if there is no fixed length +*/ + +static int +find_fixedlength(uschar *code) +{ +int length = -1; + +register int branchlength = 0; +register uschar *cc = code + 3; + +/* Scan along the opcodes for this branch. If we get to the end of the +branch, check the length against that of the other branches. */ + +for (;;) + { + int d; + register int op = *cc; + if (op >= OP_BRA) op = OP_BRA; + + switch (op) + { + case OP_BRA: + case OP_ONCE: + case OP_COND: + d = find_fixedlength(cc); + if (d < 0) return -1; + branchlength += d; + do cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT); + cc += 3; + break; + + /* Reached end of a branch; if it's a ket it is the end of a nested + call. If it's ALT it is an alternation in a nested call. If it is + END it's the end of the outer call. All can be handled by the same code. */ + + case OP_ALT: + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_END: + if (length < 0) length = branchlength; + else if (length != branchlength) return -1; + if (*cc != OP_ALT) return length; + cc += 3; + branchlength = 0; + break; + + /* Skip over assertive subpatterns */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT); + cc += 3; + break; + + /* Skip over things that don't match chars */ + + case OP_REVERSE: + cc++; + /* Fall through */ + + case OP_CREF: + case OP_OPT: + cc++; + /* Fall through */ + + case OP_SOD: + case OP_EOD: + case OP_EODN: + case OP_CIRC: + case OP_DOLL: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + cc++; + break; + + /* Handle char strings */ + + case OP_CHARS: + branchlength += *(++cc); + cc += *cc + 1; + break; + + /* Handle exact repetitions */ + + case OP_EXACT: + case OP_TYPEEXACT: + branchlength += (cc[1] << 8) + cc[2]; + cc += 4; + break; + + /* Handle single-char matchers */ + + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + branchlength++; + cc++; + break; + + + /* Check a class for variable quantification */ + + case OP_CLASS: + cc += (*cc == OP_REF)? 2 : 33; + + switch (*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + return -1; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if ((cc[1] << 8) + cc[2] != (cc[3] << 8) + cc[4]) return -1; + branchlength += (cc[1] << 8) + cc[2]; + cc += 5; + break; + + default: + branchlength++; + } + break; + + /* Anything else is variable length */ + + default: + return -1; + } + } +/* Control never gets here */ +} + + + + +/************************************************* +* Check for POSIX class syntax * +*************************************************/ + +/* This function is called when the sequence "[:" or "[." or "[=" is +encountered in a character class. It checks whether this is followed by an +optional ^ and then a sequence of letters, terminated by a matching ":]" or +".]" or "=]". + +Argument: + ptr pointer to the initial [ + endptr where to return the end pointer + cd pointer to compile data + +Returns: TRUE or FALSE +*/ + +static BOOL +check_posix_syntax(const uschar *ptr, const uschar **endptr, compile_data *cd) +{ +int terminator; /* Don't combine these lines; the Solaris cc */ +terminator = *(++ptr); /* compiler warns about "non-constant" initializer. */ +if (*(++ptr) == '^') ptr++; +while ((cd->ctypes[*ptr] & ctype_letter) != 0) ptr++; +if (*ptr == terminator && ptr[1] == ']') + { + *endptr = ptr; + return TRUE; + } +return FALSE; +} + + + + +/************************************************* +* Check POSIX class name * +*************************************************/ + +/* This function is called to check the name given in a POSIX-style class entry +such as [:alnum:]. + +Arguments: + ptr points to the first letter + len the length of the name + +Returns: a value representing the name, or -1 if unknown +*/ + +static int +check_posix_name(const uschar *ptr, int len) +{ +register int yield = 0; +while (posix_name_lengths[yield] != 0) + { + if (len == posix_name_lengths[yield] && + strncmp((const char *)ptr, posix_names[yield], len) == 0) return yield; + yield++; + } +return -1; +} + + + + +/************************************************* +* Compile one branch * +*************************************************/ + +/* Scan the pattern, compiling it into the code vector. + +Arguments: + options the option bits + brackets points to number of brackets used + code points to the pointer to the current code point + ptrptr points to the current pattern pointer + errorptr points to pointer to error message + optchanged set to the value of the last OP_OPT item compiled + reqchar set to the last literal character required, else -1 + countlits set to count of mandatory literal characters + cd contains pointers to tables + +Returns: TRUE on success + FALSE, with *errorptr set on error +*/ + +static BOOL +compile_branch(int options, int *brackets, uschar **codeptr, + const uschar **ptrptr, const char **errorptr, int *optchanged, + int *reqchar, int *countlits, compile_data *cd) +{ +int repeat_type, op_type; +int repeat_min, repeat_max; +int bravalue, length; +int greedy_default, greedy_non_default; +int prevreqchar; +int condcount = 0; +int subcountlits = 0; +register int c; +register uschar *code = *codeptr; +uschar *tempcode; +const uschar *ptr = *ptrptr; +const uschar *tempptr; +uschar *previous = NULL; +uschar class[32]; + +/* Set up the default and non-default settings for greediness */ + +greedy_default = ((options & PCRE_UNGREEDY) != 0); +greedy_non_default = greedy_default ^ 1; + +/* Initialize no required char, and count of literals */ + +*reqchar = prevreqchar = -1; +*countlits = 0; + +/* Switch on next character until the end of the branch */ + +for (;; ptr++) + { + BOOL negate_class; + int class_charcount; + int class_lastchar; + int newoptions; + int condref; + int subreqchar; + + c = *ptr; + if ((options & PCRE_EXTENDED) != 0) + { + if ((cd->ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while ((c = *(++ptr)) != 0 && c != '\n'); + continue; + } + } + + switch(c) + { + /* The branch terminates at end of string, |, or ). */ + + case 0: + case '|': + case ')': + *codeptr = code; + *ptrptr = ptr; + return TRUE; + + /* Handle single-character metacharacters */ + + case '^': + previous = NULL; + *code++ = OP_CIRC; + break; + + case '$': + previous = NULL; + *code++ = OP_DOLL; + break; + + case '.': + previous = code; + *code++ = OP_ANY; + break; + + /* Character classes. These always build a 32-byte bitmap of the permitted + characters, except in the special case where there is only one character. + For negated classes, we build the map as usual, then invert it at the end. + */ + + case '[': + previous = code; + *code++ = OP_CLASS; + + /* If the first character is '^', set the negation flag and skip it. */ + + if ((c = *(++ptr)) == '^') + { + negate_class = TRUE; + c = *(++ptr); + } + else negate_class = FALSE; + + /* Keep a count of chars so that we can optimize the case of just a single + character. */ + + class_charcount = 0; + class_lastchar = -1; + + /* Initialize the 32-char bit map to all zeros. We have to build the + map in a temporary bit of store, in case the class contains only 1 + character, because in that case the compiled code doesn't use the + bit map. */ + + memset(class, 0, 32 * sizeof(uschar)); + + /* Process characters until ] is reached. By writing this as a "do" it + means that an initial ] is taken as a data character. */ + + do + { + if (c == 0) + { + *errorptr = ERR6; + goto FAILED; + } + + /* Handle POSIX class names. Perl allows a negation extension of the + form [:^name]. A square bracket that doesn't match the syntax is + treated as a literal. We also recognize the POSIX constructions + [.ch.] and [=ch=] ("collating elements") and fault them, as Perl + 5.6 does. */ + + if (c == '[' && + (ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && + check_posix_syntax(ptr, &tempptr, cd)) + { + BOOL local_negate = FALSE; + int posix_class, i; + register const uschar *cbits = cd->cbits; + + if (ptr[1] != ':') + { + *errorptr = ERR31; + goto FAILED; + } + + ptr += 2; + if (*ptr == '^') + { + local_negate = TRUE; + ptr++; + } + + posix_class = check_posix_name(ptr, tempptr - ptr); + if (posix_class < 0) + { + *errorptr = ERR30; + goto FAILED; + } + + /* If matching is caseless, upper and lower are converted to + alpha. This relies on the fact that the class table starts with + alpha, lower, upper as the first 3 entries. */ + + if ((options & PCRE_CASELESS) != 0 && posix_class <= 2) + posix_class = 0; + + /* Or into the map we are building up to 3 of the static class + tables, or their negations. */ + + posix_class *= 3; + for (i = 0; i < 3; i++) + { + int taboffset = posix_class_maps[posix_class + i]; + if (taboffset < 0) break; + if (local_negate) + for (c = 0; c < 32; c++) class[c] |= ~cbits[c+taboffset]; + else + for (c = 0; c < 32; c++) class[c] |= cbits[c+taboffset]; + } + + ptr = tempptr + 1; + class_charcount = 10; /* Set > 1; assumes more than 1 per class */ + continue; + } + + /* Backslash may introduce a single character, or it may introduce one + of the specials, which just set a flag. Escaped items are checked for + validity in the pre-compiling pass. The sequence \b is a special case. + Inside a class (and only there) it is treated as backspace. Elsewhere + it marks a word boundary. Other escapes have preset maps ready to + or into the one we are building. We assume they have more than one + character in them, so set class_count bigger than one. */ + + if (c == '\\') + { + c = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd); + if (-c == ESC_b) c = '\b'; + else if (c < 0) + { + register const uschar *cbits = cd->cbits; + class_charcount = 10; + switch (-c) + { + case ESC_d: + for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_digit]; + continue; + + case ESC_D: + for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_digit]; + continue; + + case ESC_w: + for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_word]; + continue; + + case ESC_W: + for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_word]; + continue; + + case ESC_s: + for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_space]; + continue; + + case ESC_S: + for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_space]; + continue; + + default: + *errorptr = ERR7; + goto FAILED; + } + } + /* Fall through if single character */ + } + + /* A single character may be followed by '-' to form a range. However, + Perl does not permit ']' to be the end of the range. A '-' character + here is treated as a literal. */ + + if (ptr[1] == '-' && ptr[2] != ']') + { + int d; + ptr += 2; + d = *ptr; + + if (d == 0) + { + *errorptr = ERR6; + goto FAILED; + } + + /* The second part of a range can be a single-character escape, but + not any of the other escapes. */ + + if (d == '\\') + { + d = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd); + if (d < 0) + { + if (d == -ESC_b) d = '\b'; else + { + *errorptr = ERR7; + goto FAILED; + } + } + } + + if (d < c) + { + *errorptr = ERR8; + goto FAILED; + } + + for (; c <= d; c++) + { + class[c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + int uc = cd->fcc[c]; /* flip case */ + class[uc/8] |= (1 << (uc&7)); + } + class_charcount++; /* in case a one-char range */ + class_lastchar = c; + } + continue; /* Go get the next char in the class */ + } + + /* Handle a lone single character - we can get here for a normal + non-escape char, or after \ that introduces a single character. */ + + class [c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + c = cd->fcc[c]; /* flip case */ + class[c/8] |= (1 << (c&7)); + } + class_charcount++; + class_lastchar = c; + } + + /* Loop until ']' reached; the check for end of string happens inside the + loop. This "while" is the end of the "do" above. */ + + while ((c = *(++ptr)) != ']'); + + /* If class_charcount is 1 and class_lastchar is not negative, we saw + precisely one character. This doesn't need the whole 32-byte bit map. + We turn it into a 1-character OP_CHAR if it's positive, or OP_NOT if + it's negative. */ + + if (class_charcount == 1 && class_lastchar >= 0) + { + if (negate_class) + { + code[-1] = OP_NOT; + } + else + { + code[-1] = OP_CHARS; + *code++ = 1; + } + *code++ = class_lastchar; + } + + /* Otherwise, negate the 32-byte map if necessary, and copy it into + the code vector. */ + + else + { + if (negate_class) + for (c = 0; c < 32; c++) code[c] = ~class[c]; + else + memcpy(code, class, 32); + code += 32; + } + break; + + /* Various kinds of repeat */ + + case '{': + if (!is_counted_repeat(ptr+1, cd)) goto NORMAL_CHAR; + ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorptr, cd); + if (*errorptr != NULL) goto FAILED; + goto REPEAT; + + case '*': + repeat_min = 0; + repeat_max = -1; + goto REPEAT; + + case '+': + repeat_min = 1; + repeat_max = -1; + goto REPEAT; + + case '?': + repeat_min = 0; + repeat_max = 1; + + REPEAT: + if (previous == NULL) + { + *errorptr = ERR9; + goto FAILED; + } + + /* If the next character is '?' this is a minimizing repeat, by default, + but if PCRE_UNGREEDY is set, it works the other way round. Advance to the + next character. */ + + if (ptr[1] == '?') + { repeat_type = greedy_non_default; ptr++; } + else repeat_type = greedy_default; + + /* If previous was a string of characters, chop off the last one and use it + as the subject of the repeat. If there was only one character, we can + abolish the previous item altogether. A repeat with a zero minimum wipes + out any reqchar setting, backing up to the previous value. We must also + adjust the countlits value. */ + + if (*previous == OP_CHARS) + { + int len = previous[1]; + + if (repeat_min == 0) *reqchar = prevreqchar; + *countlits += repeat_min - 1; + + if (len == 1) + { + c = previous[2]; + code = previous; + } + else + { + c = previous[len+1]; + previous[1]--; + code--; + } + op_type = 0; /* Use single-char op codes */ + goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ + } + + /* If previous was a single negated character ([^a] or similar), we use + one of the special opcodes, replacing it. The code is shared with single- + character repeats by adding a suitable offset into repeat_type. */ + + else if ((int)*previous == OP_NOT) + { + op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ + c = previous[1]; + code = previous; + goto OUTPUT_SINGLE_REPEAT; + } + + /* If previous was a character type match (\d or similar), abolish it and + create a suitable repeat item. The code is shared with single-character + repeats by adding a suitable offset into repeat_type. */ + + else if ((int)*previous < OP_EODN || *previous == OP_ANY) + { + op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + c = *previous; + code = previous; + + OUTPUT_SINGLE_REPEAT: + + /* If the maximum is zero then the minimum must also be zero; Perl allows + this case, so we do too - by simply omitting the item altogether. */ + + if (repeat_max == 0) goto END_REPEAT; + + /* Combine the op_type with the repeat_type */ + + repeat_type += op_type; + + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ + + if (repeat_min == 0) + { + if (repeat_max == -1) *code++ = OP_STAR + repeat_type; + else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; + else + { + *code++ = OP_UPTO + repeat_type; + *code++ = repeat_max >> 8; + *code++ = (repeat_max & 255); + } + } + + /* The case {1,} is handled as the special case + */ + + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_PLUS + repeat_type; + + /* The case {n,n} is just an EXACT, while the general case {n,m} is + handled as an EXACT followed by an UPTO. An EXACT of 1 is optimized. */ + + else + { + if (repeat_min != 1) + { + *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ + *code++ = repeat_min >> 8; + *code++ = (repeat_min & 255); + } + + /* If the mininum is 1 and the previous item was a character string, + we either have to put back the item that got cancelled if the string + length was 1, or add the character back onto the end of a longer + string. For a character type nothing need be done; it will just get + put back naturally. Note that the final character is always going to + get added below. */ + + else if (*previous == OP_CHARS) + { + if (code == previous) code += 2; else previous[1]++; + } + + /* For a single negated character we also have to put back the + item that got cancelled. */ + + else if (*previous == OP_NOT) code++; + + /* If the maximum is unlimited, insert an OP_STAR. */ + + if (repeat_max < 0) + { + *code++ = c; + *code++ = OP_STAR + repeat_type; + } + + /* Else insert an UPTO if the max is greater than the min. */ + + else if (repeat_max != repeat_min) + { + *code++ = c; + repeat_max -= repeat_min; + *code++ = OP_UPTO + repeat_type; + *code++ = repeat_max >> 8; + *code++ = (repeat_max & 255); + } + } + + /* The character or character type itself comes last in all cases. */ + + *code++ = c; + } + + /* If previous was a character class or a back reference, we put the repeat + stuff after it, but just skip the item if the repeat was {0,0}. */ + + else if (*previous == OP_CLASS || *previous == OP_REF) + { + if (repeat_max == 0) + { + code = previous; + goto END_REPEAT; + } + if (repeat_min == 0 && repeat_max == -1) + *code++ = OP_CRSTAR + repeat_type; + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_CRPLUS + repeat_type; + else if (repeat_min == 0 && repeat_max == 1) + *code++ = OP_CRQUERY + repeat_type; + else + { + *code++ = OP_CRRANGE + repeat_type; + *code++ = repeat_min >> 8; + *code++ = repeat_min & 255; + if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ + *code++ = repeat_max >> 8; + *code++ = repeat_max & 255; + } + } + + /* If previous was a bracket group, we may have to replicate it in certain + cases. */ + + else if ((int)*previous >= OP_BRA || (int)*previous == OP_ONCE || + (int)*previous == OP_COND) + { + register int i; + int ketoffset = 0; + int len = code - previous; + uschar *bralink = NULL; + + /* If the maximum repeat count is unlimited, find the end of the bracket + by scanning through from the start, and compute the offset back to it + from the current code pointer. There may be an OP_OPT setting following + the final KET, so we can't find the end just by going back from the code + pointer. */ + + if (repeat_max == -1) + { + register uschar *ket = previous; + do ket += (ket[1] << 8) + ket[2]; while (*ket != OP_KET); + ketoffset = code - ket; + } + + /* The case of a zero minimum is special because of the need to stick + OP_BRAZERO in front of it, and because the group appears once in the + data, whereas in other cases it appears the minimum number of times. For + this reason, it is simplest to treat this case separately, as otherwise + the code gets far too mess. There are several special subcases when the + minimum is zero. */ + + if (repeat_min == 0) + { + /* If we set up a required char from the bracket, we must back off + to the previous value and reset the countlits value too. */ + + if (subcountlits > 0) + { + *reqchar = prevreqchar; + *countlits -= subcountlits; + } + + /* If the maximum is also zero, we just omit the group from the output + altogether. */ + + if (repeat_max == 0) + { + code = previous; + goto END_REPEAT; + } + + /* If the maximum is 1 or unlimited, we just have to stick in the + BRAZERO and do no more at this point. */ + + if (repeat_max <= 1) + { + memmove(previous+1, previous, len); + code++; + *previous++ = OP_BRAZERO + repeat_type; + } + + /* If the maximum is greater than 1 and limited, we have to replicate + in a nested fashion, sticking OP_BRAZERO before each set of brackets. + The first one has to be handled carefully because it's the original + copy, which has to be moved up. The remainder can be handled by code + that is common with the non-zero minimum case below. We just have to + adjust the value or repeat_max, since one less copy is required. */ + + else + { + int offset; + memmove(previous+4, previous, len); + code += 4; + *previous++ = OP_BRAZERO + repeat_type; + *previous++ = OP_BRA; + + /* We chain together the bracket offset fields that have to be + filled in later when the ends of the brackets are reached. */ + + offset = (bralink == NULL)? 0 : previous - bralink; + bralink = previous; + *previous++ = offset >> 8; + *previous++ = offset & 255; + } + + repeat_max--; + } + + /* If the minimum is greater than zero, replicate the group as many + times as necessary, and adjust the maximum to the number of subsequent + copies that we need. */ + + else + { + for (i = 1; i < repeat_min; i++) + { + memcpy(code, previous, len); + code += len; + } + if (repeat_max > 0) repeat_max -= repeat_min; + } + + /* This code is common to both the zero and non-zero minimum cases. If + the maximum is limited, it replicates the group in a nested fashion, + remembering the bracket starts on a stack. In the case of a zero minimum, + the first one was set up above. In all cases the repeat_max now specifies + the number of additional copies needed. */ + + if (repeat_max >= 0) + { + for (i = repeat_max - 1; i >= 0; i--) + { + *code++ = OP_BRAZERO + repeat_type; + + /* All but the final copy start a new nesting, maintaining the + chain of brackets outstanding. */ + + if (i != 0) + { + int offset; + *code++ = OP_BRA; + offset = (bralink == NULL)? 0 : code - bralink; + bralink = code; + *code++ = offset >> 8; + *code++ = offset & 255; + } + + memcpy(code, previous, len); + code += len; + } + + /* Now chain through the pending brackets, and fill in their length + fields (which are holding the chain links pro tem). */ + + while (bralink != NULL) + { + int oldlinkoffset; + int offset = code - bralink + 1; + uschar *bra = code - offset; + oldlinkoffset = (bra[1] << 8) + bra[2]; + bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; + *code++ = OP_KET; + *code++ = bra[1] = offset >> 8; + *code++ = bra[2] = (offset & 255); + } + } + + /* If the maximum is unlimited, set a repeater in the final copy. We + can't just offset backwards from the current code point, because we + don't know if there's been an options resetting after the ket. The + correct offset was computed above. */ + + else code[-ketoffset] = OP_KETRMAX + repeat_type; + } + + /* Else there's some kind of shambles */ + + else + { + *errorptr = ERR11; + goto FAILED; + } + + /* In all case we no longer have a previous item. */ + + END_REPEAT: + previous = NULL; + break; + + + /* Start of nested bracket sub-expression, or comment or lookahead or + lookbehind or option setting or condition. First deal with special things + that can come after a bracket; all are introduced by ?, and the appearance + of any of them means that this is not a referencing group. They were + checked for validity in the first pass over the string, so we don't have to + check for syntax errors here. */ + + case '(': + newoptions = options; + condref = -1; + + if (*(++ptr) == '?') + { + int set, unset; + int *optset; + + switch (*(++ptr)) + { + case '#': /* Comment; skip to ket */ + ptr++; + while (*ptr != ')') ptr++; + continue; + + case ':': /* Non-extracting bracket */ + bravalue = OP_BRA; + ptr++; + break; + + case '(': + bravalue = OP_COND; /* Conditional group */ + if ((cd->ctypes[*(++ptr)] & ctype_digit) != 0) + { + condref = *ptr - '0'; + while (*(++ptr) != ')') condref = condref*10 + *ptr - '0'; + ptr++; + } + else ptr--; + break; + + case '=': /* Positive lookahead */ + bravalue = OP_ASSERT; + ptr++; + break; + + case '!': /* Negative lookahead */ + bravalue = OP_ASSERT_NOT; + ptr++; + break; + + case '<': /* Lookbehinds */ + switch (*(++ptr)) + { + case '=': /* Positive lookbehind */ + bravalue = OP_ASSERTBACK; + ptr++; + break; + + case '!': /* Negative lookbehind */ + bravalue = OP_ASSERTBACK_NOT; + ptr++; + break; + + default: /* Syntax error */ + *errorptr = ERR24; + goto FAILED; + } + break; + + case '>': /* One-time brackets */ + bravalue = OP_ONCE; + ptr++; + break; + + case 'R': /* Pattern recursion */ + *code++ = OP_RECURSE; + ptr++; + continue; + + default: /* Option setting */ + set = unset = 0; + optset = &set; + + while (*ptr != ')' && *ptr != ':') + { + switch (*ptr++) + { + case '-': optset = &unset; break; + + case 'i': *optset |= PCRE_CASELESS; break; + case 'm': *optset |= PCRE_MULTILINE; break; + case 's': *optset |= PCRE_DOTALL; break; + case 'x': *optset |= PCRE_EXTENDED; break; + case 'U': *optset |= PCRE_UNGREEDY; break; + case 'X': *optset |= PCRE_EXTRA; break; + + default: + *errorptr = ERR12; + goto FAILED; + } + } + + /* Set up the changed option bits, but don't change anything yet. */ + + newoptions = (options | set) & (~unset); + + /* If the options ended with ')' this is not the start of a nested + group with option changes, so the options change at this level. At top + level there is nothing else to be done (the options will in fact have + been set from the start of compiling as a result of the first pass) but + at an inner level we must compile code to change the ims options if + necessary, and pass the new setting back so that it can be put at the + start of any following branches, and when this group ends, a resetting + item can be compiled. */ + + if (*ptr == ')') + { + if ((options & PCRE_INGROUP) != 0 && + (options & PCRE_IMS) != (newoptions & PCRE_IMS)) + { + *code++ = OP_OPT; + *code++ = *optchanged = newoptions & PCRE_IMS; + } + options = newoptions; /* Change options at this level */ + previous = NULL; /* This item can't be repeated */ + continue; /* It is complete */ + } + + /* If the options ended with ':' we are heading into a nested group + with possible change of options. Such groups are non-capturing and are + not assertions of any kind. All we need to do is skip over the ':'; + the newoptions value is handled below. */ + + bravalue = OP_BRA; + ptr++; + } + } + + /* Else we have a referencing group; adjust the opcode. */ + + else + { + if (++(*brackets) > EXTRACT_MAX) + { + *errorptr = ERR13; + goto FAILED; + } + bravalue = OP_BRA + *brackets; + } + + /* Process nested bracketed re. Assertions may not be repeated, but other + kinds can be. We copy code into a non-register variable in order to be able + to pass its address because some compilers complain otherwise. Pass in a + new setting for the ims options if they have changed. */ + + previous = (bravalue >= OP_ONCE)? code : NULL; + *code = bravalue; + tempcode = code; + + if (!compile_regex( + options | PCRE_INGROUP, /* Set for all nested groups */ + ((options & PCRE_IMS) != (newoptions & PCRE_IMS))? + newoptions & PCRE_IMS : -1, /* Pass ims options if changed */ + brackets, /* Bracket level */ + &tempcode, /* Where to put code (updated) */ + &ptr, /* Input pointer (updated) */ + errorptr, /* Where to put an error message */ + (bravalue == OP_ASSERTBACK || + bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ + condref, /* Condition reference number */ + &subreqchar, /* For possible last char */ + &subcountlits, /* For literal count */ + cd)) /* Tables block */ + goto FAILED; + + /* At the end of compiling, code is still pointing to the start of the + group, while tempcode has been updated to point past the end of the group + and any option resetting that may follow it. The pattern pointer (ptr) + is on the bracket. */ + + /* If this is a conditional bracket, check that there are no more than + two branches in the group. */ + + if (bravalue == OP_COND) + { + uschar *tc = code; + condcount = 0; + + do { + condcount++; + tc += (tc[1] << 8) | tc[2]; + } + while (*tc != OP_KET); + + if (condcount > 2) + { + *errorptr = ERR27; + goto FAILED; + } + } + + /* Handle updating of the required character. If the subpattern didn't + set one, leave it as it was. Otherwise, update it for normal brackets of + all kinds, forward assertions, and conditions with two branches. Don't + update the literal count for forward assertions, however. If the bracket + is followed by a quantifier with zero repeat, we have to back off. Hence + the definition of prevreqchar and subcountlits outside the main loop so + that they can be accessed for the back off. */ + + if (subreqchar > 0 && + (bravalue >= OP_BRA || bravalue == OP_ONCE || bravalue == OP_ASSERT || + (bravalue == OP_COND && condcount == 2))) + { + prevreqchar = *reqchar; + *reqchar = subreqchar; + if (bravalue != OP_ASSERT) *countlits += subcountlits; + } + + /* Now update the main code pointer to the end of the group. */ + + code = tempcode; + + /* Error if hit end of pattern */ + + if (*ptr != ')') + { + *errorptr = ERR14; + goto FAILED; + } + break; + + /* Check \ for being a real metacharacter; if not, fall through and handle + it as a data character at the start of a string. Escape items are checked + for validity in the pre-compiling pass. */ + + case '\\': + tempptr = ptr; + c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd); + + /* Handle metacharacters introduced by \. For ones like \d, the ESC_ values + are arranged to be the negation of the corresponding OP_values. For the + back references, the values are ESC_REF plus the reference number. Only + back references and those types that consume a character may be repeated. + We can test for values between ESC_b and ESC_Z for the latter; this may + have to change if any new ones are ever created. */ + + if (c < 0) + { + if (-c >= ESC_REF) + { + previous = code; + *code++ = OP_REF; + *code++ = -c - ESC_REF; + } + else + { + previous = (-c > ESC_b && -c < ESC_Z)? code : NULL; + *code++ = -c; + } + continue; + } + + /* Data character: reset and fall through */ + + ptr = tempptr; + c = '\\'; + + /* Handle a run of data characters until a metacharacter is encountered. + The first character is guaranteed not to be whitespace or # when the + extended flag is set. */ + + NORMAL_CHAR: + default: + previous = code; + *code = OP_CHARS; + code += 2; + length = 0; + + do + { + if ((options & PCRE_EXTENDED) != 0) + { + if ((cd->ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while ((c = *(++ptr)) != 0 && c != '\n'); + if (c == 0) break; + continue; + } + } + + /* Backslash may introduce a data char or a metacharacter. Escaped items + are checked for validity in the pre-compiling pass. Stop the string + before a metaitem. */ + + if (c == '\\') + { + tempptr = ptr; + c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd); + if (c < 0) { ptr = tempptr; break; } + } + + /* Ordinary character or single-char escape */ + + *code++ = c; + length++; + } + + /* This "while" is the end of the "do" above. */ + + while (length < 255 && (cd->ctypes[c = *(++ptr)] & ctype_meta) == 0); + + /* Update the last character and the count of literals */ + + prevreqchar = (length > 1)? code[-2] : *reqchar; + *reqchar = code[-1]; + *countlits += length; + + /* Compute the length and set it in the data vector, and advance to + the next state. */ + + previous[1] = length; + if (length < 255) ptr--; + break; + } + } /* end of big loop */ + +/* Control never reaches here by falling through, only by a goto for all the +error states. Pass back the position in the pattern so that it can be displayed +to the user for diagnosing the error. */ + +FAILED: +*ptrptr = ptr; +return FALSE; +} + + + + +/************************************************* +* Compile sequence of alternatives * +*************************************************/ + +/* On entry, ptr is pointing past the bracket character, but on return +it points to the closing bracket, or vertical bar, or end of string. +The code variable is pointing at the byte into which the BRA operator has been +stored. If the ims options are changed at the start (for a (?ims: group) or +during any branch, we need to insert an OP_OPT item at the start of every +following branch to ensure they get set correctly at run time, and also pass +the new options into every subsequent branch compile. + +Argument: + options the option bits + optchanged new ims options to set as if (?ims) were at the start, or -1 + for no change + brackets -> int containing the number of extracting brackets used + codeptr -> the address of the current code pointer + ptrptr -> the address of the current pattern pointer + errorptr -> pointer to error message + lookbehind TRUE if this is a lookbehind assertion + condref > 0 for OPT_CREF setting at start of conditional group + reqchar -> place to put the last required character, or a negative number + countlits -> place to put the shortest literal count of any branch + cd points to the data block with tables pointers + +Returns: TRUE on success +*/ + +static BOOL +compile_regex(int options, int optchanged, int *brackets, uschar **codeptr, + const uschar **ptrptr, const char **errorptr, BOOL lookbehind, int condref, + int *reqchar, int *countlits, compile_data *cd) +{ +const uschar *ptr = *ptrptr; +uschar *code = *codeptr; +uschar *last_branch = code; +uschar *start_bracket = code; +uschar *reverse_count = NULL; +int oldoptions = options & PCRE_IMS; +int branchreqchar, branchcountlits; + +*reqchar = -1; +*countlits = INT_MAX; +code += 3; + +/* At the start of a reference-based conditional group, insert the reference +number as an OP_CREF item. */ + +if (condref > 0) + { + *code++ = OP_CREF; + *code++ = condref; + } + +/* Loop for each alternative branch */ + +for (;;) + { + int length; + + /* Handle change of options */ + + if (optchanged >= 0) + { + *code++ = OP_OPT; + *code++ = optchanged; + options = (options & ~PCRE_IMS) | optchanged; + } + + /* Set up dummy OP_REVERSE if lookbehind assertion */ + + if (lookbehind) + { + *code++ = OP_REVERSE; + reverse_count = code; + *code++ = 0; + *code++ = 0; + } + + /* Now compile the branch */ + + if (!compile_branch(options, brackets, &code, &ptr, errorptr, &optchanged, + &branchreqchar, &branchcountlits, cd)) + { + *ptrptr = ptr; + return FALSE; + } + + /* Fill in the length of the last branch */ + + length = code - last_branch; + last_branch[1] = length >> 8; + last_branch[2] = length & 255; + + /* Save the last required character if all branches have the same; a current + value of -1 means unset, while -2 means "previous branch had no last required + char". */ + + if (*reqchar != -2) + { + if (branchreqchar >= 0) + { + if (*reqchar == -1) *reqchar = branchreqchar; + else if (*reqchar != branchreqchar) *reqchar = -2; + } + else *reqchar = -2; + } + + /* Keep the shortest literal count */ + + if (branchcountlits < *countlits) *countlits = branchcountlits; + DPRINTF(("literal count = %d min=%d\n", branchcountlits, *countlits)); + + /* If lookbehind, check that this branch matches a fixed-length string, + and put the length into the OP_REVERSE item. Temporarily mark the end of + the branch with OP_END. */ + + if (lookbehind) + { + *code = OP_END; + length = find_fixedlength(last_branch); + DPRINTF(("fixed length = %d\n", length)); + if (length < 0) + { + *errorptr = ERR25; + *ptrptr = ptr; + return FALSE; + } + reverse_count[0] = (length >> 8); + reverse_count[1] = length & 255; + } + + /* Reached end of expression, either ')' or end of pattern. Insert a + terminating ket and the length of the whole bracketed item, and return, + leaving the pointer at the terminating char. If any of the ims options + were changed inside the group, compile a resetting op-code following. */ + + if (*ptr != '|') + { + length = code - start_bracket; + *code++ = OP_KET; + *code++ = length >> 8; + *code++ = length & 255; + if (optchanged >= 0) + { + *code++ = OP_OPT; + *code++ = oldoptions; + } + *codeptr = code; + *ptrptr = ptr; + return TRUE; + } + + /* Another branch follows; insert an "or" node and advance the pointer. */ + + *code = OP_ALT; + last_branch = code; + code += 3; + ptr++; + } +/* Control never reaches here */ +} + + + + +/************************************************* +* Find first significant op code * +*************************************************/ + +/* This is called by several functions that scan a compiled expression looking +for a fixed first character, or an anchoring op code etc. It skips over things +that do not influence this. For one application, a change of caseless option is +important. + +Arguments: + code pointer to the start of the group + options pointer to external options + optbit the option bit whose changing is significant, or + zero if none are + optstop TRUE to return on option change, otherwise change the options + value and continue + +Returns: pointer to the first significant opcode +*/ + +static const uschar* +first_significant_code(const uschar *code, int *options, int optbit, + BOOL optstop) +{ +for (;;) + { + switch ((int)*code) + { + case OP_OPT: + if (optbit > 0 && ((int)code[1] & optbit) != (*options & optbit)) + { + if (optstop) return code; + *options = (int)code[1]; + } + code += 2; + break; + + case OP_CREF: + code += 2; + break; + + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + code++; + break; + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do code += (code[1] << 8) + code[2]; while (*code == OP_ALT); + code += 3; + break; + + default: + return code; + } + } +/* Control never reaches here */ +} + + + + +/************************************************* +* Check for anchored expression * +*************************************************/ + +/* Try to find out if this is an anchored regular expression. Consider each +alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket +all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then +it's anchored. However, if this is a multiline pattern, then only OP_SOD +counts, since OP_CIRC can match in the middle. + +A branch is also implicitly anchored if it starts with .* and DOTALL is set, +because that will try the rest of the pattern at all possible matching points, +so there is no point trying them again. + +Arguments: + code points to start of expression (the bracket) + options points to the options setting + +Returns: TRUE or FALSE +*/ + +static BOOL +is_anchored(register const uschar *code, int *options) +{ +do { + const uschar *scode = first_significant_code(code + 3, options, + PCRE_MULTILINE, FALSE); + register int op = *scode; + if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) + { if (!is_anchored(scode, options)) return FALSE; } + else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR) && + (*options & PCRE_DOTALL) != 0) + { if (scode[1] != OP_ANY) return FALSE; } + else if (op != OP_SOD && + ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC)) + return FALSE; + code += (code[1] << 8) + code[2]; + } +while (*code == OP_ALT); +return TRUE; +} + + + +/************************************************* +* Check for starting with ^ or .* * +*************************************************/ + +/* This is called to find out if every branch starts with ^ or .* so that +"first char" processing can be done to speed things up in multiline +matching and for non-DOTALL patterns that start with .* (which must start at +the beginning or after \n). + +Argument: points to start of expression (the bracket) +Returns: TRUE or FALSE +*/ + +static BOOL +is_startline(const uschar *code) +{ +do { + const uschar *scode = first_significant_code(code + 3, NULL, 0, FALSE); + register int op = *scode; + if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) + { if (!is_startline(scode)) return FALSE; } + else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR) + { if (scode[1] != OP_ANY) return FALSE; } + else if (op != OP_CIRC) return FALSE; + code += (code[1] << 8) + code[2]; + } +while (*code == OP_ALT); +return TRUE; +} + + + +/************************************************* +* Check for fixed first char * +*************************************************/ + +/* Try to find out if there is a fixed first character. This is called for +unanchored expressions, as it speeds up their processing quite considerably. +Consider each alternative branch. If they all start with the same char, or with +a bracket all of whose alternatives start with the same char (recurse ad lib), +then we return that char, otherwise -1. + +Arguments: + code points to start of expression (the bracket) + options pointer to the options (used to check casing changes) + +Returns: -1 or the fixed first char +*/ + +static int +find_firstchar(const uschar *code, int *options) +{ +register int c = -1; +do { + int d; + const uschar *scode = first_significant_code(code + 3, options, + PCRE_CASELESS, TRUE); + register int op = *scode; + + if (op >= OP_BRA) op = OP_BRA; + + switch(op) + { + default: + return -1; + + case OP_BRA: + case OP_ASSERT: + case OP_ONCE: + case OP_COND: + if ((d = find_firstchar(scode, options)) < 0) return -1; + if (c < 0) c = d; else if (c != d) return -1; + break; + + case OP_EXACT: /* Fall through */ + scode++; + + case OP_CHARS: /* Fall through */ + scode++; + + case OP_PLUS: + case OP_MINPLUS: + if (c < 0) c = scode[1]; else if (c != scode[1]) return -1; + break; + } + + code += (code[1] << 8) + code[2]; + } +while (*code == OP_ALT); +return c; +} + + + + + +/************************************************* +* Compile a Regular Expression * +*************************************************/ + +/* This function takes a string and returns a pointer to a block of store +holding a compiled version of the expression. + +Arguments: + pattern the regular expression + options various option bits + errorptr pointer to pointer to error text + erroroffset ptr offset in pattern where error was detected + tables pointer to character tables or NULL + +Returns: pointer to compiled data block, or NULL on error, + with errorptr and erroroffset set +*/ + +pcre * +pcre_compile(const char *pattern, int options, const char **errorptr, + int *erroroffset, const unsigned char *tables) +{ +real_pcre *re; +int length = 3; /* For initial BRA plus length */ +int runlength; +int c, reqchar, countlits; +int bracount = 0; +int top_backref = 0; +int branch_extra = 0; +int branch_newextra; +unsigned int brastackptr = 0; +size_t size; +uschar *code; +const uschar *ptr; +compile_data compile_block; +int brastack[BRASTACK_SIZE]; +uschar bralenstack[BRASTACK_SIZE]; + +#ifdef DEBUG +uschar *code_base, *code_end; +#endif + +/* We can't pass back an error message if errorptr is NULL; I guess the best we +can do is just return NULL. */ + +if (errorptr == NULL) return NULL; +*errorptr = NULL; + +/* However, we can give a message for this error */ + +if (erroroffset == NULL) + { + *errorptr = ERR16; + return NULL; + } +*erroroffset = 0; + +if ((options & ~PUBLIC_OPTIONS) != 0) + { + *errorptr = ERR17; + return NULL; + } + +/* Set up pointers to the individual character tables */ + +if (tables == NULL) tables = pcre_default_tables; +compile_block.lcc = tables + lcc_offset; +compile_block.fcc = tables + fcc_offset; +compile_block.cbits = tables + cbits_offset; +compile_block.ctypes = tables + ctypes_offset; + +/* Reflect pattern for debugging output */ + +DPRINTF(("------------------------------------------------------------------\n")); +DPRINTF(("%s\n", pattern)); + +/* The first thing to do is to make a pass over the pattern to compute the +amount of store required to hold the compiled code. This does not have to be +perfect as long as errors are overestimates. At the same time we can detect any +internal flag settings. Make an attempt to correct for any counted white space +if an "extended" flag setting appears late in the pattern. We can't be so +clever for #-comments. */ + +ptr = (const uschar *)(pattern - 1); +while ((c = *(++ptr)) != 0) + { + int min, max; + int class_charcount; + + if ((options & PCRE_EXTENDED) != 0) + { + if ((compile_block.ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while ((c = *(++ptr)) != 0 && c != '\n'); + continue; + } + } + + switch(c) + { + /* A backslashed item may be an escaped "normal" character or a + character type. For a "normal" character, put the pointers and + character back so that tests for whitespace etc. in the input + are done correctly. */ + + case '\\': + { + const uschar *save_ptr = ptr; + c = check_escape(&ptr, errorptr, bracount, options, FALSE, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (c >= 0) + { + ptr = save_ptr; + c = '\\'; + goto NORMAL_CHAR; + } + } + length++; + + /* A back reference needs an additional char, plus either one or 5 + bytes for a repeat. We also need to keep the value of the highest + back reference. */ + + if (c <= -ESC_REF) + { + int refnum = -c - ESC_REF; + if (refnum > top_backref) top_backref = refnum; + length++; /* For single back reference */ + if (ptr[1] == '{' && is_counted_repeat(ptr+2, &compile_block)) + { + ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else length += 5; + if (ptr[1] == '?') ptr++; + } + } + continue; + + case '^': + case '.': + case '$': + case '*': /* These repeats won't be after brackets; */ + case '+': /* those are handled separately */ + case '?': + length++; + continue; + + /* This covers the cases of repeats after a single char, metachar, class, + or back reference. */ + + case '{': + if (!is_counted_repeat(ptr+1, &compile_block)) goto NORMAL_CHAR; + ptr = read_repeat_counts(ptr+1, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else + { + length--; /* Uncount the original char or metachar */ + if (min == 1) length++; else if (min > 0) length += 4; + if (max > 0) length += 4; else length += 2; + } + if (ptr[1] == '?') ptr++; + continue; + + /* An alternation contains an offset to the next branch or ket. If any ims + options changed in the previous branch(es), and/or if we are in a + lookbehind assertion, extra space will be needed at the start of the + branch. This is handled by branch_extra. */ + + case '|': + length += 3 + branch_extra; + continue; + + /* A character class uses 33 characters. Don't worry about character types + that aren't allowed in classes - they'll get picked up during the compile. + A character class that contains only one character uses 2 or 3 bytes, + depending on whether it is negated or not. Notice this where we can. */ + + case '[': + class_charcount = 0; + if (*(++ptr) == '^') ptr++; + do + { + if (*ptr == '\\') + { + int ch = check_escape(&ptr, errorptr, bracount, options, TRUE, + &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (-ch == ESC_b) class_charcount++; else class_charcount = 10; + } + else class_charcount++; + ptr++; + } + while (*ptr != 0 && *ptr != ']'); + + /* Repeats for negated single chars are handled by the general code */ + + if (class_charcount == 1) length += 3; else + { + length += 33; + + /* A repeat needs either 1 or 5 bytes. */ + + if (*ptr != 0 && ptr[1] == '{' && is_counted_repeat(ptr+2, &compile_block)) + { + ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else length += 5; + if (ptr[1] == '?') ptr++; + } + } + continue; + + /* Brackets may be genuine groups or special things */ + + case '(': + branch_newextra = 0; + + /* Handle special forms of bracket, which all start (? */ + + if (ptr[1] == '?') + { + int set, unset; + int *optset; + + switch (c = ptr[2]) + { + /* Skip over comments entirely */ + case '#': + ptr += 3; + while (*ptr != 0 && *ptr != ')') ptr++; + if (*ptr == 0) + { + *errorptr = ERR18; + goto PCRE_ERROR_RETURN; + } + continue; + + /* Non-referencing groups and lookaheads just move the pointer on, and + then behave like a non-special bracket, except that they don't increment + the count of extracting brackets. Ditto for the "once only" bracket, + which is in Perl from version 5.005. */ + + case ':': + case '=': + case '!': + case '>': + ptr += 2; + break; + + /* A recursive call to the regex is an extension, to provide the + facility which can be obtained by $(?p{perl-code}) in Perl 5.6. */ + + case 'R': + if (ptr[3] != ')') + { + *errorptr = ERR29; + goto PCRE_ERROR_RETURN; + } + ptr += 3; + length += 1; + break; + + /* Lookbehinds are in Perl from version 5.005 */ + + case '<': + if (ptr[3] == '=' || ptr[3] == '!') + { + ptr += 3; + branch_newextra = 3; + length += 3; /* For the first branch */ + break; + } + *errorptr = ERR24; + goto PCRE_ERROR_RETURN; + + /* Conditionals are in Perl from version 5.005. The bracket must either + be followed by a number (for bracket reference) or by an assertion + group. */ + + case '(': + if ((compile_block.ctypes[ptr[3]] & ctype_digit) != 0) + { + ptr += 4; + length += 2; + while ((compile_block.ctypes[*ptr] & ctype_digit) != 0) ptr++; + if (*ptr != ')') + { + *errorptr = ERR26; + goto PCRE_ERROR_RETURN; + } + } + else /* An assertion must follow */ + { + ptr++; /* Can treat like ':' as far as spacing is concerned */ + + if (ptr[2] != '?' || strchr("=!<", ptr[3]) == NULL) + { + ptr += 2; /* To get right offset in message */ + *errorptr = ERR28; + goto PCRE_ERROR_RETURN; + } + } + break; + + /* Else loop checking valid options until ) is met. Anything else is an + error. If we are without any brackets, i.e. at top level, the settings + act as if specified in the options, so massage the options immediately. + This is for backward compatibility with Perl 5.004. */ + + default: + set = unset = 0; + optset = &set; + ptr += 2; + + for (;; ptr++) + { + c = *ptr; + switch (c) + { + case 'i': + *optset |= PCRE_CASELESS; + continue; + + case 'm': + *optset |= PCRE_MULTILINE; + continue; + + case 's': + *optset |= PCRE_DOTALL; + continue; + + case 'x': + *optset |= PCRE_EXTENDED; + continue; + + case 'X': + *optset |= PCRE_EXTRA; + continue; + + case 'U': + *optset |= PCRE_UNGREEDY; + continue; + + case '-': + optset = &unset; + continue; + + /* A termination by ')' indicates an options-setting-only item; + this is global at top level; otherwise nothing is done here and + it is handled during the compiling process on a per-bracket-group + basis. */ + + case ')': + if (brastackptr == 0) + { + options = (options | set) & (~unset); + set = unset = 0; /* To save length */ + } + /* Fall through */ + + /* A termination by ':' indicates the start of a nested group with + the given options set. This is again handled at compile time, but + we must allow for compiled space if any of the ims options are + set. We also have to allow for resetting space at the end of + the group, which is why 4 is added to the length and not just 2. + If there are several changes of options within the same group, this + will lead to an over-estimate on the length, but this shouldn't + matter very much. We also have to allow for resetting options at + the start of any alternations, which we do by setting + branch_newextra to 2. Finally, we record whether the case-dependent + flag ever changes within the regex. This is used by the "required + character" code. */ + + case ':': + if (((set|unset) & PCRE_IMS) != 0) + { + length += 4; + branch_newextra = 2; + if (((set|unset) & PCRE_CASELESS) != 0) options |= PCRE_ICHANGED; + } + goto END_OPTIONS; + + /* Unrecognized option character */ + + default: + *errorptr = ERR12; + goto PCRE_ERROR_RETURN; + } + } + + /* If we hit a closing bracket, that's it - this is a freestanding + option-setting. We need to ensure that branch_extra is updated if + necessary. The only values branch_newextra can have here are 0 or 2. + If the value is 2, then branch_extra must either be 2 or 5, depending + on whether this is a lookbehind group or not. */ + + END_OPTIONS: + if (c == ')') + { + if (branch_newextra == 2 && (branch_extra == 0 || branch_extra == 3)) + branch_extra += branch_newextra; + continue; + } + + /* If options were terminated by ':' control comes here. Fall through + to handle the group below. */ + } + } + + /* Extracting brackets must be counted so we can process escapes in a + Perlish way. */ + + else bracount++; + + /* Non-special forms of bracket. Save length for computing whole length + at end if there's a repeat that requires duplication of the group. Also + save the current value of branch_extra, and start the new group with + the new value. If non-zero, this will either be 2 for a (?imsx: group, or 3 + for a lookbehind assertion. */ + + if (brastackptr >= sizeof(brastack)/sizeof(int)) + { + *errorptr = ERR19; + goto PCRE_ERROR_RETURN; + } + + bralenstack[brastackptr] = branch_extra; + branch_extra = branch_newextra; + + brastack[brastackptr++] = length; + length += 3; + continue; + + /* Handle ket. Look for subsequent max/min; for certain sets of values we + have to replicate this bracket up to that many times. If brastackptr is + 0 this is an unmatched bracket which will generate an error, but take care + not to try to access brastack[-1] when computing the length and restoring + the branch_extra value. */ + + case ')': + length += 3; + { + int minval = 1; + int maxval = 1; + int duplength; + + if (brastackptr > 0) + { + duplength = length - brastack[--brastackptr]; + branch_extra = bralenstack[brastackptr]; + } + else duplength = 0; + + /* Leave ptr at the final char; for read_repeat_counts this happens + automatically; for the others we need an increment. */ + + if ((c = ptr[1]) == '{' && is_counted_repeat(ptr+2, &compile_block)) + { + ptr = read_repeat_counts(ptr+2, &minval, &maxval, errorptr, + &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + } + else if (c == '*') { minval = 0; maxval = -1; ptr++; } + else if (c == '+') { maxval = -1; ptr++; } + else if (c == '?') { minval = 0; ptr++; } + + /* If the minimum is zero, we have to allow for an OP_BRAZERO before the + group, and if the maximum is greater than zero, we have to replicate + maxval-1 times; each replication acquires an OP_BRAZERO plus a nesting + bracket set - hence the 7. */ + + if (minval == 0) + { + length++; + if (maxval > 0) length += (maxval - 1) * (duplength + 7); + } + + /* When the minimum is greater than zero, 1 we have to replicate up to + minval-1 times, with no additions required in the copies. Then, if + there is a limited maximum we have to replicate up to maxval-1 times + allowing for a BRAZERO item before each optional copy and nesting + brackets for all but one of the optional copies. */ + + else + { + length += (minval - 1) * duplength; + if (maxval > minval) /* Need this test as maxval=-1 means no limit */ + length += (maxval - minval) * (duplength + 7) - 6; + } + } + continue; + + /* Non-special character. For a run of such characters the length required + is the number of characters + 2, except that the maximum run length is 255. + We won't get a skipped space or a non-data escape or the start of a # + comment as the first character, so the length can't be zero. */ + + NORMAL_CHAR: + default: + length += 2; + runlength = 0; + do + { + if ((options & PCRE_EXTENDED) != 0) + { + if ((compile_block.ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while ((c = *(++ptr)) != 0 && c != '\n'); + continue; + } + } + + /* Backslash may introduce a data char or a metacharacter; stop the + string before the latter. */ + + if (c == '\\') + { + const uschar *saveptr = ptr; + c = check_escape(&ptr, errorptr, bracount, options, FALSE, + &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (c < 0) { ptr = saveptr; break; } + } + + /* Ordinary character or single-char escape */ + + runlength++; + } + + /* This "while" is the end of the "do" above. */ + + while (runlength < 255 && + (compile_block.ctypes[c = *(++ptr)] & ctype_meta) == 0); + + ptr--; + length += runlength; + continue; + } + } + +length += 4; /* For final KET and END */ + +if (length > 65539) + { + *errorptr = ERR20; + return NULL; + } + +/* Compute the size of data block needed and get it, either from malloc or +externally provided function. We specify "code[0]" in the offsetof() expression +rather than just "code", because it has been reported that one broken compiler +fails on "code" because it is also an independent variable. It should make no +difference to the value of the offsetof(). */ + +size = length + offsetof(real_pcre, code[0]); +re = (real_pcre *)(pcre_malloc)(size); + +if (re == NULL) + { + *errorptr = ERR21; + return NULL; + } + +/* Put in the magic number, and save the size, options, and table pointer */ + +re->magic_number = MAGIC_NUMBER; +re->size = size; +re->options = options; +re->tables = tables; + +/* Set up a starting, non-extracting bracket, then compile the expression. On +error, *errorptr will be set non-NULL, so we don't need to look at the result +of the function here. */ + +ptr = (const uschar *)pattern; +code = re->code; +*code = OP_BRA; +bracount = 0; +(void)compile_regex(options, -1, &bracount, &code, &ptr, errorptr, FALSE, -1, + &reqchar, &countlits, &compile_block); +re->top_bracket = bracount; +re->top_backref = top_backref; + +/* If not reached end of pattern on success, there's an excess bracket. */ + +if (*errorptr == NULL && *ptr != 0) *errorptr = ERR22; + +/* Fill in the terminating state and check for disastrous overflow, but +if debugging, leave the test till after things are printed out. */ + +*code++ = OP_END; + +#ifndef DEBUG +if (code - re->code > length) *errorptr = ERR23; +#endif + +/* Give an error if there's back reference to a non-existent capturing +subpattern. */ + +if (top_backref > re->top_bracket) *errorptr = ERR15; + +/* Failed to compile */ + +if (*errorptr != NULL) + { + (pcre_free)(re); + PCRE_ERROR_RETURN: + *erroroffset = ptr - (const uschar *)pattern; + return NULL; + } + +/* If the anchored option was not passed, set flag if we can determine that the +pattern is anchored by virtue of ^ characters or \A or anything else (such as +starting with .* when DOTALL is set). + +Otherwise, see if we can determine what the first character has to be, because +that speeds up unanchored matches no end. If not, see if we can set the +PCRE_STARTLINE flag. This is helpful for multiline matches when all branches +start with ^. and also when all branches start with .* for non-DOTALL matches. +*/ + +if ((options & PCRE_ANCHORED) == 0) + { + int temp_options = options; + if (is_anchored(re->code, &temp_options)) + re->options |= PCRE_ANCHORED; + else + { + int ch = find_firstchar(re->code, &temp_options); + if (ch >= 0) + { + re->first_char = ch; + re->options |= PCRE_FIRSTSET; + } + else if (is_startline(re->code)) + re->options |= PCRE_STARTLINE; + } + } + +/* Save the last required character if there are at least two literal +characters on all paths, or if there is no first character setting. */ + +if (reqchar >= 0 && (countlits > 1 || (re->options & PCRE_FIRSTSET) == 0)) + { + re->req_char = reqchar; + re->options |= PCRE_REQCHSET; + } + +/* Print out the compiled data for debugging */ + +#ifdef DEBUG + +printf("Length = %d top_bracket = %d top_backref = %d\n", + length, re->top_bracket, re->top_backref); + +if (re->options != 0) + { + printf("%s%s%s%s%s%s%s%s%s\n", + ((re->options & PCRE_ANCHORED) != 0)? "anchored " : "", + ((re->options & PCRE_CASELESS) != 0)? "caseless " : "", + ((re->options & PCRE_ICHANGED) != 0)? "case state changed " : "", + ((re->options & PCRE_EXTENDED) != 0)? "extended " : "", + ((re->options & PCRE_MULTILINE) != 0)? "multiline " : "", + ((re->options & PCRE_DOTALL) != 0)? "dotall " : "", + ((re->options & PCRE_DOLLAR_ENDONLY) != 0)? "endonly " : "", + ((re->options & PCRE_EXTRA) != 0)? "extra " : "", + ((re->options & PCRE_UNGREEDY) != 0)? "ungreedy " : ""); + } + +if ((re->options & PCRE_FIRSTSET) != 0) + { + if (isprint(re->first_char)) printf("First char = %c\n", re->first_char); + else printf("First char = \\x%02x\n", re->first_char); + } + +if ((re->options & PCRE_REQCHSET) != 0) + { + if (isprint(re->req_char)) printf("Req char = %c\n", re->req_char); + else printf("Req char = \\x%02x\n", re->req_char); + } + +code_end = code; +code_base = code = re->code; + +while (code < code_end) + { + int charlength; + + printf("%3d ", code - code_base); + + if (*code >= OP_BRA) + { + printf("%3d Bra %d", (code[1] << 8) + code[2], *code - OP_BRA); + code += 2; + } + + else switch(*code) + { + case OP_OPT: + printf(" %.2x %s", code[1], OP_names[*code]); + code++; + break; + + case OP_COND: + printf("%3d Cond", (code[1] << 8) + code[2]); + code += 2; + break; + + case OP_CREF: + printf(" %.2d %s", code[1], OP_names[*code]); + code++; + break; + + case OP_CHARS: + charlength = *(++code); + printf("%3d ", charlength); + while (charlength-- > 0) + if (isprint(c = *(++code))) printf("%c", c); else printf("\\x%02x", c); + break; + + case OP_KETRMAX: + case OP_KETRMIN: + case OP_ALT: + case OP_KET: + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + printf("%3d %s", (code[1] << 8) + code[2], OP_names[*code]); + code += 2; + break; + + case OP_REVERSE: + printf("%3d %s", (code[1] << 8) + code[2], OP_names[*code]); + code += 2; + break; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + if (*code >= OP_TYPESTAR) + printf(" %s", OP_names[code[1]]); + else if (isprint(c = code[1])) printf(" %c", c); + else printf(" \\x%02x", c); + printf("%s", OP_names[*code++]); + break; + + case OP_EXACT: + case OP_UPTO: + case OP_MINUPTO: + if (isprint(c = code[3])) printf(" %c{", c); + else printf(" \\x%02x{", c); + if (*code != OP_EXACT) printf("0,"); + printf("%d}", (code[1] << 8) + code[2]); + if (*code == OP_MINUPTO) printf("?"); + code += 3; + break; + + case OP_TYPEEXACT: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + printf(" %s{", OP_names[code[3]]); + if (*code != OP_TYPEEXACT) printf(","); + printf("%d}", (code[1] << 8) + code[2]); + if (*code == OP_TYPEMINUPTO) printf("?"); + code += 3; + break; + + case OP_NOT: + if (isprint(c = *(++code))) printf(" [^%c]", c); + else printf(" [^\\x%02x]", c); + break; + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + if (isprint(c = code[1])) printf(" [^%c]", c); + else printf(" [^\\x%02x]", c); + printf("%s", OP_names[*code++]); + break; + + case OP_NOTEXACT: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + if (isprint(c = code[3])) printf(" [^%c]{", c); + else printf(" [^\\x%02x]{", c); + if (*code != OP_NOTEXACT) printf(","); + printf("%d}", (code[1] << 8) + code[2]); + if (*code == OP_NOTMINUPTO) printf("?"); + code += 3; + break; + + case OP_REF: + printf(" \\%d", *(++code)); + code ++; + goto CLASS_REF_REPEAT; + + case OP_CLASS: + { + int i, min, max; + code++; + printf(" ["); + + for (i = 0; i < 256; i++) + { + if ((code[i/8] & (1 << (i&7))) != 0) + { + int j; + for (j = i+1; j < 256; j++) + if ((code[j/8] & (1 << (j&7))) == 0) break; + if (i == '-' || i == ']') printf("\\"); + if (isprint(i)) printf("%c", i); else printf("\\x%02x", i); + if (--j > i) + { + printf("-"); + if (j == '-' || j == ']') printf("\\"); + if (isprint(j)) printf("%c", j); else printf("\\x%02x", j); + } + i = j; + } + } + printf("]"); + code += 32; + + CLASS_REF_REPEAT: + + switch(*code) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + printf("%s", OP_names[*code]); + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + min = (code[1] << 8) + code[2]; + max = (code[3] << 8) + code[4]; + if (max == 0) printf("{%d,}", min); + else printf("{%d,%d}", min, max); + if (*code == OP_CRMINRANGE) printf("?"); + code += 4; + break; + + default: + code--; + } + } + break; + + /* Anything else is just a one-node item */ + + default: + printf(" %s", OP_names[*code]); + break; + } + + code++; + printf("\n"); + } +printf("------------------------------------------------------------------\n"); + +/* This check is done here in the debugging case so that the code that +was compiled can be seen. */ + +if (code - re->code > length) + { + *errorptr = ERR23; + (pcre_free)(re); + *erroroffset = ptr - (uschar *)pattern; + return NULL; + } +#endif + +return (pcre *)re; +} + + + +/************************************************* +* Match a back-reference * +*************************************************/ + +/* If a back reference hasn't been set, the length that is passed is greater +than the number of characters left in the string, so the match fails. + +Arguments: + offset index into the offset vector + eptr points into the subject + length length to be matched + md points to match data block + ims the ims flags + +Returns: TRUE if matched +*/ + +static BOOL +match_ref(int offset, register const uschar *eptr, int length, match_data *md, + unsigned long int ims) +{ +const uschar *p = md->start_subject + md->offset_vector[offset]; + +#ifdef DEBUG +if (eptr >= md->end_subject) + printf("matching subject "); +else + { + printf("matching subject "); + pchars(eptr, length, TRUE, md); + } +printf(" against backref "); +pchars(p, length, FALSE, md); +printf("\n"); +#endif + +/* Always fail if not enough characters left */ + +if (length > md->end_subject - eptr) return FALSE; + +/* Separate the caselesss case for speed */ + +if ((ims & PCRE_CASELESS) != 0) + { + while (length-- > 0) + if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE; + } +else + { while (length-- > 0) if (*p++ != *eptr++) return FALSE; } + +return TRUE; +} + + + +/************************************************* +* Match from current position * +*************************************************/ + +/* On entry ecode points to the first opcode, and eptr to the first character +in the subject string, while eptrb holds the value of eptr at the start of the +last bracketed group - used for breaking infinite loops matching zero-length +strings. + +Arguments: + eptr pointer in subject + ecode position in code + offset_top current top pointer + md pointer to "static" info for the match + ims current /i, /m, and /s options + condassert TRUE if called to check a condition assertion + eptrb eptr at start of last bracket + +Returns: TRUE if matched +*/ + +static BOOL +match(register const uschar *eptr, register const uschar *ecode, + int offset_top, match_data *md, unsigned long int ims, BOOL condassert, + const uschar *eptrb) +{ +unsigned long int original_ims = ims; /* Save for resetting on ')' */ + +for (;;) + { + int op = (int)*ecode; + int min, max, ctype; + register int i; + register int c; + BOOL minimize = FALSE; + + /* Opening capturing bracket. If there is space in the offset vector, save + the current subject position in the working slot at the top of the vector. We + mustn't change the current values of the data slot, because they may be set + from a previous iteration of this group, and be referred to by a reference + inside the group. + + If the bracket fails to match, we need to restore this value and also the + values of the final offsets, in case they were set by a previous iteration of + the same bracket. + + If there isn't enough space in the offset vector, treat this as if it were a + non-capturing bracket. Don't worry about setting the flag for the error case + here; that is handled in the code for KET. */ + + if (op > OP_BRA) + { + int number = op - OP_BRA; + int offset = number << 1; + +#ifdef DEBUG + printf("start bracket %d subject=", number); + pchars(eptr, 16, TRUE, md); + printf("\n"); +#endif + + if (offset < md->offset_max) + { + int save_offset1 = md->offset_vector[offset]; + int save_offset2 = md->offset_vector[offset+1]; + int save_offset3 = md->offset_vector[md->offset_end - number]; + + DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3)); + md->offset_vector[md->offset_end - number] = eptr - md->start_subject; + + do + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr)) return TRUE; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + + DPRINTF(("bracket %d failed\n", number)); + + md->offset_vector[offset] = save_offset1; + md->offset_vector[offset+1] = save_offset2; + md->offset_vector[md->offset_end - number] = save_offset3; + return FALSE; + } + + /* Insufficient room for saving captured contents */ + + else op = OP_BRA; + } + + /* Other types of node can be handled by a switch */ + + switch(op) + { + case OP_BRA: /* Non-capturing bracket: optimized */ + DPRINTF(("start bracket 0\n")); + do + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr)) return TRUE; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + DPRINTF(("bracket 0 failed\n")); + return FALSE; + + /* Conditional group: compilation checked that there are no more than + two branches. If the condition is false, skipping the first branch takes us + past the end if there is only one branch, but that's OK because that is + exactly what going to the ket would do. */ + + case OP_COND: + if (ecode[3] == OP_CREF) /* Condition is extraction test */ + { + int offset = ecode[4] << 1; /* Doubled reference number */ + return match(eptr, + ecode + ((offset < offset_top && md->offset_vector[offset] >= 0)? + 5 : 3 + (ecode[1] << 8) + ecode[2]), + offset_top, md, ims, FALSE, eptr); + } + + /* The condition is an assertion. Call match() to evaluate it - setting + the final argument TRUE causes it to stop at the end of an assertion. */ + + else + { + if (match(eptr, ecode+3, offset_top, md, ims, TRUE, NULL)) + { + ecode += 3 + (ecode[4] << 8) + ecode[5]; + while (*ecode == OP_ALT) ecode += (ecode[1] << 8) + ecode[2]; + } + else ecode += (ecode[1] << 8) + ecode[2]; + return match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr); + } + /* Control never reaches here */ + + /* Skip over conditional reference data if encountered (should not be) */ + + case OP_CREF: + ecode += 2; + break; + + /* End of the pattern. If PCRE_NOTEMPTY is set, fail if we have matched + an empty string - recursion will then try other alternatives, if any. */ + + case OP_END: + if (md->notempty && eptr == md->start_match) return FALSE; + md->end_match_ptr = eptr; /* Record where we ended */ + md->end_offset_top = offset_top; /* and how many extracts were taken */ + return TRUE; + + /* Change option settings */ + + case OP_OPT: + ims = ecode[1]; + ecode += 2; + DPRINTF(("ims set to %02lx\n", ims)); + break; + + /* Assertion brackets. Check the alternative branches in turn - the + matching won't pass the KET for an assertion. If any one branch matches, + the assertion is true. Lookbehind assertions have an OP_REVERSE item at the + start of each branch to move the current point backwards, so the code at + this level is identical to the lookahead case. */ + + case OP_ASSERT: + case OP_ASSERTBACK: + do + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, NULL)) break; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + if (*ecode == OP_KET) return FALSE; + + /* If checking an assertion for a condition, return TRUE. */ + + if (condassert) return TRUE; + + /* Continue from after the assertion, updating the offsets high water + mark, since extracts may have been taken during the assertion. */ + + do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); + ecode += 3; + offset_top = md->end_offset_top; + continue; + + /* Negative assertion: all branches must fail to match */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK_NOT: + do + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, NULL)) return FALSE; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + + if (condassert) return TRUE; + ecode += 3; + continue; + + /* Move the subject pointer back. This occurs only at the start of + each branch of a lookbehind assertion. If we are too close to the start to + move back, this match function fails. */ + + case OP_REVERSE: + eptr -= (ecode[1] << 8) + ecode[2]; + if (eptr < md->start_subject) return FALSE; + ecode += 3; + break; + + /* Recursion matches the current regex, nested. If there are any capturing + brackets started but not finished, we have to save their starting points + and reinstate them after the recursion. However, we don't know how many + such there are (offset_top records the completed total) so we just have + to save all the potential data. There may be up to 99 such values, which + is a bit large to put on the stack, but using malloc for small numbers + seems expensive. As a compromise, the stack is used when there are fewer + than 16 values to store; otherwise malloc is used. A problem is what to do + if the malloc fails ... there is no way of returning to the top level with + an error. Save the top 15 values on the stack, and accept that the rest + may be wrong. */ + + case OP_RECURSE: + { + BOOL rc; + int *save; + int stacksave[15]; + + c = md->offset_max; + + if (c < 16) save = stacksave; else + { + save = (int *)(pcre_malloc)((c+1) * sizeof(int)); + if (save == NULL) + { + save = stacksave; + c = 15; + } + } + + for (i = 1; i <= c; i++) + save[i] = md->offset_vector[md->offset_end - i]; + rc = match(eptr, md->start_pattern, offset_top, md, ims, FALSE, eptrb); + for (i = 1; i <= c; i++) + md->offset_vector[md->offset_end - i] = save[i]; + if (save != stacksave) (pcre_free)(save); + if (!rc) return FALSE; + + /* In case the recursion has set more capturing values, save the final + number, then move along the subject till after the recursive match, + and advance one byte in the pattern code. */ + + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + ecode++; + } + break; + + /* "Once" brackets are like assertion brackets except that after a match, + the point in the subject string is not moved back. Thus there can never be + a move back into the brackets. Check the alternative branches in turn - the + matching won't pass the KET for this kind of subpattern. If any one branch + matches, we carry on as at the end of a normal bracket, leaving the subject + pointer. */ + + case OP_ONCE: + { + const uschar *prev = ecode; + + do + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr)) break; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + + /* If hit the end of the group (which could be repeated), fail */ + + if (*ecode != OP_ONCE && *ecode != OP_ALT) return FALSE; + + /* Continue as from after the assertion, updating the offsets high water + mark, since extracts may have been taken. */ + + do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); + + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. If there is an options reset, it will get obeyed in the normal + course of events. */ + + if (*ecode == OP_KET || eptr == eptrb) + { + ecode += 3; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. We need to reset any options + that changed within the bracket before re-running it, so check the next + opcode. */ + + if (ecode[3] == OP_OPT) + { + ims = (ims & ~PCRE_IMS) | ecode[4]; + DPRINTF(("ims set to %02lx at group repeat\n", ims)); + } + + if (*ecode == OP_KETRMIN) + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr) || + match(eptr, prev, offset_top, md, ims, FALSE, eptr)) return TRUE; + } + else /* OP_KETRMAX */ + { + if (match(eptr, prev, offset_top, md, ims, FALSE, eptr) || + match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr)) return TRUE; + } + } + return FALSE; + + /* An alternation is the end of a branch; scan along to find the end of the + bracketed group and go to there. */ + + case OP_ALT: + do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); + break; + + /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating + that it may occur zero times. It may repeat infinitely, or not at all - + i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper + repeat limits are compiled as a number of copies, with the optional ones + preceded by BRAZERO or BRAMINZERO. */ + + case OP_BRAZERO: + { + const uschar *next = ecode+1; + if (match(eptr, next, offset_top, md, ims, FALSE, eptr)) return TRUE; + do next += (next[1] << 8) + next[2]; while (*next == OP_ALT); + ecode = next + 3; + } + break; + + case OP_BRAMINZERO: + { + const uschar *next = ecode+1; + do next += (next[1] << 8) + next[2]; while (*next == OP_ALT); + if (match(eptr, next+3, offset_top, md, ims, FALSE, eptr)) return TRUE; + ecode++; + } + break; + + /* End of a group, repeated or non-repeating. If we are at the end of + an assertion "group", stop matching and return TRUE, but record the + current high water mark for use by positive assertions. Do this also + for the "once" (not-backup up) groups. */ + + case OP_KET: + case OP_KETRMIN: + case OP_KETRMAX: + { + const uschar *prev = ecode - (ecode[1] << 8) - ecode[2]; + + if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT || + *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT || + *prev == OP_ONCE) + { + md->end_match_ptr = eptr; /* For ONCE */ + md->end_offset_top = offset_top; + return TRUE; + } + + /* In all other cases except a conditional group we have to check the + group number back at the start and if necessary complete handling an + extraction by setting the offsets and bumping the high water mark. */ + + if (*prev != OP_COND) + { + int number = *prev - OP_BRA; + int offset = number << 1; + + DPRINTF(("end bracket %d\n", number)); + + if (number > 0) + { + if (offset >= md->offset_max) md->offset_overflow = TRUE; else + { + md->offset_vector[offset] = + md->offset_vector[md->offset_end - number]; + md->offset_vector[offset+1] = eptr - md->start_subject; + if (offset_top <= offset) offset_top = offset + 2; + } + } + } + + /* Reset the value of the ims flags, in case they got changed during + the group. */ + + ims = original_ims; + DPRINTF(("ims reset to %02lx\n", ims)); + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. If there is an options reset, it will get obeyed in the normal + course of events. */ + + if (*ecode == OP_KET || eptr == eptrb) + { + ecode += 3; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. */ + + if (*ecode == OP_KETRMIN) + { + if (match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr) || + match(eptr, prev, offset_top, md, ims, FALSE, eptr)) return TRUE; + } + else /* OP_KETRMAX */ + { + if (match(eptr, prev, offset_top, md, ims, FALSE, eptr) || + match(eptr, ecode+3, offset_top, md, ims, FALSE, eptr)) return TRUE; + } + } + return FALSE; + + /* Start of subject unless notbol, or after internal newline if multiline */ + + case OP_CIRC: + if (md->notbol && eptr == md->start_subject) return FALSE; + if ((ims & PCRE_MULTILINE) != 0) + { + if (eptr != md->start_subject && eptr[-1] != '\n') return FALSE; + ecode++; + break; + } + /* ... else fall through */ + + /* Start of subject assertion */ + + case OP_SOD: + if (eptr != md->start_subject) return FALSE; + ecode++; + break; + + /* Assert before internal newline if multiline, or before a terminating + newline unless endonly is set, else end of subject unless noteol is set. */ + + case OP_DOLL: + if ((ims & PCRE_MULTILINE) != 0) + { + if (eptr < md->end_subject) { if (*eptr != '\n') return FALSE; } + else { if (md->noteol) return FALSE; } + ecode++; + break; + } + else + { + if (md->noteol) return FALSE; + if (!md->endonly) + { + if (eptr < md->end_subject - 1 || + (eptr == md->end_subject - 1 && *eptr != '\n')) return FALSE; + + ecode++; + break; + } + } + /* ... else fall through */ + + /* End of subject assertion (\z) */ + + case OP_EOD: + if (eptr < md->end_subject) return FALSE; + ecode++; + break; + + /* End of subject or ending \n assertion (\Z) */ + + case OP_EODN: + if (eptr < md->end_subject - 1 || + (eptr == md->end_subject - 1 && *eptr != '\n')) return FALSE; + ecode++; + break; + + /* Word boundary assertions */ + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + { + BOOL prev_is_word = (eptr != md->start_subject) && + ((md->ctypes[eptr[-1]] & ctype_word) != 0); + BOOL cur_is_word = (eptr < md->end_subject) && + ((md->ctypes[*eptr] & ctype_word) != 0); + if ((*ecode++ == OP_WORD_BOUNDARY)? + cur_is_word == prev_is_word : cur_is_word != prev_is_word) + return FALSE; + } + break; + + /* Match a single character type; inline for speed */ + + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject && *eptr == '\n') + return FALSE; + if (eptr++ >= md->end_subject) return FALSE; + ecode++; + break; + + case OP_NOT_DIGIT: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_digit) != 0) + return FALSE; + ecode++; + break; + + case OP_DIGIT: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_digit) == 0) + return FALSE; + ecode++; + break; + + case OP_NOT_WHITESPACE: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_space) != 0) + return FALSE; + ecode++; + break; + + case OP_WHITESPACE: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_space) == 0) + return FALSE; + ecode++; + break; + + case OP_NOT_WORDCHAR: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_word) != 0) + return FALSE; + ecode++; + break; + + case OP_WORDCHAR: + if (eptr >= md->end_subject || + (md->ctypes[*eptr++] & ctype_word) == 0) + return FALSE; + ecode++; + break; + + /* Match a back reference, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. The code is similar + to that for character classes, but repeated for efficiency. Then obey + similar code to character type repeats - written out again for speed. + However, if the referenced string is the empty string, always treat + it as matched, any number of times (otherwise there could be infinite + loops). */ + + case OP_REF: + { + int length; + int offset = ecode[1] << 1; /* Doubled reference number */ + ecode += 2; /* Advance past the item */ + + /* If the reference is unset, set the length to be longer than the amount + of subject left; this ensures that every attempt at a match fails. We + can't just fail here, because of the possibility of quantifiers with zero + minima. */ + + length = (offset >= offset_top || md->offset_vector[offset] < 0)? + md->end_subject - eptr + 1 : + md->offset_vector[offset+1] - md->offset_vector[offset]; + + /* Set up for repetition, or handle the non-repeated case */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = (ecode[1] << 8) + ecode[2]; + max = (ecode[3] << 8) + ecode[4]; + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + if (!match_ref(offset, eptr, length, md, ims)) return FALSE; + eptr += length; + continue; /* With the main loop */ + } + + /* If the length of the reference is zero, just continue with the + main loop. */ + + if (length == 0) continue; + + /* First, ensure the minimum number of matches are present. We get back + the length of the reference string explicitly rather than passing the + address of eptr, so that eptr can be a register variable. */ + + for (i = 1; i <= min; i++) + { + if (!match_ref(offset, eptr, length, md, ims)) return FALSE; + eptr += length; + } + + /* If min = max, continue at the same level without recursion. + They are not both allowed to be zero. */ + + if (min == max) continue; + + /* If minimizing, keep trying and advancing the pointer */ + + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || !match_ref(offset, eptr, length, md, ims)) + return FALSE; + eptr += length; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest string and work backwards */ + + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (!match_ref(offset, eptr, length, md, ims)) break; + eptr += length; + } + while (eptr >= pp) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + eptr -= length; + } + return FALSE; + } + } + /* Control never gets here */ + + + + /* Match a character class, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. Then obey similar + code to character type repeats - written out again for speed. */ + + case OP_CLASS: + { + const uschar *data = ecode + 1; /* Save for matching */ + ecode += 33; /* Advance past the item */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = (ecode[1] << 8) + ecode[2]; + max = (ecode[3] << 8) + ecode[4]; + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + min = max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) return FALSE; + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + return FALSE; + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == max) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || eptr >= md->end_subject) return FALSE; + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + return FALSE; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + const uschar *pp = eptr; + for (i = min; i < max; eptr++, i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + break; + } + + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + } + /* Control never gets here */ + + /* Match a run of characters */ + + case OP_CHARS: + { + register int length = ecode[1]; + ecode += 2; + +#ifdef DEBUG /* Sigh. Some compilers never learn. */ + if (eptr >= md->end_subject) + printf("matching subject against pattern "); + else + { + printf("matching subject "); + pchars(eptr, length, TRUE, md); + printf(" against pattern "); + } + pchars(ecode, length, FALSE, md); + printf("\n"); +#endif + + if (length > md->end_subject - eptr) return FALSE; + if ((ims & PCRE_CASELESS) != 0) + { + while (length-- > 0) + if (md->lcc[*ecode++] != md->lcc[*eptr++]) + return FALSE; + } + else + { + while (length-- > 0) if (*ecode++ != *eptr++) return FALSE; + } + } + break; + + /* Match a single character repeatedly; different opcodes share code. */ + + case OP_EXACT: + min = max = (ecode[1] << 8) + ecode[2]; + ecode += 3; + goto REPEATCHAR; + + case OP_UPTO: + case OP_MINUPTO: + min = 0; + max = (ecode[1] << 8) + ecode[2]; + minimize = *ecode == OP_MINUPTO; + ecode += 3; + goto REPEATCHAR; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + c = *ecode++ - OP_STAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-character matches. We can give + up quickly if there are fewer than the minimum number of characters left in + the subject. */ + + REPEATCHAR: + if (min > md->end_subject - eptr) return FALSE; + c = *ecode++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max, + max, eptr)); + + if ((ims & PCRE_CASELESS) != 0) + { + c = md->lcc[c]; + for (i = 1; i <= min; i++) + if (c != md->lcc[*eptr++]) return FALSE; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || eptr >= md->end_subject || + c != md->lcc[*eptr++]) + return FALSE; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c != md->lcc[*eptr]) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + /* Control never gets here */ + } + + /* Caseful comparisons */ + + else + { + for (i = 1; i <= min; i++) if (c != *eptr++) return FALSE; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || eptr >= md->end_subject || c != *eptr++) return FALSE; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c != *eptr) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + } + /* Control never gets here */ + + /* Match a negated single character */ + + case OP_NOT: + if (eptr >= md->end_subject) return FALSE; + ecode++; + if ((ims & PCRE_CASELESS) != 0) + { + if (md->lcc[*ecode++] == md->lcc[*eptr++]) return FALSE; + } + else + { + if (*ecode++ == *eptr++) return FALSE; + } + break; + + /* Match a negated single character repeatedly. This is almost a repeat of + the code for a repeated single character, but I haven't found a nice way of + commoning these up that doesn't require a test of the positive/negative + option for each character match. Maybe that wouldn't add very much to the + time taken, but character matching *is* what this is all about... */ + + case OP_NOTEXACT: + min = max = (ecode[1] << 8) + ecode[2]; + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTUPTO: + case OP_NOTMINUPTO: + min = 0; + max = (ecode[1] << 8) + ecode[2]; + minimize = *ecode == OP_NOTMINUPTO; + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + c = *ecode++ - OP_NOTSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-character matches. We can give + up quickly if there are fewer than the minimum number of characters left in + the subject. */ + + REPEATNOTCHAR: + if (min > md->end_subject - eptr) return FALSE; + c = *ecode++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, max, + max, eptr)); + + if ((ims & PCRE_CASELESS) != 0) + { + c = md->lcc[c]; + for (i = 1; i <= min; i++) + if (c == md->lcc[*eptr++]) return FALSE; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || eptr >= md->end_subject || + c == md->lcc[*eptr++]) + return FALSE; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c == md->lcc[*eptr]) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + /* Control never gets here */ + } + + /* Caseful comparisons */ + + else + { + for (i = 1; i <= min; i++) if (c == *eptr++) return FALSE; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + if (i >= max || eptr >= md->end_subject || c == *eptr++) return FALSE; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c == *eptr) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + } + /* Control never gets here */ + + /* Match a single character type repeatedly; several different opcodes + share code. This is very similar to the code for single characters, but we + repeat it in the interests of efficiency. */ + + case OP_TYPEEXACT: + min = max = (ecode[1] << 8) + ecode[2]; + minimize = TRUE; + ecode += 3; + goto REPEATTYPE; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + min = 0; + max = (ecode[1] << 8) + ecode[2]; + minimize = *ecode == OP_TYPEMINUPTO; + ecode += 3; + goto REPEATTYPE; + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + c = *ecode++ - OP_TYPESTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single character type matches */ + + REPEATTYPE: + ctype = *ecode++; /* Code for the character type */ + + /* First, ensure the minimum number of matches are present. Use inline + code for maximizing the speed, and do the type test once at the start + (i.e. keep it out of the loop). Also test that there are at least the + minimum number of characters before we start. */ + + if (min > md->end_subject - eptr) return FALSE; + if (min > 0) switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0) + { for (i = 1; i <= min; i++) if (*eptr++ == '\n') return FALSE; } + else eptr += min; + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_digit) != 0) return FALSE; + break; + + case OP_DIGIT: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_digit) == 0) return FALSE; + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_space) != 0) return FALSE; + break; + + case OP_WHITESPACE: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_space) == 0) return FALSE; + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_word) != 0) + return FALSE; + break; + + case OP_WORDCHAR: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_word) == 0) + return FALSE; + break; + } + + /* If min = max, continue at the same level without recursing */ + + if (min == max) continue; + + /* If minimizing, we have to test the rest of the pattern before each + subsequent match. */ + + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md, ims, FALSE, eptrb)) return TRUE; + if (i >= max || eptr >= md->end_subject) return FALSE; + + c = *eptr++; + switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0 && c == '\n') return FALSE; + break; + + case OP_NOT_DIGIT: + if ((md->ctypes[c] & ctype_digit) != 0) return FALSE; + break; + + case OP_DIGIT: + if ((md->ctypes[c] & ctype_digit) == 0) return FALSE; + break; + + case OP_NOT_WHITESPACE: + if ((md->ctypes[c] & ctype_space) != 0) return FALSE; + break; + + case OP_WHITESPACE: + if ((md->ctypes[c] & ctype_space) == 0) return FALSE; + break; + + case OP_NOT_WORDCHAR: + if ((md->ctypes[c] & ctype_word) != 0) return FALSE; + break; + + case OP_WORDCHAR: + if ((md->ctypes[c] & ctype_word) == 0) return FALSE; + break; + } + } + /* Control never gets here */ + } + + /* If maximizing it is worth using inline code for speed, doing the type + test once at the start (i.e. keep it out of the loop). */ + + else + { + const uschar *pp = eptr; + switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || *eptr == '\n') break; + eptr++; + } + } + else + { + c = max - min; + if (c > md->end_subject - eptr) c = md->end_subject - eptr; + eptr += c; + } + break; + + case OP_NOT_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0) + break; + eptr++; + } + break; + + case OP_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0) + break; + eptr++; + } + break; + + case OP_NOT_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0) + break; + eptr++; + } + break; + + case OP_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0) + break; + eptr++; + } + break; + + case OP_NOT_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0) + break; + eptr++; + } + break; + + case OP_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0) + break; + eptr++; + } + break; + } + + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md, ims, FALSE, eptrb)) + return TRUE; + return FALSE; + } + /* Control never gets here */ + + /* There's been some horrible disaster. */ + + default: + DPRINTF(("Unknown opcode %d\n", *ecode)); + md->errorcode = PCRE_ERROR_UNKNOWN_NODE; + return FALSE; + } + + /* Do not stick any code in here without much thought; it is assumed + that "continue" in the code above comes out to here to repeat the main + loop. */ + + } /* End of main loop */ +/* Control never reaches here */ +} + + + + +/************************************************* +* Execute a Regular Expression * +*************************************************/ + +/* This function applies a compiled re to a subject string and picks out +portions of the string if it matches. Two elements in the vector are set for +each substring: the offsets to the start and end of the substring. + +Arguments: + external_re points to the compiled expression + external_extra points to "hints" from pcre_study() or is NULL + subject points to the subject string + length length of subject string (may contain binary zeros) + start_offset where to start in the subject string + options option bits + offsets points to a vector of ints to be filled in with offsets + offsetcount the number of elements in the vector + +Returns: > 0 => success; value is the number of elements filled in + = 0 => success, but offsets is not big enough + -1 => failed to match + < -1 => some kind of unexpected problem +*/ + +int +pcre_exec(const pcre *external_re, const pcre_extra *external_extra, + const char *subject, int length, int start_offset, int options, int *offsets, + int offsetcount) +{ +int resetcount, ocount; +int first_char = -1; +int req_char = -1; +int req_char2 = -1; +unsigned long int ims = 0; +match_data match_block; +const uschar *start_bits = NULL; +const uschar *start_match = (const uschar *)subject + start_offset; +const uschar *end_subject; +const uschar *req_char_ptr = start_match - 1; +const real_pcre *re = (const real_pcre *)external_re; +const real_pcre_extra *extra = (const real_pcre_extra *)external_extra; +BOOL using_temporary_offsets = FALSE; +BOOL anchored = ((re->options | options) & PCRE_ANCHORED) != 0; +BOOL startline = (re->options & PCRE_STARTLINE) != 0; + +if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; + +if (re == NULL || subject == NULL || + (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; + +match_block.start_pattern = re->code; +match_block.start_subject = (const uschar *)subject; +match_block.end_subject = match_block.start_subject + length; +end_subject = match_block.end_subject; + +match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; + +match_block.notbol = (options & PCRE_NOTBOL) != 0; +match_block.noteol = (options & PCRE_NOTEOL) != 0; +match_block.notempty = (options & PCRE_NOTEMPTY) != 0; + +match_block.errorcode = PCRE_ERROR_NOMATCH; /* Default error */ + +match_block.lcc = re->tables + lcc_offset; +match_block.ctypes = re->tables + ctypes_offset; + +/* The ims options can vary during the matching as a result of the presence +of (?ims) items in the pattern. They are kept in a local variable so that +restoring at the exit of a group is easy. */ + +ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL); + +/* If the expression has got more back references than the offsets supplied can +hold, we get a temporary bit of working store to use during the matching. +Otherwise, we can use the vector supplied, rounding down its size to a multiple +of 3. */ + +ocount = offsetcount - (offsetcount % 3); + +if (re->top_backref > 0 && re->top_backref >= ocount/3) + { + ocount = re->top_backref * 3 + 3; + match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int)); + if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY; + using_temporary_offsets = TRUE; + DPRINTF(("Got memory to hold back references\n")); + } +else match_block.offset_vector = offsets; + +match_block.offset_end = ocount; +match_block.offset_max = (2*ocount)/3; +match_block.offset_overflow = FALSE; + +/* Compute the minimum number of offsets that we need to reset each time. Doing +this makes a huge difference to execution time when there aren't many brackets +in the pattern. */ + +resetcount = 2 + re->top_bracket * 2; +if (resetcount > offsetcount) resetcount = ocount; + +/* Reset the working variable associated with each extraction. These should +never be used unless previously set, but they get saved and restored, and so we +initialize them to avoid reading uninitialized locations. */ + +if (match_block.offset_vector != NULL) + { + register int *iptr = match_block.offset_vector + ocount; + register int *iend = iptr - resetcount/2 + 1; + while (--iptr >= iend) *iptr = -1; + } + +/* Set up the first character to match, if available. The first_char value is +never set for an anchored regular expression, but the anchoring may be forced +at run time, so we have to test for anchoring. The first char may be unset for +an unanchored pattern, of course. If there's no first char and the pattern was +studied, there may be a bitmap of possible first characters. */ + +if (!anchored) + { + if ((re->options & PCRE_FIRSTSET) != 0) + { + first_char = re->first_char; + if ((ims & PCRE_CASELESS) != 0) first_char = match_block.lcc[first_char]; + } + else + if (!startline && extra != NULL && + (extra->options & PCRE_STUDY_MAPPED) != 0) + start_bits = extra->start_bits; + } + +/* For anchored or unanchored matches, there may be a "last known required +character" set. If the PCRE_CASELESS is set, implying that the match starts +caselessly, or if there are any changes of this flag within the regex, set up +both cases of the character. Otherwise set the two values the same, which will +avoid duplicate testing (which takes significant time). This covers the vast +majority of cases. It will be suboptimal when the case flag changes in a regex +and the required character in fact is caseful. */ + +if ((re->options & PCRE_REQCHSET) != 0) + { + req_char = re->req_char; + req_char2 = ((re->options & (PCRE_CASELESS | PCRE_ICHANGED)) != 0)? + (re->tables + fcc_offset)[req_char] : req_char; + } + +/* Loop for handling unanchored repeated matching attempts; for anchored regexs +the loop runs just once. */ + +do + { + int rc; + register int *iptr = match_block.offset_vector; + register int *iend = iptr + resetcount; + + /* Reset the maximum number of extractions we might see. */ + + while (iptr < iend) *iptr++ = -1; + + /* Advance to a unique first char if possible */ + + if (first_char >= 0) + { + if ((ims & PCRE_CASELESS) != 0) + while (start_match < end_subject && + match_block.lcc[*start_match] != first_char) + start_match++; + else + while (start_match < end_subject && *start_match != first_char) + start_match++; + } + + /* Or to just after \n for a multiline match if possible */ + + else if (startline) + { + if (start_match > match_block.start_subject + start_offset) + { + while (start_match < end_subject && start_match[-1] != '\n') + start_match++; + } + } + + /* Or to a non-unique first char after study */ + + else if (start_bits != NULL) + { + while (start_match < end_subject) + { + register int c = *start_match; + if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break; + } + } + +#ifdef DEBUG /* Sigh. Some compilers never learn. */ + printf(">>>> Match against: "); + pchars(start_match, end_subject - start_match, TRUE, &match_block); + printf("\n"); +#endif + + /* If req_char is set, we know that that character must appear in the subject + for the match to succeed. If the first character is set, req_char must be + later in the subject; otherwise the test starts at the match point. This + optimization can save a huge amount of backtracking in patterns with nested + unlimited repeats that aren't going to match. We don't know what the state of + case matching may be when this character is hit, so test for it in both its + cases if necessary. However, the different cased versions will not be set up + unless PCRE_CASELESS was given or the casing state changes within the regex. + Writing separate code makes it go faster, as does using an autoincrement and + backing off on a match. */ + + if (req_char >= 0) + { + register const uschar *p = start_match + ((first_char >= 0)? 1 : 0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it at last time. */ + + if (p > req_char_ptr) + { + /* Do a single test if no case difference is set up */ + + if (req_char == req_char2) + { + while (p < end_subject) + { + if (*p++ == req_char) { p--; break; } + } + } + + /* Otherwise test for either case */ + + else + { + while (p < end_subject) + { + register int pp = *p++; + if (pp == req_char || pp == req_char2) { p--; break; } + } + } + + /* If we can't find the required character, break the matching loop */ + + if (p >= end_subject) break; + + /* If we have found the required character, save the point where we + found it, so that we don't search again next time round the loop if + the start hasn't passed this character yet. */ + + req_char_ptr = p; + } + } + + /* When a match occurs, substrings will be set for all internal extractions; + we just need to set up the whole thing as substring 0 before returning. If + there were too many extractions, set the return code to zero. In the case + where we had to get some local store to hold offsets for backreferences, copy + those back references that we can. In this case there need not be overflow + if certain parts of the pattern were not used. */ + + match_block.start_match = start_match; + if (!match(start_match, re->code, 2, &match_block, ims, FALSE, start_match)) + continue; + + /* Copy the offset information from temporary store if necessary */ + + if (using_temporary_offsets) + { + if (offsetcount >= 4) + { + memcpy(offsets + 2, match_block.offset_vector + 2, + (offsetcount - 2) * sizeof(int)); + DPRINTF(("Copied offsets from temporary memory\n")); + } + if (match_block.end_offset_top > offsetcount) + match_block.offset_overflow = TRUE; + + DPRINTF(("Freeing temporary memory\n")); + (pcre_free)(match_block.offset_vector); + } + + rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2; + + if (match_block.offset_end < 2) rc = 0; else + { + offsets[0] = start_match - match_block.start_subject; + offsets[1] = match_block.end_match_ptr - match_block.start_subject; + } + + DPRINTF((">>>> returning %d\n", rc)); + return rc; + } + +/* This "while" is the end of the "do" above */ + +while (!anchored && + match_block.errorcode == PCRE_ERROR_NOMATCH && + start_match++ < end_subject); + +if (using_temporary_offsets) + { + DPRINTF(("Freeing temporary memory\n")); + (pcre_free)(match_block.offset_vector); + } + +DPRINTF((">>>> returning %d\n", match_block.errorcode)); + +return match_block.errorcode; +} + +/* End of pcre.c */ diff --git a/srclib/pcre/pcre.def b/srclib/pcre/pcre.def new file mode 100644 index 00000000000..0e8cf3f442a --- /dev/null +++ b/srclib/pcre/pcre.def @@ -0,0 +1,19 @@ +EXPORTS + +pcre_malloc DATA +pcre_free DATA + +pcre_compile +pcre_copy_substring +pcre_exec +pcre_get_substring +pcre_get_substring_list +pcre_info +pcre_maketables +pcre_study +pcre_version + +regcomp +regexec +regerror +regfree diff --git a/srclib/pcre/pcre.in b/srclib/pcre/pcre.in new file mode 100644 index 00000000000..74b0cfc579b --- /dev/null +++ b/srclib/pcre/pcre.in @@ -0,0 +1,105 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* Copyright (c) 1997-2000 University of Cambridge */ + +#ifndef _PCRE_H +#define _PCRE_H + +#define PCRE_MAJOR @PCRE_MAJOR@ +#define PCRE_MINOR @PCRE_MINOR@ +#define PCRE_DATE @PCRE_DATE@ + +/* Win32 uses DLL by default */ + +#ifdef _WIN32 +# ifdef STATIC +# define PCRE_DL_IMPORT +# else +# define PCRE_DL_IMPORT __declspec(dllimport) +# endif +#else +# define PCRE_DL_IMPORT +#endif + +/* Have to include stdlib.h in order to ensure that size_t is defined; +it is needed here for malloc. */ + +#include +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options */ + +#define PCRE_CASELESS 0x0001 +#define PCRE_MULTILINE 0x0002 +#define PCRE_DOTALL 0x0004 +#define PCRE_EXTENDED 0x0008 +#define PCRE_ANCHORED 0x0010 +#define PCRE_DOLLAR_ENDONLY 0x0020 +#define PCRE_EXTRA 0x0040 +#define PCRE_NOTBOL 0x0080 +#define PCRE_NOTEOL 0x0100 +#define PCRE_UNGREEDY 0x0200 +#define PCRE_NOTEMPTY 0x0400 + +/* Exec-time and get-time error codes */ + +#define PCRE_ERROR_NOMATCH (-1) +#define PCRE_ERROR_NULL (-2) +#define PCRE_ERROR_BADOPTION (-3) +#define PCRE_ERROR_BADMAGIC (-4) +#define PCRE_ERROR_UNKNOWN_NODE (-5) +#define PCRE_ERROR_NOMEMORY (-6) +#define PCRE_ERROR_NOSUBSTRING (-7) + +/* Request types for pcre_fullinfo() */ + +#define PCRE_INFO_OPTIONS 0 +#define PCRE_INFO_SIZE 1 +#define PCRE_INFO_CAPTURECOUNT 2 +#define PCRE_INFO_BACKREFMAX 3 +#define PCRE_INFO_FIRSTCHAR 4 +#define PCRE_INFO_FIRSTTABLE 5 +#define PCRE_INFO_LASTLITERAL 6 + +/* Types */ + +typedef void pcre; +typedef void pcre_extra; + +/* Store get and free functions. These can be set to alternative malloc/free +functions if required. Some magic is required for Win32 DLL; it is null on +other OS. */ + +PCRE_DL_IMPORT extern void *(*pcre_malloc)(size_t); +PCRE_DL_IMPORT extern void (*pcre_free)(void *); + +#undef PCRE_DL_IMPORT + +/* Functions */ + +extern pcre *pcre_compile(const char *, int, const char **, int *, + const unsigned char *); +extern int pcre_copy_substring(const char *, int *, int, int, char *, int); +extern int pcre_exec(const pcre *, const pcre_extra *, const char *, + int, int, int, int *, int); +extern int pcre_get_substring(const char *, int *, int, int, const char **); +extern int pcre_get_substring_list(const char *, int *, int, const char ***); +extern int pcre_info(const pcre *, int *, int *); +extern int pcre_fullinfo(const pcre *, const pcre_extra *, int, void *); +extern unsigned const char *pcre_maketables(void); +extern pcre_extra *pcre_study(const pcre *, int, const char **); +extern const char *pcre_version(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcre.h */ diff --git a/srclib/pcre/pcreposix.c b/srclib/pcre/pcreposix.c new file mode 100644 index 00000000000..7c66ccec89a --- /dev/null +++ b/srclib/pcre/pcreposix.c @@ -0,0 +1,276 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +This module is a wrapper that provides a POSIX API to the underlying PCRE +functions. + +Written by: Philip Hazel + + Copyright (c) 1997-2000 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- +*/ + +#include "internal.h" +#include "pcreposix.h" +#include "stdlib.h" + + + +/* Corresponding tables of PCRE error messages and POSIX error codes. */ + +static const char *estring[] = { + ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, ERR10, + ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20, + ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR29, ERR29, ERR30, + ERR31 }; + +static int eint[] = { + REG_EESCAPE, /* "\\ at end of pattern" */ + REG_EESCAPE, /* "\\c at end of pattern" */ + REG_EESCAPE, /* "unrecognized character follows \\" */ + REG_BADBR, /* "numbers out of order in {} quantifier" */ + REG_BADBR, /* "number too big in {} quantifier" */ + REG_EBRACK, /* "missing terminating ] for character class" */ + REG_ECTYPE, /* "invalid escape sequence in character class" */ + REG_ERANGE, /* "range out of order in character class" */ + REG_BADRPT, /* "nothing to repeat" */ + REG_BADRPT, /* "operand of unlimited repeat could match the empty string" */ + REG_ASSERT, /* "internal error: unexpected repeat" */ + REG_BADPAT, /* "unrecognized character after (?" */ + REG_ESIZE, /* "too many capturing parenthesized sub-patterns" */ + REG_EPAREN, /* "missing )" */ + REG_ESUBREG, /* "back reference to non-existent subpattern" */ + REG_INVARG, /* "erroffset passed as NULL" */ + REG_INVARG, /* "unknown option bit(s) set" */ + REG_EPAREN, /* "missing ) after comment" */ + REG_ESIZE, /* "too many sets of parentheses" */ + REG_ESIZE, /* "regular expression too large" */ + REG_ESPACE, /* "failed to get memory" */ + REG_EPAREN, /* "unmatched brackets" */ + REG_ASSERT, /* "internal error: code overflow" */ + REG_BADPAT, /* "unrecognized character after (?<" */ + REG_BADPAT, /* "lookbehind assertion is not fixed length" */ + REG_BADPAT, /* "malformed number after (?(" */ + REG_BADPAT, /* "conditional group containe more than two branches" */ + REG_BADPAT, /* "assertion expected after (?(" */ + REG_BADPAT, /* "(?p must be followed by )" */ + REG_ECTYPE, /* "unknown POSIX class name" */ + REG_BADPAT /* "POSIX collating elements are not supported" */ +}; + +/* Table of texts corresponding to POSIX error codes */ + +static const char *pstring[] = { + "", /* Dummy for value 0 */ + "internal error", /* REG_ASSERT */ + "invalid repeat counts in {}", /* BADBR */ + "pattern error", /* BADPAT */ + "? * + invalid", /* BADRPT */ + "unbalanced {}", /* EBRACE */ + "unbalanced []", /* EBRACK */ + "collation error - not relevant", /* ECOLLATE */ + "bad class", /* ECTYPE */ + "bad escape sequence", /* EESCAPE */ + "empty expression", /* EMPTY */ + "unbalanced ()", /* EPAREN */ + "bad range inside []", /* ERANGE */ + "expression too big", /* ESIZE */ + "failed to get memory", /* ESPACE */ + "bad back reference", /* ESUBREG */ + "bad argument", /* INVARG */ + "match failed" /* NOMATCH */ +}; + + + + +/************************************************* +* Translate PCRE text code to int * +*************************************************/ + +/* PCRE compile-time errors are given as strings defined as macros. We can just +look them up in a table to turn them into POSIX-style error codes. */ + +static int +pcre_posix_error_code(const char *s) +{ +size_t i; +for (i = 0; i < sizeof(estring)/sizeof(char *); i++) + if (strcmp(s, estring[i]) == 0) return eint[i]; +return REG_ASSERT; +} + + + +/************************************************* +* Translate error code to string * +*************************************************/ + +size_t +regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) +{ +const char *message, *addmessage; +size_t length, addlength; + +message = (errcode >= (int)(sizeof(pstring)/sizeof(char *)))? + "unknown error code" : pstring[errcode]; +length = strlen(message) + 1; + +addmessage = " at offset "; +addlength = (preg != NULL && (int)preg->re_erroffset != -1)? + strlen(addmessage) + 6 : 0; + +if (errbuf_size > 0) + { + if (addlength > 0 && errbuf_size >= length + addlength) + sprintf(errbuf, "%s%s%-6d", message, addmessage, (int)preg->re_erroffset); + else + { + strncpy(errbuf, message, errbuf_size - 1); + errbuf[errbuf_size-1] = 0; + } + } + +return length + addlength; +} + + + + +/************************************************* +* Free store held by a regex * +*************************************************/ + +void +regfree(regex_t *preg) +{ +(pcre_free)(preg->re_pcre); +} + + + + +/************************************************* +* Compile a regular expression * +*************************************************/ + +/* +Arguments: + preg points to a structure for recording the compiled expression + pattern the pattern to compile + cflags compilation flags + +Returns: 0 on success + various non-zero codes on failure +*/ + +int +regcomp(regex_t *preg, const char *pattern, int cflags) +{ +const char *errorptr; +int erroffset; +int options = 0; + +if ((cflags & REG_ICASE) != 0) options |= PCRE_CASELESS; +if ((cflags & REG_NEWLINE) != 0) options |= PCRE_MULTILINE; + +preg->re_pcre = pcre_compile(pattern, options, &errorptr, &erroffset, NULL); +preg->re_erroffset = erroffset; + +if (preg->re_pcre == NULL) return pcre_posix_error_code(errorptr); + +preg->re_nsub = pcre_info(preg->re_pcre, NULL, NULL); +return 0; +} + + + + +/************************************************* +* Match a regular expression * +*************************************************/ + +/* Unfortunately, PCRE requires 3 ints of working space for each captured +substring, so we have to get and release working store instead of just using +the POSIX structures as was done in earlier releases when PCRE needed only 2 +ints. */ + +int +regexec(regex_t *preg, const char *string, size_t nmatch, + regmatch_t pmatch[], int eflags) +{ +int rc; +int options = 0; +int *ovector = NULL; + +if ((eflags & REG_NOTBOL) != 0) options |= PCRE_NOTBOL; +if ((eflags & REG_NOTEOL) != 0) options |= PCRE_NOTEOL; + +preg->re_erroffset = (size_t)(-1); /* Only has meaning after compile */ + +if (nmatch > 0) + { + ovector = (int *)malloc(sizeof(int) * nmatch * 3); + if (ovector == NULL) return REG_ESPACE; + } + +rc = pcre_exec(preg->re_pcre, NULL, string, (int)strlen(string), 0, options, + ovector, nmatch * 3); + +if (rc == 0) rc = nmatch; /* All captured slots were filled in */ + +if (rc >= 0) + { + size_t i; + for (i = 0; i < rc; i++) + { + pmatch[i].rm_so = ovector[i*2]; + pmatch[i].rm_eo = ovector[i*2+1]; + } + if (ovector != NULL) free(ovector); + for (; i < nmatch; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; + return 0; + } + +else + { + if (ovector != NULL) free(ovector); + switch(rc) + { + case PCRE_ERROR_NOMATCH: return REG_NOMATCH; + case PCRE_ERROR_NULL: return REG_INVARG; + case PCRE_ERROR_BADOPTION: return REG_INVARG; + case PCRE_ERROR_BADMAGIC: return REG_INVARG; + case PCRE_ERROR_UNKNOWN_NODE: return REG_ASSERT; + case PCRE_ERROR_NOMEMORY: return REG_ESPACE; + default: return REG_ASSERT; + } + } +} + +/* End of pcreposix.c */ diff --git a/srclib/pcre/pcreposix.h b/srclib/pcre/pcreposix.h new file mode 100644 index 00000000000..7660acbd553 --- /dev/null +++ b/srclib/pcre/pcreposix.h @@ -0,0 +1,88 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* Copyright (c) 1997-2000 University of Cambridge */ + +#ifndef _PCREPOSIX_H +#define _PCREPOSIX_H + +/* This is the header for the POSIX wrapper interface to the PCRE Perl- +Compatible Regular Expression library. It defines the things POSIX says should +be there. I hope. */ + +/* Have to include stdlib.h in order to ensure that size_t is defined. */ + +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options defined by POSIX. */ + +#define REG_ICASE 0x01 +#define REG_NEWLINE 0x02 +#define REG_NOTBOL 0x04 +#define REG_NOTEOL 0x08 + +/* These are not used by PCRE, but by defining them we make it easier +to slot PCRE into existing programs that make POSIX calls. */ + +#define REG_EXTENDED 0 +#define REG_NOSUB 0 + +/* Error values. Not all these are relevant or used by the wrapper. */ + +enum { + REG_ASSERT = 1, /* internal error ? */ + REG_BADBR, /* invalid repeat counts in {} */ + REG_BADPAT, /* pattern error */ + REG_BADRPT, /* ? * + invalid */ + REG_EBRACE, /* unbalanced {} */ + REG_EBRACK, /* unbalanced [] */ + REG_ECOLLATE, /* collation error - not relevant */ + REG_ECTYPE, /* bad class */ + REG_EESCAPE, /* bad escape sequence */ + REG_EMPTY, /* empty expression */ + REG_EPAREN, /* unbalanced () */ + REG_ERANGE, /* bad range inside [] */ + REG_ESIZE, /* expression too big */ + REG_ESPACE, /* failed to get memory */ + REG_ESUBREG, /* bad back reference */ + REG_INVARG, /* bad argument */ + REG_NOMATCH /* match failed */ +}; + + +/* The structure representing a compiled regular expression. */ + +typedef struct { + void *re_pcre; + size_t re_nsub; + size_t re_erroffset; +} regex_t; + +/* The structure in which a captured offset is returned. */ + +typedef int regoff_t; + +typedef struct { + regoff_t rm_so; + regoff_t rm_eo; +} regmatch_t; + +/* The functions */ + +extern int regcomp(regex_t *, const char *, int); +extern int regexec(regex_t *, const char *, size_t, regmatch_t *, int); +extern size_t regerror(int, const regex_t *, char *, size_t); +extern void regfree(regex_t *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcreposix.h */ diff --git a/srclib/pcre/pcretest.c b/srclib/pcre/pcretest.c new file mode 100644 index 00000000000..85569fbeb77 --- /dev/null +++ b/srclib/pcre/pcretest.c @@ -0,0 +1,1071 @@ +/************************************************* +* PCRE testing program * +*************************************************/ + +#include +#include +#include +#include +#include +#include + +/* Use the internal info for displaying the results of pcre_study(). */ + +#include "internal.h" + +/* It is possible to compile this test program without including support for +testing the POSIX interface, though this is not available via the standard +Makefile. */ + +#if !defined NOPOSIX +#include "pcreposix.h" +#endif + +#ifndef CLOCKS_PER_SEC +#ifdef CLK_TCK +#define CLOCKS_PER_SEC CLK_TCK +#else +#define CLOCKS_PER_SEC 100 +#endif +#endif + +#define LOOPREPEAT 20000 + + +static FILE *outfile; +static int log_store = 0; +static size_t gotten_store; + + + +/* Debugging function to print the internal form of the regex. This is the same +code as contained in pcre.c under the DEBUG macro. */ + +static const char *OP_names[] = { + "End", "\\A", "\\B", "\\b", "\\D", "\\d", + "\\S", "\\s", "\\W", "\\w", "\\Z", "\\z", + "Opt", "^", "$", "Any", "chars", "not", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", + "class", "Ref", "Recurse", + "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", + "AssertB", "AssertB not", "Reverse", "Once", "Cond", "Cref", + "Brazero", "Braminzero", "Bra" +}; + + +static void print_internals(pcre *re) +{ +unsigned char *code = ((real_pcre *)re)->code; + +fprintf(outfile, "------------------------------------------------------------------\n"); + +for(;;) + { + int c; + int charlength; + + fprintf(outfile, "%3d ", (int)(code - ((real_pcre *)re)->code)); + + if (*code >= OP_BRA) + { + fprintf(outfile, "%3d Bra %d", (code[1] << 8) + code[2], *code - OP_BRA); + code += 2; + } + + else switch(*code) + { + case OP_END: + fprintf(outfile, " %s\n", OP_names[*code]); + fprintf(outfile, "------------------------------------------------------------------\n"); + return; + + case OP_OPT: + fprintf(outfile, " %.2x %s", code[1], OP_names[*code]); + code++; + break; + + case OP_COND: + fprintf(outfile, "%3d Cond", (code[1] << 8) + code[2]); + code += 2; + break; + + case OP_CREF: + fprintf(outfile, " %.2d %s", code[1], OP_names[*code]); + code++; + break; + + case OP_CHARS: + charlength = *(++code); + fprintf(outfile, "%3d ", charlength); + while (charlength-- > 0) + if (isprint(c = *(++code))) fprintf(outfile, "%c", c); + else fprintf(outfile, "\\x%02x", c); + break; + + case OP_KETRMAX: + case OP_KETRMIN: + case OP_ALT: + case OP_KET: + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + fprintf(outfile, "%3d %s", (code[1] << 8) + code[2], OP_names[*code]); + code += 2; + break; + + case OP_REVERSE: + fprintf(outfile, "%3d %s", (code[1] << 8) + code[2], OP_names[*code]); + code += 2; + break; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + if (*code >= OP_TYPESTAR) + fprintf(outfile, " %s", OP_names[code[1]]); + else if (isprint(c = code[1])) fprintf(outfile, " %c", c); + else fprintf(outfile, " \\x%02x", c); + fprintf(outfile, "%s", OP_names[*code++]); + break; + + case OP_EXACT: + case OP_UPTO: + case OP_MINUPTO: + if (isprint(c = code[3])) fprintf(outfile, " %c{", c); + else fprintf(outfile, " \\x%02x{", c); + if (*code != OP_EXACT) fprintf(outfile, ","); + fprintf(outfile, "%d}", (code[1] << 8) + code[2]); + if (*code == OP_MINUPTO) fprintf(outfile, "?"); + code += 3; + break; + + case OP_TYPEEXACT: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + fprintf(outfile, " %s{", OP_names[code[3]]); + if (*code != OP_TYPEEXACT) fprintf(outfile, "0,"); + fprintf(outfile, "%d}", (code[1] << 8) + code[2]); + if (*code == OP_TYPEMINUPTO) fprintf(outfile, "?"); + code += 3; + break; + + case OP_NOT: + if (isprint(c = *(++code))) fprintf(outfile, " [^%c]", c); + else fprintf(outfile, " [^\\x%02x]", c); + break; + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + if (isprint(c = code[1])) fprintf(outfile, " [^%c]", c); + else fprintf(outfile, " [^\\x%02x]", c); + fprintf(outfile, "%s", OP_names[*code++]); + break; + + case OP_NOTEXACT: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + if (isprint(c = code[3])) fprintf(outfile, " [^%c]{", c); + else fprintf(outfile, " [^\\x%02x]{", c); + if (*code != OP_NOTEXACT) fprintf(outfile, ","); + fprintf(outfile, "%d}", (code[1] << 8) + code[2]); + if (*code == OP_NOTMINUPTO) fprintf(outfile, "?"); + code += 3; + break; + + case OP_REF: + fprintf(outfile, " \\%d", *(++code)); + code++; + goto CLASS_REF_REPEAT; + + case OP_CLASS: + { + int i, min, max; + code++; + fprintf(outfile, " ["); + + for (i = 0; i < 256; i++) + { + if ((code[i/8] & (1 << (i&7))) != 0) + { + int j; + for (j = i+1; j < 256; j++) + if ((code[j/8] & (1 << (j&7))) == 0) break; + if (i == '-' || i == ']') fprintf(outfile, "\\"); + if (isprint(i)) fprintf(outfile, "%c", i); else fprintf(outfile, "\\x%02x", i); + if (--j > i) + { + fprintf(outfile, "-"); + if (j == '-' || j == ']') fprintf(outfile, "\\"); + if (isprint(j)) fprintf(outfile, "%c", j); else fprintf(outfile, "\\x%02x", j); + } + i = j; + } + } + fprintf(outfile, "]"); + code += 32; + + CLASS_REF_REPEAT: + + switch(*code) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + fprintf(outfile, "%s", OP_names[*code]); + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + min = (code[1] << 8) + code[2]; + max = (code[3] << 8) + code[4]; + if (max == 0) fprintf(outfile, "{%d,}", min); + else fprintf(outfile, "{%d,%d}", min, max); + if (*code == OP_CRMINRANGE) fprintf(outfile, "?"); + code += 4; + break; + + default: + code--; + } + } + break; + + /* Anything else is just a one-node item */ + + default: + fprintf(outfile, " %s", OP_names[*code]); + break; + } + + code++; + fprintf(outfile, "\n"); + } +} + + + +/* Character string printing function. */ + +static void pchars(unsigned char *p, int length) +{ +int c; +while (length-- > 0) + if (isprint(c = *(p++))) fprintf(outfile, "%c", c); + else fprintf(outfile, "\\x%02x", c); +} + + + +/* Alternative malloc function, to test functionality and show the size of the +compiled re. */ + +static void *new_malloc(size_t size) +{ +gotten_store = size; +if (log_store) + fprintf(outfile, "Memory allocation (code space): %d\n", + (int)((int)size - offsetof(real_pcre, code[0]))); +return malloc(size); +} + + + + +/* Get one piece of information from the pcre_fullinfo() function */ + +static void new_info(pcre *re, pcre_extra *study, int option, void *ptr) +{ +int rc; +if ((rc = pcre_fullinfo(re, study, option, ptr)) < 0) + fprintf(outfile, "Error %d from pcre_fullinfo(%d)\n", rc, option); +} + + + + +/* Read lines from named file or stdin and write to named file or stdout; lines +consist of a regular expression, in delimiters and optionally followed by +options, followed by a set of test data, terminated by an empty line. */ + +int main(int argc, char **argv) +{ +FILE *infile = stdin; +int options = 0; +int study_options = 0; +int op = 1; +int timeit = 0; +int showinfo = 0; +int showstore = 0; +int posix = 0; +int debug = 0; +int done = 0; +unsigned char buffer[30000]; +unsigned char dbuffer[1024]; + +/* Static so that new_malloc can use it. */ + +outfile = stdout; + +/* Scan options */ + +while (argc > 1 && argv[op][0] == '-') + { + if (strcmp(argv[op], "-s") == 0 || strcmp(argv[op], "-m") == 0) + showstore = 1; + else if (strcmp(argv[op], "-t") == 0) timeit = 1; + else if (strcmp(argv[op], "-i") == 0) showinfo = 1; + else if (strcmp(argv[op], "-d") == 0) showinfo = debug = 1; + else if (strcmp(argv[op], "-p") == 0) posix = 1; + else + { + printf("*** Unknown option %s\n", argv[op]); + printf("Usage: pcretest [-d] [-i] [-p] [-s] [-t] [ []]\n"); + printf(" -d debug: show compiled code; implies -i\n" + " -i show information about compiled pattern\n" + " -p use POSIX interface\n" + " -s output store information\n" + " -t time compilation and execution\n"); + return 1; + } + op++; + argc--; + } + +/* Sort out the input and output files */ + +if (argc > 1) + { + infile = fopen(argv[op], "r"); + if (infile == NULL) + { + printf("** Failed to open %s\n", argv[op]); + return 1; + } + } + +if (argc > 2) + { + outfile = fopen(argv[op+1], "w"); + if (outfile == NULL) + { + printf("** Failed to open %s\n", argv[op+1]); + return 1; + } + } + +/* Set alternative malloc function */ + +pcre_malloc = new_malloc; + +/* Heading line, then prompt for first regex if stdin */ + +fprintf(outfile, "PCRE version %s\n\n", pcre_version()); + +/* Main loop */ + +while (!done) + { + pcre *re = NULL; + pcre_extra *extra = NULL; + +#if !defined NOPOSIX /* There are still compilers that require no indent */ + regex_t preg; + int do_posix = 0; +#endif + + const char *error; + unsigned char *p, *pp, *ppp; + unsigned const char *tables = NULL; + int do_study = 0; + int do_debug = debug; + int do_G = 0; + int do_g = 0; + int do_showinfo = showinfo; + int do_showrest = 0; + int erroroffset, len, delimiter; + + if (infile == stdin) printf(" re> "); + if (fgets((char *)buffer, sizeof(buffer), infile) == NULL) break; + if (infile != stdin) fprintf(outfile, "%s", (char *)buffer); + + p = buffer; + while (isspace(*p)) p++; + if (*p == 0) continue; + + /* Get the delimiter and seek the end of the pattern; if is isn't + complete, read more. */ + + delimiter = *p++; + + if (isalnum(delimiter) || delimiter == '\\') + { + fprintf(outfile, "** Delimiter must not be alphameric or \\\n"); + goto SKIP_DATA; + } + + pp = p; + + for(;;) + { + while (*pp != 0) + { + if (*pp == '\\' && pp[1] != 0) pp++; + else if (*pp == delimiter) break; + pp++; + } + if (*pp != 0) break; + + len = sizeof(buffer) - (pp - buffer); + if (len < 256) + { + fprintf(outfile, "** Expression too long - missing delimiter?\n"); + goto SKIP_DATA; + } + + if (infile == stdin) printf(" > "); + if (fgets((char *)pp, len, infile) == NULL) + { + fprintf(outfile, "** Unexpected EOF\n"); + done = 1; + goto CONTINUE; + } + if (infile != stdin) fprintf(outfile, "%s", (char *)pp); + } + + /* If the first character after the delimiter is backslash, make + the pattern end with backslash. This is purely to provide a way + of testing for the error message when a pattern ends with backslash. */ + + if (pp[1] == '\\') *pp++ = '\\'; + + /* Terminate the pattern at the delimiter */ + + *pp++ = 0; + + /* Look for options after final delimiter */ + + options = 0; + study_options = 0; + log_store = showstore; /* default from command line */ + + while (*pp != 0) + { + switch (*pp++) + { + case 'g': do_g = 1; break; + case 'i': options |= PCRE_CASELESS; break; + case 'm': options |= PCRE_MULTILINE; break; + case 's': options |= PCRE_DOTALL; break; + case 'x': options |= PCRE_EXTENDED; break; + + case '+': do_showrest = 1; break; + case 'A': options |= PCRE_ANCHORED; break; + case 'D': do_debug = do_showinfo = 1; break; + case 'E': options |= PCRE_DOLLAR_ENDONLY; break; + case 'G': do_G = 1; break; + case 'I': do_showinfo = 1; break; + case 'M': log_store = 1; break; + +#if !defined NOPOSIX + case 'P': do_posix = 1; break; +#endif + + case 'S': do_study = 1; break; + case 'U': options |= PCRE_UNGREEDY; break; + case 'X': options |= PCRE_EXTRA; break; + + case 'L': + ppp = pp; + while (*ppp != '\n' && *ppp != ' ') ppp++; + *ppp = 0; + if (setlocale(LC_CTYPE, (const char *)pp) == NULL) + { + fprintf(outfile, "** Failed to set locale \"%s\"\n", pp); + goto SKIP_DATA; + } + tables = pcre_maketables(); + pp = ppp; + break; + + case '\n': case ' ': break; + default: + fprintf(outfile, "** Unknown option '%c'\n", pp[-1]); + goto SKIP_DATA; + } + } + + /* Handle compiling via the POSIX interface, which doesn't support the + timing, showing, or debugging options, nor the ability to pass over + local character tables. */ + +#if !defined NOPOSIX + if (posix || do_posix) + { + int rc; + int cflags = 0; + if ((options & PCRE_CASELESS) != 0) cflags |= REG_ICASE; + if ((options & PCRE_MULTILINE) != 0) cflags |= REG_NEWLINE; + rc = regcomp(&preg, (char *)p, cflags); + + /* Compilation failed; go back for another re, skipping to blank line + if non-interactive. */ + + if (rc != 0) + { + (void)regerror(rc, &preg, (char *)buffer, sizeof(buffer)); + fprintf(outfile, "Failed: POSIX code %d: %s\n", rc, buffer); + goto SKIP_DATA; + } + } + + /* Handle compiling via the native interface */ + + else +#endif /* !defined NOPOSIX */ + + { + if (timeit) + { + register int i; + clock_t time_taken; + clock_t start_time = clock(); + for (i = 0; i < LOOPREPEAT; i++) + { + re = pcre_compile((char *)p, options, &error, &erroroffset, tables); + if (re != NULL) free(re); + } + time_taken = clock() - start_time; + fprintf(outfile, "Compile time %.3f milliseconds\n", + ((double)time_taken * 1000.0) / + ((double)LOOPREPEAT * (double)CLOCKS_PER_SEC)); + } + + re = pcre_compile((char *)p, options, &error, &erroroffset, tables); + + /* Compilation failed; go back for another re, skipping to blank line + if non-interactive. */ + + if (re == NULL) + { + fprintf(outfile, "Failed: %s at offset %d\n", error, erroroffset); + SKIP_DATA: + if (infile != stdin) + { + for (;;) + { + if (fgets((char *)buffer, sizeof(buffer), infile) == NULL) + { + done = 1; + goto CONTINUE; + } + len = (int)strlen((char *)buffer); + while (len > 0 && isspace(buffer[len-1])) len--; + if (len == 0) break; + } + fprintf(outfile, "\n"); + } + goto CONTINUE; + } + + /* Compilation succeeded; print data if required. There are now two + info-returning functions. The old one has a limited interface and + returns only limited data. Check that it agrees with the newer one. */ + + if (do_showinfo) + { + int old_first_char, old_options, old_count; + int count, backrefmax, first_char, need_char; + size_t size; + + if (do_debug) print_internals(re); + + new_info(re, NULL, PCRE_INFO_OPTIONS, &options); + new_info(re, NULL, PCRE_INFO_SIZE, &size); + new_info(re, NULL, PCRE_INFO_CAPTURECOUNT, &count); + new_info(re, NULL, PCRE_INFO_BACKREFMAX, &backrefmax); + new_info(re, NULL, PCRE_INFO_FIRSTCHAR, &first_char); + new_info(re, NULL, PCRE_INFO_LASTLITERAL, &need_char); + + old_count = pcre_info(re, &old_options, &old_first_char); + if (count < 0) fprintf(outfile, + "Error %d from pcre_info()\n", count); + else + { + if (old_count != count) fprintf(outfile, + "Count disagreement: pcre_fullinfo=%d pcre_info=%d\n", count, + old_count); + + if (old_first_char != first_char) fprintf(outfile, + "First char disagreement: pcre_fullinfo=%d pcre_info=%d\n", + first_char, old_first_char); + + if (old_options != options) fprintf(outfile, + "Options disagreement: pcre_fullinfo=%d pcre_info=%d\n", options, + old_options); + } + + if (size != gotten_store) fprintf(outfile, + "Size disagreement: pcre_fullinfo=%d call to malloc for %d\n", + size, gotten_store); + + fprintf(outfile, "Capturing subpattern count = %d\n", count); + if (backrefmax > 0) + fprintf(outfile, "Max back reference = %d\n", backrefmax); + if (options == 0) fprintf(outfile, "No options\n"); + else fprintf(outfile, "Options:%s%s%s%s%s%s%s%s\n", + ((options & PCRE_ANCHORED) != 0)? " anchored" : "", + ((options & PCRE_CASELESS) != 0)? " caseless" : "", + ((options & PCRE_EXTENDED) != 0)? " extended" : "", + ((options & PCRE_MULTILINE) != 0)? " multiline" : "", + ((options & PCRE_DOTALL) != 0)? " dotall" : "", + ((options & PCRE_DOLLAR_ENDONLY) != 0)? " dollar_endonly" : "", + ((options & PCRE_EXTRA) != 0)? " extra" : "", + ((options & PCRE_UNGREEDY) != 0)? " ungreedy" : ""); + + if (((((real_pcre *)re)->options) & PCRE_ICHANGED) != 0) + fprintf(outfile, "Case state changes\n"); + + if (first_char == -1) + { + fprintf(outfile, "First char at start or follows \\n\n"); + } + else if (first_char < 0) + { + fprintf(outfile, "No first char\n"); + } + else + { + if (isprint(first_char)) + fprintf(outfile, "First char = \'%c\'\n", first_char); + else + fprintf(outfile, "First char = %d\n", first_char); + } + + if (need_char < 0) + { + fprintf(outfile, "No need char\n"); + } + else + { + if (isprint(need_char)) + fprintf(outfile, "Need char = \'%c\'\n", need_char); + else + fprintf(outfile, "Need char = %d\n", need_char); + } + } + + /* If /S was present, study the regexp to generate additional info to + help with the matching. */ + + if (do_study) + { + if (timeit) + { + register int i; + clock_t time_taken; + clock_t start_time = clock(); + for (i = 0; i < LOOPREPEAT; i++) + extra = pcre_study(re, study_options, &error); + time_taken = clock() - start_time; + if (extra != NULL) free(extra); + fprintf(outfile, " Study time %.3f milliseconds\n", + ((double)time_taken * 1000.0)/ + ((double)LOOPREPEAT * (double)CLOCKS_PER_SEC)); + } + + extra = pcre_study(re, study_options, &error); + if (error != NULL) + fprintf(outfile, "Failed to study: %s\n", error); + else if (extra == NULL) + fprintf(outfile, "Study returned NULL\n"); + + else if (do_showinfo) + { + uschar *start_bits = NULL; + new_info(re, extra, PCRE_INFO_FIRSTTABLE, &start_bits); + if (start_bits == NULL) + fprintf(outfile, "No starting character set\n"); + else + { + int i; + int c = 24; + fprintf(outfile, "Starting character set: "); + for (i = 0; i < 256; i++) + { + if ((start_bits[i/8] & (1<<(i%8))) != 0) + { + if (c > 75) + { + fprintf(outfile, "\n "); + c = 2; + } + if (isprint(i) && i != ' ') + { + fprintf(outfile, "%c ", i); + c += 2; + } + else + { + fprintf(outfile, "\\x%02x ", i); + c += 5; + } + } + } + fprintf(outfile, "\n"); + } + } + } + } + + /* Read data lines and test them */ + + for (;;) + { + unsigned char *q; + unsigned char *bptr = dbuffer; + int count, c; + int copystrings = 0; + int getstrings = 0; + int getlist = 0; + int gmatched = 0; + int start_offset = 0; + int g_notempty = 0; + int offsets[45]; + int size_offsets = sizeof(offsets)/sizeof(int); + + options = 0; + + if (infile == stdin) printf("data> "); + if (fgets((char *)buffer, sizeof(buffer), infile) == NULL) + { + done = 1; + goto CONTINUE; + } + if (infile != stdin) fprintf(outfile, "%s", (char *)buffer); + + len = (int)strlen((char *)buffer); + while (len > 0 && isspace(buffer[len-1])) len--; + buffer[len] = 0; + if (len == 0) break; + + p = buffer; + while (isspace(*p)) p++; + + q = dbuffer; + while ((c = *p++) != 0) + { + int i = 0; + int n = 0; + if (c == '\\') switch ((c = *p++)) + { + case 'a': c = 7; break; + case 'b': c = '\b'; break; + case 'e': c = 27; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c -= '0'; + while (i++ < 2 && isdigit(*p) && *p != '8' && *p != '9') + c = c * 8 + *p++ - '0'; + break; + + case 'x': + c = 0; + while (i++ < 2 && isxdigit(*p)) + { + c = c * 16 + tolower(*p) - ((isdigit(*p))? '0' : 'W'); + p++; + } + break; + + case 0: /* Allows for an empty line */ + p--; + continue; + + case 'A': /* Option setting */ + options |= PCRE_ANCHORED; + continue; + + case 'B': + options |= PCRE_NOTBOL; + continue; + + case 'C': + while(isdigit(*p)) n = n * 10 + *p++ - '0'; + copystrings |= 1 << n; + continue; + + case 'G': + while(isdigit(*p)) n = n * 10 + *p++ - '0'; + getstrings |= 1 << n; + continue; + + case 'L': + getlist = 1; + continue; + + case 'N': + options |= PCRE_NOTEMPTY; + continue; + + case 'O': + while(isdigit(*p)) n = n * 10 + *p++ - '0'; + if (n <= (int)(sizeof(offsets)/sizeof(int))) size_offsets = n; + continue; + + case 'Z': + options |= PCRE_NOTEOL; + continue; + } + *q++ = c; + } + *q = 0; + len = q - dbuffer; + + /* Handle matching via the POSIX interface, which does not + support timing. */ + +#if !defined NOPOSIX + if (posix || do_posix) + { + int rc; + int eflags = 0; + regmatch_t pmatch[sizeof(offsets)/sizeof(int)]; + if ((options & PCRE_NOTBOL) != 0) eflags |= REG_NOTBOL; + if ((options & PCRE_NOTEOL) != 0) eflags |= REG_NOTEOL; + + rc = regexec(&preg, (const char *)bptr, size_offsets, pmatch, eflags); + + if (rc != 0) + { + (void)regerror(rc, &preg, (char *)buffer, sizeof(buffer)); + fprintf(outfile, "No match: POSIX code %d: %s\n", rc, buffer); + } + else + { + size_t i; + for (i = 0; i < size_offsets; i++) + { + if (pmatch[i].rm_so >= 0) + { + fprintf(outfile, "%2d: ", (int)i); + pchars(dbuffer + pmatch[i].rm_so, + pmatch[i].rm_eo - pmatch[i].rm_so); + fprintf(outfile, "\n"); + if (i == 0 && do_showrest) + { + fprintf(outfile, " 0+ "); + pchars(dbuffer + pmatch[i].rm_eo, len - pmatch[i].rm_eo); + fprintf(outfile, "\n"); + } + } + } + } + } + + /* Handle matching via the native interface - repeats for /g and /G */ + + else +#endif /* !defined NOPOSIX */ + + for (;; gmatched++) /* Loop for /g or /G */ + { + if (timeit) + { + register int i; + clock_t time_taken; + clock_t start_time = clock(); + for (i = 0; i < LOOPREPEAT; i++) + count = pcre_exec(re, extra, (char *)bptr, len, + start_offset, options | g_notempty, offsets, size_offsets); + time_taken = clock() - start_time; + fprintf(outfile, "Execute time %.3f milliseconds\n", + ((double)time_taken * 1000.0)/ + ((double)LOOPREPEAT * (double)CLOCKS_PER_SEC)); + } + + count = pcre_exec(re, extra, (char *)bptr, len, + start_offset, options | g_notempty, offsets, size_offsets); + + if (count == 0) + { + fprintf(outfile, "Matched, but too many substrings\n"); + count = size_offsets/3; + } + + /* Matched */ + + if (count >= 0) + { + int i; + for (i = 0; i < count * 2; i += 2) + { + if (offsets[i] < 0) + fprintf(outfile, "%2d: \n", i/2); + else + { + fprintf(outfile, "%2d: ", i/2); + pchars(bptr + offsets[i], offsets[i+1] - offsets[i]); + fprintf(outfile, "\n"); + if (i == 0) + { + if (do_showrest) + { + fprintf(outfile, " 0+ "); + pchars(bptr + offsets[i+1], len - offsets[i+1]); + fprintf(outfile, "\n"); + } + } + } + } + + for (i = 0; i < 32; i++) + { + if ((copystrings & (1 << i)) != 0) + { + char copybuffer[16]; + int rc = pcre_copy_substring((char *)bptr, offsets, count, + i, copybuffer, sizeof(copybuffer)); + if (rc < 0) + fprintf(outfile, "copy substring %d failed %d\n", i, rc); + else + fprintf(outfile, "%2dC %s (%d)\n", i, copybuffer, rc); + } + } + + for (i = 0; i < 32; i++) + { + if ((getstrings & (1 << i)) != 0) + { + const char *substring; + int rc = pcre_get_substring((char *)bptr, offsets, count, + i, &substring); + if (rc < 0) + fprintf(outfile, "get substring %d failed %d\n", i, rc); + else + { + fprintf(outfile, "%2dG %s (%d)\n", i, substring, rc); + free((void *)substring); + } + } + } + + if (getlist) + { + const char **stringlist; + int rc = pcre_get_substring_list((char *)bptr, offsets, count, + &stringlist); + if (rc < 0) + fprintf(outfile, "get substring list failed %d\n", rc); + else + { + for (i = 0; i < count; i++) + fprintf(outfile, "%2dL %s\n", i, stringlist[i]); + if (stringlist[i] != NULL) + fprintf(outfile, "string list not terminated by NULL\n"); + free((void *)stringlist); + } + } + } + + /* Failed to match. If this is a /g or /G loop and we previously set + PCRE_NOTEMPTY after a null match, this is not necessarily the end. + We want to advance the start offset, and continue. Fudge the offset + values to achieve this. We won't be at the end of the string - that + was checked before setting PCRE_NOTEMPTY. */ + + else + { + if (g_notempty != 0) + { + offsets[0] = start_offset; + offsets[1] = start_offset + 1; + } + else + { + if (gmatched == 0) /* Error if no previous matches */ + { + if (count == -1) fprintf(outfile, "No match\n"); + else fprintf(outfile, "Error %d\n", count); + } + break; /* Out of the /g loop */ + } + } + + /* If not /g or /G we are done */ + + if (!do_g && !do_G) break; + + /* If we have matched an empty string, first check to see if we are at + the end of the subject. If so, the /g loop is over. Otherwise, mimic + what Perl's /g options does. This turns out to be rather cunning. First + we set PCRE_NOTEMPTY and try the match again at the same point. If this + fails (picked up above) we advance to the next character. */ + + g_notempty = 0; + if (offsets[0] == offsets[1]) + { + if (offsets[0] == len) break; + g_notempty = PCRE_NOTEMPTY; + } + + /* For /g, update the start offset, leaving the rest alone */ + + if (do_g) start_offset = offsets[1]; + + /* For /G, update the pointer and length */ + + else + { + bptr += offsets[1]; + len -= offsets[1]; + } + } /* End of loop for /g and /G */ + } /* End of loop for data lines */ + + CONTINUE: + +#if !defined NOPOSIX + if (posix || do_posix) regfree(&preg); +#endif + + if (re != NULL) free(re); + if (extra != NULL) free(extra); + if (tables != NULL) + { + free((void *)tables); + setlocale(LC_CTYPE, "C"); + } + } + +fprintf(outfile, "\n"); +return 0; +} + +/* End */ diff --git a/srclib/pcre/perltest b/srclib/pcre/perltest new file mode 100755 index 00000000000..1e96c792b96 --- /dev/null +++ b/srclib/pcre/perltest @@ -0,0 +1,169 @@ +#! /usr/bin/perl + +# Program for testing regular expressions with perl to check that PCRE handles +# them the same. + + +# Function for turning a string into a string of printing chars + +sub pchars { +my($t) = ""; + +foreach $c (split(//, @_[0])) + { + if (ord $c >= 32 && ord $c < 127) { $t .= $c; } + else { $t .= sprintf("\\x%02x", ord $c); } + } +$t; +} + + + +# Read lines from named file or stdin and write to named file or stdout; lines +# consist of a regular expression, in delimiters and optionally followed by +# options, followed by a set of test data, terminated by an empty line. + +# Sort out the input and output files + +if (@ARGV > 0) + { + open(INFILE, "<$ARGV[0]") || die "Failed to open $ARGV[0]\n"; + $infile = "INFILE"; + } +else { $infile = "STDIN"; } + +if (@ARGV > 1) + { + open(OUTFILE, ">$ARGV[1]") || die "Failed to open $ARGV[1]\n"; + $outfile = "OUTFILE"; + } +else { $outfile = "STDOUT"; } + +printf($outfile "Perl $] Regular Expressions\n\n"); + +# Main loop + +NEXT_RE: +for (;;) + { + printf " re> " if $infile eq "STDIN"; + last if ! ($_ = <$infile>); + printf $outfile "$_" if $infile ne "STDIN"; + next if ($_ eq ""); + + $pattern = $_; + + while ($pattern !~ /^\s*(.).*\1/s) + { + printf " > " if $infile eq "STDIN"; + last if ! ($_ = <$infile>); + printf $outfile "$_" if $infile ne "STDIN"; + $pattern .= $_; + } + + chomp($pattern); + $pattern =~ s/\s+$//; + + # The private /+ modifier means "print $' afterwards". We use it + # only on the end of patterns to make it easy to chop off here. + + $showrest = ($pattern =~ s/\+(?=[a-z]*$)//); + + # Check that the pattern is valid + + eval "\$_ =~ ${pattern}"; + if ($@) + { + printf $outfile "Error: $@"; + next NEXT_RE; + } + + # If the /g modifier is present, we want to put a loop round the matching; + # otherwise just a single "if". + + $cmd = ($pattern =~ /g[a-z]*$/)? "while" : "if"; + + # If the pattern is actually the null string, Perl uses the most recently + # executed (and successfully compiled) regex is used instead. This is a + # nasty trap for the unwary! The PCRE test suite does contain null strings + # in places - if they are allowed through here all sorts of weird and + # unexpected effects happen. To avoid this, we replace such patterns with + # a non-null pattern that has the same effect. + + $pattern = "/(?#)/$2" if ($pattern =~ /^(.)\1(.*)$/); + + # Read data lines and test them + + for (;;) + { + printf "data> " if $infile eq "STDIN"; + last NEXT_RE if ! ($_ = <$infile>); + chomp; + printf $outfile "$_\n" if $infile ne "STDIN"; + + s/\s+$//; + s/^\s+//; + + last if ($_ eq ""); + + $x = eval "\"$_\""; # To get escapes processed + + # Empty array for holding results, then do the matching. + + @subs = (); + + eval "${cmd} (\$x =~ ${pattern}) {" . + "push \@subs,\$&;" . + "push \@subs,\$1;" . + "push \@subs,\$2;" . + "push \@subs,\$3;" . + "push \@subs,\$4;" . + "push \@subs,\$5;" . + "push \@subs,\$6;" . + "push \@subs,\$7;" . + "push \@subs,\$8;" . + "push \@subs,\$9;" . + "push \@subs,\$10;" . + "push \@subs,\$11;" . + "push \@subs,\$12;" . + "push \@subs,\$13;" . + "push \@subs,\$14;" . + "push \@subs,\$15;" . + "push \@subs,\$16;" . + "push \@subs,\$'; }"; + + if ($@) + { + printf $outfile "Error: $@\n"; + next NEXT_RE; + } + elsif (scalar(@subs) == 0) + { + printf $outfile "No match\n"; + } + else + { + while (scalar(@subs) != 0) + { + printf $outfile (" 0: %s\n", &pchars($subs[0])); + printf $outfile (" 0+ %s\n", &pchars($subs[17])) if $showrest; + $last_printed = 0; + for ($i = 1; $i <= 16; $i++) + { + if (defined $subs[$i]) + { + while ($last_printed++ < $i-1) + { printf $outfile ("%2d: \n", $last_printed); } + printf $outfile ("%2d: %s\n", $i, &pchars($subs[$i])); + $last_printed = $i; + } + } + splice(@subs, 0, 18); + } + } + } + } + +printf $outfile "\n"; + +# End diff --git a/srclib/pcre/pgrep.c b/srclib/pcre/pgrep.c new file mode 100644 index 00000000000..ad1b87eae97 --- /dev/null +++ b/srclib/pcre/pgrep.c @@ -0,0 +1,225 @@ +/************************************************* +* PCRE grep program * +*************************************************/ + +#include +#include +#include +#include +#include "config.h" +#include "pcre.h" + +#define FALSE 0 +#define TRUE 1 + +typedef int BOOL; + + + +/************************************************* +* Global variables * +*************************************************/ + +static pcre *pattern; +static pcre_extra *hints; + +static BOOL count_only = FALSE; +static BOOL filenames_only = FALSE; +static BOOL invert = FALSE; +static BOOL number = FALSE; +static BOOL silent = FALSE; +static BOOL whole_lines = FALSE; + + + +#if ! HAVE_STRERROR +/************************************************* +* Provide strerror() for non-ANSI libraries * +*************************************************/ + +/* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror() +in their libraries, but can provide the same facility by this simple +alternative function. */ + +extern int sys_nerr; +extern char *sys_errlist[]; + +char * +strerror(int n) +{ +if (n < 0 || n >= sys_nerr) return "unknown error number"; +return sys_errlist[n]; +} +#endif /* HAVE_STRERROR */ + + + +/************************************************* +* Grep an individual file * +*************************************************/ + +static int +pgrep(FILE *in, char *name) +{ +int rc = 1; +int linenumber = 0; +int count = 0; +int offsets[99]; +char buffer[BUFSIZ]; + +while (fgets(buffer, sizeof(buffer), in) != NULL) + { + BOOL match; + int length = (int)strlen(buffer); + if (length > 0 && buffer[length-1] == '\n') buffer[--length] = 0; + linenumber++; + + match = pcre_exec(pattern, hints, buffer, length, 0, 0, offsets, 99) >= 0; + if (match && whole_lines && offsets[1] != length) match = FALSE; + + if (match != invert) + { + if (count_only) count++; + + else if (filenames_only) + { + fprintf(stdout, "%s\n", (name == NULL)? "" : name); + return 0; + } + + else if (silent) return 0; + + else + { + if (name != NULL) fprintf(stdout, "%s:", name); + if (number) fprintf(stdout, "%d:", linenumber); + fprintf(stdout, "%s\n", buffer); + } + + rc = 0; + } + } + +if (count_only) + { + if (name != NULL) fprintf(stdout, "%s:", name); + fprintf(stdout, "%d\n", count); + } + +return rc; +} + + + + +/************************************************* +* Usage function * +*************************************************/ + +static int +usage(int rc) +{ +fprintf(stderr, "Usage: pgrep [-Vchilnsvx] pattern [file] ...\n"); +return rc; +} + + + + +/************************************************* +* Main program * +*************************************************/ + +int +main(int argc, char **argv) +{ +int i; +int rc = 1; +int options = 0; +int errptr; +const char *error; +BOOL filenames = TRUE; + +/* Process the options */ + +for (i = 1; i < argc; i++) + { + char *s; + if (argv[i][0] != '-') break; + s = argv[i] + 1; + while (*s != 0) + { + switch (*s++) + { + case 'c': count_only = TRUE; break; + case 'h': filenames = FALSE; break; + case 'i': options |= PCRE_CASELESS; break; + case 'l': filenames_only = TRUE; + case 'n': number = TRUE; break; + case 's': silent = TRUE; break; + case 'v': invert = TRUE; break; + case 'x': whole_lines = TRUE; options |= PCRE_ANCHORED; break; + + case 'V': + fprintf(stderr, "PCRE version %s\n", pcre_version()); + break; + + default: + fprintf(stderr, "pgrep: unknown option %c\n", s[-1]); + return usage(2); + } + } + } + +/* There must be at least a regexp argument */ + +if (i >= argc) return usage(0); + +/* Compile the regular expression. */ + +pattern = pcre_compile(argv[i++], options, &error, &errptr, NULL); +if (pattern == NULL) + { + fprintf(stderr, "pgrep: error in regex at offset %d: %s\n", errptr, error); + return 2; + } + +/* Study the regular expression, as we will be running it may times */ + +hints = pcre_study(pattern, 0, &error); +if (error != NULL) + { + fprintf(stderr, "pgrep: error while studing regex: %s\n", error); + return 2; + } + +/* If there are no further arguments, do the business on stdin and exit */ + +if (i >= argc) return pgrep(stdin, NULL); + +/* Otherwise, work through the remaining arguments as files. If there is only +one, don't give its name on the output. */ + +if (i == argc - 1) filenames = FALSE; +if (filenames_only) filenames = TRUE; + +for (; i < argc; i++) + { + FILE *in = fopen(argv[i], "r"); + if (in == NULL) + { + fprintf(stderr, "%s: failed to open: %s\n", argv[i], strerror(errno)); + rc = 2; + } + else + { + int frc = pgrep(in, filenames? argv[i] : NULL); + if (frc == 0 && rc == 1) rc = 0; + fclose(in); + } + } + +return rc; +} + +/* End */ diff --git a/srclib/pcre/study.c b/srclib/pcre/study.c new file mode 100644 index 00000000000..676db94665c --- /dev/null +++ b/srclib/pcre/study.c @@ -0,0 +1,397 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-2000 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- +*/ + + +/* Include the internals header, which itself includes Standard C headers plus +the external pcre header. */ + +#include "internal.h" + + + +/************************************************* +* Set a bit and maybe its alternate case * +*************************************************/ + +/* Given a character, set its bit in the table, and also the bit for the other +version of a letter if we are caseless. + +Arguments: + start_bits points to the bit map + c is the character + caseless the caseless flag + cd the block with char table pointers + +Returns: nothing +*/ + +static void +set_bit(uschar *start_bits, int c, BOOL caseless, compile_data *cd) +{ +start_bits[c/8] |= (1 << (c&7)); +if (caseless && (cd->ctypes[c] & ctype_letter) != 0) + start_bits[cd->fcc[c]/8] |= (1 << (cd->fcc[c]&7)); +} + + + +/************************************************* +* Create bitmap of starting chars * +*************************************************/ + +/* This function scans a compiled unanchored expression and attempts to build a +bitmap of the set of initial characters. If it can't, it returns FALSE. As time +goes by, we may be able to get more clever at doing this. + +Arguments: + code points to an expression + start_bits points to a 32-byte table, initialized to 0 + caseless the current state of the caseless flag + cd the block with char table pointers + +Returns: TRUE if table built, FALSE otherwise +*/ + +static BOOL +set_start_bits(const uschar *code, uschar *start_bits, BOOL caseless, + compile_data *cd) +{ +register int c; + +/* This next statement and the later reference to dummy are here in order to +trick the optimizer of the IBM C compiler for OS/2 into generating correct +code. Apparently IBM isn't going to fix the problem, and we would rather not +disable optimization (in this module it actually makes a big difference, and +the pcre module can use all the optimization it can get). */ + +volatile int dummy; + +do + { + const uschar *tcode = code + 3; + BOOL try_next = TRUE; + + while (try_next) + { + try_next = FALSE; + + /* If a branch starts with a bracket or a positive lookahead assertion, + recurse to set bits from within them. That's all for this branch. */ + + if ((int)*tcode >= OP_BRA || *tcode == OP_ASSERT) + { + if (!set_start_bits(tcode, start_bits, caseless, cd)) + return FALSE; + } + + else switch(*tcode) + { + default: + return FALSE; + + /* Skip over lookbehind and negative lookahead assertions */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + try_next = TRUE; + do tcode += (tcode[1] << 8) + tcode[2]; while (*tcode == OP_ALT); + tcode += 3; + break; + + /* Skip over an option setting, changing the caseless flag */ + + case OP_OPT: + caseless = (tcode[1] & PCRE_CASELESS) != 0; + tcode += 2; + try_next = TRUE; + break; + + /* BRAZERO does the bracket, but carries on. */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + if (!set_start_bits(++tcode, start_bits, caseless, cd)) + return FALSE; + dummy = 1; + do tcode += (tcode[1] << 8) + tcode[2]; while (*tcode == OP_ALT); + tcode += 3; + try_next = TRUE; + break; + + /* Single-char * or ? sets the bit and tries the next item */ + + case OP_STAR: + case OP_MINSTAR: + case OP_QUERY: + case OP_MINQUERY: + set_bit(start_bits, tcode[1], caseless, cd); + tcode += 2; + try_next = TRUE; + break; + + /* Single-char upto sets the bit and tries the next */ + + case OP_UPTO: + case OP_MINUPTO: + set_bit(start_bits, tcode[3], caseless, cd); + tcode += 4; + try_next = TRUE; + break; + + /* At least one single char sets the bit and stops */ + + case OP_EXACT: /* Fall through */ + tcode++; + + case OP_CHARS: /* Fall through */ + tcode++; + + case OP_PLUS: + case OP_MINPLUS: + set_bit(start_bits, tcode[1], caseless, cd); + break; + + /* Single character type sets the bits and stops */ + + case OP_NOT_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_digit]; + break; + + case OP_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_digit]; + break; + + case OP_NOT_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_space]; + break; + + case OP_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_space]; + break; + + case OP_NOT_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_word]; + break; + + case OP_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_word]; + break; + + /* One or more character type fudges the pointer and restarts, knowing + it will hit a single character type and stop there. */ + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + tcode++; + try_next = TRUE; + break; + + case OP_TYPEEXACT: + tcode += 3; + try_next = TRUE; + break; + + /* Zero or more repeats of character types set the bits and then + try again. */ + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + tcode += 2; /* Fall through */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + switch(tcode[1]) + { + case OP_NOT_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_digit]; + break; + + case OP_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_digit]; + break; + + case OP_NOT_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_space]; + break; + + case OP_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_space]; + break; + + case OP_NOT_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_word]; + break; + + case OP_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_word]; + break; + } + + tcode += 2; + try_next = TRUE; + break; + + /* Character class: set the bits and either carry on or not, + according to the repeat count. */ + + case OP_CLASS: + { + tcode++; + for (c = 0; c < 32; c++) start_bits[c] |= tcode[c]; + tcode += 32; + switch (*tcode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + tcode++; + try_next = TRUE; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (((tcode[1] << 8) + tcode[2]) == 0) + { + tcode += 5; + try_next = TRUE; + } + break; + } + } + break; /* End of class handling */ + + } /* End of switch */ + } /* End of try_next loop */ + + code += (code[1] << 8) + code[2]; /* Advance to next branch */ + } +while (*code == OP_ALT); +return TRUE; +} + + + +/************************************************* +* Study a compiled expression * +*************************************************/ + +/* This function is handed a compiled expression that it must study to produce +information that will speed up the matching. It returns a pcre_extra block +which then gets handed back to pcre_exec(). + +Arguments: + re points to the compiled expression + options contains option bits + errorptr points to where to place error messages; + set NULL unless error + +Returns: pointer to a pcre_extra block, + NULL on error or if no optimization possible +*/ + +pcre_extra * +pcre_study(const pcre *external_re, int options, const char **errorptr) +{ +uschar start_bits[32]; +real_pcre_extra *extra; +const real_pcre *re = (const real_pcre *)external_re; +compile_data compile_block; + +*errorptr = NULL; + +if (re == NULL || re->magic_number != MAGIC_NUMBER) + { + *errorptr = "argument is not a compiled regular expression"; + return NULL; + } + +if ((options & ~PUBLIC_STUDY_OPTIONS) != 0) + { + *errorptr = "unknown or incorrect option bit(s) set"; + return NULL; + } + +/* For an anchored pattern, or an unchored pattern that has a first char, or a +multiline pattern that matches only at "line starts", no further processing at +present. */ + +if ((re->options & (PCRE_ANCHORED|PCRE_FIRSTSET|PCRE_STARTLINE)) != 0) + return NULL; + +/* Set the character tables in the block which is passed around */ + +compile_block.lcc = re->tables + lcc_offset; +compile_block.fcc = re->tables + fcc_offset; +compile_block.cbits = re->tables + cbits_offset; +compile_block.ctypes = re->tables + ctypes_offset; + +/* See if we can find a fixed set of initial characters for the pattern. */ + +memset(start_bits, 0, 32 * sizeof(uschar)); +if (!set_start_bits(re->code, start_bits, (re->options & PCRE_CASELESS) != 0, + &compile_block)) return NULL; + +/* Get an "extra" block and put the information therein. */ + +extra = (real_pcre_extra *)(pcre_malloc)(sizeof(real_pcre_extra)); + +if (extra == NULL) + { + *errorptr = "failed to get memory"; + return NULL; + } + +extra->options = PCRE_STUDY_MAPPED; +memcpy(extra->start_bits, start_bits, sizeof(start_bits)); + +return (pcre_extra *)extra; +} + +/* End of study.c */ diff --git a/srclib/pcre/testdata/testinput1 b/srclib/pcre/testdata/testinput1 new file mode 100644 index 00000000000..d72a2c580b8 --- /dev/null +++ b/srclib/pcre/testdata/testinput1 @@ -0,0 +1,1901 @@ +/the quick brown fox/ + the quick brown fox + The quick brown FOX + What do you know about the quick brown fox? + What do you know about THE QUICK BROWN FOX? + +/The quick brown fox/i + the quick brown fox + The quick brown FOX + What do you know about the quick brown fox? + What do you know about THE QUICK BROWN FOX? + +/abcd\t\n\r\f\a\e\071\x3b\$\\\?caxyz/ + abcd\t\n\r\f\a\e9;\$\\?caxyz + +/a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz/ + abxyzpqrrrabbxyyyypqAzz + abxyzpqrrrabbxyyyypqAzz + aabxyzpqrrrabbxyyyypqAzz + aaabxyzpqrrrabbxyyyypqAzz + aaaabxyzpqrrrabbxyyyypqAzz + abcxyzpqrrrabbxyyyypqAzz + aabcxyzpqrrrabbxyyyypqAzz + aaabcxyzpqrrrabbxyyyypAzz + aaabcxyzpqrrrabbxyyyypqAzz + aaabcxyzpqrrrabbxyyyypqqAzz + aaabcxyzpqrrrabbxyyyypqqqAzz + aaabcxyzpqrrrabbxyyyypqqqqAzz + aaabcxyzpqrrrabbxyyyypqqqqqAzz + aaabcxyzpqrrrabbxyyyypqqqqqqAzz + aaaabcxyzpqrrrabbxyyyypqAzz + abxyzzpqrrrabbxyyyypqAzz + aabxyzzzpqrrrabbxyyyypqAzz + aaabxyzzzzpqrrrabbxyyyypqAzz + aaaabxyzzzzpqrrrabbxyyyypqAzz + abcxyzzpqrrrabbxyyyypqAzz + aabcxyzzzpqrrrabbxyyyypqAzz + aaabcxyzzzzpqrrrabbxyyyypqAzz + aaaabcxyzzzzpqrrrabbxyyyypqAzz + aaaabcxyzzzzpqrrrabbbxyyyypqAzz + aaaabcxyzzzzpqrrrabbbxyyyyypqAzz + aaabcxyzpqrrrabbxyyyypABzz + aaabcxyzpqrrrabbxyyyypABBzz + >>>aaabxyzpqrrrabbxyyyypqAzz + >aaaabxyzpqrrrabbxyyyypqAzz + >>>>abcxyzpqrrrabbxyyyypqAzz + *** Failers + abxyzpqrrabbxyyyypqAzz + abxyzpqrrrrabbxyyyypqAzz + abxyzpqrrrabxyyyypqAzz + aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz + aaaabcxyzzzzpqrrrabbbxyyypqAzz + aaabcxyzpqrrrabbxyyyypqqqqqqqAzz + +/^(abc){1,2}zz/ + abczz + abcabczz + *** Failers + zz + abcabcabczz + >>abczz + +/^(b+?|a){1,2}?c/ + bc + bbc + bbbc + bac + bbac + aac + abbbbbbbbbbbc + bbbbbbbbbbbac + *** Failers + aaac + abbbbbbbbbbbac + +/^(b+|a){1,2}c/ + bc + bbc + bbbc + bac + bbac + aac + abbbbbbbbbbbc + bbbbbbbbbbbac + *** Failers + aaac + abbbbbbbbbbbac + +/^(b+|a){1,2}?bc/ + bbc + +/^(b*|ba){1,2}?bc/ + babc + bbabc + bababc + *** Failers + bababbc + babababc + +/^(ba|b*){1,2}?bc/ + babc + bbabc + bababc + *** Failers + bababbc + babababc + +/^\ca\cA\c[\c{\c:/ + \x01\x01\e;z + +/^[ab\]cde]/ + athing + bthing + ]thing + cthing + dthing + ething + *** Failers + fthing + [thing + \\thing + +/^[]cde]/ + ]thing + cthing + dthing + ething + *** Failers + athing + fthing + +/^[^ab\]cde]/ + fthing + [thing + \\thing + *** Failers + athing + bthing + ]thing + cthing + dthing + ething + +/^[^]cde]/ + athing + fthing + *** Failers + ]thing + cthing + dthing + ething + +/^\/ +  + +/^ÿ/ + ÿ + +/^[0-9]+$/ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 100 + *** Failers + abc + +/^.*nter/ + enter + inter + uponter + +/^xxx[0-9]+$/ + xxx0 + xxx1234 + *** Failers + xxx + +/^.+[0-9][0-9][0-9]$/ + x123 + xx123 + 123456 + *** Failers + 123 + x1234 + +/^.+?[0-9][0-9][0-9]$/ + x123 + xx123 + 123456 + *** Failers + 123 + x1234 + +/^([^!]+)!(.+)=apquxz\.ixr\.zzz\.ac\.uk$/ + abc!pqr=apquxz.ixr.zzz.ac.uk + *** Failers + !pqr=apquxz.ixr.zzz.ac.uk + abc!=apquxz.ixr.zzz.ac.uk + abc!pqr=apquxz:ixr.zzz.ac.uk + abc!pqr=apquxz.ixr.zzz.ac.ukk + +/:/ + Well, we need a colon: somewhere + *** Fail if we don't + +/([\da-f:]+)$/i + 0abc + abc + fed + E + :: + 5f03:12C0::932e + fed def + Any old stuff + *** Failers + 0zzz + gzzz + fed\x20 + Any old rubbish + +/^.*\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ + .1.2.3 + A.12.123.0 + *** Failers + .1.2.3333 + 1.2.3 + 1234.2.3 + +/^(\d+)\s+IN\s+SOA\s+(\S+)\s+(\S+)\s*\(\s*$/ + 1 IN SOA non-sp1 non-sp2( + 1 IN SOA non-sp1 non-sp2 ( + *** Failers + 1IN SOA non-sp1 non-sp2( + +/^[a-zA-Z\d][a-zA-Z\d\-]*(\.[a-zA-Z\d][a-zA-z\d\-]*)*\.$/ + a. + Z. + 2. + ab-c.pq-r. + sxk.zzz.ac.uk. + x-.y-. + *** Failers + -abc.peq. + +/^\*\.[a-z]([a-z\-\d]*[a-z\d]+)?(\.[a-z]([a-z\-\d]*[a-z\d]+)?)*$/ + *.a + *.b0-a + *.c3-b.c + *.c-a.b-c + *** Failers + *.0 + *.a- + *.a-b.c- + *.c-a.0-c + +/^(?=ab(de))(abd)(e)/ + abde + +/^(?!(ab)de|x)(abd)(f)/ + abdf + +/^(?=(ab(cd)))(ab)/ + abcd + +/^[\da-f](\.[\da-f])*$/i + a.b.c.d + A.B.C.D + a.b.c.1.2.3.C + +/^\".*\"\s*(;.*)?$/ + \"1234\" + \"abcd\" ; + \"\" ; rhubarb + *** Failers + \"1234\" : things + +/^$/ + \ + *** Failers + +/ ^ a (?# begins with a) b\sc (?# then b c) $ (?# then end)/x + ab c + *** Failers + abc + ab cde + +/(?x) ^ a (?# begins with a) b\sc (?# then b c) $ (?# then end)/ + ab c + *** Failers + abc + ab cde + +/^ a\ b[c ]d $/x + a bcd + a b d + *** Failers + abcd + ab d + +/^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$/ + abcdefhijklm + +/^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$/ + abcdefhijklm + +/^[\w][\W][\s][\S][\d][\D][\b][\n][\c]][\022]/ + a+ Z0+\x08\n\x1d\x12 + +/^[.^$|()*+?{,}]+/ + .^\$(*+)|{?,?} + +/^a*\w/ + z + az + aaaz + a + aa + aaaa + a+ + aa+ + +/^a*?\w/ + z + az + aaaz + a + aa + aaaa + a+ + aa+ + +/^a+\w/ + az + aaaz + aa + aaaa + aa+ + +/^a+?\w/ + az + aaaz + aa + aaaa + aa+ + +/^\d{8}\w{2,}/ + 1234567890 + 12345678ab + 12345678__ + *** Failers + 1234567 + +/^[aeiou\d]{4,5}$/ + uoie + 1234 + 12345 + aaaaa + *** Failers + 123456 + +/^[aeiou\d]{4,5}?/ + uoie + 1234 + 12345 + aaaaa + 123456 + +/\A(abc|def)=(\1){2,3}\Z/ + abc=abcabc + def=defdefdef + *** Failers + abc=defdef + +/^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\11*(\3\4)\1(?#)2$/ + abcdefghijkcda2 + abcdefghijkkkkcda2 + +/(cat(a(ract|tonic)|erpillar)) \1()2(3)/ + cataract cataract23 + catatonic catatonic23 + caterpillar caterpillar23 + + +/^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]/ + From abcd Mon Sep 01 12:33:02 1997 + +/^From\s+\S+\s+([a-zA-Z]{3}\s+){2}\d{1,2}\s+\d\d:\d\d/ + From abcd Mon Sep 01 12:33:02 1997 + From abcd Mon Sep 1 12:33:02 1997 + *** Failers + From abcd Sep 01 12:33:02 1997 + +/^12.34/s + 12\n34 + 12\r34 + +/\w+(?=\t)/ + the quick brown\t fox + +/foo(?!bar)(.*)/ + foobar is foolish see? + +/(?:(?!foo)...|^.{0,2})bar(.*)/ + foobar crowbar etc + barrel + 2barrel + A barrel + +/^(\D*)(?=\d)(?!123)/ + abc456 + *** Failers + abc123 + +/^1234(?# test newlines + inside)/ + 1234 + +/^1234 #comment in extended re + /x + 1234 + +/#rhubarb + abcd/x + abcd + +/^abcd#rhubarb/x + abcd + +/^(a)\1{2,3}(.)/ + aaab + aaaab + aaaaab + aaaaaab + +/(?!^)abc/ + the abc + *** Failers + abc + +/(?=^)abc/ + abc + *** Failers + the abc + +/^[ab]{1,3}(ab*|b)/ + aabbbbb + +/^[ab]{1,3}?(ab*|b)/ + aabbbbb + +/^[ab]{1,3}?(ab*?|b)/ + aabbbbb + +/^[ab]{1,3}(ab*?|b)/ + aabbbbb + +/ (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* # optional leading comment +(?: (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +) # initial word +(?: (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +) )* # further okay, if led by a period +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* @ (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # initial subdomain +(?: # +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. # if led by a period... +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # ...further okay +)* +# address +| # or +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +) # one word, optionally followed by.... +(?: +[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037] | # atom and space parts, or... +\( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) | # comments, or... + +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +# quoted strings +)* +< (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* # leading < +(?: @ (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # initial subdomain +(?: # +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. # if led by a period... +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # ...further okay +)* + +(?: (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* , (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* @ (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # initial subdomain +(?: # +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. # if led by a period... +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # ...further okay +)* +)* # further okay, if led by comma +: # closing colon +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* )? # optional route +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +) # initial word +(?: (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +) )* # further okay, if led by a period +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* @ (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # initial subdomain +(?: # +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. # if led by a period... +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # ...further okay +)* +# address spec +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* > # trailing > +# name and address +) (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* # optional trailing comment +/x + Alan Other + + user\@dom.ain + \"A. Other\" (a comment) + A. Other (a comment) + \"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"\@x400-re.lay + A missing angle @,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +# Atom +| # or +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +# Quoted string +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +# Atom +| # or +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +# Quoted string +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# additional words +)* +@ +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +)* +# address +| # or +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +# Atom +| # or +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +# Quoted string +) +# leading word +[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037] * # "normal" atoms and or spaces +(?: +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +| +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +) # "special" comment or quoted string +[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037] * # more "normal" +)* +< +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# < +(?: +@ +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +)* +(?: , +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +@ +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +)* +)* # additional domains +: +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +)? # optional route +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +# Atom +| # or +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +# Quoted string +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +# Atom +| # or +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +# Quoted string +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# additional words +)* +@ +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +)* +# address spec +> # > +# name and address +) +/x + Alan Other + + user\@dom.ain + \"A. Other\" (a comment) + A. Other (a comment) + \"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"\@x400-re.lay + A missing angle / + abcghinop + +/<.*?>/ + abcghinop + +/<.*>/U + abcghinop + +/<.*>(?U)/ + abcghinop + +/<.*?>/U + abcghinop + +/={3,}/U + abc========def + +/(?U)={3,}?/ + abc========def + +/(?^abc)/m + abc + def\nabc + *** Failers + defabc + +/(?<=ab(c+)d)ef/ + +/(?<=ab(?<=c+)d)ef/ + +/(?<=ab(c|de)f)g/ + +/The next three are in testinput2 because they have variable length branches/ + +/(?<=bullock|donkey)-cart/ + the bullock-cart + a donkey-cart race + *** Failers + cart + horse-and-cart + +/(?<=ab(?i)x|y|z)/ + +/(?>.*)(?<=(abcd)|(xyz))/ + alphabetabcd + endingxyz + +/(?<=ab(?i)x(?-i)y|(?i)z|b)ZZ/ + abxyZZ + abXyZZ + ZZZ + zZZ + bZZ + BZZ + *** Failers + ZZ + abXYZZ + zzz + bzz + +/(?[^()]+) # Either a sequence of non-brackets (no backtracking) + | # Or + (?R) # Recurse - i.e. nested bracketed string + )* # Zero or more contents + \) # Closing ) + /x + (abcd) + (abcd)xyz + xyz(abcd) + (ab(xy)cd)pqr + (ab(xycd)pqr + () abc () + 12(abcde(fsh)xyz(foo(bar))lmno)89 + *** Failers + abcd + abcd) + (abcd + +/\( ( (?>[^()]+) | (?R) )* \) /xg + (ab(xy)cd)pqr + 1(abcd)(x(y)z)pqr + +/\( (?: (?>[^()]+) | (?R) ) \) /x + (abcd) + (ab(xy)cd) + (a(b(c)d)e) + ((ab)) + *** Failers + () + +/\( (?: (?>[^()]+) | (?R) )? \) /x + () + 12(abcde(fsh)xyz(foo(bar))lmno)89 + +/\( ( (?>[^()]+) | (?R) )* \) /x + (ab(xy)cd) + +/\( ( ( (?>[^()]+) | (?R) )* ) \) /x + (ab(xy)cd) + +/\( (123)? ( ( (?>[^()]+) | (?R) )* ) \) /x + (ab(xy)cd) + (123ab(xy)cd) + +/\( ( (123)? ( (?>[^()]+) | (?R) )* ) \) /x + (ab(xy)cd) + (123ab(xy)cd) + +/\( (((((((((( ( (?>[^()]+) | (?R) )* )))))))))) \) /x + (ab(xy)cd) + +/\( ( ( (?>[^()<>]+) | ((?>[^()]+)) | (?R) )* ) \) /x + (abcd(xyz

    qrs)123) + +/\( ( ( (?>[^()]+) | ((?R)) )* ) \) /x + (ab(cd)ef) + (ab(cd(ef)gh)ij) + +/^[[:alnum:]]/D + +/^[[:alpha:]]/D + +/^[[:ascii:]]/D + +/^[[:cntrl:]]/D + +/^[[:digit:]]/D + +/^[[:graph:]]/D + +/^[[:lower:]]/D + +/^[[:print:]]/D + +/^[[:punct:]]/D + +/^[[:space:]]/D + +/^[[:upper:]]/D + +/^[[:xdigit:]]/D + +/^[[:word:]]/D + +/^[[:^cntrl:]]/D + +/^[12[:^digit:]]/D + +/[01[:alpha:]%]/D + +/[[.ch.]]/ + +/[[=ch=]]/ + +/[[:rhubarb:]]/ + +/[[:upper:]]/i + A + a + +/[[:lower:]]/i + A + a + +/((?-i)[[:lower:]])[[:lower:]]/i + ab + aB + *** Failers + Ab + AB + +/ End of test input / diff --git a/srclib/pcre/testdata/testinput3 b/srclib/pcre/testdata/testinput3 new file mode 100644 index 00000000000..0c884d3b7c1 --- /dev/null +++ b/srclib/pcre/testdata/testinput3 @@ -0,0 +1,1692 @@ +/(?.*/)foo" + /this/is/a/very/long/line/in/deed/with/very/many/slashes/in/it/you/see/ + +"(?>.*/)foo" + /this/is/a/very/long/line/in/deed/with/very/many/slashes/in/and/foo + +/(?>(\.\d\d[1-9]?))\d+/ + 1.230003938 + 1.875000282 + *** Failers + 1.235 + +/^((?>\w+)|(?>\s+))*$/ + now is the time for all good men to come to the aid of the party + *** Failers + this is not a line with only words and spaces! + +/(\d+)(\w)/ + 12345a + 12345+ + +/((?>\d+))(\w)/ + 12345a + *** Failers + 12345+ + +/(?>a+)b/ + aaab + +/((?>a+)b)/ + aaab + +/(?>(a+))b/ + aaab + +/(?>b)+/ + aaabbbccc + +/(?>a+|b+|c+)*c/ + aaabbbbccccd + +/((?>[^()]+)|\([^()]*\))+/ + ((abc(ade)ufh()()x + +/\(((?>[^()]+)|\([^()]+\))+\)/ + (abc) + (abc(def)xyz) + *** Failers + ((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +/a(?-i)b/i + ab + *** Failers + Ab + aB + AB + +/(a (?x)b c)d e/ + a bcd e + *** Failers + a b cd e + abcd e + a bcde + +/(a b(?x)c d (?-x)e f)/ + a bcde f + *** Failers + abcdef + +/(a(?i)b)c/ + abc + aBc + *** Failers + abC + aBC + Abc + ABc + ABC + AbC + +/a(?i:b)c/ + abc + aBc + *** Failers + ABC + abC + aBC + +/a(?i:b)*c/ + aBc + aBBc + *** Failers + aBC + aBBC + +/a(?=b(?i)c)\w\wd/ + abcd + abCd + *** Failers + aBCd + abcD + +/(?s-i:more.*than).*million/i + more than million + more than MILLION + more \n than Million + *** Failers + MORE THAN MILLION + more \n than \n million + +/(?:(?s-i)more.*than).*million/i + more than million + more than MILLION + more \n than Million + *** Failers + MORE THAN MILLION + more \n than \n million + +/(?>a(?i)b+)+c/ + abc + aBbc + aBBc + *** Failers + Abc + abAb + abbC + +/(?=a(?i)b)\w\wc/ + abc + aBc + *** Failers + Ab + abC + aBC + +/(?<=a(?i)b)(\w\w)c/ + abxxc + aBxxc + *** Failers + Abxxc + ABxxc + abxxC + +/(?:(a)|b)(?(1)A|B)/ + aA + bB + *** Failers + aB + bA + +/^(a)?(?(1)a|b)+$/ + aa + b + bb + *** Failers + ab + +/^(?(?=abc)\w{3}:|\d\d)$/ + abc: + 12 + *** Failers + 123 + xyz + +/^(?(?!abc)\d\d|\w{3}:)$/ + abc: + 12 + *** Failers + 123 + xyz + +/(?(?<=foo)bar|cat)/ + foobar + cat + fcat + focat + *** Failers + foocat + +/(?(?a*)*/ + a + aa + aaaa + +/(abc|)+/ + abc + abcabc + abcabcabc + xyz + +/([a]*)*/ + a + aaaaa + +/([ab]*)*/ + a + b + ababab + aaaabcde + bbbb + +/([^a]*)*/ + b + bbbb + aaa + +/([^ab]*)*/ + cccc + abab + +/([a]*?)*/ + a + aaaa + +/([ab]*?)*/ + a + b + abab + baba + +/([^a]*?)*/ + b + bbbb + aaa + +/([^ab]*?)*/ + c + cccc + baba + +/(?>a*)*/ + a + aaabcde + +/((?>a*))*/ + aaaaa + aabbaa + +/((?>a*?))*/ + aaaaa + aabbaa + +/(?(?=[^a-z]+[a-z]) \d{2}-[a-z]{3}-\d{2} | \d{2}-\d{2}-\d{2} ) /x + 12-sep-98 + 12-09-98 + *** Failers + sep-12-98 + +/(?<=(foo))bar\1/ + foobarfoo + foobarfootling + *** Failers + foobar + barfoo + +/(?i:saturday|sunday)/ + saturday + sunday + Saturday + Sunday + SATURDAY + SUNDAY + SunDay + +/(a(?i)bc|BB)x/ + abcx + aBCx + bbx + BBx + *** Failers + abcX + aBCX + bbX + BBX + +/^([ab](?i)[cd]|[ef])/ + ac + aC + bD + elephant + Europe + frog + France + *** Failers + Africa + +/^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)/ + ab + aBd + xy + xY + zebra + Zambesi + *** Failers + aCD + XY + +/(?<=foo\n)^bar/m + foo\nbar + *** Failers + bar + baz\nbar + +/(?<=(?]&/ + <&OUT + +/^(a\1?){4}$/ + aaaaaaaaaa + *** Failers + AB + aaaaaaaaa + aaaaaaaaaaa + +/^(a(?(1)\1)){4}$/ + aaaaaaaaaa + *** Failers + aaaaaaaaa + aaaaaaaaaaa + +/(?:(f)(o)(o)|(b)(a)(r))*/ + foobar + +/(?<=a)b/ + ab + *** Failers + cb + b + +/(?a+)ab/ + +/(?>a+)b/ + aaab + +/([[:]+)/ + a:[b]: + +/([[=]+)/ + a=[b]= + +/([[.]+)/ + a.[b]. + +/((?>a+)b)/ + aaab + +/(?>(a+))b/ + aaab + +/((?>[^()]+)|\([^()]*\))+/ + ((abc(ade)ufh()()x + +/a\Z/ + *** Failers + aaab + a\nb\n + +/b\Z/ + a\nb\n + +/b\z/ + +/b\Z/ + a\nb + +/b\z/ + a\nb + *** Failers + +/^(?>(?(1)\.|())[^\W_](?>[a-z0-9-]*[^\W_])?)+$/ + a + abc + a-b + 0-9 + a.b + 5.6.7 + the.quick.brown.fox + a100.b200.300c + 12-ab.1245 + ***Failers + \ + .a + -a + a- + a. + a_b + a.- + a.. + ab..bc + the.quick.brown.fox- + the.quick.brown.fox. + the.quick.brown.fox_ + the.quick.brown.fox+ + +/(?>.*)(?<=(abcd|wxyz))/ + alphabetabcd + endingwxyz + *** Failers + a rather long string that doesn't end with one of them + +/word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword/ + word cat dog elephant mussel cow horse canary baboon snake shark otherword + word cat dog elephant mussel cow horse canary baboon snake shark + +/word (?>[a-zA-Z0-9]+ ){0,30}otherword/ + word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope + +/(?<=\d{3}(?!999))foo/ + 999foo + 123999foo + *** Failers + 123abcfoo + +/(?<=(?!...999)\d{3})foo/ + 999foo + 123999foo + *** Failers + 123abcfoo + +/(?<=\d{3}(?!999)...)foo/ + 123abcfoo + 123456foo + *** Failers + 123999foo + +/(?<=\d{3}...)(?\s*)=(?>\s*) # find >>aaabxyzpqrrrabbxyyyypqAzz + 0: aaabxyzpqrrrabbxyyyypqAzz + >aaaabxyzpqrrrabbxyyyypqAzz + 0: aaaabxyzpqrrrabbxyyyypqAzz + >>>>abcxyzpqrrrabbxyyyypqAzz + 0: abcxyzpqrrrabbxyyyypqAzz + *** Failers +No match + abxyzpqrrabbxyyyypqAzz +No match + abxyzpqrrrrabbxyyyypqAzz +No match + abxyzpqrrrabxyyyypqAzz +No match + aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz +No match + aaaabcxyzzzzpqrrrabbbxyyypqAzz +No match + aaabcxyzpqrrrabbxyyyypqqqqqqqAzz +No match + +/^(abc){1,2}zz/ + abczz + 0: abczz + 1: abc + abcabczz + 0: abcabczz + 1: abc + *** Failers +No match + zz +No match + abcabcabczz +No match + >>abczz +No match + +/^(b+?|a){1,2}?c/ + bc + 0: bc + 1: b + bbc + 0: bbc + 1: b + bbbc + 0: bbbc + 1: bb + bac + 0: bac + 1: a + bbac + 0: bbac + 1: a + aac + 0: aac + 1: a + abbbbbbbbbbbc + 0: abbbbbbbbbbbc + 1: bbbbbbbbbbb + bbbbbbbbbbbac + 0: bbbbbbbbbbbac + 1: a + *** Failers +No match + aaac +No match + abbbbbbbbbbbac +No match + +/^(b+|a){1,2}c/ + bc + 0: bc + 1: b + bbc + 0: bbc + 1: bb + bbbc + 0: bbbc + 1: bbb + bac + 0: bac + 1: a + bbac + 0: bbac + 1: a + aac + 0: aac + 1: a + abbbbbbbbbbbc + 0: abbbbbbbbbbbc + 1: bbbbbbbbbbb + bbbbbbbbbbbac + 0: bbbbbbbbbbbac + 1: a + *** Failers +No match + aaac +No match + abbbbbbbbbbbac +No match + +/^(b+|a){1,2}?bc/ + bbc + 0: bbc + 1: b + +/^(b*|ba){1,2}?bc/ + babc + 0: babc + 1: ba + bbabc + 0: bbabc + 1: ba + bababc + 0: bababc + 1: ba + *** Failers +No match + bababbc +No match + babababc +No match + +/^(ba|b*){1,2}?bc/ + babc + 0: babc + 1: ba + bbabc + 0: bbabc + 1: ba + bababc + 0: bababc + 1: ba + *** Failers +No match + bababbc +No match + babababc +No match + +/^\ca\cA\c[\c{\c:/ + \x01\x01\e;z + 0: \x01\x01\x1b;z + +/^[ab\]cde]/ + athing + 0: a + bthing + 0: b + ]thing + 0: ] + cthing + 0: c + dthing + 0: d + ething + 0: e + *** Failers +No match + fthing +No match + [thing +No match + \\thing +No match + +/^[]cde]/ + ]thing + 0: ] + cthing + 0: c + dthing + 0: d + ething + 0: e + *** Failers +No match + athing +No match + fthing +No match + +/^[^ab\]cde]/ + fthing + 0: f + [thing + 0: [ + \\thing + 0: \ + *** Failers + 0: * + athing +No match + bthing +No match + ]thing +No match + cthing +No match + dthing +No match + ething +No match + +/^[^]cde]/ + athing + 0: a + fthing + 0: f + *** Failers + 0: * + ]thing +No match + cthing +No match + dthing +No match + ething +No match + +/^\/ +  + 0: \x81 + +/^ÿ/ + ÿ + 0: \xff + +/^[0-9]+$/ + 0 + 0: 0 + 1 + 0: 1 + 2 + 0: 2 + 3 + 0: 3 + 4 + 0: 4 + 5 + 0: 5 + 6 + 0: 6 + 7 + 0: 7 + 8 + 0: 8 + 9 + 0: 9 + 10 + 0: 10 + 100 + 0: 100 + *** Failers +No match + abc +No match + +/^.*nter/ + enter + 0: enter + inter + 0: inter + uponter + 0: uponter + +/^xxx[0-9]+$/ + xxx0 + 0: xxx0 + xxx1234 + 0: xxx1234 + *** Failers +No match + xxx +No match + +/^.+[0-9][0-9][0-9]$/ + x123 + 0: x123 + xx123 + 0: xx123 + 123456 + 0: 123456 + *** Failers +No match + 123 +No match + x1234 + 0: x1234 + +/^.+?[0-9][0-9][0-9]$/ + x123 + 0: x123 + xx123 + 0: xx123 + 123456 + 0: 123456 + *** Failers +No match + 123 +No match + x1234 + 0: x1234 + +/^([^!]+)!(.+)=apquxz\.ixr\.zzz\.ac\.uk$/ + abc!pqr=apquxz.ixr.zzz.ac.uk + 0: abc!pqr=apquxz.ixr.zzz.ac.uk + 1: abc + 2: pqr + *** Failers +No match + !pqr=apquxz.ixr.zzz.ac.uk +No match + abc!=apquxz.ixr.zzz.ac.uk +No match + abc!pqr=apquxz:ixr.zzz.ac.uk +No match + abc!pqr=apquxz.ixr.zzz.ac.ukk +No match + +/:/ + Well, we need a colon: somewhere + 0: : + *** Fail if we don't +No match + +/([\da-f:]+)$/i + 0abc + 0: 0abc + 1: 0abc + abc + 0: abc + 1: abc + fed + 0: fed + 1: fed + E + 0: E + 1: E + :: + 0: :: + 1: :: + 5f03:12C0::932e + 0: 5f03:12C0::932e + 1: 5f03:12C0::932e + fed def + 0: def + 1: def + Any old stuff + 0: ff + 1: ff + *** Failers +No match + 0zzz +No match + gzzz +No match + fed\x20 +No match + Any old rubbish +No match + +/^.*\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ + .1.2.3 + 0: .1.2.3 + 1: 1 + 2: 2 + 3: 3 + A.12.123.0 + 0: A.12.123.0 + 1: 12 + 2: 123 + 3: 0 + *** Failers +No match + .1.2.3333 +No match + 1.2.3 +No match + 1234.2.3 +No match + +/^(\d+)\s+IN\s+SOA\s+(\S+)\s+(\S+)\s*\(\s*$/ + 1 IN SOA non-sp1 non-sp2( + 0: 1 IN SOA non-sp1 non-sp2( + 1: 1 + 2: non-sp1 + 3: non-sp2 + 1 IN SOA non-sp1 non-sp2 ( + 0: 1 IN SOA non-sp1 non-sp2 ( + 1: 1 + 2: non-sp1 + 3: non-sp2 + *** Failers +No match + 1IN SOA non-sp1 non-sp2( +No match + +/^[a-zA-Z\d][a-zA-Z\d\-]*(\.[a-zA-Z\d][a-zA-z\d\-]*)*\.$/ + a. + 0: a. + Z. + 0: Z. + 2. + 0: 2. + ab-c.pq-r. + 0: ab-c.pq-r. + 1: .pq-r + sxk.zzz.ac.uk. + 0: sxk.zzz.ac.uk. + 1: .uk + x-.y-. + 0: x-.y-. + 1: .y- + *** Failers +No match + -abc.peq. +No match + +/^\*\.[a-z]([a-z\-\d]*[a-z\d]+)?(\.[a-z]([a-z\-\d]*[a-z\d]+)?)*$/ + *.a + 0: *.a + *.b0-a + 0: *.b0-a + 1: 0-a + *.c3-b.c + 0: *.c3-b.c + 1: 3-b + 2: .c + *.c-a.b-c + 0: *.c-a.b-c + 1: -a + 2: .b-c + 3: -c + *** Failers +No match + *.0 +No match + *.a- +No match + *.a-b.c- +No match + *.c-a.0-c +No match + +/^(?=ab(de))(abd)(e)/ + abde + 0: abde + 1: de + 2: abd + 3: e + +/^(?!(ab)de|x)(abd)(f)/ + abdf + 0: abdf + 1: + 2: abd + 3: f + +/^(?=(ab(cd)))(ab)/ + abcd + 0: ab + 1: abcd + 2: cd + 3: ab + +/^[\da-f](\.[\da-f])*$/i + a.b.c.d + 0: a.b.c.d + 1: .d + A.B.C.D + 0: A.B.C.D + 1: .D + a.b.c.1.2.3.C + 0: a.b.c.1.2.3.C + 1: .C + +/^\".*\"\s*(;.*)?$/ + \"1234\" + 0: "1234" + \"abcd\" ; + 0: "abcd" ; + 1: ; + \"\" ; rhubarb + 0: "" ; rhubarb + 1: ; rhubarb + *** Failers +No match + \"1234\" : things +No match + +/^$/ + \ + 0: + *** Failers +No match + +/ ^ a (?# begins with a) b\sc (?# then b c) $ (?# then end)/x + ab c + 0: ab c + *** Failers +No match + abc +No match + ab cde +No match + +/(?x) ^ a (?# begins with a) b\sc (?# then b c) $ (?# then end)/ + ab c + 0: ab c + *** Failers +No match + abc +No match + ab cde +No match + +/^ a\ b[c ]d $/x + a bcd + 0: a bcd + a b d + 0: a b d + *** Failers +No match + abcd +No match + ab d +No match + +/^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$/ + abcdefhijklm + 0: abcdefhijklm + 1: abc + 2: bc + 3: c + 4: def + 5: ef + 6: f + 7: hij + 8: ij + 9: j +10: klm +11: lm +12: m + +/^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$/ + abcdefhijklm + 0: abcdefhijklm + 1: bc + 2: c + 3: ef + 4: f + 5: ij + 6: j + 7: lm + 8: m + +/^[\w][\W][\s][\S][\d][\D][\b][\n][\c]][\022]/ + a+ Z0+\x08\n\x1d\x12 + 0: a+ Z0+\x08\x0a\x1d\x12 + +/^[.^$|()*+?{,}]+/ + .^\$(*+)|{?,?} + 0: .^$(*+)|{?,?} + +/^a*\w/ + z + 0: z + az + 0: az + aaaz + 0: aaaz + a + 0: a + aa + 0: aa + aaaa + 0: aaaa + a+ + 0: a + aa+ + 0: aa + +/^a*?\w/ + z + 0: z + az + 0: a + aaaz + 0: a + a + 0: a + aa + 0: a + aaaa + 0: a + a+ + 0: a + aa+ + 0: a + +/^a+\w/ + az + 0: az + aaaz + 0: aaaz + aa + 0: aa + aaaa + 0: aaaa + aa+ + 0: aa + +/^a+?\w/ + az + 0: az + aaaz + 0: aa + aa + 0: aa + aaaa + 0: aa + aa+ + 0: aa + +/^\d{8}\w{2,}/ + 1234567890 + 0: 1234567890 + 12345678ab + 0: 12345678ab + 12345678__ + 0: 12345678__ + *** Failers +No match + 1234567 +No match + +/^[aeiou\d]{4,5}$/ + uoie + 0: uoie + 1234 + 0: 1234 + 12345 + 0: 12345 + aaaaa + 0: aaaaa + *** Failers +No match + 123456 +No match + +/^[aeiou\d]{4,5}?/ + uoie + 0: uoie + 1234 + 0: 1234 + 12345 + 0: 1234 + aaaaa + 0: aaaa + 123456 + 0: 1234 + +/\A(abc|def)=(\1){2,3}\Z/ + abc=abcabc + 0: abc=abcabc + 1: abc + 2: abc + def=defdefdef + 0: def=defdefdef + 1: def + 2: def + *** Failers +No match + abc=defdef +No match + +/^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\11*(\3\4)\1(?#)2$/ + abcdefghijkcda2 + 0: abcdefghijkcda2 + 1: a + 2: b + 3: c + 4: d + 5: e + 6: f + 7: g + 8: h + 9: i +10: j +11: k +12: cd + abcdefghijkkkkcda2 + 0: abcdefghijkkkkcda2 + 1: a + 2: b + 3: c + 4: d + 5: e + 6: f + 7: g + 8: h + 9: i +10: j +11: k +12: cd + +/(cat(a(ract|tonic)|erpillar)) \1()2(3)/ + cataract cataract23 + 0: cataract cataract23 + 1: cataract + 2: aract + 3: ract + 4: + 5: 3 + catatonic catatonic23 + 0: catatonic catatonic23 + 1: catatonic + 2: atonic + 3: tonic + 4: + 5: 3 + caterpillar caterpillar23 + 0: caterpillar caterpillar23 + 1: caterpillar + 2: erpillar + 3: + 4: + 5: 3 + + +/^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]/ + From abcd Mon Sep 01 12:33:02 1997 + 0: From abcd Mon Sep 01 12:33 + 1: abcd + +/^From\s+\S+\s+([a-zA-Z]{3}\s+){2}\d{1,2}\s+\d\d:\d\d/ + From abcd Mon Sep 01 12:33:02 1997 + 0: From abcd Mon Sep 01 12:33 + 1: Sep + From abcd Mon Sep 1 12:33:02 1997 + 0: From abcd Mon Sep 1 12:33 + 1: Sep + *** Failers +No match + From abcd Sep 01 12:33:02 1997 +No match + +/^12.34/s + 12\n34 + 0: 12\x0a34 + 12\r34 + 0: 12\x0d34 + +/\w+(?=\t)/ + the quick brown\t fox + 0: brown + +/foo(?!bar)(.*)/ + foobar is foolish see? + 0: foolish see? + 1: lish see? + +/(?:(?!foo)...|^.{0,2})bar(.*)/ + foobar crowbar etc + 0: rowbar etc + 1: etc + barrel + 0: barrel + 1: rel + 2barrel + 0: 2barrel + 1: rel + A barrel + 0: A barrel + 1: rel + +/^(\D*)(?=\d)(?!123)/ + abc456 + 0: abc + 1: abc + *** Failers +No match + abc123 +No match + +/^1234(?# test newlines + inside)/ + 1234 + 0: 1234 + +/^1234 #comment in extended re + /x + 1234 + 0: 1234 + +/#rhubarb + abcd/x + abcd + 0: abcd + +/^abcd#rhubarb/x + abcd + 0: abcd + +/^(a)\1{2,3}(.)/ + aaab + 0: aaab + 1: a + 2: b + aaaab + 0: aaaab + 1: a + 2: b + aaaaab + 0: aaaaa + 1: a + 2: a + aaaaaab + 0: aaaaa + 1: a + 2: a + +/(?!^)abc/ + the abc + 0: abc + *** Failers +No match + abc +No match + +/(?=^)abc/ + abc + 0: abc + *** Failers +No match + the abc +No match + +/^[ab]{1,3}(ab*|b)/ + aabbbbb + 0: aabb + 1: b + +/^[ab]{1,3}?(ab*|b)/ + aabbbbb + 0: aabbbbb + 1: abbbbb + +/^[ab]{1,3}?(ab*?|b)/ + aabbbbb + 0: aa + 1: a + +/^[ab]{1,3}(ab*?|b)/ + aabbbbb + 0: aabb + 1: b + +/ (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* # optional leading comment +(?: (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +) # initial word +(?: (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +) )* # further okay, if led by a period +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* @ (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # initial subdomain +(?: # +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. # if led by a period... +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # ...further okay +)* +# address +| # or +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +) # one word, optionally followed by.... +(?: +[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037] | # atom and space parts, or... +\( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) | # comments, or... + +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +# quoted strings +)* +< (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* # leading < +(?: @ (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # initial subdomain +(?: # +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. # if led by a period... +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # ...further okay +)* + +(?: (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* , (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* @ (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # initial subdomain +(?: # +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. # if led by a period... +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # ...further okay +)* +)* # further okay, if led by comma +: # closing colon +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* )? # optional route +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +) # initial word +(?: (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +" (?: # opening quote... +[^\\\x80-\xff\n\015"] # Anything except backslash and quote +| # or +\\ [^\x80-\xff] # Escaped something (something != CR) +)* " # closing quote +) )* # further okay, if led by a period +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* @ (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # initial subdomain +(?: # +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* \. # if led by a period... +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* (?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| \[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) # ...further okay +)* +# address spec +(?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* > # trailing > +# name and address +) (?: [\040\t] | \( +(?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] | \( (?: [^\\\x80-\xff\n\015()] | \\ [^\x80-\xff] )* \) )* +\) )* # optional trailing comment +/x + Alan Other + 0: Alan Other + + 0: user@dom.ain + user\@dom.ain + 0: user@dom.ain + \"A. Other\" (a comment) + 0: "A. Other" (a comment) + A. Other (a comment) + 0: Other (a comment) + \"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"\@x400-re.lay + 0: "/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/"@x400-re.lay + A missing angle @,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +# Atom +| # or +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +# Quoted string +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +# Atom +| # or +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +# Quoted string +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# additional words +)* +@ +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +)* +# address +| # or +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +# Atom +| # or +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +# Quoted string +) +# leading word +[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037] * # "normal" atoms and or spaces +(?: +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +| +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +) # "special" comment or quoted string +[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037] * # more "normal" +)* +< +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# < +(?: +@ +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +)* +(?: , +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +@ +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +)* +)* # additional domains +: +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +)? # optional route +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +# Atom +| # or +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +# Quoted string +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +# Atom +| # or +" # " +[^\\\x80-\xff\n\015"] * # normal +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015"] * )* # ( special normal* )* +" # " +# Quoted string +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# additional words +)* +@ +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +(?: +\. +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +(?: +[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+ # some number of atom characters... +(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]) # ..not followed by something that could be part of an atom +| +\[ # [ +(?: [^\\\x80-\xff\n\015\[\]] | \\ [^\x80-\xff] )* # stuff +\] # ] +) +[\040\t]* # Nab whitespace. +(?: +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: # ( +(?: \\ [^\x80-\xff] | +\( # ( +[^\\\x80-\xff\n\015()] * # normal* +(?: \\ [^\x80-\xff] [^\\\x80-\xff\n\015()] * )* # (special normal*)* +\) # ) +) # special +[^\\\x80-\xff\n\015()] * # normal* +)* # )* +\) # ) +[\040\t]* )* # If comment found, allow more spaces. +# optional trailing comments +)* +# address spec +> # > +# name and address +) +/x + Alan Other + 0: Alan Other + + 0: user@dom.ain + user\@dom.ain + 0: user@dom.ain + \"A. Other\" (a comment) + 0: "A. Other" + A. Other (a comment) + 0: Other + \"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"\@x400-re.lay + 0: "/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/"@x400-re.lay + A missing angle ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff + +/P[^*]TAIRE[^*]{1,6}?LL/ + xxxxxxxxxxxPSTAIREISLLxxxxxxxxx + 0: PSTAIREISLL + +/P[^*]TAIRE[^*]{1,}?LL/ + xxxxxxxxxxxPSTAIREISLLxxxxxxxxx + 0: PSTAIREISLL + +/(\.\d\d[1-9]?)\d+/ + 1.230003938 + 0: .230003938 + 1: .23 + 1.875000282 + 0: .875000282 + 1: .875 + 1.235 + 0: .235 + 1: .23 + +/(\.\d\d((?=0)|\d(?=\d)))/ + 1.230003938 + 0: .23 + 1: .23 + 2: + 1.875000282 + 0: .875 + 1: .875 + 2: 5 + *** Failers +No match + 1.235 +No match + +/a(?)b/ + ab + 0: ab + +/\b(foo)\s+(\w+)/i + Food is on the foo table + 0: foo table + 1: foo + 2: table + +/foo(.*)bar/ + The food is under the bar in the barn. + 0: food is under the bar in the bar + 1: d is under the bar in the + +/foo(.*?)bar/ + The food is under the bar in the barn. + 0: food is under the bar + 1: d is under the + +/(.*)(\d*)/ + I have 2 numbers: 53147 + 0: I have 2 numbers: 53147 + 1: I have 2 numbers: 53147 + 2: + +/(.*)(\d+)/ + I have 2 numbers: 53147 + 0: I have 2 numbers: 53147 + 1: I have 2 numbers: 5314 + 2: 7 + +/(.*?)(\d*)/ + I have 2 numbers: 53147 + 0: + 1: + 2: + +/(.*?)(\d+)/ + I have 2 numbers: 53147 + 0: I have 2 + 1: I have + 2: 2 + +/(.*)(\d+)$/ + I have 2 numbers: 53147 + 0: I have 2 numbers: 53147 + 1: I have 2 numbers: 5314 + 2: 7 + +/(.*?)(\d+)$/ + I have 2 numbers: 53147 + 0: I have 2 numbers: 53147 + 1: I have 2 numbers: + 2: 53147 + +/(.*)\b(\d+)$/ + I have 2 numbers: 53147 + 0: I have 2 numbers: 53147 + 1: I have 2 numbers: + 2: 53147 + +/(.*\D)(\d+)$/ + I have 2 numbers: 53147 + 0: I have 2 numbers: 53147 + 1: I have 2 numbers: + 2: 53147 + +/^\D*(?!123)/ + ABC123 + 0: AB + +/^(\D*)(?=\d)(?!123)/ + ABC445 + 0: ABC + 1: ABC + *** Failers +No match + ABC123 +No match + +/^[W-]46]/ + W46]789 + 0: W46] + -46]789 + 0: -46] + *** Failers +No match + Wall +No match + Zebra +No match + 42 +No match + [abcd] +No match + ]abcd[ +No match + +/^[W-\]46]/ + W46]789 + 0: W + Wall + 0: W + Zebra + 0: Z + Xylophone + 0: X + 42 + 0: 4 + [abcd] + 0: [ + ]abcd[ + 0: ] + \\backslash + 0: \ + *** Failers +No match + -46]789 +No match + well +No match + +/\d\d\/\d\d\/\d\d\d\d/ + 01/01/2000 + 0: 01/01/2000 + +/word (?:[a-zA-Z0-9]+ ){0,10}otherword/ + word cat dog elephant mussel cow horse canary baboon snake shark otherword + 0: word cat dog elephant mussel cow horse canary baboon snake shark otherword + word cat dog elephant mussel cow horse canary baboon snake shark +No match + +/word (?:[a-zA-Z0-9]+ ){0,300}otherword/ + word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope +No match + +/^(a){0,0}/ + bcd + 0: + abc + 0: + aab + 0: + +/^(a){0,1}/ + bcd + 0: + abc + 0: a + 1: a + aab + 0: a + 1: a + +/^(a){0,2}/ + bcd + 0: + abc + 0: a + 1: a + aab + 0: aa + 1: a + +/^(a){0,3}/ + bcd + 0: + abc + 0: a + 1: a + aab + 0: aa + 1: a + aaa + 0: aaa + 1: a + +/^(a){0,}/ + bcd + 0: + abc + 0: a + 1: a + aab + 0: aa + 1: a + aaa + 0: aaa + 1: a + aaaaaaaa + 0: aaaaaaaa + 1: a + +/^(a){1,1}/ + bcd +No match + abc + 0: a + 1: a + aab + 0: a + 1: a + +/^(a){1,2}/ + bcd +No match + abc + 0: a + 1: a + aab + 0: aa + 1: a + +/^(a){1,3}/ + bcd +No match + abc + 0: a + 1: a + aab + 0: aa + 1: a + aaa + 0: aaa + 1: a + +/^(a){1,}/ + bcd +No match + abc + 0: a + 1: a + aab + 0: aa + 1: a + aaa + 0: aaa + 1: a + aaaaaaaa + 0: aaaaaaaa + 1: a + +/.*\.gif/ + borfle\nbib.gif\nno + 0: bib.gif + +/.{0,}\.gif/ + borfle\nbib.gif\nno + 0: bib.gif + +/.*\.gif/m + borfle\nbib.gif\nno + 0: bib.gif + +/.*\.gif/s + borfle\nbib.gif\nno + 0: borfle\x0abib.gif + +/.*\.gif/ms + borfle\nbib.gif\nno + 0: borfle\x0abib.gif + +/.*$/ + borfle\nbib.gif\nno + 0: no + +/.*$/m + borfle\nbib.gif\nno + 0: borfle + +/.*$/s + borfle\nbib.gif\nno + 0: borfle\x0abib.gif\x0ano + +/.*$/ms + borfle\nbib.gif\nno + 0: borfle\x0abib.gif\x0ano + +/.*$/ + borfle\nbib.gif\nno\n + 0: no + +/.*$/m + borfle\nbib.gif\nno\n + 0: borfle + +/.*$/s + borfle\nbib.gif\nno\n + 0: borfle\x0abib.gif\x0ano\x0a + +/.*$/ms + borfle\nbib.gif\nno\n + 0: borfle\x0abib.gif\x0ano\x0a + +/(.*X|^B)/ + abcde\n1234Xyz + 0: 1234X + 1: 1234X + BarFoo + 0: B + 1: B + *** Failers +No match + abcde\nBar +No match + +/(.*X|^B)/m + abcde\n1234Xyz + 0: 1234X + 1: 1234X + BarFoo + 0: B + 1: B + abcde\nBar + 0: B + 1: B + +/(.*X|^B)/s + abcde\n1234Xyz + 0: abcde\x0a1234X + 1: abcde\x0a1234X + BarFoo + 0: B + 1: B + *** Failers +No match + abcde\nBar +No match + +/(.*X|^B)/ms + abcde\n1234Xyz + 0: abcde\x0a1234X + 1: abcde\x0a1234X + BarFoo + 0: B + 1: B + abcde\nBar + 0: B + 1: B + +/(?s)(.*X|^B)/ + abcde\n1234Xyz + 0: abcde\x0a1234X + 1: abcde\x0a1234X + BarFoo + 0: B + 1: B + *** Failers +No match + abcde\nBar +No match + +/(?s:.*X|^B)/ + abcde\n1234Xyz + 0: abcde\x0a1234X + BarFoo + 0: B + *** Failers +No match + abcde\nBar +No match + +/^.*B/ + **** Failers +No match + abc\nB +No match + +/(?s)^.*B/ + abc\nB + 0: abc\x0aB + +/(?m)^.*B/ + abc\nB + 0: B + +/(?ms)^.*B/ + abc\nB + 0: abc\x0aB + +/(?ms)^B/ + abc\nB + 0: B + +/(?s)B$/ + B\n + 0: B + +/^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/ + 123456654321 + 0: 123456654321 + +/^\d\d\d\d\d\d\d\d\d\d\d\d/ + 123456654321 + 0: 123456654321 + +/^[\d][\d][\d][\d][\d][\d][\d][\d][\d][\d][\d][\d]/ + 123456654321 + 0: 123456654321 + +/^[abc]{12}/ + abcabcabcabc + 0: abcabcabcabc + +/^[a-c]{12}/ + abcabcabcabc + 0: abcabcabcabc + +/^(a|b|c){12}/ + abcabcabcabc + 0: abcabcabcabc + 1: c + +/^[abcdefghijklmnopqrstuvwxy0123456789]/ + n + 0: n + *** Failers +No match + z +No match + +/abcde{0,0}/ + abcd + 0: abcd + *** Failers +No match + abce +No match + +/ab[cd]{0,0}e/ + abe + 0: abe + *** Failers +No match + abcde +No match + +/ab(c){0,0}d/ + abd + 0: abd + *** Failers +No match + abcd +No match + +/a(b*)/ + a + 0: a + 1: + ab + 0: ab + 1: b + abbbb + 0: abbbb + 1: bbbb + *** Failers + 0: a + 1: + bbbbb +No match + +/ab\d{0}e/ + abe + 0: abe + *** Failers +No match + ab1e +No match + +/"([^\\"]+|\\.)*"/ + the \"quick\" brown fox + 0: "quick" + 1: quick + \"the \\\"quick\\\" brown fox\" + 0: "the \"quick\" brown fox" + 1: brown fox + +/.*?/g+ + abc + 0: + 0+ abc + 0: a + 0+ bc + 0: + 0+ bc + 0: b + 0+ c + 0: + 0+ c + 0: c + 0+ + 0: + 0+ + +/\b/g+ + abc + 0: + 0+ abc + 0: + 0+ + +/\b/+g + abc + 0: + 0+ abc + 0: + 0+ + +//g + abc + 0: + 0: + 0: + 0: + +/ End of test input / + diff --git a/srclib/pcre/testdata/testoutput2 b/srclib/pcre/testdata/testoutput2 new file mode 100644 index 00000000000..d0fc036f0f2 --- /dev/null +++ b/srclib/pcre/testdata/testoutput2 @@ -0,0 +1,2072 @@ +PCRE version 3.1 09-Feb-2000 + +/(a)b|/ +Capturing subpattern count = 1 +No options +No first char +No need char + +/abc/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'c' + abc + 0: abc + defabc + 0: abc + \Aabc + 0: abc + *** Failers +No match + \Adefabc +No match + ABC +No match + +/^abc/ +Capturing subpattern count = 0 +Options: anchored +No first char +Need char = 'c' + abc + 0: abc + \Aabc + 0: abc + *** Failers +No match + defabc +No match + \Adefabc +No match + +/a+bc/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'c' + +/a*bc/ +Capturing subpattern count = 0 +No options +No first char +Need char = 'c' + +/a{3}bc/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'c' + +/(abc|a+z)/ +Capturing subpattern count = 1 +No options +First char = 'a' +No need char + +/^abc$/ +Capturing subpattern count = 0 +Options: anchored +No first char +Need char = 'c' + abc + 0: abc + *** Failers +No match + def\nabc +No match + +/ab\gdef/X +Failed: unrecognized character follows \ at offset 3 + +/(?X)ab\gdef/X +Failed: unrecognized character follows \ at offset 7 + +/x{5,4}/ +Failed: numbers out of order in {} quantifier at offset 5 + +/z{65536}/ +Failed: number too big in {} quantifier at offset 7 + +/[abcd/ +Failed: missing terminating ] for character class at offset 5 + +/[\B]/ +Failed: invalid escape sequence in character class at offset 2 + +/[a-\w]/ +Failed: invalid escape sequence in character class at offset 4 + +/[z-a]/ +Failed: range out of order in character class at offset 3 + +/^*/ +Failed: nothing to repeat at offset 1 + +/(abc/ +Failed: missing ) at offset 4 + +/(?# abc/ +Failed: missing ) after comment at offset 7 + +/(?z)abc/ +Failed: unrecognized character after (? at offset 2 + +/.*b/ +Capturing subpattern count = 0 +No options +First char at start or follows \n +Need char = 'b' + +/.*?b/ +Capturing subpattern count = 0 +No options +First char at start or follows \n +Need char = 'b' + +/cat|dog|elephant/ +Capturing subpattern count = 0 +No options +No first char +No need char + this sentence eventually mentions a cat + 0: cat + this sentences rambles on and on for a while and then reaches elephant + 0: elephant + +/cat|dog|elephant/S +Capturing subpattern count = 0 +No options +No first char +No need char +Starting character set: c d e + this sentence eventually mentions a cat + 0: cat + this sentences rambles on and on for a while and then reaches elephant + 0: elephant + +/cat|dog|elephant/iS +Capturing subpattern count = 0 +Options: caseless +No first char +No need char +Starting character set: C D E c d e + this sentence eventually mentions a CAT cat + 0: CAT + this sentences rambles on and on for a while to elephant ElePhant + 0: elephant + +/a|[bcd]/S +Capturing subpattern count = 0 +No options +No first char +No need char +Starting character set: a b c d + +/(a|[^\dZ])/S +Capturing subpattern count = 1 +No options +No first char +No need char +Starting character set: \x00 \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0a + \x0b \x0c \x0d \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 + \x1a \x1b \x1c \x1d \x1e \x1f \x20 ! " # $ % & ' ( ) * + , - . / : ; < = > + ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y [ \ ] ^ _ ` a b c d + e f g h i j k l m n o p q r s t u v w x y z { | } ~ \x7f \x80 \x81 \x82 \x83 + \x84 \x85 \x86 \x87 \x88 \x89 \x8a \x8b \x8c \x8d \x8e \x8f \x90 \x91 \x92 + \x93 \x94 \x95 \x96 \x97 \x98 \x99 \x9a \x9b \x9c \x9d \x9e \x9f \xa0 \xa1 + \xa2 \xa3 \xa4 \xa5 \xa6 \xa7 \xa8 \xa9 \xaa \xab \xac \xad \xae \xaf \xb0 + \xb1 \xb2 \xb3 \xb4 \xb5 \xb6 \xb7 \xb8 \xb9 \xba \xbb \xbc \xbd \xbe \xbf + \xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 \xc8 \xc9 \xca \xcb \xcc \xcd \xce + \xcf \xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 \xd8 \xd9 \xda \xdb \xdc \xdd + \xde \xdf \xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec + \xed \xee \xef \xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7 \xf8 \xf9 \xfa \xfb + \xfc \xfd \xfe \xff + +/(a|b)*[\s]/S +Capturing subpattern count = 1 +No options +No first char +No need char +Starting character set: \x09 \x0a \x0b \x0c \x0d \x20 a b + +/(ab\2)/ +Failed: back reference to non-existent subpattern at offset 6 + +/{4,5}abc/ +Failed: nothing to repeat at offset 4 + +/(a)(b)(c)\2/ +Capturing subpattern count = 3 +Max back reference = 2 +No options +First char = 'a' +Need char = 'c' + abcb + 0: abcb + 1: a + 2: b + 3: c + \O0abcb +Matched, but too many substrings + \O3abcb +Matched, but too many substrings + 0: abcb + \O6abcb +Matched, but too many substrings + 0: abcb + 1: a + \O9abcb +Matched, but too many substrings + 0: abcb + 1: a + 2: b + \O12abcb + 0: abcb + 1: a + 2: b + 3: c + +/(a)bc|(a)(b)\2/ +Capturing subpattern count = 3 +Max back reference = 2 +No options +First char = 'a' +No need char + abc + 0: abc + 1: a + \O0abc +Matched, but too many substrings + \O3abc +Matched, but too many substrings + 0: abc + \O6abc + 0: abc + 1: a + aba + 0: aba + 1: + 2: a + 3: b + \O0aba +Matched, but too many substrings + \O3aba +Matched, but too many substrings + 0: aba + \O6aba +Matched, but too many substrings + 0: aba + 1: + \O9aba +Matched, but too many substrings + 0: aba + 1: + 2: a + \O12aba + 0: aba + 1: + 2: a + 3: b + +/abc$/E +Capturing subpattern count = 0 +Options: dollar_endonly +First char = 'a' +Need char = 'c' + abc + 0: abc + *** Failers +No match + abc\n +No match + abc\ndef +No match + +/(a)(b)(c)(d)(e)\6/ +Failed: back reference to non-existent subpattern at offset 17 + +/the quick brown fox/ +Capturing subpattern count = 0 +No options +First char = 't' +Need char = 'x' + the quick brown fox + 0: the quick brown fox + this is a line with the quick brown fox + 0: the quick brown fox + +/the quick brown fox/A +Capturing subpattern count = 0 +Options: anchored +No first char +Need char = 'x' + the quick brown fox + 0: the quick brown fox + *** Failers +No match + this is a line with the quick brown fox +No match + +/ab(?z)cd/ +Failed: unrecognized character after (? at offset 4 + +/^abc|def/ +Capturing subpattern count = 0 +No options +No first char +No need char + abcdef + 0: abc + abcdef\B + 0: def + +/.*((abc)$|(def))/ +Capturing subpattern count = 3 +No options +First char at start or follows \n +No need char + defabc + 0: defabc + 1: abc + 2: abc + \Zdefabc + 0: def + 1: def + 2: + 3: def + +/abc/P + abc + 0: abc + *** Failers +No match: POSIX code 17: match failed + +/^abc|def/P + abcdef + 0: abc + abcdef\B + 0: def + +/.*((abc)$|(def))/P + defabc + 0: defabc + 1: abc + 2: abc + \Zdefabc + 0: def + 1: def + 3: def + +/the quick brown fox/P + the quick brown fox + 0: the quick brown fox + *** Failers +No match: POSIX code 17: match failed + The Quick Brown Fox +No match: POSIX code 17: match failed + +/the quick brown fox/Pi + the quick brown fox + 0: the quick brown fox + The Quick Brown Fox + 0: The Quick Brown Fox + +/abc.def/P + *** Failers +No match: POSIX code 17: match failed + abc\ndef +No match: POSIX code 17: match failed + +/abc$/P + abc + 0: abc + abc\n + 0: abc + +/(abc)\2/P +Failed: POSIX code 15: bad back reference at offset 7 + +/(abc\1)/P + abc +No match: POSIX code 17: match failed + +/)/ +Failed: unmatched parentheses at offset 0 + +/a[]b/ +Failed: missing terminating ] for character class at offset 4 + +/[^aeiou ]{3,}/ +Capturing subpattern count = 0 +No options +No first char +No need char + co-processors, and for + 0: -pr + +/<.*>/ +Capturing subpattern count = 0 +No options +First char = '<' +Need char = '>' + abcghinop + 0: ghi + +/<.*?>/ +Capturing subpattern count = 0 +No options +First char = '<' +Need char = '>' + abcghinop + 0: + +/<.*>/U +Capturing subpattern count = 0 +Options: ungreedy +First char = '<' +Need char = '>' + abcghinop + 0: + +/<.*>(?U)/ +Capturing subpattern count = 0 +Options: ungreedy +First char = '<' +Need char = '>' + abcghinop + 0: + +/<.*?>/U +Capturing subpattern count = 0 +Options: ungreedy +First char = '<' +Need char = '>' + abcghinop + 0: ghi + +/={3,}/U +Capturing subpattern count = 0 +Options: ungreedy +First char = '=' +Need char = '=' + abc========def + 0: === + +/(?U)={3,}?/ +Capturing subpattern count = 0 +Options: ungreedy +First char = '=' +Need char = '=' + abc========def + 0: ======== + +/(?^abc)/m +Capturing subpattern count = 0 +Options: multiline +First char at start or follows \n +Need char = 'c' + abc + 0: abc + def\nabc + 0: abc + *** Failers +No match + defabc +No match + +/(?<=ab(c+)d)ef/ +Failed: lookbehind assertion is not fixed length at offset 11 + +/(?<=ab(?<=c+)d)ef/ +Failed: lookbehind assertion is not fixed length at offset 12 + +/(?<=ab(c|de)f)g/ +Failed: lookbehind assertion is not fixed length at offset 13 + +/The next three are in testinput2 because they have variable length branches/ +Capturing subpattern count = 0 +No options +First char = 'T' +Need char = 's' + +/(?<=bullock|donkey)-cart/ +Capturing subpattern count = 0 +No options +First char = '-' +Need char = 't' + the bullock-cart + 0: -cart + a donkey-cart race + 0: -cart + *** Failers +No match + cart +No match + horse-and-cart +No match + +/(?<=ab(?i)x|y|z)/ +Capturing subpattern count = 0 +No options +Case state changes +No first char +No need char + +/(?>.*)(?<=(abcd)|(xyz))/ +Capturing subpattern count = 2 +No options +First char at start or follows \n +No need char + alphabetabcd + 0: alphabetabcd + 1: abcd + endingxyz + 0: endingxyz + 1: + 2: xyz + +/(?<=ab(?i)x(?-i)y|(?i)z|b)ZZ/ +Capturing subpattern count = 0 +No options +Case state changes +First char = 'Z' +Need char = 'Z' + abxyZZ + 0: ZZ + abXyZZ + 0: ZZ + ZZZ + 0: ZZ + zZZ + 0: ZZ + bZZ + 0: ZZ + BZZ + 0: ZZ + *** Failers +No match + ZZ +No match + abXYZZ +No match + zzz +No match + bzz +No match + +/(? + 3: f + 1G a (1) + 2G (0) + 3G f (1) +get substring 4 failed -7 + 0L adef + 1L a + 2L + 3L f + bcdef\G1\G2\G3\G4\L + 0: bcdef + 1: bc + 2: bc + 3: f + 1G bc (2) + 2G bc (2) + 3G f (1) +get substring 4 failed -7 + 0L bcdef + 1L bc + 2L bc + 3L f + adefghijk\C0 + 0: adef + 1: a + 2: + 3: f + 0C adef (4) + +/^abc\00def/ +Capturing subpattern count = 0 +Options: anchored +No first char +Need char = 'f' + abc\00def\L\C0 + 0: abc\x00def + 0C abc (7) + 0L abc + +/word ((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ +)((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ +)?)?)?)?)?)?)?)?)?otherword/M +Memory allocation (code space): 428 +Capturing subpattern count = 8 +No options +First char = 'w' +Need char = 'd' + +/.*X/D +------------------------------------------------------------------ + 0 8 Bra 0 + 3 Any* + 5 1 X + 8 8 Ket + 11 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +No options +First char at start or follows \n +Need char = 'X' + +/.*X/Ds +------------------------------------------------------------------ + 0 8 Bra 0 + 3 Any* + 5 1 X + 8 8 Ket + 11 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored dotall +No first char +Need char = 'X' + +/(.*X|^B)/D +------------------------------------------------------------------ + 0 21 Bra 0 + 3 8 Bra 1 + 6 Any* + 8 1 X + 11 7 Alt + 14 ^ + 15 1 B + 18 15 Ket + 21 21 Ket + 24 End +------------------------------------------------------------------ +Capturing subpattern count = 1 +No options +First char at start or follows \n +No need char + +/(.*X|^B)/Ds +------------------------------------------------------------------ + 0 21 Bra 0 + 3 8 Bra 1 + 6 Any* + 8 1 X + 11 7 Alt + 14 ^ + 15 1 B + 18 15 Ket + 21 21 Ket + 24 End +------------------------------------------------------------------ +Capturing subpattern count = 1 +Options: anchored dotall +No first char +No need char + +/(?s)(.*X|^B)/D +------------------------------------------------------------------ + 0 21 Bra 0 + 3 8 Bra 1 + 6 Any* + 8 1 X + 11 7 Alt + 14 ^ + 15 1 B + 18 15 Ket + 21 21 Ket + 24 End +------------------------------------------------------------------ +Capturing subpattern count = 1 +Options: anchored dotall +No first char +No need char + +/(?s:.*X|^B)/D +------------------------------------------------------------------ + 0 27 Bra 0 + 3 10 Bra 0 + 6 04 Opt + 8 Any* + 10 1 X + 13 9 Alt + 16 04 Opt + 18 ^ + 19 1 B + 22 19 Ket + 25 00 Opt + 27 27 Ket + 30 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +No options +First char at start or follows \n +No need char + +/\Biss\B/+ +Capturing subpattern count = 0 +No options +First char = 'i' +Need char = 's' + Mississippi + 0: iss + 0+ issippi + +/\Biss\B/+P + Mississippi + 0: iss + 0+ issippi + +/iss/G+ +Capturing subpattern count = 0 +No options +First char = 'i' +Need char = 's' + Mississippi + 0: iss + 0+ issippi + 0: iss + 0+ ippi + +/\Biss\B/G+ +Capturing subpattern count = 0 +No options +First char = 'i' +Need char = 's' + Mississippi + 0: iss + 0+ issippi + +/\Biss\B/g+ +Capturing subpattern count = 0 +No options +First char = 'i' +Need char = 's' + Mississippi + 0: iss + 0+ issippi + 0: iss + 0+ ippi + *** Failers +No match + Mississippi\A +No match + +/(?<=[Ms])iss/g+ +Capturing subpattern count = 0 +No options +First char = 'i' +Need char = 's' + Mississippi + 0: iss + 0+ issippi + 0: iss + 0+ ippi + +/(?<=[Ms])iss/G+ +Capturing subpattern count = 0 +No options +First char = 'i' +Need char = 's' + Mississippi + 0: iss + 0+ issippi + +/^iss/g+ +Capturing subpattern count = 0 +Options: anchored +No first char +Need char = 's' + ississippi + 0: iss + 0+ issippi + +/.*iss/g+ +Capturing subpattern count = 0 +No options +First char at start or follows \n +Need char = 's' + abciss\nxyzisspqr + 0: abciss + 0+ \x0axyzisspqr + 0: xyziss + 0+ pqr + +/.i./+g +Capturing subpattern count = 0 +No options +No first char +Need char = 'i' + Mississippi + 0: Mis + 0+ sissippi + 0: sis + 0+ sippi + 0: sip + 0+ pi + Mississippi\A + 0: Mis + 0+ sissippi + 0: sis + 0+ sippi + 0: sip + 0+ pi + Missouri river + 0: Mis + 0+ souri river + 0: ri + 0+ river + 0: riv + 0+ er + Missouri river\A + 0: Mis + 0+ souri river + +/^.is/+g +Capturing subpattern count = 0 +Options: anchored +No first char +Need char = 's' + Mississippi + 0: Mis + 0+ sissippi + +/^ab\n/g+ +Capturing subpattern count = 0 +Options: anchored +No first char +Need char = 10 + ab\nab\ncd + 0: ab\x0a + 0+ ab\x0acd + +/^ab\n/mg+ +Capturing subpattern count = 0 +Options: multiline +First char at start or follows \n +Need char = 10 + ab\nab\ncd + 0: ab\x0a + 0+ ab\x0acd + 0: ab\x0a + 0+ cd + +/abc/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'c' + +/abc|bac/ +Capturing subpattern count = 0 +No options +No first char +Need char = 'c' + +/(abc|bac)/ +Capturing subpattern count = 1 +No options +No first char +Need char = 'c' + +/(abc|(c|dc))/ +Capturing subpattern count = 2 +No options +No first char +Need char = 'c' + +/(abc|(d|de)c)/ +Capturing subpattern count = 2 +No options +No first char +Need char = 'c' + +/a*/ +Capturing subpattern count = 0 +No options +No first char +No need char + +/a+/ +Capturing subpattern count = 0 +No options +First char = 'a' +No need char + +/(baa|a+)/ +Capturing subpattern count = 1 +No options +No first char +Need char = 'a' + +/a{0,3}/ +Capturing subpattern count = 0 +No options +No first char +No need char + +/baa{3,}/ +Capturing subpattern count = 0 +No options +First char = 'b' +Need char = 'a' + +/"([^\\"]+|\\.)*"/ +Capturing subpattern count = 1 +No options +First char = '"' +Need char = '"' + +/(abc|ab[cd])/ +Capturing subpattern count = 1 +No options +First char = 'a' +No need char + +/(a|.)/ +Capturing subpattern count = 1 +No options +No first char +No need char + +/a|ba|\w/ +Capturing subpattern count = 0 +No options +No first char +No need char + +/abc(?=pqr)/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'r' + +/...(?<=abc)/ +Capturing subpattern count = 0 +No options +No first char +No need char + +/abc(?!pqr)/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'c' + +/ab./ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'b' + +/ab[xyz]/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'b' + +/abc*/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'b' + +/ab.c*/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'b' + +/a.c*/ +Capturing subpattern count = 0 +No options +First char = 'a' +No need char + +/.c*/ +Capturing subpattern count = 0 +No options +No first char +No need char + +/ac*/ +Capturing subpattern count = 0 +No options +First char = 'a' +No need char + +/(a.c*|b.c*)/ +Capturing subpattern count = 1 +No options +No first char +No need char + +/a.c*|aba/ +Capturing subpattern count = 0 +No options +First char = 'a' +No need char + +/.+a/ +Capturing subpattern count = 0 +No options +No first char +Need char = 'a' + +/(?=abcda)a.*/ +Capturing subpattern count = 0 +No options +First char = 'a' +No need char + +/(?=a)a.*/ +Capturing subpattern count = 0 +No options +First char = 'a' +No need char + +/a(b)*/ +Capturing subpattern count = 1 +No options +First char = 'a' +No need char + +/a\d*/ +Capturing subpattern count = 0 +No options +First char = 'a' +No need char + +/ab\d*/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'b' + +/a(\d)*/ +Capturing subpattern count = 1 +No options +First char = 'a' +No need char + +/abcde{0,0}/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'd' + +/ab\d+/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'b' + +/a(?(1)b)/ +Capturing subpattern count = 0 +No options +First char = 'a' +No need char + +/a(?(1)bag|big)/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'g' + +/a(?(1)bag|big)*/ +Capturing subpattern count = 0 +No options +First char = 'a' +No need char + +/a(?(1)bag|big)+/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'g' + +/a(?(1)b..|b..)/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'b' + +/ab\d{0}e/ +Capturing subpattern count = 0 +No options +First char = 'a' +Need char = 'e' + +/a?b?/ +Capturing subpattern count = 0 +No options +No first char +No need char + a + 0: a + b + 0: b + ab + 0: ab + \ + 0: + *** Failers + 0: + \N +No match + +/|-/ +Capturing subpattern count = 0 +No options +No first char +No need char + abcd + 0: + -abc + 0: + \Nab-c + 0: - + *** Failers + 0: + \Nabc +No match + +/a*(b+)(z)(z)/P + aaaabbbbzzzz + 0: aaaabbbbzz + 1: bbbb + 2: z + 3: z + aaaabbbbzzzz\O0 + aaaabbbbzzzz\O1 + 0: aaaabbbbzz + aaaabbbbzzzz\O2 + 0: aaaabbbbzz + 1: bbbb + aaaabbbbzzzz\O3 + 0: aaaabbbbzz + 1: bbbb + 2: z + aaaabbbbzzzz\O4 + 0: aaaabbbbzz + 1: bbbb + 2: z + 3: z + aaaabbbbzzzz\O5 + 0: aaaabbbbzz + 1: bbbb + 2: z + 3: z + +/^.?abcd/S +Capturing subpattern count = 0 +Options: anchored +No first char +Need char = 'd' +Study returned NULL + +/\( # ( at start + (?: # Non-capturing bracket + (?>[^()]+) # Either a sequence of non-brackets (no backtracking) + | # Or + (?R) # Recurse - i.e. nested bracketed string + )* # Zero or more contents + \) # Closing ) + /x +Capturing subpattern count = 0 +Options: extended +First char = '(' +Need char = ')' + (abcd) + 0: (abcd) + (abcd)xyz + 0: (abcd) + xyz(abcd) + 0: (abcd) + (ab(xy)cd)pqr + 0: (ab(xy)cd) + (ab(xycd)pqr + 0: (xycd) + () abc () + 0: () + 12(abcde(fsh)xyz(foo(bar))lmno)89 + 0: (abcde(fsh)xyz(foo(bar))lmno) + *** Failers +No match + abcd +No match + abcd) +No match + (abcd +No match + +/\( ( (?>[^()]+) | (?R) )* \) /xg +Capturing subpattern count = 1 +Options: extended +First char = '(' +Need char = ')' + (ab(xy)cd)pqr + 0: (ab(xy)cd) + 1: cd + 1(abcd)(x(y)z)pqr + 0: (abcd) + 1: abcd + 0: (x(y)z) + 1: z + +/\( (?: (?>[^()]+) | (?R) ) \) /x +Capturing subpattern count = 0 +Options: extended +First char = '(' +Need char = ')' + (abcd) + 0: (abcd) + (ab(xy)cd) + 0: (xy) + (a(b(c)d)e) + 0: (c) + ((ab)) + 0: ((ab)) + *** Failers +No match + () +No match + +/\( (?: (?>[^()]+) | (?R) )? \) /x +Capturing subpattern count = 0 +Options: extended +First char = '(' +Need char = ')' + () + 0: () + 12(abcde(fsh)xyz(foo(bar))lmno)89 + 0: (fsh) + +/\( ( (?>[^()]+) | (?R) )* \) /x +Capturing subpattern count = 1 +Options: extended +First char = '(' +Need char = ')' + (ab(xy)cd) + 0: (ab(xy)cd) + 1: cd + +/\( ( ( (?>[^()]+) | (?R) )* ) \) /x +Capturing subpattern count = 2 +Options: extended +First char = '(' +Need char = ')' + (ab(xy)cd) + 0: (ab(xy)cd) + 1: ab(xy)cd + 2: cd + +/\( (123)? ( ( (?>[^()]+) | (?R) )* ) \) /x +Capturing subpattern count = 3 +Options: extended +First char = '(' +Need char = ')' + (ab(xy)cd) + 0: (ab(xy)cd) + 1: + 2: ab(xy)cd + 3: cd + (123ab(xy)cd) + 0: (123ab(xy)cd) + 1: 123 + 2: ab(xy)cd + 3: cd + +/\( ( (123)? ( (?>[^()]+) | (?R) )* ) \) /x +Capturing subpattern count = 3 +Options: extended +First char = '(' +Need char = ')' + (ab(xy)cd) + 0: (ab(xy)cd) + 1: ab(xy)cd + 2: + 3: cd + (123ab(xy)cd) + 0: (123ab(xy)cd) + 1: 123ab(xy)cd + 2: 123 + 3: cd + +/\( (((((((((( ( (?>[^()]+) | (?R) )* )))))))))) \) /x +Capturing subpattern count = 11 +Options: extended +First char = '(' +Need char = ')' + (ab(xy)cd) + 0: (ab(xy)cd) + 1: ab(xy)cd + 2: ab(xy)cd + 3: ab(xy)cd + 4: ab(xy)cd + 5: ab(xy)cd + 6: ab(xy)cd + 7: ab(xy)cd + 8: ab(xy)cd + 9: ab(xy)cd +10: ab(xy)cd +11: cd + +/\( ( ( (?>[^()<>]+) | ((?>[^()]+)) | (?R) )* ) \) /x +Capturing subpattern count = 3 +Options: extended +First char = '(' +Need char = ')' + (abcd(xyz

    qrs)123) + 0: (abcd(xyz

    qrs)123) + 1: abcd(xyz

    qrs)123 + 2: 123 + 3:

    qrs + +/\( ( ( (?>[^()]+) | ((?R)) )* ) \) /x +Capturing subpattern count = 3 +Options: extended +First char = '(' +Need char = ')' + (ab(cd)ef) + 0: (ab(cd)ef) + 1: ab(cd)ef + 2: ef + 3: (cd) + (ab(cd(ef)gh)ij) + 0: (ab(cd(ef)gh)ij) + 1: ab(cd(ef)gh)ij + 2: ij + 3: (cd(ef)gh) + +/^[[:alnum:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [0-9A-Za-z] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:alpha:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [A-Za-z] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:ascii:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [\x00-\x7f] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:cntrl:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [\x00-\x1f\x7f] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:digit:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [0-9] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:graph:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [!-~] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:lower:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [a-z] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:print:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [ -~] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:punct:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [!-/:-@[-`{-~] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:space:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [\x09-\x0d ] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:upper:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [A-Z] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:xdigit:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [0-9A-Fa-f] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:word:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [0-9A-Z_a-z] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[[:^cntrl:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [ -~\x80-\xff] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/^[12[:^digit:]]/D +------------------------------------------------------------------ + 0 37 Bra 0 + 3 ^ + 4 [\x00-/1-2:-\xff] + 37 37 Ket + 40 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +Options: anchored +No first char +No need char + +/[01[:alpha:]%]/D +------------------------------------------------------------------ + 0 36 Bra 0 + 3 [%0-1A-Za-z] + 36 36 Ket + 39 End +------------------------------------------------------------------ +Capturing subpattern count = 0 +No options +No first char +No need char + +/[[.ch.]]/ +Failed: POSIX collating elements are not supported at offset 1 + +/[[=ch=]]/ +Failed: POSIX collating elements are not supported at offset 1 + +/[[:rhubarb:]]/ +Failed: unknown POSIX class name at offset 3 + +/[[:upper:]]/i +Capturing subpattern count = 0 +Options: caseless +No first char +No need char + A + 0: A + a + 0: a + +/[[:lower:]]/i +Capturing subpattern count = 0 +Options: caseless +No first char +No need char + A + 0: A + a + 0: a + +/((?-i)[[:lower:]])[[:lower:]]/i +Capturing subpattern count = 1 +Options: caseless +Case state changes +No first char +No need char + ab + 0: ab + 1: a + aB + 0: aB + 1: a + *** Failers + 0: ai + 1: a + Ab +No match + AB +No match + +/ End of test input / +Capturing subpattern count = 0 +No options +First char = ' ' +Need char = ' ' + diff --git a/srclib/pcre/testdata/testoutput3 b/srclib/pcre/testdata/testoutput3 new file mode 100644 index 00000000000..8eb215eb594 --- /dev/null +++ b/srclib/pcre/testdata/testoutput3 @@ -0,0 +1,2929 @@ +PCRE version 3.1 09-Feb-2000 + +/(?.*/)foo" + /this/is/a/very/long/line/in/deed/with/very/many/slashes/in/it/you/see/ +No match + +"(?>.*/)foo" + /this/is/a/very/long/line/in/deed/with/very/many/slashes/in/and/foo + 0: /this/is/a/very/long/line/in/deed/with/very/many/slashes/in/and/foo + +/(?>(\.\d\d[1-9]?))\d+/ + 1.230003938 + 0: .230003938 + 1: .23 + 1.875000282 + 0: .875000282 + 1: .875 + *** Failers +No match + 1.235 +No match + +/^((?>\w+)|(?>\s+))*$/ + now is the time for all good men to come to the aid of the party + 0: now is the time for all good men to come to the aid of the party + 1: party + *** Failers +No match + this is not a line with only words and spaces! +No match + +/(\d+)(\w)/ + 12345a + 0: 12345a + 1: 12345 + 2: a + 12345+ + 0: 12345 + 1: 1234 + 2: 5 + +/((?>\d+))(\w)/ + 12345a + 0: 12345a + 1: 12345 + 2: a + *** Failers +No match + 12345+ +No match + +/(?>a+)b/ + aaab + 0: aaab + +/((?>a+)b)/ + aaab + 0: aaab + 1: aaab + +/(?>(a+))b/ + aaab + 0: aaab + 1: aaa + +/(?>b)+/ + aaabbbccc + 0: bbb + +/(?>a+|b+|c+)*c/ + aaabbbbccccd + 0: aaabbbbc + +/((?>[^()]+)|\([^()]*\))+/ + ((abc(ade)ufh()()x + 0: abc(ade)ufh()()x + 1: x + +/\(((?>[^()]+)|\([^()]+\))+\)/ + (abc) + 0: (abc) + 1: abc + (abc(def)xyz) + 0: (abc(def)xyz) + 1: xyz + *** Failers +No match + ((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +No match + +/a(?-i)b/i + ab + 0: ab + *** Failers +No match + Ab +No match + aB +No match + AB +No match + +/(a (?x)b c)d e/ + a bcd e + 0: a bcd e + 1: a bc + *** Failers +No match + a b cd e +No match + abcd e +No match + a bcde +No match + +/(a b(?x)c d (?-x)e f)/ + a bcde f + 0: a bcde f + 1: a bcde f + *** Failers +No match + abcdef +No match + +/(a(?i)b)c/ + abc + 0: abc + 1: ab + aBc + 0: aBc + 1: aB + *** Failers +No match + abC +No match + aBC +No match + Abc +No match + ABc +No match + ABC +No match + AbC +No match + +/a(?i:b)c/ + abc + 0: abc + aBc + 0: aBc + *** Failers +No match + ABC +No match + abC +No match + aBC +No match + +/a(?i:b)*c/ + aBc + 0: aBc + aBBc + 0: aBBc + *** Failers +No match + aBC +No match + aBBC +No match + +/a(?=b(?i)c)\w\wd/ + abcd + 0: abcd + abCd + 0: abCd + *** Failers +No match + aBCd +No match + abcD +No match + +/(?s-i:more.*than).*million/i + more than million + 0: more than million + more than MILLION + 0: more than MILLION + more \n than Million + 0: more \x0a than Million + *** Failers +No match + MORE THAN MILLION +No match + more \n than \n million +No match + +/(?:(?s-i)more.*than).*million/i + more than million + 0: more than million + more than MILLION + 0: more than MILLION + more \n than Million + 0: more \x0a than Million + *** Failers +No match + MORE THAN MILLION +No match + more \n than \n million +No match + +/(?>a(?i)b+)+c/ + abc + 0: abc + aBbc + 0: aBbc + aBBc + 0: aBBc + *** Failers +No match + Abc +No match + abAb +No match + abbC +No match + +/(?=a(?i)b)\w\wc/ + abc + 0: abc + aBc + 0: aBc + *** Failers +No match + Ab +No match + abC +No match + aBC +No match + +/(?<=a(?i)b)(\w\w)c/ + abxxc + 0: xxc + 1: xx + aBxxc + 0: xxc + 1: xx + *** Failers +No match + Abxxc +No match + ABxxc +No match + abxxC +No match + +/(?:(a)|b)(?(1)A|B)/ + aA + 0: aA + 1: a + bB + 0: bB + *** Failers +No match + aB +No match + bA +No match + +/^(a)?(?(1)a|b)+$/ + aa + 0: aa + 1: a + b + 0: b + bb + 0: bb + *** Failers +No match + ab +No match + +/^(?(?=abc)\w{3}:|\d\d)$/ + abc: + 0: abc: + 12 + 0: 12 + *** Failers +No match + 123 +No match + xyz +No match + +/^(?(?!abc)\d\d|\w{3}:)$/ + abc: + 0: abc: + 12 + 0: 12 + *** Failers +No match + 123 +No match + xyz +No match + +/(?(?<=foo)bar|cat)/ + foobar + 0: bar + cat + 0: cat + fcat + 0: cat + focat + 0: cat + *** Failers +No match + foocat +No match + +/(?(?a*)*/ + a + 0: a + aa + 0: aa + aaaa + 0: aaaa + +/(abc|)+/ + abc + 0: abc + 1: + abcabc + 0: abcabc + 1: + abcabcabc + 0: abcabcabc + 1: + xyz + 0: + 1: + +/([a]*)*/ + a + 0: a + 1: + aaaaa + 0: aaaaa + 1: + +/([ab]*)*/ + a + 0: a + 1: + b + 0: b + 1: + ababab + 0: ababab + 1: + aaaabcde + 0: aaaab + 1: + bbbb + 0: bbbb + 1: + +/([^a]*)*/ + b + 0: b + 1: + bbbb + 0: bbbb + 1: + aaa + 0: + 1: + +/([^ab]*)*/ + cccc + 0: cccc + 1: + abab + 0: + 1: + +/([a]*?)*/ + a + 0: + 1: + aaaa + 0: + 1: + +/([ab]*?)*/ + a + 0: + 1: + b + 0: + 1: + abab + 0: + 1: + baba + 0: + 1: + +/([^a]*?)*/ + b + 0: + 1: + bbbb + 0: + 1: + aaa + 0: + 1: + +/([^ab]*?)*/ + c + 0: + 1: + cccc + 0: + 1: + baba + 0: + 1: + +/(?>a*)*/ + a + 0: a + aaabcde + 0: aaa + +/((?>a*))*/ + aaaaa + 0: aaaaa + 1: + aabbaa + 0: aa + 1: + +/((?>a*?))*/ + aaaaa + 0: + 1: + aabbaa + 0: + 1: + +/(?(?=[^a-z]+[a-z]) \d{2}-[a-z]{3}-\d{2} | \d{2}-\d{2}-\d{2} ) /x + 12-sep-98 + 0: 12-sep-98 + 12-09-98 + 0: 12-09-98 + *** Failers +No match + sep-12-98 +No match + +/(?<=(foo))bar\1/ + foobarfoo + 0: barfoo + 1: foo + foobarfootling + 0: barfoo + 1: foo + *** Failers +No match + foobar +No match + barfoo +No match + +/(?i:saturday|sunday)/ + saturday + 0: saturday + sunday + 0: sunday + Saturday + 0: Saturday + Sunday + 0: Sunday + SATURDAY + 0: SATURDAY + SUNDAY + 0: SUNDAY + SunDay + 0: SunDay + +/(a(?i)bc|BB)x/ + abcx + 0: abcx + 1: abc + aBCx + 0: aBCx + 1: aBC + bbx + 0: bbx + 1: bb + BBx + 0: BBx + 1: BB + *** Failers +No match + abcX +No match + aBCX +No match + bbX +No match + BBX +No match + +/^([ab](?i)[cd]|[ef])/ + ac + 0: ac + 1: ac + aC + 0: aC + 1: aC + bD + 0: bD + 1: bD + elephant + 0: e + 1: e + Europe + 0: E + 1: E + frog + 0: f + 1: f + France + 0: F + 1: F + *** Failers +No match + Africa +No match + +/^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)/ + ab + 0: ab + 1: ab + aBd + 0: aBd + 1: aBd + xy + 0: xy + 1: xy + xY + 0: xY + 1: xY + zebra + 0: z + 1: z + Zambesi + 0: Z + 1: Z + *** Failers +No match + aCD +No match + XY +No match + +/(?<=foo\n)^bar/m + foo\nbar + 0: bar + *** Failers +No match + bar +No match + baz\nbar +No match + +/(?<=(?]&/ + <&OUT + 0: <& + +/^(a\1?){4}$/ + aaaaaaaaaa + 0: aaaaaaaaaa + 1: aaaa + *** Failers +No match + AB +No match + aaaaaaaaa +No match + aaaaaaaaaaa +No match + +/^(a(?(1)\1)){4}$/ + aaaaaaaaaa + 0: aaaaaaaaaa + 1: aaaa + *** Failers +No match + aaaaaaaaa +No match + aaaaaaaaaaa +No match + +/(?:(f)(o)(o)|(b)(a)(r))*/ + foobar + 0: foobar + 1: f + 2: o + 3: o + 4: b + 5: a + 6: r + +/(?<=a)b/ + ab + 0: b + *** Failers +No match + cb +No match + b +No match + +/(? + 2: abcd + xy:z:::abcd + 0: xy:z:::abcd + 1: xy:z::: + 2: abcd + +/^[^bcd]*(c+)/ + aexycd + 0: aexyc + 1: c + +/(a*)b+/ + caab + 0: aab + 1: aa + +/([\w:]+::)?(\w+)$/ + abcd + 0: abcd + 1: + 2: abcd + xy:z:::abcd + 0: xy:z:::abcd + 1: xy:z::: + 2: abcd + *** Failers + 0: Failers + 1: + 2: Failers + abcd: +No match + abcd: +No match + +/^[^bcd]*(c+)/ + aexycd + 0: aexyc + 1: c + +/(>a+)ab/ + +/(?>a+)b/ + aaab + 0: aaab + +/([[:]+)/ + a:[b]: + 0: :[ + 1: :[ + +/([[=]+)/ + a=[b]= + 0: =[ + 1: =[ + +/([[.]+)/ + a.[b]. + 0: .[ + 1: .[ + +/((?>a+)b)/ + aaab + 0: aaab + 1: aaab + +/(?>(a+))b/ + aaab + 0: aaab + 1: aaa + +/((?>[^()]+)|\([^()]*\))+/ + ((abc(ade)ufh()()x + 0: abc(ade)ufh()()x + 1: x + +/a\Z/ + *** Failers +No match + aaab +No match + a\nb\n +No match + +/b\Z/ + a\nb\n + 0: b + +/b\z/ + +/b\Z/ + a\nb + 0: b + +/b\z/ + a\nb + 0: b + *** Failers +No match + +/^(?>(?(1)\.|())[^\W_](?>[a-z0-9-]*[^\W_])?)+$/ + a + 0: a + 1: + abc + 0: abc + 1: + a-b + 0: a-b + 1: + 0-9 + 0: 0-9 + 1: + a.b + 0: a.b + 1: + 5.6.7 + 0: 5.6.7 + 1: + the.quick.brown.fox + 0: the.quick.brown.fox + 1: + a100.b200.300c + 0: a100.b200.300c + 1: + 12-ab.1245 + 0: 12-ab.1245 + 1: + ***Failers +No match + \ +No match + .a +No match + -a +No match + a- +No match + a. +No match + a_b +No match + a.- +No match + a.. +No match + ab..bc +No match + the.quick.brown.fox- +No match + the.quick.brown.fox. +No match + the.quick.brown.fox_ +No match + the.quick.brown.fox+ +No match + +/(?>.*)(?<=(abcd|wxyz))/ + alphabetabcd + 0: alphabetabcd + 1: abcd + endingwxyz + 0: endingwxyz + 1: wxyz + *** Failers +No match + a rather long string that doesn't end with one of them +No match + +/word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword/ + word cat dog elephant mussel cow horse canary baboon snake shark otherword + 0: word cat dog elephant mussel cow horse canary baboon snake shark otherword + word cat dog elephant mussel cow horse canary baboon snake shark +No match + +/word (?>[a-zA-Z0-9]+ ){0,30}otherword/ + word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope +No match + +/(?<=\d{3}(?!999))foo/ + 999foo + 0: foo + 123999foo + 0: foo + *** Failers +No match + 123abcfoo +No match + +/(?<=(?!...999)\d{3})foo/ + 999foo + 0: foo + 123999foo + 0: foo + *** Failers +No match + 123abcfoo +No match + +/(?<=\d{3}(?!999)...)foo/ + 123abcfoo + 0: foo + 123456foo + 0: foo + *** Failers +No match + 123999foo +No match + +/(?<=\d{3}...)(? + 2: + 3: abcd + + 2: + 3: abcd + \s*)=(?>\s*) # find + 2: + 3: abcd + . + +README.sha1 + this file + +convert-sha1.pl + takes an ldif dump from Netscape's web server on + standard in, outputs apache htpasswd format on standard out. + + Usage: convert.pl < ldif > passwords + +htpasswd-sha1.pl + perl script to generate entries in apache htpasswd format. + + Usage: htpasswd-sha1.pl some_user some_password + +ldif-sha1.example + sample ldif dump with one sha1 password and one crypt password. + diff --git a/support/SHA1/convert-sha1.pl b/support/SHA1/convert-sha1.pl new file mode 100644 index 00000000000..35228022a08 --- /dev/null +++ b/support/SHA1/convert-sha1.pl @@ -0,0 +1,36 @@ +#!/usr/bin/perl -w +use strict; + +# This is public domain code. Do whatever you want with it. +# It was originally included in Clinton Wong's Apache 1.3.6 SHA1/ldif +# patch distribution as sample code for converting accounts from +# ldif format (as used by Netscape web servers) to Apache password format. + +my $uid=''; +my $passwd=''; + +while (my $line = <>) { + chomp $line; + if ( $line =~ /uid:\s*(.+)/) { $uid = $1 } + if ( $line =~ /userpassword:\s*(\{\w+\}.+)/) { + $passwd = $1; + $passwd =~ s/^\{crypt\}//i; # Apache stores crypt without a magic string + } + + if (length($line)==0) { + + if (length $uid and length $passwd) { + print $uid, ':', $passwd, "\n"; + } # output if we have something to print + + $uid = ''; + $passwd = ''; + + } # if newline +} # while something to read + +# handle last entry if there isn't a newline before EOF + if (length $uid and length $passwd) { + print $uid, ':', $passwd, "\n"; +} + diff --git a/support/SHA1/htpasswd-sha1.pl b/support/SHA1/htpasswd-sha1.pl new file mode 100644 index 00000000000..ad624d1101f --- /dev/null +++ b/support/SHA1/htpasswd-sha1.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w +use strict; +# +# Utility which takes a username and password +# on the command line and generates a username +# sha1-encrytped password on the stdout. +# +# Typical useage: +# ./htpasswd-sha1.pl dirkx MySecret >> sha1-passwd +# +# This is public domain code. Do whatever you want with it. +# It was originally included in Clinton Wong's Apache 1.3.6 SHA1/ldif +# patch distribution as sample code for generating entries for +# Apache password files using SHA1. + +use MIME::Base64; # http://www.cpan.org/modules/by-module/MIME/ +use Digest::SHA1; # http://www.cpan.org/modules/by-module/MD5/ + +if ($#ARGV!=1) { die "Usage $0: user password\n" } + +print $ARGV[0], ':{SHA}', encode_base64( Digest::SHA1::sha1($ARGV[1]) ); + diff --git a/support/SHA1/ldif-sha1.example b/support/SHA1/ldif-sha1.example new file mode 100644 index 00000000000..b8fe917eaf3 --- /dev/null +++ b/support/SHA1/ldif-sha1.example @@ -0,0 +1,19 @@ +dn: cn=someuser +cn: someuser +sn: someuser +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: someuser +userpassword: {SHA}GvF+c3IdvgxAARuC7Uuxp9vjzik= + +dn: cn=anotheruser +cn: anotheruser +sn: anotheruser +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: anotheruser +userpassword: {crypt}eFnp.4sz5XnH6 diff --git a/support/ab.c b/support/ab.c new file mode 100644 index 00000000000..0d968b247a2 --- /dev/null +++ b/support/ab.c @@ -0,0 +1,1100 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + ** This program is based on ZeusBench V1.0 written by Adam Twiss + ** which is Copyright (c) 1996 by Zeus Technology Ltd. http://www.zeustech.net/ + ** + ** This software is provided "as is" and any express or implied waranties, + ** including but not limited to, the implied warranties of merchantability and + ** fitness for a particular purpose are disclaimed. In no event shall + ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, + ** exemplary, or consequential damaged (including, but not limited to, + ** procurement of substitute good or services; loss of use, data, or profits; + ** or business interruption) however caused and on theory of liability. Whether + ** in contract, strict liability or tort (including negligence or otherwise) + ** arising in any way out of the use of this software, even if advised of the + ** possibility of such damage. + ** + */ + +/* + ** HISTORY: + ** - Originally written by Adam Twiss , March 1996 + ** with input from Mike Belshe and + ** Michael Campanella + ** - Enhanced by Dean Gaudet , November 1997 + ** - Cleaned up by Ralf S. Engelschall , March 1998 + ** - POST and verbosity by Kurt Sussman , August 1998 + ** - HTML table output added by David N. Welton , January 1999 + ** - Added Cookie, Arbitrary header and auth support. , April 199 + ** + */ + +/* + * BUGS: + * + * - uses strcpy/etc. + * - has various other poor buffer attacks related to the lazy parsing of + * response headers from the server + * - doesn't implement much of HTTP/1.x, only accepts certain forms of + * responses + * - (performance problem) heavy use of strstr shows up top in profile + * only an issue for loopback usage + */ + +#define VERSION "1.3a" + +/* -------------------------------------------------------------------- */ + +/* affects include files on Solaris */ +#define BSD_COMP + +/* allow compilation outside an Apache build tree */ +#ifdef NO_APACHE_INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ap_select select +#else /* (!)NO_APACHE_INCLUDES */ +#include "ap_config.h" +#include "ap.h" +#ifdef CHARSET_EBCDIC +#include "ebcdic.h" +#endif +#include +#include + +#ifndef NO_WRITEV +#include +#include +#endif + +#endif /* NO_APACHE_INCLUDES */ +/* ------------------- DEFINITIONS -------------------------- */ + +/* maximum number of requests on a time limited test */ +#define MAX_REQUESTS 50000 + +/* good old state hostname */ +#define STATE_UNCONNECTED 0 +#define STATE_CONNECTING 1 +#define STATE_READ 2 + +#define CBUFFSIZE 512 + +struct connection { + int fd; + int state; + int read; /* amount of bytes read */ + int bread; /* amount of body read */ + int length; /* Content-Length value used for keep-alive */ + char cbuff[CBUFFSIZE]; /* a buffer to store server response header */ + int cbx; /* offset in cbuffer */ + int keepalive; /* non-zero if a keep-alive request */ + int gotheader; /* non-zero if we have the entire header in + * cbuff */ + struct timeval start, connect, done; +}; + +struct data { + int read; /* number of bytes read */ + int ctime; /* time in ms to connect */ + int time; /* time in ms for connection */ +}; + +#define ap_min(a,b) ((a)<(b))?(a):(b) +#define ap_max(a,b) ((a)>(b))?(a):(b) + +/* --------------------- GLOBALS ---------------------------- */ + +int verbosity = 0; /* no verbosity by default */ +int posting = 0; /* GET by default */ +int requests = 1; /* Number of requests to make */ +int concurrency = 1; /* Number of multiple requests to make */ +int tlimit = 0; /* time limit in cs */ +int keepalive = 0; /* try and do keepalive connections */ +char servername[1024]; /* name that server reports */ +char hostname[1024]; /* host name */ +char path[1024]; /* path name */ +char postfile[1024]; /* name of file containing post data */ +char *postdata; /* *buffer containing data from postfile */ +int postlen = 0; /* length of data to be POSTed */ +char content_type[1024]; /* content type to put in POST header */ +char cookie[1024], /* optional cookie line */ + auth[1024], /* optional (basic/uuencoded) + * authentification */ + hdrs[4096]; /* optional arbitrary headers */ +int port = 80; /* port number */ + +int use_html = 0; /* use html in the report */ +char *tablestring; +char *trstring; +char *tdstring; + +int doclen = 0; /* the length the document should be */ +int totalread = 0; /* total number of bytes read */ +int totalbread = 0; /* totoal amount of entity body read */ +int totalposted = 0; /* total number of bytes posted, inc. headers */ +int done = 0; /* number of requests we have done */ +int doneka = 0; /* number of keep alive connections done */ +int good = 0, bad = 0; /* number of good and bad requests */ + +/* store error cases */ +int err_length = 0, err_conn = 0, err_except = 0; +int err_response = 0; + +struct timeval start, endtime; + +/* global request (and its length) */ +char request[512]; +int reqlen; + +/* one global throw-away buffer to read stuff into */ +char buffer[8192]; + +struct connection *con; /* connection array */ +struct data *stats; /* date for each request */ + +fd_set readbits, writebits; /* bits for select */ +struct sockaddr_in server; /* server addr structure */ + +/* --------------------------------------------------------- */ + +/* simple little function to perror and exit */ + +static void err(char *s) +{ + if (errno) { + perror(s); + } + else { + printf("%s", s); + } + exit(errno); +} + +/* --------------------------------------------------------- */ + +/* write out request to a connection - assumes we can write + (small) request out in one go into our new socket buffer */ + +static void write_request(struct connection * c) +{ +#ifndef NO_WRITEV + struct iovec out[2]; int outcnt = 1; +#endif + gettimeofday(&c->connect, 0); +#ifndef NO_WRITEV + out[0].iov_base = request; + out[0].iov_len = reqlen; + + if (posting) { + out[1].iov_base = postdata; + out[1].iov_len = postlen; + outcnt = 2; + totalposted += (reqlen + postlen); + } + writev(c->fd,out, outcnt); +#else + write(c->fd,request,reqlen); + if (posting) { + write(c->fd,postdata,postlen); + totalposted += (reqlen + postlen); + } +#endif + + c->state = STATE_READ; + FD_SET(c->fd, &readbits); + FD_CLR(c->fd, &writebits); +} + +/* --------------------------------------------------------- */ + +/* make an fd non blocking */ + +static void nonblock(int fd) +{ + int i = 1; + ioctl(fd, FIONBIO, &i); +} + +/* --------------------------------------------------------- */ + +/* returns the time in ms between two timevals */ + +static int timedif(struct timeval a, struct timeval b) +{ + register int us, s; + + us = a.tv_usec - b.tv_usec; + us /= 1000; + s = a.tv_sec - b.tv_sec; + s *= 1000; + return s + us; +} + +/* --------------------------------------------------------- */ + +/* calculate and output results */ + +static void output_results(void) +{ + int timetaken; + + gettimeofday(&endtime, 0); + timetaken = timedif(endtime, start); + + printf("\r \r"); + printf("Server Software: %s\n", servername); + printf("Server Hostname: %s\n", hostname); + printf("Server Port: %d\n", port); + printf("\n"); + printf("Document Path: %s\n", path); + printf("Document Length: %d bytes\n", doclen); + printf("\n"); + printf("Concurrency Level: %d\n", concurrency); + printf("Time taken for tests: %d.%03d seconds\n", + timetaken / 1000, timetaken % 1000); + printf("Complete requests: %d\n", done); + printf("Failed requests: %d\n", bad); + if (bad) + printf(" (Connect: %d, Length: %d, Exceptions: %d)\n", + err_conn, err_length, err_except); + if (err_response) + printf("Non-2xx responses: %d\n", err_response); + if (keepalive) + printf("Keep-Alive requests: %d\n", doneka); + printf("Total transferred: %d bytes\n", totalread); + if (posting) + printf("Total POSTed: %d\n", totalposted); + printf("HTML transferred: %d bytes\n", totalbread); + + /* avoid divide by zero */ + if (timetaken) { + printf("Requests per second: %.2f\n", 1000 * (float) (done) / timetaken); + printf("Transfer rate: %.2f kb/s received\n", + (float) (totalread) / timetaken); + if (posting) { + printf(" %.2f kb/s sent\n", + (float) (totalposted) / timetaken); + printf(" %.2f kb/s total\n", + (float) (totalread + totalposted) / timetaken); + } + } + + { + /* work out connection times */ + int i; + int totalcon = 0, total = 0; + int mincon = 9999999, mintot = 999999; + int maxcon = 0, maxtot = 0; + + for (i = 0; i < requests; i++) { + struct data s = stats[i]; + mincon = ap_min(mincon, s.ctime); + mintot = ap_min(mintot, s.time); + maxcon = ap_max(maxcon, s.ctime); + maxtot = ap_max(maxtot, s.time); + totalcon += s.ctime; + total += s.time; + } + if (requests > 0) { /* avoid division by zero (if 0 requests) */ + printf("\nConnnection Times (ms)\n"); + printf(" min avg max\n"); + printf("Connect: %5d %5d %5d\n", mincon, totalcon / requests, maxcon); + printf("Processing: %5d %5d %5d\n", + mintot - mincon, (total / requests) - (totalcon / requests), + maxtot - maxcon); + printf("Total: %5d %5d %5d\n", mintot, total / requests, maxtot); + } + } +} + +/* --------------------------------------------------------- */ + +/* calculate and output results in HTML */ + +static void output_html_results(void) +{ + int timetaken; + + gettimeofday(&endtime, 0); + timetaken = timedif(endtime, start); + + printf("\n\n\n", tablestring); + printf("" + "\n", + trstring, tdstring, tdstring, servername); + printf("" + "\n", + trstring, tdstring, tdstring, hostname); + printf("" + "\n", + trstring, tdstring, tdstring, port); + printf("" + "\n", + trstring, tdstring, tdstring, path); + printf("" + "\n", + trstring, tdstring, tdstring, doclen); + printf("" + "\n", + trstring, tdstring, tdstring, concurrency); + printf("" + "\n", + trstring, tdstring, tdstring, timetaken / 1000, timetaken % 1000); + printf("" + "\n", + trstring, tdstring, tdstring, done); + printf("" + "\n", + trstring, tdstring, tdstring, bad); + if (bad) + printf("\n", + trstring, tdstring, err_conn, err_length, err_except); + if (err_response) + printf("" + "\n", + trstring, tdstring, tdstring, err_response); + if (keepalive) + printf("" + "\n", + trstring, tdstring, tdstring, doneka); + printf("" + "\n", + trstring, tdstring, tdstring, totalread); + if (posting) + printf("" + "\n", + trstring, tdstring, tdstring, totalposted); + printf("" + "\n", + trstring, tdstring, tdstring, totalbread); + + /* avoid divide by zero */ + if (timetaken) { + printf("" + "\n", + trstring, tdstring, tdstring, 1000 * (float) (done) / timetaken); + printf("" + "\n", + trstring, tdstring, tdstring, (float) (totalread) / timetaken); + if (posting) { + printf("" + "\n", + trstring, tdstring, tdstring, + (float) (totalposted) / timetaken); + printf("" + "\n", + trstring, tdstring, tdstring, + (float) (totalread + totalposted) / timetaken); + } + } + + { + /* work out connection times */ + int i; + int totalcon = 0, total = 0; + int mincon = 9999999, mintot = 999999; + int maxcon = 0, maxtot = 0; + + for (i = 0; i < requests; i++) { + struct data s = stats[i]; + mincon = ap_min(mincon, s.ctime); + mintot = ap_min(mintot, s.time); + maxcon = ap_max(maxcon, s.ctime); + maxtot = ap_max(maxtot, s.time); + totalcon += s.ctime; + total += s.time; + } + + if (requests > 0) { /* avoid division by zero (if 0 requests) */ + printf("\n", + trstring, tdstring); + printf("\n", + trstring, tdstring, tdstring, tdstring, tdstring); + printf("" + "" + "" + "\n", + trstring, tdstring, tdstring, mincon, tdstring, totalcon / requests, tdstring, maxcon); + printf("" + "" + "" + "\n", + trstring, tdstring, tdstring, mintot - mincon, tdstring, + (total / requests) - (totalcon / requests), tdstring, maxtot - maxcon); + printf("" + "" + "" + "\n", + trstring, tdstring, tdstring, mintot, tdstring, total / requests, tdstring, maxtot); + } + printf("
    Server Software:%s
    Server Hostname:%s
    Server Port:%d
    Document Path:%s
    Document Length:%d bytes
    Concurrency Level:%d
    Time taken for tests:%d.%03d seconds
    Complete requests:%d
    Failed requests:%d
    (Connect: %d, Length: %d, Exceptions: %d)
    Non-2xx responses:%d
    Keep-Alive requests:%d
    Total transferred:%d bytes
    Total POSTed:%d
    HTML transferred:%d bytes
    Requests per second:%.2f
    Transfer rate:%.2f kb/s received
     %.2f kb/s sent
     %.2f kb/s total
    Connnection Times (ms)
      min avg max
    Connect:%5d%5d%5d
    Processing:%5d%5d%5d
    Total:%5d%5d%5d
    \n"); + } +} + +/* --------------------------------------------------------- */ + +/* start asnchronous non-blocking connection */ + +static void start_connect(struct connection * c) +{ + c->read = 0; + c->bread = 0; + c->keepalive = 0; + c->cbx = 0; + c->gotheader = 0; + + c->fd = socket(AF_INET, SOCK_STREAM, 0); + if (c->fd < 0) + err("socket"); + + nonblock(c->fd); + gettimeofday(&c->start, 0); + + if (connect(c->fd, (struct sockaddr *) & server, sizeof(server)) < 0) { + if (errno == EINPROGRESS) { + c->state = STATE_CONNECTING; + FD_SET(c->fd, &writebits); + return; + } + else { + close(c->fd); + err_conn++; + if (bad++ > 10) { + err("\nTest aborted after 10 failures\n\n"); + } + start_connect(c); + } + } + + /* connected first time */ + write_request(c); +} + +/* --------------------------------------------------------- */ + +/* close down connection and save stats */ + +static void close_connection(struct connection * c) +{ + if (c->read == 0 && c->keepalive) { + /* server has legitimately shut down an idle keep alive request */ + good--; /* connection never happend */ + } + else { + if (good == 1) { + /* first time here */ + doclen = c->bread; + } + else if (c->bread != doclen) { + bad++; + err_length++; + } + + /* save out time */ + if (done < requests) { + struct data s; + gettimeofday(&c->done, 0); + s.read = c->read; + s.ctime = timedif(c->connect, c->start); + s.time = timedif(c->done, c->start); + stats[done++] = s; + } + } + + close(c->fd); + FD_CLR(c->fd, &readbits); + FD_CLR(c->fd, &writebits); + + /* connect again */ + start_connect(c); + return; +} + +/* --------------------------------------------------------- */ + +/* read data from connection */ + +static void read_connection(struct connection * c) +{ + int r; + char *part; + char respcode[4]; /* 3 digits and null */ + + r = read(c->fd, buffer, sizeof(buffer)); + if (r == 0 || (r < 0 && errno != EAGAIN)) { + good++; + close_connection(c); + return; + } + + if (r < 0 && errno == EAGAIN) + return; + + c->read += r; + totalread += r; + + if (!c->gotheader) { + char *s; + int l = 4; + int space = CBUFFSIZE - c->cbx - 1; /* -1 to allow for 0 + * terminator */ + int tocopy = (space < r) ? space : r; +#ifndef CHARSET_EBCDIC + memcpy(c->cbuff + c->cbx, buffer, tocopy); +#else /* CHARSET_EBCDIC */ + ascii2ebcdic(c->cbuff + c->cbx, buffer, tocopy); +#endif /* CHARSET_EBCDIC */ + c->cbx += tocopy; + space -= tocopy; + c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */ + if (verbosity >= 4) { + printf("LOG: header received:\n%s\n", c->cbuff); + } + s = strstr(c->cbuff, "\r\n\r\n"); + /* + * this next line is so that we talk to NCSA 1.5 which blatantly + * breaks the http specification + */ + if (!s) { + s = strstr(c->cbuff, "\n\n"); + l = 2; + } + + if (!s) { + /* read rest next time */ + if (space) + return; + else { + /* header is in invalid or too big - close connection */ + close(c->fd); + if (bad++ > 10) { + err("\nTest aborted after 10 failures\n\n"); + } + FD_CLR(c->fd, &writebits); + start_connect(c); + } + } + else { + /* have full header */ + if (!good) { + /* this is first time, extract some interesting info */ + char *p, *q; + p = strstr(c->cbuff, "Server:"); + q = servername; + if (p) { + p += 8; + while (*p > 32) + *q++ = *p++; + } + *q = 0; + } + + /* + * XXX: this parsing isn't even remotely HTTP compliant... but in + * the interest of speed it doesn't totally have to be, it just + * needs to be extended to handle whatever servers folks want to + * test against. -djg + */ + + /* check response code */ + part = strstr(c->cbuff, "HTTP"); /* really HTTP/1.x_ */ + strncpy(respcode, (part + strlen("HTTP/1.x_")), 3); + respcode[3] = '\0'; + if (respcode[0] != '2') { + err_response++; + if (verbosity >= 2) + printf("WARNING: Response code not 2xx (%s)\n", respcode); + } + else if (verbosity >= 3) { + printf("LOG: Response code = %s\n", respcode); + } + + c->gotheader = 1; + *s = 0; /* terminate at end of header */ + if (keepalive && + (strstr(c->cbuff, "Keep-Alive") + || strstr(c->cbuff, "keep-alive"))) { /* for benefit of MSIIS */ + char *cl; + cl = strstr(c->cbuff, "Content-Length:"); + /* handle NCSA, which sends Content-length: */ + if (!cl) + cl = strstr(c->cbuff, "Content-length:"); + if (cl) { + c->keepalive = 1; + c->length = atoi(cl + 16); + } + } + c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy; + totalbread += c->bread; + } + } + else { + /* outside header, everything we have read is entity body */ + c->bread += r; + totalbread += r; + } + + if (c->keepalive && (c->bread >= c->length)) { + /* finished a keep-alive connection */ + good++; + doneka++; + /* save out time */ + if (good == 1) { + /* first time here */ + doclen = c->bread; + } + else if (c->bread != doclen) { + bad++; + err_length++; + } + if (done < requests) { + struct data s; + gettimeofday(&c->done, 0); + s.read = c->read; + s.ctime = timedif(c->connect, c->start); + s.time = timedif(c->done, c->start); + stats[done++] = s; + } + c->keepalive = 0; + c->length = 0; + c->gotheader = 0; + c->cbx = 0; + c->read = c->bread = 0; + write_request(c); + c->start = c->connect; /* zero connect time with keep-alive */ + } +} + +/* --------------------------------------------------------- */ + +/* run the tests */ + +static void test(void) +{ + struct timeval timeout, now; + fd_set sel_read, sel_except, sel_write; + int i; + + if (!use_html) { + printf("Benchmarking %s (be patient)...", hostname); + fflush(stdout); + } + + { + /* get server information */ + struct hostent *he; + he = gethostbyname(hostname); + if (!he) + err("bad hostname"); + server.sin_family = he->h_addrtype; + server.sin_port = htons(port); + server.sin_addr.s_addr = ((unsigned long *) (he->h_addr_list[0]))[0]; + } + + con = malloc(concurrency * sizeof(struct connection)); + memset(con, 0, concurrency * sizeof(struct connection)); + + stats = malloc(requests * sizeof(struct data)); + + FD_ZERO(&readbits); + FD_ZERO(&writebits); + + /* setup request */ + if (!posting) { + sprintf(request, "GET %s HTTP/1.0\r\n" + "User-Agent: ApacheBench/%s\r\n" + "%s" "%s" "%s" + "Host: %s\r\n" + "Accept: */*\r\n" + "\r\n" "%s", + path, + VERSION, + keepalive ? "Connection: Keep-Alive\r\n" : "", + cookie, auth, hostname, hdrs); + } + else { + sprintf(request, "POST %s HTTP/1.0\r\n" + "User-Agent: ApacheBench/%s\r\n" + "%s" "%s" "%s" + "Host: %s\r\n" + "Accept: */*\r\n" + "Content-length: %d\r\n" + "Content-type: %s\r\n" + "%s" + "\r\n", + path, + VERSION, + keepalive ? "Connection: Keep-Alive\r\n" : "", + cookie, auth, + hostname, postlen, + (content_type[0]) ? content_type : "text/plain", hdrs); + } + + if (verbosity >= 2) + printf("INFO: POST header == \n---\n%s\n---\n", request); + + reqlen = strlen(request); + +#ifdef CHARSET_EBCDIC + ebcdic2ascii(request, request, reqlen); +#endif /* CHARSET_EBCDIC */ + + /* ok - lets start */ + gettimeofday(&start, 0); + + /* initialise lots of requests */ + for (i = 0; i < concurrency; i++) + start_connect(&con[i]); + + while (done < requests) { + int n; + /* setup bit arrays */ + memcpy(&sel_except, &readbits, sizeof(readbits)); + memcpy(&sel_read, &readbits, sizeof(readbits)); + memcpy(&sel_write, &writebits, sizeof(readbits)); + + /* check for time limit expiry */ + gettimeofday(&now, 0); + if (tlimit && timedif(now, start) > (tlimit * 1000)) { + requests = done; /* so stats are correct */ + } + + /* Timeout of 30 seconds. */ + timeout.tv_sec = 30; + timeout.tv_usec = 0; + n = ap_select(FD_SETSIZE, &sel_read, &sel_write, &sel_except, &timeout); + if (!n) { + err("\nServer timed out\n\n"); + } + if (n < 1) + err("select"); + + for (i = 0; i < concurrency; i++) { + int s = con[i].fd; + if (FD_ISSET(s, &sel_except)) { + bad++; + err_except++; + start_connect(&con[i]); + continue; + } + if (FD_ISSET(s, &sel_read)) + read_connection(&con[i]); + if (FD_ISSET(s, &sel_write)) + write_request(&con[i]); + } + } + if (use_html) + output_html_results(); + else + output_results(); +} + +/* ------------------------------------------------------- */ + +/* display copyright information */ +static void copyright(void) +{ + if (!use_html) { + printf("This is ApacheBench, Version %s\n", VERSION); + printf("Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n"); + printf("Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/\n"); + printf("\n"); + } + else { + printf("

    \n"); + printf(" This is ApacheBench, Version %s
    \n", VERSION); + printf(" Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    \n"); + printf(" Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/
    \n"); + printf("

    \n

    \n"); + } +} + +/* display usage information */ +static void usage(char *progname) +{ + fprintf(stderr, "Usage: %s [options] [http://]hostname[:port]/path\n", progname); + fprintf(stderr, "Options are:\n"); + fprintf(stderr, " -n requests Number of requests to perform\n"); + fprintf(stderr, " -c concurrency Number of multiple requests to make\n"); + fprintf(stderr, " -t timelimit Seconds to max. wait for responses\n"); + fprintf(stderr, " -p postfile File containg data to POST\n"); + fprintf(stderr, " -T content-type Content-type header for POSTing\n"); + fprintf(stderr, " -v verbosity How much troubleshooting info to print\n"); + fprintf(stderr, " -w Print out results in HTML tables\n"); + fprintf(stderr, " -x attributes String to insert as table attributes\n"); + fprintf(stderr, " -y attributes String to insert as tr attributes\n"); + fprintf(stderr, " -z attributes String to insert as td or th attributes\n"); + fprintf(stderr, " -C attribute Add cookie, eg. 'Apache=1234. (repeatable)\n"); + fprintf(stderr, " -H attribute Add Arbitrary header line, eg. 'Accept-Encoding: zop'\n"); + fprintf(stderr, " Inserted after all normal header lines. (repeatable)\n"); + fprintf(stderr, " -A attribute Add Basic WWW Authentication, the attributes\n"); + fprintf(stderr, " are a colon separated username and password.\n"); + fprintf(stderr, " -p attribute Add Basic Proxy Authentication, the attributes\n"); + fprintf(stderr, " are a colon separated username and password.\n"); + fprintf(stderr, " -V Print version number and exit\n"); + fprintf(stderr, " -k Use HTTP KeepAlive feature\n"); + fprintf(stderr, " -h Display usage information (this message)\n"); + exit(EINVAL); +} + +/* ------------------------------------------------------- */ + +/* split URL into parts */ + +static int parse_url(char *url) +{ + char *cp; + char *h; + char *p = NULL; + + if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0) + url += 7; + h = url; + if ((cp = strchr(url, ':')) != NULL) { + *cp++ = '\0'; + p = cp; + url = cp; + } + if ((cp = strchr(url, '/')) == NULL) + return 1; + strcpy(path, cp); + *cp = '\0'; + strcpy(hostname, h); + if (p != NULL) + port = atoi(p); + return 0; +} + +/* ------------------------------------------------------- */ + +/* read data to POST from file, save contents and length */ + +static int open_postfile(char *pfile) +{ + int postfd, status; + struct stat postfilestat; + + if ((postfd = open(pfile, O_RDONLY)) == -1) { + printf("Invalid postfile name (%s)\n", pfile); + return errno; + } + if ((status = fstat(postfd, &postfilestat)) == -1) { + perror("Can\'t stat postfile\n"); + return status; + } + postdata = malloc(postfilestat.st_size); + if (!postdata) { + printf("Can\'t alloc postfile buffer\n"); + return ENOMEM; + } + if (read(postfd, postdata, postfilestat.st_size) != postfilestat.st_size) { + printf("error reading postfilen"); + return EIO; + } + postlen = postfilestat.st_size; + return 0; +} + +/* ------------------------------------------------------- */ + +extern char *optarg; +extern int optind, opterr, optopt; + +/* sort out command-line args and call test */ +int main(int argc, char **argv) +{ + int c, r,l; + char tmp[1024]; + + /* table defaults */ + tablestring = ""; + trstring = ""; + tdstring = "bgcolor=white"; + cookie[0] = '\0'; + auth[0] = '\0'; + hdrs[0] = '\0'; + optind = 1; + while ((c = getopt(argc, argv, "n:c:t:T:p:v:kVhwx:y:z:C:H:P:A:")) > 0) { + switch (c) { + case 'n': + requests = atoi(optarg); + if (!requests) { + err("Invalid number of requests\n"); + } + break; + case 'k': + keepalive = 1; + break; + case 'c': + concurrency = atoi(optarg); + break; + case 'p': + if (0 == (r = open_postfile(optarg))) { + posting = 1; + } + else if (postdata) { + exit(r); + } + break; + case 'v': + verbosity = atoi(optarg); + break; + case 't': + tlimit = atoi(optarg); + requests = MAX_REQUESTS; /* need to size data array on + * something */ + break; + case 'T': + strcpy(content_type, optarg); + break; + case 'C': + strncat(cookie, "Cookie: ", sizeof(cookie)); + strncat(cookie, optarg, sizeof(cookie)); + strncat(cookie, "\r\n", sizeof(cookie)); + break; + case 'A': + /* assume username passwd already to be in colon separated form. Ready + * to be uu-encoded. + */ + while(isspace(*optarg)) + optarg++; + l=ap_base64encode(tmp,optarg,strlen(optarg)); + tmp[l]='\0'; + + strncat(auth, "Authorization: basic ", sizeof(auth)); + strncat(auth, tmp, sizeof(auth)); + strncat(auth, "\r\n", sizeof(auth)); + break; + case 'P': + /* + * assume username passwd already to be in colon separated form. + */ + while(isspace(*optarg)) + optarg++; + l=ap_base64encode(tmp,optarg,strlen(optarg)); + tmp[l]='\0'; + + strncat(auth, "Proxy-Authorization: basic ", sizeof(auth)); + strncat(auth, tmp, sizeof(auth)); + strncat(auth, "\r\n", sizeof(auth)); + break; + case 'H': + strncat(hdrs, optarg, sizeof(hdrs)); + strncat(hdrs, "\r\n", sizeof(hdrs)); + break; + case 'V': + copyright(); + exit(0); + break; + case 'w': + use_html = 1; + break; + /* + * if any of the following three are used, turn on html output + * automatically + */ + case 'x': + use_html = 1; + tablestring = optarg; + break; + case 'y': + use_html = 1; + trstring = optarg; + break; + case 'z': + use_html = 1; + tdstring = optarg; + break; + case 'h': + usage(argv[0]); + break; + default: + fprintf(stderr, "%s: invalid option `%c'\n", argv[0], c); + usage(argv[0]); + break; + } + } + if (optind != argc - 1) { + fprintf(stderr, "%s: wrong number of arguments\n", argv[0]); + usage(argv[0]); + } + + if (parse_url(argv[optind++])) { + fprintf(stderr, "%s: invalid URL\n", argv[0]); + usage(argv[0]); + } + + copyright(); + test(); + + exit(0); +} diff --git a/support/ab.dsp b/support/ab.dsp new file mode 100644 index 00000000000..d99141d5983 --- /dev/null +++ b/support/ab.dsp @@ -0,0 +1,93 @@ +# Microsoft Developer Studio Project File - Name="ab" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=ab - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ab.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 "ab.mak" CFG="ab - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ab - Win32 Release" (based on\ + "Win32 (x86) Console Application") +!MESSAGE "ab - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ab - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ab" +# PROP BASE Intermediate_Dir "ab" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib advapi32.lib wsock32.lib ws2_32.lib /nologo /subsystem:console /machine:I386 /out:"release/ab.exe" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "ab - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ab0" +# PROP BASE Intermediate_Dir "ab0" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\lib\apr\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib advapi32.lib wsock32.lib ws2_32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"debug/ab.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "ab - Win32 Release" +# Name "ab - Win32 Debug" +# Begin Source File + +SOURCE=.\ab.c +# End Source File +# End Target +# End Project diff --git a/support/apxs.in b/support/apxs.in new file mode 100644 index 00000000000..3bdd35d2b91 --- /dev/null +++ b/support/apxs.in @@ -0,0 +1,655 @@ +#!/usr/local/bin/perl +## ==================================================================== +## Copyright (c) 1998-1999 The Apache Group. All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## +## 2. Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## +## 3. All advertising materials mentioning features or use of this +## software must display the following acknowledgment: +## "This product includes software developed by the Apache Group +## for use in the Apache HTTP server project (http://www.apache.org/)." +## +## 4. The names "Apache Server" and "Apache Group" must not be used to +## endorse or promote products derived from this software without +## prior written permission. For written permission, please contact +## apache@apache.org. +## +## 5. Products derived from this software may not be called "Apache" +## nor may "Apache" appear in their names without prior written +## permission of the Apache Group. +## +## 6. Redistributions of any form whatsoever must retain the following +## acknowledgment: +## "This product includes software developed by the Apache Group +## for use in the Apache HTTP server project (http://www.apache.org/)." +## +## THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +## EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +## ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +## STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +## OF THE POSSIBILITY OF SUCH DAMAGE. +## ==================================================================== +## +## This software consists of voluntary contributions made by many +## individuals on behalf of the Apache Group and was originally based +## on public domain software written at the National Center for +## Supercomputing Applications, University of Illinois, Urbana-Champaign. +## For more information on the Apache Group and the Apache HTTP server +## project, please see . +## + +## +## apxs -- APache eXtenSion tool +## Written by Ralf S. Engelschall +## + +require 5.003; +use strict; +package apxs; + +## +## Configuration +## + +my $CFG_TARGET = '@TARGET@'; # substituted via Makefile.tmpl +my $CFG_CC = '@CC@'; # substituted via Makefile.tmpl +my $CFG_CFLAGS = '@CFLAGS@'; # substituted via Makefile.tmpl +my $CFG_CFLAGS_SHLIB = '@CFLAGS_SHLIB@'; # substituted via Makefile.tmpl +my $CFG_LD_SHLIB = '@LD_SHLIB@'; # substituted via Makefile.tmpl +my $CFG_LDFLAGS_SHLIB = '@LDFLAGS_MOD_SHLIB@'; # substituted via Makefile.tmpl +my $CFG_LIBS_SHLIB = '@LIBS_SHLIB@'; # substituted via Makefile.tmpl +my $CFG_PREFIX = '@prefix@'; # substituted via APACI install +my $CFG_SBINDIR = '@sbindir@'; # substituted via APACI install +my $CFG_INCLUDEDIR = '@includedir@'; # substituted via APACI install +my $CFG_LIBEXECDIR = '@libexecdir@'; # substituted via APACI install +my $CFG_SYSCONFDIR = '@sysconfdir@'; # substituted via APACI install + +## +## Cleanup the above stuff +## +$CFG_CFLAGS =~ s|^\s+||; +$CFG_CFLAGS =~ s|\s+$||; +$CFG_CFLAGS =~ s|\s+`.+apaci`||; + +## +## Initial shared object support check +## +if (not -x "$CFG_SBINDIR/$CFG_TARGET") { + print STDERR "apxs:Error: $CFG_SBINDIR/$CFG_TARGET not found or not executable\n"; + exit(1); +} +if (not grep(/mod_so/, `$CFG_SBINDIR/$CFG_TARGET -l`)) { + print STDERR "apxs:Error: Sorry, no shared object support for Apache\n"; + print STDERR "apxs:Error: available under your platform. Make sure\n"; + print STDERR "apxs:Error: the Apache module mod_so is compiled into\n"; + print STDERR "apxs:Error: your server binary `$CFG_SBINDIR/$CFG_TARGET'.\n"; + exit(1); +} + +## +## parse argument line +## + +# defaults for parameters +my $opt_n = ''; +my $opt_g = ''; +my $opt_c = 0; +my $opt_o = ''; +my @opt_D = (); +my @opt_I = (); +my @opt_L = (); +my @opt_l = (); +my @opt_W = (); +my @opt_S = (); +my $opt_e = 0; +my $opt_i = 0; +my $opt_a = 0; +my $opt_A = 0; +my $opt_q = 0; + +# this subroutine is derived from Perl's getopts.pl with the enhancement of +# the "+" metacharater at the format string to allow a list to be build by +# subsequent occurance of the same option. +sub Getopts { + my ($argumentative, @ARGV) = @_; + my (@args, $first, $rest, $pos); + my ($errs) = 0; + local ($_); + local ($[) = 0; + + @args = split( / */, $argumentative); + while(@ARGV && ($_ = $ARGV[0]) =~ /^-(.)(.*)/) { + ($first, $rest) = ($1,$2); + if ($_ =~ m|^--$|) { + shift(@ARGV); + last; + } + $pos = index($argumentative,$first); + if($pos >= $[) { + if($args[$pos+1] eq ':') { + shift(@ARGV); + if($rest eq '') { + unless (@ARGV) { + print STDERR "apxs:Error: Incomplete option: $first (needs an argument)\n"; + ++$errs; + } + $rest = shift(@ARGV); + } + eval "\$opt_$first = \$rest;"; + } + elsif ($args[$pos+1] eq '+') { + shift(@ARGV); + if($rest eq '') { + unless (@ARGV) { + print STDERR "apxs:Error: Incomplete option: $first (needs an argument)\n"; + ++$errs; + } + $rest = shift(@ARGV); + } + eval "push(\@opt_$first, \$rest);"; + } + else { + eval "\$opt_$first = 1"; + if($rest eq '') { + shift(@ARGV); + } + else { + $ARGV[0] = "-$rest"; + } + } + } + else { + print STDERR "apxs:Error: Unknown option: $first\n"; + ++$errs; + if($rest ne '') { + $ARGV[0] = "-$rest"; + } + else { + shift(@ARGV); + } + } + } + return ($errs == 0, @ARGV); +} + +sub usage { + print STDERR "Usage: apxs -g [-S =] -n \n"; + print STDERR " apxs -q [-S =] ...\n"; + print STDERR " apxs -c [-S =] [-o ] [-D [=]]\n"; + print STDERR " [-I ] [-L ] [-l ] [-Wc,]\n"; + print STDERR " [-Wl,] ...\n"; + print STDERR " apxs -i [-S =] [-a] [-A] [-n ] ...\n"; + print STDERR " apxs -e [-S =] [-a] [-A] [-n ] ...\n"; + exit(1); +} + +# option handling +my $rc; +($rc, @ARGV) = &Getopts("qn:gco:I+D+L+l+W+S+eiaA", @ARGV); +&usage if ($rc == 0); +&usage if ($#ARGV == -1 and not $opt_g); +&usage if (not $opt_q and not ($opt_g and $opt_n) and not $opt_i and not $opt_c and not $opt_e); + +# argument handling +my @args = @ARGV; +my $name = 'unknown'; +$name = $opt_n if ($opt_n ne ''); + +if (@opt_S) { + my ($opt_S); + foreach $opt_S (@opt_S) { + if ($opt_S =~ m/^([^=]+)=(.*)$/) { + my ($var) = $1; + my ($val) = $2; + my $oldval = eval "\$CFG_$var"; + + unless ($var and $oldval) { + print STDERR "apxs:Error: no config variable $var\n"; + &usage; + } + + eval "\$CFG_${var}=\"${val}\""; + } else { + print STDERR "apxs:Error: malformatted -S option\n"; + &usage; + } + } +} + +## +## Operation +## + +# helper function for executing a list of +# system command with return code checks +sub execute_cmds { + my (@cmds) = @_; + my ($cmd, $rc); + + foreach $cmd (@cmds) { + print STDERR "$cmd\n"; + $rc = system("$cmd"); + if ($rc != 0) { + printf(STDERR "apxs:Break: Command failed with rc=%d\n", $rc << 8); + exit(1); + } + } +} + +if ($opt_g) { + ## + ## SAMPLE MODULE SOURCE GENERATION + ## + + if (-d $name) { + print STDERR "apxs:Error: Directory `$name' already exists. Remove first\n"; + exit(1); + } + + my $data = join('', ); + $data =~ s|%NAME%|$name|sg; + $data =~ s|%TARGET%|$CFG_TARGET|sg; + + my ($mkf, $src) = ($data =~ m|^(.+)-=#=-\n(.+)|s); + + print STDERR "Creating [DIR] $name\n"; + system("mkdir $name"); + print STDERR "Creating [FILE] $name/Makefile\n"; + open(FP, ">${name}/Makefile") || die; + print FP $mkf; + close(FP); + print STDERR "Creating [FILE] $name/mod_$name.c\n"; + open(FP, ">${name}/mod_${name}.c") || die; + print FP $src; + close(FP); + + exit(0); +} + + +if ($opt_q) { + ## + ## QUERY INFORMATION + ## + + my $result = ''; + my $arg; + foreach $arg (@args) { + my $ok = 0; + my $name; + foreach $name (qw( + TARGET CC CFLAGS CFLAGS_SHLIB LD_SHLIB LDFLAGS_SHLIB LIBS_SHLIB + PREFIX SBINDIR INCLUDEDIR LIBEXECDIR SYSCONFDIR + )) { + if ($arg eq $name or $arg eq lc($name)) { + my $val = eval "\$CFG_$name"; + $result .= "${val}::"; + $ok = 1; + } + } + if (not $ok) { + printf(STDERR "apxs:Error: Invalid query string `%s'\n", $arg); + exit(1); + } + } + $result =~ s|::$||; + $result =~ s|::| |; + print $result; +} + +if ($opt_c) { + ## + ## SHARED OBJECT COMPILATION + ## + + # split files into sources and objects + my @srcs = (); + my @objs = (); + my $f; + foreach $f (@args) { + if ($f =~ m|\.c$|) { + push(@srcs, $f); + } + else { + push(@objs, $f); + } + } + + # determine output file + my $dso_file; + if ($opt_o eq '') { + if ($#srcs > -1) { + $dso_file = $srcs[0]; + $dso_file =~ s|\.[^.]+$|.so|; + } + elsif ($#objs > -1) { + $dso_file = $objs[0]; + $dso_file =~ s|\.[^.]+$|.so|; + } + else { + $dso_file = "mod_unknown.so"; + } + } + else { + $dso_file = $opt_o; + } + + # create compilation commands + my @cmds = (); + my $opt = ''; + my ($opt_Wc, $opt_I, $opt_D); + foreach $opt_Wc (@opt_W) { + $opt .= "$1 " if ($opt_Wc =~ m|^\s*c,(.*)$|); + } + foreach $opt_I (@opt_I) { + $opt .= "-I$opt_I "; + } + foreach $opt_D (@opt_D) { + $opt .= "-D$opt_D "; + } + my $cflags = "$CFG_CFLAGS $CFG_CFLAGS_SHLIB"; + my $s; + foreach $s (@srcs) { + my $o = $s; + $o =~ s|\.c$|.o|; + push(@cmds, "$CFG_CC $cflags -I$CFG_INCLUDEDIR $opt -c $s"); + unshift(@objs, $o); + } + + # create link command + my $cmd = "$CFG_LD_SHLIB $CFG_LDFLAGS_SHLIB -o $dso_file"; + my $o; + foreach $o (@objs) { + $cmd .= " $o"; + } + $opt = ''; + my ($opt_Wl, $opt_L, $opt_l); + foreach $opt_Wl (@opt_W) { + if($CFG_LD_SHLIB ne "gcc") { + $opt .= " $1" if ($opt_Wl =~ m|^\s*l,(.*)$|); + } else { + $opt .= " -W$opt_Wl"; + } + } + foreach $opt_L (@opt_L) { + $opt .= " -L$opt_L"; + } + foreach $opt_l (@opt_l) { + $opt .= " -l$opt_l"; + } + $cmd .= $opt; + $cmd .= " $CFG_LIBS_SHLIB"; + push(@cmds, $cmd); + + # execute the commands + &execute_cmds(@cmds); + + # allow one-step compilation and installation + if ($opt_i or $opt_e) { + @args = ( $dso_file ); + } +} + +if ($opt_i or $opt_e) { + ## + ## SHARED OBJECT INSTALLATION + ## + + # determine installation commands + # and corresponding LoadModule/AddModule directives + my @lmd = (); + my @amd = (); + my @cmds = (); + my $f; + foreach $f (@args) { + if ($f !~ m|\.so$|) { + print STDERR "apxs:Error: file $f is not a shared object\n"; + exit(1); + } + my $t = $f; + $t =~ s|^.+/([^/]+)$|$1|; + if ($opt_i) { + push(@cmds, "cp $f $CFG_LIBEXECDIR/$t"); + push(@cmds, "chmod 755 $CFG_LIBEXECDIR/$t"); + } + + # determine module symbolname and filename + my $filename = ''; + if ($name eq 'unknown') { + $name = ''; + my $base = $f; + $base =~ s|\.[^.]+$||; + if (-f "$base.c") { + open(FP, "<$base.c"); + my $content = join('', ); + close(FP); + if ($content =~ m|.*module\s+(?:MODULE_VAR_EXPORT\s+)?([a-zA-Z0-9_]+)_module\s*=\s*.*|s) { + $name = "$1"; + $filename = "$base.c"; + $filename =~ s|^[^/]+/||; + } + } + if ($name eq '') { + if ($base =~ m|.*mod_([a-zA-Z0-9_]+)\..+|) { + $name = "$1"; + $filename = $base; + $filename =~ s|^[^/]+/||; + } + } + if ($name eq '') { + print "apxs:Error: Sorry, cannot determine bootstrap symbol name\n"; + print "apxs:Error: Please specify one with option `-n'\n"; + exit(1); + } + } + if ($filename eq '') { + $filename = "mod_${name}.c"; + } + my $dir = $CFG_LIBEXECDIR; + $dir =~ s|^$CFG_PREFIX/?||; + $dir =~ s|(.)$|$1/|; + push(@lmd, sprintf("LoadModule %-18s %s", "${name}_module", "$dir$t")); + push(@amd, sprintf("AddModule %s", $filename)); + } + + # execute the commands + &execute_cmds(@cmds); + + # activate module via LoadModule/AddModule directive + if ($opt_a or $opt_A) { + if (not -f "$CFG_SYSCONFDIR/$CFG_TARGET.conf") { + print "apxs:Error: Config file $CFG_SYSCONFDIR/$CFG_TARGET.conf not found\n"; + exit(1); + } + + open(FP, "<$CFG_SYSCONFDIR/$CFG_TARGET.conf") || die; + my $content = join('', ); + close(FP); + + if ($content !~ m|\n#?\s*LoadModule\s+|) { + print STDERR "apxs:Error: Activation failed for custom $CFG_SYSCONFDIR/$CFG_TARGET.conf file.\n"; + print STDERR "apxs:Error: At least one `LoadModule' directive already has to exist.\n"; + exit(1); + } + + my $lmd; + my $c = ''; + $c = '#' if ($opt_A); + foreach $lmd (@lmd) { + my $what = $opt_A ? "preparing" : "activating"; + if ($content !~ m|\n#?\s*$lmd|) { + $content =~ s|^(.*\n#?\s*LoadModule\s+[^\n]+\n)|$1$c$lmd\n|sg; + } else { + $content =~ s|^(.*\n)#?\s*$lmd[^\n]*\n|$1$c$lmd\n|sg; + } + $lmd =~ m|LoadModule\s+(.+?)_module.*|; + print STDERR "[$what module `$1' in $CFG_SYSCONFDIR/$CFG_TARGET.conf]\n"; + } + my $amd; + foreach $amd (@amd) { + if ($content !~ m|\n#?\s*$amd|) { + $content =~ s|^(.*\n#?\s*AddModule\s+[^\n]+\n)|$1$c$amd\n|sg; + } else { + $content =~ s|^(.*\n)#?\s*$amd[^\n]*\n|$1$c$amd\n|sg; + } + } + if (@lmd or @amd) { + open(FP, ">$CFG_SYSCONFDIR/$CFG_TARGET.conf.new") || die; + print FP $content; + close(FP); + system("cp $CFG_SYSCONFDIR/$CFG_TARGET.conf $CFG_SYSCONFDIR/$CFG_TARGET.conf.bak && " . + "cp $CFG_SYSCONFDIR/$CFG_TARGET.conf.new $CFG_SYSCONFDIR/$CFG_TARGET.conf && " . + "rm $CFG_SYSCONFDIR/$CFG_TARGET.conf.new"); + } + } +} + +##EOF## +__DATA__ +## +## Makefile -- Build procedure for sample %NAME% Apache module +## Autogenerated via ``apxs -n %NAME% -g''. +## + +# the used tools +APXS=apxs +APACHECTL=apachectl + +# additional defines, includes and libraries +#DEF=-Dmy_define=my_value +#INC=-Imy/include/dir +#LIB=-Lmy/lib/dir -lmylib + +# the default target +all: mod_%NAME%.so + +# compile the shared object file +mod_%NAME%.so: mod_%NAME%.c + $(APXS) -c $(DEF) $(INC) $(LIB) mod_%NAME%.c + +# install the shared object file into Apache +install: all + $(APXS) -i -a -n '%NAME%' mod_%NAME%.so + +# cleanup +clean: + -rm -f mod_%NAME%.o mod_%NAME%.so + +# simple test +test: reload + lynx -mime_header http://localhost/%NAME% + +# install and activate shared object by reloading Apache to +# force a reload of the shared object file +reload: install restart + +# the general Apache start/restart/stop +# procedures +start: + $(APACHECTL) start +restart: + $(APACHECTL) restart +stop: + $(APACHECTL) stop + +-=#=- +/* +** mod_%NAME%.c -- Apache sample %NAME% module +** [Autogenerated via ``apxs -n %NAME% -g''] +** +** To play with this sample module first compile it into a +** DSO file and install it into Apache's libexec directory +** by running: +** +** $ apxs -c -i mod_%NAME%.c +** +** Then activate it in Apache's %TARGET%.conf file for instance +** for the URL /%NAME% in as follows: +** +** # %TARGET%.conf +** LoadModule %NAME%_module libexec/mod_%NAME%.so +** +** SetHandler %NAME% +** +** +** Then after restarting Apache via +** +** $ apachectl restart +** +** you immediately can request the URL /%NAME and watch for the +** output of this module. This can be achieved for instance via: +** +** $ lynx -mime_header http://localhost/%NAME% +** +** The output should be similar to the following one: +** +** HTTP/1.1 200 OK +** Date: Tue, 31 Mar 1998 14:42:22 GMT +** Server: Apache/1.3.4 (Unix) +** Connection: close +** Content-Type: text/html +** +** The sample page from mod_%NAME%.c +*/ + +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" +#include "ap_config.h" + +/* The sample content handler */ +static int %NAME%_handler(request_rec *r) +{ + r->content_type = "text/html"; + ap_send_http_header(r); + if (!r->header_only) + ap_rputs("The sample page from mod_%NAME%.c\n", r); + return OK; +} + +/* Dispatch list of content handlers */ +static const handler_rec %NAME%_handlers[] = { + { "%NAME%", %NAME%_handler }, + { NULL, NULL } +}; + +/* Dispatch list for API hooks */ +module MODULE_VAR_EXPORT %NAME%_module = { + STANDARD_MODULE_STUFF, + NULL, /* module initializer */ + NULL, /* create per-dir config structures */ + NULL, /* merge per-dir config structures */ + NULL, /* create per-server config structures */ + NULL, /* merge per-server config structures */ + NULL, /* table of config file commands */ + %NAME%_handlers, /* [#8] MIME-typed-dispatched handlers */ + NULL, /* [#1] URI to filename translation */ + NULL, /* [#4] validate user id from request */ + NULL, /* [#5] check if the user is ok _here_ */ + NULL, /* [#3] check access by host address */ + NULL, /* [#6] determine MIME type */ + NULL, /* [#7] pre-run fixups */ + NULL, /* [#9] log a transaction */ + NULL, /* [#2] header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* [#0] post read-request */ +}; + diff --git a/support/htdigest.c b/support/htdigest.c new file mode 100644 index 00000000000..d1fc41800fd --- /dev/null +++ b/support/htdigest.c @@ -0,0 +1,271 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ +/****************************************************************************** + ****************************************************************************** + * NOTE! This program is not safe as a setuid executable! Do not make it + * setuid! + ****************************************************************************** + *****************************************************************************/ +/* + * htdigest.c: simple program for manipulating digest passwd file for Apache + * + * by Alexei Kosut, based on htpasswd.c, by Rob McCool + */ + +#include "ap_config.h" +#include +#include "ap.h" +#include "ap_md5.h" +#if defined(MPE) || defined(QNX) || defined(WIN32) || defined(__TANDEM) +#include +#else +#include +#endif + +#ifdef WIN32 +#include +#define unlink _unlink +#endif + +#ifdef CHARSET_EBCDIC +#define LF '\n' +#define CR '\r' +#else +#define LF 10 +#define CR 13 +#endif /* CHARSET_EBCDIC */ + +#define MAX_STRING_LEN 256 + +char *tn; + +static void getword(char *word, char *line, char stop) +{ + int x = 0, y; + + for (x = 0; ((line[x]) && (line[x] != stop)); x++) + word[x] = line[x]; + + word[x] = '\0'; + if (line[x]) + ++x; + y = 0; + + while ((line[y++] = line[x++])); +} + +static int getline(char *s, int n, FILE *f) +{ + register int i = 0; + + while (1) { + s[i] = (char) fgetc(f); + + if (s[i] == CR) + s[i] = fgetc(f); + + if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) { + s[i] = '\0'; + return (feof(f) ? 1 : 0); + } + ++i; + } +} + +static void putline(FILE *f, char *l) +{ + int x; + + for (x = 0; l[x]; x++) + fputc(l[x], f); + fputc('\n', f); +} + + +static void add_password(char *user, char *realm, FILE *f) +{ + char *pw; + AP_MD5_CTX context; + unsigned char digest[16]; + char string[MAX_STRING_LEN]; + char pwin[MAX_STRING_LEN]; + char pwv[MAX_STRING_LEN]; + unsigned int i; + + if (ap_getpass("New password: ", pwin, sizeof(pwin)) != 0) { + fprintf(stderr, "password too long"); + exit(5); + } + ap_getpass("Re-type new password: ", pwv, sizeof(pwv)); + if (strcmp(pwin, pwv) != 0) { + fprintf(stderr, "They don't match, sorry.\n"); + if (tn) { + unlink(tn); + } + exit(1); + } + pw = pwin; + fprintf(f, "%s:%s:", user, realm); + + /* Do MD5 stuff */ + sprintf(string, "%s:%s:%s", user, realm, pw); + + ap_MD5Init(&context); + ap_MD5Update(&context, (unsigned char *) string, strlen(string)); + ap_MD5Final(digest, &context); + + for (i = 0; i < 16; i++) + fprintf(f, "%02x", digest[i]); + + fprintf(f, "\n"); +} + +static void usage(void) +{ + fprintf(stderr, "Usage: htdigest [-c] passwordfile realm username\n"); + fprintf(stderr, "The -c flag creates a new file.\n"); + exit(1); +} + +static void interrupted(void) +{ + fprintf(stderr, "Interrupted.\n"); + if (tn) + unlink(tn); + exit(1); +} + +int main(int argc, char *argv[]) +{ + FILE *tfp, *f; + char user[MAX_STRING_LEN]; + char realm[MAX_STRING_LEN]; + char line[MAX_STRING_LEN]; + char l[MAX_STRING_LEN]; + char w[MAX_STRING_LEN]; + char x[MAX_STRING_LEN]; + char command[MAX_STRING_LEN]; + int found; + + tn = NULL; + signal(SIGINT, (void (*)(int)) interrupted); + if (argc == 5) { + if (strcmp(argv[1], "-c")) + usage(); + if (!(tfp = fopen(argv[2], "w"))) { + fprintf(stderr, "Could not open passwd file %s for writing.\n", + argv[2]); + perror("fopen"); + exit(1); + } + printf("Adding password for %s in realm %s.\n", argv[4], argv[3]); + add_password(argv[4], argv[3], tfp); + fclose(tfp); + exit(0); + } + else if (argc != 4) + usage(); + + tn = tmpnam(NULL); + if (!(tfp = fopen(tn, "w"))) { + fprintf(stderr, "Could not open temp file.\n"); + exit(1); + } + + if (!(f = fopen(argv[1], "r"))) { + fprintf(stderr, + "Could not open passwd file %s for reading.\n", argv[1]); + fprintf(stderr, "Use -c option to create new one.\n"); + exit(1); + } + strcpy(user, argv[3]); + strcpy(realm, argv[2]); + + found = 0; + while (!(getline(line, MAX_STRING_LEN, f))) { + if (found || (line[0] == '#') || (!line[0])) { + putline(tfp, line); + continue; + } + strcpy(l, line); + getword(w, l, ':'); + getword(x, l, ':'); + if (strcmp(user, w) || strcmp(realm, x)) { + putline(tfp, line); + continue; + } + else { + printf("Changing password for user %s in realm %s\n", user, realm); + add_password(user, realm, tfp); + found = 1; + } + } + if (!found) { + printf("Adding user %s in realm %s\n", user, realm); + add_password(user, realm, tfp); + } + fclose(f); + fclose(tfp); +#if defined(OS2) || defined(WIN32) + sprintf(command, "copy \"%s\" \"%s\"", tn, argv[1]); +#else + sprintf(command, "cp %s %s", tn, argv[1]); +#endif + system(command); + unlink(tn); + return 0; +} diff --git a/support/htdigest.dsp b/support/htdigest.dsp new file mode 100644 index 00000000000..f00f87e7e74 --- /dev/null +++ b/support/htdigest.dsp @@ -0,0 +1,103 @@ +# Microsoft Developer Studio Project File - Name="htdigest" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=htdigest - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "htdigest.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 "htdigest.mak" CFG="htdigest - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "htdigest - Win32 Release" (based on\ + "Win32 (x86) Console Application") +!MESSAGE "htdigest - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "htdigest - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "htdigest" +# PROP BASE Intermediate_Dir "htdigest" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"release/htdigest.exe" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "htdigest - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "htdiges0" +# PROP BASE Intermediate_Dir "htdiges0" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"debug/htdigest.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "htdigest - Win32 Release" +# Name "htdigest - Win32 Debug" +# Begin Source File + +SOURCE=..\ap\ap_cpystrn.c +# End Source File +# Begin Source File + +SOURCE=..\ap\ap_getpass.c +# End Source File +# Begin Source File + +SOURCE=..\ap\ap_md5c.c +# End Source File +# Begin Source File + +SOURCE=.\htdigest.c +# End Source File +# End Target +# End Project diff --git a/support/htpasswd.c b/support/htpasswd.c new file mode 100644 index 00000000000..21a24e0c0a3 --- /dev/null +++ b/support/htpasswd.c @@ -0,0 +1,577 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/****************************************************************************** + ****************************************************************************** + * NOTE! This program is not safe as a setuid executable! Do not make it + * setuid! + ****************************************************************************** + *****************************************************************************/ +/* + * htpasswd.c: simple program for manipulating password file for + * the Apache HTTP server + * + * Originally by Rob McCool + * + * Exit values: + * 0: Success + * 1: Failure; file access/permission problem + * 2: Failure; command line syntax problem (usage message issued) + * 3: Failure; password verification failure + * 4: Failure; operation interrupted (such as with CTRL/C) + * 5: Failure; buffer would overflow (username, filename, or computed + * record too long) + * 6: Failure; username contains illegal or reserved characters + */ + +#include "ap_config.h" +#include +#include +#include +#include "ap.h" +#include "ap_md5.h" +#include "ap_sha1.h" + +#ifdef WIN32 +#include +#include "../os/win32/getopt.h" +#define unlink _unlink +#endif + +#ifndef CHARSET_EBCDIC +#define LF 10 +#define CR 13 +#else /*CHARSET_EBCDIC*/ +#define LF '\n' +#define CR '\r' +#endif /*CHARSET_EBCDIC*/ + +#define MAX_STRING_LEN 256 +#define ALG_PLAIN 0 +#define ALG_CRYPT 1 +#define ALG_APMD5 2 +#define ALG_APSHA 3 + +#define ERR_FILEPERM 1 +#define ERR_SYNTAX 2 +#define ERR_PWMISMATCH 3 +#define ERR_INTERRUPTED 4 +#define ERR_OVERFLOW 5 +#define ERR_BADUSER 6 + +/* + * This needs to be declared statically so the signal handler can + * access it. + */ +static char *tempfilename; + +/* + * Get a line of input from the user, not including any terminating + * newline. + */ +static int getline(char *s, int n, FILE *f) +{ + register int i = 0; + + while (1) { + s[i] = (char) fgetc(f); + + if (s[i] == CR) { + s[i] = fgetc(f); + } + + if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) { + s[i] = '\0'; + return (feof(f) ? 1 : 0); + } + ++i; + } +} + +static void putline(FILE *f, char *l) +{ + int x; + + for (x = 0; l[x]; x++) { + fputc(l[x], f); + } + fputc('\n', f); +} + +/* + * Make a password record from the given information. A zero return + * indicates success; failure means that the output buffer contains an + * error message instead. + */ +static int mkrecord(char *user, char *record, size_t rlen, char *passwd, + int alg) +{ + char *pw; + char cpw[120]; + char pwin[MAX_STRING_LEN]; + char pwv[MAX_STRING_LEN]; + char salt[9]; + + if (passwd != NULL) { + pw = passwd; + } + else { + if (ap_getpass("New password: ", pwin, sizeof(pwin)) != 0) { + ap_snprintf(record, (rlen - 1), "password too long (>%d)", + sizeof(pwin) - 1); + return ERR_OVERFLOW; + } + ap_getpass("Re-type new password: ", pwv, sizeof(pwv)); + if (strcmp(pwin, pwv) != 0) { + ap_cpystrn(record, "password verification error", (rlen - 1)); + return ERR_PWMISMATCH; + } + pw = pwin; + memset(pwv, '\0', sizeof(pwin)); + } + switch (alg) { + + case ALG_APSHA: + /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */ + ap_sha1_base64(pw,strlen(pw),cpw); + break; + + case ALG_APMD5: + (void) srand((int) time((time_t *) NULL)); + ap_to64(&salt[0], rand(), 8); + salt[8] = '\0'; + + ap_MD5Encode((const unsigned char *)pw, (const unsigned char *)salt, + cpw, sizeof(cpw)); + break; + + case ALG_PLAIN: + /* XXX this len limitation is not in sync with any HTTPd len. */ + ap_cpystrn(cpw,pw,sizeof(cpw)); + break; + + case ALG_CRYPT: + default: + (void) srand((int) time((time_t *) NULL)); + ap_to64(&salt[0], rand(), 8); + salt[8] = '\0'; + + ap_cpystrn(cpw, (char *)crypt(pw, salt), sizeof(cpw) - 1); + break; + } + memset(pw, '\0', strlen(pw)); + + /* + * Check to see if the buffer is large enough to hold the username, + * hash, and delimiters. + */ + if ((strlen(user) + 1 + strlen(cpw)) > (rlen - 1)) { + ap_cpystrn(record, "resultant record too long", (rlen - 1)); + return ERR_OVERFLOW; + } + strcpy(record, user); + strcat(record, ":"); + strcat(record, cpw); + return 0; +} + +static int usage(void) +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\thtpasswd [-cmdps] passwordfile username\n"); + fprintf(stderr, "\thtpasswd -b[cmdps] passwordfile username password\n\n"); + fprintf(stderr, " -c Create a new file.\n"); + fprintf(stderr, " -m Force MD5 encryption of the password" +#if defined(WIN32) || defined(TPF) + " (default)" +#endif + ".\n"); + fprintf(stderr, " -d Force CRYPT encryption of the password" +#if (!(defined(WIN32) || defined(TPF))) + " (default)" +#endif + ".\n"); + fprintf(stderr, " -p Do not encrypt the password (plaintext).\n"); + fprintf(stderr, " -s Force SHA encryption of the password.\n"); + fprintf(stderr, " -b Use the password from the command line rather " + "than prompting for it.\n"); + fprintf(stderr, + "On Windows and TPF systems the '-m' flag is used by default.\n"); + fprintf(stderr, + "On all other systems, the '-p' flag will probably not work.\n"); + return ERR_SYNTAX; +} + +static void interrupted(void) +{ + fprintf(stderr, "Interrupted.\n"); + if (tempfilename != NULL) { + unlink(tempfilename); + } + exit(ERR_INTERRUPTED); +} + +/* + * Check to see if the specified file can be opened for the given + * access. + */ +static int accessible(char *fname, char *mode) +{ + FILE *s; + + s = fopen(fname, mode); + if (s == NULL) { + return 0; + } + fclose(s); + return 1; +} + +/* + * Return true if a file is readable. + */ +static int readable(char *fname) +{ + return accessible(fname, "r"); +} + +/* + * Return true if the specified file can be opened for write access. + */ +static int writable(char *fname) +{ + return accessible(fname, "a"); +} + +/* + * Return true if the named file exists, regardless of permissions. + */ +static int exists(char *fname) +{ +#ifdef WIN32 + struct _stat sbuf; +#else + struct stat sbuf; +#endif + int check; + +#ifdef WIN32 + check = _stat(fname, &sbuf); +#else + check = stat(fname, &sbuf); +#endif + return ((check == -1) && (errno == ENOENT)) ? 0 : 1; +} + +/* + * Copy from the current position of one file to the current position + * of another. + */ +static void copy_file(FILE *target, FILE *source) +{ + static char line[MAX_STRING_LEN]; + + while (fgets(line, sizeof(line), source) != NULL) { + fputs(line, target); + } +} + +/* + * Let's do it. We end up doing a lot of file opening and closing, + * but what do we care? This application isn't run constantly. + */ +int main(int argc, char *argv[]) +{ + FILE *ftemp = NULL; + FILE *fpw = NULL; + char user[MAX_STRING_LEN]; + char password[MAX_STRING_LEN]; + char record[MAX_STRING_LEN]; + char line[MAX_STRING_LEN]; + char pwfilename[MAX_STRING_LEN]; + char *arg; + int found = 0; + int alg = ALG_CRYPT; + int newfile = 0; + int noninteractive = 0; + int i; + int args_left = 2; + + tempfilename = NULL; + signal(SIGINT, (void (*)(int)) interrupted); + + /* + * Preliminary check to make sure they provided at least + * three arguments, we'll do better argument checking as + * we parse the command line. + */ + if (argc < 3) { + return usage(); + } + + /* + * Go through the argument list and pick out any options. They + * have to precede any other arguments. + */ + for (i = 1; i < argc; i++) { + arg = argv[i]; + if (*arg != '-') { + break; + } + while (*++arg != '\0') { + if (*arg == 'c') { + newfile++; + } + else if (*arg == 'm') { + alg = ALG_APMD5; + } + else if (*arg == 's') { + alg = ALG_APSHA; + } + else if (*arg == 'p') { + alg = ALG_PLAIN; + } + else if (*arg == 'd') { + alg = ALG_CRYPT; + } + else if (*arg == 'b') { + noninteractive++; + args_left++; + } + else { + return usage(); + } + } + } + + /* + * Make sure we still have exactly the right number of arguments left + * (the filename, the username, and possibly the password if -b was + * specified). + */ + if ((argc - i) != args_left) { + return usage(); + } + if (strlen(argv[i]) > (sizeof(pwfilename) - 1)) { + fprintf(stderr, "%s: filename too long\n", argv[0]); + return ERR_OVERFLOW; + } + strcpy(pwfilename, argv[i]); + if (strlen(argv[i + 1]) > (sizeof(user) - 1)) { + fprintf(stderr, "%s: username too long (>%d)\n", argv[0], + sizeof(user) - 1); + return ERR_OVERFLOW; + } + strcpy(user, argv[i + 1]); + if ((arg = strchr(user, ':')) != NULL) { + fprintf(stderr, "%s: username contains illegal character '%c'\n", + argv[0], *arg); + return ERR_BADUSER; + } + if (noninteractive) { + if (strlen(argv[i + 2]) > (sizeof(password) - 1)) { + fprintf(stderr, "%s: password too long (>%d)\n", argv[0], + sizeof(password) - 1); + return ERR_OVERFLOW; + } + strcpy(password, argv[i + 2]); + } + +#ifdef WIN32 + if (alg == ALG_CRYPT) { + alg = ALG_APMD5; + fprintf(stderr, "Automatically using MD5 format on Windows.\n"); + } +#endif + +#if (!(defined(WIN32) || defined(TPF))) + if (alg == ALG_PLAIN) { + fprintf(stderr,"Warning: storing passwords as plain text might " + "just not work on this platform.\n"); + } +#endif + /* + * Verify that the file exists if -c was omitted. We give a special + * message if it doesn't. + */ + if ((! newfile) && (! exists(pwfilename))) { + fprintf(stderr, "%s: cannot modify file %s; use '-c' to create it\n", + argv[0], pwfilename); + perror("fopen"); + exit(ERR_FILEPERM); + } + /* + * Verify that we can read the existing file in the case of an update + * to it (rather than creation of a new one). + */ + if ((! newfile) && (! readable(pwfilename))) { + fprintf(stderr, "%s: cannot open file %s for read access\n", + argv[0], pwfilename); + perror("fopen"); + exit(ERR_FILEPERM); + } + /* + * Now check to see if we can preserve an existing file in case + * of password verification errors on a -c operation. + */ + if (newfile && exists(pwfilename) && (! readable(pwfilename))) { + fprintf(stderr, "%s: cannot open file %s for read access\n" + "%s: existing auth data would be lost on password mismatch", + argv[0], pwfilename, argv[0]); + perror("fopen"); + exit(ERR_FILEPERM); + } + /* + * Now verify that the file is writable! + */ + if (! writable(pwfilename)) { + fprintf(stderr, "%s: cannot open file %s for write access\n", + argv[0], pwfilename); + perror("fopen"); + exit(ERR_FILEPERM); + } + + /* + * All the file access checks have been made. Time to go to work; + * try to create the record for the username in question. If that + * fails, there's no need to waste any time on file manipulations. + * Any error message text is returned in the record buffer, since + * the mkrecord() routine doesn't have access to argv[]. + */ + i = mkrecord(user, record, sizeof(record) - 1, + noninteractive ? password : NULL, + alg); + if (i != 0) { + fprintf(stderr, "%s: %s\n", argv[0], record); + exit(i); + } + + /* + * We can access the files the right way, and we have a record + * to add or update. Let's do it.. + */ + tempfilename = tmpnam(NULL); + ftemp = fopen(tempfilename, "w+"); + if (ftemp == NULL) { + fprintf(stderr, "%s: unable to create temporary file\n", argv[0]); + perror("fopen"); + exit(ERR_FILEPERM); + } + /* + * If we're not creating a new file, copy records from the existing + * one to the temporary file until we find the specified user. + */ + if (! newfile) { + char scratch[MAX_STRING_LEN]; + + fpw = fopen(pwfilename, "r"); + while (! (getline(line, sizeof(line), fpw))) { + char *colon; + + if ((line[0] == '#') || (line[0] == '\0')) { + putline(ftemp, line); + continue; + } + strcpy(scratch, line); + /* + * See if this is our user. + */ + colon = strchr(scratch, ':'); + if (colon != NULL) { + *colon = '\0'; + } + if (strcmp(user, scratch) != 0) { + putline(ftemp, line); + continue; + } + found++; + break; + } + } + if (found) { + fprintf(stderr, "Updating "); + } + else { + fprintf(stderr, "Adding "); + } + fprintf(stderr, "password for user %s\n", user); + /* + * Now add the user record we created. + */ + putline(ftemp, record); + /* + * If we're updating an existing file, there may be additional + * records beyond the one we're updating, so copy them. + */ + if (! newfile) { + copy_file(ftemp, fpw); + fclose(fpw); + } + /* + * The temporary file now contains the information that should be + * in the actual password file. Close the open files, re-open them + * in the appropriate mode, and copy them file to the real one. + */ + fclose(ftemp); + fpw = fopen(pwfilename, "w+"); + ftemp = fopen(tempfilename, "r"); + copy_file(fpw, ftemp); + fclose(fpw); + fclose(ftemp); + unlink(tempfilename); + return 0; +} diff --git a/support/htpasswd.dsp b/support/htpasswd.dsp new file mode 100644 index 00000000000..7949d7f7e82 --- /dev/null +++ b/support/htpasswd.dsp @@ -0,0 +1,119 @@ +# Microsoft Developer Studio Project File - Name="htpasswd" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=htpasswd - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "htpasswd.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 "htpasswd.mak" CFG="htpasswd - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "htpasswd - Win32 Release" (based on\ + "Win32 (x86) Console Application") +!MESSAGE "htpasswd - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "htpasswd - 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 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "htpasswd - 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 /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "htpasswd - Win32 Release" +# Name "htpasswd - Win32 Debug" +# Begin Source File + +SOURCE=..\ap\ap_cpystrn.c +# End Source File +# Begin Source File + +SOURCE=..\ap\ap_getpass.c +# End Source File +# Begin Source File + +SOURCE=..\ap\ap_md5c.c +# End Source File +# Begin Source File + +SOURCE=..\ap\ap_snprintf.c +# End Source File +# Begin Source File + +SOURCE=..\ap\ap_sha1.c +# End Source File +# Begin Source File + +SOURCE=..\ap\ap_checkpass.c +# End Source File +# Begin Source File + +SOURCE=..\ap\ap_base64.c +# End Source File +# Begin Source File + +SOURCE=.\htpasswd.c +# End Source File +# End Target +# End Project diff --git a/support/logresolve.c b/support/logresolve.c new file mode 100644 index 00000000000..bcdc6e51b56 --- /dev/null +++ b/support/logresolve.c @@ -0,0 +1,345 @@ +/* + * logresolve 1.1 + * + * Tom Rathborne - tomr@uunet.ca - http://www.uunet.ca/~tomr/ + * UUNET Canada, April 16, 1995 + * + * Rewritten by David Robinson. (drtr@ast.cam.ac.uk) + * + * Usage: logresolve [-s filename] [-c] < access_log > new_log + * + * Arguments: + * -s filename name of a file to record statistics + * -c check the DNS for a matching A record for the host. + * + * Notes: + * + * To generate meaningful statistics from an HTTPD log file, it's good + * to have the domain name of each machine that accessed your site, but + * doing this on the fly can slow HTTPD down. + * + * Compiling NCSA HTTPD with the -DMINIMAL_DNS flag turns IP#->hostname + * resolution off. Before running your stats program, just run your log + * file through this program (logresolve) and all of your IP numbers will + * be resolved into hostnames (where possible). + * + * logresolve takes an HTTPD access log (in the COMMON log file format, + * or any other format that has the IP number/domain name as the first + * field for that matter), and outputs the same file with all of the + * domain names looked up. Where no domain name can be found, the IP + * number is left in. + * + * To minimize impact on your nameserver, logresolve has its very own + * internal hash-table cache. This means that each IP number will only + * be looked up the first time it is found in the log file. + * + * The -c option causes logresolve to apply the same check as httpd + * compiled with -DMAXIMUM_DNS; after finding the hostname from the IP + * address, it looks up the IP addresses for the hostname and checks + * that one of these matches the original address. + */ + +#include "ap_config.h" +#include + +#include + +#ifndef MPE +#include +#endif + +static void cgethost(struct in_addr ipnum, char *string, int check); +static int getline(char *s, int n); +static void stats(FILE *output); + + +/* maximum line length */ +#define MAXLINE 1024 + +/* maximum length of a domain name */ +#ifndef MAXDNAME +#define MAXDNAME 256 +#endif + +/* number of buckets in cache hash table */ +#define BUCKETS 256 + +#if defined(NEED_STRDUP) +char *strdup (const char *str) +{ + char *dup; + + if (!(dup = (char *) malloc(strlen(str) + 1))) + return NULL; + dup = strcpy(dup, str); + + return dup; +} +#endif + +/* + * struct nsrec - record of nameservice for cache linked list + * + * ipnum - IP number hostname - hostname noname - nonzero if IP number has no + * hostname, i.e. hostname=IP number + */ + +struct nsrec { + struct in_addr ipnum; + char *hostname; + int noname; + struct nsrec *next; +} *nscache[BUCKETS]; + +/* + * statistics - obvious + */ + +#ifndef h_errno +extern int h_errno; /* some machines don't have this in their headers */ +#endif + +/* largeste value for h_errno */ +#define MAX_ERR (NO_ADDRESS) +#define UNKNOWN_ERR (MAX_ERR+1) +#define NO_REVERSE (MAX_ERR+2) + +static int cachehits = 0; +static int cachesize = 0; +static int entries = 0; +static int resolves = 0; +static int withname = 0; +static int errors[MAX_ERR + 3]; + +/* + * cgethost - gets hostname by IP address, caching, and adding unresolvable + * IP numbers with their IP number as hostname, setting noname flag + */ + +static void cgethost (struct in_addr ipnum, char *string, int check) +{ + struct nsrec **current, *new; + struct hostent *hostdata; + char *name; + + current = &nscache[((ipnum.s_addr + (ipnum.s_addr >> 8) + + (ipnum.s_addr >> 16) + (ipnum.s_addr >> 24)) % BUCKETS)]; + + while (*current != NULL && ipnum.s_addr != (*current)->ipnum.s_addr) + current = &(*current)->next; + + if (*current == NULL) { + cachesize++; + new = (struct nsrec *) malloc(sizeof(struct nsrec)); + if (new == NULL) { + perror("malloc"); + fprintf(stderr, "Insufficient memory\n"); + exit(1); + } + *current = new; + new->next = NULL; + + new->ipnum = ipnum; + + hostdata = gethostbyaddr((const char *) &ipnum, sizeof(struct in_addr), + AF_INET); + if (hostdata == NULL) { + if (h_errno > MAX_ERR) + errors[UNKNOWN_ERR]++; + else + errors[h_errno]++; + new->noname = h_errno; + name = strdup(inet_ntoa(ipnum)); + } + else { + new->noname = 0; + name = strdup(hostdata->h_name); + if (check) { + if (name == NULL) { + perror("strdup"); + fprintf(stderr, "Insufficient memory\n"); + exit(1); + } + hostdata = gethostbyname(name); + if (hostdata != NULL) { + char **hptr; + + for (hptr = hostdata->h_addr_list; *hptr != NULL; hptr++) + if (((struct in_addr *) (*hptr))->s_addr == ipnum.s_addr) + break; + if (*hptr == NULL) + hostdata = NULL; + } + if (hostdata == NULL) { + fprintf(stderr, "Bad host: %s != %s\n", name, + inet_ntoa(ipnum)); + new->noname = NO_REVERSE; + free(name); + name = strdup(inet_ntoa(ipnum)); + errors[NO_REVERSE]++; + } + } + } + new->hostname = name; + if (new->hostname == NULL) { + perror("strdup"); + fprintf(stderr, "Insufficient memory\n"); + exit(1); + } + } + else + cachehits++; + + /* size of string == MAXDNAME +1 */ + strncpy(string, (*current)->hostname, MAXDNAME); + string[MAXDNAME] = '\0'; +} + +/* + * prints various statistics to output + */ + +static void stats (FILE *output) +{ + int i; + char *ipstring; + struct nsrec *current; + char *errstring[MAX_ERR + 3]; + + for (i = 0; i < MAX_ERR + 3; i++) + errstring[i] = "Unknown error"; + errstring[HOST_NOT_FOUND] = "Host not found"; + errstring[TRY_AGAIN] = "Try again"; + errstring[NO_RECOVERY] = "Non recoverable error"; + errstring[NO_DATA] = "No data record"; + errstring[NO_ADDRESS] = "No address"; + errstring[NO_REVERSE] = "No reverse entry"; + + fprintf(output, "logresolve Statistics:\n"); + + fprintf(output, "Entries: %d\n", entries); + fprintf(output, " With name : %d\n", withname); + fprintf(output, " Resolves : %d\n", resolves); + if (errors[HOST_NOT_FOUND]) + fprintf(output, " - Not found : %d\n", errors[HOST_NOT_FOUND]); + if (errors[TRY_AGAIN]) + fprintf(output, " - Try again : %d\n", errors[TRY_AGAIN]); + if (errors[NO_DATA]) + fprintf(output, " - No data : %d\n", errors[NO_DATA]); + if (errors[NO_ADDRESS]) + fprintf(output, " - No address: %d\n", errors[NO_ADDRESS]); + if (errors[NO_REVERSE]) + fprintf(output, " - No reverse: %d\n", errors[NO_REVERSE]); + fprintf(output, "Cache hits : %d\n", cachehits); + fprintf(output, "Cache size : %d\n", cachesize); + fprintf(output, "Cache buckets : IP number * hostname\n"); + + for (i = 0; i < BUCKETS; i++) + for (current = nscache[i]; current != NULL; current = current->next) { + ipstring = inet_ntoa(current->ipnum); + if (current->noname == 0) + fprintf(output, " %3d %15s - %s\n", i, ipstring, + current->hostname); + else { + if (current->noname > MAX_ERR + 2) + fprintf(output, " %3d %15s : Unknown error\n", i, + ipstring); + else + fprintf(output, " %3d %15s : %s\n", i, ipstring, + errstring[current->noname]); + } + } +} + + +/* + * gets a line from stdin + */ + +static int getline (char *s, int n) +{ + char *cp; + + if (!fgets(s, n, stdin)) + return (0); + cp = strchr(s, '\n'); + if (cp) + *cp = '\0'; + return (1); +} + +int main (int argc, char *argv[]) +{ + struct in_addr ipnum; + char *bar, hoststring[MAXDNAME + 1], line[MAXLINE], *statfile; + int i, check; + + check = 0; + statfile = NULL; + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-c") == 0) + check = 1; + else if (strcmp(argv[i], "-s") == 0) { + if (i == argc - 1) { + fprintf(stderr, "logresolve: missing filename to -s\n"); + exit(1); + } + i++; + statfile = argv[i]; + } + else { + fprintf(stderr, "Usage: logresolve [-s statfile] [-c] < input > output\n"); + exit(0); + } + } + + + for (i = 0; i < BUCKETS; i++) + nscache[i] = NULL; + for (i = 0; i < MAX_ERR + 2; i++) + errors[i] = 0; + + while (getline(line, MAXLINE)) { + if (line[0] == '\0') + continue; + entries++; + if (!isdigit(line[0])) { /* short cut */ + puts(line); + withname++; + continue; + } + bar = strchr(line, ' '); + if (bar != NULL) + *bar = '\0'; + ipnum.s_addr = inet_addr(line); + if (ipnum.s_addr == 0xffffffffu) { + if (bar != NULL) + *bar = ' '; + puts(line); + withname++; + continue; + } + + resolves++; + + cgethost(ipnum, hoststring, check); + if (bar != NULL) + printf("%s %s\n", hoststring, bar + 1); + else + puts(hoststring); + } + + if (statfile != NULL) { + FILE *fp; + fp = fopen(statfile, "w"); + if (fp == NULL) { + fprintf(stderr, "logresolve: could not open statistics file '%s'\n" + ,statfile); + exit(1); + } + stats(fp); + fclose(fp); + } + + return (0); +} diff --git a/support/logresolve.dsp b/support/logresolve.dsp new file mode 100644 index 00000000000..2f5d87ed8be --- /dev/null +++ b/support/logresolve.dsp @@ -0,0 +1,94 @@ +# Microsoft Developer Studio Project File - Name="logresolve" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=logresolve - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "logresolve.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 "logresolve.mak" CFG="logresolve - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "logresolve - Win32 Release" (based on\ + "Win32 (x86) Console Application") +!MESSAGE "logresolve - Win32 Debug" (based on\ + "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "logresolve - 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 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../include" /I "../lib/apr/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "WIN32_LEAN_AND_MEAN" /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib wsock32.lib ws2_32.lib /nologo /subsystem:console /map /machine:I386 +# SUBTRACT LINK32 /pdb:none /nodefaultlib + +!ELSEIF "$(CFG)" == "logresolve - 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 /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I "../include" /I "../lib/apr/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "WIN32_LEAN_AND_MEAN" /FD /c +# SUBTRACT CPP /Fr +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib wsock32.lib ws2_32.lib /nologo /subsystem:console /map /debug /machine:I386 /nodefaultlib +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "logresolve - Win32 Release" +# Name "logresolve - Win32 Debug" +# Begin Source File + +SOURCE=.\logresolve.c +# End Source File +# End Target +# End Project diff --git a/support/rotatelogs.c b/support/rotatelogs.c new file mode 100644 index 00000000000..dc6bd977296 --- /dev/null +++ b/support/rotatelogs.c @@ -0,0 +1,82 @@ +/* + * Simple program to rotate Apache logs without having to kill the server. + * + * Contributed by Ben Laurie + * + * 12 Mar 1996 + */ + + +#define BUFSIZE 65536 +#define MAX_PATH 1024 + +#include "ap_config.h" +#include +#include +#include + +int main (int argc, char **argv) +{ + char buf[BUFSIZE], buf2[MAX_PATH]; + time_t tLogEnd = 0; + time_t tRotation; + int nLogFD = -1; + int nRead; + char *szLogRoot; + + if (argc != 3) { + fprintf(stderr, + "%s \n\n", + argv[0]); +#ifdef OS2 + fprintf(stderr, + "Add this:\n\nTransferLog \"|%s.exe /some/where 86400\"\n\n", + argv[0]); +#else + fprintf(stderr, + "Add this:\n\nTransferLog \"|%s /some/where 86400\"\n\n", + argv[0]); +#endif + fprintf(stderr, + "to httpd.conf. The generated name will be /some/where.nnnn " + "where nnnn is the\nsystem time at which the log nominally " + "starts (N.B. this time will always be a\nmultiple of the " + "rotation time, so you can synchronize cron scripts with it).\n" + "At the end of each rotation time a new log is started.\n"); + exit(1); + } + + szLogRoot = argv[1]; + tRotation = atoi(argv[2]); + if (tRotation <= 0) { + fprintf(stderr, "Rotation time must be > 0\n"); + exit(6); + } + + for (;;) { + nRead = read(0, buf, sizeof buf); + if (nRead == 0) + exit(3); + if (nRead < 0) + if (errno != EINTR) + exit(4); + if (nLogFD >= 0 && (time(NULL) >= tLogEnd || nRead < 0)) { + close(nLogFD); + nLogFD = -1; + } + if (nLogFD < 0) { + time_t tLogStart = (time(NULL) / tRotation) * tRotation; + sprintf(buf2, "%s.%010d", szLogRoot, (int) tLogStart); + tLogEnd = tLogStart + tRotation; + nLogFD = open(buf2, O_WRONLY | O_CREAT | O_APPEND, 0666); + if (nLogFD < 0) { + perror(buf2); + exit(2); + } + } + if (write(nLogFD, buf, nRead) != nRead) { + perror(buf2); + exit(5); + } + } +} diff --git a/support/rotatelogs.dsp b/support/rotatelogs.dsp new file mode 100644 index 00000000000..3e0209d5ccb --- /dev/null +++ b/support/rotatelogs.dsp @@ -0,0 +1,89 @@ +# Microsoft Developer Studio Project File - Name="rotatelogs" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=rotatelogs - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rotatelogs.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 "rotatelogs.mak" CFG="rotatelogs - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rotatelogs - Win32 Release" (based on\ + "Win32 (x86) Console Application") +!MESSAGE "rotatelogs - Win32 Debug" (based on\ + "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rotatelogs - 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 Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "../include" /I "../lib/apr/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "rotatelogs - 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 Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "../include" /I "../lib/apr/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "rotatelogs - Win32 Release" +# Name "rotatelogs - Win32 Debug" +# Begin Source File + +SOURCE=.\rotatelogs.c +# End Source File +# End Target +# End Project diff --git a/support/suexec.c b/support/suexec.c new file mode 100644 index 00000000000..13f3fcc134d --- /dev/null +++ b/support/suexec.c @@ -0,0 +1,566 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * suexec.c -- "Wrapper" support program for suEXEC behaviour for Apache + * + *********************************************************************** + * + * NOTE! : DO NOT edit this code!!! Unless you know what you are doing, + * editing this code might open up your system in unexpected + * ways to would-be crackers. Every precaution has been taken + * to make this code as safe as possible; alter it at your own + * risk. + * + *********************************************************************** + * + * + */ + +#include "ap_config.h" +#include +#include +#include + +#include + +#include "suexec.h" + +/* + *********************************************************************** + * There is no initgroups() in QNX, so I believe this is safe :-) + * Use cc -osuexec -3 -O -mf -DQNX suexec.c to compile. + * + * May 17, 1997. + * Igor N. Kovalenko -- infoh@mail.wplus.net + *********************************************************************** + */ + +#if defined(NEED_INITGROUPS) +int initgroups(const char *name, gid_t basegid) +{ +/* QNX and MPE do not appear to support supplementary groups. */ + return 0; +} +#endif + +#if defined(PATH_MAX) +#define AP_MAXPATH PATH_MAX +#elif defined(MAXPATHLEN) +#define AP_MAXPATH MAXPATHLEN +#else +#define AP_MAXPATH 8192 +#endif + +#define AP_ENVBUF 256 + +extern char **environ; +static FILE *log = NULL; + +char *safe_env_lst[] = +{ + "AUTH_TYPE", + "CONTENT_LENGTH", + "CONTENT_TYPE", + "DATE_GMT", + "DATE_LOCAL", + "DOCUMENT_NAME", + "DOCUMENT_PATH_INFO", + "DOCUMENT_ROOT", + "DOCUMENT_URI", + "FILEPATH_INFO", + "GATEWAY_INTERFACE", + "LAST_MODIFIED", + "PATH_INFO", + "PATH_TRANSLATED", + "QUERY_STRING", + "QUERY_STRING_UNESCAPED", + "REMOTE_ADDR", + "REMOTE_HOST", + "REMOTE_IDENT", + "REMOTE_PORT", + "REMOTE_USER", + "REDIRECT_QUERY_STRING", + "REDIRECT_STATUS", + "REDIRECT_URL", + "REQUEST_METHOD", + "REQUEST_URI", + "SCRIPT_FILENAME", + "SCRIPT_NAME", + "SCRIPT_URI", + "SCRIPT_URL", + "SERVER_ADMIN", + "SERVER_NAME", + "SERVER_ADDR", + "SERVER_PORT", + "SERVER_PROTOCOL", + "SERVER_SOFTWARE", + "UNIQUE_ID", + "USER_NAME", + "TZ", + NULL +}; + + +static void err_output(const char *fmt, va_list ap) +{ +#ifdef LOG_EXEC + time_t timevar; + struct tm *lt; + + if (!log) { + if ((log = fopen(LOG_EXEC, "a")) == NULL) { + fprintf(stderr, "failed to open log file\n"); + perror("fopen"); + exit(1); + } + } + + time(&timevar); + lt = localtime(&timevar); + + fprintf(log, "[%d-%.2d-%.2d %.2d:%.2d:%.2d]: ", + lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, + lt->tm_hour, lt->tm_min, lt->tm_sec); + + vfprintf(log, fmt, ap); + + fflush(log); +#endif /* LOG_EXEC */ + return; +} + +static void log_err(const char *fmt,...) +{ +#ifdef LOG_EXEC + va_list ap; + + va_start(ap, fmt); + err_output(fmt, ap); + va_end(ap); +#endif /* LOG_EXEC */ + return; +} + +static void clean_env(void) +{ + char pathbuf[512]; + char **cleanenv; + char **ep; + int cidx = 0; + int idx; + + + if ((cleanenv = (char **) calloc(AP_ENVBUF, sizeof(char *))) == NULL) { + log_err("failed to malloc memory for environment\n"); + exit(120); + } + + sprintf(pathbuf, "PATH=%s", SAFE_PATH); + cleanenv[cidx] = strdup(pathbuf); + cidx++; + + for (ep = environ; *ep && cidx < AP_ENVBUF-1; ep++) { + if (!strncmp(*ep, "HTTP_", 5)) { + cleanenv[cidx] = *ep; + cidx++; + } + else { + for (idx = 0; safe_env_lst[idx]; idx++) { + if (!strncmp(*ep, safe_env_lst[idx], + strlen(safe_env_lst[idx]))) { + cleanenv[cidx] = *ep; + cidx++; + break; + } + } + } + } + + cleanenv[cidx] = NULL; + + environ = cleanenv; +} + +int main(int argc, char *argv[]) +{ + int userdir = 0; /* ~userdir flag */ + uid_t uid; /* user information */ + gid_t gid; /* target group placeholder */ + char *target_uname; /* target user name */ + char *target_gname; /* target group name */ + char *target_homedir; /* target home directory */ + char *actual_uname; /* actual user name */ + char *actual_gname; /* actual group name */ + char *prog; /* name of this program */ + char *cmd; /* command to be executed */ + char cwd[AP_MAXPATH]; /* current working directory */ + char dwd[AP_MAXPATH]; /* docroot working directory */ + struct passwd *pw; /* password entry holder */ + struct group *gr; /* group entry holder */ + struct stat dir_info; /* directory info holder */ + struct stat prg_info; /* program info holder */ + + /* + * If there are a proper number of arguments, set + * all of them to variables. Otherwise, error out. + */ + prog = argv[0]; + if (argc < 4) { + log_err("too few arguments\n"); + exit(101); + } + target_uname = argv[1]; + target_gname = argv[2]; + cmd = argv[3]; + + /* + * Check existence/validity of the UID of the user + * running this program. Error out if invalid. + */ + uid = getuid(); + if ((pw = getpwuid(uid)) == NULL) { + log_err("invalid uid: (%ld)\n", uid); + exit(102); + } + + /* + * Check to see if the user running this program + * is the user allowed to do so as defined in + * suexec.h. If not the allowed user, error out. + */ +#ifdef _OSD_POSIX + /* User name comparisons are case insensitive on BS2000/OSD */ + if (strcasecmp(HTTPD_USER, pw->pw_name)) { + log_err("user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER); + exit(103); + } +#else /*_OSD_POSIX*/ + if (strcmp(HTTPD_USER, pw->pw_name)) { + log_err("user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER); + exit(103); + } +#endif /*_OSD_POSIX*/ + + /* + * Check for a leading '/' (absolute path) in the command to be executed, + * or attempts to back up out of the current directory, + * to protect against attacks. If any are + * found, error out. Naughty naughty crackers. + */ + if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3)) + || (strstr(cmd, "/../") != NULL)) { + log_err("invalid command (%s)\n", cmd); + exit(104); + } + + /* + * Check to see if this is a ~userdir request. If + * so, set the flag, and remove the '~' from the + * target username. + */ + if (!strncmp("~", target_uname, 1)) { + target_uname++; + userdir = 1; + } + + /* + * Error out if the target username is invalid. + */ + if ((pw = getpwnam(target_uname)) == NULL) { + log_err("invalid target user name: (%s)\n", target_uname); + exit(105); + } + + /* + * Error out if the target group name is invalid. + */ + if (strspn(target_gname, "1234567890") != strlen(target_gname)) { + if ((gr = getgrnam(target_gname)) == NULL) { + log_err("invalid target group name: (%s)\n", target_gname); + exit(106); + } + gid = gr->gr_gid; + actual_gname = strdup(gr->gr_name); + } + else { + gid = atoi(target_gname); + actual_gname = strdup(target_gname); + } + +#ifdef _OSD_POSIX + /* + * Initialize BS2000 user environment + */ + { + pid_t pid; + int status; + + switch (pid = ufork(target_uname)) + { + case -1: /* Error */ + log_err("failed to setup bs2000 environment for user %s: %s\n", + target_uname, strerror(errno)); + exit(150); + case 0: /* Child */ + break; + default: /* Father */ + while (pid != waitpid(pid, &status, 0)) + ; + /* @@@ FIXME: should we deal with STOP signals as well? */ + if (WIFSIGNALED(status)) + kill (getpid(), WTERMSIG(status)); + exit(WEXITSTATUS(status)); + } + } +#endif /*_OSD_POSIX*/ + + /* + * Save these for later since initgroups will hose the struct + */ + uid = pw->pw_uid; + actual_uname = strdup(pw->pw_name); + target_homedir = strdup(pw->pw_dir); + + /* + * Log the transaction here to be sure we have an open log + * before we setuid(). + */ + log_err("uid: (%s/%s) gid: (%s/%s) cmd: %s\n", + target_uname, actual_uname, + target_gname, actual_gname, + cmd); + + /* + * Error out if attempt is made to execute as root or as + * a UID less than UID_MIN. Tsk tsk. + */ + if ((uid == 0) || (uid < UID_MIN)) { + log_err("cannot run as forbidden uid (%d/%s)\n", uid, cmd); + exit(107); + } + + /* + * Error out if attempt is made to execute as root group + * or as a GID less than GID_MIN. Tsk tsk. + */ + if ((gid == 0) || (gid < GID_MIN)) { + log_err("cannot run as forbidden gid (%d/%s)\n", gid, cmd); + exit(108); + } + + /* + * Change UID/GID here so that the following tests work over NFS. + * + * Initialize the group access list for the target user, + * and setgid() to the target group. If unsuccessful, error out. + */ + if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) { + log_err("failed to setgid (%ld: %s)\n", gid, cmd); + exit(109); + } + + /* + * setuid() to the target user. Error out on fail. + */ + if ((setuid(uid)) != 0) { + log_err("failed to setuid (%ld: %s)\n", uid, cmd); + exit(110); + } + + /* + * Get the current working directory, as well as the proper + * document root (dependant upon whether or not it is a + * ~userdir request). Error out if we cannot get either one, + * or if the current working directory is not in the docroot. + * Use chdir()s and getcwd()s to avoid problems with symlinked + * directories. Yuck. + */ + if (getcwd(cwd, AP_MAXPATH) == NULL) { + log_err("cannot get current working directory\n"); + exit(111); + } + + if (userdir) { + if (((chdir(target_homedir)) != 0) || + ((chdir(USERDIR_SUFFIX)) != 0) || + ((getcwd(dwd, AP_MAXPATH)) == NULL) || + ((chdir(cwd)) != 0)) { + log_err("cannot get docroot information (%s)\n", target_homedir); + exit(112); + } + } + else { + if (((chdir(DOC_ROOT)) != 0) || + ((getcwd(dwd, AP_MAXPATH)) == NULL) || + ((chdir(cwd)) != 0)) { + log_err("cannot get docroot information (%s)\n", DOC_ROOT); + exit(113); + } + } + + if ((strncmp(cwd, dwd, strlen(dwd))) != 0) { + log_err("command not in docroot (%s/%s)\n", cwd, cmd); + exit(114); + } + + /* + * Stat the cwd and verify it is a directory, or error out. + */ + if (((lstat(cwd, &dir_info)) != 0) || !(S_ISDIR(dir_info.st_mode))) { + log_err("cannot stat directory: (%s)\n", cwd); + exit(115); + } + + /* + * Error out if cwd is writable by others. + */ + if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) { + log_err("directory is writable by others: (%s)\n", cwd); + exit(116); + } + + /* + * Error out if we cannot stat the program. + */ + if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) { + log_err("cannot stat program: (%s)\n", cmd); + exit(117); + } + + /* + * Error out if the program is writable by others. + */ + if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) { + log_err("file is writable by others: (%s/%s)\n", cwd, cmd); + exit(118); + } + + /* + * Error out if the file is setuid or setgid. + */ + if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID)) { + log_err("file is either setuid or setgid: (%s/%s)\n", cwd, cmd); + exit(119); + } + + /* + * Error out if the target name/group is different from + * the name/group of the cwd or the program. + */ + if ((uid != dir_info.st_uid) || + (gid != dir_info.st_gid) || + (uid != prg_info.st_uid) || + (gid != prg_info.st_gid)) { + log_err("target uid/gid (%ld/%ld) mismatch " + "with directory (%ld/%ld) or program (%ld/%ld)\n", + uid, gid, + dir_info.st_uid, dir_info.st_gid, + prg_info.st_uid, prg_info.st_gid); + exit(120); + } + /* + * Error out if the program is not executable for the user. + * Otherwise, she won't find any error in the logs except for + * "[error] Premature end of script headers: ..." + */ + if (!(prg_info.st_mode & S_IXUSR)) { + log_err("file has no execute permission: (%s/%s)\n", cwd, cmd); + exit(121); + } + + clean_env(); + + /* + * Be sure to close the log file so the CGI can't + * mess with it. If the exec fails, it will be reopened + * automatically when log_err is called. Note that the log + * might not actually be open if LOG_EXEC isn't defined. + * However, the "log" cell isn't ifdef'd so let's be defensive + * and assume someone might have done something with it + * outside an ifdef'd LOG_EXEC block. + */ + if (log != NULL) { + fclose(log); + log = NULL; + } + + /* + * Execute the command, replacing our image with its own. + */ +#ifdef NEED_HASHBANG_EMUL + /* We need the #! emulation when we want to execute scripts */ + { + extern char **environ; + + ap_execve(cmd, &argv[3], environ); + } +#else /*NEED_HASHBANG_EMUL*/ + execv(cmd, &argv[3]); +#endif /*NEED_HASHBANG_EMUL*/ + + /* + * (I can't help myself...sorry.) + * + * Uh oh. Still here. Where's the kaboom? There was supposed to be an + * EARTH-shattering kaboom! + * + * Oh well, log the failure and error out. + */ + log_err("(%d)%s: exec failed (%s)\n", errno, strerror(errno), cmd); + exit(255); +} diff --git a/support/suexec.h b/support/suexec.h new file mode 100644 index 00000000000..892badd1e0b --- /dev/null +++ b/support/suexec.h @@ -0,0 +1,143 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * suexec.h -- user-definable variables for the suexec wrapper code. + * (See README.configure on how to customize these variables.) + */ + + +#ifndef _SUEXEC_H +#define _SUEXEC_H + +/* + * HTTPD_USER -- Define as the username under which Apache normally + * runs. This is the only user allowed to execute + * this program. + */ +#ifndef HTTPD_USER +#define HTTPD_USER "www" +#endif + +/* + * UID_MIN -- Define this as the lowest UID allowed to be a target user + * for suEXEC. For most systems, 500 or 100 is common. + */ +#ifndef UID_MIN +#define UID_MIN 100 +#endif + +/* + * GID_MIN -- Define this as the lowest GID allowed to be a target group + * for suEXEC. For most systems, 100 is common. + */ +#ifndef GID_MIN +#define GID_MIN 100 +#endif + +/* + * USERDIR_SUFFIX -- Define to be the subdirectory under users' + * home directories where suEXEC access should + * be allowed. All executables under this directory + * will be executable by suEXEC as the user so + * they should be "safe" programs. If you are + * using a "simple" UserDir directive (ie. one + * without a "*" in it) this should be set to + * the same value. suEXEC will not work properly + * in cases where the UserDir directive points to + * a location that is not the same as the user's + * home directory as referenced in the passwd file. + * + * If you have VirtualHosts with a different + * UserDir for each, you will need to define them to + * all reside in one parent directory; then name that + * parent directory here. IF THIS IS NOT DEFINED + * PROPERLY, ~USERDIR CGI REQUESTS WILL NOT WORK! + * See the suEXEC documentation for more detailed + * information. + */ +#ifndef USERDIR_SUFFIX +#define USERDIR_SUFFIX "public_html" +#endif + +/* + * LOG_EXEC -- Define this as a filename if you want all suEXEC + * transactions and errors logged for auditing and + * debugging purposes. + */ +#ifndef LOG_EXEC +#define LOG_EXEC "/usr/local/apache/logs/cgi.log" /* Need me? */ +#endif + +/* + * DOC_ROOT -- Define as the DocumentRoot set for Apache. This + * will be the only hierarchy (aside from UserDirs) + * that can be used for suEXEC behavior. + */ +#ifndef DOC_ROOT +#define DOC_ROOT "/usr/local/apache/htdocs" +#endif + +/* + * SAFE_PATH -- Define a safe PATH environment to pass to CGI executables. + * + */ +#ifndef SAFE_PATH +#define SAFE_PATH "/usr/local/bin:/usr/bin:/bin" +#endif + +#endif /* _SUEXEC_H */ diff --git a/test/.cvsignore b/test/.cvsignore new file mode 100644 index 00000000000..e2244a438e6 --- /dev/null +++ b/test/.cvsignore @@ -0,0 +1,11 @@ +a.out +time-FCNTL +time-FLOCK +time-SYSVSEM +time-SYSVSEM2 +time-PTHREAD +time-USLOCK +zb +test-writev +test_date +test_select diff --git a/test/.indent.pro b/test/.indent.pro new file mode 100644 index 00000000000..a9fbe9f9a1f --- /dev/null +++ b/test/.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/test/README b/test/README new file mode 100644 index 00000000000..9f8be502b88 --- /dev/null +++ b/test/README @@ -0,0 +1,3 @@ +This directory contains useful test code for testing various bits +of Apache functionality. This stuff is for the developers only, +so we might remove it on public releases. diff --git a/test/check_chunked b/test/check_chunked new file mode 100644 index 00000000000..6a121672113 --- /dev/null +++ b/test/check_chunked @@ -0,0 +1,42 @@ +#!/usr/bin/perl -w + +# This is meant to be used on the raw output of an HTTP/1.1 connection +# to check that the chunks are all correctly laid out. It's easiest +# to use a tool like netcat to generate the output. This script +# *insists* that \r exist in the output. +# +# You can find netcat at avian.org:/src/hacks/nc110.tgz. + +use strict; + +my $is_chunked = 0; + +# must toss headers +while(<>) { + if (/^Transfer-Encoding:\s+chunked/i) { + $is_chunked = 1; + } + last if ($_ eq "\r\n"); +} + +$is_chunked || die "wasn't chunked\n"; + +for(;;) { + $_ = <> || die "unexpected end of file!\n"; + + m#^([0-9a-f]+) *\r$#i || die "bogus chunklen: $_"; + + my $chunklen = hex($1); + + exit 0 if ($chunklen == 0); + + chop; chop; + print "$_ "; + + my $data = ''; + read(ARGV, $data, $chunklen) == $chunklen || die "short read!\n"; + + $_ = <> || die "unexpected end of file!\n"; + + $_ eq "\r\n" || die "missing chunk trailer!\n"; +} diff --git a/test/cls.c b/test/cls.c new file mode 100644 index 00000000000..91114a784ca --- /dev/null +++ b/test/cls.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include + +/* + * Compare a string to a mask + * Mask characters: + * @ - uppercase letter + * # - lowercase letter + * & - hex digit + * # - digit + * * - swallow remaining characters + * - exact match for any other character + */ +static int checkmask(const char *data, const char *mask) +{ + int i, ch, d; + + for (i = 0; mask[i] != '\0' && mask[i] != '*'; i++) { + ch = mask[i]; + d = data[i]; + if (ch == '@') { + if (!isupper(d)) + return 0; + } + else if (ch == '$') { + if (!islower(d)) + return 0; + } + else if (ch == '#') { + if (!isdigit(d)) + return 0; + } + else if (ch == '&') { + if (!isxdigit(d)) + return 0; + } + else if (ch != d) + return 0; + } + + if (mask[i] == '*') + return 1; + else + return (data[i] == '\0'); +} + +/* + * Converts 8 hex digits to a time integer + */ +static int hex2sec(const char *x) +{ + int i, ch; + unsigned int j; + + for (i = 0, j = 0; i < 8; i++) { + ch = x[i]; + j <<= 4; + if (isdigit(ch)) + j |= ch - '0'; + else if (isupper(ch)) + j |= ch - ('A' - 10); + else + j |= ch - ('a' - 10); + } + if (j == 0xffffffff) + return -1; /* so that it works with 8-byte ints */ + else + return j; +} + +int main(int argc, char **argv) +{ + int i, ver; + DIR *d; + struct dirent *e; + const char *s; + FILE *fp; + char path[FILENAME_MAX + 1]; + char line[1035]; + time_t date, lmod, expire; + unsigned int len; + struct tm ts; + char sdate[30], slmod[30], sexpire[30]; + const char time_format[] = "%e %b %Y %R"; + + if (argc != 2) { + printf("Usage: cls directory\n"); + exit(0); + } + + d = opendir(argv[1]); + if (d == NULL) { + perror("opendir"); + exit(1); + } + + for (;;) { + e = readdir(d); + if (e == NULL) + break; + s = e->d_name; + if (s[0] == '.' || s[0] == '#') + continue; + sprintf(path, "%s/%s", argv[1], s); + fp = fopen(path, "r"); + if (fp == NULL) { + perror("fopen"); + continue; + } + if (fgets(line, 1034, fp) == NULL) { + perror("fgets"); + fclose(fp); + continue; + } + if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&\n")) { + fprintf(stderr, "Bad cache file\n"); + fclose(fp); + continue; + } + date = hex2sec(line); + lmod = hex2sec(line + 9); + expire = hex2sec(line + 18); + ver = hex2sec(line + 27); + len = hex2sec(line + 35); + if (fgets(line, 1034, fp) == NULL) { + perror("fgets"); + fclose(fp); + continue; + } + fclose(fp); + i = strlen(line); + if (strncmp(line, "X-URL: ", 7) != 0 || line[i - 1] != '\n') { + fprintf(stderr, "Bad cache file\n"); + continue; + } + line[i - 1] = '\0'; + if (date != -1) { + ts = *gmtime(&date); + strftime(sdate, 30, time_format, &ts); + } + else + strcpy(sdate, "-"); + + if (lmod != -1) { + ts = *gmtime(&lmod); + strftime(slmod, 30, time_format, &ts); + } + else + strcpy(slmod, "-"); + + if (expire != -1) { + ts = *gmtime(&expire); + strftime(sexpire, 30, time_format, &ts); + } + else + strcpy(sexpire, "-"); + + printf("%s: %d; %s %s %s\n", line + 7, ver, sdate, slmod, sexpire); + } + + closedir(d); + return 0; +} diff --git a/test/tcpdumpscii.txt b/test/tcpdumpscii.txt new file mode 100644 index 00000000000..9c1060edab2 --- /dev/null +++ b/test/tcpdumpscii.txt @@ -0,0 +1,50 @@ + +From marcs@znep.com Fri Apr 17 15:16:16 1998 +Date: Sat, 22 Nov 1997 20:44:10 -0700 (MST) +From: Marc Slemko +To: TLOSAP +Subject: Re: Getting ethernet packets content under FreeBSD? (fwd) +Reply-To: new-httpd@apache.org + +Anyone too lazy to hack tcpdump (eg. my tcpdump has a -X option to display +the data in ASCII) can use something like the below to grab HTTP headers +when debugging broken clients. + +Nothing complicated, but handy. + +---------- Forwarded message ---------- +Date: Sat, 22 Nov 1997 14:35:23 PST +From: Bill Fenner +To: Nate Williams +Cc: bmah@ca.sandia.gov, hackers@FreeBSD.ORG +Subject: Re: Getting ethernet packets content under FreeBSD? + +I usually just use this perl script, which I call "tcpdumpscii". +Then run "tcpdumpscii -s 1500 -x [other tcpdump args]". + + Bill + +#!/import/misc/bin/perl +# +# +open(TCPDUMP,"tcpdump -l @ARGV|"); +while () { + if (/^\s+(\S\S)+/) { + $sav = $_; + $asc = ""; + while (s/\s*(\S\S)\s*//) { + $i = hex($1); + if ($i < 32 || $i > 126) { + $asc .= "."; + } else { + $asc .= pack(C,hex($1)); + } + } + $foo = "." x length($asc); + $_ = $sav; + s/\t/ /g; + s/^$foo/$asc/; + } + print; +} + diff --git a/test/test-writev.c b/test/test-writev.c new file mode 100644 index 00000000000..852c7017662 --- /dev/null +++ b/test/test-writev.c @@ -0,0 +1,85 @@ +/* + test-writev: use this to figure out if your writev() does intelligent + things on the network. Some writev()s when given multiple buffers + will break them up into multiple packets, which is a waste. + + Linux prior to 2.0.31 has this problem. + + Solaris 2.5, 2.5.1 doesn't appear to, 2.6 hasn't been tested. + + IRIX 5.3 doesn't have this problem. + + To use this you want to snoop the wire with tcpdump, and then run + "test-writev a.b.c.d port#" ... against some TCP service on another + box. For example you can run it against port 80 on another server. + You want to look to see how many data packets are sent, you're hoping + only one of size 300 is sent. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef INADDR_NONE +#define INADDR_NONE (-1ul) +#endif + +void main( int argc, char **argv ) +{ + struct sockaddr_in server_addr; + int s; + struct iovec vector[3]; + char buf[100]; + int i; + const int just_say_no = 1; + + if( argc != 3 ) { +usage: + fprintf( stderr, "usage: test-writev a.b.c.d port#\n" ); + exit( 1 ); + } + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = inet_addr( argv[1] ); + if( server_addr.sin_addr.s_addr == INADDR_NONE ) { + fprintf( stderr, "bogus address\n" ); + goto usage; + } + server_addr.sin_port = htons( atoi( argv[2] ) ); + + s = socket( AF_INET, SOCK_STREAM, 0 ); + if( s < 0 ) { + perror("socket"); + exit(1); + } + if( connect( s, (struct sockaddr *)&server_addr, sizeof( server_addr ) ) + != 0 ) { + perror("connect"); + exit(1); + } + + if( setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&just_say_no, + sizeof(just_say_no)) != 0 ) { + perror( "TCP_NODELAY" ); + exit(1); + } + /* now build up a two part writev and write it out */ + for( i = 0; i < sizeof( buf ); ++i ) { + buf[i] = 'x'; + } + vector[0].iov_base = buf; + vector[0].iov_len = sizeof(buf); + vector[1].iov_base = buf; + vector[1].iov_len = sizeof(buf); + vector[2].iov_base = buf; + vector[2].iov_len = sizeof(buf); + + i = writev( s, &vector[0], 3 ); + fprintf( stdout, "i=%d, errno=%d\n", i, errno ); + exit(0); +} diff --git a/test/test_find.c b/test/test_find.c new file mode 100644 index 00000000000..6f887f50078 --- /dev/null +++ b/test/test_find.c @@ -0,0 +1,62 @@ +/* This program tests the ap_find_list_item routine in ../main/util.c. + * + * The defines in this sample compile line are specific to Roy's system. + * They should match whatever was used to compile Apache first. + * + gcc -g -O2 -I../os/unix -I../include -o test_find \ + -DSOLARIS2=250 -Wall -DALLOC_DEBUG -DPOOL_DEBUG \ + ../main/alloc.o ../main/buff.o ../main/util.o \ + ../ap/libap.a -lsocket -lnsl test_find.c + * + * Roy Fielding, 1999 + */ +#include +#include +#include "httpd.h" +#include "alloc.h" + +/* + * Dummy a bunch of stuff just to get a compile + */ +uid_t ap_user_id; +gid_t ap_group_id; +void *ap_dummy_mutex = &ap_dummy_mutex; +char *ap_server_argv0; + +API_EXPORT(void) ap_block_alarms(void) +{ + ; +} + +API_EXPORT(void) ap_unblock_alarms(void) +{ + ; +} + +API_EXPORT(void) ap_log_error(const char *file, int line, int level, + const request_rec *r, const char *fmt, ...) +{ + ; +} + +int main (void) +{ + ap_pool *p; + char line[512]; + char tok[512]; + + p = ap_init_alloc(); + + printf("Enter field value to find items within:\n"); + if (!gets(line)) + exit(0); + + printf("Enter search item:\n"); + while (gets(tok)) { + printf(" [%s] == %s\n", tok, ap_find_list_item(p, line, tok) + ? "Yes" : "No"); + printf("Enter search item:\n"); + } + + exit(0); +} diff --git a/test/test_limits.c b/test/test_limits.c new file mode 100644 index 00000000000..8d76ce2e083 --- /dev/null +++ b/test/test_limits.c @@ -0,0 +1,200 @@ +/************************************************************** + * test_limits.c + * + * A simple program for sending abusive requests to a server, based + * on the sioux.c exploit code that this nimrod posted (see below). + * Roy added options for testing long header fieldsize (-t h), long + * request-lines (-t r), and a long request body (-t b). + * + * FreeBSD 2.2.x, FreeBSD 3.0, IRIX 5.3, IRIX 6.2: + * gcc -o test_limits test_limits.c + * + * Solaris 2.5.1: + * gcc -o test_limits test_limits.c -lsocket -lnsl + * + * + * Message-ID: <861zqspvtw.fsf@niobe.ewox.org> + * Date: Fri, 7 Aug 1998 19:04:27 +0200 + * Sender: Bugtraq List + * From: Dag-Erling Coidan =?ISO-8859-1?Q?Sm=F8rgrav?= + * Subject: YA Apache DoS attack + * + * Copyright (c) 1998 Dag-Erling Codan Smrgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * Kudos to Mark Huizer who originally suggested this on freebsd-current + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#define TEST_LONG_REQUEST_LINE 1 +#define TEST_LONG_REQUEST_FIELDS 2 +#define TEST_LONG_REQUEST_FIELDSIZE 3 +#define TEST_LONG_REQUEST_BODY 4 + +void +usage(void) +{ + fprintf(stderr, + "usage: test_limits [-t (r|n|h|b)] [-a address] [-p port] [-n num]\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_in sin; + struct hostent *he; + FILE *f; + int o, sd; + + /* default parameters */ + char *addr = "localhost"; + int port = 80; + int num = 1000; + int testtype = TEST_LONG_REQUEST_FIELDS; + + /* get options */ + while ((o = getopt(argc, argv, "t:a:p:n:")) != EOF) + switch (o) { + case 't': + if (*optarg == 'r') + testtype = TEST_LONG_REQUEST_LINE; + else if (*optarg == 'n') + testtype = TEST_LONG_REQUEST_FIELDS; + else if (*optarg == 'h') + testtype = TEST_LONG_REQUEST_FIELDSIZE; + else if (*optarg == 'b') + testtype = TEST_LONG_REQUEST_BODY; + break; + case 'a': + addr = optarg; + break; + case 'p': + port = atoi(optarg); + break; + case 'n': + num = atoi(optarg); + break; + default: + usage(); + } + + if (argc != optind) + usage(); + + /* connect */ + if ((he = gethostbyname(addr)) == NULL) { + perror("gethostbyname"); + exit(1); + } + bzero(&sin, sizeof(sin)); + bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length); + sin.sin_family = he->h_addrtype; + sin.sin_port = htons(port); + + if ((sd = socket(sin.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { + perror("socket"); + exit(1); + } + + if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + perror("connect"); + exit(1); + } + + if ((f = fdopen(sd, "r+")) == NULL) { + perror("fdopen"); + exit(1); + } + + /* attack! */ + fprintf(stderr, "Testing like a plague of locusts on %s\n", addr); + + if (testtype == TEST_LONG_REQUEST_LINE) { + fprintf(f, "GET "); + while (num-- && !ferror(f)) { + fprintf(f, "/123456789"); + fflush(f); + } + fprintf(f, " HTTP/1.0\r\n\r\n"); + } + else { + fprintf(f, "GET /fred/foo HTTP/1.0\r\n"); + + if (testtype == TEST_LONG_REQUEST_FIELDSIZE) { + while (num-- && !ferror(f)) { + fprintf(f, "User-Agent: sioux"); + fflush(f); + } + fprintf(f, "\r\n"); + } + else if (testtype == TEST_LONG_REQUEST_FIELDS) { + while (num-- && !ferror(f)) + fprintf(f, "User-Agent: sioux\r\n"); + fprintf(f, "\r\n"); + } + else if (testtype == TEST_LONG_REQUEST_BODY) { + fprintf(f, "User-Agent: sioux\r\n"); + fprintf(f, "Content-Length: 33554433\r\n"); + fprintf(f, "\r\n"); + while (num-- && !ferror(f)) + fprintf(f, "User-Agent: sioux\r\n"); + } + else { + fprintf(f, "\r\n"); + } + } + fflush(f); + + { + ssize_t len; + char buff[512]; + + while ((len = read(sd, buff, 512)) > 0) + len = write(1, buff, len); + } + if (ferror(f)) { + perror("fprintf"); + exit(1); + } + + fclose(f); + exit(0); +} diff --git a/test/test_parser.c b/test/test_parser.c new file mode 100644 index 00000000000..23e2c21c506 --- /dev/null +++ b/test/test_parser.c @@ -0,0 +1,59 @@ +/* This program tests the ap_get_list_item routine in ../main/util.c. + * + * The defines in this sample compile line are specific to Roy's system. + * They should match whatever was used to compile Apache first. + * + gcc -g -O2 -I../os/unix -I../include -o test_parser \ + -DSOLARIS2=250 -Wall -DALLOC_DEBUG -DPOOL_DEBUG \ + ../main/alloc.o ../main/buff.o ../main/util.o \ + ../ap/libap.a -lsocket -lnsl test_parser.c + * + * Roy Fielding, 1999 + */ +#include +#include +#include "httpd.h" +#include "alloc.h" + +/* + * Dummy a bunch of stuff just to get a compile + */ +uid_t ap_user_id; +gid_t ap_group_id; +void *ap_dummy_mutex = &ap_dummy_mutex; +char *ap_server_argv0; + +API_EXPORT(void) ap_block_alarms(void) +{ + ; +} + +API_EXPORT(void) ap_unblock_alarms(void) +{ + ; +} + +API_EXPORT(void) ap_log_error(const char *file, int line, int level, + const request_rec *r, const char *fmt, ...) +{ + ; +} + +int main (void) +{ + ap_pool *p; + const char *field; + char *newstr; + char instr[512]; + + p = ap_init_alloc(); + + while (gets(instr)) { + printf(" [%s] ==\n", instr); + field = instr; + while ((newstr = ap_get_list_item(p, &field)) != NULL) + printf(" <%s> ..\n", newstr); + } + + exit(0); +} diff --git a/test/test_select.c b/test/test_select.c new file mode 100644 index 00000000000..8c0a6a43634 --- /dev/null +++ b/test/test_select.c @@ -0,0 +1,30 @@ +/* This is just a quick test program to see how long a wait is + * produced by a select loop with an exponential backoff. + * + * gcc -g -O2 -o test_select test_select.c + * test_select + * + * Roy Fielding, 1996 + */ + +#include +#include +#include + +int main (void) +{ + int srv; + long waittime = 4096; + struct timeval tv; + + printf("Start\n"); + while ((waittime > 0) && (waittime < 3000000)) { + printf("%d\n", waittime); + tv.tv_sec = waittime/1000000; + tv.tv_usec = waittime%1000000; + waittime <<= 1; + srv = select(0, NULL, NULL, NULL, &tv); + } + printf("End\n"); + exit(0); +} diff --git a/test/time-sem.c b/test/time-sem.c new file mode 100644 index 00000000000..8bf67c72546 --- /dev/null +++ b/test/time-sem.c @@ -0,0 +1,572 @@ +/* +time-sem.c has the basics of the semaphores we use in http_main.c. It's +intended for timing differences between various methods on an +architecture. In practice we've found many things affect which semaphore +to be used: + + - NFS filesystems absolutely suck for fcntl() and flock() + + - uslock absolutely sucks on single-processor IRIX boxes, but + absolutely rocks on multi-processor boxes. The converse + is true for fcntl. sysvsem seems a moderate balance. + + - Under Solaris you can't have too many processes use SEM_UNDO, there + might be a tuneable somewhere that increases the limit from 29. + We're not sure what the tunable is, so there's a define + NO_SEM_UNDO which can be used to simulate us trapping/blocking + signals to be able to properly release the semaphore on a clean + child death. You'll also need to define NEED_UNION_SEMUN + under solaris. + +You'll need to define USE_SHMGET_SCOREBOARD if anonymous shared mmap() +doesn't work on your system (i.e. linux). + +argv[1] is the #children, argv[2] is the #iterations per child + +You should run each over many different #children inputs, and choose +#iter such that the program runs for at least a second or so... or even +longer depending on your patience. + +compile with: + +gcc -o time-FCNTL -Wall -O time-sem.c -DUSE_FCNTL_SERIALIZED_ACCEPT +gcc -o time-FLOCK -Wall -O time-sem.c -DUSE_FLOCK_SERIALIZED_ACCEPT +gcc -o time-SYSVSEM -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT +gcc -o time-SYSVSEM2 -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT -DNO_SEM_UNDO +gcc -o time-PTHREAD -Wall -O time-sem.c -DUSE_PTHREAD_SERIALIZED_ACCEPT -lpthread +gcc -o time-USLOCK -Wall -O time-sem.c -DUSE_USLOCK_SERIALIZED_ACCEPT + +not all versions work on all systems. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) + +static struct flock lock_it; +static struct flock unlock_it; + +static int fcntl_fd=-1; + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +void +accept_mutex_init(void) +{ + + lock_it.l_whence = SEEK_SET; /* from current point */ + lock_it.l_start = 0; /* -"- */ + lock_it.l_len = 0; /* until end of file */ + lock_it.l_type = F_WRLCK; /* set exclusive/write lock */ + lock_it.l_pid = 0; /* pid not actually interesting */ + unlock_it.l_whence = SEEK_SET; /* from current point */ + unlock_it.l_start = 0; /* -"- */ + unlock_it.l_len = 0; /* until end of file */ + unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */ + unlock_it.l_pid = 0; /* pid not actually interesting */ + + printf("opening test-lock-thing in current directory\n"); + fcntl_fd = open("test-lock-thing", O_CREAT | O_WRONLY | O_EXCL, 0644); + if (fcntl_fd == -1) + { + perror ("open"); + fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing"); + exit (1); + } + unlink("test-lock-thing"); +} + +void accept_mutex_on(void) +{ + int ret; + + while ((ret = fcntl(fcntl_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + perror ("fcntl lock_it"); + exit(1); + } +} + +void accept_mutex_off(void) +{ + if (fcntl (fcntl_fd, F_SETLKW, &unlock_it) < 0) + { + perror ("fcntl unlock_it"); + exit(1); + } +} + +#elif defined(USE_FLOCK_SERIALIZED_ACCEPT) + +#include + +static int flock_fd=-1; + +#define FNAME "test-lock-thing" + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +void accept_mutex_init(void) +{ + + printf("opening " FNAME " in current directory\n"); + flock_fd = open(FNAME, O_CREAT | O_WRONLY | O_EXCL, 0644); + if (flock_fd == -1) + { + perror ("open"); + fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing"); + exit (1); + } +} + +void accept_mutex_child_init(void) +{ + flock_fd = open(FNAME, O_WRONLY, 0600); + if (flock_fd == -1) { + perror("open"); + exit(1); + } +} + +void accept_mutex_cleanup(void) +{ + unlink(FNAME); +} + +void accept_mutex_on(void) +{ + int ret; + + while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + perror ("flock(LOCK_EX)"); + exit(1); + } +} + +void accept_mutex_off(void) +{ + if (flock (flock_fd, LOCK_UN) < 0) + { + perror ("flock(LOCK_UN)"); + exit(1); + } +} + +#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT) + +#include +#include +#include + +static int sem_id = -1; +#ifdef NO_SEM_UNDO +static sigset_t accept_block_mask; +static sigset_t accept_previous_mask; +#endif + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +void accept_mutex_init(void) +{ +#ifdef NEED_UNION_SEMUN + /* believe it or not, you need to define this under solaris */ + union semun { + int val; + struct semid_ds *buf; + ushort *array; + }; +#endif + + union semun ick; + + sem_id = semget(999, 1, IPC_CREAT | 0666); + if (sem_id < 0) { + perror ("semget"); + exit (1); + } + ick.val = 1; + if (semctl(sem_id, 0, SETVAL, ick) < 0) { + perror ("semctl"); + exit(1); + } +#ifdef NO_SEM_UNDO + sigfillset(&accept_block_mask); + sigdelset(&accept_block_mask, SIGHUP); + sigdelset(&accept_block_mask, SIGTERM); + sigdelset(&accept_block_mask, SIGUSR1); +#endif +} + +void accept_mutex_on() +{ + struct sembuf op; + +#ifdef NO_SEM_UNDO + if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) { + perror("sigprocmask(SIG_BLOCK)"); + exit (1); + } + op.sem_flg = 0; +#else + op.sem_flg = SEM_UNDO; +#endif + op.sem_num = 0; + op.sem_op = -1; + if (semop(sem_id, &op, 1) < 0) { + perror ("accept_mutex_on"); + exit (1); + } +} + +void accept_mutex_off() +{ + struct sembuf op; + + op.sem_num = 0; + op.sem_op = 1; +#ifdef NO_SEM_UNDO + op.sem_flg = 0; +#else + op.sem_flg = SEM_UNDO; +#endif + if (semop(sem_id, &op, 1) < 0) { + perror ("accept_mutex_off"); + exit (1); + } +#ifdef NO_SEM_UNDO + if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) { + perror("sigprocmask(SIG_SETMASK)"); + exit (1); + } +#endif +} + +#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT) + +/* note: pthread mutexes aren't released on child death, hence the + * signal goop ... in a real implementation we'd do special things + * during hup, term, usr1. + */ + +#include + +static pthread_mutex_t *mutex; +static sigset_t accept_block_mask; +static sigset_t accept_previous_mask; + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +void accept_mutex_init(void) +{ + pthread_mutexattr_t mattr; + int fd; + + fd = open ("/dev/zero", O_RDWR); + if (fd == -1) { + perror ("open(/dev/zero)"); + exit (1); + } + mutex = (pthread_mutex_t *)mmap ((caddr_t)0, sizeof (*mutex), + PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (mutex == (void *)(caddr_t)-1) { + perror ("mmap"); + exit (1); + } + close (fd); + if (pthread_mutexattr_init(&mattr)) { + perror ("pthread_mutexattr_init"); + exit (1); + } + if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED)) { + perror ("pthread_mutexattr_setpshared"); + exit (1); + } + if (pthread_mutex_init(mutex, &mattr)) { + perror ("pthread_mutex_init"); + exit (1); + } + sigfillset(&accept_block_mask); + sigdelset(&accept_block_mask, SIGHUP); + sigdelset(&accept_block_mask, SIGTERM); + sigdelset(&accept_block_mask, SIGUSR1); +} + +void accept_mutex_on() +{ + if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) { + perror("sigprocmask(SIG_BLOCK)"); + exit (1); + } + if (pthread_mutex_lock (mutex)) { + perror ("pthread_mutex_lock"); + exit (1); + } +} + +void accept_mutex_off() +{ + if (pthread_mutex_unlock (mutex)) { + perror ("pthread_mutex_unlock"); + exit (1); + } + if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) { + perror("sigprocmask(SIG_SETMASK)"); + exit (1); + } +} + +#elif defined (USE_USLOCK_SERIALIZED_ACCEPT) + +#include + +static usptr_t *us = NULL; +static ulock_t uslock = NULL; + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +void accept_mutex_init(void) +{ + ptrdiff_t old; + /* default is 8 */ +#define CONF_INITUSERS_MAX 15 + if ((old = usconfig(CONF_INITUSERS, CONF_INITUSERS_MAX)) == -1) { + perror("usconfig"); + exit(-1); + } + if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) { + perror("usconfig"); + exit(-1); + } + if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) { + perror("usconfig"); + exit(-1); + } + if ((us = usinit("/dev/zero")) == NULL) { + perror("usinit"); + exit(-1); + } + if ((uslock = usnewlock(us)) == NULL) { + perror("usnewlock"); + exit(-1); + } +} +void accept_mutex_on() +{ + switch(ussetlock(uslock)) { + case 1: + /* got lock */ + break; + case 0: + fprintf(stderr, "didn't get lock\n"); + exit(-1); + case -1: + perror("ussetlock"); + exit(-1); + } +} +void accept_mutex_off() +{ + if (usunsetlock(uslock) == -1) { + perror("usunsetlock"); + exit(-1); + } +} +#endif + + +#ifndef USE_SHMGET_SCOREBOARD +static void *get_shared_mem(size_t size) +{ + void *result; + + /* allocate shared memory for the shared_counter */ + result = (unsigned long *)mmap ((caddr_t)0, size, + PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); + if (result == (void *)(caddr_t)-1) { + perror ("mmap"); + exit (1); + } + return result; +} +#else +#include +#include +#include + +static void *get_shared_mem(size_t size) +{ + key_t shmkey = IPC_PRIVATE; + int shmid = -1; + void *result; +#ifdef MOVEBREAK + char *obrk; +#endif + + if ((shmid = shmget(shmkey, size, IPC_CREAT | SHM_R | SHM_W)) == -1) { + perror("shmget"); + exit(1); + } + +#ifdef MOVEBREAK + /* + * Some SysV systems place the shared segment WAY too close + * to the dynamic memory break point (sbrk(0)). This severely + * limits the use of malloc/sbrk in the program since sbrk will + * refuse to move past that point. + * + * To get around this, we move the break point "way up there", + * attach the segment and then move break back down. Ugly + */ + if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) { + perror("sbrk"); + } +#endif + +#define BADSHMAT ((void *)(-1)) + if ((result = shmat(shmid, 0, 0)) == BADSHMAT) { + perror("shmat"); + } + /* + * We must avoid leaving segments in the kernel's + * (small) tables. + */ + if (shmctl(shmid, IPC_RMID, NULL) != 0) { + perror("shmctl(IPC_RMID)"); + } + if (result == BADSHMAT) /* now bailout */ + exit(1); + +#ifdef MOVEBREAK + if (obrk == (char *) -1) + return; /* nothing else to do */ + if (sbrk(-(MOVEBREAK)) == (char *) -1) { + perror("sbrk 2"); + } +#endif + return result; +} +#endif + +#ifdef _POSIX_PRIORITY_SCHEDULING +/* don't ask */ +#define _P __P +#include +#define YIELD sched_yield() +#else +#define YIELD do { struct timeval zero; zero.tv_sec = zero.tv_usec = 0; select(0,0,0,0,&zero); } while(0) +#endif + +void main (int argc, char **argv) +{ + int num_iter; + int num_child; + int i; + struct timeval first; + struct timeval last; + long ms; + int pid; + unsigned long *shared_counter; + + if (argc != 3) { + fprintf (stderr, "Usage: time-sem num-child num-iter\n"); + exit (1); + } + + num_child = atoi (argv[1]); + num_iter = atoi (argv[2]); + + /* allocate shared memory for the shared_counter */ + shared_counter = get_shared_mem(sizeof(*shared_counter)); + + /* initialize counter to 0 */ + *shared_counter = 0; + + accept_mutex_init (); + + /* parent grabs mutex until done spawning children */ + accept_mutex_on (); + + for (i = 0; i < num_child; ++i) { + pid = fork(); + if (pid == 0) { + /* child, do our thing */ + accept_mutex_child_init(); + for (i = 0; i < num_iter; ++i) { + unsigned long tmp; + + accept_mutex_on (); + tmp = *shared_counter; + YIELD; + *shared_counter = tmp + 1; + accept_mutex_off (); + } + exit (0); + } else if (pid == -1) { + perror ("fork"); + exit (1); + } + } + + /* a quick test to see that nothing is screwed up */ + if (*shared_counter != 0) { + puts ("WTF! shared_counter != 0 before the children have been started!"); + exit (1); + } + + gettimeofday (&first, NULL); + /* launch children into action */ + accept_mutex_off (); + for (i = 0; i < num_child; ++i) { + if (wait(NULL) == -1) { + perror ("wait"); + } + } + gettimeofday (&last, NULL); + + if (*shared_counter != num_child * num_iter) { + printf ("WTF! shared_counter != num_child * num_iter!\n" + "shared_counter = %lu\nnum_child = %d\nnum_iter=%d\n", + *shared_counter, + num_child, num_iter); + } + + last.tv_sec -= first.tv_sec; + ms = last.tv_usec - first.tv_usec; + if (ms < 0) { + --last.tv_sec; + ms += 1000000; + } + last.tv_usec = ms; + printf ("%8lu.%06lu\n", last.tv_sec, last.tv_usec); + + accept_mutex_cleanup(); + + exit(0); +} + diff --git a/test/zb.c b/test/zb.c new file mode 100644 index 00000000000..0a6666eb25d --- /dev/null +++ b/test/zb.c @@ -0,0 +1,567 @@ + +/* ZeusBench V1.01 + =============== + +This program is Copyright (C) Zeus Technology Limited 1996. + +This program may be used and copied freely providing this copyright notice +is not removed. + +This software is provided "as is" and any express or implied waranties, +including but not limited to, the implied warranties of merchantability and +fitness for a particular purpose are disclaimed. In no event shall +Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, +exemplary, or consequential damaged (including, but not limited to, +procurement of substitute good or services; loss of use, data, or profits; +or business interruption) however caused and on theory of liability. Whether +in contract, strict liability or tort (including negligence or otherwise) +arising in any way out of the use of this software, even if advised of the +possibility of such damage. + + Written by Adam Twiss (adam@zeus.co.uk). March 1996 + +Thanks to the following people for their input: + Mike Belshe (mbelshe@netscape.com) + Michael Campanella (campanella@stevms.enet.dec.com) + +*/ + +/* -------------------- Notes on compiling ------------------------------ + +This should compile unmodified using gcc on HP-UX, FreeBSD, Linux, +IRIX, Solaris, AIX and Digital Unix (OSF). On Solaris 2.x you will +need to compile with "-lnsl -lsocket" options. If you have any +difficulties compiling then let me know. + +On SunOS 4.x.x you may need to compile with -DSUNOS4 to add the following +two lines of code which appear not to exist in my SunOS headers */ + +#ifdef SUNOS4 +extern char *optarg; +extern int optind, opterr, optopt; +#endif + +/* -------------------------------------------------------------------- */ + +/* affects include files on Solaris */ +#define BSD_COMP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ------------------- DEFINITIONS -------------------------- */ + +/* maximum number of requests on a time limited test */ +#define MAX_REQUESTS 50000 + +/* good old state machine */ +#define STATE_UNCONNECTED 0 +#define STATE_CONNECTING 1 +#define STATE_READ 2 + +#define CBUFFSIZE 512 + +struct connection +{ + int fd; + int state; + int read; /* amount of bytes read */ + int bread; /* amount of body read */ + int length; /* Content-Length value used for keep-alive */ + char cbuff[CBUFFSIZE]; /* a buffer to store server response header */ + int cbx; /* offset in cbuffer */ + int keepalive; /* non-zero if a keep-alive request */ + int gotheader; /* non-zero if we have the entire header in cbuff */ + struct timeval start, connect, done; +}; + +struct data +{ + int read; /* number of bytes read */ + int ctime; /* time in ms to connect */ + int time; /* time in ms for connection */ +}; + +#define min(a,b) ((a)<(b))?(a):(b) +#define max(a,b) ((a)>(b))?(a):(b) + +/* --------------------- GLOBALS ---------------------------- */ + +int requests = 1; /* Number of requests to make */ +int concurrency = 1; /* Number of multiple requests to make */ +int tlimit = 0; /* time limit in cs */ +int keepalive = 0; /* try and do keepalive connections */ +char *machine; /* Machine name */ +char *file; /* file name to use */ +char server_name[80]; /* name that server reports */ +int port = 80; /* port to use */ + +int doclen = 0; /* the length the document should be */ +int totalread = 0; /* total number of bytes read */ +int totalbread = 0; /* totoal amount of entity body read */ +int done=0; /* number of requests we have done */ +int doneka=0; /* number of keep alive connections done */ +int good=0, bad=0; /* number of good and bad requests */ + +/* store error cases */ +int err_length = 0, err_conn = 0, err_except = 0; + +struct timeval start, endtime; + +/* global request (and its length) */ +char request[512]; +int reqlen; + +/* one global throw-away buffer to read stuff into */ +char buffer[4096]; + +struct connection *con; /* connection array */ +struct data *stats; /* date for each request */ + +fd_set readbits, writebits; /* bits for select */ +struct sockaddr_in server; /* server addr structure */ + +/* --------------------------------------------------------- */ + +/* simple little function to perror and exit */ + +static void err(char *s) +{ + perror(s); + exit(errno); +} + +/* --------------------------------------------------------- */ + +/* write out request to a connection - assumes we can write + (small) request out in one go into our new socket buffer */ + +void write_request(struct connection *c) +{ + gettimeofday(&c->connect,0); + write(c->fd,request, reqlen); + c->state = STATE_READ; + FD_SET(c->fd, &readbits); + FD_CLR(c->fd, &writebits); +} + +/* --------------------------------------------------------- */ + +/* make an fd non blocking */ + +void nonblock(int fd) +{ + int i=1; + ioctl(fd, FIONBIO, &i); +} + +/* --------------------------------------------------------- */ + +/* returns the time in ms between two timevals */ + +int timedif(struct timeval a, struct timeval b) +{ + register int us,s; + + us = a.tv_usec - b.tv_usec; + us /= 1000; + s = a.tv_sec - b.tv_sec; + s *= 1000; + return s+us; +} + +/* --------------------------------------------------------- */ + +/* calculate and output results and exit */ + +void output_results() +{ + int timetaken; + + gettimeofday(&endtime,0); + timetaken = timedif(endtime, start); + + printf("\n---\n"); + printf("Server: %s\n", server_name); + printf("Document Length: %d\n", doclen); + printf("Concurency Level: %d\n", concurrency); + printf("Time taken for tests: %d.%03d seconds\n", + timetaken/1000, timetaken%1000); + printf("Complete requests: %d\n", done); + printf("Failed requests: %d\n", bad); + if(bad) printf(" (Connect: %d, Length: %d, Exceptions: %d)\n", + err_conn, err_length, err_except); + if(keepalive) printf("Keep-Alive requests: %d\n", doneka); + printf("Bytes transfered: %d\n", totalread); + printf("HTML transfered: %d\n", totalbread); + + /* avoid divide by zero */ + if(timetaken) { + printf("Requests per seconds: %.2f\n", 1000*(float)(done)/timetaken); + printf("Transfer rate: %.2f kb/s\n", + (float)(totalread)/timetaken); + } + + { + /* work out connection times */ + int i; + int totalcon=0, total=0; + int mincon=9999999, mintot=999999; + int maxcon=0, maxtot=0; + + for(i=0; iread = 0; + c->bread = 0; + c->keepalive = 0; + c->cbx = 0; + c->gotheader = 0; + + c->fd = socket(AF_INET, SOCK_STREAM, 0); + if(c->fd<0) err("socket"); + + nonblock(c->fd); + gettimeofday(&c->start,0); + + if(connect(c->fd, (struct sockaddr *) &server, sizeof(server))<0) { + if(errno==EINPROGRESS) { + c->state = STATE_CONNECTING; + FD_SET(c->fd, &writebits); + return; + } + else { + close(c->fd); + err_conn++; + if(bad++>10) { + printf("\nTest aborted after 10 failures\n\n"); + exit(1); + } + start_connect(c); + } + } + + /* connected first time */ + write_request(c); +} + +/* --------------------------------------------------------- */ + +/* close down connection and save stats */ + +void close_connection(struct connection *c) +{ + if(c->read == 0 && c->keepalive) { + /* server has legitiamately shut down an idle keep alive request */ + good--; /* connection never happend */ + } + else { + if(good==1) { + /* first time here */ + doclen = c->bread; + } else if (c->bread!=doclen) { + bad++; + err_length++; + } + + /* save out time */ + if(done < requests) { + struct data s; + gettimeofday(&c->done,0); + s.read = c->read; + s.ctime = timedif(c->connect, c->start); + s.time = timedif(c->done, c->start); + stats[done++] = s; + } + } + + close(c->fd); + FD_CLR(c->fd, &readbits); + FD_CLR(c->fd, &writebits); + + /* connect again */ + start_connect(c); + return; +} + +/* --------------------------------------------------------- */ + +/* read data from connection */ + +void read_connection(struct connection *c) +{ + int r; + + r=read(c->fd,buffer,sizeof(buffer)); + if(r==0 || (r<0 && errno!=EAGAIN)) { + good++; + close_connection(c); + return; + } + + if(r<0 && errno==EAGAIN) return; + + c->read += r; + totalread += r; + + if(!c->gotheader) { + char *s; + int l=4; + int space = CBUFFSIZE - c->cbx - 1; /* -1 to allow for 0 terminator */ + int tocopy = (spacecbuff+c->cbx, buffer, space); + c->cbx += tocopy; space -= tocopy; + c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */ + s = strstr(c->cbuff, "\r\n\r\n"); + /* this next line is so that we talk to NCSA 1.5 which blatantly breaks + the http specifaction */ + if(!s) { s = strstr(c->cbuff,"\n\n"); l=2; } + + if(!s) { + /* read rest next time */ + if(space) + return; + else { + /* header is in invalid or too big - close connection */ + close(c->fd); + if(bad++>10) { + printf("\nTest aborted after 10 failures\n\n"); + exit(1); + } + FD_CLR(c->fd, &writebits); + start_connect(c); + } + } + else { + /* have full header */ + if(!good) { + /* this is first time, extract some interesting info */ + char *p, *q; + p = strstr(c->cbuff, "Server:"); + q = server_name; + if(p) { p+=8; while(*p>32) *q++ = *p++; } + *q = 0; + } + + c->gotheader = 1; + *s = 0; /* terminate at end of header */ + if(keepalive && + (strstr(c->cbuff, "Keep-Alive") + || strstr(c->cbuff, "keep-alive"))) /* for benefit of MSIIS */ + { + char *cl; + cl = strstr(c->cbuff, "Content-Length:"); + /* for cacky servers like NCSA which break the spec and send a + lower case 'l' */ + if(!cl) cl = strstr(c->cbuff, "Content-length:"); + if(cl) { + c->keepalive=1; + c->length = atoi(cl+16); + } + } + c->bread += c->cbx - (s+l-c->cbuff) + r-tocopy; + totalbread += c->bread; + } + } + else { + /* outside header, everything we have read is entity body */ + c->bread += r; + totalbread += r; + } + + if(c->keepalive && (c->bread >= c->length)) { + /* finished a keep-alive connection */ + good++; doneka++; + /* save out time */ + if(good==1) { + /* first time here */ + doclen = c->bread; + } else if(c->bread!=doclen) { bad++; err_length++; } + if(done < requests) { + struct data s; + gettimeofday(&c->done,0); + s.read = c->read; + s.ctime = timedif(c->connect, c->start); + s.time = timedif(c->done, c->start); + stats[done++] = s; + } + c->keepalive = 0; c->length = 0; c->gotheader=0; c->cbx = 0; + c->read = c->bread = 0; + write_request(c); + c->start = c->connect; /* zero connect time with keep-alive */ + } +} + +/* --------------------------------------------------------- */ + +/* run the tests */ + +int test() +{ + struct timeval timeout, now; + fd_set sel_read, sel_except, sel_write; + int i; + + { + /* get server information */ + struct hostent *he; + he = gethostbyname(machine); + if (!he) err("gethostbyname"); + server.sin_family = he->h_addrtype; + server.sin_port = htons(port); + server.sin_addr.s_addr = ((unsigned long *)(he->h_addr_list[0]))[0]; + } + + con = malloc(concurrency*sizeof(struct connection)); + memset(con,0,concurrency*sizeof(struct connection)); + + stats = malloc(requests * sizeof(struct data)); + + FD_ZERO(&readbits); + FD_ZERO(&writebits); + + /* setup request */ + sprintf(request,"GET %s HTTP/1.0\r\nUser-Agent: ZeusBench/1.0\r\n" + "%sHost: %s\r\nAccept: */*\r\n\r\n", file, + keepalive?"Connection: Keep-Alive\r\n":"", machine ); + + reqlen = strlen(request); + + /* ok - lets start */ + gettimeofday(&start,0); + + /* initialise lots of requests */ + for(i=0; i (tlimit*1000)) { + requests=done; /* so stats are correct */ + output_results(); + } + + /* Timeout of 30 seconds. */ + timeout.tv_sec=30; timeout.tv_usec=0; + n=select(256, &sel_read, &sel_write, &sel_except, &timeout); + if(!n) { + printf("\nServer timed out\n\n"); + exit(1); + } + if(n<1) err("select"); + + for(i=0; i=requests) output_results(); + } + return 0; +} + +/* ------------------------------------------------------- */ + +/* display usage information */ + +void usage(char *progname) { + printf("\nZeusBench v1.0\n\n"); + printf("Usage: %s [-k] [-n requests | -t timelimit (sec)]" + "\n\t\t[-c concurrency] [-p port] \n",progname); + printf("Filename should start with a '/' e.g. /index.html\n\n"); + exit(EINVAL); +} + +/* ------------------------------------------------------- */ + +/* sort out command-line args and call test */ + +int main(int argc, char **argv) { + int c; + if (argc < 3) usage(argv[0]); + + machine = argv[1]; + file = argv[2]; + optind = 3; + while ((c = getopt(argc,argv,"p:n:c:d:t:d:k"))>0) { + switch(c) { + case 'd': + break; + case 'n': + requests = atoi(optarg); + if(!requests) { + printf("Invalid number of requests\n"); + exit(1); + } + break; + case 'k': + keepalive=1; + break; + case 'c': + concurrency = atoi(optarg); + break; + case 'p': + port = atoi(optarg); + break; + case 't': + tlimit = atoi(optarg); + requests = MAX_REQUESTS; /* need to size data array on something */ + break; + default: + usage(argv[0]); + break; + } + } + test(); + return 0; +} + + + + + +