]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
This commit was manufactured by cvs2svn to create branch 'avendor'.
author(no author) <(no author)@unknown>
Tue, 16 Oct 2001 21:29:08 +0000 (21:29 +0000)
committer(no author) <(no author)@unknown>
Tue, 16 Oct 2001 21:29:08 +0000 (21:29 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/avendor@91505 13f79535-47bb-0310-9956-ffa450edef68

213 files changed:
build/.cvsignore [new file with mode: 0644]
build/make_exports.awk [new file with mode: 0644]
build/make_var_export.awk [new file with mode: 0644]
build/rules.mk.in [new file with mode: 0644]
build/win32/.cvsignore [new file with mode: 0644]
build/win32/apache.ico [new file with mode: 0644]
build/win32/win32ver.awk [new file with mode: 0644]
docs/docroot/apache_pb.png [new file with mode: 0644]
docs/docroot/apache_pb2.png [new file with mode: 0644]
docs/docroot/index.html.cz.iso8859-2 [new file with mode: 0644]
docs/docroot/index.html.hr.iso8859-2 [new file with mode: 0644]
docs/docroot/index.html.nn [new file with mode: 0644]
docs/docroot/index.html.po.iso8859-2 [new file with mode: 0644]
docs/docroot/index.html.var [new file with mode: 0644]
docs/error/HTTP_BAD_GATEWAY.html.var [new file with mode: 0644]
docs/error/HTTP_BAD_REQUEST.html.var [new file with mode: 0644]
docs/error/HTTP_FORBIDDEN.html.var [new file with mode: 0644]
docs/error/HTTP_GONE.html.var [new file with mode: 0644]
docs/error/HTTP_INTERNAL_SERVER_ERROR.html.var [new file with mode: 0644]
docs/error/HTTP_LENGTH_REQUIRED.html.var [new file with mode: 0644]
docs/error/HTTP_METHOD_NOT_ALLOWED.html.var [new file with mode: 0644]
docs/error/HTTP_NOT_ACCEPTABLE.html.var [new file with mode: 0644]
docs/error/HTTP_NOT_FOUND.html.var [new file with mode: 0644]
docs/error/HTTP_NOT_IMPLEMENTED.html.var [new file with mode: 0644]
docs/error/HTTP_PRECONDITION_FAILED.html.var [new file with mode: 0644]
docs/error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var [new file with mode: 0644]
docs/error/HTTP_REQUEST_TIME_OUT.html.var [new file with mode: 0644]
docs/error/HTTP_REQUEST_URI_TOO_LARGE.html.var [new file with mode: 0644]
docs/error/HTTP_SERVICE_UNAVAILABLE.html.var [new file with mode: 0644]
docs/error/HTTP_UNAUTHORIZED.html.var [new file with mode: 0644]
docs/error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var [new file with mode: 0644]
docs/error/HTTP_VARIANT_ALSO_VARIES.html.var [new file with mode: 0644]
docs/error/contact.html.var [new file with mode: 0644]
docs/error/include/bottom.html [new file with mode: 0644]
docs/error/include/spacer.html [new file with mode: 0644]
docs/error/include/top.html [new file with mode: 0644]
docs/icons/a.png [new file with mode: 0644]
docs/icons/alert.black.png [new file with mode: 0644]
docs/icons/alert.red.png [new file with mode: 0644]
docs/icons/apache_pb.png [new file with mode: 0644]
docs/icons/apache_pb2.png [new file with mode: 0644]
docs/icons/back.png [new file with mode: 0644]
docs/icons/ball.gray.png [new file with mode: 0644]
docs/icons/ball.red.png [new file with mode: 0644]
docs/icons/binary.png [new file with mode: 0644]
docs/icons/binhex.png [new file with mode: 0644]
docs/icons/blank.png [new file with mode: 0644]
docs/icons/bomb.png [new file with mode: 0644]
docs/icons/box1.png [new file with mode: 0644]
docs/icons/box2.png [new file with mode: 0644]
docs/icons/broken.png [new file with mode: 0644]
docs/icons/burst.png [new file with mode: 0644]
docs/icons/c.png [new file with mode: 0644]
docs/icons/comp.blue.png [new file with mode: 0644]
docs/icons/comp.gray.png [new file with mode: 0644]
docs/icons/compressed.png [new file with mode: 0644]
docs/icons/continued.png [new file with mode: 0644]
docs/icons/dir.png [new file with mode: 0644]
docs/icons/down.png [new file with mode: 0644]
docs/icons/dvi.png [new file with mode: 0644]
docs/icons/f.png [new file with mode: 0644]
docs/icons/folder.open.png [new file with mode: 0644]
docs/icons/folder.png [new file with mode: 0644]
docs/icons/folder.sec.png [new file with mode: 0644]
docs/icons/forward.png [new file with mode: 0644]
docs/icons/generic.png [new file with mode: 0644]
docs/icons/generic.red.png [new file with mode: 0644]
docs/icons/generic.sec.png [new file with mode: 0644]
docs/icons/hand.right.png [new file with mode: 0644]
docs/icons/hand.up.png [new file with mode: 0644]
docs/icons/icon.sheet.png [new file with mode: 0644]
docs/icons/image1.png [new file with mode: 0644]
docs/icons/image2.png [new file with mode: 0644]
docs/icons/image3.png [new file with mode: 0644]
docs/icons/index.png [new file with mode: 0644]
docs/icons/layout.png [new file with mode: 0644]
docs/icons/left.png [new file with mode: 0644]
docs/icons/link.png [new file with mode: 0644]
docs/icons/movie.png [new file with mode: 0644]
docs/icons/p.png [new file with mode: 0644]
docs/icons/patch.png [new file with mode: 0644]
docs/icons/pdf.png [new file with mode: 0644]
docs/icons/pie0.png [new file with mode: 0644]
docs/icons/pie1.png [new file with mode: 0644]
docs/icons/pie2.png [new file with mode: 0644]
docs/icons/pie3.png [new file with mode: 0644]
docs/icons/pie4.png [new file with mode: 0644]
docs/icons/pie5.png [new file with mode: 0644]
docs/icons/pie6.png [new file with mode: 0644]
docs/icons/pie7.png [new file with mode: 0644]
docs/icons/pie8.png [new file with mode: 0644]
docs/icons/portal.png [new file with mode: 0644]
docs/icons/ps.png [new file with mode: 0644]
docs/icons/quill.png [new file with mode: 0644]
docs/icons/right.png [new file with mode: 0644]
docs/icons/screw1.png [new file with mode: 0644]
docs/icons/screw2.png [new file with mode: 0644]
docs/icons/script.png [new file with mode: 0644]
docs/icons/small/back.png [new file with mode: 0644]
docs/icons/small/binary.png [new file with mode: 0644]
docs/icons/small/binhex.png [new file with mode: 0644]
docs/icons/small/blank.png [new file with mode: 0644]
docs/icons/small/broken.png [new file with mode: 0644]
docs/icons/small/burst.png [new file with mode: 0644]
docs/icons/small/comp1.png [new file with mode: 0644]
docs/icons/small/comp2.png [new file with mode: 0644]
docs/icons/small/compressed.png [new file with mode: 0644]
docs/icons/small/continued.png [new file with mode: 0644]
docs/icons/small/dir.png [new file with mode: 0644]
docs/icons/small/dir2.png [new file with mode: 0644]
docs/icons/small/doc.png [new file with mode: 0644]
docs/icons/small/forward.png [new file with mode: 0644]
docs/icons/small/generic.png [new file with mode: 0644]
docs/icons/small/generic2.png [new file with mode: 0644]
docs/icons/small/generic3.png [new file with mode: 0644]
docs/icons/small/image.png [new file with mode: 0644]
docs/icons/small/image2.png [new file with mode: 0644]
docs/icons/small/index.png [new file with mode: 0644]
docs/icons/small/key.png [new file with mode: 0644]
docs/icons/small/movie.png [new file with mode: 0644]
docs/icons/small/patch.png [new file with mode: 0644]
docs/icons/small/ps.png [new file with mode: 0644]
docs/icons/small/rainbow.png [new file with mode: 0644]
docs/icons/small/sound.png [new file with mode: 0644]
docs/icons/small/sound2.png [new file with mode: 0644]
docs/icons/small/tar.png [new file with mode: 0644]
docs/icons/small/text.png [new file with mode: 0644]
docs/icons/small/transfer.png [new file with mode: 0644]
docs/icons/small/unknown.png [new file with mode: 0644]
docs/icons/small/uu.png [new file with mode: 0644]
docs/icons/sound1.png [new file with mode: 0644]
docs/icons/sound2.png [new file with mode: 0644]
docs/icons/sphere1.png [new file with mode: 0644]
docs/icons/sphere2.png [new file with mode: 0644]
docs/icons/tar.png [new file with mode: 0644]
docs/icons/tex.png [new file with mode: 0644]
docs/icons/text.png [new file with mode: 0644]
docs/icons/transfer.png [new file with mode: 0644]
docs/icons/unknown.png [new file with mode: 0644]
docs/icons/up.png [new file with mode: 0644]
docs/icons/uu.png [new file with mode: 0644]
docs/icons/uuencoded.png [new file with mode: 0644]
docs/icons/world1.png [new file with mode: 0644]
docs/icons/world2.png [new file with mode: 0644]
docs/manual/developer/documenting.html [new file with mode: 0644]
docs/manual/developer/request.html [new file with mode: 0644]
docs/manual/faq/index.html [new file with mode: 0644]
docs/manual/faq/support.html [new file with mode: 0644]
docs/manual/howto/auth.html [new file with mode: 0644]
docs/manual/howto/auth.html.en [new file with mode: 0644]
docs/manual/images/custom_errordocs.png [new file with mode: 0644]
docs/manual/images/mod_rewrite_fig1.png [new file with mode: 0644]
docs/manual/images/mod_rewrite_fig2.png [new file with mode: 0644]
docs/manual/logs.html [new file with mode: 0644]
docs/manual/logs.html.en [new file with mode: 0644]
docs/manual/mod/mod_cgid.html [new file with mode: 0644]
docs/manual/mod/mod_suexec.html [new file with mode: 0644]
include/util_ldap.h [new file with mode: 0644]
include/util_time.h [new file with mode: 0644]
modules/experimental/cache_storage.c [new file with mode: 0644]
modules/experimental/cache_util.c [new file with mode: 0644]
modules/experimental/mod_auth_ldap.c [new file with mode: 0644]
modules/experimental/mod_cache.dsp [new file with mode: 0644]
modules/experimental/mod_case_filter_in.c [new file with mode: 0644]
modules/experimental/mod_mem_cache.c [new file with mode: 0644]
modules/experimental/util_ldap.c [new file with mode: 0644]
modules/experimental/util_ldap_cache.c [new file with mode: 0644]
modules/experimental/util_ldap_cache.h [new file with mode: 0644]
modules/experimental/util_ldap_cache_mgr.c [new file with mode: 0644]
modules/generators/mod_cgi.h [new file with mode: 0644]
modules/mappers/mod_vhost_alias.dsp [new file with mode: 0644]
modules/metadata/mod_mime_magic.dsp [new file with mode: 0644]
modules/metadata/mod_unique_id.dsp [new file with mode: 0644]
modules/proxy/CHANGES [new file with mode: 0644]
modules/ssl/Makefile.in [new file with mode: 0644]
modules/ssl/config.m4 [new file with mode: 0644]
modules/ssl/mod_ssl.dsp [new file with mode: 0644]
modules/test/Makefile.in [new file with mode: 0644]
modules/test/config.m4 [new file with mode: 0644]
modules/test/mod_optional_hook_export.c [new file with mode: 0644]
modules/test/mod_optional_hook_export.h [new file with mode: 0644]
modules/test/mod_optional_hook_import.c [new file with mode: 0644]
server/mpm/mpmt_os2/.cvsignore [new file with mode: 0644]
server/mpm/mpmt_os2/Makefile.in [new file with mode: 0644]
server/mpm/mpmt_os2/config5.m4 [new file with mode: 0644]
server/mpm/mpmt_os2/mpm.h [new file with mode: 0644]
server/mpm/mpmt_os2/mpm_default.h [new file with mode: 0644]
server/mpm/mpmt_os2/mpmt_os2.c [new file with mode: 0644]
server/mpm/mpmt_os2/mpmt_os2_child.c [new file with mode: 0644]
server/mpm/worker/.cvsignore [new file with mode: 0644]
server/mpm/worker/Makefile.in [new file with mode: 0644]
server/mpm/worker/config5.m4 [new file with mode: 0644]
server/mpm/worker/fdqueue.c [new file with mode: 0644]
server/mpm/worker/fdqueue.h [new file with mode: 0644]
server/mpm/worker/mpm.h [new file with mode: 0644]
server/mpm/worker/mpm_default.h [new file with mode: 0644]
server/mpm/worker/worker.c [new file with mode: 0644]
server/util_time.c [new file with mode: 0644]
support/abs.dsp [new file with mode: 0644]
support/config.m4 [new file with mode: 0644]
support/list_hooks.pl [new file with mode: 0755]
support/win32/.cvsignore [new file with mode: 0644]
support/win32/ApacheMonitor.c [new file with mode: 0644]
support/win32/ApacheMonitor.dsp [new file with mode: 0644]
support/win32/ApacheMonitor.h [new file with mode: 0644]
support/win32/ApacheMonitor.ico [new file with mode: 0644]
support/win32/apache_header.bmp [new file with mode: 0644]
support/win32/aprun.ico [new file with mode: 0644]
support/win32/apstop.ico [new file with mode: 0644]
support/win32/srun.bmp [new file with mode: 0644]
support/win32/sstop.bmp [new file with mode: 0644]
support/win32/wintty.c [new file with mode: 0644]
support/win32/wintty.dsp [new file with mode: 0644]

diff --git a/build/.cvsignore b/build/.cvsignore
new file mode 100644 (file)
index 0000000..29d51bb
--- /dev/null
@@ -0,0 +1 @@
+rules.mk
diff --git a/build/make_exports.awk b/build/make_exports.awk
new file mode 100644 (file)
index 0000000..27fd631
--- /dev/null
@@ -0,0 +1,120 @@
+
+BEGIN {
+    printf("/*\n")
+    printf(" * THIS FILE WAS AUTOGENERATED BY make_exports.awk\n")
+    printf(" *\n")
+    printf(" * This is an ugly hack that needs to be here, so\n")
+    printf(" * that libtool will link all of the APR functions\n")
+    printf(" * into server regardless of whether the base server\n")
+    printf(" * uses them.\n")
+    printf(" */\n")
+    printf("\n")
+    printf("#define CORE_PRIVATE\n")
+    printf("\n")
+    
+    for (i = 1; i < ARGC; i++) {
+        file = ARGV[i]
+        sub("([^/]*[/])*", "", file)
+        printf("#include \"%s\"\n", file)
+    }
+
+    printf("\n")
+    printf("const void *ap_ugly_hack = NULL;\n")
+    printf("\n")
+    
+    TYPE_NORMAL = 0
+    TYPE_HEADER = 1
+
+    stackptr = 0
+}
+
+function push(line) {
+    stack[stackptr] = line
+    stackptr++
+}
+
+function do_output() {
+    printf("/*\n")
+    printf(" * %s\n", FILENAME)
+    printf(" */\n")
+    
+    for (i = 0; i < stackptr; i++) {
+        printf("%s\n", stack[i])
+    }
+    
+    stackptr = 0
+
+    printf("\n");
+}
+
+function enter_scope(type) {
+    scope++
+    scope_type[scope] = type
+    scope_stack[scope] = stackptr
+    delete scope_used[scope]
+}
+
+function leave_scope() {
+    used = scope_used[scope]
+   
+    if (!used)
+        stackptr = scope_stack[scope]
+
+    scope--
+    if (used) {
+        scope_used[scope] = 1
+        
+        if (!scope)
+            do_output()
+    }
+}
+
+function add_symbol(symbol) {
+    idx = index(symbol, "#")
+
+    if (!idx) {
+        push("const void *ap_hack_" symbol " = (const void *)" symbol ";")
+        scope_used[scope] = 1
+    }
+}
+
+/^[ \t]*AP[RU]?_DECLARE[^(]*[(][^)]*[)]([^ ]* )*[^(]+[(]/  { 
+    sub("[ \t]*AP[RU]?_DECLARE[^(]*[(][^)]*[)]", "");
+    sub("[(].*", "");
+    sub("^[ \t]+", "");
+    sub("([^ ]* ^([ \t]*[(]))*", "");
+
+    add_symbol($0)
+    next
+}
+
+/^#[ \t]*if(ndef| !defined[(])([^_]*_)*H/ {
+    enter_scope(TYPE_HEADER)
+    next
+}
+
+/^#[ \t]*if([n]?def)? / {
+    enter_scope(TYPE_NORMAL)
+    push($0)
+    next
+}
+
+/^#[ \t]*endif/ {
+    if (scope_type[scope] == TYPE_NORMAL)
+        push($0)
+        
+    leave_scope()
+    next
+}
+
+/^#[ \t]*else/ {
+    push($0)
+    next
+}
+
+/^#[ \t]*elif/ {
+    push($0)
+    next
+}
+
+
diff --git a/build/make_var_export.awk b/build/make_var_export.awk
new file mode 100644 (file)
index 0000000..62263f1
--- /dev/null
@@ -0,0 +1,59 @@
+# Based on apr's make_export.awk, which is
+# based on Ryan Bloom's make_export.pl
+
+/^#[ \t]*if(def)? (AP[RU]?_|!?defined).*/ {
+       if (old_filename != FILENAME) {
+               if (old_filename != "") printf("%s", line)
+               macro_no = 0
+               found = 0
+               count = 0
+               old_filename = FILENAME
+               line = ""
+       }
+       macro_stack[macro_no++] = macro
+       macro = substr($0, length($1)+2)
+       count++
+       line = line "#ifdef " macro "\n"
+       next
+}
+
+/^#[ \t]*endif/ {
+       if (count > 0) {
+               count--
+               line = line "#endif " macro "\n"
+               macro = macro_stack[--macro_no]
+       }
+       if (count == 0) {
+               if (found != 0) {
+                       printf("%s", line)
+               }
+               line = ""
+       }
+       next
+}
+
+function add_symbol (sym_name) {
+       if (count) {
+               found++
+       }
+       for (i = 0; i < count; i++) {
+               line = line "\t"
+       }
+       line = line sym_name "\n"
+
+       if (count == 0) {
+               printf("%s", line)
+               line = ""
+       }
+}
+
+/^[ \t]*AP[RU]?_DECLARE_DATA .*;$/ {
+       varname = $NF;
+       gsub( /[*;]/, "", varname);
+       gsub( /\[.*\]/, "", varname);
+       add_symbol(varname);
+}
+
+END {
+       printf("%s", line)
+}
diff --git a/build/rules.mk.in b/build/rules.mk.in
new file mode 100644 (file)
index 0000000..659cbcf
--- /dev/null
@@ -0,0 +1,264 @@
+# ====================================================================
+# The Apache Software License, Version 1.1
+#
+# Copyright (c) 2000-2001 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
+# <http://www.apache.org/>.
+#
+# The build environment was originally provided by Sascha Schumann.
+#
+
+include $(top_builddir)/config_vars.mk
+
+# Combine all of the flags together in the proper order so that
+# the user-defined flags can always override the configure ones, if needed.
+# Note that includes are listed after the flags because -I options have
+# left-to-right precedence and CPPFLAGS may include user-defined overrides.
+#
+ALL_CFLAGS   = $(EXTRA_CFLAGS) $(NOTEST_CFLAGS) $(CFLAGS)
+ALL_CPPFLAGS = $(DEFS) $(EXTRA_CPPFLAGS) $(NOTEST_CPPFLAGS) $(CPPFLAGS)
+ALL_CXXFLAGS = $(EXTRA_CXXFLAGS) $(NOTEST_CXXFLAGS) $(CXXFLAGS)
+ALL_LDFLAGS  = $(EXTRA_LDFLAGS) $(NOTEST_LDFLAGS) $(LDFLAGS)
+ALL_LIBS     = $(EXTRA_LIBS) $(NOTEST_LIBS) $(LIBS)
+ALL_INCLUDES = $(INCLUDES) $(EXTRA_INCLUDES)
+
+# Compile commands
+
+COMPILE      = $(CC)  $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(ALL_INCLUDES)
+CXX_COMPILE  = $(CXX) $(ALL_CXXFLAGS) $(ALL_CPPFLAGS) $(ALL_INCLUDES)
+
+SH_COMPILE     = $(LIBTOOL) --mode=compile $(COMPILE) @SHLTCFLAGS@ -c $< && touch $@
+SH_CXX_COMPILE = $(LIBTOOL) --mode=compile $(CXX_COMPILE) @SHLTCFLAGS@ -c $< && touch $@
+
+LT_COMPILE     = $(LIBTOOL) --mode=compile $(COMPILE) @LTCFLAGS@ -c $< && touch $@
+LT_CXX_COMPILE = $(LIBTOOL) --mode=compile $(CXX_COMPILE) @LTCFLAGS@ -c $< && touch $@
+
+# Link-related commands
+
+LINK     = $(LIBTOOL) --mode=link $(COMPILE) $(LT_LDFLAGS) $(ALL_LDFLAGS) -o $@
+SH_LINK  = $(SH_LIBTOOL) --mode=link $(COMPILE) $(LT_LDFLAGS) $(ALL_LDFLAGS) $(SH_LDFLAGS) $(CORE_IMPLIB) -o $@
+MOD_LINK = $(LIBTOOL) --mode=link $(COMPILE) -module $(LT_LDFLAGS) $(ALL_LDFLAGS) -o $@
+
+# Cross compile commands
+
+# Helper programs
+
+MKINSTALLDIRS = $(abs_srcdir)/build/mkdir.sh
+INSTALL = $(abs_srcdir)/build/install.sh -c
+INSTALL_DATA = $(INSTALL) -m 644
+INSTALL_PROGRAM = $(INSTALL) -m 755 $(INSTALL_PROG_FLAGS)
+
+#
+# Standard build rules
+#
+all: all-recursive
+depend: depend-recursive
+clean: clean-recursive
+distclean: distclean-recursive
+extraclean: extraclean-recursive
+install: install-recursive
+shared-build: shared-build-recursive
+
+all-recursive install-recursive depend-recursive:
+       @otarget=`echo $@|sed s/-recursive//`; \
+       list='$(SUBDIRS)'; \
+       for i in $$list; do \
+           if test -d "$$i"; then \
+               target="$$otarget"; \
+               echo "Making $$target in $$i"; \
+               if test "$$i" = "."; then \
+                       made_local=yes; \
+                       target="local-$$target"; \
+               fi; \
+               (cd $$i && $(MAKE) $$target) || exit 1; \
+           fi; \
+       done; \
+       if test "$$otarget" = "all" && test -z '$(TARGETS)'; then \
+           made_local=yes; \
+       fi; \
+       if test "$$made_local" != "yes"; then \
+           $(MAKE) "local-$$otarget" || exit 1; \
+       fi
+
+clean-recursive distclean-recursive extraclean-recursive:
+       @otarget=`echo $@|sed s/-recursive//`; \
+       list='$(SUBDIRS) $(CLEAN_SUBDIRS)'; \
+       for i in $$list; do \
+           if test -d "$$i"; then \
+               target="$$otarget"; \
+               echo "Making $$target in $$i"; \
+               if test "$$i" = "."; then \
+                       made_local=yes; \
+                       target="local-$$target"; \
+               fi; \
+               (cd $$i && $(MAKE) $$target); \
+           fi; \
+       done; \
+       if test "$$otarget" = "all" && test -z '$(TARGETS)'; then \
+           made_local=yes; \
+       fi; \
+       if test "$$made_local" != "yes"; then \
+           $(MAKE) "local-$$otarget"; \
+       fi
+
+shared-build-recursive:
+       @if test `pwd` = "$(top_builddir)"; then \
+           $(PRE_SHARED_CMDS) ; \
+       fi; \
+       list='$(SUBDIRS)'; for i in $$list; do \
+           target="shared-build"; \
+           if test "$$i" = "."; then \
+               made_local=yes; \
+               target="local-shared-build"; \
+           fi; \
+           if test "$$i" != "srclib"; then \
+               (cd $$i && $(MAKE) $$target) || exit 1; \
+           fi; \
+       done; \
+       if test -f 'modules.mk'; then \
+           if test -n '$(SHARED_TARGETS)'; then \
+               echo "Building shared: $(SHARED_TARGETS)"; \
+               if test "$$made_local" != "yes"; then \
+                       $(MAKE) "local-shared-build" || exit 1; \
+               fi; \
+           fi; \
+       fi; \
+       if test `pwd` = "$(top_builddir)"; then \
+               $(POST_SHARED_CMDS) ; \
+       fi
+
+local-all: $(TARGETS)
+
+local-shared-build: $(SHARED_TARGETS)
+
+local-depend: x-local-depend
+       if test "`echo $(srcdir)/*.c`" != "$(srcdir)'/*.c'"; then \
+               $(CC) -MM $(ALL_CPPFLAGS) $(ALL_INCLUDES) $(srcdir)/*.c | sed 's/\.o:/.lo:/' > $(builddir)/.deps || true;           \
+       fi
+
+local-clean: x-local-clean
+       rm -f *.o *.lo *.slo *.obj *.a *.la $(CLEAN_TARGETS) $(TARGETS)
+       rm -rf .libs
+
+local-distclean: local-clean x-local-distclean
+       rm -f .deps Makefile $(DISTCLEAN_TARGETS)
+
+local-extraclean: local-distclean x-local-extraclean
+       @if test -n "$(EXTRACLEAN_TARGETS)"; then \
+           echo "rm -f $(EXTRACLEAN_TARGETS)"; \
+           rm -f $(EXTRACLEAN_TARGETS) ; \
+       fi
+
+local-install: $(TARGETS) $(SHARED_TARGETS) $(INSTALL_TARGETS)
+       @if test -n '$(PROGRAMS)'; then \
+           test -d $(bindir) || $(MKINSTALLDIRS) $(bindir); \
+           list='$(PROGRAMS)'; for i in $$list; do \
+               $(INSTALL_PROGRAM) $$i $(bindir); \
+           done; \
+       fi
+
+# to be filled in by the actual Makefile if extra commands are needed
+x-local-depend x-local-clean x-local-distclean x-local-extraclean:
+
+#
+# Implicit rules for creating outputs from input files
+#
+CXX_SUFFIX = cpp
+SHLIB_SUFFIX = so
+
+.SUFFIXES:
+.SUFFIXES: .S .c .$(CXX_SUFFIX) .lo .o .s .y .l .slo .def .la
+
+.c.o:
+       $(COMPILE) -c $<
+
+.s.o:
+       $(COMPILE) -c $<
+
+.c.lo:
+       $(LT_COMPILE)
+
+.s.lo:
+       $(LT_COMPILE)
+
+.c.slo:
+       $(SH_COMPILE)
+
+.$(CXX_SUFFIX).lo:
+       $(LT_CXX_COMPILE)
+
+.$(CXX_SUFFIX).slo:
+       $(SH_CXX_COMPILE)
+
+.y.c:
+       $(YACC) $(YFLAGS) $< && mv y.tab.c $*.c
+       if test -f y.tab.h; then \
+       if cmp -s y.tab.h $*.h; then rm -f y.tab.h; else mv y.tab.h $*.h; fi; \
+       else :; fi
+
+.l.c:
+       $(LEX) $(LFLAGS) $< && mv $(LEX_OUTPUT_ROOT).c $@
+
+# Makes an import library from a def file
+.def.la:
+       $(LIBTOOL) --mode=compile $(MK_IMPLIB) -o $@ $<
+
+#
+# Dependencies
+#
+include $(builddir)/.deps
+
+.PHONY: all all-recursive install-recursive local-all $(PHONY_TARGETS) \
+       shared-build shared-build-recursive local-shared-build \
+       depend depend-recursive local-depend x-local-depend \
+       clean clean-recursive local-clean x-local-clean \
+       distclean distclean-recursive local-distclean x-local-distclean \
+       extraclean extraclean-recursive local-extraclean x-local-extraclean \
+       install local-install $(INSTALL_TARGETS)
+
diff --git a/build/win32/.cvsignore b/build/win32/.cvsignore
new file mode 100644 (file)
index 0000000..3a75560
--- /dev/null
@@ -0,0 +1,2 @@
+Apache.aps
+Apache.rc
diff --git a/build/win32/apache.ico b/build/win32/apache.ico
new file mode 100644 (file)
index 0000000..5e8adcb
Binary files /dev/null and b/build/win32/apache.ico differ
diff --git a/build/win32/win32ver.awk b/build/win32/win32ver.awk
new file mode 100644 (file)
index 0000000..8862aa8
--- /dev/null
@@ -0,0 +1,108 @@
+BEGIN {
+
+  # ff bits: 1(debug), 2(prerelease), 4(patched), 8(vendor) and 32(special)
+  # debug is summed based on the /Define _DEBUG
+  # prerelease is based on the -dev extension,
+  # patched is based on a non-standard "-ver" extension, 
+  # special and vendor are toggled by their args.
+  #
+  ff = 0;
+
+  file=ARGV[1];
+  desc=ARGV[2];
+  rel_h=ARGV[3];
+
+  i = 4;
+  while (length(ARGV[i])) {
+    if (match(ARGV[i], /icon=/)) {
+      icon = substr(ARGV[i], 6);
+    }
+    if (match(ARGV[i], /vendor=/)) {
+      vendor = substr(ARGV[i], 8);
+      ff = ff + 8;
+    }
+    if (match(ARGV[i], /special=/)) {
+      special = substr(ARGV[i], 9);
+      ff = ff + 32;
+    }
+    i = i + 1
+  }
+
+  i = i - 1;
+  while (i) {
+    delete ARGV[i];
+    i = i - 1;
+  }
+
+  while ((getline < rel_h) > 0) {
+    if (match ($0, /^#define AP_SERVER_BASEREVISION "[^"]+"/)) {
+      ver = substr($0, RSTART + 32, RLENGTH - 33);
+    }
+  }
+
+  verc = ver;
+  gsub(/\./, ",", verc);
+  if (build) {
+    sub(/-.*/, "", verc)
+    verc = verc "," build;
+  } else if (sub(/-dev/, ",0", verc)) {
+      ff = ff + 2;
+  } else if (!sub(/-alpha/, ",10", verc)  \
+          && !sub(/-beta/, ",100", verc)  \
+          && !sub(/-gold/, ",200", verc)) {
+    sub(/-.*/, "", verc);
+    verc = verc "," 0;
+  }
+  
+  if (length(vendor)) {
+    ff = ff + 8;
+  }
+
+  if (length(icon)) {
+    print "1 ICON DISCARDABLE \"" icon "\"";
+  }
+  print "1 VERSIONINFO";
+  print " FILEVERSION " verc "";
+  print " PRODUCTVERSION " verc "";
+  print " FILEFLAGSMASK 0x3fL";
+  print "#if defined(_DEBUG)"
+  print " FILEFLAGS 0x" sprintf("%02x", ff + 1) "L";
+  print "#else"
+  print " FILEFLAGS 0x" sprintf("%02x", ff) "L";
+  print "#endif"
+  print " FILEOS 0x40004L";
+  print " FILETYPE 0x1L";
+  print " FILESUBTYPE 0x0L";
+  print "BEGIN";
+  print "  BLOCK \"StringFileInfo\"";
+  print "  BEGIN";
+  print "    BLOCK \"00000000\"";
+  print "    BEGIN";
+  print "      VALUE \"Comments\", \"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 <http://www.apache.org/>\\0\"";
+  print "      VALUE \"CompanyName\", \"Apache Software Foundation.\\0\"";
+  print "      VALUE \"FileDescription\", \"" desc "\\0\"";
+  print "      VALUE \"FileVersion\", \"" ver "\\0\"";
+  print "      VALUE \"InternalName\", \"" file "\\0\"";
+  print "      VALUE \"LegalCopyright\", \"Copyright (c) 2001, " \
+        "The Apache Software Foundation.  Current License is available from " \
+        "<http://www.apache.org/LICENSE.txt>\\0\"";
+  print "      VALUE \"OriginalFilename\", \"" file ".exe\\0\"";
+  if (vendor) {
+    print "      VALUE \"PrivateBuild\", \"" vendor "\\0\"";
+  }
+  if (special) {
+    print "      VALUE \"SpecialBuild\", \"" vendor "\\0\"";
+  }
+  print "      VALUE \"ProductName\", \"Apache httpd Server\\0\"";
+  print "      VALUE \"ProductVersion\", \"" ver "\\0\"";
+  print "    END";
+  print "  END";
+  print "  BLOCK \"VarFileInfo\"";
+  print "  BEGIN";
+  print "    VALUE \"Translation\", 0, 1200";
+  print "  END";
+  print "END";
+}
\ No newline at end of file
diff --git a/docs/docroot/apache_pb.png b/docs/docroot/apache_pb.png
new file mode 100644 (file)
index 0000000..eb99a8c
Binary files /dev/null and b/docs/docroot/apache_pb.png differ
diff --git a/docs/docroot/apache_pb2.png b/docs/docroot/apache_pb2.png
new file mode 100644 (file)
index 0000000..28baa70
Binary files /dev/null and b/docs/docroot/apache_pb2.png differ
diff --git a/docs/docroot/index.html.cz.iso8859-2 b/docs/docroot/index.html.cz.iso8859-2
new file mode 100644 (file)
index 0000000..e0cd2ba
--- /dev/null
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+  <META HTTP-Equiv="Content-Type" CONTENT="text/html; charset=iso-8859-2">
+  <TITLE>Testovací stránka instalace web serveru Apache</TITLE>
+ </HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+  BGCOLOR="#FFFFFF"
+  TEXT="#000000"
+  LINK="#0000FF"
+  VLINK="#000080"
+  ALINK="#FF0000"
+ >
+  <H1 ALIGN="CENTER">
+   Funguje to! Na tomto serveru bì¾í Apache!
+  </H1>
+  <P>
+  Pokud vidíte tuto zprávu, správce tohoto poèítaèe právì Ãºspì¹nì
+  nainstaloval Apache web server.
+  Teï je¹tì musí doplnit obsah a nahradit tuto standardní uvítací
+  stránku, nebo pøesmìrovat server na skuteènou domovskou stránku.
+  </P>
+  <HR>
+  <BLOCKQUOTE>
+   Pokud vidíte tuto zprávu místo oèekávané domovské stránky, 
+   <STRONG>kontaktujte, prosím, správce pøíslu¹ného web
+   serveru.</STRONG> (Zkuste poslat e-mail na adresu
+   <SAMP>&lt;Webmaster@<EM>domain</EM>&gt;</SAMP>.) 
+   Pøesto¾e tento server pou¾ívá program Apache, témìø jistì nemá nic
+   spoleèného s Apache Group, proto prosím neposílejte e-maily o tomto
+   serveru nebo jeho obsahu autorùm Apache. Pokud to udìláte, budeme
+   va¹e zprávy <STRONG><BIG>ignorovat</BIG></STRONG>.
+  </BLOCKQUOTE>
+  <HR>
+  <P>
+  Zde najdete
+  <A
+   HREF="manual/"
+  >dokumentaci</A>
+  Apache (anglicky) obsa¾enou v této distribuci.
+  </P>
+  <P>
+  Následující logo mù¾e být pou¾ito bez omezení na web serveru s
+  programem Apache. Dìkujeme za pou¾ití Apache!
+  </P>
+  <DIV ALIGN="CENTER">
+   <IMG SRC="apache_pb.gif" ALT="">
+  </DIV>
+ </BODY>
+</HTML>
diff --git a/docs/docroot/index.html.hr.iso8859-2 b/docs/docroot/index.html.hr.iso8859-2
new file mode 100644 (file)
index 0000000..7af963d
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2">
+<title>Testna Stranica Apache Instalacije</title>
+</head>
+<!-- Background white, links blue (unvisited), navy (visited), red
+(active) -->
+<body bgcolor="#FFFFFF" text="#000000" link="#0000FF"
+vlink="#000080" alink="#FF0000">
+<h2 align="center">Èestitamo! Apache web poslu¾itelj funkcionira!</h2>
+<p>Ukoliko vidite ovu stranicu, to znaèi da je instalacija <a
+href="http://www.apache.org/foundation/preFAQ.html">Apache web poslu¾itelja</a> uspje¹no izvr¹ena na ovom raèunalu.
+Sada mo¾ete dodati sadr¾aj u ovaj direktorij te promijeniti ovu stranicu.</p>
+
+<hr width="50%" size="8" />
+<h2 align="center">Oèekivali ste neku drugu stranicu?</h2>
+
+<p>Ovu stranica vidite stoga Â¹to je administrator ovog poslu¾itelja promijenio
+konfiguraciju. Apache Softver Fondacija napisala je softver koji koristi
+administrator ovog web poslu¾itelja i nije odgovorna za odr¾avanje ovog poslu¾itelja. Molimo vas da za sva pitanja <strong>kontaktirate odgovornu
+osobu za odr¾avanje ovog poslu¾itelja.</strong></p>
+
+<hr width="50%" size="8" />
+<p>Ovdje se nalazi <a href="manual/">Dokumentacija</a> o Apache web poslu¾itelju
+(engleski).</p>
+
+<p>Donju slièicu mo¾ete slobodno koristiti na stranicama ovog veb poslu¾itelja.
+Hvala vam Â¹to koristite Apache!</p>
+
+<div align="center"><img src="apache_pb.gif" alt="Apache Web Poslu¾itelj" /></div>
+</body>
+</html>
+
diff --git a/docs/docroot/index.html.nn b/docs/docroot/index.html.nn
new file mode 100644 (file)
index 0000000..67db957
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+  <TITLE>Testside Apache-installasjon</TITLE>
+ </HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+  BGCOLOR="#FFFFFF"
+  TEXT="#000000"
+  LINK="#0000FF"
+  VLINK="#000080"
+  ALINK="#FF0000"
+ >
+  <H1 ALIGN="CENTER">
+   Det gjekk bra! Apache er no installert pÃ¥ denne maskina!
+  </H1>
+  <P>
+  Dersom du kan sjÃ¥ denne sida, har den eller dei som driv denne nettstaden
+  installert <A href="http://www.apache.org/foundation/preFAQ.html">Apache vevtenar</a>.
+  No mÃ¥ denne testsida erstattast med verkeleg innhald.
+  </P>
+  <HR>
+  <BLOCKQUOTE>
+  Dersom du hadde venta Ã¥ sjÃ¥ ei anna side enn denne, bør du
+  <strong>ta kontakt med den som er ansvarleg for denne nettstaden</strong>
+   (Prøv Ã¥ sende e-post til <SAMP>&lt;webmaster@<EM>domene</EM>&gt;</SAMP>.)
+   Sjølv om denne nettstaden vert kjørt pÃ¥ Apache, har den ingen annan
+   tilknytning til Apache-gruppa, som har utvikla programvaren.
+   Ver snill og ikkje send e-post om denne nettstaden eller
+   innhaldet du finn her til utviklarane i Apache-gruppa. 
+   I sÃ¥ tilfelle vil førespurnaden <strong><BIG>ignorerast</BIG></strong>.
+  </BLOCKQUOTE>   
+  <HR>
+  <P>
+  <A
+   HREF="manual/index.html"
+  >Dokumentasjon</A>
+  for Apache er inkludert i denne pakka.
+  </P>
+  <P>
+  Logoen under kan brukast pÃ¥ kva som helst av maskiner som køyrer Apache.
+  Takk for at du nyttar Apache!
+  </P>
+  <DIV ALIGN="CENTER">
+   <IMG SRC="apache_pb.gif" ALT="">
+  </DIV>
+ </BODY>
+</HTML>
diff --git a/docs/docroot/index.html.po.iso8859-2 b/docs/docroot/index.html.po.iso8859-2
new file mode 100644 (file)
index 0000000..8b030bd
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+  <TITLE>Strona testowa instalacji serwera Apache</TITLE>
+  <META http-equiv="Content-Type" content="text/html; charset=iso-8859-2">
+  <!-- translated by Bart Grzybicki, bgrzybicki@morliny.pl - http://www.bgrzybicki.morliny.pl on 19th of December 2000 -->
+ </HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+  BGCOLOR="#FFFFFF"
+  TEXT="#000000"
+  LINK="#0000FF"
+  VLINK="#000080"
+  ALINK="#FF0000"
+ >
+
+
+<P>
+Je¿eli strona ta jest widoczna, oznacza to poprawn± instalacjê serwera Apache.  Mo¿na ju¿ zamieniæ zawarto¶æ tej witryny.
+
+<P><HR WIDTH="50%" SIZE="8">
+
+<H2 ALIGN="CENTER">Czy zamiast spodziewanej witryny WWW widoczna jest ta?</H2>
+
+<P>
+Strona ta jest widoczna, poniewa¿ administrator serwera WWW zmieni³ jego konfiguracjê.  
+Proszê <STRONG>skontaktowaæ siê z osob± odpowiedzialn± za zarz±dzanie tym serwerem.</STRONG> Apache Software Foundation, producent oprogramowania serwerowego Apache, nie administruje t± witryn± i nie jest w stanie pomóc w sprawach zwi±zanych z jej konfiguracj±.
+
+<P><HR WIDTH="50%" SIZE="8">
+
+
+<P>
+Do niniejszej dystrybucji serwera Apache do³±czono anglojêzyczn± <A HREF="manual/index.html">dokumentacjê</A>.
+
+<P>
+Poni¿sze logo, "Powered by Apache", mo¿na stosowaæ bez ograniczeñ. Dziêkujemy za wybranie Apache'a!
+
+<DIV ALIGN="CENTER"><IMG SRC="apache_pb.gif" ALT=""></DIV>
+</BODY>
+</HTML>
diff --git a/docs/docroot/index.html.var b/docs/docroot/index.html.var
new file mode 100644 (file)
index 0000000..12693ff
--- /dev/null
@@ -0,0 +1,34 @@
+index.html.ca
+index.html.cz
+index.html.de
+index.html.dk
+index.html.ee
+index.html.el
+index.html.en
+index.html.es
+index.html.et
+index.html.fr
+index.html.he.iso8859-8
+index.html.it
+index.html.ja.iso2022-jp
+index.html.ja.jis
+index.html.kr.iso-kr
+index.html.kr.iso2022-kr
+index.html.ltz
+index.html.lu
+index.html.nl
+index.html.no
+index.html.po.iso-pl
+index.html.pt
+index.html.pt-br
+index.html.ru.cp-1251
+index.html.ru.cp866
+index.html.ru.iso-ru
+index.html.ru.koi8-r
+index.html.ru.ucs2
+index.html.ru.ucs4
+index.html.ru.utf8
+index.html.se
+index.html.tw
+index.html.tw.Big5
+index.html.var
diff --git a/docs/error/HTTP_BAD_GATEWAY.html.var b/docs/error/HTTP_BAD_GATEWAY.html.var
new file mode 100644 (file)
index 0000000..c6f81de
--- /dev/null
@@ -0,0 +1,66 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Fehlerhaftes Gateway!" -->
+<!--#include virtual="include/top.html" -->
+
+    Der Proxy-Server erhielt eine fehlerhafte Antwort
+    eines Ã¼bergeordneten Servers oder Proxies. 
+
+  <!--#if expr="$REDIRECT_ERROR_NOTES" -->
+    <!--#include virtual="include/spacer.html" -->
+    <!--#echo var="REDIRECT_ERROR_NOTES" -->
+  <!--#endif -->
+     
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Bad Gateway!" -->
+<!--#include virtual="include/top.html" -->
+
+    The proxy server received an invalid
+    response from an upstream server.
+
+  <!--#if expr="$REDIRECT_ERROR_NOTES" -->
+    <!--#include virtual="include/spacer.html" -->
+    <!--#echo var="REDIRECT_ERROR_NOTES" -->
+  <!--#endif -->
+    
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Bad Gateway!" -->
+<!--#include virtual="include/top.html" -->
+
+       El servidor fuente recibio informaci&oacute;n 
+       inv&aacute;lida por parte del servidor destino.
+
+  <!--#if expr="$REDIRECT_ERROR_NOTES" -->
+    <!--#include virtual="include/spacer.html" -->
+    <!--#echo var="REDIRECT_ERROR_NOTES" -->
+  <!--#endif -->
+    
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Gateway incorrect!" -->
+<!--#include virtual="include/top.html" -->
+
+    Le serveur proxy a re&ccedil;u une r&eacute;ponse incorrecte de la part d'un serveur sup&eacute;rieur. 
+
+  <!--#if expr="$REDIRECT_ERROR_NOTES" -->
+    <!--#include virtual="include/spacer.html" -->
+    <!--#echo var="REDIRECT_ERROR_NOTES" -->
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->   
+----------fr--
diff --git a/docs/error/HTTP_BAD_REQUEST.html.var b/docs/error/HTTP_BAD_REQUEST.html.var
new file mode 100644 (file)
index 0000000..938db81
--- /dev/null
@@ -0,0 +1,46 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Fehlerhafte Anfrage!" -->
+<!--#include virtual="include/top.html" -->
+
+    Ihr Browser (oder Proxy) hat eine ungültige Anfrage gesendet,
+    die vom Server nicht beantwortet werden kann.
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Bad request!" -->
+<!--#include virtual="include/top.html" -->
+
+    Your browser (or proxy) sent a request that
+    this server could not understand.
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Bad request!" -->
+<!--#include virtual="include/top.html" -->
+
+       El buscador ha solicitado una operaci&oacute;n
+       que no puede ser procesada por el servidor.
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="demande incorrecte!" -->
+<!--#include virtual="include/top.html" -->
+
+    Votre navigateur (ou votre proxy) a envoy&eacute; une demande que ce serveur n'a pas comprise.
+
+<!--#include virtual="include/bottom.html" -->  
+----------fr--
diff --git a/docs/error/HTTP_FORBIDDEN.html.var b/docs/error/HTTP_FORBIDDEN.html.var
new file mode 100644 (file)
index 0000000..9a30ceb
--- /dev/null
@@ -0,0 +1,84 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Zugriff verweigert!" -->
+<!--#include virtual="include/top.html" -->
+
+  <!--#if expr="$REDIRECT_URL=//$/" -->
+
+    Der Zugriff auf das angeforderte Verzeichnis ist nicht möglich.
+    Entweder ist kein Index-Dokument vorhanden oder das Verzeichnis
+    ist zugriffsgeschützt.
+
+  <!--#else -->
+
+    Der Zugriff auf das angeforderte Objekt ist nicht möglich.
+    Entweder kann es vom Server nicht gelesen werden oder es
+    ist zugriffsgeschützt.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Access forbidden!" -->
+<!--#include virtual="include/top.html" -->
+<!--#if expr="$REDIRECT_URL=//$/" -->
+
+   You don't have permission to access the requested directory.
+   There is either no index document or the directory is read-protected.
+
+  <!--#else -->
+
+   You don't have permission to access the requested object.
+   It is either read-protected or not readable by the server.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Access forbidden!" -->
+<!--#include virtual="include/top.html" -->
+
+  <!--#if expr="$REDIRECT_URL=//$/" -->
+
+        Usted no tiene permiso para accesar a la direcci&oacute;n
+        solicitada. Existe la posibilidad de que el directorio
+        este protegido contra lectura o que no exista la
+        documentaci&oacute;n requerida.
+
+  <!--#else -->
+
+        Usted no tiene permiso de accesar al objeto solicitado.
+        Existe la posibilidad de que este protegido contra
+        lectura o que no haya podido ser leido por el servidor.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Acc&egrave;s interdit!" -->
+<!--#include virtual="include/top.html" -->
+<!--#if expr="$REDIRECT_URL=//$/" -->
+Vous n'avez pas le droit d'acc&eacute;der au r&eacute;pertoire
+demand&eacute;. Soit il n'y a pas de document index soit le r&eacute;pertoire
+est prot&eacute;g&eacute;.
+<!--#else -->
+Vous n'avez pas le droit d'acc&eacute;der &agrave; l'objet
+demand&eacute;. Soit celui-ci est prot&eacute;g&eacute;, soit il ne peut
+&ecirc;tre lu par le serveur.
+<!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_GONE.html.var b/docs/error/HTTP_GONE.html.var
new file mode 100644 (file)
index 0000000..22aef7c
--- /dev/null
@@ -0,0 +1,100 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Objekt nicht mehr verfügbar!" -->
+<!--#include virtual="include/top.html" -->
+
+    Der angeforderte URL existiert auf dem Server nicht mehr
+    und wurde dauerhaft entfernt.
+    Eine Weiterleitungsadresse ist nicht verfügbar.
+
+  <!--#if expr="$HTTP_REFERER" -->
+
+    Bitte informieren Sie den Autor der
+    <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">verweisenden Seite</a>
+    das der Link nicht mehr aktuell ist.
+
+  <!--#else -->
+
+    Falls Sie einem Link von einer anderen Seite gefolgt sind,
+    informieren Sie bitte den Autor der Seite hierüber.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Resource is no longer available!" -->
+<!--#include virtual="include/top.html" -->
+
+   The requested URL is no longer available on this server and there is no
+   forwarding address.
+  
+  <!--#if expr="$HTTP_REFERER" -->
+  
+   Please inform the author of the
+   <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">referring
+   page</a> that the link is outdated.
+   
+  <!--#else -->
+  
+   If you followed a link from a foreign page, please contact the
+   author of this page.
+   
+  <!--#endif -->
+  
+<!--#include virtual="include/bottom.html" --> 
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Resource is no longer available!" -->
+<!--#include virtual="include/top.html" -->
+
+       Los recursos solicitados ya no estan disponibles en
+       este servidor y no existe una direcci&oacute;n alterna.
+
+  <!--#if expr="$HTTP_REFERER" -->
+
+       Le solicitamos que comunique al autor de la 
+       <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">p&aacute;gina 
+       referida</a> que el URL esta fuera de tiempo.
+
+  <!--#else -->
+       
+       Si usted siguio el URL desde una p&aacute;gina externa, 
+       porfavor contacte al autor de esa p&aacute;gina.
+
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Cette ressource n'existe plus!" -->
+<!--#include virtual="include/top.html" -->
+
+   L'URL demand&eacute;e n'est plus accessible sur ce serveur et il
+   n'y a pas d'adresse de suite. 
+
+  <!--#if expr="$HTTP_REFERER" --> 
+
+   Nous vous prions d'informer l'auteur de
+   <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">la 
+   page en question</a> que la r&eacute;f&eacute;rence n'est plus actuelle. 
+
+  <!--#else --> 
+
+   Si vous avez suivi une r&eacute;f&eacute;rence issue d'une page autre, 
+   veuillez contacter l'auteur de cette page.
+
+  <!--#endif -->
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_INTERNAL_SERVER_ERROR.html.var b/docs/error/HTTP_INTERNAL_SERVER_ERROR.html.var
new file mode 100644 (file)
index 0000000..742e235
--- /dev/null
@@ -0,0 +1,86 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Serverfehler!" -->
+<!--#include virtual="include/top.html" -->
+
+  <!--#if expr="$REDIRECT_ERROR_NOTES" -->
+
+    Dir Proxy-Anfrage konnte nicht ausgeführt werden.
+
+    <!--#include virtual="include/spacer.html" -->
+
+    Fehlermeldung:
+    <BR><!--#echo var="REDIRECT_ERROR_NOTES" -->
+
+
+  <!--#else -->
+
+    Die Anfrage kann nicht beantwortet werden, da im Server
+    ein interner Fehler aufgetreten ist.
+    Der Server ist entweder Ã¼berlastet oder ein Fehler in
+    einem CGI-Script ist aufgetreten.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Server error!" -->
+<!--#include virtual="include/top.html" -->
+
+  <!--#if expr="$REDIRECT_ERROR_NOTES" -->
+    <!--#echo var="REDIRECT_ERROR_NOTES" -->
+  <!--#else -->
+
+    The server encountered an internal error and was 
+    unable to complete your request. The server is either
+    overloaded or there was an error in a CGI script.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Server error!" -->
+<!--#include virtual="include/top.html" -->
+
+  <!--#if expr="$REDIRECT_ERROR_NOTES" -->
+    <!--#echo var="REDIRECT_ERROR_NOTES" -->
+  <!--#else -->
+
+       El servidor encontro un error interno y fue
+       imposible completar su solicitud. 
+       Existe tambien la posibilidad de que el servidor 
+       este sobrecargado       o de alg&uacute;n error en un 
+       programa de CGI.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Erreur du serveur!" -->
+<!--#include virtual="include/top.html" -->
+
+  <!--#if expr="$REDIRECT_ERROR_NOTES" -->
+    <!--#echo var="REDIRECT_ERROR_NOTES" -->
+  <!--#else -->
+
+    Le serveur a &egrave;t&eacute; victime d'une erreur interne et n'a pas &eacute;t&eacute;
+    capable de faire aboutir votre requ&ecirc;te. Soit le server est surcharg&eacute;
+    soit il s'agit d'une erreur dans le script CGI.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->   
+----------fr--
diff --git a/docs/error/HTTP_LENGTH_REQUIRED.html.var b/docs/error/HTTP_LENGTH_REQUIRED.html.var
new file mode 100644 (file)
index 0000000..24d6909
--- /dev/null
@@ -0,0 +1,37 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Content-Length-Angabe fehlerhaft!" -->
+<!--#include virtual="include/top.html" -->
+
+    Die Anfrage kann nicht beantwortet werden.
+    Bei Verwendung der <!--#echo encoding="none" var="REDIRECT_METHOD"-->-Methode
+    muß ein korrekter <code>Content-Length</code>-Header
+    angegeben werden. 
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Bad Content-Length!" -->
+<!--#include virtual="include/top.html" -->
+
+    A request with the <!--#echo encoding="none" var="REDIRECT_METHOD"-->
+    method requires a valid <code>Content-Length</code> header.
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Longueur du contenu ill&eacute;gal!" -->
+<!--#include virtual="include/top.html" -->
+
+    Une requ&ecirc;te utilisant la m&eacute;thode <!--#echo encoding="none" var="REDIRECT_METHOD"-->
+    n&eacute;cessite un header valable <code>Content-Length</code> (indiquant la longueur).
+
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_METHOD_NOT_ALLOWED.html.var b/docs/error/HTTP_METHOD_NOT_ALLOWED.html.var
new file mode 100644 (file)
index 0000000..38d2bd8
--- /dev/null
@@ -0,0 +1,48 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Methode nicht erlaubt!" -->
+<!--#include virtual="include/top.html" -->
+
+    Die <!--#echo encoding="none" var="REDIRECT_METHOD"-->-Methode
+    ist für den angeforderten URL nicht erlaubt.
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Method not allowed!" -->
+<!--#include virtual="include/top.html" -->
+
+    The <!--#echo encoding="none" var="REDIRECT_METHOD"-->
+    method is not allowed for the requested URL.
+    
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Method not allowed!" -->
+<!--#include virtual="include/top.html" -->
+
+       El <!--#echo encoding="none" var="REDIRECT_METHOD"-->
+       m&eacute;todo utilizado por su solicitud no esta 
+       permitido por el URL.
+    
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="M&eacute;thode interdite!" -->
+<!--#include virtual="include/top.html" -->
+
+    La m&eacute;thode <!--#echo encoding="none" var="REDIRECT_METHOD"-->
+    n'est pas utilisable pour l'URL requise.
+
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_NOT_ACCEPTABLE.html.var b/docs/error/HTTP_NOT_ACCEPTABLE.html.var
new file mode 100644 (file)
index 0000000..fe4236f
--- /dev/null
@@ -0,0 +1,49 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Kein passendes Objekt gefunden!" -->
+<!--#include virtual="include/top.html" -->
+
+    Unter dem angeforderten URL konnte kein geeignetes Objekt
+    auf dem Server gefunden werden, das dem vom Browser
+    geforderten Format entsprechen würde.  
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="No acceptable object found!" -->
+<!--#include virtual="include/top.html" -->
+
+    An appropriate representation of the requested resource
+    could not be found on this server.
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="No acceptable object found!" -->
+<!--#include virtual="include/top.html" -->
+
+       Una representaci&oacute;n apropiada de los recursos 
+       solicitados no ha podido ser localizada en 
+       este servidor.
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="L'objet trouv&eacute; n'est pas acceptable!" -->
+<!--#include virtual="include/top.html" -->
+
+    Une repr&eacute;sentation appropri&eacute;e de la ressource requise
+    n'a pu &ecirc;tre trouv&eacute;e sur ce serveur.
+
+<!--#include virtual="include/bottom.html" -->  
+----------fr--
diff --git a/docs/error/HTTP_NOT_FOUND.html.var b/docs/error/HTTP_NOT_FOUND.html.var
new file mode 100644 (file)
index 0000000..8a7e8ee
--- /dev/null
@@ -0,0 +1,113 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Objekt nicht gefunden!" -->
+<!--#include virtual="include/top.html" -->
+
+    Der angeforderte URL konnte auf dem Server nicht gefunden werden.
+
+  <!--#if expr="$HTTP_REFERER" -->
+
+    Der Link auf der
+    <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">verweisenden
+Seite<
+/a>
+    scheint falsch oder nicht mehr aktuell zu sein.
+    Bitte informieren Sie den Autor
+    <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">dieser Seite</a>
+    Ã¼ber den Fehler.
+
+  <!--#else -->
+
+    Sofern Sie den URL manuell eingegeben haben,
+    Ã¼berprüfen Sie bitte die Schreibweise und versuchen es erneut.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Object not found!" -->
+<!--#include virtual="include/top.html" -->
+
+    The requested URL was not found on this server.
+
+  <!--#if expr="$HTTP_REFERER" -->
+
+    The link on the
+    <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">referring
+page</a>
+    seems to be wrong or outdated. Please inform the author of
+    <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">that page</a>
+    about the error.
+
+  <!--#else -->
+
+    If you entered the URL manually please check your
+    spelling and try again.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Object not found!" -->
+<!--#include virtual="include/top.html" -->
+
+        El URL requerido no ha sido localizado en
+        este servidor.
+
+  <!--#if expr="$HTTP_REFERER" -->
+
+        El URL en la
+    <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">p&aacute;gina
+referida</a>
+        parece tener algun error o estar fuera de tiempo. Porfavor
+        comunique al autor de
+    <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">la
+p&aacute;gina</a>
+        acerca del error.
+
+  <!--#else -->
+
+        Si usted provisiono el URL de manera manual le solicitamos
+        que porfavor revise los datos e intente de nuevo.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Objet non trouv&eacute;!" -->
+<!--#include virtual="include/top.html" -->
+
+    L'URL requise n'a pu etre trouv&eacute;e sur ce serveur.
+
+  <!--#if expr="$HTTP_REFERER" -->
+
+    La r&eacute;f&eacute;rence sur
+    <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">la page
+cit&eacute;e</a>
+    semble &ecirc;tre erron&eacute;e ou perim&eacute;e. Nous vous prions
+d'informer l'auteur de
+    <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">cette page</a>
+    de cette erreur.
+
+  <!--#else -->
+
+    Si vous avez tap&eacute; l'URL &agrave; la main, veuillez v&eacute;rifier
+    l'orthographe et r&eacute;essayer.
+
+  <!--#endif -->
+
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_NOT_IMPLEMENTED.html.var b/docs/error/HTTP_NOT_IMPLEMENTED.html.var
new file mode 100644 (file)
index 0000000..ed3468e
--- /dev/null
@@ -0,0 +1,45 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Anfrage nicht ausführbar!" -->
+<!--#include virtual="include/top.html" -->
+
+    Die vom Browser angeforderte Aktion wird vom Server
+    nicht unterstützt.
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Cannot proceed request!" -->
+<!--#include virtual="include/top.html" -->
+
+   The server does not support the action requested by the browser.
+   
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Cannot proceed request!" -->
+<!--#include virtual="include/top.html" -->
+
+       El buscador esta solicitando una acci&oacute;n 
+       que no puede ser procesada.
+    
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="La requ&ecirc;te ne peut &ecirc;tre effectu&eacute;e!" -->
+<!--#include virtual="include/top.html" -->
+
+Le serveur n'est pas en mesure d'effectuer l'action requise par le navigateur. 
+
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_PRECONDITION_FAILED.html.var b/docs/error/HTTP_PRECONDITION_FAILED.html.var
new file mode 100644 (file)
index 0000000..164bfce
--- /dev/null
@@ -0,0 +1,44 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Vorbedingung negativ!" -->
+<!--#include virtual="include/top.html" -->
+
+    Die Vorbedingung unter dem angeforderten URL wurde
+    negativ ausgewertet.
+    
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Precondition failed!" -->
+<!--#include virtual="include/top.html" -->
+
+  The precondition on the request for the URL failed positive evaluation.
+  
+<!--#include virtual="include/bottom.html" --> 
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Precondition failed!" -->
+<!--#include virtual="include/top.html" -->
+
+       La precondicion para que exista una 
+       conecci&oacute;n al URL solicitado es falsa.
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Pr&eacute;condition n&eacute;gative!" -->
+<!--#include virtual="include/top.html" -->
+
+La pr&eacute;condition pour l'URL requise a &eacute;t&eacute; &eacute;valu&eacute;e n&eacute;gativement.
+
+<!--#include virtual="include/bottom.html" -->----------fr--
diff --git a/docs/error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var b/docs/error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var
new file mode 100644 (file)
index 0000000..af7ef26
--- /dev/null
@@ -0,0 +1,51 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Übergebene Daten zu groß!" -->
+<!--#include virtual="include/top.html" -->
+
+    Die bei der Anfrage Ã¼bermittelnden Daten sind für
+    die <!--#echo encoding="none" var="REDIRECT_METHOD"-->-Methode nicht erlaubt
+    oder die Datenmenge hat das Maximum Ã¼berschritten.
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Request entity too large!" -->
+<!--#include virtual="include/top.html" -->
+
+  The <!--#echo encoding="none" var="REDIRECT_METHOD"--> method does not allow the data
+  transmitted, or the data volume exceeds the capacity limit.
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Request entity too large!" -->
+<!--#include virtual="include/top.html" -->
+
+       Los recursos establecidos no permiten peticiones con
+       el <!--#echo encoding="none" var="REDIRECT_METHOD"--> m&eacute;todo 
+       suministrado por su solicitud, o, la cantidad de datos 
+       provistos exceden los l&iacute;mites de capacidad.
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Volume de la demande trop grand!" -->
+<!--#include virtual="include/top.html" -->
+
+La m&eacute;thode <!--#echo encoding="none" var="REDIRECT_METHOD"--> 
+n'autorise pas le transfert de ces donn&eacute;es ou bien le volume
+des donn&eacute;es exc&egrave;de la limite de capacit&eacute;.
+
+<!--#include virtual="include/bottom.html" -->               
+----------fr--
diff --git a/docs/error/HTTP_REQUEST_TIME_OUT.html.var b/docs/error/HTTP_REQUEST_TIME_OUT.html.var
new file mode 100644 (file)
index 0000000..6295279
--- /dev/null
@@ -0,0 +1,48 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Zeitlimit Ã¼berschritten!" -->
+<!--#include virtual="include/top.html" -->
+
+    Der Server konnte nicht mehr länger auf die Beendigung
+    der Browseranfrage warten, die Netzwerkverbindung wurde
+    vom Server geschlossen.
+    
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Request time-out!" -->
+<!--#include virtual="include/top.html" -->
+
+   The server closed the network connection because the browser
+   didn't finish the request within the specified time.
+   
+<!--#include virtual="include/bottom.html" --> 
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Request time-out!" -->
+<!--#include virtual="include/top.html" -->
+
+       El servidor ha cerrado la conecci&oacute;n de red 
+       debido a que el buscador no termino la solicitud
+       dentro del tiempo especificado.
+    
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Pr&eacute;condition n&eacute;gative!" -->
+<!--#include virtual="include/top.html" -->
+
+La pr&eacute;condition pour l'URL requise a &eacute;t&eacute; &eacute;valu&eacute;e n&eacute;gativement.
+
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_REQUEST_URI_TOO_LARGE.html.var b/docs/error/HTTP_REQUEST_URI_TOO_LARGE.html.var
new file mode 100644 (file)
index 0000000..f34de55
--- /dev/null
@@ -0,0 +1,48 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Übergebener URI zu groß!" -->
+<!--#include virtual="include/top.html" -->
+
+    Der bei der Anfrage Ã¼bermittelnde URI Ã¼berschreitet
+    die maximale Länge.
+    Die Anfrage kann nicht ausgeführt werden.
+    
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Submitted URI too large!" -->
+<!--#include virtual="include/top.html" -->
+
+   The length of the requested URL exceeds the capacity limit for this server.
+   The request cannot be processed.
+   
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Submitted URI too large!" -->
+<!--#include virtual="include/top.html" -->
+
+       Su solicitud no puede procesarse debido a que la
+       longitud del URL excede la capacidad limite del 
+       servidor. 
+    
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="L'URI demandee est trop longue!" -->
+<!--#include virtual="include/top.html" -->
+
+La longueur de l'URL demand&eacute;e exc&egrave;de la limite de capacit&egrave; pour ce serveur. Nous ne pouvons donner suite &agrave; votre requ&ecirc;te.
+
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_SERVICE_UNAVAILABLE.html.var b/docs/error/HTTP_SERVICE_UNAVAILABLE.html.var
new file mode 100644 (file)
index 0000000..d29f16f
--- /dev/null
@@ -0,0 +1,56 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Zugriff nicht möglich!" -->
+<!--#include virtual="include/top.html" -->
+
+    Der Server ist derzeit nicht in der Lage die Anfrage
+    zu bearbeiten. Entweder ist der Server derzeit Ã¼berlastet
+    oder wegen Wartungsarbeiten nicht verfügbar.
+    Bitte versuchen Sie es später wieder. 
+    
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Service unavailable!" -->
+<!--#include virtual="include/top.html" -->
+
+    The server is temporarily unable to service your
+    request due to maintenance downtime or capacity
+    problems. Please try again later. 
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Service unavailable!" -->
+<!--#include virtual="include/top.html" -->
+
+       El servidor no puede atender su solicitud por
+       el momento debido a problemas de mantenimiento
+       o de capacidad.
+
+       Le solicitamos que porfavor repita la operaci&oacute;n
+       mas tarde.
+       
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Service inaccessible!" -->
+<!--#include virtual="include/top.html" -->
+
+    En raison de travaux de maintenance ou de probl&egrave;mes de capacit&eacute;
+    le serveur n'est pas en mesure de r&eacute;pondre &agrave; votre requ&ecirc;te pour l'instant.
+    Veuillez r&eacute;essayer plus tard.
+
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_UNAUTHORIZED.html.var b/docs/error/HTTP_UNAUTHORIZED.html.var
new file mode 100644 (file)
index 0000000..4152b66
--- /dev/null
@@ -0,0 +1,84 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Authentikation fehlgeschlagen!" -->
+<!--#include virtual="include/top.html" -->
+
+    Der Server konnte nicht verifizieren, ob Sie authorisiert sind,
+    auf den URL "<!--#echo encoding="url" var="REDIRECT_URL"-->" zuzugreifen.
+    Entweder wurden falsche Referenzen (z.B. ein falsches Passwort)
+    angeben oder ihr Browser versteht nicht, wie die geforderten
+    Referenzen zu Ã¼bermitteln sind.
+
+<!--#include virtual="include/spacer.html" -->
+
+    Sofern Sie für den Zugriff berechtigt sind, Ã¼berprüfen Sie bitte
+    die eingegebene User-ID und das Passwort und versuchen es erneut.  
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Authentication required!" -->
+<!--#include virtual="include/top.html" -->
+
+    This server could not verify that you are authorized to access
+    the URL "<!--#echo encoding="url" var="REDIRECT_URL" -->".
+    You either supplied the wrong credentials (e.g., bad password), or your
+    browser doesn't understand how to supply the credentials required.
+
+  <!--#include virtual="include/spacer.html" -->
+
+    In case you are allowed to request the document, please
+    check your user-id and password and try again.
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Authentication required!" -->
+<!--#include virtual="include/top.html" -->
+
+ El servidor no puede certificar que usted este autorizado
+ para accesar el URL "<!--#echo encoding="url" var="REDIRECT_URL" -->".
+ Usted pudo suministrar informaci&oacute;n incorrecta accidentalmente
+ (ejem. una contrase&ntilde;a inv&aacute;lida) o, el buscador no sabe 
+ como suministrar la informaci&oacute;n requerida.
+
+  <!--#include virtual="include/spacer.html" -->
+
+ En caso de que a usted le este permitido el uso del
+ documento requerido, le solicitamos de la manera mas atenta
+ que porfavor vuelva a intentar la operaci&oacute;n suministrando
+ nuevamente su numero de identificaci&oacute;n y su contrase&ntilde;a.
+
+ Muchas Gracias.
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="Autorisation requise!" -->
+<!--#include virtual="include/top.html" -->
+
+    Ce server n'a pas &eacute;t&eacute; en mesure de v&eacute;rifier que
+    vous &ecirc;tes autoris&eacute; &agrave; acc&eacute;der &agrave; cette
+    URL "<!--#echo encoding="url" var="REDIRECT_URL" -->".
+
+    Vous avez ou bien fourni des coordonn&eacute;es erron&eacute;es
+    (p.ex. mot de passe inexact) ou bien votre navigateur ne parvient
+    pas &agrave; fournir les donn&eacute;es exactes.
+
+  <!--#include virtual="include/spacer.html" -->
+
+    Si vous &ecirc;tez autoris&eacute; &agrave; requ&eacute;rir le document,
+    veuillez v&eacute;rifier votre nom d'utilisateur et votre mot de passe
+    et r&eacute;essayer.
+
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var b/docs/error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var
new file mode 100644 (file)
index 0000000..78c48bd
--- /dev/null
@@ -0,0 +1,45 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Nicht unterstütztes Format!" -->
+<!--#include virtual="include/top.html" -->
+
+    Das bei der Anfrage Ã¼bermittelte Format (media type)
+    wird vom Server nicht unterstützt.
+    
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Unsupported media type!" -->
+<!--#include virtual="include/top.html" -->
+
+   The server does not support the media type transmitted in the request.
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Unsupported media type!" -->
+<!--#include virtual="include/top.html" -->
+
+       Los datos de su solicitud no se encuentran en
+       un formato aceptado por este recurso.
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="type de m&eacute;dia invalide!" -->
+<!--#include virtual="include/top.html" -->
+
+Le serveur ne supporte pas le type de m&eacute;dia utilis&eacute; dans votre requ&ecirc;te. 
+
+<!--#include virtual="include/bottom.html" -->
+----------fr--
diff --git a/docs/error/HTTP_VARIANT_ALSO_VARIES.html.var b/docs/error/HTTP_VARIANT_ALSO_VARIES.html.var
new file mode 100644 (file)
index 0000000..b558284
--- /dev/null
@@ -0,0 +1,51 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+<!--#set var="TITLE" value="Variante ebenfalls veränderlich!" -->
+<!--#include virtual="include/top.html" -->
+
+    Ein Zugriff auf das angeforderte Objekt bzw. einer
+    Variante dieses Objektes ist nicht möglich, da es ebenfalls
+    ein variables Objekt darstellt.
+
+<!--#include virtual="include/bottom.html" -->
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+<!--#set var="TITLE" value="Variant also varies!" -->
+<!--#include virtual="include/top.html" -->
+
+    A variant for the requested entity
+    is itself a negotiable resource.
+    Access not possible.
+
+<!--#include virtual="include/bottom.html" -->
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+<!--#set var="TITLE" value="Variant also varies!" -->
+<!--#include virtual="include/top.html" -->
+
+       No es posible tener acceso debido a que
+       una variante de la solicitud es por si 
+       misma un recurso negociable.
+
+<!--#include virtual="include/bottom.html" -->
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+<!--#set var="TITLE" value="La variante varie elle-m&ecirc;me!" -->
+<!--#include virtual="include/top.html" -->
+
+    Une variante pour l'entit&eacute; requise
+    est elle-m&ecirc;me une ressource n&eacute;gociable.
+    L'acc&egrave;s est impossible.
+
+<!--#include virtual="include/bottom.html" -->  
+----------fr--
diff --git a/docs/error/contact.html.var b/docs/error/contact.html.var
new file mode 100644 (file)
index 0000000..f39136b
--- /dev/null
@@ -0,0 +1,30 @@
+Content-language: de
+Content-type: text/html
+Body:----------de--
+ Sofern Sie dies für eine Fehlfunktion des Servers halten,
+ informieren Sie bitte den
+ <A HREF="mailto:<!--#echo encoding="none" var="SERVER_ADMIN" -->">Webmaster</A>
+ hierüber.
+----------de--
+
+Content-language: en
+Content-type: text/html
+Body:----------en--
+ In case you think this is a server error, please contact
+ the <A HREF="mailto:<!--#echo encoding="none" var="SERVER_ADMIN" -->">Webmaster</A>
+----------en--
+
+Content-language: es
+Content-type: text/html
+Body:----------es--
+Favor de contactar al 
+<A HREF="mailto:<!--#echo encoding="none" var="SERVER_ADMIN" -->">Webmaster</A>
+en caso de que usted crea que existe un error en el servidor. 
+----------es--
+
+Content-language: fr
+Content-type: text/html
+Body:----------fr--
+Si vous pensez qu'il s'agit d'une erreur du serveur, veuillez contacter 
+le <A HREF="mailto:<!--#echo encoding="none" var="SERVER_ADMIN" -->">gestionnaire du site</A>   
+----------fr--
diff --git a/docs/error/include/bottom.html b/docs/error/include/bottom.html
new file mode 100644 (file)
index 0000000..a3b2369
--- /dev/null
@@ -0,0 +1,12 @@
+<!--#include virtual="spacer.html" -->
+<!--#include virtual="../contact" -->
+</DL>
+<!--#if expr="$INCLUDE_LOGO=yes" -->
+ <BR CLEAR=<!--#if expr="$ALIGN_LOGO=left" -->right<!--#else -->left<!--#endif -->>
+<!--#endif -->
+
+<TABLE BORDER=0 CELLPADDING=6 WIDTH="100%">
+<TR><TD ALIGN=CENTER BGCOLOR="#000000">
+<FONT SIZE="+2" COLOR="#FFFFFF"><strong>Error <!--#echo encoding="none" var="REDIRECT_STATUS" --></strong></FONT>
+</TD></TR></TABLE>
+<!--#include virtual="footer.html" -->
diff --git a/docs/error/include/spacer.html b/docs/error/include/spacer.html
new file mode 100644 (file)
index 0000000..70c5b25
--- /dev/null
@@ -0,0 +1 @@
+</DL><DL><DD>
diff --git a/docs/error/include/top.html b/docs/error/include/top.html
new file mode 100644 (file)
index 0000000..3e24e48
--- /dev/null
@@ -0,0 +1,12 @@
+<!--#include virtual="header.html" -->
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000CC">
+<BR>
+<TABLE BORDER=0 CELLPADDING=6 WIDTH="100%">
+<TR><TD ALIGN=CENTER BGCOLOR="#000000">
+<FONT SIZE="+2" COLOR="#FFFFFF"><strong><!--#echo encoding="none" var="TITLE" --></strong></FONT>
+</TD></TR></TABLE>
+<!--#if expr="$INCLUDE_LOGO=yes" -->
+ <IMG ALIGN=<!--#echo encoding="none" var="ALIGN_LOGO" --> SRC="/error/images/logo.gif" ALT="">
+<!--#endif -->
+<DL>
+<DD>
diff --git a/docs/icons/a.png b/docs/icons/a.png
new file mode 100644 (file)
index 0000000..c184025
Binary files /dev/null and b/docs/icons/a.png differ
diff --git a/docs/icons/alert.black.png b/docs/icons/alert.black.png
new file mode 100644 (file)
index 0000000..af6b124
Binary files /dev/null and b/docs/icons/alert.black.png differ
diff --git a/docs/icons/alert.red.png b/docs/icons/alert.red.png
new file mode 100644 (file)
index 0000000..b9222fd
Binary files /dev/null and b/docs/icons/alert.red.png differ
diff --git a/docs/icons/apache_pb.png b/docs/icons/apache_pb.png
new file mode 100644 (file)
index 0000000..eb99a8c
Binary files /dev/null and b/docs/icons/apache_pb.png differ
diff --git a/docs/icons/apache_pb2.png b/docs/icons/apache_pb2.png
new file mode 100644 (file)
index 0000000..28baa70
Binary files /dev/null and b/docs/icons/apache_pb2.png differ
diff --git a/docs/icons/back.png b/docs/icons/back.png
new file mode 100644 (file)
index 0000000..2d8d353
Binary files /dev/null and b/docs/icons/back.png differ
diff --git a/docs/icons/ball.gray.png b/docs/icons/ball.gray.png
new file mode 100644 (file)
index 0000000..7b756f2
Binary files /dev/null and b/docs/icons/ball.gray.png differ
diff --git a/docs/icons/ball.red.png b/docs/icons/ball.red.png
new file mode 100644 (file)
index 0000000..05f3e50
Binary files /dev/null and b/docs/icons/ball.red.png differ
diff --git a/docs/icons/binary.png b/docs/icons/binary.png
new file mode 100644 (file)
index 0000000..c5119d1
Binary files /dev/null and b/docs/icons/binary.png differ
diff --git a/docs/icons/binhex.png b/docs/icons/binhex.png
new file mode 100644 (file)
index 0000000..eff5322
Binary files /dev/null and b/docs/icons/binhex.png differ
diff --git a/docs/icons/blank.png b/docs/icons/blank.png
new file mode 100644 (file)
index 0000000..3802c03
Binary files /dev/null and b/docs/icons/blank.png differ
diff --git a/docs/icons/bomb.png b/docs/icons/bomb.png
new file mode 100644 (file)
index 0000000..5261a05
Binary files /dev/null and b/docs/icons/bomb.png differ
diff --git a/docs/icons/box1.png b/docs/icons/box1.png
new file mode 100644 (file)
index 0000000..c55fccf
Binary files /dev/null and b/docs/icons/box1.png differ
diff --git a/docs/icons/box2.png b/docs/icons/box2.png
new file mode 100644 (file)
index 0000000..26d1432
Binary files /dev/null and b/docs/icons/box2.png differ
diff --git a/docs/icons/broken.png b/docs/icons/broken.png
new file mode 100644 (file)
index 0000000..e8fd150
Binary files /dev/null and b/docs/icons/broken.png differ
diff --git a/docs/icons/burst.png b/docs/icons/burst.png
new file mode 100644 (file)
index 0000000..2329898
Binary files /dev/null and b/docs/icons/burst.png differ
diff --git a/docs/icons/c.png b/docs/icons/c.png
new file mode 100644 (file)
index 0000000..41593b3
Binary files /dev/null and b/docs/icons/c.png differ
diff --git a/docs/icons/comp.blue.png b/docs/icons/comp.blue.png
new file mode 100644 (file)
index 0000000..60ff156
Binary files /dev/null and b/docs/icons/comp.blue.png differ
diff --git a/docs/icons/comp.gray.png b/docs/icons/comp.gray.png
new file mode 100644 (file)
index 0000000..01538f8
Binary files /dev/null and b/docs/icons/comp.gray.png differ
diff --git a/docs/icons/compressed.png b/docs/icons/compressed.png
new file mode 100644 (file)
index 0000000..de7276d
Binary files /dev/null and b/docs/icons/compressed.png differ
diff --git a/docs/icons/continued.png b/docs/icons/continued.png
new file mode 100644 (file)
index 0000000..8f656e2
Binary files /dev/null and b/docs/icons/continued.png differ
diff --git a/docs/icons/dir.png b/docs/icons/dir.png
new file mode 100644 (file)
index 0000000..6b97905
Binary files /dev/null and b/docs/icons/dir.png differ
diff --git a/docs/icons/down.png b/docs/icons/down.png
new file mode 100644 (file)
index 0000000..be3904b
Binary files /dev/null and b/docs/icons/down.png differ
diff --git a/docs/icons/dvi.png b/docs/icons/dvi.png
new file mode 100644 (file)
index 0000000..19c417f
Binary files /dev/null and b/docs/icons/dvi.png differ
diff --git a/docs/icons/f.png b/docs/icons/f.png
new file mode 100644 (file)
index 0000000..c946f5b
Binary files /dev/null and b/docs/icons/f.png differ
diff --git a/docs/icons/folder.open.png b/docs/icons/folder.open.png
new file mode 100644 (file)
index 0000000..dd2d7e0
Binary files /dev/null and b/docs/icons/folder.open.png differ
diff --git a/docs/icons/folder.png b/docs/icons/folder.png
new file mode 100644 (file)
index 0000000..6b97905
Binary files /dev/null and b/docs/icons/folder.png differ
diff --git a/docs/icons/folder.sec.png b/docs/icons/folder.sec.png
new file mode 100644 (file)
index 0000000..833f59a
Binary files /dev/null and b/docs/icons/folder.sec.png differ
diff --git a/docs/icons/forward.png b/docs/icons/forward.png
new file mode 100644 (file)
index 0000000..c5584a4
Binary files /dev/null and b/docs/icons/forward.png differ
diff --git a/docs/icons/generic.png b/docs/icons/generic.png
new file mode 100644 (file)
index 0000000..0227cab
Binary files /dev/null and b/docs/icons/generic.png differ
diff --git a/docs/icons/generic.red.png b/docs/icons/generic.red.png
new file mode 100644 (file)
index 0000000..be63249
Binary files /dev/null and b/docs/icons/generic.red.png differ
diff --git a/docs/icons/generic.sec.png b/docs/icons/generic.sec.png
new file mode 100644 (file)
index 0000000..0bd3d96
Binary files /dev/null and b/docs/icons/generic.sec.png differ
diff --git a/docs/icons/hand.right.png b/docs/icons/hand.right.png
new file mode 100644 (file)
index 0000000..93035c6
Binary files /dev/null and b/docs/icons/hand.right.png differ
diff --git a/docs/icons/hand.up.png b/docs/icons/hand.up.png
new file mode 100644 (file)
index 0000000..1405a6f
Binary files /dev/null and b/docs/icons/hand.up.png differ
diff --git a/docs/icons/icon.sheet.png b/docs/icons/icon.sheet.png
new file mode 100644 (file)
index 0000000..b875cb6
Binary files /dev/null and b/docs/icons/icon.sheet.png differ
diff --git a/docs/icons/image1.png b/docs/icons/image1.png
new file mode 100644 (file)
index 0000000..c1374fd
Binary files /dev/null and b/docs/icons/image1.png differ
diff --git a/docs/icons/image2.png b/docs/icons/image2.png
new file mode 100644 (file)
index 0000000..606d4fb
Binary files /dev/null and b/docs/icons/image2.png differ
diff --git a/docs/icons/image3.png b/docs/icons/image3.png
new file mode 100644 (file)
index 0000000..701fb1e
Binary files /dev/null and b/docs/icons/image3.png differ
diff --git a/docs/icons/index.png b/docs/icons/index.png
new file mode 100644 (file)
index 0000000..9a0de35
Binary files /dev/null and b/docs/icons/index.png differ
diff --git a/docs/icons/layout.png b/docs/icons/layout.png
new file mode 100644 (file)
index 0000000..0a97c1c
Binary files /dev/null and b/docs/icons/layout.png differ
diff --git a/docs/icons/left.png b/docs/icons/left.png
new file mode 100644 (file)
index 0000000..d6e2404
Binary files /dev/null and b/docs/icons/left.png differ
diff --git a/docs/icons/link.png b/docs/icons/link.png
new file mode 100644 (file)
index 0000000..4714d0e
Binary files /dev/null and b/docs/icons/link.png differ
diff --git a/docs/icons/movie.png b/docs/icons/movie.png
new file mode 100644 (file)
index 0000000..5615180
Binary files /dev/null and b/docs/icons/movie.png differ
diff --git a/docs/icons/p.png b/docs/icons/p.png
new file mode 100644 (file)
index 0000000..3fbe0e8
Binary files /dev/null and b/docs/icons/p.png differ
diff --git a/docs/icons/patch.png b/docs/icons/patch.png
new file mode 100644 (file)
index 0000000..808ed78
Binary files /dev/null and b/docs/icons/patch.png differ
diff --git a/docs/icons/pdf.png b/docs/icons/pdf.png
new file mode 100644 (file)
index 0000000..516142b
Binary files /dev/null and b/docs/icons/pdf.png differ
diff --git a/docs/icons/pie0.png b/docs/icons/pie0.png
new file mode 100644 (file)
index 0000000..12e0200
Binary files /dev/null and b/docs/icons/pie0.png differ
diff --git a/docs/icons/pie1.png b/docs/icons/pie1.png
new file mode 100644 (file)
index 0000000..c44c793
Binary files /dev/null and b/docs/icons/pie1.png differ
diff --git a/docs/icons/pie2.png b/docs/icons/pie2.png
new file mode 100644 (file)
index 0000000..e0b7167
Binary files /dev/null and b/docs/icons/pie2.png differ
diff --git a/docs/icons/pie3.png b/docs/icons/pie3.png
new file mode 100644 (file)
index 0000000..820a3c3
Binary files /dev/null and b/docs/icons/pie3.png differ
diff --git a/docs/icons/pie4.png b/docs/icons/pie4.png
new file mode 100644 (file)
index 0000000..35490d8
Binary files /dev/null and b/docs/icons/pie4.png differ
diff --git a/docs/icons/pie5.png b/docs/icons/pie5.png
new file mode 100644 (file)
index 0000000..359b7d3
Binary files /dev/null and b/docs/icons/pie5.png differ
diff --git a/docs/icons/pie6.png b/docs/icons/pie6.png
new file mode 100644 (file)
index 0000000..4b293ea
Binary files /dev/null and b/docs/icons/pie6.png differ
diff --git a/docs/icons/pie7.png b/docs/icons/pie7.png
new file mode 100644 (file)
index 0000000..6bfa2d0
Binary files /dev/null and b/docs/icons/pie7.png differ
diff --git a/docs/icons/pie8.png b/docs/icons/pie8.png
new file mode 100644 (file)
index 0000000..716cf28
Binary files /dev/null and b/docs/icons/pie8.png differ
diff --git a/docs/icons/portal.png b/docs/icons/portal.png
new file mode 100644 (file)
index 0000000..937c0f8
Binary files /dev/null and b/docs/icons/portal.png differ
diff --git a/docs/icons/ps.png b/docs/icons/ps.png
new file mode 100644 (file)
index 0000000..ccccf73
Binary files /dev/null and b/docs/icons/ps.png differ
diff --git a/docs/icons/quill.png b/docs/icons/quill.png
new file mode 100644 (file)
index 0000000..b697770
Binary files /dev/null and b/docs/icons/quill.png differ
diff --git a/docs/icons/right.png b/docs/icons/right.png
new file mode 100644 (file)
index 0000000..41f8529
Binary files /dev/null and b/docs/icons/right.png differ
diff --git a/docs/icons/screw1.png b/docs/icons/screw1.png
new file mode 100644 (file)
index 0000000..11673ab
Binary files /dev/null and b/docs/icons/screw1.png differ
diff --git a/docs/icons/screw2.png b/docs/icons/screw2.png
new file mode 100644 (file)
index 0000000..5d7d2cf
Binary files /dev/null and b/docs/icons/screw2.png differ
diff --git a/docs/icons/script.png b/docs/icons/script.png
new file mode 100644 (file)
index 0000000..2520570
Binary files /dev/null and b/docs/icons/script.png differ
diff --git a/docs/icons/small/back.png b/docs/icons/small/back.png
new file mode 100644 (file)
index 0000000..2257df2
Binary files /dev/null and b/docs/icons/small/back.png differ
diff --git a/docs/icons/small/binary.png b/docs/icons/small/binary.png
new file mode 100644 (file)
index 0000000..2e2e1b0
Binary files /dev/null and b/docs/icons/small/binary.png differ
diff --git a/docs/icons/small/binhex.png b/docs/icons/small/binhex.png
new file mode 100644 (file)
index 0000000..9deab41
Binary files /dev/null and b/docs/icons/small/binhex.png differ
diff --git a/docs/icons/small/blank.png b/docs/icons/small/blank.png
new file mode 100644 (file)
index 0000000..86f57a5
Binary files /dev/null and b/docs/icons/small/blank.png differ
diff --git a/docs/icons/small/broken.png b/docs/icons/small/broken.png
new file mode 100644 (file)
index 0000000..79c998c
Binary files /dev/null and b/docs/icons/small/broken.png differ
diff --git a/docs/icons/small/burst.png b/docs/icons/small/burst.png
new file mode 100644 (file)
index 0000000..2b21436
Binary files /dev/null and b/docs/icons/small/burst.png differ
diff --git a/docs/icons/small/comp1.png b/docs/icons/small/comp1.png
new file mode 100644 (file)
index 0000000..6d8c345
Binary files /dev/null and b/docs/icons/small/comp1.png differ
diff --git a/docs/icons/small/comp2.png b/docs/icons/small/comp2.png
new file mode 100644 (file)
index 0000000..57f7ad1
Binary files /dev/null and b/docs/icons/small/comp2.png differ
diff --git a/docs/icons/small/compressed.png b/docs/icons/small/compressed.png
new file mode 100644 (file)
index 0000000..43acd8b
Binary files /dev/null and b/docs/icons/small/compressed.png differ
diff --git a/docs/icons/small/continued.png b/docs/icons/small/continued.png
new file mode 100644 (file)
index 0000000..db17c42
Binary files /dev/null and b/docs/icons/small/continued.png differ
diff --git a/docs/icons/small/dir.png b/docs/icons/small/dir.png
new file mode 100644 (file)
index 0000000..9bd6256
Binary files /dev/null and b/docs/icons/small/dir.png differ
diff --git a/docs/icons/small/dir2.png b/docs/icons/small/dir2.png
new file mode 100644 (file)
index 0000000..836daf4
Binary files /dev/null and b/docs/icons/small/dir2.png differ
diff --git a/docs/icons/small/doc.png b/docs/icons/small/doc.png
new file mode 100644 (file)
index 0000000..c560df2
Binary files /dev/null and b/docs/icons/small/doc.png differ
diff --git a/docs/icons/small/forward.png b/docs/icons/small/forward.png
new file mode 100644 (file)
index 0000000..4ddbc61
Binary files /dev/null and b/docs/icons/small/forward.png differ
diff --git a/docs/icons/small/generic.png b/docs/icons/small/generic.png
new file mode 100644 (file)
index 0000000..16374a1
Binary files /dev/null and b/docs/icons/small/generic.png differ
diff --git a/docs/icons/small/generic2.png b/docs/icons/small/generic2.png
new file mode 100644 (file)
index 0000000..40d60c1
Binary files /dev/null and b/docs/icons/small/generic2.png differ
diff --git a/docs/icons/small/generic3.png b/docs/icons/small/generic3.png
new file mode 100644 (file)
index 0000000..aa38963
Binary files /dev/null and b/docs/icons/small/generic3.png differ
diff --git a/docs/icons/small/image.png b/docs/icons/small/image.png
new file mode 100644 (file)
index 0000000..d92f0a5
Binary files /dev/null and b/docs/icons/small/image.png differ
diff --git a/docs/icons/small/image2.png b/docs/icons/small/image2.png
new file mode 100644 (file)
index 0000000..4049bda
Binary files /dev/null and b/docs/icons/small/image2.png differ
diff --git a/docs/icons/small/index.png b/docs/icons/small/index.png
new file mode 100644 (file)
index 0000000..080453e
Binary files /dev/null and b/docs/icons/small/index.png differ
diff --git a/docs/icons/small/key.png b/docs/icons/small/key.png
new file mode 100644 (file)
index 0000000..1a45f67
Binary files /dev/null and b/docs/icons/small/key.png differ
diff --git a/docs/icons/small/movie.png b/docs/icons/small/movie.png
new file mode 100644 (file)
index 0000000..7c12604
Binary files /dev/null and b/docs/icons/small/movie.png differ
diff --git a/docs/icons/small/patch.png b/docs/icons/small/patch.png
new file mode 100644 (file)
index 0000000..c39f144
Binary files /dev/null and b/docs/icons/small/patch.png differ
diff --git a/docs/icons/small/ps.png b/docs/icons/small/ps.png
new file mode 100644 (file)
index 0000000..5c60423
Binary files /dev/null and b/docs/icons/small/ps.png differ
diff --git a/docs/icons/small/rainbow.png b/docs/icons/small/rainbow.png
new file mode 100644 (file)
index 0000000..175053c
Binary files /dev/null and b/docs/icons/small/rainbow.png differ
diff --git a/docs/icons/small/sound.png b/docs/icons/small/sound.png
new file mode 100644 (file)
index 0000000..6e3e95d
Binary files /dev/null and b/docs/icons/small/sound.png differ
diff --git a/docs/icons/small/sound2.png b/docs/icons/small/sound2.png
new file mode 100644 (file)
index 0000000..bc46eb4
Binary files /dev/null and b/docs/icons/small/sound2.png differ
diff --git a/docs/icons/small/tar.png b/docs/icons/small/tar.png
new file mode 100644 (file)
index 0000000..12f0347
Binary files /dev/null and b/docs/icons/small/tar.png differ
diff --git a/docs/icons/small/text.png b/docs/icons/small/text.png
new file mode 100644 (file)
index 0000000..b4e30f4
Binary files /dev/null and b/docs/icons/small/text.png differ
diff --git a/docs/icons/small/transfer.png b/docs/icons/small/transfer.png
new file mode 100644 (file)
index 0000000..3240481
Binary files /dev/null and b/docs/icons/small/transfer.png differ
diff --git a/docs/icons/small/unknown.png b/docs/icons/small/unknown.png
new file mode 100644 (file)
index 0000000..cad7e7a
Binary files /dev/null and b/docs/icons/small/unknown.png differ
diff --git a/docs/icons/small/uu.png b/docs/icons/small/uu.png
new file mode 100644 (file)
index 0000000..ef87c82
Binary files /dev/null and b/docs/icons/small/uu.png differ
diff --git a/docs/icons/sound1.png b/docs/icons/sound1.png
new file mode 100644 (file)
index 0000000..7a766be
Binary files /dev/null and b/docs/icons/sound1.png differ
diff --git a/docs/icons/sound2.png b/docs/icons/sound2.png
new file mode 100644 (file)
index 0000000..4511290
Binary files /dev/null and b/docs/icons/sound2.png differ
diff --git a/docs/icons/sphere1.png b/docs/icons/sphere1.png
new file mode 100644 (file)
index 0000000..2198ae8
Binary files /dev/null and b/docs/icons/sphere1.png differ
diff --git a/docs/icons/sphere2.png b/docs/icons/sphere2.png
new file mode 100644 (file)
index 0000000..257632b
Binary files /dev/null and b/docs/icons/sphere2.png differ
diff --git a/docs/icons/tar.png b/docs/icons/tar.png
new file mode 100644 (file)
index 0000000..6c40521
Binary files /dev/null and b/docs/icons/tar.png differ
diff --git a/docs/icons/tex.png b/docs/icons/tex.png
new file mode 100644 (file)
index 0000000..906622d
Binary files /dev/null and b/docs/icons/tex.png differ
diff --git a/docs/icons/text.png b/docs/icons/text.png
new file mode 100644 (file)
index 0000000..34d0edf
Binary files /dev/null and b/docs/icons/text.png differ
diff --git a/docs/icons/transfer.png b/docs/icons/transfer.png
new file mode 100644 (file)
index 0000000..efaf17b
Binary files /dev/null and b/docs/icons/transfer.png differ
diff --git a/docs/icons/unknown.png b/docs/icons/unknown.png
new file mode 100644 (file)
index 0000000..7c241c3
Binary files /dev/null and b/docs/icons/unknown.png differ
diff --git a/docs/icons/up.png b/docs/icons/up.png
new file mode 100644 (file)
index 0000000..a69ea00
Binary files /dev/null and b/docs/icons/up.png differ
diff --git a/docs/icons/uu.png b/docs/icons/uu.png
new file mode 100644 (file)
index 0000000..b1d1a85
Binary files /dev/null and b/docs/icons/uu.png differ
diff --git a/docs/icons/uuencoded.png b/docs/icons/uuencoded.png
new file mode 100644 (file)
index 0000000..b1d1a85
Binary files /dev/null and b/docs/icons/uuencoded.png differ
diff --git a/docs/icons/world1.png b/docs/icons/world1.png
new file mode 100644 (file)
index 0000000..3a65c00
Binary files /dev/null and b/docs/icons/world1.png differ
diff --git a/docs/icons/world2.png b/docs/icons/world2.png
new file mode 100644 (file)
index 0000000..9f8a3ea
Binary files /dev/null and b/docs/icons/world2.png differ
diff --git a/docs/manual/developer/documenting.html b/docs/manual/developer/documenting.html
new file mode 100644 (file)
index 0000000..8f8163f
--- /dev/null
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Documenting Apache 2.0</title>
+</head>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+
+<!--#include virtual="header.html" -->
+
+<h1 align="center">Documentating Apache 2.0</h1>
+
+<p>Apache 2.0 uses DoxyGen to document the API's and global variables in the
+   the code.  This will explain the basics of how to document using DoxyGen.
+
+<p>To start a documentation block, use /**<br />
+   To end a documentation block, use */</p>
+
+<p>In the middle of the block, there are multiple tags we can use:</p>
+<pre>
+    Description of this functions purpose
+    @param parameter_name description
+</p>
+
+<p>The deffunc is not always necessary.  DoxyGen does not have a full parser 
+   in it, so any prototype that use a macro in the return type declaration 
+   is too complex for scandoc.  Those functions require a deffunc.</p>
+
+<p>An example (using &&gt; rather than &gt;):</p>
+<pre>
+/**
+ * return the final element of the pathname
+ * @param pathname The path to get the final element of
+ * @return the final element of the path
+ * @tip Examples:
+ * &lt;pre&gt;
+ *                 "/foo/bar/gum"   -&&gt; "gum"
+ *                 "/foo/bar/gum/"  -&&gt; ""
+ *                 "gum"            -&&gt; "gum"
+ *                 "wi\\n32\\stuff" -&&gt; "stuff"
+ * &lt;/pre&gt;
+ * @deffunc const char * ap_filename_of_pathname(const char *pathname)
+ */
+</pre>
+
+<p>At the top of the header file, always include:</p>
+<pre>
+/**
+ * @package Name of library header
+ */
+</pre>
+
+<p>ScanDoc uses a new html file for each package.  The html files are named
+   {Name_of_library_header}.html, so try to be concise with your names.</p>
+<!--#include virtual="footer.html" -->
+</body>
+</html>
diff --git a/docs/manual/developer/request.html b/docs/manual/developer/request.html
new file mode 100644 (file)
index 0000000..6becc93
--- /dev/null
@@ -0,0 +1,217 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Request Processing in Apache 2.0</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+
+<!--#include virtual="header.html" -->
+
+<h1>Request Processing in Apache 2.0</h1>
+
+<p>Warning - this is a first (fast) draft that needs further revision!</p>
+
+<p>Several changes in Apache 2.0 affect the internal request processing
+   mechanics.  Module authors need to be aware of these changes so they
+   may take advantage of the optimizations and security enhancements.</p>
+
+<p>The first major change is to the subrequest and redirect mechanisms.
+   There were a number of different code paths in Apache 1.3 to attempt
+   to optimize subrequest or redirect behavior.  As patches were introduced
+   to 2.0, these optimizations (and the server behavior) were quickly broken
+   due to this duplication of code.  All duplicate code has been folded
+   back into <code>ap_process_internal_request()</code> to prevent the
+   code from falling out of sync again.</p>
+
+<p>This means that much of the existing code was 'unoptimized'.  It is 
+   the Apache HTTP Project's first goal to create a robust and correct
+   implementation of the HTTP server RFC.  Additional goals include
+   security, scalability and optimization.  New methods were sought to
+   optimize the server (beyond the performance of Apache 1.3) without
+   introducing fragile or insecure code.</p>
+
+<h2>The Request Processing Cycle</h2>
+
+<p>All requests pass through <code>ap_process_request_internal()</code>
+   in request.c, including subrequests and redirects.  If a module doesn't 
+   pass generated requests through this code, the author is cautioned that
+   the module may be broken by future changes to request processing.</p>
+
+<p>To streamline requests, the module author can take advantage of the
+   hooks offered to drop out of the request cycle early, or to bypass
+   core Apache hooks which are irrelevant (and costly in terms of CPU.)</p>
+
+<h2>The Request Parsing Phase</h3>
+
+<h3>Unescapes the URL</h3>
+
+<p>The request's parsed_uri path is unescaped, once and only once, at the
+   beginning of internal request processing.</p>
+
+<p>This step is bypassed if the proxyreq flag is set, or the parsed_uri.path
+   element is unset.  The module has no further control of this one-time 
+   unescape operation, either failing to unescape or multiply unescaping
+   the URL leads to security reprecussions.</p>
+
+<h3>Strips Parent and This Elements from the URI</h3>
+
+<p>All <code>/../</code> and <code>/./</code> elements are removed by
+   <code>ap_getparents()</code>.  This helps to ensure the path is (nearly) 
+   absolute before the request processing continues.</p>
+
+<p>This step cannot be bypassed.</p>
+
+<h3>Initial URI Location Walk</h3>
+
+<p>Every request is subject to an <code>ap_location_walk()</code> call.
+   This ensures that &lt;Location &gt; sections are consistently enforced for 
+   all requests.  If the request is an internal redirect or a sub-request, 
+   it may borrow some or all of the processing from the previous or parent 
+   request's ap_location_walk, so this step is generally very efficient 
+   after processing the main request.</p>
+
+<h3>Hook: translate_name</h3>
+
+<p>Modules can determine the file name, or alter the given URI in this step.
+   For example, mod_vhost_alias will translate the URI's path into the
+   configured virtual host, mod_alias will translate the path to an alias
+   path, and if the request falls back on the core, the DocumentRoot is
+   prepended to the request resource.
+
+<p>If all modules DECLINE this phase, an error 500 is returned to the browser,
+   and a "couldn't translate name" error is logged automatically.</p>
+
+<h3>Hook: map_to_storage</h3>
+
+<p>After the file or correct URI was determined, the appropriate per-dir
+   configurations are merged together.  For example, mod_proxy compares 
+   and merges the appropriate &lt;Proxy &gt; sections.  If the URI is nothing 
+   more than a local (non-proxy) TRACE request, the core handles the
+   request and returns DONE.  If no module answers this hook with OK or
+   DONE, the core will run the request filename against the &lt;Directory &gt;
+   and &lt;Files &gt; sections.  If the request 'filename' isn't an absolute,
+   legal filename, a note is set for later termination.</p>
+
+<h3>Initial URI Location Walk</h3>
+
+<p>Every request is hardened by a second <code>ap_location_walk()</code> 
+  call.  This reassures that a translated request is still subjected to 
+  the configured &lt;Location &gt; sections.  The request again borrows 
+  some or all of the processing from it's previous location_walk above, 
+  so this step is almost always very efficient unless the translated URI 
+  mapped to a substantially different path or Virtual Host.</p>
+
+<h3>Hook: header_parser</h3>
+
+<p>The main request then parses the client's headers.  This prepares 
+the remaining request processing steps to better serve the client's 
+request.</p>
+
+<h2>The Security Phase</h3>
+
+<p>Needs Documentation.  Code is;</p>
+<pre>
+    switch (ap_satisfies(r)) {
+    case SATISFY_ALL:
+    case SATISFY_NOSPEC:
+        if ((access_status = ap_run_access_checker(r)) != 0) {
+            return decl_die(access_status, "check access", r);
+        }
+        if (ap_some_auth_required(r)) {
+            if (((access_status = ap_run_check_user_id(r)) != 0) || !ap_auth_type(r)) {
+                return decl_die(access_status, ap_auth_type(r)
+                           ? "check user.  No user file?"
+                           : "perform authentication. AuthType not set!", r);
+            }
+            if (((access_status = ap_run_auth_checker(r)) != 0) || !ap_auth_type(r)) {
+                return decl_die(access_status, ap_auth_type(r)
+                           ? "check access.  No groups file?"
+                           : "perform authentication. AuthType not set!", r);
+            }
+        }
+        break;
+    case SATISFY_ANY:
+        if (((access_status = ap_run_access_checker(r)) != 0) || !ap_auth_type(r)) {
+            if (!ap_some_auth_required(r)) {
+                return decl_die(access_status, ap_auth_type(r)
+                           ? "check access"
+                           : "perform authentication. AuthType not set!", r);
+            }
+            if (((access_status = ap_run_check_user_id(r)) != 0) || !ap_auth_type(r)) {
+                return decl_die(access_status, ap_auth_type(r)
+                           ? "check user.  No user file?"
+                           : "perform authentication. AuthType not set!", r);
+            }
+            if (((access_status = ap_run_auth_checker(r)) != 0) || !ap_auth_type(r)) {
+                return decl_die(access_status, ap_auth_type(r)
+                           ? "check access.  No groups file?"
+                           : "perform authentication. AuthType not set!", r);
+            }
+               }
+        break;
+    }
+</pre>
+<h2>The Preparation Phase</h2>
+
+<h3>Hook: type_checker</h3>
+
+<p>The modules have an opportunity to test the URI or filename against
+   the target resource, and set mime information for the request.  Both
+   mod_mime and mod_mime_magic use this phase to compare the file name
+   or contents against the administrator's configuration and set the
+   content type, language, character set and request handler.  Some
+   modules may set up their filters or other request handling parameters
+   at this time.</p>
+
+<p>If all modules DECLINE this phase, an error 500 is returned to the browser,
+   and a "couldn't find types" error is logged automatically.</p>
+
+<h3>Hook: fixups</h3>
+
+<p>Many modules are 'trounced' by some phase above.  The fixups phase is
+   used by modules to 'reassert' their ownership or force the request's
+   fields to their appropriate values.  It isn't always the cleanest
+   mechanism, but occasionally it's the only option.</p>
+
+<h3>Hook: insert_filter</h3>
+
+<p>Modules that transform the content in some way can insert their values
+   and override existing filters, such that if the user configured a more
+   advanced filter out-of-order, then the module can move it's order as
+   need be.
+
+<h2>The Handler Phase</h2>
+
+<p>This phase is <strong><em>not</em></strong> part of the processing in
+   <code>ap_process_request_internal()</code>.  Many modules prepare one
+   or more subrequests prior to creating any content at all.  After the
+   core, or a module calls <code>ap_process_request_internal()</code> it 
+   then calls <code>ap_invoke_handler()</code> to generate the request.</p>
+
+<h3>Hook: handler</h3>
+
+<p>The module finally has a chance to serve the request in it's handler
+   hook.  Note that not every prepared request is sent to the handler
+   hook.  Many modules, such as mod_autoindex, will create subrequests
+   for a given URI, and then never serve the subrequest, but simply
+   lists it for the user.  Remember not to put required teardown from
+   the hooks above into this module, but register pool cleanups against
+   the request pool to free resources as required.</p>
+
+<!--#include virtual="footer.html" -->
+
+</body>
+</html>
+
+
+
+
+
diff --git a/docs/manual/faq/index.html b/docs/manual/faq/index.html
new file mode 100644 (file)
index 0000000..90c19b7
--- /dev/null
@@ -0,0 +1,67 @@
+<!--#if expr="$QUERY_STRING = ONEPAGE" -->
+  <!--#set var="ONEPAGE" value="YES" -->
+<!--#else -->
+  <!--#set var="ONEPAGE" value="" -->
+<!--#endif -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+  <TITLE>Apache Server Frequently Asked Questions</TITLE>
+ </HEAD>
+ <BODY
+  BGCOLOR="#FFFFFF"
+  TEXT="#000000"
+  LINK="#0000FF"
+  VLINK="#000080"
+  ALINK="#FF0000"
+ >
+  <!--#include virtual="header.html" -->
+  <H1 ALIGN="CENTER">Frequently Asked Questions</H1>
+
+  <P>
+  The latest version of this FAQ is always available from the main
+  Apache web site, at &lt;<A
+  HREF="http://httpd.apache.org/docs-2.0/faq/" REL="Help"
+  ><SAMP>http://httpd.apache.org/docs-2.0/faq/</SAMP></A>&gt;.
+  <!--#if expr="!$ONEPAGE" --> In addition, you can view this FAQ <a
+  href="./?ONEPAGE">all in one page</a> for easy searching and
+  printing.<!--#endif -->
+  </P>
+
+  <p>Since Apache 2.0 is very new, we don't yet know what the
+  <em>Frequently Asked Questions</em> will be.  While this section
+  fills up, you should also consult the <a
+  href="http://httpd.apache.org/docs/misc/FAQ.html">Apache 1.3 FAQ</a>
+  to see if your question is answered there.</p>
+
+
+
+  <H2>Categories</H2>
+
+<!--#if expr="!$ONEPAGE" -->
+
+<dl>
+
+<dt><a href="support.html">Support</a></dt>
+<dd>What do I do when I have problems?</dd>
+
+</dl>
+
+<!--#else -->
+
+<dl>
+
+<dt><a href="#support.html">Support</a></dt>
+<dd>What do I do when I have problems?</dd>
+
+</dl>
+
+<hr>
+
+<!--#include virtual="support.html" -->
+
+<!--#endif -->
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/faq/support.html b/docs/manual/faq/support.html
new file mode 100644 (file)
index 0000000..95aa420
--- /dev/null
@@ -0,0 +1,143 @@
+<!--#if expr="!$ONEPAGE" -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+  <TITLE>Support - Apache Server Frequently Asked Questions</TITLE>
+ </HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+  BGCOLOR="#FFFFFF"
+  TEXT="#000000"
+  LINK="#0000FF"
+  VLINK="#000080"
+  ALINK="#FF0000"
+ >
+  <!--#include virtual="header.html" -->
+  <H1 ALIGN="CENTER">Apache Server Frequently Asked Questions</H1>
+<!--#endif -->
+
+  <H2><a name="support.html">Support</a></H2>
+
+<UL>
+   <LI><A HREF="#what2do">&quot;Why can't I ...?  Why won't ...
+        work?&quot;  What to do in case of problems</A>
+   </LI>
+   <LI><A HREF="#support">Whom do I contact for support?</A>
+   </LI>
+
+</UL>
+
+<HR>
+
+ <H3><A NAME="what2do">&quot;Why can't I ...?  Why won't ... work?&quot;  
+       What to do in case of problems</A></H3>
+  <P>
+  If you are having trouble with your Apache server software, you should
+  take the following steps:
+  </P>
+  <OL>
+   <LI><STRONG>Check the errorlog!</STRONG>
+    <P>
+    Apache tries to be helpful when it encounters a problem.  In many
+    cases, it will provide some details by writing one or messages to
+    the server error log.  Sometimes this is enough for you to diagnose
+    &amp; fix the problem yourself (such as file permissions or the like).
+    The default location of the error log is
+    <SAMP>/usr/local/apache/logs/error_log</SAMP>, but see the
+    <A HREF="../mod/core.html#errorlog"><SAMP>ErrorLog</SAMP></A>
+    directive in your config files for the location on your server.
+    </P>
+   </LI>
+   <LI><STRONG>Check the
+    <A HREF="http://httpd.apache.org/docs/misc/FAQ.html">FAQ</A>!</STRONG>
+    <P>
+    The latest version of the Apache Frequently-Asked Questions list can
+    always be found at the main Apache web site.
+    </P>
+   </LI>
+   <LI><STRONG>Check the Apache bug database</STRONG>
+    <P>
+    Most problems that get reported to The Apache Group are recorded in
+    the
+    <A HREF="http://bugs.apache.org/">bug database</A>.
+    <EM><STRONG>Please</STRONG> check the existing reports, open
+    <STRONG>and</STRONG> closed, before adding one.</EM>  If you find
+    that your issue has already been reported, please <EM>don't</EM> add
+    a &quot;me, too&quot; report.  If the original report isn't closed
+    yet, we suggest that you check it periodically.  You might also
+    consider contacting the original submitter, because there may be an
+    email exchange going on about the issue that isn't getting recorded
+    in the database.
+    </P>
+   </LI>
+   <LI><STRONG>Ask in the <SAMP>comp.infosystems.www.servers.unix</SAMP>
+    or <SAMP>comp.infosystems.www.servers.ms-windows</SAMP> USENET
+    newsgroup (as appropriate for the platform you use).</STRONG>
+    <P>
+    A lot of common problems never make it to the bug database because
+    there's already high Q&amp;A traffic about them in the
+    <A HREF="news:comp.infosystems.www.servers.unix"
+    ><SAMP>comp.infosystems.www.servers.unix</SAMP></A>
+    newsgroup.  Many Apache users, and some of the developers, can be
+    found roaming its virtual halls, so it is suggested that you seek
+    wisdom there.  The chances are good that you'll get a faster answer
+    there than from the bug database, even if you <EM>don't</EM> see
+    your question already posted.
+    </P>
+   </LI>
+   <LI><STRONG>If all else fails, report the problem in the bug
+    database</STRONG>
+    <P>
+    If you've gone through those steps above that are appropriate and
+    have obtained no relief, then please <EM>do</EM> let The Apache
+    Group know about the problem by
+    <A HREF="http://httpd.apache.org/bug_report.html">logging a bug report</A>.
+    </P>
+    <P>
+    If your problem involves the server crashing and generating a core
+    dump, please include a backtrace (if possible).  As an example,
+    </P>
+    <P>
+    <DL>
+     <DD><CODE># cd <EM>ServerRoot</EM><BR>
+      # dbx httpd core<BR>
+      (dbx) where</CODE>
+     </DD>
+    </DL>
+    <P></P>
+    <P>
+    (Substitute the appropriate locations for your
+    <SAMP>ServerRoot</SAMP> and your <SAMP>httpd</SAMP> and
+    <SAMP>core</SAMP> files.  You may have to use <CODE>gdb</CODE>
+    instead of <CODE>dbx</CODE>.)
+    </P>
+   </LI>
+  </OL>
+  <HR>
+
+ <H3><A NAME="support">Whom do I contact for support?</A></H3>
+  <P>
+  There is no official support for Apache. None of the developers want to
+  be swamped by a flood of trivial questions that can be resolved elsewhere.
+  Bug reports and suggestions should be sent <EM>via</EM>
+  <A HREF="http://www.apache.org/bug_report.html">the bug report page</A>.
+  Other questions should be directed to the
+  <A HREF="news:comp.infosystems.www.servers.unix"
+  >comp.infosystems.www.servers.unix</A> or <A HREF=
+  "news:comp.infosystems.www.servers.ms-windows"
+  >comp.infosystems.www.servers.ms-windows</A>
+  newsgroup (as appropriate for the platform you use), where some of the 
+  Apache team lurk, in the company of many other httpd gurus who 
+  should be able to help.
+  </P>
+  <P>
+  Commercial support for Apache is, however, available from a number
+  of third parties.
+  </P>
+
+<!--#if expr="!$ONEPAGE" -->
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
+<!--#endif -->
diff --git a/docs/manual/howto/auth.html b/docs/manual/howto/auth.html
new file mode 100644 (file)
index 0000000..9311ffc
--- /dev/null
@@ -0,0 +1,318 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+
+<html>
+  <head>
+    <meta name="generator" content="HTML Tidy, see www.w3.org">
+
+    <title>Authentication</title>
+    <link rev="made" href="mailto:rbowen@rcbowen.com">
+  </head>
+  <!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+
+  <body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink=
+  "#000080" alink="#FF0000">
+    <!--#include virtual="header.html" -->
+
+    <h1 align="CENTER">Authentication</h1>
+    <a name="__index__"></a> <!-- INDEX BEGIN -->
+     
+
+    <ul>
+      <li><a href="#introduction">Introduction</a></li>
+
+      <li><a href="#the prerequisites">The prerequisites</a></li>
+
+      <li><a href="#getting it working.">Getting it
+      working.</a></li>
+
+      <li><a href="#letting more than one person in">Letting more
+      than one person in</a></li>
+
+      <li><a href="#possible problems">Possible problems</a></li>
+
+      <li><a href="#what other neat stuff can i do">What other neat
+      stuff can I do?</a></li>
+
+      <li><a href="#more information">More information</a></li>
+    </ul>
+    <!-- INDEX END -->
+    <hr>
+
+<table border="1">
+<tr>
+<td valign="top"><strong>Related Modules</strong><br>
+<br>
+ <a href="../mod/mod_auth.html">mod_auth</a><br>
+ </td>
+<td valign="top"><strong>Related Directives</strong><br>
+<br>
+ <a href="../mod/mod_access.html#allow">Allow</a><br>
+ <a href="../mod/mod_auth.html#authgroupfile">AuthGroupFile</a><br>
+ <a href="../mod/core.html#authname">AuthName</a><br>
+ <a href="../mod/core.html#authtype">AuthType</a><br>
+ <a href="../mod/mod_auth.html#authuserfile">AuthUserFile</a><br>
+ <a href="../mod/mod_access.html#deny">Deny</a><br>
+ <a href="../mod/core.html#options">Options</a><br>
+ <a href="../mod/core.html#require">Require</a><br>
+
+ </td>
+</tr>
+</table>
+   
+
+    <h1><a name="authentication">Authentication</a></h1>
+
+    <p>Authentication is any process by which you verify that
+    someone is who they claim they are. Authorization is any
+    process by which someone is allowed to be where they want to
+    go, or to have information that they want to have.</p>
+
+    <h2><a name="introduction">Introduction</a></h2>
+
+    <p>If you have information on your web site that is sensitive
+    or intended for only a small group of people, the techniques in
+    this article will help you make sure that the people that see
+    those pages are the people that you wanted to see them.</p>
+
+    <p>This article covers the "standard" way of protecting parts of your
+    web site that most of you are going to use.</p>
+
+    <h2><a name="the prerequisites">The prerequisites</a></h2>
+
+    <p>The directives discussed in this article will need to go either
+    in your main server configuration file, or in per-directory
+    configuration files (<code>.htaccess</code> files).</p>
+
+    <p>If you plan to use <code>.htaccess</code> files, you will need to
+    have a server configuration that permits putting authentication
+    directives in these files. This is done with the 
+    <code><a href="../mod/core.html#allowoverride">AllowOverride</a></code>
+    directive, which specifies which directives, if any, may be put in
+    per-directory configuration files.</p>
+
+    <p>Since we're talking here about authentication, you will need an
+    <code>AllowOverride</code> directive like the following:</p>
+
+<pre>
+    AllowOverride AuthConfig
+</pre>
+
+    <p>Or, if you are just going to put the directives directly in your
+    main server configuration file, you will of course need to have
+    write permission to that file.</p>
+
+    <p>And you'll need to know a little bit about the directory
+    structure of your server, in order to know where some files are
+    kept. This should not be terribly difficult, and I'll try to
+    make this clear when we come to that point.</p>
+
+    <h2><a name="getting it working.">Getting it working.</a></h2>
+
+    <p>Here's the basics of password protecting a directory on your
+    server.</p>
+
+    <p>You'll need to create a password file. This file should be
+    placed somewhere outside of your document directory. This is so
+    that folks cannot download the password file. For example, if
+    your documents are served out of
+    <code>/usr/local/apache/htdocs</code> you might want to put the
+    password file(s) in <code>/usr/local/apache/passwd</code>.</p>
+
+    <p>To create the file, use the <code>htpasswd</code> utility
+    that came with Apache. This be located in the <code>bin</code>
+    directory of wherever you installed Apache. To create the file,
+    type:</p>
+<pre>
+        htpasswd -c /usr/local/apache/passwd/password rbowen
+</pre>
+
+    <p><code>htpasswd</code> will ask you for the password, and
+    then ask you to type it again to confirm it:</p>
+<pre>
+        # htpasswd -c /usr/local/apache/passwd/passwords rbowen
+        New password: mypassword
+        Re-type new password: mypassword
+        Adding password for user rbowen
+</pre>
+
+    <p>If <code>htpasswd</code> is not in your path, of course
+    you'll have to type the full path to the file to get it to run.
+    On my server, it's located at
+    <code>/usr/local/apache/bin/htpasswd</code></p>
+
+    <p>Next, you'll need to create a file in the directory you want
+    to protect. This file is usually called <code>.htaccess</code>,
+    although on Windows it's called <code>htaccess</code> (without
+    the leading period.) <code>.htaccess</code> needs to contain
+    the following lines:</p>
+<pre>
+        AuthType Basic
+        AuthName "By Invitation Only"
+        AuthUserFile /usr/local/apache/passwd/passwords
+        AuthGroupFile /dev/null
+        require user rbowen
+</pre>
+
+    <p>The next time that you load a file from that directory, you
+    should see the familiar username/password dialog box pop up. If
+    you don't chances are pretty good that you are not permitted to
+    use <code>.htaccess</code> files in the directory in
+    question.</p>
+
+    <h2><a name="letting more than one person in">Letting more than
+    one person in</a></h2>
+
+    <p>The directives above only let one person (specifically
+    someone with a username of <code>rbowen</code>) into the
+    directory. In most cases, you'll want to let more than one
+    person in. This is where the <code>AuthGroupFile</code> comes
+    in. In the example above, we've pointed
+    <code>AuthGroupFile</code> to <code>/dev/null</code>, which is
+    Unix-speak for "nowhere", or "off into space." (The Windows
+    NT equivalent of this is <code>nul</code>.)</p>
+
+    <p>If you want to let more than one person in, you'll need to
+    create a group file that associates group names with a list of
+    users in that group. The format of this file is pretty simple,
+    and you can create it with your favorite editor. The contents
+    of the file will look like this:</p>
+<pre>
+        GroupName: rbowen dpitts sungo rshersey
+</pre>
+
+    <p>That's just a list of the members of the group in a long
+    line separated by spaces.</p>
+
+    <p>To add a user to your already existing password file,
+    type:</p>
+<pre>
+        htpasswd /usr/local/apache/passwd/password dpitts
+</pre>
+
+    <p>You'll get the same response as before, but it will be
+    appended to the existing file, rather than creating a new file.
+    (It's the <code>-c</code> that makes it create a new password
+    file.</p>
+
+    <p>Now, you need to modify your <code>.htaccess</code> file to
+    look like the following:</p>
+<pre>
+        AuthType Basic
+        AuthName "By Invitation Only"
+        AuthUserFile /usr/local/apache/passwd/passwords
+        AuthGroupFile /usr/local/apache/passwd/groups
+        require group GroupName
+</pre>
+
+    <p>Now, anyone that is listed in the group
+    <code>GroupName</code>, and has an entry in the
+    <code>password</code> file, will be let in, if they type the
+    correct password.</p>
+
+    <p>There's another way to let multiple users in that is less
+    specific. Rather than creating a group file, you can just use
+    the following directive:</p>
+<pre>
+        require valid-user
+</pre>
+
+    <p>Using that rather than the <code>require user rbowen</code>
+    line will allow anyone in that is listed in the password file,
+    and who correctly enters their password. You can even emulate
+    the group behavior here, by just keeping a separate password
+    file for each group. The advantage of this approach is that
+    Apache only has to check one file, rather than two. The
+    disadvantage is that you have to maintain a bunch of password
+    files, and remember to reference th right one in the
+    <code>AuthUserFile</code> directive.</p>
+
+    <h2><a name="possible problems">Possible problems</a></h2>
+
+    <p>Because of the way that Basic authentication is specified,
+    your username and password must be verified every time you
+    request a document from the server. This is even if you're
+    reloading the same page, and for every image on the page (if
+    they come from a protected directory). As you can imagine, this
+    slows things down a little. The amount that it slows things
+    down is proportional to the size of the password file, because
+    it has to open up that file, and go down the list of users
+    until it gets to your name. And it has to do this every time a
+    page is loaded.</p>
+
+    <p>A consequence of this is that there's a practical limit to how many
+    users you can put in one password file. This limit will vary
+    depending on the performance of your particular server machine, but
+    you can expect to see slowdowns once you get above a few hundred
+    entries, and may wish to consider a different authentication method
+    at that time.</p>
+
+    <h2><a name="what other neat stuff can i do">What other neat
+    stuff can I do?</a></h2>
+
+    <p>Authentication by username and password is only part of the
+    story. Frequently you want to let people in based on something
+    other than who they are. Something such as where they are
+    coming from.</p>
+
+    <p>The <code>allow</code> and <code>deny</code> directives let
+    you allow and deny access based on the host name, or host
+    address, of the machine requesting a document. The directive
+    goes hand-in-hand with these is the <code>order</code>
+    directive, which tells Apache in which order to apply the
+    filters.</p>
+
+    <p>The usage of these directives is:</p>
+<pre>
+        allow from address
+</pre>
+
+    <p>where <em>address</em> is an IP address (or a partial IP
+    address) or a fully qualified domain name (or a partial domain
+    name).</p>
+
+    <p>For example, if you have someone spamming your message
+    board, and you want to keep them out, you could do the
+    following:</p>
+<pre>
+        deny from 205.252.46.165
+</pre>
+
+    <p>Visitors coming from that address will not be able to see
+    the content behind this directive. If, instead, you have a
+    machine name, rather than an IP address, you can use that.</p>
+<pre>
+        deny from host.example.com
+</pre>
+
+    <p>And, if you'd like to block access from an entire domain,
+    you can specify just part of an address or domain name:</p>
+<pre>
+        deny from 192.101.205
+        deny from cyberthugs.com
+        deny from ke
+</pre>
+
+    <p>Using <code>order</code> will let you be sure that you are
+    actually restricting things to the group that you want to let
+    in, by combining a <code>deny</code> and an <code>allow</code>
+    directive:</p>
+<pre>
+        order deny,allow
+        deny from all
+        allow from dev.example.com
+</pre>
+
+    <p>Listing just the <code>allow</code> directive would not do
+    what you want, because it will let folks from that host in, in
+    addition to letting everyone in. What you want is to let
+    <em>only</em> those folks in.</p>
+
+    <h2><a name="more information">More information</a></h2>
+
+    <p>You should also read the documentation for
+    <code><a href="../mod/mod_auth.html">mod_auth</a></code>
+    which contains
+    some more information about how this all works.</p>
+  </body>
+</html>
+
diff --git a/docs/manual/howto/auth.html.en b/docs/manual/howto/auth.html.en
new file mode 100644 (file)
index 0000000..9311ffc
--- /dev/null
@@ -0,0 +1,318 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+
+<html>
+  <head>
+    <meta name="generator" content="HTML Tidy, see www.w3.org">
+
+    <title>Authentication</title>
+    <link rev="made" href="mailto:rbowen@rcbowen.com">
+  </head>
+  <!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+
+  <body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink=
+  "#000080" alink="#FF0000">
+    <!--#include virtual="header.html" -->
+
+    <h1 align="CENTER">Authentication</h1>
+    <a name="__index__"></a> <!-- INDEX BEGIN -->
+     
+
+    <ul>
+      <li><a href="#introduction">Introduction</a></li>
+
+      <li><a href="#the prerequisites">The prerequisites</a></li>
+
+      <li><a href="#getting it working.">Getting it
+      working.</a></li>
+
+      <li><a href="#letting more than one person in">Letting more
+      than one person in</a></li>
+
+      <li><a href="#possible problems">Possible problems</a></li>
+
+      <li><a href="#what other neat stuff can i do">What other neat
+      stuff can I do?</a></li>
+
+      <li><a href="#more information">More information</a></li>
+    </ul>
+    <!-- INDEX END -->
+    <hr>
+
+<table border="1">
+<tr>
+<td valign="top"><strong>Related Modules</strong><br>
+<br>
+ <a href="../mod/mod_auth.html">mod_auth</a><br>
+ </td>
+<td valign="top"><strong>Related Directives</strong><br>
+<br>
+ <a href="../mod/mod_access.html#allow">Allow</a><br>
+ <a href="../mod/mod_auth.html#authgroupfile">AuthGroupFile</a><br>
+ <a href="../mod/core.html#authname">AuthName</a><br>
+ <a href="../mod/core.html#authtype">AuthType</a><br>
+ <a href="../mod/mod_auth.html#authuserfile">AuthUserFile</a><br>
+ <a href="../mod/mod_access.html#deny">Deny</a><br>
+ <a href="../mod/core.html#options">Options</a><br>
+ <a href="../mod/core.html#require">Require</a><br>
+
+ </td>
+</tr>
+</table>
+   
+
+    <h1><a name="authentication">Authentication</a></h1>
+
+    <p>Authentication is any process by which you verify that
+    someone is who they claim they are. Authorization is any
+    process by which someone is allowed to be where they want to
+    go, or to have information that they want to have.</p>
+
+    <h2><a name="introduction">Introduction</a></h2>
+
+    <p>If you have information on your web site that is sensitive
+    or intended for only a small group of people, the techniques in
+    this article will help you make sure that the people that see
+    those pages are the people that you wanted to see them.</p>
+
+    <p>This article covers the "standard" way of protecting parts of your
+    web site that most of you are going to use.</p>
+
+    <h2><a name="the prerequisites">The prerequisites</a></h2>
+
+    <p>The directives discussed in this article will need to go either
+    in your main server configuration file, or in per-directory
+    configuration files (<code>.htaccess</code> files).</p>
+
+    <p>If you plan to use <code>.htaccess</code> files, you will need to
+    have a server configuration that permits putting authentication
+    directives in these files. This is done with the 
+    <code><a href="../mod/core.html#allowoverride">AllowOverride</a></code>
+    directive, which specifies which directives, if any, may be put in
+    per-directory configuration files.</p>
+
+    <p>Since we're talking here about authentication, you will need an
+    <code>AllowOverride</code> directive like the following:</p>
+
+<pre>
+    AllowOverride AuthConfig
+</pre>
+
+    <p>Or, if you are just going to put the directives directly in your
+    main server configuration file, you will of course need to have
+    write permission to that file.</p>
+
+    <p>And you'll need to know a little bit about the directory
+    structure of your server, in order to know where some files are
+    kept. This should not be terribly difficult, and I'll try to
+    make this clear when we come to that point.</p>
+
+    <h2><a name="getting it working.">Getting it working.</a></h2>
+
+    <p>Here's the basics of password protecting a directory on your
+    server.</p>
+
+    <p>You'll need to create a password file. This file should be
+    placed somewhere outside of your document directory. This is so
+    that folks cannot download the password file. For example, if
+    your documents are served out of
+    <code>/usr/local/apache/htdocs</code> you might want to put the
+    password file(s) in <code>/usr/local/apache/passwd</code>.</p>
+
+    <p>To create the file, use the <code>htpasswd</code> utility
+    that came with Apache. This be located in the <code>bin</code>
+    directory of wherever you installed Apache. To create the file,
+    type:</p>
+<pre>
+        htpasswd -c /usr/local/apache/passwd/password rbowen
+</pre>
+
+    <p><code>htpasswd</code> will ask you for the password, and
+    then ask you to type it again to confirm it:</p>
+<pre>
+        # htpasswd -c /usr/local/apache/passwd/passwords rbowen
+        New password: mypassword
+        Re-type new password: mypassword
+        Adding password for user rbowen
+</pre>
+
+    <p>If <code>htpasswd</code> is not in your path, of course
+    you'll have to type the full path to the file to get it to run.
+    On my server, it's located at
+    <code>/usr/local/apache/bin/htpasswd</code></p>
+
+    <p>Next, you'll need to create a file in the directory you want
+    to protect. This file is usually called <code>.htaccess</code>,
+    although on Windows it's called <code>htaccess</code> (without
+    the leading period.) <code>.htaccess</code> needs to contain
+    the following lines:</p>
+<pre>
+        AuthType Basic
+        AuthName "By Invitation Only"
+        AuthUserFile /usr/local/apache/passwd/passwords
+        AuthGroupFile /dev/null
+        require user rbowen
+</pre>
+
+    <p>The next time that you load a file from that directory, you
+    should see the familiar username/password dialog box pop up. If
+    you don't chances are pretty good that you are not permitted to
+    use <code>.htaccess</code> files in the directory in
+    question.</p>
+
+    <h2><a name="letting more than one person in">Letting more than
+    one person in</a></h2>
+
+    <p>The directives above only let one person (specifically
+    someone with a username of <code>rbowen</code>) into the
+    directory. In most cases, you'll want to let more than one
+    person in. This is where the <code>AuthGroupFile</code> comes
+    in. In the example above, we've pointed
+    <code>AuthGroupFile</code> to <code>/dev/null</code>, which is
+    Unix-speak for "nowhere", or "off into space." (The Windows
+    NT equivalent of this is <code>nul</code>.)</p>
+
+    <p>If you want to let more than one person in, you'll need to
+    create a group file that associates group names with a list of
+    users in that group. The format of this file is pretty simple,
+    and you can create it with your favorite editor. The contents
+    of the file will look like this:</p>
+<pre>
+        GroupName: rbowen dpitts sungo rshersey
+</pre>
+
+    <p>That's just a list of the members of the group in a long
+    line separated by spaces.</p>
+
+    <p>To add a user to your already existing password file,
+    type:</p>
+<pre>
+        htpasswd /usr/local/apache/passwd/password dpitts
+</pre>
+
+    <p>You'll get the same response as before, but it will be
+    appended to the existing file, rather than creating a new file.
+    (It's the <code>-c</code> that makes it create a new password
+    file.</p>
+
+    <p>Now, you need to modify your <code>.htaccess</code> file to
+    look like the following:</p>
+<pre>
+        AuthType Basic
+        AuthName "By Invitation Only"
+        AuthUserFile /usr/local/apache/passwd/passwords
+        AuthGroupFile /usr/local/apache/passwd/groups
+        require group GroupName
+</pre>
+
+    <p>Now, anyone that is listed in the group
+    <code>GroupName</code>, and has an entry in the
+    <code>password</code> file, will be let in, if they type the
+    correct password.</p>
+
+    <p>There's another way to let multiple users in that is less
+    specific. Rather than creating a group file, you can just use
+    the following directive:</p>
+<pre>
+        require valid-user
+</pre>
+
+    <p>Using that rather than the <code>require user rbowen</code>
+    line will allow anyone in that is listed in the password file,
+    and who correctly enters their password. You can even emulate
+    the group behavior here, by just keeping a separate password
+    file for each group. The advantage of this approach is that
+    Apache only has to check one file, rather than two. The
+    disadvantage is that you have to maintain a bunch of password
+    files, and remember to reference th right one in the
+    <code>AuthUserFile</code> directive.</p>
+
+    <h2><a name="possible problems">Possible problems</a></h2>
+
+    <p>Because of the way that Basic authentication is specified,
+    your username and password must be verified every time you
+    request a document from the server. This is even if you're
+    reloading the same page, and for every image on the page (if
+    they come from a protected directory). As you can imagine, this
+    slows things down a little. The amount that it slows things
+    down is proportional to the size of the password file, because
+    it has to open up that file, and go down the list of users
+    until it gets to your name. And it has to do this every time a
+    page is loaded.</p>
+
+    <p>A consequence of this is that there's a practical limit to how many
+    users you can put in one password file. This limit will vary
+    depending on the performance of your particular server machine, but
+    you can expect to see slowdowns once you get above a few hundred
+    entries, and may wish to consider a different authentication method
+    at that time.</p>
+
+    <h2><a name="what other neat stuff can i do">What other neat
+    stuff can I do?</a></h2>
+
+    <p>Authentication by username and password is only part of the
+    story. Frequently you want to let people in based on something
+    other than who they are. Something such as where they are
+    coming from.</p>
+
+    <p>The <code>allow</code> and <code>deny</code> directives let
+    you allow and deny access based on the host name, or host
+    address, of the machine requesting a document. The directive
+    goes hand-in-hand with these is the <code>order</code>
+    directive, which tells Apache in which order to apply the
+    filters.</p>
+
+    <p>The usage of these directives is:</p>
+<pre>
+        allow from address
+</pre>
+
+    <p>where <em>address</em> is an IP address (or a partial IP
+    address) or a fully qualified domain name (or a partial domain
+    name).</p>
+
+    <p>For example, if you have someone spamming your message
+    board, and you want to keep them out, you could do the
+    following:</p>
+<pre>
+        deny from 205.252.46.165
+</pre>
+
+    <p>Visitors coming from that address will not be able to see
+    the content behind this directive. If, instead, you have a
+    machine name, rather than an IP address, you can use that.</p>
+<pre>
+        deny from host.example.com
+</pre>
+
+    <p>And, if you'd like to block access from an entire domain,
+    you can specify just part of an address or domain name:</p>
+<pre>
+        deny from 192.101.205
+        deny from cyberthugs.com
+        deny from ke
+</pre>
+
+    <p>Using <code>order</code> will let you be sure that you are
+    actually restricting things to the group that you want to let
+    in, by combining a <code>deny</code> and an <code>allow</code>
+    directive:</p>
+<pre>
+        order deny,allow
+        deny from all
+        allow from dev.example.com
+</pre>
+
+    <p>Listing just the <code>allow</code> directive would not do
+    what you want, because it will let folks from that host in, in
+    addition to letting everyone in. What you want is to let
+    <em>only</em> those folks in.</p>
+
+    <h2><a name="more information">More information</a></h2>
+
+    <p>You should also read the documentation for
+    <code><a href="../mod/mod_auth.html">mod_auth</a></code>
+    which contains
+    some more information about how this all works.</p>
+  </body>
+</html>
+
diff --git a/docs/manual/images/custom_errordocs.png b/docs/manual/images/custom_errordocs.png
new file mode 100644 (file)
index 0000000..11553cb
Binary files /dev/null and b/docs/manual/images/custom_errordocs.png differ
diff --git a/docs/manual/images/mod_rewrite_fig1.png b/docs/manual/images/mod_rewrite_fig1.png
new file mode 100644 (file)
index 0000000..f3a68d9
Binary files /dev/null and b/docs/manual/images/mod_rewrite_fig1.png differ
diff --git a/docs/manual/images/mod_rewrite_fig2.png b/docs/manual/images/mod_rewrite_fig2.png
new file mode 100644 (file)
index 0000000..bc141be
Binary files /dev/null and b/docs/manual/images/mod_rewrite_fig2.png differ
diff --git a/docs/manual/logs.html b/docs/manual/logs.html
new file mode 100644 (file)
index 0000000..d3ebf48
--- /dev/null
@@ -0,0 +1,580 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Log Files - Apache HTTP Server</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<h1 align="center">Log Files</h1>
+
+<p>In order to effectively manage a web server, it is necessary to get
+feedback about the activity and performance of the server as well as
+any problems that may be occuring.  The Apache HTTP Server provides
+very comprehensive and flexible logging capabilities.  This document
+describes how to configure the various log files, and how to
+understand what the logs contain.</p>
+
+<ul>
+<li><a href="#security">Security Warning</a></li>
+<li><a href="#errorlog">Error Log</a></li>
+<li><a href="#accesslog">Access Log</a>
+  <ul>
+    <li><a href="#common">Common Log Format</a></li>
+    <li><a href="#combined">Combined Log Format</a></li>
+    <li><a href="#multiple">Multiple Access Logs</a></li>
+    <li><a href="#conditional">Conditional Logging</a></li>
+  </ul></li>
+<li><a href="#rotation">Log Rotation</a></li>
+<li><a href="#piped">Piped Logs</a></li>
+<li><a href="#virtualhosts">VirtualHosts</a>
+<li><a href="#other">Other Log Files</a>
+  <ul>
+    <li><a href="#pidfile">PID File</a></li>
+    <li><a href="#scriptlog">Script Log</a></li>
+    <li><a href="#rewritelog">Rewrite Log</a></li>
+  </ul></li>
+</ul>
+
+<hr>
+
+<h2><a name="security">Security Warning</a></h2>
+
+<p>Anyone who can write to the directory where Apache is writing a
+log file can almost certainly gain access to the uid that the server is
+started as, which is normally root.  Do <EM>NOT</EM> give people write
+access to the directory the logs are stored in without being aware of
+the consequences; see the <A HREF="misc/security_tips.html">security tips</A>
+document for details.</p>
+
+<p>In addition, log files may contain information supplied directly
+by the client, without escaping.  Therefore, it is possible for
+malicious clients to insert control-characters in the log files, so
+care must be taken in dealing with raw logs.</p>
+
+<hr>
+
+<h2><a name="errorlog">Error Log</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#errorlog">ErrorLog</a><br>
+<a href="mod/core.html#loglevel">LogLevel</a>
+</td></tr></table>
+
+<p>The server error log, whose name and location is set by the <a
+href="mod/core.html#errorlog">ErrorLog</a> directive, is the most
+important log file.  This is the place where Apache HTTPD will send
+diagnostic information and record any errors that it encounters in
+processing requests.  It is the first place to look when a problem
+occurs with starting the server or with the operation of the server,
+since it will often contain details of what went wrong and how to fix
+it.</p>
+
+<p>The error log is usually written to a file (typically
+<code>error_log</code> on unix systems and <code>error.log</code> on
+Windows and OS/2).  On unix systems it is also possible to have the
+server send errors to the <code>syslog</code> or <a href="#pipe">pipe
+them through a program</a>.</p>
+
+<p>The format of the error log is relatively free-form and
+descriptive.  But there is certain information that is contained
+in most error log entries.  For example, here is a typical message.</p>
+
+<blockquote><code>
+[Wed Oct 11 14:32:52 2000] [error] [client 127.0.0.1] client denied by server configuration: /export/home/live/ap/htdocs/test
+</code></blockquote>
+
+<p>The first item in the log entry is the date and time of the
+message.  The second entry lists the severity of the error being
+reported. The <a href="mod/core.html#loglevel">LogLevel</a> directive
+is used to control the types of errors that are sent to the error log
+by restricting the severity level.  The third entry gives the IP
+address of the client that generated the error.  Beyond that is the
+message itself, which in this case indicates that the server has been
+configured to deny the client access.  The server reports the
+file-system path (as opposed to the web path) of the requested
+document.</p>
+
+<p>A very wide variety of different messages can appear in the error
+log.  Most look similar to the example above.  The error log will also
+contain debugging output from CGI scripts.  Any information written to
+<code>stderr</code> by a CGI script will be copied directly to the
+error log.</p>
+
+<p>It is not possible to customize the error log by adding or removing
+information.  However, error log entries dealing with particular
+requests have corresponding entries in the <a href="accesslog">access
+log</a>.  For example, the above example entry corresponds to an
+access log entry with status code 403.  Since it is possible to
+customize the access log, you can obtain more information about error
+conditions using that log file.</p>
+
+<p>During testing, it is often useful to continuously monitor the
+error log for any problems.  On unix systems, you can accomplish this 
+using:</p>
+<blockquote><code>
+tail -f error_log
+</code></blockquote>
+
+<hr>
+
+<h2><a name="accesslog">Access Log</a></h2>
+
+<table border=1><tr><td valign="top">
+<strong>Related Modules</strong><br><br>
+
+<a href="mod/mod_log_config.html">mod_log_config</a><br>
+
+</td><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/mod_log_config.html#customlog">CustomLog</a><br>
+<a href="mod/mod_log_config.html#logformat">LogFormat</a><br>
+<a href="mod/mod_setenvif.html#setenvif">SetEnvIf</a>
+
+</td></tr></table>
+
+<p>The server access log records all requests processed by the server.
+The location of the access log as well as its contents are controlled
+by the <a href="mod/mod_log_config.html#customlog">CustomLog</a>
+directive.  The <a
+href="mod/mod_log_config.html#logformat">LogFormat</a> directive can
+be used to simplify the selection of the contents of the logs.
+This section describes how to configure the server to record
+information in the access log.</p>
+
+<p>Of course, storing the information in the access log is only the
+start of log management.  The next step is to analyze this information
+to produce useful statistics.  Log analysis in general is beyond the
+scope of this document, and not really part of the job of the
+web server itself.  For more information about this topic, and for
+applications which perform log analysis, check the <a
+href="http://dmoz.org/Computers/Software/Internet/Site_Management/Log_analysis/"
+>Open Directory</a> or <a
+href="http://dir.yahoo.com/Computers_and_Internet/Software/Internet/World_Wide_Web/Servers/Log_Analysis_Tools/"
+>Yahoo</a>.</p>
+
+<p>Various versions of Apache HTTPD have used other modules and
+directives to control access logging, including mod_log_referer,
+mod_log_agent, and the <code>TransferLog</code> directive.  The
+<code>CustomLog</code> directive now subsumes the functionality of all
+the older directives.</p>
+
+<p>The format of the access log is highly configurable.  The format is
+specified using a <a href="mod/mod_log_config.html#format">format
+string</a> that looks much like a C-style printf(1) format string.
+Some examples are presented in the next sections.  For a complete list
+of the possible contents of the format string, see the <a
+href="mod/mod_log_config.html">mod_log_config documentation</a>.</p>
+
+<h3><a name="common">Common Log Format</a></h3>
+
+<p>A typical configuration for the access log might look
+as follows.</p>
+
+<blockquote><code>
+LogFormat "%h %l %u %t \"%r\" %>s %b" common<br>
+CustomLog logs/access_log common
+</code></blockquote>
+
+<p>This defines the <em>nickname</em> <code>common</code> and
+associates it with a particular log format string.  The format string
+consists of percent directives, each of which tell the server to log a
+particular piece of information.  Literal characters may also be
+placed in the format string and will be copied directly into the log
+output.  The quote character (<code>"</code>) must be escaped by
+placing a back-slash before it to prevent it from being interpreted as
+the end of the format string.  The format string may also contain the
+special control characters "<code>\n</code>" for new-line and
+"<code>\t</code>" for tab.</p>
+
+<p>The <code>CustomLog</code> directive sets up a new log file using
+the defined <em>nickname</em>.  The filename for the access log is
+relative to the <a href="mod/core.html#serverroot">ServerRoot</a>
+unless it begins with a slash.</p>
+
+<p>The above configuration will write log entries in a format known as
+the Common Log Format (CLF).  This standard format can be produced by
+many different web servers and read by many log analysis programs.
+The log file entries produced in CLF will look something like
+this:</p>
+
+<blockquote><code>
+127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
+</code></blockquote>
+
+<p>Each part of this log entry is described below.</p>
+
+<dl>
+<dt><code>127.0.0.1</code> (<code>%h</code>)</dt> <dd>This is the IP
+address of the client (remote host) which made the request to the
+server.  If <a
+href="mod/core.html#hostnamelookups">HostNameLookups</a> is set to
+<code>On</code>, then the server will try to determine the hostname
+and log it in place of the IP address.  However, this configuration is
+not recommended since it can significantly slow the server.  Instead,
+it is best to use a log post-processor such as <a
+href="programs/logresolve.html">logresolve</a> to determine the
+hostnames.  The IP address reported here is not necessarily the
+address of the machine at which the user is sitting.  If a proxy
+server exists between the user and the server, this address will be
+the address of the proxy, rather than the originating machine.</dd>
+
+<dt><code>-</code> (<code>%l</code>)</dt> <dd>The "hyphen" in the
+output indicates that the requested piece of information is not
+available.  In this case, the information that is not available is the
+RFC 1413 identity of the client determined by <code>identd</code> on
+the clients machine.  This information is highly unreliable and should
+almost never be used except on tightly controlled internal networks.
+Apache HTTPD will not even attempt to determine this information
+unless <a href="mod/core.html#identitycheck">IdentityCheck</a> is set
+to <code>On</code>.</dd>
+
+<dt><code>frank</code> (<code>%u</code>)</dt> <dd>This is the userid
+of the person requesting the document as determined by HTTP
+authentication.  The same value is typically provided to CGI scripts
+in the <code>REMOTE_USER</code> environment variable.  If the status
+code for the request (see below) is 401, then this value should not be
+trusted because the user is not yet authenticated.  If the document is
+not password protected, this entry will be "<code>-</code>" just like
+the previous one.</dd>
+
+<dt><code>[10/Oct/2000:13:55:36 -0700]</code> (<code>%t</code>)</dt>
+<dd>The time that the server finished processing the request.  The
+format is:
+<BLOCKQUOTE><CODE> [day/month/year:hour:minute:second zone] <BR>
+day = 2*digit<BR>
+month = 3*letter<BR>
+year = 4*digit<BR>
+hour = 2*digit<BR>
+minute = 2*digit<BR>
+second = 2*digit<BR>
+zone = (`+' | `-') 4*digit</CODE></BLOCKQUOTE>
+It is possible to have the time displayed in another format
+by specifying <code>%{format}t</code> in the log format string, where
+<code>format</code> is as in <code>strftime(3)</code> from the C 
+standard library.
+</dd>
+
+<dt><code>"GET /apache_pb.gif HTTP/1.0"</code>
+(<code>\"%r\"</code>)</dt> <dd>The request line from the client is
+given in double quotes.  The request line contains a great deal of
+useful information.  First, the method used by the client is
+<code>GET</code>.  Second, the client requested the resource
+<code>/apache_pb.gif</code>, and third, the client used the protocol
+<code>HTTP/1.0</code>.</dd> It is also possible to log one or more
+parts of the request line independently.  For example, the format
+string "<code>%m %U%q %H</code>" will log the method, path,
+query-string, and protocol, resulting in exactly the same output as
+"<code>%r</code>".</dd>
+
+<dt><code>200</code></dt> (<code>%>s</code>) <dd>This is the status
+code that the server sends back to the client.  This information is
+very valuable, because it reveals whether the request resulted in a
+successful response (codes beginning in 2), a redirection (codes
+beginning in 3), an error caused by the client (codes beginning in 4),
+or an error in the server (codes beginning in 5). 
+The full list of possible status codes can be
+found in the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt"
+>HTTP specification</a> (RFC2616 section 10).</dd>
+
+<dt><code>2326</code> (<code>%b</code>)
+<dd>The last entry indicates the size of the object returned to
+the client, not including the response headers.  If no content
+was returned to the client, this value will be "<code>-</code>".
+To log "<code>0</code>" for no content, use <code>%B</code>
+instead.</dd>
+
+</dl>
+
+<h4><a name="combined">Combined Log Format</a></h4>
+
+<p>Another commonly used format string is called the
+Combined Log Format.  It can be used as follows.</p>
+
+<blockquote><code>
+LogFormat "%h %l %u %t \"%r\" %&gt;s %b \"%{Referer}i\" \"%{User-agent}i\"" combined<br>
+CustomLog log/acces_log combined
+</code></blockquote>
+
+<p>This format is exactly the same as the Common Log Format,
+with the addition of two more fields.  The access log under this
+format will look like:</p>
+
+<blockquote><code>
+127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)"
+</code></blockquote>
+
+<p>The additional fields are:</p>
+
+<dl>
+
+<dt><code>"http://www.example.com/start.html"</code>
+(<code>\"%{Referer}i\"</code>)</dt> <dd>The "Referer" (sic) HTTP
+request header.  This gives the site that the client reports having
+been referred from.  (This should be the page that links to or includes
+<code>/apache_pb.gif</code>).
+
+<dt><code>"Mozilla/4.08 [en] (Win98; I ;Nav)"</code>
+(<code>\"%{User-agent}i\"</code>)</dt> <dd>The User-Agent HTTP request
+header.  This is the identifying information that the client browser
+reports about itself.</dd>
+
+</dl>
+
+<h3><a name="multiple">Multiple Access Logs</a></h3>
+
+<p>Multiple access logs can be created simply by specifying multiple
+<code>CustomLog</code> directives in the configuration file.  For
+example, the following directives will create three access logs.  The
+first contains the basic CLF information, while the second and third
+contain referer and browser information.  The last two
+<code>CustomLog</code> lines show how to mimic the effects of the
+<code>ReferLog</code> and <code>AgentLog</code> directives.</p>
+
+<blockquote><code>
+LogFormat "%h %l %u %t \"%r\" %>s %b" common<br>
+CustomLog logs/access_log common<br>
+CustomLog logs/referer_log "%{Referer}i -> %U"<br>
+CustomLog logs/agent_log "%{User-agent}i"
+</code></blockquote>
+
+<p>This example also shows that it is not necessary to define a
+nickname with the <code>LogFormat</code> directive.  Instead, the log
+format can be specified directly in the <code>CustomLog</code>
+directive.</p>
+
+<h3><a name="conditional">Conditional Logging</a></h3>
+
+<p>There are times when it is convenient to exclude certain entries
+from the access logs based on characteristics of the client request.
+This is easily accomplished with the help of <a
+href="env.html">environment variables</a>.  First, an environment
+variable must be set to indicate that the request meets certain
+conditions.  This is usually accomplished with <a
+href="mod/mod_setenvif.html#setenvif">SetEnvIf</a>.  Then the
+<code>env=</code> clause of the <code>CustomLog</code> directive is
+used to include or exclude requests where the environment variable is
+set.  Some examples:</p>
+
+<blockquote><code>
+# Mark requests from the loop-back interface<br>
+SetEnvIf Remote_Addr "127\.0\.0\.1" dontlog<br>
+# Mark requests for the robots.txt file<br>
+SetEnvIf Request_URI "^/robots\.txt$" dontlog<br>
+# Log what remains<br>
+CustomLog logs/access_log common env=!dontlog
+</code></blockquote>
+
+<p>As another example, consider logging requests from english-speakers
+to one log file, and non-english speakers to a different log file.</p>
+
+<blockquote><code>
+SetEnvIf Accept-Language "en" english<br>
+CustomLog logs/english_log common env=english<br>
+CustomLog logs/non_english_log common env=!english
+</code></blockquote>
+
+<p>Although we have just shown that conditional logging is very
+powerful and flexibly, it is not the only way to control the contents
+of the logs.  Log files are more useful when they contain a complete
+record of server activity.  It is often easier to simply post-process
+the log files to remove requests that you do not want to consider.</p>
+
+<hr>
+
+<h2><a name="rotation">Log Rotation</a></h2>
+
+<p>On even a moderately busy server, the quantity of information
+stored in the log files is very large.  The access log file typically
+grows 1 MB or more per 10,000 requests.  It will consequently be
+necessary to periodically rotate the log files by moving or deleting
+the existing logs.  This cannot be done while the server is running,
+because Apache will continue writing to the old log file as long as it
+holds the file open.  Instead, the server must be <a
+href="stopping.html">restarted</a> after the log files are moved or
+deleted so that it will open new log files.</p>
+
+<p>By using a <em>graceful</em> restart, the server can be instructed
+to open new log files without losing any existing or pending
+connections from clients.  However, in order to accomplish this, the
+server must continue to write to the old log files while it finishes
+serving old requests.  It is therefore necessary to wait for some time
+after the restart before doing any processing on the log files.  A
+typical scenario that simply rotates the logs and compresses the old
+logs to save space is:</p>
+
+<blockquote><code>
+mv access_log access_log.old<br>
+mv error_log error_log.old<br>
+apachectl graceful<br>
+sleep 600<br>
+gzip access_log.old error_log.old
+</code></blockquote>
+
+<p>Another way to perform log rotation is using <a href="#piped">piped
+logs</a> as discussed in the next section.</p>
+
+<hr>
+
+<h2><a name="piped">Piped Logs</a></h2>
+
+<p>Apache HTTPD is capable of writing error and access log files
+through a pipe to another process, rather than directly to a file.
+This capability dramatically increases the flexibility of logging,
+without adding code to the main server.  In order to write logs to a
+pipe, simply replace the filename with the pipe character
+"<code>|</code>", followed by the name of the executable which should
+accept log entries on its standard input.  Apache will start the
+piped-log process when the server starts, and will restart it if it
+crashes while the server is running.  (This last feature is why we can
+refer to this technique as "reliable piped logging".)</p>
+
+<p>Piped log processes are spawned by the parent Apache HTTPD process,
+and inherit the userid of that process.  This means that piped log
+programs usually run as root.  It is therefore very important to keep
+the programs simple and secure.</p>
+
+<p>Some simple examples using piped logs:</p>
+
+<blockquote><code>
+# compressed logs<br>
+CustomLog "|/usr/bin/gzip -c >> /var/log/access_log.gz" common<br>
+# almost-real-time name resolution<br>
+CustomLog "|/usr/local/apache/bin/logresolve >> /var/log/access_log" common
+</code></blockquote>
+
+<p>Notice that quotes are used to enclose the entire command
+that will be called for the pipe.  Although these examples are
+for the access log, the same technique can be used for the
+error log.</p>
+
+<p>One important use of piped logs is to allow log rotation without
+having to restart the server.  Apache HTTPD includes a simple program
+called <a href="programs/rotatelogs.html">rotatelogs</a> for this
+purpose.  For example, to rotate the logs every 24 hours, you can
+use:</p>
+
+<blockquote><code>
+CustomLog "|/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common
+</code></blockquote>
+
+<p>A similar, but much more flexible log rotation program
+called <a href="http://www.ford-mason.co.uk/resources/cronolog/">cronolog</a>
+is available at an external site.</p>
+
+<p>As with conditional logging, piped logs are a very powerful tool,
+but they should not be used where a simpler solution like
+off-line post-processing is available.</p>
+
+<hr>
+
+<h2><a name="virtualhosts">Virtual Hosts</a></h2>
+
+<p>When running a server with many <a href="vhosts/">virtual
+hosts</a>, there are several options for dealing with log files.
+First, it is possible to use logs exactly as in a single-host server.
+Simply by placing the logging directives outside the
+<code>&lt;VirtualHost&gt;</code> sections in the main server context,
+it is possible to log all requests in the same access log and error
+log.  This technique does not allow for easy collection of statistics
+on individual virtual hosts.</p>
+
+<p>If <code>CustomLog</code> or <code>ErrorLog</code> directives are
+placed inside a <code>&lt;VirtualHost&gt;</code> section, all requests
+or errors for that virtual host will be logged only to the specified
+file.  Any virtual host which does not have logging directives will
+still have its requests sent to the main server logs.  This technique
+is very useful for a small number of virtual hosts, but if the number
+of hosts is very large, it can be complicated to manage.  In addition,
+it can often create problems with <a
+href="vhosts/fd-limits.html">insufficient file descriptors</a>.</p>
+
+<p>For the access log, there is a very good compromise.  By adding
+information on the virtual host to the log format string,
+it is possible to log all hosts to the same log, and later
+split the log into individual files.  For example, consider the
+following directives.</p>
+
+<blockquote><code>
+LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost<br>
+CustomLog logs/access_log comonvhost
+</code></blockquote>
+
+<p>The <code>%v</code> is used to log the name of the virtual host
+that is serving the request.  Then a program like <a
+href="programs/other.html">split-logfile</a> can be used to
+post-process the access log in order to split it into one file per
+virtual host.</p>
+
+<p>Unfortunately, no similar technique is available for the error log,
+so you must choose between mixing all virtual hosts in the same error
+log and using one error log per virtual host.</p>
+
+<hr>
+
+<h2><a name="other">Other Log Files</a></h2>
+
+<table border=1><tr><td valign="top">
+<strong>Related Modules</strong><br><br>
+
+<a href="mod/mod_cgi.html">mod_cgi</a><br>
+<a href="mod/mod_rewrite.html">mod_rewrite</a>
+
+</td><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#pidfile">PidFile</a><br>
+<a href="mod/mod_rewrite.html#RewriteLog">RewriteLog</a></br>
+<a href="mod/mod_rewrite.html#RewriteLogLevel">RewriteLogLevel</a></br>
+<a href="mod/mod_cgi.html#scriptlog">ScriptLog</a><br>
+<a href="mod/mod_cgi.html#scriptloglength">ScriptLogLength</a><br>
+<a href="mod/mod_cgi.html#scriptlogbuffer">ScriptLogBuffer</a>
+
+</td></tr></table>
+
+<h3><a name="pidfile">PID File</a></h3>
+
+<p>On startup, Apache HTTPD saves the process id of the parent httpd
+process to the file <code>logs/httpd.pid</code>. This filename can be
+changed with the <A HREF="mod/core.html#pidfile">PidFile</A>
+directive. The process-id is for use by the administrator in
+restarting and terminating the daemon by sending signals
+to the parent process; on Windows, use the -k command line
+option instead.  For more information see the <A
+HREF="stopping.html">Stopping and Restarting</A> page.
+
+<h3><a name="scriptlog">Script Log</a></h3>
+
+<p>In order to aid in debugging, the
+<a href="mod/mod_cgi.html#scriptlog">ScriptLog</a>
+directive allows you to record the input to and output from
+CGI scripts.  This should only be used in testing - not for
+live servers.  More information is available in the
+<a href="mod/mod_cgi.html">mod_cgi documentation</a>.
+
+<h3><a name="rewritelog">Rewrite Log</a></h3>
+
+<p>When using the powerful and complex features of <a
+href="mod/mod_rewrite.html">mod_rewrite</a>, it is almost always
+necessary to use the <a
+href="mod/mod_rewrite.html#RewriteLog">RewriteLog</a> to help in
+debugging.  This log file produces a detailed analysis of how the
+rewriting engine transforms requests.  The level of detail is
+controlled by the <a
+href="mod/mod_rewrite.html#RewriteLogLevel">RewriteLogLevel</a>
+directive.</p>
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/logs.html.en b/docs/manual/logs.html.en
new file mode 100644 (file)
index 0000000..d3ebf48
--- /dev/null
@@ -0,0 +1,580 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Log Files - Apache HTTP Server</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<h1 align="center">Log Files</h1>
+
+<p>In order to effectively manage a web server, it is necessary to get
+feedback about the activity and performance of the server as well as
+any problems that may be occuring.  The Apache HTTP Server provides
+very comprehensive and flexible logging capabilities.  This document
+describes how to configure the various log files, and how to
+understand what the logs contain.</p>
+
+<ul>
+<li><a href="#security">Security Warning</a></li>
+<li><a href="#errorlog">Error Log</a></li>
+<li><a href="#accesslog">Access Log</a>
+  <ul>
+    <li><a href="#common">Common Log Format</a></li>
+    <li><a href="#combined">Combined Log Format</a></li>
+    <li><a href="#multiple">Multiple Access Logs</a></li>
+    <li><a href="#conditional">Conditional Logging</a></li>
+  </ul></li>
+<li><a href="#rotation">Log Rotation</a></li>
+<li><a href="#piped">Piped Logs</a></li>
+<li><a href="#virtualhosts">VirtualHosts</a>
+<li><a href="#other">Other Log Files</a>
+  <ul>
+    <li><a href="#pidfile">PID File</a></li>
+    <li><a href="#scriptlog">Script Log</a></li>
+    <li><a href="#rewritelog">Rewrite Log</a></li>
+  </ul></li>
+</ul>
+
+<hr>
+
+<h2><a name="security">Security Warning</a></h2>
+
+<p>Anyone who can write to the directory where Apache is writing a
+log file can almost certainly gain access to the uid that the server is
+started as, which is normally root.  Do <EM>NOT</EM> give people write
+access to the directory the logs are stored in without being aware of
+the consequences; see the <A HREF="misc/security_tips.html">security tips</A>
+document for details.</p>
+
+<p>In addition, log files may contain information supplied directly
+by the client, without escaping.  Therefore, it is possible for
+malicious clients to insert control-characters in the log files, so
+care must be taken in dealing with raw logs.</p>
+
+<hr>
+
+<h2><a name="errorlog">Error Log</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#errorlog">ErrorLog</a><br>
+<a href="mod/core.html#loglevel">LogLevel</a>
+</td></tr></table>
+
+<p>The server error log, whose name and location is set by the <a
+href="mod/core.html#errorlog">ErrorLog</a> directive, is the most
+important log file.  This is the place where Apache HTTPD will send
+diagnostic information and record any errors that it encounters in
+processing requests.  It is the first place to look when a problem
+occurs with starting the server or with the operation of the server,
+since it will often contain details of what went wrong and how to fix
+it.</p>
+
+<p>The error log is usually written to a file (typically
+<code>error_log</code> on unix systems and <code>error.log</code> on
+Windows and OS/2).  On unix systems it is also possible to have the
+server send errors to the <code>syslog</code> or <a href="#pipe">pipe
+them through a program</a>.</p>
+
+<p>The format of the error log is relatively free-form and
+descriptive.  But there is certain information that is contained
+in most error log entries.  For example, here is a typical message.</p>
+
+<blockquote><code>
+[Wed Oct 11 14:32:52 2000] [error] [client 127.0.0.1] client denied by server configuration: /export/home/live/ap/htdocs/test
+</code></blockquote>
+
+<p>The first item in the log entry is the date and time of the
+message.  The second entry lists the severity of the error being
+reported. The <a href="mod/core.html#loglevel">LogLevel</a> directive
+is used to control the types of errors that are sent to the error log
+by restricting the severity level.  The third entry gives the IP
+address of the client that generated the error.  Beyond that is the
+message itself, which in this case indicates that the server has been
+configured to deny the client access.  The server reports the
+file-system path (as opposed to the web path) of the requested
+document.</p>
+
+<p>A very wide variety of different messages can appear in the error
+log.  Most look similar to the example above.  The error log will also
+contain debugging output from CGI scripts.  Any information written to
+<code>stderr</code> by a CGI script will be copied directly to the
+error log.</p>
+
+<p>It is not possible to customize the error log by adding or removing
+information.  However, error log entries dealing with particular
+requests have corresponding entries in the <a href="accesslog">access
+log</a>.  For example, the above example entry corresponds to an
+access log entry with status code 403.  Since it is possible to
+customize the access log, you can obtain more information about error
+conditions using that log file.</p>
+
+<p>During testing, it is often useful to continuously monitor the
+error log for any problems.  On unix systems, you can accomplish this 
+using:</p>
+<blockquote><code>
+tail -f error_log
+</code></blockquote>
+
+<hr>
+
+<h2><a name="accesslog">Access Log</a></h2>
+
+<table border=1><tr><td valign="top">
+<strong>Related Modules</strong><br><br>
+
+<a href="mod/mod_log_config.html">mod_log_config</a><br>
+
+</td><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/mod_log_config.html#customlog">CustomLog</a><br>
+<a href="mod/mod_log_config.html#logformat">LogFormat</a><br>
+<a href="mod/mod_setenvif.html#setenvif">SetEnvIf</a>
+
+</td></tr></table>
+
+<p>The server access log records all requests processed by the server.
+The location of the access log as well as its contents are controlled
+by the <a href="mod/mod_log_config.html#customlog">CustomLog</a>
+directive.  The <a
+href="mod/mod_log_config.html#logformat">LogFormat</a> directive can
+be used to simplify the selection of the contents of the logs.
+This section describes how to configure the server to record
+information in the access log.</p>
+
+<p>Of course, storing the information in the access log is only the
+start of log management.  The next step is to analyze this information
+to produce useful statistics.  Log analysis in general is beyond the
+scope of this document, and not really part of the job of the
+web server itself.  For more information about this topic, and for
+applications which perform log analysis, check the <a
+href="http://dmoz.org/Computers/Software/Internet/Site_Management/Log_analysis/"
+>Open Directory</a> or <a
+href="http://dir.yahoo.com/Computers_and_Internet/Software/Internet/World_Wide_Web/Servers/Log_Analysis_Tools/"
+>Yahoo</a>.</p>
+
+<p>Various versions of Apache HTTPD have used other modules and
+directives to control access logging, including mod_log_referer,
+mod_log_agent, and the <code>TransferLog</code> directive.  The
+<code>CustomLog</code> directive now subsumes the functionality of all
+the older directives.</p>
+
+<p>The format of the access log is highly configurable.  The format is
+specified using a <a href="mod/mod_log_config.html#format">format
+string</a> that looks much like a C-style printf(1) format string.
+Some examples are presented in the next sections.  For a complete list
+of the possible contents of the format string, see the <a
+href="mod/mod_log_config.html">mod_log_config documentation</a>.</p>
+
+<h3><a name="common">Common Log Format</a></h3>
+
+<p>A typical configuration for the access log might look
+as follows.</p>
+
+<blockquote><code>
+LogFormat "%h %l %u %t \"%r\" %>s %b" common<br>
+CustomLog logs/access_log common
+</code></blockquote>
+
+<p>This defines the <em>nickname</em> <code>common</code> and
+associates it with a particular log format string.  The format string
+consists of percent directives, each of which tell the server to log a
+particular piece of information.  Literal characters may also be
+placed in the format string and will be copied directly into the log
+output.  The quote character (<code>"</code>) must be escaped by
+placing a back-slash before it to prevent it from being interpreted as
+the end of the format string.  The format string may also contain the
+special control characters "<code>\n</code>" for new-line and
+"<code>\t</code>" for tab.</p>
+
+<p>The <code>CustomLog</code> directive sets up a new log file using
+the defined <em>nickname</em>.  The filename for the access log is
+relative to the <a href="mod/core.html#serverroot">ServerRoot</a>
+unless it begins with a slash.</p>
+
+<p>The above configuration will write log entries in a format known as
+the Common Log Format (CLF).  This standard format can be produced by
+many different web servers and read by many log analysis programs.
+The log file entries produced in CLF will look something like
+this:</p>
+
+<blockquote><code>
+127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
+</code></blockquote>
+
+<p>Each part of this log entry is described below.</p>
+
+<dl>
+<dt><code>127.0.0.1</code> (<code>%h</code>)</dt> <dd>This is the IP
+address of the client (remote host) which made the request to the
+server.  If <a
+href="mod/core.html#hostnamelookups">HostNameLookups</a> is set to
+<code>On</code>, then the server will try to determine the hostname
+and log it in place of the IP address.  However, this configuration is
+not recommended since it can significantly slow the server.  Instead,
+it is best to use a log post-processor such as <a
+href="programs/logresolve.html">logresolve</a> to determine the
+hostnames.  The IP address reported here is not necessarily the
+address of the machine at which the user is sitting.  If a proxy
+server exists between the user and the server, this address will be
+the address of the proxy, rather than the originating machine.</dd>
+
+<dt><code>-</code> (<code>%l</code>)</dt> <dd>The "hyphen" in the
+output indicates that the requested piece of information is not
+available.  In this case, the information that is not available is the
+RFC 1413 identity of the client determined by <code>identd</code> on
+the clients machine.  This information is highly unreliable and should
+almost never be used except on tightly controlled internal networks.
+Apache HTTPD will not even attempt to determine this information
+unless <a href="mod/core.html#identitycheck">IdentityCheck</a> is set
+to <code>On</code>.</dd>
+
+<dt><code>frank</code> (<code>%u</code>)</dt> <dd>This is the userid
+of the person requesting the document as determined by HTTP
+authentication.  The same value is typically provided to CGI scripts
+in the <code>REMOTE_USER</code> environment variable.  If the status
+code for the request (see below) is 401, then this value should not be
+trusted because the user is not yet authenticated.  If the document is
+not password protected, this entry will be "<code>-</code>" just like
+the previous one.</dd>
+
+<dt><code>[10/Oct/2000:13:55:36 -0700]</code> (<code>%t</code>)</dt>
+<dd>The time that the server finished processing the request.  The
+format is:
+<BLOCKQUOTE><CODE> [day/month/year:hour:minute:second zone] <BR>
+day = 2*digit<BR>
+month = 3*letter<BR>
+year = 4*digit<BR>
+hour = 2*digit<BR>
+minute = 2*digit<BR>
+second = 2*digit<BR>
+zone = (`+' | `-') 4*digit</CODE></BLOCKQUOTE>
+It is possible to have the time displayed in another format
+by specifying <code>%{format}t</code> in the log format string, where
+<code>format</code> is as in <code>strftime(3)</code> from the C 
+standard library.
+</dd>
+
+<dt><code>"GET /apache_pb.gif HTTP/1.0"</code>
+(<code>\"%r\"</code>)</dt> <dd>The request line from the client is
+given in double quotes.  The request line contains a great deal of
+useful information.  First, the method used by the client is
+<code>GET</code>.  Second, the client requested the resource
+<code>/apache_pb.gif</code>, and third, the client used the protocol
+<code>HTTP/1.0</code>.</dd> It is also possible to log one or more
+parts of the request line independently.  For example, the format
+string "<code>%m %U%q %H</code>" will log the method, path,
+query-string, and protocol, resulting in exactly the same output as
+"<code>%r</code>".</dd>
+
+<dt><code>200</code></dt> (<code>%>s</code>) <dd>This is the status
+code that the server sends back to the client.  This information is
+very valuable, because it reveals whether the request resulted in a
+successful response (codes beginning in 2), a redirection (codes
+beginning in 3), an error caused by the client (codes beginning in 4),
+or an error in the server (codes beginning in 5). 
+The full list of possible status codes can be
+found in the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt"
+>HTTP specification</a> (RFC2616 section 10).</dd>
+
+<dt><code>2326</code> (<code>%b</code>)
+<dd>The last entry indicates the size of the object returned to
+the client, not including the response headers.  If no content
+was returned to the client, this value will be "<code>-</code>".
+To log "<code>0</code>" for no content, use <code>%B</code>
+instead.</dd>
+
+</dl>
+
+<h4><a name="combined">Combined Log Format</a></h4>
+
+<p>Another commonly used format string is called the
+Combined Log Format.  It can be used as follows.</p>
+
+<blockquote><code>
+LogFormat "%h %l %u %t \"%r\" %&gt;s %b \"%{Referer}i\" \"%{User-agent}i\"" combined<br>
+CustomLog log/acces_log combined
+</code></blockquote>
+
+<p>This format is exactly the same as the Common Log Format,
+with the addition of two more fields.  The access log under this
+format will look like:</p>
+
+<blockquote><code>
+127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)"
+</code></blockquote>
+
+<p>The additional fields are:</p>
+
+<dl>
+
+<dt><code>"http://www.example.com/start.html"</code>
+(<code>\"%{Referer}i\"</code>)</dt> <dd>The "Referer" (sic) HTTP
+request header.  This gives the site that the client reports having
+been referred from.  (This should be the page that links to or includes
+<code>/apache_pb.gif</code>).
+
+<dt><code>"Mozilla/4.08 [en] (Win98; I ;Nav)"</code>
+(<code>\"%{User-agent}i\"</code>)</dt> <dd>The User-Agent HTTP request
+header.  This is the identifying information that the client browser
+reports about itself.</dd>
+
+</dl>
+
+<h3><a name="multiple">Multiple Access Logs</a></h3>
+
+<p>Multiple access logs can be created simply by specifying multiple
+<code>CustomLog</code> directives in the configuration file.  For
+example, the following directives will create three access logs.  The
+first contains the basic CLF information, while the second and third
+contain referer and browser information.  The last two
+<code>CustomLog</code> lines show how to mimic the effects of the
+<code>ReferLog</code> and <code>AgentLog</code> directives.</p>
+
+<blockquote><code>
+LogFormat "%h %l %u %t \"%r\" %>s %b" common<br>
+CustomLog logs/access_log common<br>
+CustomLog logs/referer_log "%{Referer}i -> %U"<br>
+CustomLog logs/agent_log "%{User-agent}i"
+</code></blockquote>
+
+<p>This example also shows that it is not necessary to define a
+nickname with the <code>LogFormat</code> directive.  Instead, the log
+format can be specified directly in the <code>CustomLog</code>
+directive.</p>
+
+<h3><a name="conditional">Conditional Logging</a></h3>
+
+<p>There are times when it is convenient to exclude certain entries
+from the access logs based on characteristics of the client request.
+This is easily accomplished with the help of <a
+href="env.html">environment variables</a>.  First, an environment
+variable must be set to indicate that the request meets certain
+conditions.  This is usually accomplished with <a
+href="mod/mod_setenvif.html#setenvif">SetEnvIf</a>.  Then the
+<code>env=</code> clause of the <code>CustomLog</code> directive is
+used to include or exclude requests where the environment variable is
+set.  Some examples:</p>
+
+<blockquote><code>
+# Mark requests from the loop-back interface<br>
+SetEnvIf Remote_Addr "127\.0\.0\.1" dontlog<br>
+# Mark requests for the robots.txt file<br>
+SetEnvIf Request_URI "^/robots\.txt$" dontlog<br>
+# Log what remains<br>
+CustomLog logs/access_log common env=!dontlog
+</code></blockquote>
+
+<p>As another example, consider logging requests from english-speakers
+to one log file, and non-english speakers to a different log file.</p>
+
+<blockquote><code>
+SetEnvIf Accept-Language "en" english<br>
+CustomLog logs/english_log common env=english<br>
+CustomLog logs/non_english_log common env=!english
+</code></blockquote>
+
+<p>Although we have just shown that conditional logging is very
+powerful and flexibly, it is not the only way to control the contents
+of the logs.  Log files are more useful when they contain a complete
+record of server activity.  It is often easier to simply post-process
+the log files to remove requests that you do not want to consider.</p>
+
+<hr>
+
+<h2><a name="rotation">Log Rotation</a></h2>
+
+<p>On even a moderately busy server, the quantity of information
+stored in the log files is very large.  The access log file typically
+grows 1 MB or more per 10,000 requests.  It will consequently be
+necessary to periodically rotate the log files by moving or deleting
+the existing logs.  This cannot be done while the server is running,
+because Apache will continue writing to the old log file as long as it
+holds the file open.  Instead, the server must be <a
+href="stopping.html">restarted</a> after the log files are moved or
+deleted so that it will open new log files.</p>
+
+<p>By using a <em>graceful</em> restart, the server can be instructed
+to open new log files without losing any existing or pending
+connections from clients.  However, in order to accomplish this, the
+server must continue to write to the old log files while it finishes
+serving old requests.  It is therefore necessary to wait for some time
+after the restart before doing any processing on the log files.  A
+typical scenario that simply rotates the logs and compresses the old
+logs to save space is:</p>
+
+<blockquote><code>
+mv access_log access_log.old<br>
+mv error_log error_log.old<br>
+apachectl graceful<br>
+sleep 600<br>
+gzip access_log.old error_log.old
+</code></blockquote>
+
+<p>Another way to perform log rotation is using <a href="#piped">piped
+logs</a> as discussed in the next section.</p>
+
+<hr>
+
+<h2><a name="piped">Piped Logs</a></h2>
+
+<p>Apache HTTPD is capable of writing error and access log files
+through a pipe to another process, rather than directly to a file.
+This capability dramatically increases the flexibility of logging,
+without adding code to the main server.  In order to write logs to a
+pipe, simply replace the filename with the pipe character
+"<code>|</code>", followed by the name of the executable which should
+accept log entries on its standard input.  Apache will start the
+piped-log process when the server starts, and will restart it if it
+crashes while the server is running.  (This last feature is why we can
+refer to this technique as "reliable piped logging".)</p>
+
+<p>Piped log processes are spawned by the parent Apache HTTPD process,
+and inherit the userid of that process.  This means that piped log
+programs usually run as root.  It is therefore very important to keep
+the programs simple and secure.</p>
+
+<p>Some simple examples using piped logs:</p>
+
+<blockquote><code>
+# compressed logs<br>
+CustomLog "|/usr/bin/gzip -c >> /var/log/access_log.gz" common<br>
+# almost-real-time name resolution<br>
+CustomLog "|/usr/local/apache/bin/logresolve >> /var/log/access_log" common
+</code></blockquote>
+
+<p>Notice that quotes are used to enclose the entire command
+that will be called for the pipe.  Although these examples are
+for the access log, the same technique can be used for the
+error log.</p>
+
+<p>One important use of piped logs is to allow log rotation without
+having to restart the server.  Apache HTTPD includes a simple program
+called <a href="programs/rotatelogs.html">rotatelogs</a> for this
+purpose.  For example, to rotate the logs every 24 hours, you can
+use:</p>
+
+<blockquote><code>
+CustomLog "|/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common
+</code></blockquote>
+
+<p>A similar, but much more flexible log rotation program
+called <a href="http://www.ford-mason.co.uk/resources/cronolog/">cronolog</a>
+is available at an external site.</p>
+
+<p>As with conditional logging, piped logs are a very powerful tool,
+but they should not be used where a simpler solution like
+off-line post-processing is available.</p>
+
+<hr>
+
+<h2><a name="virtualhosts">Virtual Hosts</a></h2>
+
+<p>When running a server with many <a href="vhosts/">virtual
+hosts</a>, there are several options for dealing with log files.
+First, it is possible to use logs exactly as in a single-host server.
+Simply by placing the logging directives outside the
+<code>&lt;VirtualHost&gt;</code> sections in the main server context,
+it is possible to log all requests in the same access log and error
+log.  This technique does not allow for easy collection of statistics
+on individual virtual hosts.</p>
+
+<p>If <code>CustomLog</code> or <code>ErrorLog</code> directives are
+placed inside a <code>&lt;VirtualHost&gt;</code> section, all requests
+or errors for that virtual host will be logged only to the specified
+file.  Any virtual host which does not have logging directives will
+still have its requests sent to the main server logs.  This technique
+is very useful for a small number of virtual hosts, but if the number
+of hosts is very large, it can be complicated to manage.  In addition,
+it can often create problems with <a
+href="vhosts/fd-limits.html">insufficient file descriptors</a>.</p>
+
+<p>For the access log, there is a very good compromise.  By adding
+information on the virtual host to the log format string,
+it is possible to log all hosts to the same log, and later
+split the log into individual files.  For example, consider the
+following directives.</p>
+
+<blockquote><code>
+LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost<br>
+CustomLog logs/access_log comonvhost
+</code></blockquote>
+
+<p>The <code>%v</code> is used to log the name of the virtual host
+that is serving the request.  Then a program like <a
+href="programs/other.html">split-logfile</a> can be used to
+post-process the access log in order to split it into one file per
+virtual host.</p>
+
+<p>Unfortunately, no similar technique is available for the error log,
+so you must choose between mixing all virtual hosts in the same error
+log and using one error log per virtual host.</p>
+
+<hr>
+
+<h2><a name="other">Other Log Files</a></h2>
+
+<table border=1><tr><td valign="top">
+<strong>Related Modules</strong><br><br>
+
+<a href="mod/mod_cgi.html">mod_cgi</a><br>
+<a href="mod/mod_rewrite.html">mod_rewrite</a>
+
+</td><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#pidfile">PidFile</a><br>
+<a href="mod/mod_rewrite.html#RewriteLog">RewriteLog</a></br>
+<a href="mod/mod_rewrite.html#RewriteLogLevel">RewriteLogLevel</a></br>
+<a href="mod/mod_cgi.html#scriptlog">ScriptLog</a><br>
+<a href="mod/mod_cgi.html#scriptloglength">ScriptLogLength</a><br>
+<a href="mod/mod_cgi.html#scriptlogbuffer">ScriptLogBuffer</a>
+
+</td></tr></table>
+
+<h3><a name="pidfile">PID File</a></h3>
+
+<p>On startup, Apache HTTPD saves the process id of the parent httpd
+process to the file <code>logs/httpd.pid</code>. This filename can be
+changed with the <A HREF="mod/core.html#pidfile">PidFile</A>
+directive. The process-id is for use by the administrator in
+restarting and terminating the daemon by sending signals
+to the parent process; on Windows, use the -k command line
+option instead.  For more information see the <A
+HREF="stopping.html">Stopping and Restarting</A> page.
+
+<h3><a name="scriptlog">Script Log</a></h3>
+
+<p>In order to aid in debugging, the
+<a href="mod/mod_cgi.html#scriptlog">ScriptLog</a>
+directive allows you to record the input to and output from
+CGI scripts.  This should only be used in testing - not for
+live servers.  More information is available in the
+<a href="mod/mod_cgi.html">mod_cgi documentation</a>.
+
+<h3><a name="rewritelog">Rewrite Log</a></h3>
+
+<p>When using the powerful and complex features of <a
+href="mod/mod_rewrite.html">mod_rewrite</a>, it is almost always
+necessary to use the <a
+href="mod/mod_rewrite.html#RewriteLog">RewriteLog</a> to help in
+debugging.  This log file produces a detailed analysis of how the
+rewriting engine transforms requests.  The level of detail is
+controlled by the <a
+href="mod/mod_rewrite.html#RewriteLogLevel">RewriteLogLevel</a>
+directive.</p>
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/mod/mod_cgid.html b/docs/manual/mod/mod_cgid.html
new file mode 100644 (file)
index 0000000..396d608
--- /dev/null
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Apache module mod_cgi</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+
+<H1 ALIGN="CENTER">Module mod_cgid</H1>
+
+<p>This module provides for execution of CGI scripts using an external
+CGI daemon.</p>
+
+<P><A
+HREF="module-dict.html#Status"
+REL="Help"
+><STRONG>Status:</STRONG></A> Base (unix threaded MPMs only)
+<BR>
+<A
+HREF="module-dict.html#SourceFile"
+REL="Help"
+><STRONG>Source File:</STRONG></A> mod_cgid.c
+<BR>
+<A
+HREF="module-dict.html#ModuleIdentifier"
+REL="Help"
+><STRONG>Module Identifier:</STRONG></A> cgid_module
+</P>
+
+
+<H2>Summary</H2>
+
+<p>On certain unix operating systems, forking a process from a
+multi-threaded server is a very expensive operation because the new
+process will replicate all the threads of the parent process.  In
+order to avoid incurring this expense on each CGI invocation, mod_cgid
+creates an external daemon that is responsible for forking child
+processes to run CGI scripts.  The main server communicates with this
+daemon using a unix domain socket.</p>
+
+<p>This module is used by default whenever a multi-threaded MPM is
+selected during the compilation process.  At the user level, this
+module is identical in configuration and operation to <a
+href="mod_cgi.html">mod_cgi</a>.  The only exception is the additional
+directive <code>ScriptSock</code> which gives the name of the socket
+to use for communication with the cgi daemon.</p>
+
+<h2>Directives</h2>
+
+<ul>
+<li><a href="mod_cgi.html#scriptlog">ScriptLog</a></li>
+<li><a href="mod_cgi.html#scriptloglength">ScriptLogLength</a></li>
+<li><a href="mod_cgi.html#scriptlogbuffer">ScriptLogBuffer</a></li>
+<li><a href="#scriptsock">ScriptSock</a></li>
+</ul>
+
+<hr>
+
+<H3><A NAME="scriptsock">ScriptSock</A> directive</H3>
+
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> Scriptsock <EM>filename</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> logs/cgisock<BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> Base<br>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_cgid</p>
+
+<p>This directive sets the name of the socket to use for communication
+with the CGI daemon.  The socket will be opened using the permissions
+of the user who starts Apache (usually root).  To maintain the security
+of communications with CGI scripts, it is important that no other
+user has permission to write in the directory where the socket is
+located.</p>
+
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
+
diff --git a/docs/manual/mod/mod_suexec.html b/docs/manual/mod/mod_suexec.html
new file mode 100644 (file)
index 0000000..0910999
--- /dev/null
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Apache module mod_suexec</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<H1 ALIGN="CENTER">Module mod_suexec</H1>
+
+<P>
+This module provides support for <A HREF="../suexec.html">
+running CGI scripts as a specified User and Group</A>.
+</P>
+
+<P><A
+HREF="module-dict.html#Status"
+REL="Help"
+><STRONG>Status:</STRONG></A> Extension
+<BR>
+<A
+HREF="module-dict.html#SourceFile"
+REL="Help"
+><STRONG>Source File:</STRONG></A> mod_suexec.c
+<BR>
+<A
+HREF="module-dict.html#ModuleIdentifier"
+REL="Help"
+><STRONG>Module Identifier:</STRONG></A> suexec_module
+<BR>
+<A
+HREF="module-dict.html#Compatibility"
+REL="Help"
+><STRONG>Compatibility:</STRONG></A> Available in Apache 2.0 and later.
+</P>
+
+<h2>Summary</h2>
+
+<p>This module allows CGI scripts to run as a specified user and Group.</p>
+
+<H2>Directives</H2>
+<UL>
+  <LI><A HREF="#suexecusergroup">SuexecUserGroup</A>
+</UL>
+
+<H2><A NAME="suexecusergroup">SuexecUserGroup directive</A></H2>
+<P>
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> SuexecUserGroup <EM>User Group</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> None<BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config, virtual host<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> Extension<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_suexec<BR>
+<A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+><STRONG>Compatibility:</STRONG></A> SuexecUserGroup is only available in 2.0 and later.</P>
+<P>
+The <CODE>SuexecUserGroup</CODE> directive allows you to specify a user and
+group for CGI programs to run as.  Non-CGI requests are still processes
+with the user specified in the User directive.  This directive replaces
+using the User and Group directives inside of VirtualHosts.
+</P>
+<HR>
+
+<H3 ALIGN="CENTER">
+ Apache HTTP Server Version 2.0
+</H3>
+
+<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A>
+<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A>
+
+</BODY>
+</HTML>
diff --git a/include/util_ldap.h b/include/util_ldap.h
new file mode 100644 (file)
index 0000000..10dc2ea
--- /dev/null
@@ -0,0 +1,275 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ */
+
+#ifndef UTIL_LDAP_H
+#define UTIL_LDAP_H
+
+#include <apr_ldap.h>
+
+/* this whole thing disappears if LDAP is not enabled */
+#ifdef APU_HAS_LDAP
+
+/* APR header files */
+#include <apr_lock.h>
+#include <apr_tables.h>
+#include <apr_time.h>
+
+/* Apache header files */
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+
+
+/*
+ * LDAP Connections
+ */
+
+/* Values that the deref member can have */
+typedef enum {
+    never=LDAP_DEREF_NEVER, 
+    searching=LDAP_DEREF_SEARCHING, 
+    finding=LDAP_DEREF_FINDING, 
+    always=LDAP_DEREF_ALWAYS
+} deref_options;
+
+/* Structure representing an LDAP connection */
+typedef struct util_ldap_connection_t {
+    LDAP *ldap;
+    apr_lock_t *lock;                  /* Lock to indicate this connection is in use */
+    int bound;                         /* Flag to indicate whether this connection is bound yet */
+
+    const char *host;                          /* Name of the LDAP server (or space separated list) */
+    int port;                          /* Port of the LDAP server */
+    deref_options deref;               /* how to handle alias dereferening */
+
+    const char *binddn;                        /* DN to bind to server (can be NULL) */
+    const char *bindpw;                        /* Password to bind to server (can be NULL) */
+
+    int netscapessl;                   /* True if use Netscape SSL connection */
+    const char *certtdb;                       /* Path to Netscape CA database */
+
+    int starttls;                      /* True if StartTLS is enabled */
+    int withtls;                       /* True if StartTLS on this connection */
+
+    const char *reason;                        /* Reason for an error failure */
+
+    struct util_ldap_connection_t *next;
+} util_ldap_connection_t;
+
+/* LDAP cache state information */ 
+typedef struct util_ldap_state_t {
+    apr_pool_t *pool;          /* pool from which this state is allocated */
+    apr_lock_t *mutex;         /* mutex lock for the connection list */
+
+    apr_size_t cache_bytes;    /* Size (in bytes) of shared memory cache */
+    long search_cache_ttl;     /* TTL for search cache */
+    long search_cache_size;    /* Size (in entries) of search cache */
+    long compare_cache_ttl;    /* TTL for compare cache */
+    long compare_cache_size;   /* Size (in entries) of compare cache */
+
+    struct util_ldap_connection_t *connections;
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+    int have_certdb;
+#endif
+} util_ldap_state_t;
+
+
+/**
+ * Open a connection to an LDAP server
+ * @param ldc A structure containing the expanded details of the server
+ *            to connect to. The handle to the LDAP connection is returned
+ *            as ldc->ldap.
+ * @tip This function connects to the LDAP server and binds. It does not
+ *      connect if already connected (ldc->ldap != NULL). Does not bind
+ *      if already bound.
+ * @return If successful LDAP_SUCCESS is returned.
+ * @deffunc int util_ldap_connection_open(util_ldap_connection_t *ldc)
+ */
+int util_ldap_connection_open(util_ldap_connection_t *ldc);
+
+/**
+ * Close a connection to an LDAP server
+ * @param ldc A structure containing the expanded details of the server
+              that was connected.
+ * @tip This function unbinds from the LDAP server, and clears ldc->ldap.
+ *      It is possible to rebind to this server again using the same ldc
+ *      structure, using apr_ldap_open_connection().
+ * @deffunc util_ldap_close_connection(util_ldap_connection_t *ldc)
+ */
+void util_ldap_connection_close(util_ldap_connection_t *ldc);
+
+/**
+ * Find a connection in a list of connections
+ * @param r The request record
+ * @param host The hostname to connect to (multiple hosts space separated)
+ * @param port The port to connect to
+ * @param binddn The DN to bind with
+ * @param bindpw The password to bind with
+ * @param deref The dereferencing behavior
+ * @param netscapessl Start SSL on the connection using ldapssl_client_init() [0|1]
+ * @param starttls Start TLS using STARTTLS parameter [0|1]
+ * @tip Once a connection is found and returned, a lock will be acquired to
+ *      lock that particular connection, so that another thread does not try and
+ *      use this connection while it is busy. Once you are finished with a connection,
+ *      apr_ldap_connection_close() must be called to release this connection.
+ * @deffunc util_ldap_connection_t *util_ldap_connection_find(request_rec *r, const char *host, int port,
+ *                                                           const char *binddn, const char *bindpw, deref_options deref,
+ *                                                           int netscapessl, int starttls)
+ */
+util_ldap_connection_t *util_ldap_connection_find(request_rec *r, const char *host, int port,
+                                                  const char *binddn, const char *bindpw, deref_options deref,
+                                                  int netscapessl, int starttls);
+
+
+/**
+ * Compare two DNs for sameness
+ * @param r The request record
+ * @param ldc The LDAP connection being used.
+ * @param url The URL of the LDAP connection - used for deciding which cache to use.
+ * @param dn The first DN to compare.
+ * @param reqdn The DN to compare the first DN to.
+ * @param compare_dn_on_server Flag to determine whether the DNs should be checked using
+ *                             LDAP calls or with a direct string comparision. A direct
+ *                             string comparison is faster, but not as accurate - false
+ *                             negative comparisons are possible.
+ * @tip Two DNs can be equal and still fail a string comparison. Eg "dc=example,dc=com"
+ *      and "dc=example, dc=com". Use the compare_dn_on_server unless there are serious
+ *      performance issues.
+ * @deffunc int util_ldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
+ *                                        const char *url, const char *dn, const char *reqdn,
+ *                                        int compare_dn_on_server)
+ */
+int util_ldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc, 
+                              const char *url, const char *dn, const char *reqdn, 
+                              int compare_dn_on_server);
+
+/**
+ * A generic LDAP compare function
+ * @param r The request record
+ * @param ldc The LDAP connection being used.
+ * @param url The URL of the LDAP connection - used for deciding which cache to use.
+ * @param dn The DN of the object in which we do the compare.
+ * @param attrib The attribute within the object we are comparing for.
+ * @param value The value of the attribute we are trying to compare for. 
+ * @tip Use this function to determine whether an attribute/value pair exists within an
+ *      object. Typically this would be used to determine LDAP group membership.
+ * @deffunc int util_ldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
+ *                                      const char *url, const char *dn, const char *attrib, const char *value)
+ */
+int util_ldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
+                            const char *url, const char *dn, const char *attrib, const char *value);
+
+/**
+ * Checks a username/password combination by binding to the LDAP server
+ * @param r The request record
+ * @param ldc The LDAP connection being used.
+ * @param url The URL of the LDAP connection - used for deciding which cache to use.
+ * @param basedn The Base DN to search for the user in.
+ * @param scope LDAP scope of the search.
+ * @param filter The user to search for in the form of an LDAP filter. This filter must return
+ *               exactly one user for the check to be successful.
+ * @param bindpw The user password to bind as.
+ * @param binddn The DN of the user will be returned in this variable.
+ * @tip The filter supplied will be searched for. If a single entry is returned, an attempt
+ *      is made to bind as that user. If this bind succeeds, the user is not validated.
+ * @deffunc int util_ldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
+ *                                          char *url, const char *basedn, int scope,
+ *                                          char *filter, char *bindpw, char **binddn)
+ */
+int util_ldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
+                                const char *url, const char *basedn, int scope, 
+                                const char *filter, const char *bindpw, const char **binddn);
+
+/* from apr_ldap_cache.c */
+
+/**
+ * Init the LDAP cache
+ * @param pool The pool to use to initialise the cache
+ * @param reqsize The size of the shared memory segement to request. A size
+ *                of zero requests the max size possible from
+ *                apr_shmem_init()
+ * @deffunc void util_ldap_cache_init(apr_pool_t *p)
+ * @return The status code returned is the status code of the
+ *         apr_smmem_init() call. Regardless of the status, the cache
+ *         will be set up at least for in-process or in-thread operation.
+ */
+apr_status_t util_ldap_cache_init(apr_pool_t *pool, apr_size_t reqsize);
+
+/**
+ * Display formatted stats for cache
+ * @param The pool to allocate the returned string from
+ * @tip This function returns a string allocated from the provided pool that describes
+ *      various stats about the cache.
+ * @deffunc char *util_ald_cache_display(apr_pool_t *pool)
+ */
+char *util_ald_cache_display(apr_pool_t *pool);
+
+
+/* from apr_ldap_cache_mgr.c */
+
+/**
+ * Display formatted stats for cache
+ * @param The pool to allocate the returned string from
+ * @tip This function returns a string allocated from the provided pool that describes
+ *      various stats about the cache.
+ * @deffunc char *util_ald_cache_display(apr_pool_t *pool)
+ */
+char *util_ald_cache_display(apr_pool_t *pool);
+
+#endif /* APU_HAS_LDAP */
+#endif /* UTIL_LDAP_H */
diff --git a/include/util_time.h b/include/util_time.h
new file mode 100644 (file)
index 0000000..704d0e0
--- /dev/null
@@ -0,0 +1,111 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 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
+ * <http://www.apache.org/>.
+ *
+ */
+
+#ifndef APACHE_UTIL_TIME_H
+#define APACHE_UTIL_TIME_H
+
+#include "apr.h"
+#include "apr_time.h"
+#include "httpd.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @package Apache date/time handling functions
+ */
+
+/* Maximum delta from the current time, in seconds, for a past time
+ * to qualify as "recent" for use in the ap_explode_recent_*() functions:
+ */
+#define AP_TIME_RECENT_THRESHOLD 15
+
+/**
+ * convert a recent time to its human readable components in local timezone
+ * @param tm the exploded time
+ * @param t the time to explode: MUST be within the last
+ *          AP_TIME_RECENT_THRESHOLD seconds
+ * @note This is a faster alternative to apr_explode_localtime that uses
+ *       a cache of pre-exploded time structures.  It is useful for things
+ *       that need to explode the current time multiple times per second,
+ *       like loggers.
+ * @return APR_SUCCESS iff successful
+ * @deffunc apr_status_t ap_explode_recent_localtime(apr_exploded_time_t *tm, apr_time_t t);
+ */
+AP_DECLARE(apr_status_t) ap_explode_recent_localtime(apr_exploded_time_t *tm,
+                                                     apr_time_t t);
+
+/**
+ * convert a recent time to its human readable components in GMT timezone
+ * @param tm the exploded time
+ * @param t the time to explode: MUST be within the last
+ *          AP_TIME_RECENT_THRESHOLD seconds
+ * @note This is a faster alternative to apr_explode_gmt that uses
+ *       a cache of pre-exploded time structures.  It is useful for things
+ *       that need to explode the current time multiple times per second,
+ *       like loggers.
+ * @return APR_SUCCESS iff successful
+ * @deffunc apr_status_t ap_explode_recent_gmt(apr_exploded_time_t *tm, apr_time_t t);
+ */
+AP_DECLARE(apr_status_t) ap_explode_recent_gmt(apr_exploded_time_t *tm,
+                                               apr_time_t t);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* !APACHE_UTIL_TIME_H */
diff --git a/modules/experimental/cache_storage.c b/modules/experimental/cache_storage.c
new file mode 100644 (file)
index 0000000..050628d
--- /dev/null
@@ -0,0 +1,276 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * 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 "mod_cache.h"
+
+APR_HOOK_STRUCT(
+       APR_HOOK_LINK(remove_url)
+       APR_HOOK_LINK(create_entity)
+       APR_HOOK_LINK(open_entity)
+)
+
+module AP_MODULE_DECLARE_DATA tcache_module;
+
+/* -------------------------------------------------------------- */
+
+/*
+ * delete all URL entities from the cache
+ *
+ */
+int cache_remove_url(request_rec *r, const char *types, char *url)
+{
+    const char *next = types;
+    const char *type;
+
+    /* for each specified cache type, delete the URL */
+    while ((type = ap_cache_tokstr(r->pool, next, &next))) {
+       cache_run_remove_url(type, url);
+    }
+    return OK;
+}
+
+
+/*
+ * create a new URL entity in the cache
+ *
+ * It is possible to store more than once entity per URL. This
+ * function will always create a new entity, regardless of whether
+ * other entities already exist for the same URL.
+ *
+ * The size of the entity is provided so that a cache module can
+ * decide whether or not it wants to cache this particular entity.
+ * If the size is unknown, a size of -1 should be set.
+ */
+int cache_create_entity(request_rec *r, const char *types, char *url, apr_size_t size)
+{
+    cache_handle *h;
+    const char *next = types;
+    const char *type;
+    apr_status_t rv;
+    cache_request_rec *cache = (cache_request_rec *) ap_get_module_config(r->request_config, 
+                                                                          &tcache_module);
+
+    /* for each specified cache type, delete the URL */
+    while (next) {
+        type = ap_cache_tokstr(r->pool, next, &next);
+        switch (rv = cache_run_create_entity(&h, type, url, size)) {
+        case OK: {
+            cache->handle = h;
+            return OK;
+        }
+        case DECLINED: {
+            continue;
+        }
+        default: {
+            return rv;
+        }
+        }
+    }
+    return DECLINED;
+}
+
+/*
+ * remove a specific URL entity from the cache
+ *
+ * The specific entity referenced by the cache_handle is removed
+ * from the cache, and the cache_handle is closed.
+ */
+int cache_remove_entity(request_rec *r, const char *types, cache_handle *h)
+{
+    const char *next = types;
+    const char *type;
+
+    while (next) {
+        type = ap_cache_tokstr(r->pool, next, &next);
+    }
+    return 1;
+}
+
+/*
+ * select a specific URL entity in the cache
+ *
+ * It is possible to store more than one entity per URL. Content
+ * negotiation is used to select an entity. Once an entity is
+ * selected, details of it are stored in the per request
+ * config to save time when serving the request later.
+ *
+ * This function returns OK if successful, DECLINED if no
+ * cached entity fits the bill.
+ */
+int cache_select_url(request_rec *r, const char *types, char *url)
+{
+    cache_handle *h;
+    const char *next = types;
+    const char *type;
+    apr_status_t rv;
+    cache_request_rec *cache = (cache_request_rec *) ap_get_module_config(r->request_config, 
+                                                                          &tcache_module);
+
+    /* go through the cache types till we get a match */
+    while (next) {
+        type = ap_cache_tokstr(r->pool, next, &next);
+        switch ((rv = cache_run_open_entity(&h, type, url))) {
+        case OK: {
+            /* cool bananas! */
+            cache->handle = h;
+/*** loop through returned entities */
+/*** do freshness calculation here */
+            cache->fresh = 1;
+/*** do content negotiation here */
+            return OK;
+        }
+        case DECLINED: {
+            /* try again with next cache type */
+            continue;
+        }
+        default: {
+            /* oo-er! an error */
+            return rv;
+        }
+        }
+    }
+    return DECLINED;
+}
+
+apr_status_t cache_write_entity_headers(cache_handle *h, request_rec *r, cache_info *info,
+                                        apr_table_t *headers_in, apr_table_t *headers_out) 
+{
+    const char *ct;
+
+    ct = ap_table_get(r->headers_out, "Content-Type");
+    info->content_type = ct;
+    h->write_headers(h, r, info);
+    return APR_SUCCESS;
+}
+apr_status_t cache_write_entity_body(cache_handle *h, apr_bucket_brigade *b) 
+{
+    apr_status_t rv = APR_SUCCESS;
+    if (h->write_body(h, b) != OK) {
+    }
+    return rv;
+}
+
+apr_status_t cache_read_entity_headers(cache_handle *h, request_rec *r, 
+                                       apr_table_t **headers)
+{
+    cache_info *info;
+
+    /* Be careful to not modify info. */
+    h->read_headers(h, r, &info);
+
+    /* Build the header table from info in the info struct */
+    *headers = apr_table_make(r->pool, 15);
+    /* Content-Length */
+    if (info->len)
+        apr_table_set(*headers, "Content-Length", apr_psprintf(r->pool, "%lu", info->len));
+
+    /* Last-Modified */
+    if (info->lastmod) {
+    }
+    /* Expires */
+    if (info->expire) {
+    }
+    if (info->content_type) {
+        r->content_type = apr_pstrdup(r->pool, info->content_type);
+    }
+    /* Date */
+    
+    return APR_SUCCESS;
+}
+apr_status_t cache_read_entity_body(cache_handle *h, apr_bucket_brigade *b) 
+{
+    h->read_body(h, b);
+    return APR_SUCCESS;
+}
+
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, create_entity, 
+                                      (cache_handle **hp, const char *type, 
+                                      char *url, apr_size_t len),(hp,type,url,len),DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, open_entity,  
+                                      (cache_handle **hp, const char *type, 
+                                      char *url),(hp,type,url),DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(cache, CACHE, int, remove_url, 
+                                    (const char *type, char *url),(type,url),OK,DECLINED)
+#if 0
+/* BillS doesn't think these should be hooks.
+ * All functions which accept a cache_handle * argument should use
+ * function pointers in the cache_handle. Leave them here for now as 
+ * points for discussion...
+ */
+
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, remove_entity, 
+                                     (cache_handle *h),(h),DECLINED)
+
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, read_entity_headers, 
+                                     (cache_handle *h, request_rec *r,
+                                      apr_table_t **headers),
+                                      (h,info,headers_in,headers_out),DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, read_entity_body, 
+                                     (cache_handle *h,
+                                      apr_bucket_brigade *out),(h,out),DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, write_entity_headers, 
+                                     (cache_handle *h, cache_info *info,
+                                      apr_table_t *headers_in, 
+                                      apr_table_t *headers_out),
+                                      (h,info,headers_in,headers_out),DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, write_entity_body, 
+                                     (cache_handle *h,
+                                      apr_bucket_brigade *in),(h,in),DECLINED)
+#endif
diff --git a/modules/experimental/cache_util.c b/modules/experimental/cache_util.c
new file mode 100644 (file)
index 0000000..cc37aa9
--- /dev/null
@@ -0,0 +1,206 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * 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 "mod_cache.h"
+
+
+
+/* -------------------------------------------------------------- */
+
+/* return true if the request is conditional */
+int ap_cache_request_is_conditional(request_rec *r)
+{
+    if (apr_table_get(r->headers_in, "If-Match") ||
+        apr_table_get(r->headers_in, "If-None-Match") ||
+        apr_table_get(r->headers_in, "If-Modified-Since") ||
+        apr_table_get(r->headers_in, "If-Unmodified-Since")) {
+
+       return 1;
+    }
+    return 0;
+}
+
+
+/* remove other filters from filter stack */
+void ap_cache_reset_output_filters(request_rec *r)
+{
+    ap_filter_t *f = r->output_filters;
+
+    while (f) {
+        if (!strcasecmp(f->frec->name, "CORE") ||
+            !strcasecmp(f->frec->name, "CONTENT_LENGTH") ||
+            !strcasecmp(f->frec->name, "HTTP_HEADER")) {
+            f = f->next;
+            continue;
+        }
+        else {
+            ap_remove_output_filter(f);
+            f = f->next;
+        }
+    }
+}
+
+const char *ap_cache_get_cachetype(request_rec *r, cache_server_conf *conf, const char *url)
+{
+    const char *type = NULL;
+    int i;
+
+    /* loop through all the cacheenable entries */
+    for (i = 0; i < conf->cacheenable->nelts; i++) {
+       struct cache_enable *ent = (struct cache_enable *) conf->cacheenable->elts;
+       const char *thisurl = ent[i].url;
+       const char *thistype = ent[i].type;
+       if ((thisurl) && !strncasecmp(thisurl, url, strlen(thisurl))) {
+           if (!type) {
+               type = thistype;
+           }
+           else {
+               type = apr_pstrcat(r->pool, type, ",", thistype, NULL);
+           }
+       }
+    }
+
+    /* then loop through all the cachedisable entries */
+    for (i = 0; i < conf->cachedisable->nelts; i++) {
+       struct cache_disable *ent = (struct cache_disable *) conf->cachedisable->elts;
+       const char *thisurl = ent[i].url;
+       if ((thisurl) && !strncasecmp(thisurl, url, strlen(thisurl))) {
+           type = NULL;
+       }
+    }
+
+    return type;
+}
+
+/*
+ * 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_cache_liststr(const char *list, const char *key, char **val)
+{
+    int len, i;
+    char *p;
+    char valbuf[HUGE_STRING_LEN];
+    valbuf[sizeof(valbuf)-1] = 0; /* safety terminating zero */
+
+    len = strlen(key);
+
+    while (list != NULL) {
+       p = strchr((char *) 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, key, len) == 0) {
+           if (val) {
+               p = strchr((char *) list, ',');
+               while (ap_isspace(*list)) {
+                   list++;
+               }
+               if ('=' == list[0])
+                   list++;
+               while (ap_isspace(*list)) {
+                   list++;
+               }
+               strncpy(valbuf, list, MIN(p-list, sizeof(valbuf)-1));
+               *val = valbuf;
+           }
+           return 1;
+       }
+       list = p;
+    }
+    return 0;
+}
+
+/* return each comma separated token, one at a time */
+const char *ap_cache_tokstr(apr_pool_t *p, const char *list, const char **str)
+{
+    apr_off_t len, i;
+    const char *s;
+
+    s = ap_strchr_c(list, ',');
+    if (s != NULL) {
+       i = s - list;
+       do
+           s++;
+       while (apr_isspace(*s));
+    }
+    else
+       i = strlen(list);
+
+    while (i > 0 && apr_isspace(list[i - 1]))
+       i--;
+
+    *str = s;
+    if (len)
+       return apr_pstrndup(p, list, len);
+    else
+       return NULL;
+
+}
diff --git a/modules/experimental/mod_auth_ldap.c b/modules/experimental/mod_auth_ldap.c
new file mode 100644 (file)
index 0000000..6be891c
--- /dev/null
@@ -0,0 +1,862 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ */
+
+/*
+ * mod_auth_ldap.c: LDAP authentication module
+ * 
+ * Original code from auth_ldap module for Apache v1.3:
+ * Copyright 1998, 1999 Enbridge Pipelines Inc. 
+ * Copyright 1999-2001 Dave Carrigan
+ */
+
+#include <apr_ldap.h>
+#include <apr_strings.h>
+
+#include "ap_config.h"
+#if APR_HAVE_UNISTD_H
+/* for getpid() */
+#include <unistd.h>
+#endif
+#include <ctype.h>
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "util_ldap.h"
+
+/* per directory configuration */
+typedef struct {
+    apr_pool_t *pool;                  /* Pool that this config is allocated from */
+    apr_lock_t *lock;                  /* Lock for this config */
+    int auth_authoritative;            /* Is this auth method the one and only? */
+    int enabled;                       /* Is auth_ldap enabled in this directory? */
+
+    /* These parameters are all derived from the AuthLDAPURL directive */
+    char *url;                         /* String representation of the URL */
+
+    char *host;                                /* Name of the LDAP server (or space separated list) */
+    int port;                          /* Port of the LDAP server */
+    char *basedn;                      /* Base DN to do all searches from */
+    char *attribute;                   /* Attribute to search for */
+    char **attributes;                 /* Array of all the attributes to return */
+    int scope;                         /* Scope of the search */
+    char *filter;                      /* Filter to further limit the search  */
+    deref_options deref;               /* how to handle alias dereferening */
+    char *binddn;                      /* DN to bind to server (can be NULL) */
+    char *bindpw;                      /* Password to bind to server (can be NULL) */
+
+    int frontpage_hack;                        /* Hack for frontpage support */
+    int user_is_dn;                    /* If true, connection->user is DN instead of userid */
+    int compare_dn_on_server;          /* If true, will use server to do DN compare */
+
+    int have_ldap_url;                 /* Set if we have found an LDAP url */
+    apr_array_header_t *groupattr;     /* List of Group attributes */
+    int group_attrib_is_dn;            /* If true, the group attribute is the DN, otherwise, 
+                                          it's the exact string passed by the HTTP client */
+
+    int netscapessl;                   /* True if Netscape SSL is enabled */
+    int starttls;                       /* True if StartTLS is enabled */
+} mod_auth_ldap_config_t;
+
+typedef struct mod_auth_ldap_request_t {
+    char *dn;                          /* The saved dn from a successful search */
+    char *user;                                /* The username provided by the client */
+} mod_auth_ldap_request_t;
+
+/* maximum group elements supported */
+#define GROUPATTR_MAX_ELTS 10
+
+struct mod_auth_ldap_groupattr_entry_t {
+    char *name;
+};
+
+module AP_MODULE_DECLARE_DATA auth_ldap_module;
+
+/* function prototypes */
+void mod_auth_ldap_build_filter(char *filtbuf, 
+                                request_rec *r, 
+                                mod_auth_ldap_config_t *sec);
+int mod_auth_ldap_check_user_id(request_rec *r);
+int mod_auth_ldap_auth_checker(request_rec *r);
+void *mod_auth_ldap_create_dir_config(apr_pool_t *p, char *d);
+
+/* ---------------------------------------- */
+
+
+/*
+ * Build the search filter, or at least as much of the search filter that
+ * will fit in the buffer. We don't worry about the buffer not being able
+ * to hold the entire filter. If the buffer wasn't big enough to hold the
+ * filter, ldap_search_s will complain, but the only situation where this
+ * is likely to happen is if the client sent a really, really long
+ * username, most likely as part of an attack.
+ *
+ * The search filter consists of the filter provided with the URL,
+ * combined with a filter made up of the attribute provided with the URL,
+ * and the actual username passed by the HTTP client. For example, assume
+ * that the LDAP URL is
+ * 
+ *   ldap://ldap.airius.com/ou=People, o=Airius?uid??(posixid=*)
+ *
+ * Further, assume that the userid passed by the client was `userj'.  The
+ * search filter will be (&(posixid=*)(uid=userj)).
+ */
+#define FILTER_LENGTH MAX_STRING_LEN
+void mod_auth_ldap_build_filter(char *filtbuf, 
+                                request_rec *r, 
+                                mod_auth_ldap_config_t *sec)
+{
+    char *p, *q, *filtbuf_end;
+    /* 
+     * Create the first part of the filter, which consists of the 
+     * config-supplied portions.
+     */
+    apr_snprintf(filtbuf, FILTER_LENGTH, "(&(%s)(%s=", sec->filter, sec->attribute);
+
+    /* 
+     * Now add the client-supplied username to the filter, ensuring that any
+     * LDAP filter metachars are escaped.
+     */
+    filtbuf_end = filtbuf + FILTER_LENGTH - 1;
+    for (p = r->user, q=filtbuf + strlen(filtbuf);
+         *p && q < filtbuf_end; *q++ = *p++) {
+        if (strchr("*()\\", *p) != NULL) {
+            *q++ = '\\';
+            if (q >= filtbuf_end) {
+               break;
+           }
+        }
+    }
+    *q = '\0';
+
+    /* 
+     * Append the closing parens of the filter, unless doing so would 
+     * overrun the buffer.
+     */
+    if (q + 2 <= filtbuf_end)
+        strcat(filtbuf, "))");
+}
+
+
+/*
+ * Authentication Phase
+ * --------------------
+ *
+ * This phase authenticates the credentials the user has sent with
+ * the request (ie the username and password are checked). This is done
+ * by making an attempt to bind to the LDAP server using this user's
+ * DN and the supplied password.
+ *
+ */
+int mod_auth_ldap_check_user_id(request_rec *r)
+{
+    const char **vals = NULL;
+    char filtbuf[FILTER_LENGTH];
+    mod_auth_ldap_config_t *sec =
+        (mod_auth_ldap_config_t *)ap_get_module_config(r->per_dir_config, &auth_ldap_module);
+
+    util_ldap_connection_t *ldc = NULL;
+    const char *sent_pw;
+    int result = 0;
+    const char *dn = NULL;
+
+    mod_auth_ldap_request_t *req =
+        (mod_auth_ldap_request_t *)apr_pcalloc(r->pool, sizeof(mod_auth_ldap_request_t));
+    ap_set_module_config(r->request_config, &auth_ldap_module, req);
+
+    if (!sec->enabled) {
+        return DECLINED;
+    }
+
+    /* 
+     * Basic sanity checks before any LDAP operations even happen.
+     */
+    if (!sec->have_ldap_url) {
+        return DECLINED;
+    }
+
+    /* There is a good AuthLDAPURL, right? */
+    if (sec->host) {
+        ldc = util_ldap_connection_find(r, sec->host, sec->port,
+                                       sec->binddn, sec->bindpw, sec->deref,
+                                       sec->netscapessl, sec->starttls);
+    }
+    else {
+        ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r, 
+                      "[%d] auth_ldap authenticate: no sec->host - weird...?", getpid());
+        return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+    }
+
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+                 "[%d] auth_ldap authenticate: using URL %s", getpid(), sec->url);
+
+    /* Get the password that the client sent */
+    if ((result = ap_get_basic_auth_pw(r, &sent_pw))) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+                     "[%d] auth_ldap authenticate: "
+                     "ap_get_basic_auth_pw() returns %d", getpid(), result);
+        util_ldap_connection_close(ldc);
+        return result;
+    }
+
+    /* build the username filter */
+    mod_auth_ldap_build_filter(filtbuf, r, sec);
+
+    /* do the user search */
+    result = util_ldap_cache_checkuserid(r, ldc, sec->url, sec->basedn, sec->scope,
+                                         sec->attributes, filtbuf, sent_pw, &dn, &vals);
+    util_ldap_connection_close(ldc);
+
+    if (result != LDAP_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r, 
+                      "[%d] auth_ldap authenticate: "
+                      "user %s authentication failed; URI %s [%s][%s]",
+                     getpid(), r->user, r->uri, ldc->reason, ldap_err2string(result));
+        if (LDAP_INVALID_CREDENTIALS == result) {
+            ap_note_basic_auth_failure(r);
+            return HTTP_UNAUTHORIZED;
+        }
+        else {
+            return sec->auth_authoritative? HTTP_UNAUTHORIZED: DECLINED;
+        }
+    }
+
+    /* mark the user and DN */
+    req->dn = apr_pstrdup(r->pool, dn);
+    req->user = r->user;
+    if (sec->user_is_dn) {
+        r->user = req->dn;
+    }
+
+    /* add environment variables */
+    if (sec->attributes && vals) {
+        apr_table_t *e = r->subprocess_env;
+        int i = 0;
+        while (sec->attributes[i]) {
+            char *str = apr_pstrcat(r->pool, "AUTHENTICATE_", sec->attributes[i], NULL);
+            int j = 13;
+            while (str[j]) {
+                if (str[j] >= 'a' && str[j] <= 'z') {
+                    str[j] = str[j] - ('a' - 'A');
+                }
+                j++;
+            }
+            apr_table_setn(e, str, vals[i]);
+            i++;
+        }
+    }
+
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                 "[%d] auth_ldap authenticate: accepting %s", getpid(), r->user);
+
+    return OK;
+}
+
+
+/*
+ * Authorisation Phase
+ * -------------------
+ *
+ * After checking whether the username and password are correct, we need
+ * to check whether that user is authorised to view this resource. The
+ * require directive is used to do this:
+ *
+ *  require valid-user         Any authenticated is allowed in.
+ *  require user <username>    This particular user is allowed in.
+ *  require group <groupname>  The user must be a member of this group
+ *                              in order to be allowed in.
+ *  require dn <dn>             The user must have the following DN in the
+ *                              LDAP tree to be let in.
+ *
+ */
+int mod_auth_ldap_auth_checker(request_rec *r)
+{
+    int result = 0;
+    mod_auth_ldap_request_t *req =
+        (mod_auth_ldap_request_t *)ap_get_module_config(r->request_config,
+        &auth_ldap_module);
+    mod_auth_ldap_config_t *sec =
+        (mod_auth_ldap_config_t *)ap_get_module_config(r->per_dir_config, 
+        &auth_ldap_module);
+
+    util_ldap_connection_t *ldc = NULL;
+    int m = r->method_number;
+
+    const apr_array_header_t *reqs_arr = ap_requires(r);
+    require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL;
+
+    register int x;
+    const char *t;
+    char *w;
+    int method_restricted = 0;
+
+    if (!sec->enabled) {
+        return DECLINED;
+    }
+
+    if (!sec->have_ldap_url) {
+        return DECLINED;
+    }
+
+    if (sec->host) {
+        ldc = util_ldap_connection_find(r, sec->host, sec->port,
+                                       sec->binddn, sec->bindpw, sec->deref,
+                                       sec->netscapessl, sec->starttls);
+    }
+    else {
+        ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r, 
+                      "[%d] auth_ldap authorise: no sec->host - weird...?", getpid());
+        return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+    }
+
+    /* 
+     * If there are no elements in the group attribute array, the default should be
+     * member and uniquemember; populate the array now.
+     */
+    if (sec->groupattr->nelts == 0) {
+        struct mod_auth_ldap_groupattr_entry_t *grp;
+        apr_lock_acquire(sec->lock);
+        grp = apr_array_push(sec->groupattr);
+        grp->name = "member";
+        grp = apr_array_push(sec->groupattr);
+        grp->name = "uniquemember";
+        apr_lock_release(sec->lock);
+    }
+
+    if (!reqs_arr) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+                     "[%d] auth_ldap authorise: no requirements array", getpid());
+        return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+    }
+
+    /* Loop through the requirements array until there's no elements
+     * left, or something causes a return from inside the loop */
+    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(r->pool, &t, ' ');
+    
+        if (strcmp(w, "valid-user") == 0) {
+            /*
+             * Valid user will always be true if we authenticated with ldap,
+             * but when using front page, valid user should only be true if
+             * he exists in the frontpage password file. This hack will get
+             * auth_ldap to look up the user in the the pw file to really be
+             * sure that he's valid. Naturally, it requires mod_auth to be
+             * compiled in, but if mod_auth wasn't in there, then the need
+             * for this hack wouldn't exist anyway.
+             */
+            if (sec->frontpage_hack) {
+               ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                             "[%d] auth_ldap authorise: "
+                             "deferring authorisation to mod_auth (FP Hack)", 
+                             getpid());
+                return OK;
+            }
+            else {
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                              "[%d] auth_ldap authorise: "
+                              "successful authorisation because user "
+                              "is valid-user", getpid());
+                return OK;
+            }
+        }
+        else if (strcmp(w, "user") == 0) {
+            if (req->dn == NULL || strlen(req->dn) == 0) {
+               ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+                              "[%d] auth_ldap authorise: "
+                              "require user: user's DN has not been defined; failing authorisation", 
+                              getpid());
+                return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+            }
+            /* 
+             * First do a whole-line compare, in case it's something like
+             *   require user Babs Jensen
+             */
+            result = util_ldap_cache_compare(r, ldc, sec->url, req->dn, sec->attribute, t);
+            switch(result) {
+                case LDAP_COMPARE_TRUE: {
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                                  "[%d] auth_ldap authorise: "
+                                  "require user: authorisation successful", getpid());
+                    return OK;
+                }
+                default: {
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                                  "[%d] auth_ldap authorise: require user: "
+                                  "authorisation failed [%s][%s]", getpid(),
+                                  ldc->reason, ldap_err2string(result));
+                }
+            }
+            /* 
+             * Now break apart the line and compare each word on it 
+             */
+            while (t[0]) {
+               w = ap_getword_conf(r->pool, &t);
+                result = util_ldap_cache_compare(r, ldc, sec->url, req->dn, sec->attribute, w);
+                switch(result) {
+                    case LDAP_COMPARE_TRUE: {
+                        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                                      "[%d] auth_ldap authorise: "
+                                      "require user: authorisation successful", getpid());
+                        return OK;
+                    }
+                    default: {
+                        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                                      "[%d] auth_ldap authorise: "
+                                      "require user: authorisation failed [%s][%s]",
+                                      getpid(), ldc->reason, ldap_err2string(result));
+                    }
+                }
+            }
+        }
+        else if (strcmp(w, "dn") == 0) {
+            if (req->dn == NULL || strlen(req->dn) == 0) {
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+                              "[%d] auth_ldap authorise: "
+                              "require dn: user's DN has not been defined; failing authorisation", 
+                              getpid());
+                return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+            }
+
+            result = util_ldap_cache_comparedn(r, ldc, sec->url, req->dn, t, sec->compare_dn_on_server);
+            switch(result) {
+                case LDAP_COMPARE_TRUE: {
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                                  "[%d] auth_ldap authorise: "
+                                  "require dn: authorisation successful", getpid());
+                    return OK;
+                }
+                default: {
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                                  "[%d] auth_ldap authorise: "
+                                  "require dn: LDAP error [%s][%s]",
+                                  getpid(), ldc->reason, ldap_err2string(result));
+                }
+            }
+        }
+        else if (strcmp(w, "group") == 0) {
+            struct mod_auth_ldap_groupattr_entry_t *ent = (struct mod_auth_ldap_groupattr_entry_t *) sec->groupattr->elts;
+            int i;
+
+            if (sec->group_attrib_is_dn) {
+                if (req->dn == NULL || strlen(req->dn) == 0) {
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+                                  "[%d] auth_ldap authorise: require group: user's DN has not been defined; failing authorisation", 
+                                  getpid());
+                    return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+                }
+            }
+            else {
+                if (req->user == NULL || strlen(req->user) == 0) {
+                   /* We weren't called in the authentication phase, so we didn't have a 
+                     * chance to set the user field. Do so now. */
+                    req->user = r->user;
+                }
+            }
+
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                          "[%d] auth_ldap authorise: require group: testing for group membership in `%s'", 
+                         getpid(), t);
+
+            for (i = 0; i < sec->groupattr->nelts; i++) {
+               ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                              "[%d] auth_ldap authorise: require group: testing for %s: %s (%s)", getpid(),
+                              ent[i].name, sec->group_attrib_is_dn ? req->dn : req->user, t);
+
+                result = util_ldap_cache_compare(r, ldc, sec->url, t, ent[i].name, 
+                                     sec->group_attrib_is_dn ? req->dn : req->user);
+                switch(result) {
+                    case LDAP_COMPARE_TRUE: {
+                        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                                      "[%d] auth_ldap authorise: require group: "
+                                      "authorisation successful (attribute %s) [%s][%s]",
+                                      getpid(), ent[i].name, ldc->reason, ldap_err2string(result));
+                        return OK;
+                    }
+                    default: {
+                        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                                      "[%d] auth_ldap authorise: require group: "
+                                      "authorisation failed [%s][%s]",
+                                      getpid(), ldc->reason, ldap_err2string(result));
+                    }
+                }
+            }
+        }
+    }
+
+    if (!method_restricted) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                      "[%d] auth_ldap authorise: agreeing because non-restricted", 
+                      getpid());
+        return OK;
+    }
+
+    if (!sec->auth_authoritative) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                      "[%d] auth_ldap authorise: declining to authorise", getpid());
+        return DECLINED;
+    }
+
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, 
+                  "[%d] auth_ldap authorise: authorisation denied", getpid());
+    ap_note_basic_auth_failure (r);
+
+    return HTTP_UNAUTHORIZED;
+}
+
+
+/* ---------------------------------------- */
+/* config directives */
+
+
+void *mod_auth_ldap_create_dir_config(apr_pool_t *p, char *d)
+{
+    mod_auth_ldap_config_t *sec = 
+        (mod_auth_ldap_config_t *)apr_pcalloc(p, sizeof(mod_auth_ldap_config_t));
+
+    sec->pool = p;
+    apr_lock_create(&sec->lock, APR_MUTEX, APR_INTRAPROCESS, NULL, p);
+    sec->auth_authoritative = 1;
+    sec->enabled = 1;
+    sec->groupattr = apr_array_make(p, GROUPATTR_MAX_ELTS, 
+                                  sizeof(struct mod_auth_ldap_groupattr_entry_t));
+
+    sec->have_ldap_url = 0;
+    sec->url = "";
+    sec->host = NULL;
+    sec->binddn = NULL;
+    sec->bindpw = NULL;
+    sec->deref = always;
+    sec->group_attrib_is_dn = 1;
+
+    sec->frontpage_hack = 0;
+    sec->netscapessl = 0;
+    sec->starttls = 0;
+
+    sec->user_is_dn = 0;
+    sec->compare_dn_on_server = 0;
+
+    return sec;
+}
+
+/* 
+ * Use the ldap url parsing routines to break up the ldap url into
+ * host and port.
+ */
+static const char *mod_auth_ldap_parse_url(cmd_parms *cmd, 
+                                    void *config,
+                                    const char *url)
+{
+    int result;
+    LDAPURLDesc *urld;
+
+    mod_auth_ldap_config_t *sec = config;
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+                cmd->server, "[%d] auth_ldap url parse: `%s'", 
+                getpid(), url);
+
+    result = ldap_url_parse(url, &(urld));
+    if (result != LDAP_SUCCESS) {
+        switch (result) {
+        case LDAP_URL_ERR_NOTLDAP:
+            return "LDAP URL does not begin with ldap://";
+        case LDAP_URL_ERR_NODN:
+            return "LDAP URL does not have a DN";
+        case LDAP_URL_ERR_BADSCOPE:
+            return "LDAP URL has an invalid scope";
+        case LDAP_URL_ERR_MEM:
+            return "Out of memory parsing LDAP URL";
+        default:
+            return "Could not parse LDAP URL";
+        }
+    }
+    sec->url = apr_pstrdup(cmd->pool, url);
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+                cmd->server, "[%d] auth_ldap url parse: Host: %s", getpid(), urld->lud_host);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+                cmd->server, "[%d] auth_ldap url parse: Port: %d", getpid(), urld->lud_port);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+                cmd->server, "[%d] auth_ldap url parse: DN: %s", getpid(), urld->lud_dn);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+                cmd->server, "[%d] auth_ldap url parse: attrib: %s", getpid(), urld->lud_attrs? urld->lud_attrs[0] : "(null)");
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+                cmd->server, "[%d] auth_ldap url parse: scope: %s", getpid(), 
+                (urld->lud_scope == LDAP_SCOPE_SUBTREE? "subtree" : 
+                urld->lud_scope == LDAP_SCOPE_BASE? "base" : 
+                urld->lud_scope == LDAP_SCOPE_ONELEVEL? "onelevel" : "unknown"));
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+                cmd->server, "[%d] auth_ldap url parse: filter: %s", getpid(), urld->lud_filter);
+
+    /* Set all the values, or at least some sane defaults */
+    if (sec->host) {
+        char *p = apr_palloc(cmd->pool, strlen(sec->host) + strlen(urld->lud_host) + 2);
+        strcpy(p, urld->lud_host);
+        strcat(p, " ");
+        strcat(p, sec->host);
+        sec->host = p;
+    }
+    else {
+        sec->host = urld->lud_host? apr_pstrdup(cmd->pool, urld->lud_host) : "localhost";
+    }
+    sec->basedn = urld->lud_dn? apr_pstrdup(cmd->pool, urld->lud_dn) : "";
+    if (urld->lud_attrs && urld->lud_attrs[0]) {
+        int i = 1;
+        while (urld->lud_attrs[i]) {
+            i++;
+        }
+        sec->attributes = apr_pcalloc(cmd->pool, sizeof(char *) * (i+1));
+        i = 0;
+        while (urld->lud_attrs[i]) {
+            sec->attributes[i] = apr_pstrdup(cmd->pool, urld->lud_attrs[i]);
+            i++;
+        }
+        sec->attribute = sec->attributes[0];
+    }
+    else {
+        sec->attribute = "uid";
+    }
+
+    sec->scope = urld->lud_scope == LDAP_SCOPE_ONELEVEL ?
+        LDAP_SCOPE_ONELEVEL : LDAP_SCOPE_SUBTREE;
+
+    if (urld->lud_filter) {
+        if (urld->lud_filter[0] == '(') {
+            /* 
+            * Get rid of the surrounding parens; later on when generating the
+            * filter, they'll be put back.
+             */
+            sec->filter = apr_pstrdup(cmd->pool, urld->lud_filter+1);
+            sec->filter[strlen(sec->filter)-1] = '\0';
+        }
+        else {
+            sec->filter = apr_pstrdup(cmd->pool, urld->lud_filter);
+        }
+    }
+    else {
+        sec->filter = "objectclass=*";
+    }
+    if (strncmp(url, "ldaps", 5) == 0) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+                    cmd->server, "[%d] auth_ldap parse url: requesting secure LDAP", getpid());
+#ifdef APU_HAS_LDAP_STARTTLS
+        sec->port = urld->lud_port? urld->lud_port : LDAPS_PORT;
+        sec->starttls = 1;
+#else
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+        sec->port = urld->lud_port? urld->lud_port : LDAPS_PORT;
+        sec->netscapessl = 1;
+#else
+        return "Secure LDAP (ldaps://) not supported. Rebuild APR-Util";
+#endif
+#endif
+    }
+    else {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+                    cmd->server, "[%d] auth_ldap parse url: not requesting secure LDAP", getpid());
+        sec->netscapessl = 0;
+        sec->starttls = 0;
+        sec->port = urld->lud_port? urld->lud_port : LDAP_PORT;
+    }
+
+    sec->have_ldap_url = 1;
+    ldap_free_urldesc(urld);
+    return NULL;
+}
+
+static const char *mod_auth_ldap_set_deref(cmd_parms *cmd, void *config, const char *arg)
+{
+    mod_auth_ldap_config_t *sec = config;
+
+    if (strcmp(arg, "never") == 0 || strcasecmp(arg, "off") == 0) {
+        sec->deref = never;
+    }
+    else if (strcmp(arg, "searching") == 0) {
+        sec->deref = searching;
+    }
+    else if (strcmp(arg, "finding") == 0) {
+        sec->deref = finding;
+    }
+    else if (strcmp(arg, "always") == 0 || strcasecmp(arg, "on") == 0) {
+        sec->deref = always;
+    }
+    else {
+        return "Unrecognized value for AuthLDAPAliasDereference directive";
+    }
+    return NULL;
+}
+
+static const char *mod_auth_ldap_add_group_attribute(cmd_parms *cmd, void *config, const char *arg)
+{
+    struct mod_auth_ldap_groupattr_entry_t *new;
+
+    mod_auth_ldap_config_t *sec = config;
+
+    if (sec->groupattr->nelts > GROUPATTR_MAX_ELTS)
+        return "Too many AuthLDAPGroupAttribute directives";
+
+    new = apr_array_push(sec->groupattr);
+    new->name = apr_pstrdup(cmd->pool, arg);
+  
+    return NULL;
+}
+
+command_rec mod_auth_ldap_cmds[] = {
+    AP_INIT_TAKE1("AuthLDAPURL", mod_auth_ldap_parse_url, NULL, OR_AUTHCFG, 
+                  "URL to define LDAP connection. This should be an RFC 2255 complaint\n"
+                  "URL of the form ldap://host[:port]/basedn[?attrib[?scope[?filter]]].\n"
+                  "<ul>\n"
+                  "<li>Host is the name of the LDAP server. Use a space separated list of hosts \n"
+                  "to specify redundant servers.\n"
+                  "<li>Port is optional, and specifies the port to connect to.\n"
+                  "<li>basedn specifies the base DN to start searches from\n"
+                  "<li>Attrib specifies what attribute to search for in the directory. If not "
+                  "provided, it defaults to <b>uid</b>.\n"
+                  "<li>Scope is the scope of the search, and can be either <b>sub</b> or "
+                  "<b>one</b>. If not provided, the default is <b>sub</b>.\n"
+                  "<li>Filter is a filter to use in the search. If not provided, "
+                  "defaults to <b>(objectClass=*)</b>.\n"
+                  "</ul>\n"
+                  "Searches are performed using the attribute and the filter combined. "
+                  "For example, assume that the\n"
+                  "LDAP URL is <b>ldap://ldap.airius.com/ou=People, o=Airius?uid?sub?(posixid=*)</b>. "
+                  "Searches will\n"
+                  "be done using the filter <b>(&((posixid=*))(uid=<i>username</i>))</b>, "
+                  "where <i>username</i>\n"
+                  "is the user name passed by the HTTP client. The search will be a subtree "
+                  "search on the branch <b>ou=People, o=Airius</b>."),
+
+    AP_INIT_TAKE1("AuthLDAPBindDN", ap_set_string_slot,
+                  (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, binddn), OR_AUTHCFG,
+                  "DN to use to bind to LDAP server. If not provided, will do an anonymous bind."),
+
+    AP_INIT_TAKE1("AuthLDAPBindPassword", ap_set_string_slot,
+                  (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, bindpw), OR_AUTHCFG,
+                  "Password to use to bind to LDAP server. If not provided, will do an anonymous bind."),
+
+    AP_INIT_FLAG("AuthLDAPRemoteUserIsDN", ap_set_flag_slot,
+                 (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, user_is_dn), OR_AUTHCFG,
+                 "Set to 'on' to set the REMOTE_USER environment variable to be the full "
+                 "DN of the remote user. By default, this is set to off, meaning that "
+                 "the REMOTE_USER variable will contain whatever value the remote user sent."),
+
+    AP_INIT_FLAG("AuthLDAPAuthoritative", ap_set_flag_slot,
+                 (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, auth_authoritative), OR_AUTHCFG,
+                 "Set to 'off' to allow access control to be passed along to lower modules if "
+                 "the UserID and/or group is not known to this module"),
+
+    AP_INIT_FLAG("AuthLDAPCompareDNOnServer", ap_set_flag_slot,
+                 (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, compare_dn_on_server), OR_AUTHCFG,
+                 "Set to 'on' to force auth_ldap to do DN compares (for the \"require dn\" "
+                 "directive) using the server, and set it 'off' to do the compares locally "
+                 "(at the expense of possible false matches). See the documentation for "
+                 "a complete description of this option."),
+
+    AP_INIT_ITERATE("AuthLDAPGroupAttribute", mod_auth_ldap_add_group_attribute, NULL, OR_AUTHCFG,
+                    "A list of attributes used to define group membership - defaults to "
+                    "member and uniquemember"),
+
+    AP_INIT_FLAG("AuthLDAPGroupAttributeIsDN", ap_set_flag_slot,
+                 (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, group_attrib_is_dn), OR_AUTHCFG,
+                 "If set to 'on', auth_ldap uses the DN that is retrieved from the server for"
+                 "subsequent group comparisons. If set to 'off', auth_ldap uses the string"
+                 "provided by the client directly. Defaults to 'on'."),
+
+    AP_INIT_TAKE1("AuthLDAPDereferenceAliases", mod_auth_ldap_set_deref, NULL, OR_AUTHCFG,
+                  "Determines how aliases are handled during a search. Can bo one of the"
+                  "values \"never\", \"searching\", \"finding\", or \"always\". "
+                  "Defaults to always."),
+
+    AP_INIT_FLAG("AuthLDAPEnabled", ap_set_flag_slot,
+                 (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, enabled), OR_AUTHCFG,
+                 "Set to off to disable auth_ldap, even if it's been enabled in a higher tree"),
+    AP_INIT_FLAG("AuthLDAPFrontPageHack", ap_set_flag_slot,
+                 (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, frontpage_hack), OR_AUTHCFG,
+                 "Set to 'on' to support Microsoft FrontPage"),
+
+#ifdef APU_HAS_LDAP_STARTTLS
+    AP_INIT_FLAG("AuthLDAPStartTLS", ap_set_flag_slot,
+                 (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, starttls), OR_AUTHCFG,
+                 "Set to 'on' to start TLS after connecting to the LDAP server."),
+#endif /* APU_HAS_LDAP_STARTTLS */
+
+    {NULL}
+};
+
+static void mod_auth_ldap_register_hooks(apr_pool_t *p)
+{
+    ap_hook_check_user_id(mod_auth_ldap_check_user_id, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_auth_checker(mod_auth_ldap_auth_checker, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module auth_ldap_module = {
+   STANDARD20_MODULE_STUFF,
+   mod_auth_ldap_create_dir_config,    /* dir config creater */
+   NULL,                               /* dir merger --- default is to override */
+   NULL,                               /* server config */
+   NULL,                               /* merge server config */
+   mod_auth_ldap_cmds,                 /* command table */
+   mod_auth_ldap_register_hooks,       /* set up request processing hooks */
+};
diff --git a/modules/experimental/mod_cache.dsp b/modules/experimental/mod_cache.dsp
new file mode 100644 (file)
index 0000000..f62455d
--- /dev/null
@@ -0,0 +1,123 @@
+# Microsoft Developer Studio Project File - Name="mod_cache" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_cache - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_cache.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_cache.mak" CFG="mod_cache - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_cache - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_cache - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_cache - 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 /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_CACHE_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /I "../../os/win32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CACHE_DECLARE_EXPORT" /D "MOD_CACHE_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /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 /dll /machine:I386 /out:"Release/mod_cache.so"
+
+!ELSEIF  "$(CFG)" == "mod_cache - 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 /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_CACHE_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /I "../../os/win32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CACHE_DECLARE_EXPORT" /D "MOD_CACHE_EXPORTS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /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 /dll /debug /machine:I386 /out:"Debug/mod_cache.so" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_cache - Win32 Release"
+# Name "mod_cache - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\cache_storage.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\cache_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_cache.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_mem_cache.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\mod_cache.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/modules/experimental/mod_case_filter_in.c b/modules/experimental/mod_case_filter_in.c
new file mode 100644 (file)
index 0000000..e4ab97f
--- /dev/null
@@ -0,0 +1,131 @@
+// Ben messing around some more...
+
+#include "httpd.h"
+#include "http_config.h"
+#include "apr_general.h"
+#include "util_filter.h"
+#include "apr_buckets.h"
+#include "http_request.h"
+
+#include <ctype.h>
+
+static const char s_szCaseFilterName[]="CaseFilter";
+module case_filter_in_module;
+
+typedef struct
+{
+    int bEnabled;
+} CaseFilterInConfig;
+
+typedef struct
+{
+    apr_bucket_brigade *pbbTmp;
+} CaseFilterInContext;
+
+static void *CaseFilterInCreateServerConfig(apr_pool_t *p,server_rec *s)
+{
+    CaseFilterInConfig *pConfig=apr_pcalloc(p,sizeof *pConfig);
+
+    pConfig->bEnabled=0;
+
+    return pConfig;
+}
+
+static void CaseFilterInInsertFilter(request_rec *r)
+{
+    CaseFilterInConfig *pConfig=ap_get_module_config(r->server->module_config,
+                                                    &case_filter_in_module);
+    CaseFilterInContext *pCtx;
+
+    if(!pConfig->bEnabled)
+       return;
+
+    pCtx=apr_palloc(r->pool,sizeof *pCtx);
+    pCtx->pbbTmp=apr_brigade_create(r->pool);
+    ap_add_input_filter(s_szCaseFilterName,pCtx,r,NULL);
+}
+
+static apr_status_t CaseFilterInFilter(ap_filter_t *f,
+                                      apr_bucket_brigade *pbbOut,
+                                      ap_input_mode_t eMode,apr_size_t nBytes)
+{
+    CaseFilterInContext *pCtx=f->ctx;
+    apr_status_t ret;
+
+    ap_assert(APR_BRIGADE_EMPTY(pCtx->pbbTmp));
+    
+    ret=ap_get_brigade(f->next,pCtx->pbbTmp,eMode,nBytes);
+    if(eMode == AP_MODE_PEEK || ret != APR_SUCCESS)
+       return ret;
+
+    while(!APR_BRIGADE_EMPTY(pCtx->pbbTmp)) {
+       apr_bucket *pbktIn=APR_BRIGADE_FIRST(pCtx->pbbTmp);
+       apr_bucket *pbktOut;
+       const char *data;
+       apr_size_t len;
+       char *buf;
+       int n;
+
+       // It is tempting to do this...
+       //APR_BUCKET_REMOVE(pB);
+       //APR_BRIGADE_INSERT_TAIL(pbbOut,pB);
+       // and change the case of the bucket data, but that would be wrong
+       // for a file or socket buffer, for example...
+
+       if(APR_BUCKET_IS_EOS(pbktIn)) {
+           APR_BUCKET_REMOVE(pbktIn);
+           APR_BRIGADE_INSERT_TAIL(pbbOut,pbktIn);
+           break;
+       }
+
+       ret=apr_bucket_read(pbktIn,&data,&len,eMode);
+       if(ret != APR_SUCCESS)
+           return ret;
+
+       buf=malloc(len);
+       for(n=0 ; n < len ; ++n)
+           buf[n]=toupper(data[n]);
+
+       pbktOut=apr_bucket_heap_create(buf,len,0,NULL);
+       APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut);
+       apr_bucket_delete(pbktIn);
+    }
+
+    return APR_SUCCESS;
+}
+           
+       
+static const char *CaseFilterEnable(cmd_parms *cmd, void *dummy, int arg)
+    {
+    CaseFilterInConfig *pConfig
+      =ap_get_module_config(cmd->server->module_config,&case_filter_in_module);
+    pConfig->bEnabled=arg;
+
+    return NULL;
+    }
+
+static const command_rec CaseFilterInCmds[] = 
+    {
+    AP_INIT_FLAG("CaseFilterIn", CaseFilterEnable, NULL, RSRC_CONF,
+                 "Run an input case filter on this host"),
+    { NULL }
+    };
+
+
+static void CaseFilterInRegisterHooks(apr_pool_t *p)
+    {
+    ap_hook_insert_filter(CaseFilterInInsertFilter,NULL,NULL,APR_HOOK_MIDDLE);
+    ap_register_input_filter(s_szCaseFilterName,CaseFilterInFilter,
+                             AP_FTYPE_CONTENT);
+    }
+
+module case_filter_in_module =
+{
+    STANDARD20_MODULE_STUFF,
+    NULL,
+    NULL,
+    CaseFilterInCreateServerConfig,
+    NULL,
+    CaseFilterInCmds,
+    CaseFilterInRegisterHooks
+};
diff --git a/modules/experimental/mod_mem_cache.c b/modules/experimental/mod_mem_cache.c
new file mode 100644 (file)
index 0000000..2d2781a
--- /dev/null
@@ -0,0 +1,509 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * 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 "mod_cache.h"
+#define MAX_CACHE 5000
+module AP_MODULE_DECLARE_DATA mem_cache_module;
+
+/* 
+ * XXX
+ * This cache uses apr_hash functions which leak storage when something is removed
+ * from the cache. This can be fixed in the apr_hash functions by making them use
+ * malloc/free rather than pools to manage their storage requirements.
+ */
+
+/*
+ * XXX Introduce a type field that identifies whether the cache obj
+ * references malloc'ed or mmap storage or a file descriptor
+ */
+typedef enum {
+    CACHE_TYPE_FILE = 1,
+    CACHE_TYPE_MALLOC,
+    CACHE_TYPE_MMAP
+} cache_type_e;
+
+typedef struct {
+    cache_type_e type;
+    char *key;
+    void *m;
+    apr_size_t m_len;
+    cache_info info;
+    int complete;
+} cache_object_t;
+
+typedef struct {
+    apr_lock_t *lock;
+    apr_hash_t *cacheht;
+    int space;
+    apr_time_t maxexpire;
+    apr_time_t defaultexpire;
+} mem_cache_conf;
+static mem_cache_conf *sconf;
+
+#define DEFAULT_CACHE_SPACE 100*1024
+#define CACHEFILE_LEN 20
+
+/* Forward declarations */
+static int remove_entity(cache_handle *h);
+static int write_headers(cache_handle *h, request_rec *r, cache_info *i);
+static int write_body(cache_handle *h, apr_bucket_brigade *b);
+static int read_headers(cache_handle *h, request_rec *r, cache_info **info);
+static int read_body(cache_handle *h, apr_bucket_brigade *bb);
+
+static void cleanup_cache_object(cache_object_t *obj)
+{
+    /* The cache object has been removed from the cache. Now clean
+     * it up, freeing any storage, closing file descriptors, etc.
+     */
+    /* XXX - 
+     * The action of freeing a cache entry is asynchronous with the rest of 
+     * the operation of the cache. Frees can be driven by garbage collection,
+     * the result of some command or an HTTP request.  It is okay to remove 
+     * an entry from the cache at anytime but we need a mechanism to keep 
+     * us from cleaning up the cache entry out from under other threads 
+     * that may still be referencing it.
+     * 
+     * Bill thinks that we need a special purpose reference counted 
+     * bucket (or three).  When an entry is removed from the cache, the
+     * bucket for that entry is marked for cleanup. A bucket marked for 
+     * cleanup is freed by the last routine referencing the bucket,
+     * either during brigade destroy or this routine.
+     */
+
+    /* 
+     * Ref count decrementing and checking needs to be atomic
+
+       obj->ref_count--;
+       if (obj->ref_count) {
+           defer_cleanup (let the brigade cleanup free the bucket)
+       }
+       else {
+           free the bucket
+       }
+    */
+
+    if (obj->info.content_type)
+        free(obj->info.content_type);
+    if (obj->key)
+        free(obj->key);
+    if (obj->m)
+        free(obj->m);
+
+    free(obj);
+}
+
+static apr_status_t cleanup_cache_mem(void *sconfv)
+{
+    cache_object_t *obj;
+    apr_hash_index_t *hi;
+    mem_cache_conf *co = (mem_cache_conf*) sconfv;
+
+    if (!co) {
+        return APR_SUCCESS;
+    }
+
+    /* Iterate over the frag hash table and clean up each entry */
+    /* XXX need to lock the hash */
+    for (hi = apr_hash_first(NULL, co->cacheht); hi; hi=apr_hash_next(hi)) {
+        apr_hash_this(hi, NULL, NULL, (void **)&obj);
+        if (obj)
+            cleanup_cache_object(obj);
+    }
+    return APR_SUCCESS;
+}
+static void *create_cache_config(apr_pool_t *p, server_rec *s)
+{
+    sconf = apr_pcalloc(p, sizeof(mem_cache_conf));
+    sconf->space = DEFAULT_CACHE_SPACE;
+#if 0
+    sconf->maxexpire = DEFAULT_CACHE_MAXEXPIRE;
+    sconf->defaultexpire = DEFAULT_CACHE_EXPIRE;
+#endif
+
+    apr_lock_create(&sconf->lock, APR_MUTEX, APR_INTRAPROCESS, "foo", p);
+    sconf->cacheht = apr_hash_make(p);
+    apr_pool_cleanup_register(p, NULL, cleanup_cache_mem, apr_pool_cleanup_null);
+
+    return sconf;
+}
+
+static int create_entity(cache_handle **hp, const char *type, char *key, apr_size_t len) 
+{
+    apr_status_t rv;
+    cache_object_t *obj;
+    cache_handle *h;
+
+    /* Create the cache handle and begin populating it.
+     */
+    if (strcasecmp(type, "mem")) {
+        return DECLINED;
+    }
+
+    /* Check len to see if it is withing acceptable bounds 
+     * XXX max cache check should be configurable variable.
+     */
+    if (len < 0 || len > MAX_CACHE) {
+        return DECLINED;
+    }
+    /* Check total cache size and number of entries. Are they within the
+     * configured limits? If not, kick off garbage collection thread.
+     */
+
+    /* Allocate the cache_handle and set up call back functions specific to 
+     * this cache handler.
+     */
+    h = malloc(sizeof(cache_handle));
+    *hp = h;
+    if (!h) {
+        /* handle the error */
+        return DECLINED;
+    }
+    h->read_body = &read_body;
+    h->read_headers = &read_headers;
+    h->write_body = &write_body;
+    h->write_headers = &write_headers;
+
+    /* Allocate and initialize the cache object. The cache object is
+     * unique to this implementation.
+     */
+    obj = malloc(sizeof(*obj));
+    if (!obj) {
+        /* Handle ther error */
+        free(h);
+        return DECLINED;
+    }
+
+    obj->key = malloc(strlen(key));
+    if (!obj->key) {
+        /* XXX Uuugh, there has got to be a better way to manage memory.
+         */
+        free(h);
+        free(obj);
+        return DECLINED;
+    }
+    obj->m_len = len;     /* One of these len fields can go */
+    obj->info.len = len;
+    strcpy(obj->key, key);
+    h->cache_obj = (void *) obj;
+    
+    /* Mark the cache object as incomplete and put it into the cache */
+    obj->complete = 0;
+
+    /* XXX Need a way to insert into the cache w/o sch coarse grained locking */
+    apr_lock_acquire(sconf->lock);
+    apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), obj);
+    apr_lock_release(sconf->lock);
+}
+
+static int open_entity(cache_handle **hp, const char *type, char *key) 
+{
+    cache_object_t *obj;
+    cache_handle *h;
+
+    /* Look up entity keyed to 'url' */
+    if (strcasecmp(type, "mem")) {
+        return DECLINED;
+    }
+    apr_lock_acquire(sconf->lock);
+    obj = (cache_object_t *) apr_hash_get(sconf->cacheht, key, APR_HASH_KEY_STRING);
+    apr_lock_release(sconf->lock);
+
+    if (!obj || !(obj->complete)) {
+        return DECLINED;
+    }
+
+    /* Allocate the cache_handle and initialize it */
+    h = malloc(sizeof(cache_handle));
+    *hp = h;
+    if (!h) {
+        /* handle the error */
+        return DECLINED;
+    }
+    h->read_body = &read_body;
+    h->read_headers = &read_headers;
+    h->write_body = &write_body;
+    h->write_headers = &write_headers;
+    h->cache_obj = obj;
+    if (!obj || !(obj->complete)) {
+        return DECLINED;
+    }
+    return OK;
+}
+
+static int remove_entity(cache_handle *h) 
+{
+    cache_object_t *obj = (cache_object_t *) h->cache_obj;
+
+    apr_lock_acquire(sconf->lock);
+    apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
+    apr_lock_release(sconf->lock);
+
+    cleanup_cache_object(obj);
+    
+    /* Reinit the cache_handle fields? */
+    h->cache_obj = NULL;
+
+    /* The caller should free the cache_handle ? */
+    free(h);
+}
+
+/* Define request processing hook handlers */
+static int remove_url(const char *type, char *key) 
+{
+    cache_object_t *obj;
+
+    if (strcasecmp(type, "mem")) {
+        return DECLINED;
+    }
+
+    /* WIBNIF
+     * apr_hash_set(..,..,..,NULL) returned pointer to the object just removed.
+     * That way, we could free the object w/o doing another call to an
+     * apr_hash function.
+     */
+
+    /* First, find the object in the cache */
+    apr_lock_acquire(sconf->lock);
+    obj = (cache_object_t *) apr_hash_get(sconf->cacheht, key, APR_HASH_KEY_STRING);
+    apr_lock_release(sconf->lock);
+
+    if (!obj) {
+        return DECLINED;
+    }
+
+    /* Found it. Now take it out of the cache and free it. */
+    apr_lock_acquire(sconf->lock);
+    apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
+    apr_lock_release(sconf->lock);
+
+    cleanup_cache_object(obj);
+
+    return OK;
+}
+
+static int read_headers(cache_handle *h, request_rec *r, cache_info **info) 
+{
+    cache_object_t *obj = (cache_object_t*) h->cache_obj;
+
+    *info = &(obj->info);
+
+    return OK;
+}
+
+static int read_body(cache_handle *h, apr_bucket_brigade *bb) 
+{
+    apr_bucket *b;
+    cache_object_t *obj = h->cache_obj;
+    
+    b = apr_bucket_immortal_create(obj->m, obj->m_len);
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+    b = apr_bucket_eos_create();
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+
+    return OK;
+}
+
+static int write_headers(cache_handle *h, request_rec *r, cache_info *info)
+{
+    apr_size_t len;
+    cache_object_t *obj = (cache_object_t*) h->cache_obj;
+    if (info->date) {
+        obj->info.date = info->date;
+    }
+    if (info->lastmod) {
+        obj->info.lastmod = info->lastmod;
+    }
+    if (info->expire) {
+        obj->info.expire = info->expire;
+    }
+    if (info->content_type) {
+        obj->info.content_type = (char*) malloc(strlen(info->content_type));
+        if (obj->info.content_type)
+            strcpy(obj->info.content_type, info->content_type);
+    }
+
+    return OK;
+}
+
+static int write_body(cache_handle *h, apr_bucket_brigade *b) 
+{
+    apr_status_t rv;
+    apr_size_t idx = 0;
+    cache_object_t *obj = (cache_object_t *) h->cache_obj;
+    apr_read_type_e eblock = APR_BLOCK_READ;
+    apr_bucket *e;
+
+    /* XXX mmap, malloc or file? 
+     * Enable this decision to be configured....
+     */
+    char *m = malloc(obj->m_len);
+    obj->m = m;
+    if (!m) {
+        /* Cleanup cache entry and return */
+    }
+    obj->type = CACHE_TYPE_MALLOC;
+
+    /* Iterate accross the brigade and populate the cache storage */
+    /* XXX doesn't handle multiple brigades */
+    APR_BRIGADE_FOREACH(e, b) {
+        const char *s;
+        apr_size_t len;
+
+        rv = apr_bucket_read(e, &s, &len, eblock);
+        if (rv != APR_SUCCESS) {
+            /* Big problem!  Cleanup cache entry and return */
+        }
+        /* XXX Check for overflow */
+        if (len ) {
+            memcpy(m, s, len);
+            m+=len;
+        }
+    }
+
+    /* XXX - Check for EOS before setting obj->complete
+     * Open for business. This entry can be served from the cache 
+     */
+    obj->complete = 1;
+    return OK;
+}
+
+static const char 
+*set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg)
+{
+    int val;
+
+    if (sscanf(arg, "%d", &val) != 1)
+    return "CacheSize value must be an integer (kBytes)";
+    sconf->space = val;
+    return NULL;
+}
+#if 0
+static const char
+*set_cache_factor(cmd_parms *parms, void *dummy, char *arg)
+{
+    double val;
+
+    if (sscanf(arg, "%lg", &val) != 1)
+        return "CacheLastModifiedFactor value must be a float";
+    sconf->lmfactor = val;
+
+    return NULL;
+}
+#endif
+#if 0
+static const char
+*set_cache_maxex(cmd_parms *parms, void *dummy, char *arg)
+{
+    mem_cache_conf *pc = ap_get_module_config(parms->server->module_config, &mem_cache_module);
+    double val;
+
+    if (sscanf(arg, "%lg", &val) != 1)
+        return "CacheMaxExpire value must be a float";
+    sconf->maxexpire = (apr_time_t) (val * MSEC_ONE_HR);
+    return NULL;
+}
+#endif
+#if 0
+static const char
+*set_cache_defex(cmd_parms *parms, void *dummy, char *arg)
+{
+    mem_cache_conf *pc = ap_get_module_config(parms->server->module_config, &mem_cache_module);
+    double val;
+
+    if (sscanf(arg, "%lg", &val) != 1)
+        return "CacheDefaultExpire value must be a float";
+    pc->defaultexpire = (apr_time_t) (val * MSEC_ONE_HR);
+    return NULL;
+}
+#endif
+static const command_rec cache_cmds[] =
+{
+    /* XXX
+     * What config directives does this module need?
+     * Should this module manage expire policy for its entries?
+     * Certainly cache limits like max number of entries,
+     * max entry size, and max size of the cache should
+     * be managed by this module. 
+     */
+    AP_INIT_TAKE1("CacheSizeMem", set_cache_size, NULL, RSRC_CONF,
+     "The maximum disk space used by the cache in Kb"),
+    {NULL}
+};
+
+static void register_hooks(apr_pool_t *p)
+{
+    /* cache initializer */
+/*    cache_hook_cache_init(cache_init, NULL, NULL, AP_HOOK_FIRST); */
+    cache_hook_create_entity(create_entity, NULL, NULL, APR_HOOK_MIDDLE);
+    cache_hook_open_entity(open_entity,  NULL, NULL, APR_HOOK_MIDDLE);
+    cache_hook_remove_url(remove_url, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module AP_MODULE_DECLARE_DATA mem_cache_module =
+{
+    STANDARD20_MODULE_STUFF,
+    NULL,                    /* create per-directory config structure */
+    NULL,                    /* merge per-directory config structures */
+    create_cache_config,     /* create per-server config structure */
+    NULL,                    /* merge per-server config structures */
+    cache_cmds,              /* command apr_table_t */
+    register_hooks
+};
+
diff --git a/modules/experimental/util_ldap.c b/modules/experimental/util_ldap.c
new file mode 100644 (file)
index 0000000..4e9ca0e
--- /dev/null
@@ -0,0 +1,1105 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ */
+
+/*
+ * util_ldap.c: LDAP things
+ * 
+ * Original code from auth_ldap module for Apache v1.3:
+ * Copyright 1998, 1999 Enbridge Pipelines Inc. 
+ * Copyright 1999-2001 Dave Carrigan
+ */
+
+#include <apr_ldap.h>
+
+#ifdef APU_HAS_LDAP
+
+#include <apr_lock.h>
+#include <apr_strings.h>
+
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "util_ldap.h"
+#include "util_ldap_cache.h"
+
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+module AP_MODULE_DECLARE_DATA ldap_module;
+
+int util_ldap_handler(request_rec *r);
+void *util_ldap_create_config(apr_pool_t *p, server_rec *s);
+
+
+/*
+ * Some definitions to help between various versions of apache.
+ */
+
+#ifndef DOCTYPE_HTML_2_0
+#define DOCTYPE_HTML_2_0  "<!DOCTYPE HTML PUBLIC \"-//IETF//" \
+                          "DTD HTML 2.0//EN\">\n"
+#endif
+
+#ifndef DOCTYPE_HTML_3_2
+#define DOCTYPE_HTML_3_2  "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
+                          "DTD HTML 3.2 Final//EN\">\n"
+#endif
+
+#ifndef DOCTYPE_HTML_4_0S
+#define DOCTYPE_HTML_4_0S "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
+                          "DTD HTML 4.0//EN\"\n" \
+                          "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+#endif
+
+#ifndef DOCTYPE_HTML_4_0T
+#define DOCTYPE_HTML_4_0T "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
+                          "DTD HTML 4.0 Transitional//EN\"\n" \
+                          "\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"
+#endif
+
+#ifndef DOCTYPE_HTML_4_0F
+#define DOCTYPE_HTML_4_0F "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
+                          "DTD HTML 4.0 Frameset//EN\"\n" \
+                          "\"http://www.w3.org/TR/REC-html40/frameset.dtd\">\n"
+#endif
+
+/*
+ * Status Handler
+ * --------------
+ *
+ * This handler generates a status page about the current performance of
+ * the LDAP cache. It is enabled as follows:
+ *
+ * <Location /ldap-status>
+ *   SetHandler ldap-status
+ * </Location>
+ *
+ */
+int util_ldap_handler(request_rec *r)
+{
+
+    r->allowed |= (1 << M_GET);
+    if (r->method_number != M_GET)
+        return DECLINED;
+
+    if (strcmp(r->handler, "ldap-status")) {
+        return DECLINED;
+    }
+
+    r->content_type = "text/html";
+    if (r->header_only)
+        return OK;
+
+    ap_rputs(DOCTYPE_HTML_3_2
+             "<html><head><title>LDAP Cache Information</title></head>\n", r);
+    ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information</h1>\n", r);
+
+    ap_rputs("<p>\n"
+             "<table border='0'>\n"
+             "<tr bgcolor='#000000'>\n"
+             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>"
+             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>"
+             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>"
+             "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>"
+             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>"
+             "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>"
+             "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>"
+             "</tr>\n", r
+            );
+
+    ap_rputs(util_ald_cache_display(r->pool), r);
+
+    ap_rputs("</table>\n</p>\n", r);
+
+    return OK;
+}
+
+/* ------------------------------------------------------------------ */
+
+
+/*
+ * Closes an LDAP connection by unlocking it. The next time
+ * util_ldap_connection_find() is called this connection will be
+ * available for reuse.
+ */
+void util_ldap_connection_close(util_ldap_connection_t *ldc)
+{
+
+    /*
+     * QUESTION:
+     *
+     * Is it safe leaving bound connections floating around between the
+     * different modules? Keeping the user bound is a performance boost,
+     * but it is also a potential security problem - maybe.
+     *
+     * For now we unbind the user when we finish with a connection, but
+     * we don't have to...
+     */
+
+    /* mark our connection as available for reuse */
+    apr_lock_release(ldc->lock);
+
+}
+
+
+/*
+ * Destroys an LDAP connection by unbinding. This function is registered
+ * with the pool cleanup function - causing the LDAP connections to be
+ * shut down cleanly on graceful restart.
+ */
+apr_status_t util_ldap_connection_destroy(void *param)
+{
+    util_ldap_connection_t *ldc = param;
+
+    /* unbinding from the LDAP server */
+    if (ldc->ldap) {
+        ldap_unbind_s(ldc->ldap);
+        ldc->bound = 0;
+        ldc->ldap = NULL;
+    }
+
+    /* release the lock we were using */
+    apr_lock_release(ldc->lock);
+
+    return APR_SUCCESS;
+}
+
+
+/*
+ * Connect to the LDAP server and binds. Does not connect if already
+ * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
+ *
+ * Returns LDAP_SUCCESS on success; and an error code on failure
+ */
+int util_ldap_connection_open(util_ldap_connection_t *ldc)
+{
+    int result = 0;
+    int failures = 0;
+
+
+start_over:
+    if (failures++ > 10) {
+       /* too many failures - leave */
+        return result;
+    }
+
+    if (!ldc->ldap) {
+        ldc->bound = 0;
+
+       /* opening connection to LDAP server */
+        if ((ldc->ldap = ldap_init(ldc->host, ldc->port)) == NULL) {
+           /* couldn't connect */
+            ldc->reason = "ldap_init() failed";
+            return -1;
+        }
+
+       /* add the cleanup to the pool */
+        apr_pool_cleanup_register(ldc->pool, ldc,
+                                  util_ldap_connection_destroy,
+                                  apr_pool_cleanup_null);
+
+        /* Set the alias dereferencing option */
+#if LDAP_VERSION_MAX == 2
+        ldc->ldap->ld_deref = ldc->deref;
+#else
+        result = ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref));
+        if (result != LDAP_SUCCESS) {
+           /* setting LDAP dereference option failed */
+           /* we ignore this error */
+        }
+#endif /* LDAP_VERSION_MAX */
+
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+        if (ldc->netscapessl) {
+            if (!ldc->certdb) {
+               /* secure LDAP requested, but no CA cert defined */
+                ldc->reason = "secure LDAP requested, but no CA cert defined";
+                return -1;
+            } else {
+                result = ldapssl_install_routines(ldc->ldap);
+                if (result != LDAP_SUCCESS) {
+                   /* SSL initialisation failed */
+                    ldc->reason = "ldapssl_install_routines() failed";
+                    return result;
+                }
+                result = ldap_set_option(ldc->ldap, LDAP_OPT_SSL, LDAP_OPT_ON);
+                if (result != LDAP_SUCCESS) {
+                   /* SSL option failed */
+                    ldc->reason = "ldap_set_option() failed trying to set LDAP_OPT_SSL";
+                    return result;
+                }
+            }
+        }
+#endif /* APU_HAS_LDAP_NETSCAPE_SSL */
+
+#ifdef APU_HAS_LDAP_STARTTLS
+        if (ldc->starttls) {
+            int version = LDAP_VERSION3;
+
+            /* Also we have to set the connection to use protocol version 3,
+             * since we're using TLS. */
+            if ((result = ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION,
+                                         &version)) != LDAP_SUCCESS) {
+               /* setting LDAP version failed - ignore error */
+            }
+
+            /* 
+             * In util_ldap_connection_find, we compare ldc->withtls to
+             * sec->starttls to see if we have a cache match. On the off
+             * chance that apache's config processing rotines set starttls to
+             * some other true value besides 1, we set it to 1 here to ensure
+             * that the comparison succeeds.
+             */
+            ldc->starttls = 1;
+
+            result = ldap_start_tls_s(ldc->ldap, NULL, NULL);
+            if (result != LDAP_SUCCESS) {
+               /* start TLS failed */
+               ldc->withtls = 0;
+                ldc->reason = "ldap_start_tls_s() failed";
+               return result;
+            }
+            ldc->withtls = 1;
+        } else {
+            ldc->withtls = 0;
+        }
+#endif /* APU_HAS_LDAP_STARTTLS */
+    }
+
+    /* 
+     * At this point the LDAP connection is guaranteed alive. If bound says
+     * that we're bound already, we can just return.
+     */
+    if (ldc->bound) {
+        ldc->reason = "LDAP connection open successful (already bound)";
+        return LDAP_SUCCESS;
+    }
+
+    /* 
+     * Now bind with the username/password provided by the
+     * configuration. It will be an anonymous bind if no u/p was
+     * provided. 
+     */
+    if ((result = ldap_simple_bind_s(ldc->ldap, ldc->binddn, ldc->bindpw))
+        == LDAP_SERVER_DOWN) {
+       /* couldn't connect - try again */
+        ldc->reason = "ldap_simple_bind_s() failed with server down";
+        goto start_over;
+    }
+
+    if (result != LDAP_SUCCESS) {
+       /* LDAP fatal error occured */
+        ldc->reason = "ldap_simple_bind_s() failed";
+        return result;
+    }
+
+    /* note how we are bound */
+    ldc->bound = 1;
+
+    ldc->reason = "LDAP connection open successful";
+    return LDAP_SUCCESS;
+}
+
+
+/*
+ * Find an existing ldap connection struct that matches the
+ * provided ldap connection parameters.
+ *
+ * If not found in the cache, a new ldc structure will be allocated from st->pool
+ * and returned to the caller. If found in the cache, a pointer to the existing
+ * ldc structure will be returned.
+ */
+util_ldap_connection_t *util_ldap_connection_find(request_rec *r, const char *host, int port,
+                                              const char *binddn, const char *bindpw, deref_options deref,
+                                              int netscapessl, int starttls)
+{
+    struct util_ldap_connection_t *l, *p;      /* To traverse the linked list */
+
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
+        &ldap_module);
+
+
+    /* mutex lock this function */
+    if (!st->mutex) {
+        apr_lock_create(&st->mutex, APR_MUTEX, APR_INTRAPROCESS, NULL, st->pool);
+    }
+    apr_lock_acquire(st->mutex);
+
+    /* Search for an exact connection match in the list that is not
+     * being used.
+     */
+    for (l=st->connections,p=NULL; l; l=l->next) {
+        if ( (APR_SUCCESS == apr_lock_tryacquire(l->lock))
+            && l->port == port
+           && strcmp(l->host, host) == 0
+           && ( (!l->binddn && !binddn) || (l->binddn && binddn && !strcmp(l->binddn, binddn)) )
+           && ( (!l->bindpw && !bindpw) || (l->bindpw && bindpw && !strcmp(l->bindpw, bindpw)) )
+            && l->deref == deref
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+            && l->netscapessl == netscapessl
+#endif
+#ifdef APU_HAS_LDAP_STARTTLS
+           && l->withtls == starttls
+#endif
+            )
+            break;
+        p = l;
+    }
+
+    /* If nothing found, search again, but we don't care about the
+     * binddn and bindpw this time.
+     */
+    if (!l) {
+        for (l=st->connections,p=NULL; l; l=l->next) {
+            if ( (APR_SUCCESS == apr_lock_tryacquire(l->lock))
+                && l->port == port
+               && strcmp(l->host, host) == 0
+                && l->deref == deref
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+                && l->netscapessl == netscapessl
+#endif
+#ifdef APU_HAS_LDAP_STARTTLS
+                && l->withtls == starttls
+#endif
+                ) {
+                /* the bind credentials have changed */
+                l->bound = 0;
+                l->binddn = apr_pstrdup(st->pool, binddn);
+                l->bindpw = apr_pstrdup(st->pool, bindpw);
+                break;
+            }
+            p = l;
+        }
+    }
+
+/* artificially disable cache */
+//l = NULL;
+
+    /* If no connection what found after the second search, we
+     * must create one.
+     */
+    if (!l) {
+
+        /* 
+         * Add the new connection entry to the linked list. Note that we
+         * don't actually establish an LDAP connection yet; that happens
+         * the first time authentication is requested.
+         */
+        /* create the details to the pool in st */
+        l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t));
+        apr_lock_create(&l->lock, APR_MUTEX, APR_INTRAPROCESS, NULL, st->pool);
+        apr_lock_acquire(l->lock);
+        l->pool = st->pool;
+        l->bound = 0;
+        l->host = apr_pstrdup(st->pool, host);
+        l->port = port;
+        l->deref = deref;
+        l->binddn = apr_pstrdup(st->pool, binddn);
+        l->bindpw = apr_pstrdup(st->pool, bindpw);
+        l->netscapessl = netscapessl;
+        l->starttls = starttls;
+        l->withtls = 0;
+
+        if (p) {
+            p->next = l;
+        }
+        else {
+            st->connections = l;
+        }
+    }
+
+    apr_lock_release(st->mutex);
+    return l;
+}
+
+/* ------------------------------------------------------------------ */
+
+/*
+ * Compares two DNs to see if they're equal. The only way to do this correctly is to 
+ * search for the dn and then do ldap_get_dn() on the result. This should match the 
+ * initial dn, since it would have been also retrieved with ldap_get_dn(). This is
+ * expensive, so if the configuration value compare_dn_on_server is
+ * false, just does an ordinary strcmp.
+ *
+ * The lock for the ldap cache should already be acquired.
+ */
+int util_ldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc, 
+                            const char *url, const char *dn, const char *reqdn, 
+                            int compare_dn_on_server)
+{
+    int result = 0;
+    util_url_node_t *curl; 
+    util_url_node_t curnode;
+    util_dn_compare_node_t *node;
+    util_dn_compare_node_t newnode;
+    int failures = 0;
+    LDAPMessage *res, *entry;
+    char *searchdn;
+
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
+        &ldap_module);
+
+
+    /* read lock this function */
+    if (!util_ldap_cache_lock) {
+        apr_lock_create(&util_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, NULL, st->pool);
+    }
+
+    /* get cache entry (or create one) */
+    apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER);
+    curnode.url = url;
+    curl = util_ald_cache_fetch(util_ldap_cache, &curnode);
+    if (curl == NULL) {
+        curl = util_ald_create_caches(st, url);
+    }
+    apr_lock_release(util_ldap_cache_lock);
+
+    /* a simple compare? */
+    if (!compare_dn_on_server) {
+        /* unlock this read lock */
+        if (strcmp(dn, reqdn)) {
+            ldc->reason = "DN Comparison FALSE (direct strcmp())";
+            return LDAP_COMPARE_FALSE;
+        }
+        else {
+            ldc->reason = "DN Comparison TRUE (direct strcmp())";
+            return LDAP_COMPARE_TRUE;
+        }
+    }
+
+    /* no - it's a server side compare */
+    apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER);
+
+    /* is it in the compare cache? */
+    newnode.reqdn = (char *)reqdn;
+    node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
+    if (node != NULL) {
+        /* If it's in the cache, it's good */
+        /* unlock this read lock */
+        apr_lock_release(util_ldap_cache_lock);
+        ldc->reason = "DN Comparison TRUE (cached)";
+        return LDAP_COMPARE_TRUE;
+    }
+
+    /* unlock this read lock */
+    apr_lock_release(util_ldap_cache_lock);
+
+start_over:
+    if (failures++ > 10) {
+       /* too many failures */
+        return result;
+    }
+
+    /* make a server connection */
+    if (LDAP_SUCCESS != (result = util_ldap_connection_open(ldc))) {
+       /* connect to server failed */
+        return result;
+    }
+
+    /* search for reqdn */
+    if ((result = ldap_search_ext_s(ldc->ldap, const_cast(reqdn), LDAP_SCOPE_BASE, 
+                                   "(objectclass=*)", NULL, 1, 
+                                   NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) {
+        util_ldap_connection_close(ldc);
+        ldc->reason = "DN Comparison ldap_search_ext_s() failed with server down";
+        goto start_over;
+    }
+    if (result != LDAP_SUCCESS) {
+        /* search for reqdn failed - no match */
+        ldc->reason = "DN Comparison ldap_search_ext_s() failed";
+        return result;
+    }
+
+    entry = ldap_first_entry(ldc->ldap, res);
+    searchdn = ldap_get_dn(ldc->ldap, entry);
+
+    ldap_msgfree(res);
+    if (strcmp(dn, searchdn) != 0) {
+        /* compare unsuccessful */
+        ldc->reason = "DN Comparison FALSE (checked on server)";
+        result = LDAP_COMPARE_FALSE;
+    }
+    else {
+        /* compare successful - add to the compare cache */
+        apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER);
+        newnode.reqdn = (char *)reqdn;
+        newnode.dn = (char *)dn;
+        util_ald_cache_insert(curl->dn_compare_cache, &newnode);
+        apr_lock_release(util_ldap_cache_lock);
+        ldc->reason = "DN Comparison TRUE (checked on server)";
+        result = LDAP_COMPARE_TRUE;
+    }
+    ldap_memfree(searchdn);
+    return result;
+
+}
+
+/*
+ * Does an generic ldap_compare operation. It accepts a cache that it will use
+ * to lookup the compare in the cache. We cache two kinds of compares 
+ * (require group compares) and (require user compares). Each compare has a different
+ * cache node: require group includes the DN; require user does not because the
+ * require user cache is owned by the 
+ *
+ */
+int util_ldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
+                          const char *url, const char *dn,
+                          const char *attrib, const char *value)
+{
+    int result = 0;
+    util_url_node_t *curl; 
+    util_url_node_t curnode;
+    util_compare_node_t *compare_nodep;
+    util_compare_node_t the_compare_node;
+    apr_time_t curtime;
+    int failures = 0;
+
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
+        &ldap_module);
+
+
+    /* read lock this function */
+    if (!util_ldap_cache_lock) {
+        apr_lock_create(&util_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, NULL, st->pool);
+    }
+
+    /* get cache entry (or create one) */
+    apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER);
+    curnode.url = url;
+    curl = util_ald_cache_fetch(util_ldap_cache, &curnode);
+    if (curl == NULL) {
+        curl = util_ald_create_caches(st, url);
+    }
+    apr_lock_release(util_ldap_cache_lock);
+
+    /* make a comparison to the cache */
+    apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER);
+    curtime = apr_time_now();
+
+    the_compare_node.dn = (char *)dn;
+    the_compare_node.attrib = (char *)attrib;
+    the_compare_node.value = (char *)value;
+    the_compare_node.result = 0;
+
+    compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node);
+
+    if (compare_nodep != NULL) {
+        /* found it... */
+        if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
+            /* ...but it is too old */
+            util_ald_cache_remove(curl->compare_cache, compare_nodep);
+        }
+        else {
+            /* ...and it is good */
+            /* unlock this read lock */
+            apr_lock_release(util_ldap_cache_lock);
+            if (LDAP_COMPARE_TRUE == compare_nodep->result) {
+                ldc->reason = "Comparison true (cached)";
+                return compare_nodep->result;
+            }
+            else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
+                ldc->reason = "Comparison false (cached)";
+                return compare_nodep->result;
+            }
+            else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
+                ldc->reason = "Comparison no such attribute (cached)";
+                return compare_nodep->result;
+            }
+            else {
+                ldc->reason = "Comparison undefined (cached)";
+                return compare_nodep->result;
+            }
+        }
+    }
+    /* unlock this read lock */
+    apr_lock_release(util_ldap_cache_lock);
+    
+
+start_over:
+    if (failures++ > 10) {
+        /* too many failures */
+        return result;
+    }
+    if (LDAP_SUCCESS != (result = util_ldap_connection_open(ldc))) {
+        /* connect failed */
+        return result;
+    }
+
+    if ((result = ldap_compare_s(ldc->ldap, const_cast(dn), 
+                                const_cast(attrib), const_cast(value)))
+        == LDAP_SERVER_DOWN) { 
+        /* connection failed - try again */
+        util_ldap_connection_close(ldc);
+        ldc->reason = "ldap_compare_s() failed with server down";
+        goto start_over;
+    }
+
+    ldc->reason = "Comparison complete";
+    if ((LDAP_COMPARE_TRUE == result) || 
+        (LDAP_COMPARE_FALSE == result) ||
+        (LDAP_NO_SUCH_ATTRIBUTE == result)) {
+        /* compare completed; caching result */
+        apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER);
+        the_compare_node.lastcompare = curtime;
+        the_compare_node.result = result;
+        util_ald_cache_insert(curl->compare_cache, &the_compare_node);
+        apr_lock_release(util_ldap_cache_lock);
+        if (LDAP_COMPARE_TRUE == result) {
+            ldc->reason = "Comparison true (adding to cache)";
+            return LDAP_COMPARE_TRUE;
+        }
+        else if (LDAP_COMPARE_FALSE == result) {
+            ldc->reason = "Comparison false (adding to cache)";
+            return LDAP_COMPARE_FALSE;
+        }
+        else {
+            ldc->reason = "Comparison no such attribute (adding to cache)";
+            return LDAP_NO_SUCH_ATTRIBUTE;
+        }
+    }
+    return result;
+}
+
+int util_ldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
+                              const char *url, const char *basedn, int scope, char **attrs,
+                              const char *filter, const char *bindpw, const char **binddn,
+                              const char ***retvals)
+{
+    const char **vals = NULL;
+    int result = 0;
+    LDAPMessage *res, *entry;
+    char *dn;
+    int count;
+    int failures = 0;
+    util_url_node_t *curl;             /* Cached URL node */
+    util_url_node_t curnode;
+    util_search_node_t *search_nodep;  /* Cached search node */
+    util_search_node_t the_search_node;
+    apr_time_t curtime;
+
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
+        &ldap_module);
+
+    /* read lock this function */
+    if (!util_ldap_cache_lock) {
+        apr_lock_create(&util_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, NULL, st->pool);
+    }
+
+    /* Get the cache node for this url */
+    apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER);
+    curnode.url = url;
+    curl = (util_url_node_t *)util_ald_cache_fetch(util_ldap_cache, &curnode);
+    if (curl == NULL) {
+        curl = util_ald_create_caches(st, url);
+    }
+    apr_lock_release(util_ldap_cache_lock);
+
+    apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER);
+    the_search_node.username = filter;
+    search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
+    if (search_nodep != NULL && search_nodep->bindpw) {
+
+        /* found entry in search cache... */
+        curtime = apr_time_now();
+
+        /*
+         * Remove this item from the cache if its expired, or if the 
+         * sent password doesn't match the storepassword.
+         */
+        if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
+            /* ...but entry is too old */
+            util_ald_cache_remove(curl->search_cache, search_nodep);
+        }
+        else if (strcmp(search_nodep->bindpw, bindpw) != 0) {
+           /* ...but cached password doesn't match sent password */
+            util_ald_cache_remove(curl->search_cache, search_nodep);
+        }
+        else {
+            /* ...and entry is valid */
+            *binddn = search_nodep->dn;
+            *retvals = search_nodep->vals;
+            apr_lock_release(util_ldap_cache_lock);
+            ldc->reason = "Authentication successful (cached)";
+            return LDAP_SUCCESS;
+        }
+    }
+    /* unlock this read lock */
+    apr_lock_release(util_ldap_cache_lock);
+
+
+    /* 
+     * At this point, there is no valid cached search, so lets do the search.
+     */
+
+    /*
+     * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
+     */
+start_over:
+    if (failures++ > 10) {
+        return result;
+    }
+    if (LDAP_SUCCESS != (result = util_ldap_connection_open(ldc))) {
+        return result;
+    }
+
+    /* try do the search */
+    if ((result = ldap_search_ext_s(ldc->ldap,
+                                   basedn, scope, 
+                                   filter, attrs, 0, 
+                                   NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) {
+        ldc->reason = "ldap_search_ext_s() for user failed with server down";
+        goto start_over;
+    }
+
+    /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
+    if (result != LDAP_SUCCESS) {
+        ldc->reason = "ldap_search_ext_s() for user failed";
+        return result;
+    }
+
+    /* 
+     * We should have found exactly one entry; to find a different
+     * number is an error.
+     */
+    count = ldap_count_entries(ldc->ldap, res);
+    if (count != 1) {
+        ldap_msgfree(res);
+        ldc->reason = "User is not unique (search found two or more matches)";
+        return LDAP_NO_SUCH_OBJECT;
+    }
+
+    entry = ldap_first_entry(ldc->ldap, res);
+
+    /* Grab the dn, copy it into the pool, and free it again */
+    dn = ldap_get_dn(ldc->ldap, entry);
+    *binddn = apr_pstrdup(st->pool, dn);
+    ldap_memfree(dn);
+
+    /* 
+     * A bind to the server with an empty password always succeeds, so
+     * we check to ensure that the password is not empty. This implies
+     * that users who actually do have empty passwords will never be
+     * able to authenticate with this module. I don't see this as a big
+     * problem.
+     */
+    if (strlen(bindpw) <= 0) {
+        ldap_msgfree(res);
+        ldc->reason = "Empty password not allowed";
+        return LDAP_INVALID_CREDENTIALS;
+    }
+
+    /* 
+     * Attempt to bind with the retrieved dn and the password. If the bind
+     * fails, it means that the password is wrong (the dn obviously
+     * exists, since we just retrieved it)
+     */
+    if ((result = 
+         ldap_simple_bind_s(ldc->ldap, *binddn, bindpw)) == 
+         LDAP_SERVER_DOWN) {
+        ldc->reason = "ldap_simple_bind_s() to check user credentials failed with server down";
+        goto start_over;
+    }
+
+    /* failure? if so - return */
+    if (result != LDAP_SUCCESS) {
+        ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
+        return result;
+    }
+
+    /*
+     * Get values for the provided attributes.
+     */
+    if (attrs) {
+        int k = 0;
+        int i = 0;
+        while (attrs[k++]);
+        vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
+        while (attrs[i]) {
+            char **values;
+            int j = 0;
+            char *str = NULL;
+            /* get values */
+            values = ldap_get_values(ldc->ldap, entry, attrs[i]);
+            while (values && values[j]) {
+                str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) : apr_pstrdup(r->pool, values[j]);
+                j++;
+            }
+            vals[i] = str;
+            i++;
+        }
+        *retvals = vals;
+    }
+
+    /*                 
+     * Add the new username to the search cache.
+     */
+    apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER);
+    the_search_node.username = filter;
+    the_search_node.dn = *binddn;
+    the_search_node.bindpw = bindpw;
+    the_search_node.lastbind = apr_time_now();
+    the_search_node.vals = vals;
+    util_ald_cache_insert(curl->search_cache, &the_search_node);
+    ldap_msgfree(res);
+    apr_lock_release(util_ldap_cache_lock);
+
+    ldc->reason = "Authentication successful";
+    return LDAP_SUCCESS;
+}
+
+#endif /* APU_HAS_LDAP */
+
+
+
+/* ---------------------------------------- */
+/* config directives */
+
+
+static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy, const char *bytes)
+{
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
+                                                 &ldap_module);
+
+    st->cache_bytes = atol(bytes);
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
+                      "[%d] ldap cache: Setting shared memory cache size to %d bytes.", 
+                      getpid(), st->cache_bytes);
+
+    return NULL;
+}
+
+static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
+{
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
+                                                 &ldap_module);
+
+    st->search_cache_ttl = atol(ttl) * 1000000;
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
+                      "[%d] ldap cache: Setting cache TTL to %ld microseconds.", 
+                      getpid(), st->search_cache_ttl);
+
+    return NULL;
+}
+
+static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy, const char *size)
+{
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
+                                                 &ldap_module);
+
+
+    st->search_cache_size = atol(size);
+    if (st->search_cache_size < 0) {
+        st->search_cache_size = 0;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
+                      "[%d] ldap cache: Setting search cache size to %ld entries.", 
+                      getpid(), st->search_cache_size);
+
+    return NULL;
+}
+
+static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
+{
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
+                                                 &ldap_module);
+
+    st->compare_cache_ttl = atol(ttl) * 1000000;
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
+                      "[%d] ldap cache: Setting operation cache TTL to %ld microseconds.", 
+                      getpid(), st->compare_cache_ttl);
+
+    return NULL;
+}
+
+static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy, const char *size)
+{
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
+                                                 &ldap_module);
+
+    st->compare_cache_size = atol(size);
+    if (st->compare_cache_size < 0) {
+        st->compare_cache_size = 0;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
+                      "[%d] ldap cache: Setting operation cache size to %ld entries.", 
+                      getpid(), st->compare_cache_size);
+
+    return NULL;
+}
+
+#ifdef APU_HAS_LDAPSSL_CLIENT_INIT
+static const char *util_ldap_set_certdbpath(cmd_parms *cmd, void *dummy, const char *path)
+{
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
+                                                 &ldap_module);
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
+                      "[%d] ldap cache: Setting LDAP SSL client certificate dbpath to %s.", 
+                      getpid(), path);
+
+    st->have_certdb = 1;
+    if (ldapssl_client_init(path, NULL) != 0) {
+        return "Could not initialize SSL client";
+    }
+    else {
+        return NULL;
+    }
+}
+#endif
+
+void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
+{
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
+
+    st->pool = p;
+
+    st->cache_bytes = 100000;
+    st->search_cache_ttl = 600000000;
+    st->search_cache_size = 1024;
+    st->compare_cache_ttl = 600000000;
+    st->compare_cache_size = 1024;
+
+    st->connections = NULL;
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+    st->have_certdb = 0;
+#endif
+
+    return st;
+}
+
+static void util_ldap_init_module(apr_pool_t *pool, server_rec *s)
+{
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(s->module_config, 
+                                                 &ldap_module);
+
+    apr_status_t result = util_ldap_cache_init(pool, st->cache_bytes);
+    char buf[MAX_STRING_LEN];
+
+    apr_strerror(result, buf, sizeof(buf));
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, result, s, 
+                      "[%d] ldap cache init: %s", 
+                      getpid(), buf);
+}
+
+
+command_rec util_ldap_cmds[] = {
+    AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes, NULL, RSRC_CONF,
+                  "Sets the size of the shared memory cache in bytes. "
+                  "Zero means disable the shared memory cache. Defaults to 100KB."),
+
+    AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries, NULL, RSRC_CONF,
+                  "Sets the maximum number of entries that are possible in the LDAP "
+                  "search cache. "
+                  "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."),
+
+    AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl, NULL, RSRC_CONF,
+                  "Sets the maximum time (in seconds) that an item can be cached in the LDAP "
+                  "search cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."),
+
+    AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries, NULL, RSRC_CONF,
+                  "Sets the maximum number of entries that are possible in the LDAP "
+                  "compare cache. "
+                  "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."),
+
+    AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl, NULL, RSRC_CONF,
+                  "Sets the maximum time (in seconds) that an item is cached in the LDAP "
+                  "operation cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."),
+
+#ifdef APU_HAS_LDAPSSL_CLIENT_INIT
+    AP_INIT_TAKE1("LDAPCertDBPath", util_ldap_set_certdbpath, NULL, RSRC_CONF,
+                  "Specifies the file containing Certificate Authority certificates "
+                  "for validating secure LDAP server certificates. This file must be the "
+                  "cert7.db database used by Netscape Communicator"),
+#endif
+
+    {NULL}
+};
+
+static void util_ldap_register_hooks(apr_pool_t *p)
+{
+    ap_hook_child_init(util_ldap_init_module, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module ldap_module = {
+   STANDARD20_MODULE_STUFF,
+   NULL,                               /* dir config creater */
+   NULL,                               /* dir merger --- default is to override */
+   util_ldap_create_config,            /* server config */
+   NULL,                               /* merge server config */
+   util_ldap_cmds,                     /* command table */
+   util_ldap_register_hooks,           /* set up request processing hooks */
+};
diff --git a/modules/experimental/util_ldap_cache.c b/modules/experimental/util_ldap_cache.c
new file mode 100644 (file)
index 0000000..9d9844d
--- /dev/null
@@ -0,0 +1,309 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ */
+
+/*
+ * util_ldap_cache.c: LDAP cache things
+ * 
+ * Original code from auth_ldap module for Apache v1.3:
+ * Copyright 1998, 1999 Enbridge Pipelines Inc. 
+ * Copyright 1999-2001 Dave Carrigan
+ */
+
+#include <apr_ldap.h>
+#include "util_ldap.h"
+#include "util_ldap_cache.h"
+
+#ifdef APU_HAS_LDAP
+
+
+
+
+/* ------------------------------------------------------------------ */
+
+unsigned long util_ldap_url_node_hash(void *n)
+{
+    util_url_node_t *node = (util_url_node_t *)n;
+    return util_ald_hash_string(1, node->url);
+}
+
+int util_ldap_url_node_compare(void *a, void *b)
+{
+    util_url_node_t *na = (util_url_node_t *)a;
+    util_url_node_t *nb = (util_url_node_t *)b;
+
+    return(strcmp(na->url, nb->url) == 0);
+}
+
+void *util_ldap_url_node_copy(void *c)
+{
+    util_url_node_t *n = (util_url_node_t *)c;
+    util_url_node_t *node = (util_url_node_t *)util_ald_alloc(sizeof(util_url_node_t));
+
+    if (node) {
+        if (!(node->url = util_ald_strdup(n->url))) {
+            util_ald_free(node->url);
+            return NULL;
+        }
+        node->search_cache = n->search_cache;
+        node->compare_cache = n->compare_cache;
+        node->dn_compare_cache = n->dn_compare_cache;
+        return node;
+    }
+    else {
+        return NULL;
+    }
+}
+
+void util_ldap_url_node_free(void *n)
+{
+    util_url_node_t *node = (util_url_node_t *)n;
+
+    util_ald_free(node->url);
+    util_ald_destroy_cache(node->search_cache);
+    util_ald_destroy_cache(node->compare_cache);
+    util_ald_destroy_cache(node->dn_compare_cache);
+    util_ald_free(node);
+}
+
+/* ------------------------------------------------------------------ */
+
+/* Cache functions for search nodes */
+unsigned long util_ldap_search_node_hash(void *n)
+{
+    util_search_node_t *node = (util_search_node_t *)n;
+    return util_ald_hash_string(1, ((util_search_node_t *)(node))->username);
+}
+
+int util_ldap_search_node_compare(void *a, void *b)
+{
+    return(strcmp(((util_search_node_t *)a)->username,
+                 ((util_search_node_t *)b)->username) == 0);
+}
+
+void *util_ldap_search_node_copy(void *c)
+{
+    util_search_node_t *node = (util_search_node_t *)c;
+    util_search_node_t *newnode = util_ald_alloc(sizeof(util_search_node_t));
+
+    /* safety check */
+    if (newnode) {
+
+        /* copy vals */
+        if (node->vals) {
+            int k = 0;
+            int i = 0;
+            while (node->vals[k++]);
+            if (!(newnode->vals = util_ald_alloc(sizeof(char *) * (k+1)))) {
+                util_ldap_search_node_free(newnode);
+                return NULL;
+            }
+            while (node->vals[i]) {
+                if (!(newnode->vals[i] = util_ald_strdup(node->vals[i]))) {
+                    util_ldap_search_node_free(newnode);
+                    return NULL;
+                }
+                i++;
+            }
+        }
+        else {
+            newnode->vals = NULL;
+        }
+        if (!(newnode->username = util_ald_strdup(node->username)) ||
+            !(newnode->dn = util_ald_strdup(node->dn)) ||
+            !(newnode->bindpw = util_ald_strdup(node->bindpw)) ) {
+            util_ldap_search_node_free(newnode);
+            return NULL;
+        }
+        newnode->lastbind = node->lastbind;
+
+    }
+    return (void *)newnode;
+}
+
+void util_ldap_search_node_free(void *n)
+{
+    int i = 0;
+    util_search_node_t *node = (util_search_node_t *)n;
+    if (node->vals) {
+        while (node->vals[i]) {
+            util_ald_free(node->vals[i++]);
+        }
+        util_ald_free(node->vals);
+    }
+    util_ald_free(node->username);
+    util_ald_free(node->dn);
+    util_ald_free(node->bindpw);
+    util_ald_free(node);
+}
+
+/* ------------------------------------------------------------------ */
+
+unsigned long util_ldap_compare_node_hash(void *n)
+{
+    util_compare_node_t *node = (util_compare_node_t *)n;
+    return util_ald_hash_string(3, node->dn, node->attrib, node->value);
+}
+
+int util_ldap_compare_node_compare(void *a, void *b)
+{
+    util_compare_node_t *na = (util_compare_node_t *)a;
+    util_compare_node_t *nb = (util_compare_node_t *)b;
+    return (strcmp(na->dn, nb->dn) == 0 &&
+           strcmp(na->attrib, nb->attrib) == 0 &&
+           strcmp(na->value, nb->value) == 0);
+}
+
+void *util_ldap_compare_node_copy(void *c)
+{
+    util_compare_node_t *n = (util_compare_node_t *)c;
+    util_compare_node_t *node = (util_compare_node_t *)util_ald_alloc(sizeof(util_compare_node_t));
+
+    if (node) {
+        if (!(node->dn = util_ald_strdup(n->dn)) ||
+            !(node->attrib = util_ald_strdup(n->attrib)) ||
+            !(node->value = util_ald_strdup(n->value))) {
+            util_ldap_compare_node_free(node);
+            return NULL;
+        }
+        node->lastcompare = n->lastcompare;
+        node->result = n->result;
+        return node;
+    }
+    else {
+        return NULL;
+    }
+}
+
+void util_ldap_compare_node_free(void *n)
+{
+    util_compare_node_t *node = (util_compare_node_t *)n;
+    util_ald_free(node->dn);
+    util_ald_free(node->attrib);
+    util_ald_free(node->value);
+    util_ald_free(node);
+}
+
+/* ------------------------------------------------------------------ */
+
+unsigned long util_ldap_dn_compare_node_hash(void *n)
+{
+    return util_ald_hash_string(1, ((util_dn_compare_node_t *)n)->reqdn);
+}
+
+int util_ldap_dn_compare_node_compare(void *a, void *b)
+{
+    return (strcmp(((util_dn_compare_node_t *)a)->reqdn,
+                  ((util_dn_compare_node_t *)b)->reqdn) == 0);
+}
+
+void *util_ldap_dn_compare_node_copy(void *c)
+{
+    util_dn_compare_node_t *n = (util_dn_compare_node_t *)c;
+    util_dn_compare_node_t *node = (util_dn_compare_node_t *)util_ald_alloc(sizeof(util_dn_compare_node_t));
+    if (node) {
+        if (!(node->reqdn = util_ald_strdup(n->reqdn)) ||
+            !(node->dn = util_ald_strdup(n->dn))) {
+            util_ldap_dn_compare_node_free(node);
+            return NULL;
+        }
+        return node;
+    }
+    else {
+        return NULL;
+    }
+}
+
+void util_ldap_dn_compare_node_free(void *n)
+{
+    util_dn_compare_node_t *node = (util_dn_compare_node_t *)n;
+    util_ald_free(node->reqdn);
+    util_ald_free(node->dn);
+    util_ald_free(node);
+}
+
+
+/* ------------------------------------------------------------------ */
+apr_status_t util_ldap_cache_child_kill(void *data);
+apr_status_t util_ldap_cache_module_kill(void *data);
+
+apr_status_t util_ldap_cache_module_kill(void *data)
+{
+#if APR_HAS_SHARED_MEMORY
+    if (util_ldap_shm != NULL) {
+        apr_status_t result = apr_shm_destroy(util_ldap_shm);
+        util_ldap_shm = NULL;
+        return result;
+    }
+#endif
+    return APR_SUCCESS;
+}
+
+apr_status_t util_ldap_cache_init(apr_pool_t *pool, apr_size_t reqsize)
+{
+    apr_status_t result = APR_SUCCESS;
+    apr_pool_cleanup_register(pool, NULL, util_ldap_cache_module_kill, apr_pool_cleanup_null);
+
+#if APR_HAS_SHARED_MEMORY
+    result = apr_shm_init(&util_ldap_shm, reqsize, "/tmp/ldap_cache", pool);
+#endif
+    util_ldap_cache = util_ald_create_cache(50,
+                                    util_ldap_url_node_hash,
+                                    util_ldap_url_node_compare,
+                                    util_ldap_url_node_copy,
+                                    util_ldap_url_node_free);
+    return result;
+}
+
+
+#endif /* APU_HAS_LDAP */
diff --git a/modules/experimental/util_ldap_cache.h b/modules/experimental/util_ldap_cache.h
new file mode 100644 (file)
index 0000000..18790eb
--- /dev/null
@@ -0,0 +1,214 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ */
+
+
+#ifndef APU_LDAP_CACHE_H
+#define APU_LDAP_CACHE_H
+
+/*
+ * This switches LDAP support on or off.
+ */
+
+/* this whole thing disappears if LDAP is not enabled */
+#ifdef APU_HAS_LDAP
+
+
+/*
+ * LDAP Cache Manager
+ */
+
+#include <apr_shmem.h>
+
+typedef struct util_cache_node_t {
+    void *payload;             /* Pointer to the payload */
+    time_t add_time;           /* Time node was added to cache */
+    struct util_cache_node_t *next;
+} util_cache_node_t;
+
+typedef struct util_ald_cache_t {
+    unsigned long size;                /* Size of cache array */
+    unsigned long maxentries;  /* Maximum number of cache entries */
+    unsigned long numentries;  /* Current number of cache entries */
+    unsigned long fullmark;    /* Used to keep track of when cache becomes 3/4 full */
+    time_t marktime;           /* Time that the cache became 3/4 full */
+    unsigned long (*hash)(void *);  /* Func to hash the payload */
+    int (*compare)(void *, void *); /* Func to compare two payloads */
+    void * (*copy)(void *);    /* Func to alloc mem and copy payload to new mem */
+    void (*free)(void *);              /* Func to free mem used by the payload */
+    util_cache_node_t **nodes;
+
+    unsigned long numpurges;   /* No. of times the cache has been purged */
+    double avg_purgetime;              /* Average time to purge the cache */
+    time_t last_purge;         /* Time of the last purge */
+    unsigned long npurged;     /* Number of elements purged in last purge. This is not
+                                  obvious: it won't be 3/4 the size of the cache if 
+                                  there were a lot of expired entries. */
+
+    unsigned long fetches;     /* Number of fetches */
+    unsigned long hits;                /* Number of cache hits */
+    unsigned long inserts;     /* Number of inserts */
+    unsigned long removes;     /* Number of removes */
+} util_ald_cache_t;
+
+#if APR_HAS_SHARED_MEMORY
+apr_shmem_t *util_ldap_shm;
+#endif
+util_ald_cache_t *util_ldap_cache;
+apr_lock_t *util_ldap_cache_lock;
+
+#ifndef WIN32
+#define ALD_MM_FILE_MODE ( S_IRUSR|S_IWUSR )
+#else
+#define ALD_MM_FILE_MODE ( _S_IREAD|_S_IWRITE )
+#endif
+
+
+/*
+ * LDAP Cache
+ */
+
+/*
+ * Maintain a cache of LDAP URLs that the server handles. Each node in
+ * the cache contains the search cache for that URL, and a compare cache
+ * for the URL. The compare cash is populated when doing require group
+ * compares.
+ */
+typedef struct util_url_node_t {
+    const char *url;
+    util_ald_cache_t *search_cache;
+    util_ald_cache_t *compare_cache;
+    util_ald_cache_t *dn_compare_cache;
+} util_url_node_t;
+
+/*
+ * We cache every successful search and bind operation, using the username 
+ * as the key. Each node in the cache contains the returned DN, plus the 
+ * password used to bind.
+ */
+typedef struct util_search_node_t {
+    const char *username;              /* Cache key */
+    const char *dn;                    /* DN returned from search */
+    const char *bindpw;                        /* The most recently used bind password; 
+                                          NULL if the bind failed */
+    apr_time_t lastbind;               /* Time of last successful bind */
+    const char **vals;                 /* Values of queried attributes */
+} util_search_node_t;
+
+/*
+ * We cache every successful compare operation, using the DN, attrib, and
+ * value as the key. 
+ */
+typedef struct util_compare_node_t {
+    const char *dn;                    /* DN, attrib and value combine to be the key */
+    const char *attrib;                        
+    const char *value;
+    apr_time_t lastcompare;
+    int result;
+} util_compare_node_t;
+
+/*
+ * We cache every successful compare dn operation, using the dn in the require
+ * statement and the dn fetched based on the client-provided username.
+ */
+typedef struct util_dn_compare_node_t {
+    const char *reqdn;         /* The DN in the require dn statement */
+    const char *dn;                    /* The DN found in the search */
+} util_dn_compare_node_t;
+
+
+/*
+ * Function prototypes for LDAP cache
+ */
+
+/* util_ldap_cache.c */
+unsigned long util_ldap_url_node_hash(void *n);
+int util_ldap_url_node_compare(void *a, void *b);
+void *util_ldap_url_node_copy(void *c);
+void util_ldap_url_node_free(void *n);
+unsigned long util_ldap_search_node_hash(void *n);
+int util_ldap_search_node_compare(void *a, void *b);
+void *util_ldap_search_node_copy(void *c);
+void util_ldap_search_node_free(void *n);
+unsigned long util_ldap_compare_node_hash(void *n);
+int util_ldap_compare_node_compare(void *a, void *b);
+void *util_ldap_compare_node_copy(void *c);
+void util_ldap_compare_node_free(void *n);
+unsigned long util_ldap_dn_compare_node_hash(void *n);
+int util_ldap_dn_compare_node_compare(void *a, void *b);
+void *util_ldap_dn_compare_node_copy(void *c);
+void util_ldap_dn_compare_node_free(void *n);
+
+
+/* util_ldap_cache_mgr.c */
+
+void util_ald_free(const void *ptr);
+void *util_ald_alloc(unsigned long size);
+const char *util_ald_strdup(const char *s);
+unsigned long util_ald_hash_string(int nstr, ...);
+void util_ald_cache_purge(util_ald_cache_t *cache);
+util_url_node_t *util_ald_create_caches(util_ldap_state_t *s, const char *url);
+util_ald_cache_t *util_ald_create_cache(unsigned long maxentries,
+                                unsigned long (*hashfunc)(void *), 
+                                int (*comparefunc)(void *, void *),
+                                void * (*copyfunc)(void *),
+                                void (*freefunc)(void *));
+void util_ald_destroy_cache(util_ald_cache_t *cache);
+void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload);
+void util_ald_cache_insert(util_ald_cache_t *cache, void *payload);
+void util_ald_cache_remove(util_ald_cache_t *cache, void *payload);
+char *util_ald_cache_display_stats(apr_pool_t *p, util_ald_cache_t *cache,
+                                 char *name);
+
+#endif /* APU_HAS_LDAP */
+#endif /* APU_LDAP_CACHE_H */
diff --git a/modules/experimental/util_ldap_cache_mgr.c b/modules/experimental/util_ldap_cache_mgr.c
new file mode 100644 (file)
index 0000000..49eece8
--- /dev/null
@@ -0,0 +1,537 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ */
+
+/*
+ * util_ldap_cache_mgr.c: LDAP cache manager things
+ * 
+ * Original code from auth_ldap module for Apache v1.3:
+ * Copyright 1998, 1999 Enbridge Pipelines Inc. 
+ * Copyright 1999-2001 Dave Carrigan
+ */
+
+#include <apr_ldap.h>
+#include "util_ldap.h"
+#include "util_ldap_cache.h"
+#include <apr_strings.h>
+
+#ifdef APU_HAS_LDAP
+
+/* only here until strdup is gone */
+#include <string.h>
+
+/* here till malloc is gone */
+#include <stdlib.h>
+
+static const int primes[] =
+{
+  11,
+  19,
+  37,
+  73,
+  109,
+  163,
+  251,
+  367,
+  557,
+  823,
+  1237,
+  1861,
+  2777,
+  4177,
+  6247,
+  9371,
+  14057,
+  21089,
+  31627,
+  47431,
+  71143,
+  106721,
+  160073,
+  240101,
+  360163,
+  540217,
+  810343,
+  1215497,
+  1823231,
+  2734867,
+  4102283,
+  6153409,
+  9230113,
+  13845163,
+  0
+};
+
+void util_ald_free(const void *ptr)
+{
+#if APR_HAS_SHARED_MEMORY
+    if (util_ldap_shm) {
+        if (ptr)
+            apr_shm_free(util_ldap_shm, (void *)ptr);
+    } else {
+        if (ptr)
+            free((void *)ptr);
+    }
+#else
+    if (ptr)
+        free((void *)ptr);
+#endif
+}
+
+void *util_ald_alloc(unsigned long size)
+{
+    if (0 == size)
+        return NULL;
+#if APR_HAS_SHARED_MEMORY
+    if (util_ldap_shm) {
+        return (void *)apr_shm_calloc(util_ldap_shm, size);
+    } else {
+        return (void *)calloc(sizeof(char), size);
+    }
+#else
+    return (void *)calloc(sizeof(char), size);
+#endif
+}
+
+const char *util_ald_strdup(const char *s)
+{
+#if APR_HAS_SHARED_MEMORY
+    if (util_ldap_shm) {
+        char *buf = apr_shm_malloc(util_ldap_shm, strlen(s)+1);
+        if (buf) {
+            strcpy(buf, s);
+            return buf;
+        }
+        else {
+            return NULL;
+        }
+    } else {
+        return strdup(s);
+    }
+#else
+    return strdup(s);
+#endif
+}
+
+
+/*
+ * Computes the hash on a set of strings. The first argument is the number
+ * of strings to hash, the rest of the args are strings. 
+ * Algorithm taken from glibc.
+ */
+unsigned long util_ald_hash_string(int nstr, ...)
+{
+    int i;
+    va_list args;
+    unsigned long h=0, g;
+    char *str, *p;
+  
+    va_start(args, nstr);
+    for (i=0; i < nstr; ++i) {
+        str = va_arg(args, char *);
+        for (p = str; *p; ++p) {
+            h = ( h << 4 ) + *p;
+            if ( ( g = h & 0xf0000000 ) ) {
+               h = h ^ (g >> 24);
+               h = h ^ g;
+            }
+        }
+    }
+    va_end(args);
+
+    return h;
+}
+
+
+/*
+  Purges a cache that has gotten full. We keep track of the time that we
+  added the entry that made the cache 3/4 full, then delete all entries
+  that were added before that time. It's pretty simplistic, but time to
+  purge is only O(n), which is more important.
+*/
+void util_ald_cache_purge(util_ald_cache_t *cache)
+{
+    int i;
+    util_cache_node_t *p, *q;
+    apr_time_t t;
+
+    if (!cache)
+        return;
+  
+    cache->last_purge = apr_time_now();
+    cache->npurged = 0;
+    cache->numpurges++;
+
+    for (i=0; i < cache->size; ++i) {
+        p = cache->nodes[i];
+        while (p != NULL) {
+            if (p->add_time < cache->marktime) {
+               q = p->next;
+               (*cache->free)(p->payload);
+               util_ald_free(p);
+               cache->numentries--;
+               cache->npurged++;
+               p = q;
+            }
+            else {
+               p = p->next;
+            }
+        }
+    }
+
+    t = apr_time_now();
+    cache->avg_purgetime = 
+         ((t - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) / 
+         cache->numpurges;
+}
+
+
+/*
+ * create caches
+ */
+util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url)
+{
+    util_url_node_t *curl = NULL;
+    util_ald_cache_t *search_cache;
+    util_ald_cache_t *compare_cache;
+    util_ald_cache_t *dn_compare_cache;
+
+    /* create the three caches */
+    search_cache = util_ald_create_cache(st->search_cache_size,
+                                         util_ldap_search_node_hash,
+                                         util_ldap_search_node_compare,
+                                         util_ldap_search_node_copy,
+                                         util_ldap_search_node_free);
+    compare_cache = util_ald_create_cache(st->compare_cache_size,
+                                          util_ldap_compare_node_hash,
+                                          util_ldap_compare_node_compare,
+                                          util_ldap_compare_node_copy,
+                                          util_ldap_compare_node_free);
+    dn_compare_cache = util_ald_create_cache(st->compare_cache_size,
+                                             util_ldap_dn_compare_node_hash,
+                                             util_ldap_dn_compare_node_compare,
+                                             util_ldap_dn_compare_node_copy,
+                                             util_ldap_dn_compare_node_free);
+
+    /* check that all the caches initialised successfully */
+    if (search_cache && compare_cache && dn_compare_cache) {
+
+        curl = (util_url_node_t *)apr_pcalloc(st->pool, sizeof(util_url_node_t));
+        curl->url = url;
+        curl->search_cache = search_cache;
+        curl->compare_cache = compare_cache;
+        curl->dn_compare_cache = dn_compare_cache;
+
+        util_ald_cache_insert(util_ldap_cache, curl);
+
+    }
+
+    return curl;
+}
+
+
+util_ald_cache_t *util_ald_create_cache(unsigned long maxentries,
+                                unsigned long (*hashfunc)(void *), 
+                                int (*comparefunc)(void *, void *),
+                                void * (*copyfunc)(void *),
+                                void (*freefunc)(void *))
+{
+    util_ald_cache_t *cache;
+    int i;
+
+    if (maxentries <= 0)
+        return NULL;
+
+    cache = (util_ald_cache_t *)util_ald_alloc(sizeof(util_ald_cache_t));
+    if (!cache)
+        return NULL;
+
+    cache->maxentries = maxentries;
+    cache->numentries = 0;
+    cache->size = maxentries / 3;
+    if (cache->size < 64) cache->size = 64;
+        for (i = 0; primes[i] && primes[i] < cache->size; ++i) ;
+            cache->size = primes[i]? primes[i] : primes[i-1];
+
+    cache->nodes = (util_cache_node_t **)util_ald_alloc(cache->size * sizeof(util_cache_node_t *));
+    if (!cache->nodes) {
+        util_ald_free(cache);
+        return NULL;
+    }
+
+    for (i=0; i < cache->size; ++i)
+        cache->nodes[i] = NULL;
+
+    cache->hash = hashfunc;
+    cache->compare = comparefunc;
+    cache->copy = copyfunc;
+    cache->free = freefunc;
+
+    cache->fullmark = cache->maxentries / 4 * 3;
+    cache->marktime = 0;
+    cache->avg_purgetime = 0.0;
+    cache->numpurges = 0;
+    cache->last_purge = 0;
+    cache->npurged = 0;
+
+    cache->fetches = 0;
+    cache->hits = 0;
+    cache->inserts = 0;
+    cache->removes = 0;
+
+    return cache;
+}
+
+void util_ald_destroy_cache(util_ald_cache_t *cache)
+{
+    int i;
+    util_cache_node_t *p, *q;
+
+    if (cache == NULL)
+        return;
+
+    for (i = 0; i < cache->size; ++i) {
+        p = cache->nodes[i];
+        q = NULL;
+        while (p != NULL) {
+            q = p->next;
+           (*cache->free)(p->payload);
+           util_ald_free(p);
+           p = q;
+        }
+    }
+    util_ald_free(cache->nodes);
+}
+
+void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
+{
+    int hashval;
+    util_cache_node_t *p;
+
+    if (cache == NULL)
+        return NULL;
+
+    cache->fetches++;
+
+    hashval = (*cache->hash)(payload) % cache->size;
+    for (p = cache->nodes[hashval]; 
+         p && !(*cache->compare)(p->payload, payload);
+    p = p->next) ;
+
+    if (p != NULL) {
+        cache->hits++;
+        return p->payload;
+    }
+    else {
+        return NULL;
+    }
+}
+
+/*
+ * Insert an item into the cache. 
+ * *** Does not catch duplicates!!! ***
+ */
+void util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
+{
+    int hashval;
+    util_cache_node_t *node;
+
+    if (cache == NULL || payload == NULL)
+        return;
+
+    cache->inserts++;
+    hashval = (*cache->hash)(payload) % cache->size;
+    node = (util_cache_node_t *)util_ald_alloc(sizeof(util_cache_node_t));
+    node->add_time = apr_time_now();
+    node->payload = (*cache->copy)(payload);
+    node->next = cache->nodes[hashval];
+    cache->nodes[hashval] = node;
+    if (++cache->numentries == cache->fullmark) 
+        cache->marktime=apr_time_now();
+    if (cache->numentries >= cache->maxentries)
+        util_ald_cache_purge(cache);
+}
+
+void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
+{
+    int hashval;
+    util_cache_node_t *p, *q;
+  
+    if (cache == NULL)
+        return;
+
+    cache->removes++;
+    hashval = (*cache->hash)(payload) % cache->size;
+    for (p = cache->nodes[hashval], q=NULL;
+         p && !(*cache->compare)(p->payload, payload);
+         p = p->next) {
+         q = p;
+    }
+
+    /* If p is null, it means that we couldn't find the node, so just return */
+    if (p == NULL)
+        return;
+
+    if (q == NULL) {
+        /* We found the node, and it's the first in the list */
+        cache->nodes[hashval] = p->next;
+    }
+    else {
+        /* We found the node and it's not the first in the list */
+        q->next = p->next;
+    }
+    (*cache->free)(p->payload);
+    util_ald_free(p);
+    cache->numentries--;
+}
+
+char *util_ald_cache_display_stats(apr_pool_t *p, util_ald_cache_t *cache, char *name)
+{
+    int i;
+    int totchainlen = 0;
+    int nchains = 0;
+    double chainlen;
+    util_cache_node_t *n;
+    char *buf;
+
+    if (cache == NULL) {
+        return "";
+    }
+
+    for (i=0; i < cache->size; ++i) {
+        if (cache->nodes[i] != NULL) {
+            nchains++;
+            for (n = cache->nodes[i]; n != NULL; n = n->next)
+               totchainlen++;
+        }
+    }
+    chainlen = nchains? (double)totchainlen / (double)nchains : 0;
+
+    buf = apr_psprintf(p, 
+             "<tr valign='top'>"
+             "<td nowrap>%s</td>"
+             "<td align='right' nowrap>%lu (%.0f%% full)</td>"
+             "<td align='right'>%.1f</td>"
+            "<td align='right'>%lu/%lu</td>"
+            "<td align='right'>%.0f%%</td>"
+             "<td align='right'>%lu/%lu</td>",
+             name,
+            cache->numentries, 
+            (double)cache->numentries / (double)cache->maxentries * 100.0,
+             chainlen,
+            cache->hits,            
+            cache->fetches,
+            (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
+            cache->inserts,
+            cache->removes);
+
+    if (cache->numpurges) {
+        char str_ctime[APR_CTIME_LEN];
+
+        apr_ctime(str_ctime, cache->last_purge);
+        buf = apr_psprintf(p,
+                           "%s"
+                          "<td align='right'>%lu</td>\n"
+                           "<td align='right' nowrap>%s</td>\n", 
+                           buf,
+                          cache->numpurges,
+                          str_ctime);
+    }
+    else {
+        buf = apr_psprintf(p, 
+                           "%s<td colspan='2' align='center'>(none)</td>\n",
+                           buf);
+    }
+
+    buf = apr_psprintf(p, "%s<td align='right'>%.2g</td>\n</tr>", buf, cache->avg_purgetime);
+
+    return buf;
+}
+
+char *util_ald_cache_display(apr_pool_t *pool)
+{
+    int i;
+    char *buf, *t1, *t2, *t3;
+
+    if (!util_ldap_cache) {
+        return "<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>";
+    }
+
+    buf = util_ald_cache_display_stats(pool, util_ldap_cache, "LDAP URL Cache");
+
+    for (i=0; i < util_ldap_cache->size; ++i) {
+        util_cache_node_t *p;
+        for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
+            util_url_node_t *n;
+
+            n = (util_url_node_t *)p->payload;
+
+            t1 = apr_psprintf(pool, "%s (Searches)", n->url);
+            t2 = apr_psprintf(pool, "%s (Compares)", n->url);
+            t3 = apr_psprintf(pool, "%s (DNCompares)", n->url);
+
+            buf = apr_psprintf(pool, "%s\n\n"
+                                     "%s\n\n"
+                                     "%s\n\n"
+                                     "%s\n\n",
+                                     buf,
+                                     util_ald_cache_display_stats(pool, n->search_cache, t1),
+                                     util_ald_cache_display_stats(pool, n->compare_cache, t2),
+                                     util_ald_cache_display_stats(pool, n->dn_compare_cache, t3)
+                              );
+        }
+    }
+    return buf;
+}
+
+#endif /* APU_HAS_LDAP */
diff --git a/modules/generators/mod_cgi.h b/modules/generators/mod_cgi.h
new file mode 100644 (file)
index 0000000..4eadfaa
--- /dev/null
@@ -0,0 +1,79 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef _MOD_CGI_H
+#define _MOD_CGI_H 1
+
+
+/**
+ * Registerable optional function to override CGI behavior;
+ * Reprocess the command and arguments to execute the given CGI script.
+ * @param cmd Pointer to the command to execute (may be overridden)
+ * @param argv Pointer to the arguments to pass (may be overridden)
+ * @param r The current request
+ * @param p The pool to allocate correct cmd/argv elements within.
+ * @deffunc apr_status_t ap_cgi_build_command(const char **cmd, const char ***argv, request_rec *r, apr_pool_t *p)
+ * @tip This callback may be registered by the os-specific module 
+ * to correct the command and arguments for apr_proc_create invocation
+ * on a given os.  mod_cgi will call the function if registered.
+ */
+APR_DECLARE_OPTIONAL_FN(apr_status_t, ap_cgi_build_command, 
+                        (const char **cmd, const char ***argv,
+                         request_rec *r, apr_pool_t *p));
+
+#endif /* _MOD_CGI_H */
diff --git a/modules/mappers/mod_vhost_alias.dsp b/modules/mappers/mod_vhost_alias.dsp
new file mode 100644 (file)
index 0000000..7a892bf
--- /dev/null
@@ -0,0 +1,128 @@
+# Microsoft Developer Studio Project File - Name="mod_vhost_alias" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_vhost_alias - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_vhost_alias.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_vhost_alias.mak" CFG="mod_vhost_alias - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_vhost_alias - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_vhost_alias - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_vhost_alias - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_vhost_alias" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias
+
+!ELSEIF  "$(CFG)" == "mod_vhost_alias - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_vhost_alias" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_vhost_alias - Win32 Release"
+# Name "mod_vhost_alias - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_vhost_alias.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_vhost_alias.rc
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "mod_vhost_alias - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_vhost_alias.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       awk -f ../../build/win32/win32ver.awk mod_vhost_alias  "vhost_alias_module for Apache" ../../include/ap_release.h > .\mod_vhost_alias.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_vhost_alias - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_vhost_alias.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       awk -f ../../build/win32/win32ver.awk mod_vhost_alias  "vhost_alias_module for Apache" ../../include/ap_release.h > .\mod_vhost_alias.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/modules/metadata/mod_mime_magic.dsp b/modules/metadata/mod_mime_magic.dsp
new file mode 100644 (file)
index 0000000..f831ae4
--- /dev/null
@@ -0,0 +1,128 @@
+# Microsoft Developer Studio Project File - Name="mod_mime_magic" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_mime_magic - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_mime_magic.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_mime_magic.mak" CFG="mod_mime_magic - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_mime_magic - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_mime_magic - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_mime_magic - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_mime_magic" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_mime_magic.so" /base:@..\..\os\win32\BaseAddr.ref,mod_mime_magic
+# ADD LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_mime_magic.so" /base:@..\..\os\win32\BaseAddr.ref,mod_mime_magic
+
+!ELSEIF  "$(CFG)" == "mod_mime_magic - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_mime_magic" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_mime_magic.so" /base:@..\..\os\win32\BaseAddr.ref,mod_mime_magic
+# ADD LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_mime_magic.so" /base:@..\..\os\win32\BaseAddr.ref,mod_mime_magic
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_mime_magic - Win32 Release"
+# Name "mod_mime_magic - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_mime_magic.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_mime_magic.rc
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "mod_mime_magic - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_mime_magic.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       awk -f ../../build/win32/win32ver.awk mod_mime_magic  "mime_magic_module for Apache" ../../include/ap_release.h  > .\mod_mime_magic.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_mime_magic - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_mime_magic.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       awk -f ../../build/win32/win32ver.awk mod_mime_magic  "mime_magic_module for Apache" ../../include/ap_release.h  > .\mod_mime_magic.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/modules/metadata/mod_unique_id.dsp b/modules/metadata/mod_unique_id.dsp
new file mode 100644 (file)
index 0000000..3045d2f
--- /dev/null
@@ -0,0 +1,128 @@
+# Microsoft Developer Studio Project File - Name="mod_unique_id" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_unique_id - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_unique_id.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_unique_id.mak" CFG="mod_unique_id - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_unique_id - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_unique_id - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_unique_id - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_unique_id" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_unique_id.so" /base:@..\..\os\win32\BaseAddr.ref,mod_unique_id
+# ADD LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_unique_id.so" /base:@..\..\os\win32\BaseAddr.ref,mod_unique_id
+
+!ELSEIF  "$(CFG)" == "mod_unique_id - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_unique_id" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_unique_id.so" /base:@..\..\os\win32\BaseAddr.ref,mod_unique_id
+# ADD LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_unique_id.so" /base:@..\..\os\win32\BaseAddr.ref,mod_unique_id
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_unique_id - Win32 Release"
+# Name "mod_unique_id - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_unique_id.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_unique_id.rc
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "mod_unique_id - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_unique_id.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       awk -f ../../build/win32/win32ver.awk mod_unique_id  "unique_id_module for Apache" ../../include/ap_release.h  > .\mod_unique_id.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_unique_id - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_unique_id.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       awk -f ../../build/win32/win32ver.awk mod_unique_id  "unique_id_module for Apache" ../../include/ap_release.h  > .\mod_unique_id.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/modules/proxy/CHANGES b/modules/proxy/CHANGES
new file mode 100644 (file)
index 0000000..d03f68c
--- /dev/null
@@ -0,0 +1,134 @@
+
+mod_proxy changes for httpd 2.0.18-dev
+
+  *) Reverse previous patch since the core reverted.
+     [Chuck Murcko <chuck@topsail.org>]
+
+  *) Remove indirection on number of bytes to read for input filters.
+     [Chuck Murcko <chuck@topsail.org>]
+
+  *) Fixed a problem with directory listing corruption in the
+     PROXY_DIR filter.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) mod_proxy and the proxy submodules now build properly as DSOs.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Stopped the HTTP proxy from trying to read entity bodies when there
+     wasn't one (response was 1xx, 204, 205 or 304).
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Made sure dates were canonicalised correctly when passed to the client
+     browser through the HTTP proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Split each individual proxy protocol into separate modules.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Added Max-Forwards support for all request types so as to prevent
+     loops.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fix warnings about byte count type on Darwin (connect handler).
+     [Chuck Murcko <chuck@topsail.org>]
+
+  *) IPV6 EPSV support for IPV6 in FTP proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) FTP directory filter works now.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fixed some thread-safety issues with the HTTP proxy in mod_proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) PASV FTP works now.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Reworked the line-at-a-time read from the control connection to
+     workaround a stray empty bucket returned by the HTTP_IN filter.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Stopped the CORE filter from sending off an HTTP response when a
+     CONNECT tunnel was closed.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fixed the poll() loop in proxy_connect.c -> it works now!!!
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Converted send_dir() to ap_proxy_send_dir_filter() in proxy_ftp.c.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Major rework of ap_proxy_ftp_handler() to use filters (begone foul
+     BUFF!!!). It compiles, but is untested, and the build environment needs
+     to be fixed to include proxy_ftp.c.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Cleanup of dead functions within proxy_util.c.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Reworked the storage of the client socket between keepalive connections
+     to fix some nasty problems with the socket lasting longer than the
+     memory pool it was allocated from.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fixed bug where a hostname without a "." in it (such as "localhost")
+     would not trigger an IP address check with ProxyBlock.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fixed ProxyBlock bugs with ap_proxy_http_handler() and
+     ap_proxy_connect_handler().
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Updated ap_proxy_connect_handler() to support APR, while
+     moving some common code between http_handler and connect_handler
+     to proxy_util.c.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Updated mod_proxy.html docs to include v2.0 configuration.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fixed problem where responses without entity bodies would cause
+     the directly following proxy keepalive request to fail.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Added support for downstream keepalives in mod_proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Changed mod_proxy ap_proxy_http_handler() to support APR properly.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fix problem where incoming response headers were not being returned
+     to the client in mod_proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Added X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Server to
+     reverse proxied request headers in mod_proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) replace INADDR_NONE with APR_INADDR_NONE [Ian Holsman <IanH@cnet.com>]
+
+  *) Fix problem with proxy configuration where globally set
+     configuration options were overridden inside virtual hosts.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fix ProxyReceiveBufferSize where default value was left
+     uninitialised.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Some small changes:
+     - Ensured hop-by-hop headers were stripped as per
+       RFC2616 13.5.1.
+     - Upgraded version code to HTTP/1.1.
+     - Added Connection: close until Keepalives come.
+     - Some cosmetic fixes and commenting.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+mod_proxy changes for 2.0.14 alpha
+
+  *) removed ProxyNoCache and ProxyCacheForceCompletion config directives,
+     since we no longer directly cache from this module [Chuck Murcko]
+
+  *) removed cache [Chuck Murcko]
+
+  *) initial rerebuild for 2.0 [Chuck Murcko]
+
diff --git a/modules/ssl/Makefile.in b/modules/ssl/Makefile.in
new file mode 100644 (file)
index 0000000..2207d48
--- /dev/null
@@ -0,0 +1,50 @@
+## ====================================================================
+## The Apache Software License, Version 1.1
+##
+## Copyright (c) 2000-2001 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.
+## ====================================================================
+
+include $(top_srcdir)/build/special.mk
+
diff --git a/modules/ssl/config.m4 b/modules/ssl/config.m4
new file mode 100644 (file)
index 0000000..4eeefd7
--- /dev/null
@@ -0,0 +1,160 @@
+dnl ## ====================================================================
+dnl ## The Apache Software License, Version 1.1
+dnl ##  
+dnl ## Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
+dnl ## reserved.
+dnl ##
+dnl ## Redistribution and use in source and binary forms, with or without
+dnl ## modification, are permitted provided that the following conditions
+dnl ## are met:
+dnl ##
+dnl ## 1. Redistributions of source code must retain the above copyright
+dnl ##    notice, this list of conditions and the following disclaimer.
+dnl ##
+dnl ## 2. Redistributions in binary form must reproduce the above copyright
+dnl ##    notice, this list of conditions and the following disclaimer in
+dnl ##    the documentation and/or other materials provided with the
+dnl ##    distribution.
+dnl ##
+dnl ## 3. The end-user documentation included with the redistribution,
+dnl ##    if any, must include the following acknowledgment:
+dnl ##       "This product includes software developed by the
+dnl ##        Apache Software Foundation (http://www.apache.org/)."
+dnl ##    Alternately, this acknowledgment may appear in the software itself,
+dnl ##    if and wherever such third-party acknowledgments normally appear.
+dnl ##
+dnl ## 4. The names "Apache" and "Apache Software Foundation" must
+dnl ##    not be used to endorse or promote products derived from this
+dnl ##    software without prior written permission. For written
+dnl ##    permission, please contact apache@apache.org.
+dnl ##
+dnl ## 5. Products derived from this software may not be called "Apache",
+dnl ##    nor may "Apache" appear in their name, without prior written
+dnl ##    permission of the Apache Software Foundation.
+dnl ##
+dnl ## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+dnl ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+dnl ## OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+dnl ## DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+dnl ## ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+dnl ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+dnl ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+dnl ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+dnl ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+dnl ## SUCH DAMAGE.
+dnl ## ====================================================================
+
+dnl #  start of module specific part
+APACHE_MODPATH_INIT(ssl)
+
+dnl #  list of module object files
+ssl_objs="mod_ssl.lo"
+ssl_objs="$ssl_objs ssl_engine_config.lo"
+
+dnl #  hook module into the Autoconf mechanism (--enable-ssl option)
+APACHE_MODULE(ssl, [SSL/TLS support (mod_ssl)], $ssl_objs, , no, [
+
+    dnl #  hook into Autoconf mechanism (--with-ssl[=DIR] option)
+    AC_MSG_CHECKING(for SSL/TLS toolkit base)
+    ssltk_base="SYSTEM"
+    AC_ARG_WITH(ssl, [  --with-ssl[=DIR]        SSL/TLS toolkit (OpenSSL)], [
+        if test ".$withval" != .yes -a ".$withval" != .; then
+            ssltk_base="$withval"
+            if test ! -d $ssltk_base; then
+                AC_MSG_ERROR([invalid SSL/TLS toolkit base directory $ssltk_base])
+            fi
+        fi
+    ])
+    AC_MSG_RESULT($ssltk_base)
+    
+    dnl #   determine SSL/TLS toolkit frontend (openssl binary)
+    AC_MSG_CHECKING(for SSL/TLS toolkit frontend)
+    ssltk_frontend=""
+    if test ".$ssltk_base" = .SYSTEM; then
+        for p in . `echo $PATH | sed -e 's/:/ /g'`; do
+            if test -f "$p/openssl"; then
+                ssltk_frontend="$p/openssl"
+                break
+            fi
+        done
+        if test ".$ssltk_frontend" = .; then
+            AC_MSG_ERROR(['openssl' not found in $PATH])
+        fi
+    else
+        if test -f "$ssltk_base/bin/openssl"; then
+            ssltk_frontend="$ssltk_base/bin/openssl"
+        else
+            AC_MSG_ERROR(['openssl' not found in $ssltk_base/bin/])
+        fi
+    fi
+    AC_MSG_RESULT($ssltk_frontend)
+
+    dnl #   determine SSL/TLS toolkit version
+    AC_MSG_CHECKING(for SSL/TLS toolkit version)
+    ssltk_version="`$ssltk_frontend version`"
+    case "$ssltk_version" in
+        *0.9.[[6789]]* ) ;;
+        * ) AC_MSG_ERROR([SSL/TLS toolkit version $ssltk_version not supported]) ;;
+    esac
+    AC_MSG_RESULT($ssltk_version)
+
+    dnl #   determine SSL/TLS toolkit include directory
+    AC_MSG_CHECKING(for SSL/TLS toolkit includes)
+    ssltk_incdir=""
+    if test ".$ssltk_base" = .SYSTEM; then
+        for p in . /usr/include /usr/include/ssl/ /usr/local/include /usr/local/include/ssl; do
+            if test -f "$p/openssl/ssl.h"; then
+                ssltk_incdir="$p"
+                break
+            fi
+        done
+        if test ".$ssltk_incdir" = .; then
+            AC_MSG_ERROR([OpenSSL headers not found])
+        fi
+    else
+        if test -f "$ssltk_base/include/openssl/ssl.h"; then
+            ssltk_incdir="$ssltk_base/include"
+        else
+            AC_MSG_ERROR([OpenSSL headers not found under $ssltk_base])
+        fi
+    fi
+    AC_MSG_RESULT($ssltk_incdir)
+
+    dnl #   determine SSL/TLS toolkit library directory
+    AC_MSG_CHECKING(for SSL/TLS toolkit libraries)
+    ssltk_libdir=""
+    if test ".$ssltk_base" = .SYSTEM; then
+        for p in . /lib /usr/lib /usr/local/lib; do
+            if test -f "$p/libssl.a" -o -f "$p/libssl.so"; then
+                ssltk_libdir="$p"
+                break
+            fi
+        done
+        if test ".$ssltk_libdir" = .; then
+            AC_MSG_ERROR([OpenSSL libraries not found])
+        fi
+    else
+        if test -f "$ssltk_base/libssl.a" -o -f "$ssltk_base/libssl.so"; then
+            ssltk_libdir="$ssltk_base"
+        elif test -f "$ssltk_base/lib/libssl.a" -o -f "$ssltk_base/lib/libssl.so"; then
+            ssltk_libdir="$ssltk_base/lib"
+        else
+            AC_MSG_ERROR([OpenSSL libraries not found under $ssltk_base])
+        fi
+    fi
+    AC_MSG_RESULT($ssltk_libdir)
+
+    dnl #  annotate the Apache build environment with determined information
+    if test ".$ssltk_incdir" != "./usr/include"; then
+        INCLUDES="$INCLUDES -I$ssltk_incdir"
+    fi
+    if test ".$ssltk_libdir" != "./usr/lib"; then
+        LIBS="$LIBS -L$ssltk_libdir -lssl -lcrypto"
+    fi
+])
+
+dnl #  end of module specific part
+APACHE_MODPATH_FINISH
+
diff --git a/modules/ssl/mod_ssl.dsp b/modules/ssl/mod_ssl.dsp
new file mode 100644 (file)
index 0000000..c1f46df
--- /dev/null
@@ -0,0 +1,217 @@
+# Microsoft Developer Studio Project File - Name="mod_ssl" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_ssl - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_ssl.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_ssl.mak" CFG="mod_ssl - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_ssl - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_ssl - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_ssl - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /I "..\..\include" /I "..\..\os\win32" /I "..\..\server\mpm\winnt" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /I "..\..\srclib\openssl\inc32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_ssl" /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 /nologo /subsystem:windows /dll /incremental:no /map /out:"Release/mod_ssl.so" /machine:I386 /base:@..\..\os\win32\BaseAddr.ref,mod_ssl
+# ADD LINK32 kernel32.lib ssleay32.lib libeay32.lib /nologo /libpath:"..\..\srclib\openssl\out32dll" /subsystem:windows /dll /incremental:no /map /out:"Release/mod_ssl.so" /machine:I386 /base:@..\..\os\win32\BaseAddr.ref,mod_ssl
+
+!ELSEIF  "$(CFG)" == "mod_ssl - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /ZI /Od /I "..\..\include" /I "..\..\os\win32" /I "..\..\server\mpm\winnt" /I "..\..\srclib\apr\include" /I "..\..\srclib\apr-util\include" /I "..\..\srclib\openssl\inc32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_ssl" /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 /nologo /subsystem:windows /dll /incremental:no /map /debug /out:"Debug/mod_ssl.so" /machine:I386 /base:@..\..\os\win32\BaseAddr.ref,mod_ssl
+# ADD LINK32 kernel32.lib ssleay32.lib libeay32.lib /nologo /libpath:"..\..\srclib\openssl\out32dll" /subsystem:windows /dll /incremental:no /map /debug /out:"Debug/mod_ssl.so" /machine:I386 /base:@..\..\os\win32\BaseAddr.ref,mod_ssl
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_ssl - Win32 Release"
+# Name "mod_ssl - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "*.c"
+# Begin Source File
+
+SOURCE=.\mod_ssl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_dh.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_ds.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_ext.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_init.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_io.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_kernel.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_log.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_mutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_pphrase.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_rand.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_vars.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_expr.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_expr_eval.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_expr_parse.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_expr_scan.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_scache.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_scache_dbm.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_scache_shmcb.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_scache_shmht.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_ssl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_table.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "*.h"
+# Begin Source File
+
+SOURCE=.\mod_ssl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_expr.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_expr_parse.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_ssl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_table.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/modules/test/Makefile.in b/modules/test/Makefile.in
new file mode 100644 (file)
index 0000000..7c5c149
--- /dev/null
@@ -0,0 +1,3 @@
+# a modules Makefile has no explicit targets -- they will be defined by
+# whatever modules are enabled. just grab special.mk to deal with this.
+include $(top_srcdir)/build/special.mk
diff --git a/modules/test/config.m4 b/modules/test/config.m4
new file mode 100644 (file)
index 0000000..01bc0fa
--- /dev/null
@@ -0,0 +1,9 @@
+
+APACHE_MODPATH_INIT(test)
+
+APACHE_MODULE(optional_hook_export, example optional hook exporter, , , no)
+APACHE_MODULE(optional_hook_import, example optional hook importer, , , no)
+APACHE_MODULE(optional_fn_import, example optional function importer, , , no)
+APACHE_MODULE(optional_fn_export, example optional function exporter, , , no)
+
+APACHE_MODPATH_FINISH
diff --git a/modules/test/mod_optional_hook_export.c b/modules/test/mod_optional_hook_export.c
new file mode 100644 (file)
index 0000000..34ef97b
--- /dev/null
@@ -0,0 +1,83 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "mod_optional_hook_export.h"
+#include "http_protocol.h"
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ap,AP_MODULE,int,generic_hook_test,
+                                   (const char *szStr),
+                                   (szStr),OK,DECLINED)
+
+static int ExportLogTransaction(request_rec *r)
+{
+    return ap_run_generic_hook_test(r->the_request);
+}
+
+static void ExportRegisterHooks(apr_pool_t *p)
+{
+    ap_hook_log_transaction(ExportLogTransaction,NULL,NULL,APR_HOOK_MIDDLE);
+}
+
+module optional_hook_export_module =
+{
+    STANDARD20_MODULE_STUFF,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    ExportRegisterHooks
+};
diff --git a/modules/test/mod_optional_hook_export.h b/modules/test/mod_optional_hook_export.h
new file mode 100644 (file)
index 0000000..7494078
--- /dev/null
@@ -0,0 +1,62 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ */
+
+#ifndef MOD_GENERIC_HOOK_EXPORT_H
+#define MOD_GENERIC_HOOK_EXPORT_H
+
+#include "apr_optional_hooks.h"
+
+APR_DECLARE_EXTERNAL_HOOK(ap,AP_MODULE,int,generic_hook_test,(const char *))
+
+#endif /* def MOD_GENERIC_HOOK_EXPORT_H */
diff --git a/modules/test/mod_optional_hook_import.c b/modules/test/mod_optional_hook_import.c
new file mode 100644 (file)
index 0000000..ebf0158
--- /dev/null
@@ -0,0 +1,83 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "mod_optional_hook_export.h"
+
+static int ImportGenericHookTestHook(const char *szStr)
+{
+    ap_log_error(APLOG_MARK,APLOG_ERR,OK,NULL,"Generic hook test said: %s",
+                szStr);
+
+    return OK;
+}
+
+static void ImportRegisterHooks(apr_pool_t *p)
+{
+    APR_OPTIONAL_HOOK(ap,generic_hook_test,ImportGenericHookTestHook,NULL,NULL,
+                     APR_HOOK_MIDDLE);
+}
+
+module optional_hook_import_module=
+{
+    STANDARD20_MODULE_STUFF,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    ImportRegisterHooks
+};
diff --git a/server/mpm/mpmt_os2/.cvsignore b/server/mpm/mpmt_os2/.cvsignore
new file mode 100644 (file)
index 0000000..dc53ac8
--- /dev/null
@@ -0,0 +1,5 @@
+*.lo
+*.la
+Makefile
+.deps
+.libs
diff --git a/server/mpm/mpmt_os2/Makefile.in b/server/mpm/mpmt_os2/Makefile.in
new file mode 100644 (file)
index 0000000..38e598e
--- /dev/null
@@ -0,0 +1,5 @@
+
+LTLIBRARY_NAME    = libmpmt_os2.la
+LTLIBRARY_SOURCES = mpmt_os2.c mpmt_os2_child.c
+
+include $(top_srcdir)/build/ltlib.mk
diff --git a/server/mpm/mpmt_os2/config5.m4 b/server/mpm/mpmt_os2/config5.m4
new file mode 100644 (file)
index 0000000..b27c296
--- /dev/null
@@ -0,0 +1,5 @@
+if test "$MPM_NAME" = "mpmt_os2" ; then
+    AC_CACHE_SAVE
+    APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
+    APR_ADDTO(CFLAGS,-Zmt)
+fi
diff --git a/server/mpm/mpmt_os2/mpm.h b/server/mpm/mpmt_os2/mpm.h
new file mode 100644 (file)
index 0000000..37d2f6c
--- /dev/null
@@ -0,0 +1,74 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_MPM_MPMT_OS2_H
+#define APACHE_MPM_MPMT_OS2_H
+
+#define MPMT_OS2_MPM
+
+#include "httpd.h"
+#include "mpm_default.h"
+#include "scoreboard.h"
+
+#define MPM_NAME "MPMT_OS2"
+
+extern server_rec *ap_server_conf;
+#define AP_MPM_WANT_SET_PIDFILE
+#define AP_MPM_WANT_SET_MAX_REQUESTS
+
+#endif /* APACHE_MPM_SPMT_OS2_H */
diff --git a/server/mpm/mpmt_os2/mpm_default.h b/server/mpm/mpmt_os2/mpm_default.h
new file mode 100644 (file)
index 0000000..100beb2
--- /dev/null
@@ -0,0 +1,116 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_MPM_DEFAULT_H
+#define APACHE_MPM_DEFAULT_H
+
+/* Number of servers processes to spawn off by default
+ */
+#ifndef DEFAULT_START_DAEMON
+#define DEFAULT_START_DAEMON 2
+#endif
+
+/* We don't need many processes, 
+ * they're only for redundancy in the event of a crash 
+ */
+#define HARD_SERVER_LIMIT 10
+
+/* Limit on the total number of threads per process
+ */
+#ifndef HARD_THREAD_LIMIT
+#define HARD_THREAD_LIMIT 256
+#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
+
+/* Where the main/parent process's pid is logged */
+#ifndef DEFAULT_PIDLOG
+#define DEFAULT_PIDLOG "logs/httpd.pid"
+#endif
+
+/*
+ * Interval, in microseconds, between scoreboard maintenance.
+ */
+#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.
+ */
+#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
+#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
+#endif
+
+/* AP_CHILD_THREAD_FROM_ID is used by the scoreboard.  */
+#define AP_CHILD_THREAD_FROM_ID(i)    (i / HARD_THREAD_LIMIT), (i % HARD_THREAD_LIMIT)
+#define AP_ID_FROM_CHILD_THREAD(c, t)    ((c * HARD_THREAD_LIMIT) + t)
+
+#endif /* AP_MPM_DEFAULT_H */
diff --git a/server/mpm/mpmt_os2/mpmt_os2.c b/server/mpm/mpmt_os2/mpmt_os2.c
new file mode 100644 (file)
index 0000000..99806d1
--- /dev/null
@@ -0,0 +1,587 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/* Multi-process, multi-threaded MPM for OS/2
+ *
+ * Server consists of
+ * - a main, parent process
+ * - a small, static number of child processes
+ *
+ * The parent process's job is to manage the child processes. This involves
+ * spawning children as required to ensure there are always ap_daemons_to_start
+ * processes accepting connections.
+ *
+ * Each child process consists of a a pool of worker threads and a
+ * main thread that accepts connections & passes them to the workers via
+ * a work queue. The worker thread pool is dynamic, managed by a maintanence
+ * thread so that the number of idle threads is kept between
+ * min_spare_threads & max_spare_threads.
+ *
+ */
+
+/*
+ Todo list
+ - Fix log file clashing between child processes
+ - Enforce MaxClients somehow
+ - Catch thread exceptions & initiate graceful shutdown of child process
+*/
+#define CORE_PRIVATE
+#define INCL_NOPMAPI
+#define INCL_DOS
+#define INCL_DOSERRORS
+
+#include "ap_config.h"
+#include "httpd.h"
+#include "mpm_default.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 "mpm.h"
+#include "ap_mpm.h"
+#include "ap_listen.h"
+#include "apr_portable.h"
+#include "mpm_common.h"
+#include "apr_strings.h"
+#include <os2.h>
+#include <process.h>
+
+server_rec *ap_server_conf;
+static apr_pool_t *pconf = NULL;               /* Pool for config stuff */
+static const char *ap_pid_fname=NULL;
+
+/* Config globals */
+static int one_process = 0;
+static int ap_daemons_to_start = 0;
+static int ap_thread_limit = 0;
+static int ap_max_requests_per_child = 0;
+int ap_min_spare_threads = 0;
+int ap_max_spare_threads = 0;
+
+/* Keep track of a few interesting statistics */
+int ap_max_daemons_limit = -1;
+
+/* volatile just in case */
+static int volatile shutdown_pending;
+static int volatile restart_pending;
+static int volatile is_graceful = 0;
+ap_generation_t volatile ap_my_generation=0; /* Used by the scoreboard */
+static int is_parent_process=TRUE;
+HMTX ap_mpm_accept_mutex = 0;
+
+/* An array of these is stored in a shared memory area for passing
+ * sockets from the parent to child processes
+ */
+typedef struct {
+    struct sockaddr_in name;
+    apr_os_sock_t listen_fd;
+} listen_socket_t;
+
+typedef struct {
+    apr_os_file_t errorlog_fd;
+    apr_time_t restart_time;
+    HMTX accept_mutex;
+    listen_socket_t listeners[1];
+} parent_info_t;
+
+static char master_main();
+static void spawn_child(int slot);
+void ap_mpm_child_main(apr_pool_t *pconf);
+static void set_signals();
+
+
+int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
+{
+    char *listener_shm_name;
+    parent_info_t *parent_info;
+    ULONG rc;
+    pconf = _pconf;
+    ap_server_conf = s;
+    restart_pending = 0;
+
+    DosSetMaxFH(ap_thread_limit * 2);
+    listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getppid());
+    rc = DosGetNamedSharedMem((PPVOID)&parent_info, listener_shm_name, PAG_READ);
+    is_parent_process = rc != 0;
+    ap_scoreboard_fname = apr_psprintf(pconf, "/sharemem/httpd/scoreboard.%d", is_parent_process ? getpid() : getppid());
+
+    if (rc == 0) {
+        /* Child process */
+        ap_listen_rec *lr;
+        int num_listeners = 0;
+
+        apr_file_close(ap_server_conf->error_log);
+        apr_os_file_put(&ap_server_conf->error_log, &parent_info->errorlog_fd, pconf);
+        ap_restart_time = parent_info->restart_time;
+        ap_mpm_accept_mutex = parent_info->accept_mutex;
+
+        /* Set up a default listener if necessary */
+        if (ap_listeners == NULL) {
+            ap_listen_rec *lr = apr_pcalloc(s->process->pool, sizeof(ap_listen_rec));
+            ap_listeners = lr;
+            apr_sockaddr_info_get(&lr->bind_addr, "0.0.0.0", APR_UNSPEC,
+                                  DEFAULT_HTTP_PORT, 0, s->process->pool);
+            apr_socket_create(&lr->sd, lr->bind_addr->sa.sin.sin_family,
+                              SOCK_STREAM, s->process->pool);
+        }
+
+        for (lr = ap_listeners; lr; lr = lr->next) {
+            apr_os_sock_put(&lr->sd, &parent_info->listeners[num_listeners].listen_fd, pconf);
+            num_listeners++;
+        }
+
+        DosFreeMem(parent_info);
+
+        /* Do the work */
+        ap_mpm_child_main(pconf);
+
+        /* Outta here */
+        return 1;
+    }
+    else {
+        /* Parent process */
+        char restart;
+        is_parent_process = TRUE;
+        ap_log_pid(pconf, ap_pid_fname);
+
+        if (ap_setup_listeners(ap_server_conf) < 1) {
+            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
+                         "no listening sockets available, shutting down");
+            return 1;
+        }
+
+        restart = master_main();
+        ++ap_my_generation;
+        ap_scoreboard_image->global.running_generation = ap_my_generation;
+
+        if (!restart) {
+            const char *pidfile = ap_server_root_relative(pconf, ap_pid_fname);
+
+            if (pidfile != NULL && remove(pidfile) == 0) {
+                ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS,
+                             ap_server_conf, "removed PID file %s (pid=%d)",
+                             pidfile, getpid());
+            }
+
+            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
+                         "caught SIGTERM, shutting down");
+            return 1;
+        }
+    }  /* Parent process */
+
+    return 0; /* Restart */
+}
+
+
+
+/* Main processing of the parent process
+ * returns TRUE if restarting
+ */
+static char master_main()
+{
+    server_rec *s = ap_server_conf;
+    ap_listen_rec *lr;
+    parent_info_t *parent_info;
+    char *listener_shm_name;
+    int listener_num, num_listeners, slot;
+    ULONG rc;
+
+    printf("%s \n", ap_get_server_version());
+    set_signals();
+
+    if (ap_setup_listeners(ap_server_conf) < 1) {
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
+                     "no listening sockets available, shutting down");
+        return FALSE;
+    }
+
+    /* Allocate a shared memory block for the array of listeners */
+    for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
+        num_listeners++;
+    }
+
+    listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getpid());
+    rc = DosAllocSharedMem((PPVOID)&parent_info, listener_shm_name,
+                           sizeof(parent_info_t) + num_listeners * sizeof(listen_socket_t),
+                           PAG_READ|PAG_WRITE|PAG_COMMIT);
+
+    if (rc) {
+        ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
+                     "failure allocating shared memory, shutting down");
+        return FALSE;
+    }
+
+    /* Store the listener sockets in the shared memory area for our children to see */
+    for (listener_num = 0, lr = ap_listeners; lr; lr = lr->next, listener_num++) {
+        apr_os_sock_get(&parent_info->listeners[listener_num].listen_fd, lr->sd);
+    }
+
+    /* Create mutex to prevent multiple child processes from detecting
+     * a connection with apr_poll()
+     */
+
+    rc = DosCreateMutexSem(NULL, &ap_mpm_accept_mutex, DC_SEM_SHARED, FALSE);
+
+    if (rc) {
+        ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
+                     "failure creating accept mutex, shutting down");
+        return FALSE;
+    }
+
+    ap_restart_time = apr_time_now();
+    parent_info->restart_time = ap_restart_time;
+    parent_info->accept_mutex = ap_mpm_accept_mutex;
+    apr_os_file_get(&parent_info->errorlog_fd, s->error_log);
+
+    /* Allocate shared memory for scoreboard */
+    if (ap_scoreboard_image == NULL) {
+        rc = DosAllocSharedMem((PPVOID)&ap_scoreboard_image, ap_scoreboard_fname,
+                               sizeof(scoreboard), PAG_COMMIT|PAG_READ|PAG_WRITE);
+
+        if (rc) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+                         "unable to allocate shared memory for scoreboard , exiting");
+            return FALSE;
+        }
+
+        memset(ap_scoreboard_image, 0, sizeof(scoreboard));
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
+               "%s configured -- resuming normal operations",
+               ap_get_server_version());
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf,
+               "Server built: %s", ap_get_server_built());
+
+    if (one_process) {
+        ap_scoreboard_image->parent[0].pid = getpid();
+        ap_mpm_child_main(pconf);
+        return FALSE;
+    }
+
+    while (!restart_pending && !shutdown_pending) {
+        RESULTCODES proc_rc;
+        PID child_pid;
+        int active_children = 0;
+
+        /* Count number of active children */
+        for (slot=0; slot < HARD_SERVER_LIMIT; slot++) {
+            active_children += ap_scoreboard_image->parent[slot].pid != 0 &&
+                !ap_scoreboard_image->parent[slot].quiescing;
+        }
+
+        /* Spawn children if needed */
+        for (slot=0; slot < HARD_SERVER_LIMIT && active_children < ap_daemons_to_start; slot++) {
+            if (ap_scoreboard_image->parent[slot].pid == 0) {
+                spawn_child(slot);
+                active_children++;
+            }
+        }
+
+        rc = DosWaitChild(DCWA_PROCESSTREE, DCWW_NOWAIT, &proc_rc, &child_pid, 0);
+
+        if (rc == 0) {
+            /* A child has terminated, remove its scoreboard entry & terminate if necessary */
+            for (slot=0; ap_scoreboard_image->parent[slot].pid != child_pid && slot < HARD_SERVER_LIMIT; slot++);
+
+            if (slot < HARD_SERVER_LIMIT) {
+                ap_scoreboard_image->parent[slot].pid = 0;
+                ap_scoreboard_image->parent[slot].quiescing = 0;
+
+                if (proc_rc.codeTerminate == TC_EXIT) {
+                    /* Child terminated normally, check its exit code and
+                     * terminate server if child indicates a fatal error
+                     */
+                    if (proc_rc.codeResult == APEXIT_CHILDFATAL)
+                        break;
+                }
+            }
+        } else if (rc == ERROR_CHILD_NOT_COMPLETE) {
+            /* No child exited, lets sleep for a while.... */
+            apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
+        }
+    }
+
+    /* Signal children to shut down, either gracefully or immediately */
+    for (slot=0; slot<HARD_SERVER_LIMIT; slot++) {
+      kill(ap_scoreboard_image->parent[slot].pid, is_graceful ? SIGHUP : SIGTERM);
+    }
+
+    DosFreeMem(parent_info);
+    return restart_pending;
+}
+
+
+
+static void spawn_child(int slot)
+{
+    PPIB ppib;
+    PTIB ptib;
+    char fail_module[100];
+    char progname[CCHMAXPATH];
+    RESULTCODES proc_rc;
+    ULONG rc;
+
+    ap_scoreboard_image->parent[slot].generation = ap_my_generation;
+    DosGetInfoBlocks(&ptib, &ppib);
+    DosQueryModuleName(ppib->pib_hmte, sizeof(progname), progname);
+    rc = DosExecPgm(fail_module, sizeof(fail_module), EXEC_ASYNCRESULT,
+                    ppib->pib_pchcmd, NULL, &proc_rc, progname);
+
+    if (rc) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+                     "error spawning child, slot %d", slot);
+    }
+
+    if (ap_max_daemons_limit < slot) {
+        ap_max_daemons_limit = slot;
+    }
+
+    ap_scoreboard_image->parent[slot].pid = proc_rc.codeTerminate;
+}
+
+
+
+/* Signal handling routines */
+
+static void sig_term(int sig)
+{
+    shutdown_pending = 1;
+}
+
+
+
+static void sig_restart(int sig)
+{
+    if (sig == SIGUSR1) {
+        is_graceful = 1;
+    }
+
+    restart_pending = 1;
+}
+
+
+
+static void set_signals()
+{
+    struct sigaction sa;
+
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;
+    sa.sa_handler = sig_term;
+
+    if (sigaction(SIGTERM, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
+
+    if (sigaction(SIGINT, &sa, NULL) < 0)
+        ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
+
+    sa.sa_handler = sig_restart;
+
+    if (sigaction(SIGHUP, &sa, NULL) < 0)
+        ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
+    if (sigaction(SIGUSR1, &sa, NULL) < 0)
+        ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGUSR1)");
+}
+
+
+
+/* Enquiry functions used get MPM status info */
+
+AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
+{
+    switch (query_code) {
+        case AP_MPMQ_MAX_DAEMON_USED:
+            *result = ap_max_daemons_limit;
+            return APR_SUCCESS;
+        case AP_MPMQ_IS_THREADED:
+            *result = AP_MPMQ_DYNAMIC;
+            return APR_SUCCESS;
+        case AP_MPMQ_IS_FORKED:
+            *result = AP_MPMQ_NOT_SUPPORTED;
+            return APR_SUCCESS;
+        case AP_MPMQ_HARD_LIMIT_DAEMONS:
+            *result = HARD_SERVER_LIMIT;
+            return APR_SUCCESS;
+        case AP_MPMQ_HARD_LIMIT_THREADS:
+            *result = HARD_THREAD_LIMIT;
+            return APR_SUCCESS;
+        case AP_MPMQ_MIN_SPARE_DEAMONS:
+            *result = 0;
+            return APR_SUCCESS;
+        case AP_MPMQ_MAX_SPARE_DAEMONS:
+            *result = 0;
+            return APR_SUCCESS;
+        case AP_MPMQ_MAX_REQUESTS_DEAMON:
+            *result = ap_max_requests_per_child;
+            return APR_SUCCESS; 
+    }
+    return APR_ENOTIMPL;
+} 
+
+
+
+int ap_graceful_stop_signalled(void)
+{
+    return is_graceful;
+}
+
+
+
+/* Configuration handling stuff */
+
+static void mpmt_os2_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
+{
+    one_process = !!ap_exists_config_define("ONE_PROCESS");
+    is_graceful = 0;
+    ap_listen_pre_config();
+    ap_daemons_to_start = DEFAULT_START_DAEMON;
+    ap_thread_limit = HARD_THREAD_LIMIT;
+    ap_pid_fname = DEFAULT_PIDLOG;
+    ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
+    ap_extended_status = 0;
+    ap_min_spare_threads = DEFAULT_MIN_SPARE_THREAD;
+    ap_max_spare_threads = DEFAULT_MAX_SPARE_THREAD;
+}
+
+
+
+static void mpmt_os2_hooks(apr_pool_t *p)
+{
+    ap_hook_pre_config(mpmt_os2_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+
+
+static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, const 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_spare_threads(cmd_parms *cmd, void *dummy,
+                                        const char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_min_spare_threads = atoi(arg);
+
+    if (ap_min_spare_threads <= 0) {
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                    "WARNING: detected MinSpareThreads set to non-positive.");
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                    "Resetting to 1 to avoid almost certain Apache failure.");
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                    "Please read the documentation.");
+       ap_min_spare_threads = 1;
+    }
+       
+    return NULL;
+}
+
+
+
+static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy,
+                                        const char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_max_spare_threads = atoi(arg);
+    return NULL;
+}
+
+
+
+static const command_rec mpmt_os2_cmds[] = {
+LISTEN_COMMANDS
+AP_INIT_TAKE1( "StartServers", set_daemons_to_start, NULL, RSRC_CONF, 
+  "Number of child processes launched at server startup" ),
+AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
+  "Minimum number of idle children, to handle request spikes"),
+AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
+  "Maximum number of idle children"),
+{ NULL }
+};
+
+module AP_MODULE_DECLARE_DATA mpm_mpmt_os2_module = {
+    MPM20_MODULE_STUFF,
+    NULL,                       /* hook to run before apache parses args */
+    NULL,                      /* create per-directory config structure */
+    NULL,                      /* merge per-directory config structures */
+    NULL,                      /* create per-server config structure */
+    NULL,                      /* merge per-server config structures */
+    mpmt_os2_cmds,             /* command apr_table_t */
+    mpmt_os2_hooks,            /* register_hooks */
+};
diff --git a/server/mpm/mpmt_os2/mpmt_os2_child.c b/server/mpm/mpmt_os2/mpmt_os2_child.c
new file mode 100644 (file)
index 0000000..36a96ca
--- /dev/null
@@ -0,0 +1,463 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * 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
+#define INCL_NOPMAPI
+#define INCL_DOS
+#define INCL_DOSERRORS
+
+#include "ap_config.h"
+#include "httpd.h"
+#include "mpm_default.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 "mpm.h"
+#include "ap_mpm.h"
+#include "ap_listen.h"
+#include "apr_portable.h"
+#include "mpm_common.h"
+#include "apr_strings.h"
+#include <os2.h>
+#include <process.h>
+
+typedef struct {
+    apr_pool_t *pconn;
+    apr_socket_t *conn_sd;
+} worker_args_t;
+
+#define WORKTYPE_CONN 0
+#define WORKTYPE_EXIT 1
+
+static apr_pool_t *pchild = NULL;
+static int child_slot;
+static int shutdown_pending = 0;
+extern int ap_my_generation;
+static int volatile is_graceful = 1;
+HEV shutdown_event; /* signaled when this child is shutting down */
+
+/* grab some MPM globals */
+extern int ap_min_spare_threads;
+extern int ap_max_spare_threads;
+extern HMTX ap_mpm_accept_mutex;
+
+static void worker_main(void *vpArg);
+static void clean_child_exit(int code);
+static void set_signals();
+static void server_maintenance(void *vpArg);
+
+
+static void clean_child_exit(int code)
+{
+    if (pchild) {
+       apr_pool_destroy(pchild);
+    }
+
+    exit(code);
+}
+
+
+
+void ap_mpm_child_main(apr_pool_t *pconf)
+{
+    ap_listen_rec *lr = NULL;
+    ap_listen_rec *first_lr = NULL;
+    int requests_this_child = 0;
+    apr_socket_t *sd = ap_listeners->sd;
+    int nsds, rv = 0;
+    unsigned long ulTimes;
+    int my_pid = getpid();
+    ULONG rc, c;
+    HQUEUE workq;
+    apr_pollfd_t *pollset;
+    int num_listeners;
+    TID server_maint_tid;
+
+    /* Stop Ctrl-C/Ctrl-Break signals going to child processes */
+    DosSetSignalExceptionFocus(0, &ulTimes);
+    set_signals();
+
+    /* Create pool for child */
+    apr_pool_create(&pchild, pconf);
+
+    /* Create an event semaphore used to trigger other threads to shutdown */
+    rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE);
+
+    if (rc) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+                     "unable to create shutdown semaphore, exiting");
+        clean_child_exit(APEXIT_CHILDFATAL);
+    }
+
+    /* Gain access to the scoreboard. */
+    rc = DosGetNamedSharedMem((PPVOID)&ap_scoreboard_image, ap_scoreboard_fname,
+                              PAG_READ|PAG_WRITE);
+
+    if (rc) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+                     "scoreboard not readable in child, exiting");
+        clean_child_exit(APEXIT_CHILDFATAL);
+    }
+
+    /* Gain access to the accpet mutex */
+    rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex);
+
+    if (rc) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+                     "accept mutex couldn't be accessed in child, exiting");
+        clean_child_exit(APEXIT_CHILDFATAL);
+    }
+
+    /* Find our pid in the scoreboard so we know what slot our parent allocated us */
+    for (child_slot = 0; ap_scoreboard_image->parent[child_slot].pid != my_pid && child_slot < HARD_SERVER_LIMIT; child_slot++);
+
+    if (child_slot == HARD_SERVER_LIMIT) {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, ap_server_conf,
+                     "child pid not found in scoreboard, exiting");
+        clean_child_exit(APEXIT_CHILDFATAL);
+    }
+
+    ap_my_generation = ap_scoreboard_image->parent[child_slot].generation;
+
+    /* Set up an OS/2 queue for passing connections & termination requests
+     * to worker threads
+     */
+    rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid));
+
+    if (rc) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+                     "unable to create work queue, exiting");
+        clean_child_exit(APEXIT_CHILDFATAL);
+    }
+
+    /* Create initial pool of worker threads */
+    for (c = 0; c < ap_min_spare_threads; c++) {
+//        ap_scoreboard_image->servers[child_slot][c].tid = _beginthread(worker_main, NULL, 128*1024, (void *)c);
+    }
+
+    /* Start maintenance thread */
+    server_maint_tid = _beginthread(server_maintenance, NULL, 32768, NULL);
+
+    /* Set up poll */
+    for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
+        num_listeners++;
+    }
+
+    apr_poll_setup(&pollset, num_listeners, pchild);
+
+    for (lr = ap_listeners; lr; lr = lr->next) {
+        apr_poll_socket_add(pollset, lr->sd, APR_POLLIN);
+    }
+
+    /* Main connection accept loop */
+    do {
+        apr_pool_t *pconn;
+        worker_args_t *worker_args;
+
+        apr_pool_create(&pconn, pchild);
+        worker_args = apr_palloc(pconn, sizeof(worker_args_t));
+        worker_args->pconn = pconn;
+
+        if (num_listeners == 1) {
+            rv = apr_accept(&worker_args->conn_sd, ap_listeners->sd, pconn);
+        } else {
+            rc = DosRequestMutexSem(ap_mpm_accept_mutex, SEM_INDEFINITE_WAIT);
+
+            if (shutdown_pending) {
+                DosReleaseMutexSem(ap_mpm_accept_mutex);
+                break;
+            }
+
+            rv = APR_FROM_OS_ERROR(rc);
+
+            if (rv == APR_SUCCESS) {
+                rv = apr_poll(pollset, &nsds, -1);
+                DosReleaseMutexSem(ap_mpm_accept_mutex);
+            }
+
+            if (rv == APR_SUCCESS) {
+                if (first_lr == NULL) {
+                    first_lr = ap_listeners;
+                }
+
+                lr = first_lr;
+
+                do {
+                    apr_int16_t event;
+
+                    apr_poll_revents_get(&event, lr->sd, pollset);
+
+                    if (event == APR_POLLIN) {
+                        apr_sockaddr_t *sa;
+                        apr_port_t port;
+                        apr_socket_addr_get(&sa, APR_LOCAL, lr->sd);
+                        apr_sockaddr_port_get(&port, sa);
+                        first_lr = lr->next;
+                        break;
+                    }
+                    lr = lr->next;
+
+                    if (!lr) {
+                        lr = ap_listeners;
+                    }
+                } while (lr != first_lr);
+
+                if (lr == first_lr) {
+                    continue;
+                }
+
+                sd = lr->sd;
+                rv = apr_accept(&worker_args->conn_sd, sd, pconn);
+            }
+        }
+
+        if (rv != APR_SUCCESS) {
+            if (!APR_STATUS_IS_EINTR(rv)) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
+                             "apr_accept");
+                clean_child_exit(APEXIT_CHILDFATAL);
+            }
+        } else {
+            DosWriteQueue(workq, WORKTYPE_CONN, sizeof(worker_args_t), worker_args, 0);
+            requests_this_child++;
+        }
+
+        if (ap_max_requests_per_child != 0 && requests_this_child >= ap_max_requests_per_child)
+            break;
+    } while (!shutdown_pending && ap_my_generation == ap_scoreboard_image->global.running_generation);
+
+    ap_scoreboard_image->parent[child_slot].quiescing = 1;
+    DosPostEventSem(shutdown_event);
+    DosWaitThread(&server_maint_tid, DCWW_WAIT);
+
+    if (is_graceful) {
+        char someleft;
+
+        /* tell our worker threads to exit */
+        for (c=0; c<HARD_THREAD_LIMIT; c++) {
+            if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
+                DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
+            }
+        }
+
+        do {
+            someleft = 0;
+
+            for (c=0; c<HARD_THREAD_LIMIT; c++) {
+                if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
+                    someleft = 1;
+                    DosSleep(1000);
+                    break;
+                }
+            }
+        } while (someleft);
+    } else {
+        DosPurgeQueue(workq);
+
+        for (c=0; c<HARD_THREAD_LIMIT; c++) {
+            if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) {
+                DosKillThread(ap_scoreboard_image->servers[child_slot][c].tid);
+            }
+        }
+    }
+
+    apr_pool_destroy(pchild);
+}
+
+
+
+void add_worker()
+{
+    int thread_slot;
+
+    /* Find a free thread slot */
+    for (thread_slot=0; thread_slot < HARD_THREAD_LIMIT; thread_slot++) {
+        if (ap_scoreboard_image->servers[child_slot][thread_slot].status == SERVER_DEAD) {
+            ap_scoreboard_image->servers[child_slot][thread_slot].status = SERVER_STARTING;
+            ap_scoreboard_image->servers[child_slot][thread_slot].tid =
+                _beginthread(worker_main, NULL, 128*1024, (void *)thread_slot);
+            break;
+        }
+    }
+}
+
+
+
+static void worker_main(void *vpArg)
+{
+    long conn_id;
+    conn_rec *current_conn;
+    apr_pool_t *pconn;
+    worker_args_t *worker_args;
+    HQUEUE workq;
+    PID owner;
+    int rc;
+    REQUESTDATA rd;
+    ULONG len;
+    BYTE priority;
+    int thread_slot = (int)vpArg;
+
+    rc = DosOpenQueue(&owner, &workq,
+                      apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
+
+    if (rc) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+                     "unable to open work queue, exiting");
+        ap_scoreboard_image->servers[child_slot][thread_slot].tid = 0;
+    }
+
+    conn_id = AP_ID_FROM_CHILD_THREAD(child_slot, thread_slot);
+    ap_update_child_status(child_slot, thread_slot, SERVER_READY, NULL);
+
+    while (rc = DosReadQueue(workq, &rd, &len, (PPVOID)&worker_args, 0, DCWW_WAIT, &priority, NULLHANDLE),
+           rc == 0 && rd.ulData != WORKTYPE_EXIT) {
+        pconn = worker_args->pconn;
+        ap_sock_disable_nagle(worker_args->conn_sd);
+        current_conn = ap_new_connection(pconn, ap_server_conf, worker_args->conn_sd, conn_id);
+
+        if (current_conn) {
+            ap_process_connection(current_conn);
+            ap_lingering_close(current_conn);
+        }
+
+        apr_pool_destroy(pconn);
+        ap_update_child_status(child_slot, thread_slot, SERVER_READY, NULL);
+    }
+
+    ap_update_child_status(child_slot, thread_slot, SERVER_DEAD, NULL);
+}
+
+
+
+static void server_maintenance(void *vpArg)
+{
+    int num_idle, num_needed;
+    ULONG num_pending = 0;
+    int threadnum;
+    HQUEUE workq;
+    ULONG rc;
+    PID owner;
+
+    rc = DosOpenQueue(&owner, &workq,
+                      apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
+
+    if (rc) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+                     "unable to open work queue in maintenance thread");
+        return;
+    }
+
+    do {
+        for (num_idle=0, threadnum=0; threadnum < HARD_THREAD_LIMIT; threadnum++) {
+            num_idle += ap_scoreboard_image->servers[child_slot][threadnum].status == SERVER_READY;
+        }
+
+        DosQueryQueue(workq, &num_pending);
+        num_needed = ap_min_spare_threads - num_idle + num_pending;
+
+        if (num_needed > 0) {
+            for (threadnum=0; threadnum < num_needed; threadnum++) {
+                add_worker();
+            }
+        }
+
+        if (num_idle - num_pending > ap_max_spare_threads) {
+            DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0);
+        }
+    } while (DosWaitEventSem(shutdown_event, 500) == ERROR_TIMEOUT);
+}
+
+
+
+/* Signal handling routines */
+
+static void sig_term(int sig)
+{
+    shutdown_pending = 1;
+    is_graceful = 0;
+    signal(SIGTERM, SIG_DFL);
+}
+
+
+
+static void sig_hup(int sig)
+{
+    shutdown_pending = 1;
+    is_graceful = 1;
+}
+
+
+
+static void set_signals()
+{
+    struct sigaction sa;
+
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;
+    sa.sa_handler = sig_term;
+
+    if (sigaction(SIGTERM, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
+
+    sa.sa_handler = sig_hup;
+
+    if (sigaction(SIGHUP, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
+}
diff --git a/server/mpm/worker/.cvsignore b/server/mpm/worker/.cvsignore
new file mode 100644 (file)
index 0000000..84df257
--- /dev/null
@@ -0,0 +1,5 @@
+.deps
+.libs
+*.lo
+*.la
+Makefile
diff --git a/server/mpm/worker/Makefile.in b/server/mpm/worker/Makefile.in
new file mode 100644 (file)
index 0000000..64a0243
--- /dev/null
@@ -0,0 +1,5 @@
+
+LTLIBRARY_NAME    = libworker.la
+LTLIBRARY_SOURCES = worker.c fdqueue.c
+
+include $(top_srcdir)/build/ltlib.mk
diff --git a/server/mpm/worker/config5.m4 b/server/mpm/worker/config5.m4
new file mode 100644 (file)
index 0000000..52ab50e
--- /dev/null
@@ -0,0 +1,5 @@
+dnl ## XXX - Need a more thorough check of the proper flags to use
+
+if test "$MPM_NAME" = "worker" ; then
+    APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
+fi
diff --git a/server/mpm/worker/fdqueue.c b/server/mpm/worker/fdqueue.c
new file mode 100644 (file)
index 0000000..147a3b7
--- /dev/null
@@ -0,0 +1,161 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "fdqueue.h"
+#include "apr_pools.h"
+
+/* Assumption: queue itself is allocated by the user */
+/* Assumption: increment and decrement are atomic on int */
+
+int ap_queue_size(FDQueue *queue) {
+    return ((queue->tail - queue->head + queue->bounds) % queue->bounds);
+}
+
+int ap_queue_full(FDQueue *queue) {
+    return(queue->blanks <= 0);
+}
+
+int ap_block_on_queue(FDQueue *queue) {
+#if 0
+    if (pthread_mutex_lock(&queue->one_big_mutex) != 0) {
+        return FD_QUEUE_FAILURE;
+    }
+#endif
+    if (ap_queue_full(queue)) {
+        pthread_cond_wait(&queue->not_full, &queue->one_big_mutex);
+    }
+#if 0
+    if (pthread_mutex_unlock(&queue->one_big_mutex) != 0) {
+        return FD_QUEUE_FAILURE;
+    }
+#endif
+    return FD_QUEUE_SUCCESS;
+}
+
+static int increase_blanks(FDQueue *queue) {
+    queue->blanks++;
+    return FD_QUEUE_SUCCESS;
+}
+
+static apr_status_t ap_queue_destroy(void *data) {
+    FDQueue *queue = data;
+    /* Ignore errors here, we can't do anything about them anyway */
+    pthread_cond_destroy(&queue->not_empty);
+    pthread_cond_destroy(&queue->not_full);
+    pthread_mutex_destroy(&queue->one_big_mutex);
+    return FD_QUEUE_SUCCESS;
+}
+
+int ap_queue_init(FDQueue *queue, int queue_capacity, apr_pool_t *a) {
+    int i;
+    int bounds = queue_capacity + 1;
+    pthread_mutex_init(&queue->one_big_mutex, NULL);
+    pthread_cond_init(&queue->not_empty, NULL);
+    pthread_cond_init(&queue->not_full, NULL);
+    queue->head = queue->tail = 0;
+    queue->data = apr_palloc(a, bounds * sizeof(FDQueueElement));
+    queue->bounds = bounds;
+    queue->blanks = queue_capacity;
+    apr_pool_cleanup_register(a, queue, ap_queue_destroy, apr_pool_cleanup_null);
+    for (i=0; i < bounds; ++i)
+        queue->data[i].sd = NULL;
+    return FD_QUEUE_SUCCESS;
+}
+
+int ap_queue_push(FDQueue *queue, apr_socket_t *sd, apr_pool_t *p) {
+    queue->data[queue->tail].sd = sd;
+    queue->data[queue->tail].p  = p;
+    queue->tail = (queue->tail + 1) % queue->bounds;
+    queue->blanks--;
+    pthread_cond_signal(&queue->not_empty);
+#if 0
+    if (queue->head == (queue->tail + 1) % queue->bounds) {
+#endif
+    if (ap_queue_full(queue)) {
+        pthread_cond_wait(&queue->not_full, &queue->one_big_mutex);
+    }
+    return FD_QUEUE_SUCCESS;
+}
+
+apr_status_t ap_queue_pop(FDQueue *queue, apr_socket_t **sd, apr_pool_t **p, int block_if_empty) {
+    increase_blanks(queue);
+    /* We have just removed one from the queue.  By definition, it is
+     * no longer full.  We can ALWAYS signal the listener thread at
+     * this point.  However, the original code didn't do it this way,
+     * so I am leaving the original code in, just commented out.  BTW,
+     * originally, the increase_blanks wasn't in this function either.
+     *
+     if (queue->blanks > 0) {
+     */
+    pthread_cond_signal(&queue->not_full);
+
+    /*    }    */
+    if (queue->head == queue->tail) {
+        if (block_if_empty) {
+            pthread_cond_wait(&queue->not_empty, &queue->one_big_mutex);
+        }
+    } 
+    
+    *sd = queue->data[queue->head].sd;
+    *p  = queue->data[queue->head].p;
+    queue->data[queue->head].sd = NULL;
+    if (*sd != NULL) {
+        queue->head = (queue->head + 1) % queue->bounds;
+    }
+    return APR_SUCCESS;
+}
diff --git a/server/mpm/worker/fdqueue.h b/server/mpm/worker/fdqueue.h
new file mode 100644 (file)
index 0000000..b1cab2d
--- /dev/null
@@ -0,0 +1,95 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef FDQUEUE_H
+#define FDQUEUE_H
+#include "httpd.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define FD_QUEUE_SUCCESS 0
+#define FD_QUEUE_FAILURE -1 /* Needs to be an invalid file descriptor because
+                               of queue_pop semantics */
+
+typedef struct fd_queue_elem {
+    apr_socket_t *sd;
+    apr_pool_t *p;
+} FDQueueElement;
+
+typedef struct fd_queue {
+    int head;
+    int tail;
+    FDQueueElement *data;
+    int bounds;
+    int blanks;
+    pthread_mutex_t one_big_mutex;
+    pthread_cond_t not_empty;
+    pthread_cond_t not_full;
+} FDQueue;
+
+int ap_queue_init(FDQueue *queue, int queue_size, apr_pool_t *a);
+int ap_queue_push(FDQueue *queue, apr_socket_t *sd, apr_pool_t *p);
+apr_status_t ap_queue_pop(FDQueue *queue, apr_socket_t **sd, apr_pool_t **p, int block_if_empty);
+int ap_queue_size(FDQueue *queue);
+int ap_queue_full(FDQueue *queue);
+int ap_block_on_queue(FDQueue *queue);
+
+#endif /* FDQUEUE_H */
diff --git a/server/mpm/worker/mpm.h b/server/mpm/worker/mpm.h
new file mode 100644 (file)
index 0000000..23ed27c
--- /dev/null
@@ -0,0 +1,78 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+#include "scoreboard.h"
+#include "unixd.h"
+
+#ifndef APACHE_MPM_WORKER_H
+#define APACHE_MPM_WORKER_H
+
+#define WORKER_MPM
+
+#define MPM_NAME "Worker"
+
+#define AP_MPM_NEEDS_RECLAIM_CHILD_PROCESSES 1
+#define MPM_SYNC_CHILD_TABLE() (ap_sync_scoreboard_image())
+#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
+#define MPM_NOTE_CHILD_KILLED(i) (MPM_CHILD_PID(i) = 0)
+
+extern int ap_threads_per_child;
+extern int ap_max_daemons_limit;
+extern server_rec *ap_server_conf;
+extern char ap_coredump_dir[MAX_STRING_LEN];
+
+#endif /* APACHE_MPM_WORKER_H */
diff --git a/server/mpm/worker/mpm_default.h b/server/mpm/worker/mpm_default.h
new file mode 100644 (file)
index 0000000..84c1304
--- /dev/null
@@ -0,0 +1,154 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_MPM_DEFAULT_H
+#define APACHE_MPM_DEFAULT_H
+
+#define AP_ID_FROM_CHILD_THREAD(c, t)    ((c * HARD_THREAD_LIMIT) + t)
+#define AP_CHILD_THREAD_FROM_ID(i)    (i / HARD_THREAD_LIMIT), (i % HARD_THREAD_LIMIT)
+
+/* 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 3
+#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 3
+#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.
+ */
+#ifdef NO_THREADS
+#define HARD_SERVER_LIMIT 256
+#endif
+#ifndef HARD_SERVER_LIMIT
+#define HARD_SERVER_LIMIT 8 
+#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.
+ */
+#ifdef NO_THREADS
+#define HARD_THREAD_LIMIT 1
+#endif
+#ifndef HARD_THREAD_LIMIT
+#define HARD_THREAD_LIMIT 64 
+#endif
+
+#ifdef NO_THREADS
+#define DEFAULT_THREADS_PER_CHILD 1
+#endif
+#ifndef DEFAULT_THREADS_PER_CHILD
+#define DEFAULT_THREADS_PER_CHILD 25
+#endif
+
+/* File used for accept locking, when we use a file */
+#ifndef DEFAULT_LOCKFILE
+#define DEFAULT_LOCKFILE "logs/accept.lock"
+#endif
+
+/* Scoreboard file, if there is one */
+#ifndef DEFAULT_SCOREBOARD
+#define DEFAULT_SCOREBOARD "logs/apache_runtime_status"
+#endif
+
+/* Where the main/parent process's pid is logged */
+#ifndef DEFAULT_PIDLOG
+#define DEFAULT_PIDLOG "logs/httpd.pid"
+#endif
+
+/*
+ * Interval, in microseconds, between scoreboard maintenance.
+ */
+#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.
+ */
+#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
+#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
+#endif
+
+#endif /* AP_MPM_DEFAULT_H */
diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c
new file mode 100644 (file)
index 0000000..9f969af
--- /dev/null
@@ -0,0 +1,1660 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "apr.h"
+#include "apr_portable.h"
+#include "apr_strings.h"
+#include "apr_file_io.h"
+#include "apr_thread_proc.h"
+#include "apr_signal.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if APR_HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if APR_HAVE_SYS_WAIT_H
+#include <sys/wait.h> 
+#endif
+#ifdef HAVE_SYS_PROCESSOR_H
+#include <sys/processor.h> /* for bindprocessor() */
+#endif
+
+#if !APR_HAS_THREADS
+#error The Worker MPM requires APR threads, but they are unavailable.
+#endif
+
+#define CORE_PRIVATE 
+#include "ap_config.h"
+#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 "unixd.h"
+#include "mpm_common.h"
+#include "ap_listen.h"
+#include "scoreboard.h" 
+#include "fdqueue.h"
+
+#include <signal.h>
+#include <limits.h>             /* for INT_MAX */
+
+/*
+ * Actual definitions of config globals
+ */
+
+int ap_threads_per_child=0;         /* Worker threads per child */
+static int ap_max_requests_per_child=0;
+static const char *ap_pid_fname=NULL;
+static int ap_daemons_to_start=0;
+static int min_spare_threads=0;
+static int max_spare_threads=0;
+static int ap_daemons_limit=0;
+static int dying = 0;
+static int workers_may_exit = 0;
+static int requests_this_child;
+static int num_listensocks = 0;
+static apr_socket_t **listensocks;
+static FDQueue *worker_queue;
+
+/* The structure used to pass unique initialization info to each thread */
+typedef struct {
+    int pid;
+    int tid;
+    int sd;
+    apr_pool_t *tpool; /* "pthread" would be confusing */
+} proc_info;
+
+/* Structure used to pass information to the thread responsible for 
+ * creating the rest of the threads.
+ */
+typedef struct {
+    apr_thread_t **threads;
+    int child_num_arg;
+    apr_threadattr_t *threadattr;
+} thread_starter;
+
+/*
+ * The max child slot ever assigned, preserved across restarts.  Necessary
+ * to deal with MaxClients changes across SIGWINCH restarts.  We use this
+ * value to optimize routines that have to scan the entire scoreboard.
+ */
+int ap_max_daemons_limit = -1;
+
+char ap_coredump_dir[MAX_STRING_LEN];
+
+static apr_file_t *pipe_of_death_in = NULL;
+static apr_file_t *pipe_of_death_out = NULL;
+static apr_lock_t *pipe_of_death_mutex;   /* insures that a child process only
+                                             consumes one character */
+
+/* *Non*-shared http_main globals... */
+
+server_rec *ap_server_conf;
+
+/* 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
+
+static apr_pool_t *pconf;              /* Pool for config stuff */
+static apr_pool_t *pchild;             /* Pool for httpd child stuff */
+
+static pid_t ap_my_pid; /* Linux getpid() doesn't work except in main 
+                           thread. Use this instead */
+/* Keep track of the number of worker threads currently active */
+static int worker_thread_count;
+static apr_lock_t *worker_thread_count_mutex;
+
+/* Locks for accept serialization */
+static apr_lock_t *accept_mutex;
+static apr_lockmech_e_np accept_lock_mech = APR_LOCK_DEFAULT;
+static const char *lock_fname;
+
+#ifdef NO_SERIALIZED_ACCEPT
+#define SAFE_ACCEPT(stmt) APR_SUCCESS
+#else
+#define SAFE_ACCEPT(stmt) (stmt)
+#endif
+
+AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
+{
+    switch(query_code){
+        case AP_MPMQ_MAX_DAEMON_USED:
+            *result = ap_max_daemons_limit;
+            return APR_SUCCESS;
+        case AP_MPMQ_IS_THREADED:
+            *result = AP_MPMQ_STATIC;
+            return APR_SUCCESS;
+        case AP_MPMQ_IS_FORKED:
+            *result = AP_MPMQ_DYNAMIC;
+            return APR_SUCCESS;
+        case AP_MPMQ_HARD_LIMIT_DAEMONS:
+            *result = HARD_SERVER_LIMIT;
+            return APR_SUCCESS;
+        case AP_MPMQ_HARD_LIMIT_THREADS:
+            *result = HARD_THREAD_LIMIT;
+            return APR_SUCCESS;
+        case AP_MPMQ_MAX_THREADS:
+            *result = ap_threads_per_child;
+            return APR_SUCCESS;
+        case AP_MPMQ_MIN_SPARE_DEAMONS:
+            *result = 0;
+            return APR_SUCCESS;
+        case AP_MPMQ_MIN_SPARE_THREADS:    
+            *result = min_spare_threads;
+            return APR_SUCCESS;
+        case AP_MPMQ_MAX_SPARE_DAEMONS:
+            *result = 0;
+            return APR_SUCCESS;
+        case AP_MPMQ_MAX_SPARE_THREADS:
+            *result = max_spare_threads;
+            return APR_SUCCESS;
+        case AP_MPMQ_MAX_REQUESTS_DEAMON:
+            *result = ap_max_requests_per_child;
+            return APR_SUCCESS;
+        case AP_MPMQ_MAX_DAEMONS:
+            *result = ap_daemons_limit;
+            return APR_SUCCESS;
+    }
+    return APR_ENOTIMPL;
+}
+
+/* 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) {
+       apr_pool_destroy(pchild);
+    }
+    exit(code);
+}
+
+/* handle all varieties of core dumping signals */
+static void sig_coredump(int sig)
+{
+    chdir(ap_coredump_dir);
+    apr_signal(sig, SIG_DFL);
+    kill(ap_my_pid, 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.
+     */
+}
+
+static void just_die(int sig)
+{
+    clean_child_exit(0);
+}
+
+/*****************************************************************
+ * Connection structures and accounting...
+ */
+
+/* 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;
+
+/*
+ * 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.
+ */
+
+static 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 */
+static 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;
+    if (is_graceful) {
+        apr_pool_cleanup_kill(pconf, NULL, ap_cleanup_scoreboard);
+    }
+}
+
+static void sig_term(int sig)
+{
+    ap_start_shutdown();
+}
+
+static void restart(int sig)
+{
+    ap_start_restart(sig == SIGWINCH);
+}
+
+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, errno, ap_server_conf, "sigaction(SIGSEGV)");
+#ifdef SIGBUS
+       if (sigaction(SIGBUS, &sa, NULL) < 0)
+           ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGBUS)");
+#endif
+#ifdef SIGABORT
+       if (sigaction(SIGABORT, &sa, NULL) < 0)
+           ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABORT)");
+#endif
+#ifdef SIGABRT
+       if (sigaction(SIGABRT, &sa, NULL) < 0)
+           ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABRT)");
+#endif
+#ifdef SIGILL
+       if (sigaction(SIGILL, &sa, NULL) < 0)
+           ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_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, errno, ap_server_conf, "sigaction(SIGTERM)");
+#ifdef SIGINT
+    if (sigaction(SIGINT, &sa, NULL) < 0)
+        ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_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, errno, ap_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, errno, ap_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, errno, ap_server_conf, "sigaction(SIGPIPE)");
+#endif
+
+    /* we want to ignore HUPs and WINCH while we're busy processing one */
+    sigaddset(&sa.sa_mask, SIGHUP);
+    sigaddset(&sa.sa_mask, SIGWINCH);
+    sa.sa_handler = restart;
+    if (sigaction(SIGHUP, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
+    if (sigaction(SIGWINCH, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGWINCH)");
+#else
+    if (!one_process) {
+       apr_signal(SIGSEGV, sig_coredump);
+#ifdef SIGBUS
+       apr_signal(SIGBUS, sig_coredump);
+#endif /* SIGBUS */
+#ifdef SIGABORT
+       apr_signal(SIGABORT, sig_coredump);
+#endif /* SIGABORT */
+#ifdef SIGABRT
+       apr_signal(SIGABRT, sig_coredump);
+#endif /* SIGABRT */
+#ifdef SIGILL
+       apr_signal(SIGILL, sig_coredump);
+#endif /* SIGILL */
+#ifdef SIGXCPU
+       apr_signal(SIGXCPU, SIG_DFL);
+#endif /* SIGXCPU */
+#ifdef SIGXFSZ
+       apr_signal(SIGXFSZ, SIG_DFL);
+#endif /* SIGXFSZ */
+    }
+
+    apr_signal(SIGTERM, sig_term);
+#ifdef SIGHUP
+    apr_signal(SIGHUP, restart);
+#endif /* SIGHUP */
+#ifdef SIGWINCH
+    apr_signal(SIGWINCH, restart);
+#endif /* SIGWINCH */
+#ifdef SIGPIPE
+    apr_signal(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+
+#endif
+}
+
+/*****************************************************************
+ * Here follows a long bunch of generic server bookkeeping stuff...
+ */
+
+int ap_graceful_stop_signalled(void)
+{
+    /* XXX - Does this really work? - Manoj */
+    return is_graceful;
+}
+
+/*****************************************************************
+ * Child process main loop.
+ */
+
+static void process_socket(apr_pool_t *p, apr_socket_t *sock, int my_child_num, int my_thread_num)
+{
+    conn_rec *current_conn;
+    long conn_id = AP_ID_FROM_CHILD_THREAD(my_child_num, my_thread_num);
+    int csd;
+
+    (void) apr_os_sock_get(&csd, sock);
+
+    if (csd >= FD_SETSIZE) {
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, NULL,
+                     "new file descriptor %d is too large; you probably need "
+                     "to rebuild Apache with a larger FD_SETSIZE "
+                     "(currently %d)", 
+                     csd, FD_SETSIZE);
+        apr_socket_close(sock);
+        return;
+    }
+
+    ap_sock_disable_nagle(sock);
+
+    current_conn = ap_new_connection(p, ap_server_conf, sock, conn_id);
+    if (current_conn) {
+        ap_process_connection(current_conn);
+        ap_lingering_close(current_conn);
+    }
+}
+
+/* requests_this_child has gone to zero or below.  See if the admin coded
+   "MaxRequestsPerChild 0", and keep going in that case.  Doing it this way
+   simplifies the hot path in worker_thread */
+
+static void check_infinite_requests(void)
+{
+    if (ap_max_requests_per_child) {
+        workers_may_exit = 1;              
+    }
+    else {
+        /* wow! if you're executing this code, you may have set a record.
+         * either this child process has served over 2 billion requests, or
+         * you're running a threaded 2.0 on a 16 bit machine.  
+         *
+         * I'll buy pizza and beers at Apachecon for the first person to do
+         * the former without cheating (dorking with INT_MAX, or running with
+         * uncommitted performance patches, for example).    
+         *
+         * for the latter case, you probably deserve a beer too.   Greg Ames
+         */
+            
+        requests_this_child = INT_MAX;      /* keep going */ 
+    }
+}
+
+/* Sets workers_may_exit if we received a character on the pipe_of_death */
+static void check_pipe_of_death(void)
+{
+    apr_lock_acquire(pipe_of_death_mutex);
+    if (!workers_may_exit) {
+        apr_status_t ret;
+        char pipe_read_char;
+       apr_size_t n = 1;
+
+        ret = apr_recv(listensocks[0], &pipe_read_char, &n);
+        if (APR_STATUS_IS_EAGAIN(ret)) {
+            /* It lost the lottery. It must continue to suffer
+             * through a life of servitude. */
+        }
+        else {
+            /* It won the lottery (or something else is very
+             * wrong). Embrace death with open arms. */
+            workers_may_exit = 1;
+        }
+    }
+    apr_lock_release(pipe_of_death_mutex);
+}
+
+static void *listener_thread(apr_thread_t *thd, void * dummy)
+{
+    proc_info * ti = dummy;
+    int process_slot = ti->pid;
+    int thread_slot = ti->tid;
+    apr_pool_t *tpool = ti->tpool;
+    apr_socket_t *csd = NULL;
+    apr_pool_t *ptrans;                /* Pool for per-transaction stuff */
+    apr_socket_t *sd = NULL;
+    int n;
+    int curr_pollfd, last_pollfd = 0;
+    apr_pollfd_t *pollset;
+    apr_status_t rv;
+
+    free(ti);
+
+    apr_pool_create(&ptrans, tpool);
+
+    apr_lock_acquire(worker_thread_count_mutex);
+    worker_thread_count++;
+    apr_lock_release(worker_thread_count_mutex);
+
+    apr_poll_setup(&pollset, num_listensocks+1, tpool);
+    for(n=0 ; n <= num_listensocks ; ++n)
+       apr_poll_socket_add(pollset, listensocks[n], APR_POLLIN);
+
+    worker_queue = apr_pcalloc(pchild, sizeof(*worker_queue));
+    ap_queue_init(worker_queue, ap_threads_per_child, pchild);
+
+    /* TODO: Switch to a system where threads reuse the results from earlier
+       poll calls - manoj */
+    while (1) {
+        if (requests_this_child <= 0) {
+            check_infinite_requests();
+        }
+        if (workers_may_exit) break;
+
+        if ((rv = SAFE_ACCEPT(apr_lock_acquire(accept_mutex)))
+            != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                         "apr_lock_acquire failed. Attempting to shutdown "
+                         "process gracefully.");
+            workers_may_exit = 1;
+        }
+
+        while (!workers_may_exit) {
+           apr_status_t ret;
+           apr_int16_t event;
+
+            ret = apr_poll(pollset, &n, -1);
+            if (ret != APR_SUCCESS) {
+                if (APR_STATUS_IS_EINTR(ret)) {
+                    continue;
+                }
+
+                /* apr_poll() will only return errors in catastrophic
+                 * circumstances. Let's try exiting gracefully, for now. */
+                ap_log_error(APLOG_MARK, APLOG_ERR, ret, (const server_rec *)
+                             ap_server_conf, "apr_poll: (listen)");
+                workers_may_exit = 1;
+            }
+
+            if (workers_may_exit) break;
+
+           apr_poll_revents_get(&event, listensocks[0], pollset);
+            if (event & APR_POLLIN) {
+                /* A process got a signal on the shutdown pipe. Check if we're
+                 * the lucky process to die. */
+                check_pipe_of_death();
+                continue;
+            }
+
+            if (num_listensocks == 1) {
+                sd = ap_listeners->sd;
+                goto got_fd;
+            }
+            else {
+                /* find a listener */
+                curr_pollfd = last_pollfd;
+                do {
+                    curr_pollfd++;
+                    if (curr_pollfd > num_listensocks) {
+                        curr_pollfd = 1;
+                    }
+                    /* XXX: Should we check for POLLERR? */
+                   apr_poll_revents_get(&event, listensocks[curr_pollfd], pollset);
+                    if (event & APR_POLLIN) {
+                        last_pollfd = curr_pollfd;
+                       sd=listensocks[curr_pollfd];
+                        goto got_fd;
+                    }
+                } while (curr_pollfd != last_pollfd);
+            }
+        }
+    got_fd:
+        if (!workers_may_exit) {
+            if ((rv = apr_accept(&csd, sd, ptrans)) != APR_SUCCESS) {
+                csd = NULL;
+                ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, 
+                             "apr_accept");
+            }
+            if ((rv = SAFE_ACCEPT(apr_lock_release(accept_mutex)))
+                != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                             "apr_lock_release failed. Attempting to shutdown "
+                             "process gracefully.");
+                workers_may_exit = 1;
+            }
+            if (csd != NULL) {
+                ap_queue_push(worker_queue, csd, ptrans);
+                ap_block_on_queue(worker_queue);
+            }
+        }
+        else {
+            if ((rv = SAFE_ACCEPT(apr_lock_release(accept_mutex)))
+                != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                             "apr_lock_release failed. Attempting to shutdown "
+                             "process gracefully.");
+                workers_may_exit = 1;
+            }
+            break;
+        }
+    }
+
+    apr_pool_destroy(tpool);
+    ap_update_child_status(process_slot, thread_slot, (dying) ? SERVER_DEAD : SERVER_GRACEFUL,
+        (request_rec *) NULL);
+    dying = 1;
+    apr_lock_acquire(worker_thread_count_mutex);
+    worker_thread_count--;
+    if (worker_thread_count == 0) {
+        /* All the threads have exited, now finish the shutdown process
+         * by signalling the sigwait thread */
+        kill(ap_my_pid, SIGTERM);
+    }
+    apr_lock_release(worker_thread_count_mutex);
+
+    return NULL;
+}
+
+static void *worker_thread(apr_thread_t *thd, void * dummy)
+{
+    proc_info * ti = dummy;
+    int process_slot = ti->pid;
+    int thread_slot = ti->tid;
+    apr_pool_t *tpool = ti->tpool;
+    apr_socket_t *csd = NULL;
+    apr_pool_t *ptrans;                /* Pool for per-transaction stuff */
+    apr_socket_t *sd = NULL;
+    int n;
+    int curr_pollfd, last_pollfd = 0;
+    apr_pollfd_t *pollset;
+    apr_status_t rv;
+
+    free(ti);
+
+    while (!workers_may_exit) {
+        ap_queue_pop(worker_queue, &csd, &ptrans, 1);
+        process_socket(ptrans, csd, process_slot, thread_slot);
+        requests_this_child--;
+        apr_pool_clear(ptrans);
+    }
+
+    apr_pool_destroy(tpool);
+    ap_update_child_status(process_slot, thread_slot, (dying) ? SERVER_DEAD : SERVER_GRACEFUL,
+        (request_rec *) NULL);
+    apr_lock_acquire(worker_thread_count_mutex);
+    if (!dying) {
+        /* this is the first thread to exit */
+        if (ap_my_pid == ap_scoreboard_image->parent[process_slot].pid) {
+            /* tell the parent that it may use this scoreboard slot */
+            ap_scoreboard_image->parent[process_slot].quiescing = 1;
+        }   
+        dying = 1;
+    }
+    worker_thread_count--;
+    if (worker_thread_count == 0) {
+        /* All the threads have exited, now finish the shutdown process
+         * by signalling the sigwait thread */
+        kill(ap_my_pid, SIGTERM);
+    }
+    apr_lock_release(worker_thread_count_mutex);
+
+    return NULL;
+}
+
+static int check_signal(int signum)
+{
+    switch (signum) {
+        case SIGTERM:
+        case SIGINT:
+            return 1;
+    }                                                                           
+    return 0;
+}
+
+static void *start_threads(apr_thread_t *thd, void * dummy)
+{
+    thread_starter *ts = dummy;
+    apr_thread_t **threads = ts->threads;
+    apr_threadattr_t *thread_attr = ts->threadattr;
+    int child_num_arg = ts->child_num_arg;
+    int i;
+    int my_child_num = child_num_arg;
+    proc_info *my_info = NULL;
+    apr_status_t rv;
+    int threads_created = 0;
+    apr_thread_t *listener;
+
+    while (1) {
+        my_info = (proc_info *)malloc(sizeof(proc_info));
+        my_info->pid = my_child_num;
+        my_info->tid = i;
+        my_info->sd = 0;
+        apr_pool_create(&my_info->tpool, pchild);
+       apr_thread_create(&listener, thread_attr, listener_thread, my_info, pchild);
+        for (i=0; i < ap_threads_per_child; i++) {
+            int status = ap_scoreboard_image->servers[child_num_arg][i].status;
+
+            if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
+                continue;
+            }
+
+           my_info = (proc_info *)malloc(sizeof(proc_info));
+            if (my_info == NULL) {
+                ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
+                            "malloc: out of memory");
+                clean_child_exit(APEXIT_CHILDFATAL);
+            }
+           my_info->pid = my_child_num;
+            my_info->tid = i;
+           my_info->sd = 0;
+           apr_pool_create(&my_info->tpool, pchild);
+       
+           /* We are creating threads right now */
+           (void) ap_update_child_status(my_child_num, i, SERVER_STARTING, 
+                                         (request_rec *) NULL);
+            /* We let each thread update its own scoreboard entry.  This is
+             * done because it lets us deal with tid better.
+            */
+           if ((rv = apr_thread_create(&threads[i], thread_attr, worker_thread, my_info, pchild))) {
+               ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
+                            "apr_thread_create: unable to create worker thread");
+                /* 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 if we exit. */
+                sleep(10);
+               clean_child_exit(APEXIT_CHILDFATAL);
+           }
+            threads_created++;
+        }
+        if (workers_may_exit || threads_created == ap_threads_per_child) {
+            break;
+        }
+        sleep(1); /* wait for previous generation to clean up an entry */
+    }
+    
+    /* What state should this child_main process be listed as in the scoreboard...?
+     *  ap_update_child_status(my_child_num, i, SERVER_STARTING, (request_rec *) NULL);
+     * 
+     *  This state should be listed separately in the scoreboard, in some kind
+     *  of process_status, not mixed in with the worker threads' status.   
+     *  "life_status" is almost right, but it's in the worker's structure, and 
+     *  the name could be clearer.   gla
+     */
+    return NULL;
+}
+
+static void child_main(int child_num_arg)
+{
+    apr_thread_t **threads;
+    int i;
+    ap_listen_rec *lr;
+    apr_status_t rv;
+    thread_starter *ts;
+    apr_threadattr_t *thread_attr;
+    apr_thread_t *start_thread_id;
+
+    ap_my_pid = getpid();
+    apr_pool_create(&pchild, pconf);
+
+    /*stuff to do before we switch id's, so we have permissions.*/
+    reopen_scoreboard(pchild);
+
+    rv = SAFE_ACCEPT(apr_lock_child_init(&accept_mutex, lock_fname,
+                     pchild));
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                     "Couldn't initialize cross-process lock in child");
+        clean_child_exit(APEXIT_CHILDFATAL);
+    }
+
+    if (unixd_setup_child()) {
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+
+    ap_run_child_init(pchild, ap_server_conf);
+
+    /*done with init critical section */
+
+    rv = apr_setup_signal_thread();
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                     "Couldn't initialize signal thread");
+        clean_child_exit(APEXIT_CHILDFATAL);
+    }
+
+    if (ap_max_requests_per_child) {
+        requests_this_child = ap_max_requests_per_child;
+    }
+    else {
+        /* coding a value of zero means infinity */
+        requests_this_child = INT_MAX;
+    }
+    
+    /* Set up the pollfd array */
+    listensocks = apr_pcalloc(pchild,
+                           sizeof(*listensocks) * (num_listensocks + 1));
+#if APR_FILES_AS_SOCKETS
+    apr_socket_from_file(&listensocks[0], pipe_of_death_in);
+#endif
+    for (lr = ap_listeners, i = 1; i <= num_listensocks; lr = lr->next, ++i)
+       listensocks[i]=lr->sd;
+
+    /* Setup worker threads */
+
+    /* clear the storage; we may not create all our threads immediately, and we want
+     * a 0 entry to indicate a thread which was not created
+     */
+    threads = (apr_thread_t **)calloc(1, sizeof(apr_thread_t *) * ap_threads_per_child);
+    if (threads == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
+                     "malloc: out of memory");
+        clean_child_exit(APEXIT_CHILDFATAL);
+    }
+    worker_thread_count = 0;
+    apr_lock_create(&worker_thread_count_mutex, APR_MUTEX, APR_INTRAPROCESS,
+                    NULL, pchild);
+    apr_lock_create(&pipe_of_death_mutex, APR_MUTEX, APR_INTRAPROCESS, 
+                    NULL, pchild);
+
+    ts = apr_palloc(pchild, sizeof(*ts));
+
+    apr_threadattr_create(&thread_attr, pchild);
+    apr_threadattr_detach_set(thread_attr, 0);    /* 0 means PTHREAD_CREATE_JOINABLE */
+
+    ts->threads = threads;
+    ts->child_num_arg = child_num_arg;
+    ts->threadattr = thread_attr;
+
+    if ((rv = apr_thread_create(&start_thread_id, thread_attr, start_threads, ts, pchild))) {
+        ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
+                     "apr_thread_create: unable to create worker thread");
+        /* 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 if we exit. */
+        sleep(10);
+        clean_child_exit(APEXIT_CHILDFATAL);
+    }
+
+    apr_signal_thread(check_signal);
+
+    workers_may_exit = 1;   /* helps us terminate a little more quickly when 
+                             * the dispatch of the signal thread
+                             * beats the Pipe of Death and the browsers
+                             */
+    
+    /* A terminating signal was received. Now join each of the workers to clean them up.
+     *   If the worker already exited, then the join frees their resources and returns.
+     *   If the worker hasn't exited, then this blocks until they have (then cleans up).
+     */
+    apr_thread_join(&rv, start_thread_id);
+    for (i = 0; i < ap_threads_per_child; i++) {
+        if (threads[i]) { /* if we ever created this thread */
+            apr_thread_join(&rv, threads[i]);
+        }
+    }
+
+    free(threads);
+
+    clean_child_exit(0);
+}
+
+static int make_child(server_rec *s, int slot) 
+{
+    int pid;
+
+    if (slot + 1 > ap_max_daemons_limit) {
+       ap_max_daemons_limit = slot + 1;
+    }
+
+    if (one_process) {
+       set_signals();
+        ap_scoreboard_image->parent[slot].pid = getpid();
+       child_main(slot);
+    }
+
+    if ((pid = fork()) == -1) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, errno, 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, 0, 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 HAVE_BINDPROCESSOR
+        /* By default, AIX binds to a single processor.  This bit unbinds
+        * children which will then bind to another CPU.
+         */
+        int status = bindprocessor(BINDPROCESS, (int)getpid(),
+                              PROCESSOR_CLASS_ANY);
+       if (status != OK)
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, errno, ap_server_conf,
+                        "processor unbind failed %d", status);
+#endif
+        RAISE_SIGSTOP(MAKE_CHILD);
+
+        apr_signal(SIGTERM, just_die);
+        child_main(slot);
+
+        clean_child_exit(0);
+    }
+    /* else */
+    ap_scoreboard_image->parent[slot].quiescing = 0;
+    ap_scoreboard_image->parent[slot].pid = pid;
+    return 0;
+}
+
+/* If there aren't many connections coming in from the network, the child 
+ * processes may need to be awakened from their network i/o waits.
+ * The pipe of death is an effective prod.
+ */
+   
+static void wake_up_and_die(void) 
+{
+    int i;
+    char char_of_death = '!';
+    apr_size_t one = 1;
+    apr_status_t rv;
+    
+    for (i = 0; i < ap_daemons_limit;) {
+        if ((rv = apr_file_write(pipe_of_death_out, &char_of_death, &one)) 
+                                 != APR_SUCCESS) {
+            if (APR_STATUS_IS_EINTR(rv)) continue;
+            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, 
+                         "write pipe_of_death");
+        }
+        i++;
+    }
+}
+
+/* start up a bunch of children */
+static void startup_children(int number_to_start)
+{
+    int i;
+
+    for (i = 0; number_to_start && i < ap_daemons_limit; ++i) {
+       if (ap_scoreboard_image->parent[i].pid != 0) {
+           continue;
+       }
+       if (make_child(ap_server_conf, i) < 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, j;
+    int idle_thread_count;
+    worker_score *ws;
+    process_score *ps;
+    int free_length;
+    int free_slots[MAX_SPAWN_RATE];
+    int last_non_dead;
+    int total_non_dead;
+    apr_size_t one = 1;
+    apr_status_t rv;
+
+    /* initialize the free_list */
+    free_length = 0;
+
+    idle_thread_count = 0;
+    last_non_dead = -1;
+    total_non_dead = 0;
+
+    ap_sync_scoreboard_image();
+    for (i = 0; i < ap_daemons_limit; ++i) {
+       /* Initialization to satisfy the compiler. It doesn't know
+        * that ap_threads_per_child is always > 0 */
+       int status = SERVER_DEAD;
+       int any_dying_threads = 0;
+       int any_dead_threads = 0;
+
+       if (i >= ap_max_daemons_limit && free_length == idle_spawn_rate)
+           break;
+        ps = &ap_scoreboard_image->parent[i];
+       for (j = 0; j < ap_threads_per_child; j++) {
+            ws = &ap_scoreboard_image->servers[i][j];
+           status = ws->status;
+
+           any_dying_threads = any_dying_threads || (status == SERVER_GRACEFUL);
+           any_dead_threads = any_dead_threads || (status == SERVER_DEAD);
+
+           /* 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 && status != SERVER_DEAD &&
+                    ps->generation == ap_my_generation && 
+                 /* XXX the following shouldn't be necessary if we clean up 
+                  *     properly after seg faults, but we're not yet    GLA 
+                  */     
+                    ps->pid != 0) {
+               ++idle_thread_count;
+           }
+       }
+        /* XXX any_dead_threads may not be needed any more GLA */
+        if (any_dead_threads && free_length < idle_spawn_rate 
+                && (!ps->pid               /* no process in the slot */
+                    || ps->quiescing)) {   /* or at least one is going away */
+           free_slots[free_length] = i;
+           ++free_length;
+       }
+       if (!any_dying_threads) {
+            last_non_dead = i;
+            ++total_non_dead;
+        }
+    }
+    ap_max_daemons_limit = last_non_dead + 1;
+
+    if (idle_thread_count > max_spare_threads) {
+        char char_of_death = '!';
+        /* Kill off one child */
+        if ((rv = apr_file_write(pipe_of_death_out, &char_of_death, &one)) != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, "write pipe_of_death");
+        }
+        idle_spawn_rate = 1;
+    }
+    else if (idle_thread_count < min_spare_threads) {
+        /* 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, 0, ap_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, 0, ap_server_conf,
+                            "server seems busy, (you may need "
+                            "to increase StartServers, ThreadsPerChild "
+                             "or Min/MaxSpareThreads), "
+                            "spawning %d children, there are around %d idle "
+                             "threads, and %d total children", free_length,
+                            idle_thread_count, total_non_dead);
+           }
+           for (i = 0; i < free_length; ++i) {
+               make_child(ap_server_conf, free_slots[i]);
+           }
+           /* 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 server_main_loop(int remaining_children_to_start)
+{
+    int child_slot;
+    apr_wait_t status;
+    apr_proc_t pid;
+    int i;
+
+    while (!restart_pending && !shutdown_pending) {
+        ap_wait_or_timeout(&status, &pid, pconf);
+        
+        if (pid.pid != -1) {
+            ap_process_child_status(&pid, status);
+            /* non-fatal death... note that it's gone in the scoreboard. */
+            child_slot = find_child_by_pid(&pid);
+            if (child_slot >= 0) {
+                for (i = 0; i < ap_threads_per_child; i++)
+                    ap_update_child_status(child_slot, i, SERVER_DEAD, (request_rec *) NULL);
+                
+                ap_scoreboard_image->parent[child_slot].pid = 0;
+                ap_scoreboard_image->parent[child_slot].quiescing = 0;
+               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(ap_server_conf, child_slot);
+                   --remaining_children_to_start;
+               }
+#if APR_HAS_OTHER_CHILD
+           }
+           else if (apr_proc_other_child_read(&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, 0,
+                            ap_server_conf,
+                            "long lost child came home! (pid %ld)",
+                            (long)pid.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();
+    }
+}
+
+int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
+{
+    int remaining_children_to_start;
+    apr_status_t rv;
+
+    pconf = _pconf;
+    ap_server_conf = s;
+
+    rv = apr_file_pipe_create(&pipe_of_death_in, &pipe_of_death_out, pconf);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv,
+                     (const server_rec*) ap_server_conf,
+                     "apr_file_pipe_create (pipe_of_death)");
+        exit(1);
+    }
+
+    if ((rv = apr_file_pipe_timeout_set(pipe_of_death_in, 0)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv,
+                     (const server_rec*) ap_server_conf,
+                     "apr_file_pipe_timeout_set (pipe_of_death)");
+        exit(1);
+    }
+
+    if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
+        /* XXX: hey, what's the right way for the mpm to indicate a fatal error? */
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
+            "no listening sockets available, shutting down");
+        return 1;
+    }
+    ap_log_pid(pconf, ap_pid_fname);
+
+    /* Initialize cross-process accept lock */
+    lock_fname = apr_psprintf(_pconf, "%s.%" APR_OS_PROC_T_FMT,
+                             ap_server_root_relative(_pconf, lock_fname),
+                             ap_my_pid);
+    rv = apr_lock_create_np(&accept_mutex, APR_MUTEX, APR_LOCKALL,
+                            accept_lock_mech, lock_fname, _pconf);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
+                     "Couldn't create accept lock");
+        return 1;
+    }
+
+    if (!is_graceful) {
+        ap_run_pre_mpm(pconf, SB_SHARED);
+    }
+
+    set_signals();
+    /* Don't thrash... */
+    if (max_spare_threads < min_spare_threads + ap_threads_per_child)
+       max_spare_threads = min_spare_threads + ap_threads_per_child;
+
+    /* 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 SIGWINCH).  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, 0, ap_server_conf,
+               "%s configured -- resuming normal operations",
+               ap_get_server_version());
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf,
+               "Server built: %s", ap_get_server_built());
+    restart_pending = shutdown_pending = 0;
+
+    server_main_loop(remaining_children_to_start);
+
+    if (shutdown_pending) {
+        /* Time to gracefully shut down:
+         * Kill child processes, tell them to call child_exit, etc...
+         */
+        wake_up_and_die();
+
+        if (unixd_killpg(getpgrp(), SIGTERM) < 0) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGTERM");
+        }
+        ap_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, 0,
+                        ap_server_conf,
+                        "removed PID file %s (pid=%ld)",
+                        pidfile, (long)getpid());
+        }
+    
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
+            "caught SIGTERM, shutting down");
+    
+       return 1;
+    }
+
+    /* we've been told to restart */
+    apr_signal(SIGHUP, 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();
+    
+    /* wake up the children...time to die.  But we'll have more soon */
+    wake_up_and_die();
+    
+    if (is_graceful) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
+                   "SIGWINCH received.  Doing graceful restart");
+
+       /* This is mostly for debugging... so that we know what is still
+         * gracefully dealing with existing request.
+         */
+       
+    }
+    else {
+      /* Kill 'em all.  Since the child acts the same on the parents SIGTERM 
+       * and a SIGHUP, we may as well use the same signal, because some user
+       * pthreads are stealing signals from us left and right.
+       */
+       if (unixd_killpg(getpgrp(), SIGTERM) < 0) {
+           ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGTERM");
+       }
+        ap_reclaim_child_processes(1);         /* Start with SIGTERM */
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
+                   "SIGHUP received.  Attempting to restart");
+    }
+    return 0;
+}
+
+static void worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
+{
+    static int restart_num = 0;
+    int no_detach = 0;
+
+    one_process = !!ap_exists_config_define("ONE_PROCESS");
+    no_detach = !!ap_exists_config_define("NO_DETACH");
+
+    /* sigh, want this only the second time around */
+    if (restart_num++ == 1) {
+       is_graceful = 0;
+
+       if (!one_process && !no_detach) {
+           apr_proc_detach();
+       }
+       ap_my_pid = getpid();
+    }
+
+    unixd_pre_config(ptemp);
+    ap_listen_pre_config();
+    ap_daemons_to_start = DEFAULT_START_DAEMON;
+    min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
+    max_spare_threads = DEFAULT_MAX_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
+    ap_daemons_limit = HARD_SERVER_LIMIT;
+    ap_threads_per_child = DEFAULT_THREADS_PER_CHILD;
+    ap_pid_fname = DEFAULT_PIDLOG;
+    ap_scoreboard_fname = DEFAULT_SCOREBOARD;
+    lock_fname = DEFAULT_LOCKFILE;
+    ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
+    ap_extended_status = 0;
+
+    apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+}
+
+static void worker_hooks(apr_pool_t *p)
+{
+    one_process = 0;
+
+    ap_hook_pre_config(worker_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+
+static const char *set_pidfile(cmd_parms *cmd, void *dummy, const 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 <VirtualHost>";
+    }
+    ap_pid_fname = arg;
+    return NULL;
+}
+
+static const char *set_scoreboard(cmd_parms *cmd, void *dummy,
+                                 const 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, const char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    lock_fname = arg;
+    return NULL;
+}
+
+static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy,
+                                       const 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_spare_threads(cmd_parms *cmd, void *dummy,
+                                        const char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    min_spare_threads = atoi(arg);
+    if (min_spare_threads <= 0) {
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                    "WARNING: detected MinSpareThreads set to non-positive.");
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                    "Resetting to 1 to avoid almost certain Apache failure.");
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                    "Please read the documentation.");
+       min_spare_threads = 1;
+    }
+       
+    return NULL;
+}
+
+static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy,
+                                        const char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    max_spare_threads = atoi(arg);
+    return NULL;
+}
+
+static const char *set_server_limit (cmd_parms *cmd, void *dummy,
+                                    const 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) {
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                    "WARNING: MaxClients of %d exceeds compile time limit "
+                    "of %d servers,", ap_daemons_limit, HARD_SERVER_LIMIT);
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                    " lowering MaxClients to %d.  To increase, please "
+                    "see the", HARD_SERVER_LIMIT);
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                    " HARD_SERVER_LIMIT define in %s.",
+                    AP_MPM_HARD_LIMITS_FILE);
+       ap_daemons_limit = HARD_SERVER_LIMIT;
+    } 
+    else if (ap_daemons_limit < 1) {
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "WARNING: Require MaxClients > 0, setting to 1");
+       ap_daemons_limit = 1;
+    }
+    return NULL;
+}
+
+static const char *set_threads_per_child (cmd_parms *cmd, void *dummy,
+                                         const 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) {
+        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                     "WARNING: ThreadsPerChild of %d exceeds compile time "
+                     "limit of %d threads,", ap_threads_per_child,
+                     HARD_THREAD_LIMIT);
+        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                     " lowering ThreadsPerChild to %d. To increase, please"
+                     " see the", HARD_THREAD_LIMIT);
+        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                     " HARD_THREAD_LIMIT define in %s.",
+                     AP_MPM_HARD_LIMITS_FILE);
+        ap_threads_per_child = HARD_THREAD_LIMIT;
+    }
+    else if (ap_threads_per_child < 1) {
+       ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
+                     "WARNING: Require ThreadsPerChild > 0, setting to 1");
+       ap_threads_per_child = 1;
+    }
+    return NULL;
+}
+
+static const char *set_max_requests(cmd_parms *cmd, void *dummy,
+                                   const 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,
+                                   const char *arg) 
+{
+    apr_finfo_t 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);
+    if ((apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool) != APR_SUCCESS) 
+        || (finfo.filetype != APR_DIR)) {
+       return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname, 
+                         " does not exist or is not a directory", NULL);
+    }
+    apr_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir));
+    return NULL;
+}
+
+static const char *set_accept_lock_mech(cmd_parms *cmd, void *dummy, const char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (!strcasecmp(arg, "default")) {
+        accept_lock_mech = APR_LOCK_DEFAULT;
+    }
+#if APR_HAS_FLOCK_SERIALIZE
+    else if (!strcasecmp(arg, "flock")) {
+        accept_lock_mech = APR_LOCK_FLOCK;
+    }
+#endif
+#if APR_HAS_FCNTL_SERIALIZE
+    else if (!strcasecmp(arg, "fcntl")) {
+        accept_lock_mech = APR_LOCK_FCNTL;
+    }
+#endif
+#if APR_HAS_SYSVSEM_SERIALIZE
+    else if (!strcasecmp(arg, "sysvsem")) {
+        accept_lock_mech = APR_LOCK_SYSVSEM;
+    }
+#endif
+#if APR_HAS_PROC_PTHREAD_SERIALIZE
+    else if (!strcasecmp(arg, "proc_pthread")) {
+        accept_lock_mech = APR_LOCK_PROC_PTHREAD;
+    }
+#endif
+    else {
+        return apr_pstrcat(cmd->pool, arg, " is an invalid mutex mechanism; valid "
+                           "ones for this platform are: default"
+#if APR_HAS_FLOCK_SERIALIZE
+                           ", flock"
+#endif
+#if APR_HAS_FCNTL_SERIALIZE
+                           ", fcntl"
+#endif
+#if APR_HAS_SYSVSEM_SERIALIZE
+                           ", sysvsem"
+#endif
+#if APR_HAS_PROC_PTHREAD_SERIALIZE
+                           ", proc_pthread"
+#endif
+                           , NULL);
+    }
+    return NULL;
+}
+
+static const command_rec worker_cmds[] = {
+UNIX_DAEMON_COMMANDS
+LISTEN_COMMANDS
+AP_INIT_TAKE1("PidFile", set_pidfile, NULL, RSRC_CONF,
+    "A file for logging the server process ID"),
+AP_INIT_TAKE1("ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF,
+    "A file for Apache to maintain runtime process management information"),
+AP_INIT_TAKE1("LockFile", set_lockfile, NULL, RSRC_CONF,
+    "The lockfile used when Apache needs to lock the accept() call"),
+AP_INIT_TAKE1("StartServers", set_daemons_to_start, NULL, RSRC_CONF,
+  "Number of child processes launched at server startup"),
+AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
+  "Minimum number of idle children, to handle request spikes"),
+AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
+  "Maximum number of idle children"),
+AP_INIT_TAKE1("MaxClients", set_server_limit, NULL, RSRC_CONF,
+  "Maximum number of children alive at the same time"),
+AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF,
+  "Number of threads each child creates"),
+AP_INIT_TAKE1("MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF,
+  "Maximum number of requests a particular child serves before dying."),
+AP_INIT_TAKE1("CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF,
+  "The location of the directory Apache changes to before dumping core"),
+AP_INIT_TAKE1("AcceptMutex", set_accept_lock_mech, NULL, RSRC_CONF,
+              "The system mutex implementation to use for the accept mutex"),
+{ NULL }
+};
+
+module AP_MODULE_DECLARE_DATA mpm_worker_module = {
+    MPM20_MODULE_STUFF,
+    NULL,                       /* hook to run before apache parses args */
+    NULL,                      /* create per-directory config structure */
+    NULL,                      /* merge per-directory config structures */
+    NULL,                      /* create per-server config structure */
+    NULL,                      /* merge per-server config structures */
+    worker_cmds,               /* command apr_table_t */
+    worker_hooks               /* register_hooks */
+};
+
diff --git a/server/util_time.c b/server/util_time.c
new file mode 100644 (file)
index 0000000..3db076b
--- /dev/null
@@ -0,0 +1,175 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 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
+ * <http://www.apache.org/>.
+ *
+ */
+
+#include "util_time.h"
+
+/* Cache for exploded values of recent timestamps
+ */
+
+struct exploded_time_cache_element {
+    apr_int64_t t;
+    apr_exploded_time_t xt;
+    apr_int64_t t_validate; /* please see comments in cached_explode() */
+};
+
+/* the "+ 1" is for the current second: */
+#define TIME_CACHE_SIZE (AP_TIME_RECENT_THRESHOLD + 1)
+
+static struct exploded_time_cache_element exploded_cache_localtime[TIME_CACHE_SIZE];
+static struct exploded_time_cache_element exploded_cache_gmt[TIME_CACHE_SIZE];
+
+
+static apr_status_t cached_explode(apr_exploded_time_t *xt, apr_time_t t,
+                                   struct exploded_time_cache_element *cache,
+                                   int use_gmt)
+{
+    apr_int64_t seconds = t / APR_USEC_PER_SEC;
+    struct exploded_time_cache_element *cache_element =
+        &(cache[seconds % TIME_CACHE_SIZE]);
+    struct exploded_time_cache_element cache_element_snapshot;
+
+    /* The cache is implemented as a ring buffer.  Each second,
+     * it uses a different element in the buffer.  The timestamp
+     * in the element indicates whether the element contains the
+     * exploded time for the current second (vs the time
+     * 'now - AP_TIME_RECENT_THRESHOLD' seconds ago).  If the
+     * cached value is for the current time, we use it.  Otherwise,
+     * we compute the apr_exploded_time_t and store it in this
+     * cache element. Note that the timestamp in the cache
+     * element is updated only after the exploded time.  Thus
+     * if two threads hit this cache element simultaneously
+     * at the start of a new second, they'll both explode the
+     * time and store it.  I.e., the writers will collide, but
+     * they'll be writing the same value.
+     */
+    if (cache_element->t >= seconds) {
+        /* There is an intentional race condition in this design:
+         * in a multithreaded app, one thread might be reading
+         * from this cache_element to resolve a timestamp from
+         * TIME_CACHE_SIZE seconds ago at the same time that
+         * another thread is copying the exploded form of the
+         * current time into the same cache_element.  (I.e., the
+         * first thread might hit this element of the ring buffer
+         * just as the element is being recycled.)  This can
+         * also happen at the start of a new second, if a
+         * reader accesses the cache_element after a writer
+         * has updated cache_element.t but before the writer
+         * has finished updating the whole cache_element.
+         *
+         * Rather than trying to prevent this race condition
+         * with locks, we allow it to happen and then detect
+         * and correct it.  The detection works like this:
+         *   Step 1: Take a "snapshot" of the cache element by
+         *           copying it into a temporary buffer.
+         *   Step 2: Check whether the snapshot contains consistent
+         *           data: the timestamps at the start and end of
+         *           the cache_element should both match the 'seconds'
+         *           value that we computed from the input time.
+         *           If these three don't match, then the snapshot
+         *           shows the cache_element in the middle of an
+         *           update, and its contents are invalid.
+         *   Step 3: If the snapshot is valid, use it.  Otherwise,
+         *           just give up on the cache and explode the
+         *           input time.
+         */
+        memcpy(&cache_element_snapshot, cache_element,
+               sizeof(struct exploded_time_cache_element));
+        if ((seconds != cache_element_snapshot.t) ||
+            (seconds != cache_element_snapshot.t_validate)) {
+            /* Invalid snapshot */
+            if (use_gmt) {
+                return apr_explode_gmt(xt, t);
+            }
+            else {
+                return apr_explode_localtime(xt, t);
+            }
+        }
+        else {
+            /* Valid snapshot */
+            memcpy(xt, &(cache_element_snapshot.xt),
+                   sizeof(apr_exploded_time_t));
+        }
+    }
+    else {
+        apr_status_t r;
+        if (use_gmt) {
+            r = apr_explode_gmt(xt, t);
+        }
+        else {
+            r = apr_explode_localtime(xt, t);
+        }
+        if (!APR_STATUS_IS_SUCCESS(r)) {
+            return r;
+        }
+        cache_element->t = seconds;
+        memcpy(&(cache_element->xt), xt, sizeof(apr_exploded_time_t));
+        cache_element->t_validate = seconds;
+    }
+    xt->tm_usec = t % APR_USEC_PER_SEC;
+    return APR_SUCCESS;
+}
+
+
+AP_DECLARE(apr_status_t) ap_explode_recent_localtime(apr_exploded_time_t * tm,
+                                                     apr_time_t t)
+{
+    return cached_explode(tm, t, exploded_cache_localtime, 0);
+}
+
+AP_DECLARE(apr_status_t) ap_explode_recent_gmt(apr_exploded_time_t * tm,
+                                               apr_time_t t)
+{
+    return cached_explode(tm, t, exploded_cache_gmt, 1);
+}
diff --git a/support/abs.dsp b/support/abs.dsp
new file mode 100644 (file)
index 0000000..cd8467a
--- /dev/null
@@ -0,0 +1,131 @@
+# Microsoft Developer Studio Project File - Name="abs" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=abs - 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 "abs.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 "abs.mak" CFG="abs - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "abs - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "abs - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "abs - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "SSL" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /I "../srclib/apr/include" /I "../srclib/apr-util/include" /I "../include" /I "../srclib/openssl/inc32" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "USE_SSL" /Fd"Release/abs" /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 wsock32.lib ws2_32.lib ssleay32.lib libeay32.lib /nologo /subsystem:console /map /machine:I386 /libpath:"../srclib/openssl/out32dll"
+# ADD LINK32 kernel32.lib advapi32.lib wsock32.lib ws2_32.lib ssleay32.lib libeay32.lib /nologo /subsystem:console /map /machine:I386 /libpath:"../srclib/openssl/out32dll"
+
+!ELSEIF  "$(CFG)" == "abs - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "SSL" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "../srclib/apr/include" /I "../srclib/apr-util/include" /I "../include" /I "../srclib/openssl/inc32" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "USE_SSL" /Fd"Debug/abs" /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 advapi32.lib wsock32.lib ws2_32.lib ssleay32.lib libeay32.lib /nologo /subsystem:console /incremental:no /map /debug /machine:I386 /libpath:"../srclib/openssl/out32dll"
+# ADD LINK32 kernel32.lib advapi32.lib wsock32.lib ws2_32.lib ssleay32.lib libeay32.lib /nologo /subsystem:console /incremental:no /map /debug /machine:I386 /libpath:"../srclib/openssl/out32dll"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "abs - Win32 Release"
+# Name "abs - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\ab.c
+
+!IF  "$(CFG)" == "abs - Win32 Release"
+# ADD CPP /Fo"Release/abs.obj"
+
+!ELSEIF  "$(CFG)" == "abs - Win32 Debug"
+# ADD CPP /Fo"Debug/abs.obj"
+
+!ENDIF 
+# End Source File
+# Begin Source File
+
+SOURCE=.\abs.rc
+# End Source File
+# Begin Source File
+
+SOURCE=..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "abs - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\build\win32\win32ver.awk
+
+".\abs.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       awk -f ../build/win32/win32ver.awk ab "ApacheBench/SSL Utility"  ../include/ap_release.h > .\abs.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "abs - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\build\win32\win32ver.awk
+
+".\abs.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       awk -f ../build/win32/win32ver.awk ab "ApacheBench/SSL Utility"  ../include/ap_release.h > .\abs.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/support/config.m4 b/support/config.m4
new file mode 100644 (file)
index 0000000..ce37747
--- /dev/null
@@ -0,0 +1,45 @@
+
+
+htpasswd_LTFLAGS=""
+htdigest_LTFLAGS=""
+rotatelogs_LTFLAGS=""
+logresolve_LTFLAGS=""
+ab_LTFLAGS=""
+
+dnl XXX Should we change the foo_LTFLAGS="-static" settings below
+dnl to something like APR_ADDTO? -aaron
+
+AC_ARG_ENABLE(static-htpasswd,[  --enable-static-htpasswd  Build a statically linked version of htpasswd],[
+if test "$enableval" = "yes" ; then
+  htpasswd_LTFLAGS="-static"
+fi
+])
+APACHE_SUBST(htpasswd_LTFLAGS)
+
+AC_ARG_ENABLE(static-htdigest,[  --enable-static-htdigest  Build a statically linked version of htdigest],[
+if test "$enableval" = "yes" ; then
+  htdigest_LTFLAGS="-static"
+fi
+])
+APACHE_SUBST(htdigest_LTFLAGS)
+
+AC_ARG_ENABLE(static-rotatelogs,[  --enable-static-rotatelogs  Build a statically linked version of rotatelogs],[
+if test "$enableval" = "yes" ; then
+  rotatelogs_LTFLAGS="-static"
+fi
+])
+APACHE_SUBST(rotatelogs_LTFLAGS)
+
+AC_ARG_ENABLE(static-logresolve,[  --enable-static-logresolve  Build a statically linked version of logresolve],[
+if test "$enableval" = "yes" ; then
+  logresolve_LTFLAGS="-static"
+fi
+])
+APACHE_SUBST(logresolve_LTFLAGS)
+
+AC_ARG_ENABLE(static-ab,[  --enable-static-ab      Build a statically linked version of ab],[
+if test "$enableval" = "yes" ; then
+  ab_LTFLAGS="-static"
+fi
+])
+APACHE_SUBST(ab_LTFLAGS)
diff --git a/support/list_hooks.pl b/support/list_hooks.pl
new file mode 100755 (executable)
index 0000000..c52c567
--- /dev/null
@@ -0,0 +1,77 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+use Carp;
+
+my $path=shift;
+
+findInDir($path);
+
+foreach my $hook (sort keys %::Hooks) {
+    my $h=$::Hooks{$hook};
+    for my $x (qw(declared implemented type args)) {
+       croak "$hook datum '$x' missing" if !exists $h->{$x};
+    }
+    print "$hook\n";
+    print "  declared in $h->{declared}\n";
+    print "  implemented in $h->{implemented}\n";
+    print "  type is $h->{type}\n";
+    print "  $h->{ret} $hook($h->{args})\n";
+    print "\n";
+}
+
+sub findInDir {
+    my $path=shift;
+
+    local(*D);
+    opendir(D,$path) || croak "Can't open $path: $!";
+    while(my $f=readdir D) {
+       next if $f=~/^\./;
+       my $file="$path/$f";
+
+       if(-d $file) {
+           findInDir($file);
+           next;
+       }
+       next if $file !~ /\.[ch]$/;
+
+       scanFile($file);
+    }
+    closedir D;
+}
+
+sub scanFile {
+    my $file=shift;
+
+#    print "scanning $file\n";
+
+    open(F,$file) || croak "Can't open $file: $!";
+    while(<F>) {
+       next if /\#define/;
+       next if /\@deffunc/;
+       if(/AP_DECLARE_HOOK\((.*)\)/) {
+           my $def=$1;
+           my($ret,$name,$args)=$def=~/([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*\((.*)\)/;
+           croak "Don't understand $def in $file" if !defined $args;
+#          print "found $ret $name($args) in $file\n";
+
+           croak "$name declared twice! ($_)"
+               if exists $::Hooks{$name}->{declared};
+           $::Hooks{$name}->{declared}=$file;
+           $::Hooks{$name}->{ret}=$ret;
+           $::Hooks{$name}->{args}=$args;
+       }
+       if(/AP_IMPLEMENT_HOOK_()(VOID)\(([^,\s]+)/
+          || /AP_IMPLEMENT(_OPTIONAL|)_HOOK_(.*?)\([^,]+?\s*,\s*([^,\s]+)/) {
+           my($type,$name)=($1 ? "OPTIONAL $2" : $2,$3);
+
+#          print "found $name $type in $file\n";
+
+           croak "$name implemented twice ($::Hooks{$name}->{implemented} and $file) ($_)"
+               if exists $::Hooks{$name}->{implemented};
+           $::Hooks{$name}->{implemented}=$file;
+           $::Hooks{$name}->{type}=$type;
+       }
+    }
+}
diff --git a/support/win32/.cvsignore b/support/win32/.cvsignore
new file mode 100644 (file)
index 0000000..7658ff5
--- /dev/null
@@ -0,0 +1,3 @@
+Release
+Debug
+*.plg
diff --git a/support/win32/ApacheMonitor.c b/support/win32/ApacheMonitor.c
new file mode 100644 (file)
index 0000000..156f896
--- /dev/null
@@ -0,0 +1,1367 @@
+/* ====================================================================
+ * 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
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/* ====================================================================
+ * ApacheService.c Simple program to manage and monitor Apache services.
+ *
+ * Contributed by Mladen Turk <mturk@mappingsoft.com>
+ *
+ * 05 Aug 2001
+ * ==================================================================== 
+ */
+
+#define _WIN32_WINNT 0x0400
+#ifndef STRICT
+#define STRICT
+#endif
+
+#include <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "ApacheMonitor.h"
+
+
+#define OS_VERSION_WINNT    1
+#define OS_VERSION_WIN9X    2
+#define OS_VERSION_WIN2K    3
+/* Should be enough */
+#define MAX_APACHE_SERVICES 128
+
+#define WM_TRAYMESSAGE         (WM_APP+1)
+#define WM_UPDATEMESSAGE       (WM_USER+1)
+#define SERVICE_APACHE_RESTART 128
+#define XBITMAP                16
+#define YBITMAP                16 
+#define MAX_LOADSTRING         100
+
+#ifndef SERVICE_RUNS_IN_SYSTEM_PROCESS
+#define SERVICE_RUNS_IN_SYSTEM_PROCESS  0x00000001
+
+typedef struct _SERVICE_STATUS_PROCESS {
+    DWORD   dwServiceType;
+    DWORD   dwCurrentState;
+    DWORD   dwControlsAccepted;
+    DWORD   dwWin32ExitCode;
+    DWORD   dwServiceSpecificExitCode;
+    DWORD   dwCheckPoint;
+    DWORD   dwWaitHint;
+    DWORD   dwProcessId;
+    DWORD   dwServiceFlags;
+} SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;
+
+typedef enum _SC_STATUS_TYPE {
+    SC_STATUS_PROCESS_INFO      = 0
+} SC_STATUS_TYPE;
+
+#endif
+
+typedef BOOL (WINAPI *QUERYSERVICESTATUSEX)(SC_HANDLE, SC_STATUS_TYPE,
+                                               LPBYTE, DWORD, LPDWORD);
+
+typedef struct _st_APACHE_SERVICE
+{
+    LPSTR    szServiceName;
+    LPSTR    szDisplayName;
+    LPSTR    szDescription;
+    LPSTR    szImagePath;
+    DWORD    dwPid;
+} ST_APACHE_SERVICE;
+
+/* Global variables */
+HINSTANCE         ap_hInstance = NULL;
+HWND              ap_hwndAboutDlg = NULL;
+TCHAR             szTitle[MAX_LOADSTRING];          /* The title bar text */
+TCHAR             szWindowClass[MAX_LOADSTRING];    /* Window Class Name  */
+HICON             ap_icoStop;
+HICON             ap_icoRun;
+UINT              ap_uiTaskbarCreated;
+DWORD             ap_OSVersion;
+BOOL              dlgAboutOn = FALSE;
+BOOL              dlgServiceOn = FALSE;
+ST_APACHE_SERVICE ap_stServices[MAX_APACHE_SERVICES];
+
+HBITMAP           hbmpStart, hbmpStop; 
+HBITMAP           hbmpPicture, hbmpOld; 
+HWND              ap_hServiceDlg;
+BOOL              ap_rescanServices;
+HWND              ap_hServiceDlg;
+
+
+void ap_ClearServicesSt()
+{
+    int i;
+    for (i = 0; i < MAX_APACHE_SERVICES; i++)
+    {
+        if (ap_stServices[i].szServiceName)
+            free(ap_stServices[i].szServiceName);
+        if (ap_stServices[i].szDisplayName)
+            free(ap_stServices[i].szDisplayName);
+        if (ap_stServices[i].szDescription)
+            free(ap_stServices[i].szDescription);
+        if (ap_stServices[i].szImagePath)
+            free(ap_stServices[i].szImagePath);
+
+    }
+    ZeroMemory(ap_stServices, sizeof(ST_APACHE_SERVICE) * MAX_APACHE_SERVICES);
+
+}
+
+void ErrorMessage(DWORD dwError)
+{
+    LPVOID lpMsgBuf;
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                  FORMAT_MESSAGE_FROM_SYSTEM |
+                  FORMAT_MESSAGE_IGNORE_INSERTS,
+                  NULL,
+                  dwError == ERROR_SUCCESS ? GetLastError() : dwError,
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                  (LPTSTR) &lpMsgBuf, 0, NULL);
+    MessageBox(NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONERROR);
+    LocalFree(lpMsgBuf);
+
+}
+
+LPTSTR GetStringRes(int id)
+{
+  static TCHAR buffer[MAX_PATH];
+
+  buffer[0] = 0;
+  LoadString(GetModuleHandle (NULL), id, buffer, MAX_PATH);
+  return buffer;
+}
+
+BOOL GetSystemOSVersion(LPSTR szVersion, LPDWORD dwVersion)
+{
+    OSVERSIONINFOEX osvi;
+    BOOL bOsVersionInfoEx;
+    char szBuff[256];
+    HKEY hKey;
+    char szProductType[80];
+    DWORD dwBufLen;
+    
+    /* 
+    Try calling GetVersionEx using the OSVERSIONINFOEX structure.
+    If that fails, try using the OSVERSIONINFO structure.
+    */
+    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+    
+    if (!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi)))
+    {
+        /* If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. */        
+        osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+        if (!GetVersionEx((OSVERSIONINFO *) &osvi)) 
+            return FALSE;
+    }
+    
+    switch (osvi.dwPlatformId)
+    {
+    case VER_PLATFORM_WIN32_NT:        
+        /* Test for the product. */        
+        if (szVersion!= NULL)
+        {
+            if (osvi.dwMajorVersion <= 4)
+                strcpy(szVersion, "MS Windows NT ");
+            else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
+                strcpy(szVersion, "MS Windows 2000 ");
+            else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
+                strcpy(szVersion, "Whistler ");
+            /* Test for product type.*/            
+#ifdef VER_VORKSTATION_NT
+            if (bOsVersionInfoEx)
+            {
+                if (osvi.wProductType == VER_NT_WORKSTATION)
+                {
+#ifdef VER_SUITE_PERSONAL
+                    if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
+                        strcat(szVersion, "Personal ");
+                    else
+#endif
+                    strcat(szVersion, "Professional ");
+                }                
+                else if (osvi.wProductType == VER_NT_SERVER)
+                {
+                    if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+                        strcat(szVersion, "DataCenter Server ");
+                    else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+                        strcat(szVersion, "Advanced Server ");
+                    else
+                        strcat(szVersion, "Server ");
+                }
+            }
+            else
+            {
+#endif                
+                RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                    "SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
+                    0, KEY_QUERY_VALUE, &hKey);
+                RegQueryValueEx(hKey, "ProductType", NULL, NULL,
+                    (LPBYTE) szProductType, &dwBufLen);
+                RegCloseKey(hKey);
+                if (lstrcmpi("WINNT", szProductType) == 0)
+                    strcat(szVersion, "Workstation ");
+                if (lstrcmpi("SERVERNT", szProductType) == 0)
+                    strcat(szVersion, "Server ");
+#ifdef VER_VORKSTATION_NT
+            }            
+#endif
+            /* Get version, service pack (if any), and build number. */
+            if (osvi.dwMajorVersion <= 4)
+            {
+                sprintf(szBuff, "version %d.%d %s (Build-%d)\n",
+                        osvi.dwMajorVersion,
+                        osvi.dwMinorVersion,
+                        osvi.szCSDVersion,
+                        osvi.dwBuildNumber & 0xFFFF);
+            }
+            else
+            { 
+                sprintf(szBuff, "%s (Build-%d)\n",
+                    osvi.szCSDVersion,
+                    osvi.dwBuildNumber & 0xFFFF);
+            }
+            strcat(szVersion, szBuff);
+        }
+        else if (dwVersion != NULL)
+        {
+            if (osvi.dwMajorVersion <= 4)
+                *dwVersion = OS_VERSION_WINNT;
+            else if (osvi.dwMajorVersion == 5)
+                *dwVersion = OS_VERSION_WIN2K;
+            else
+                return FALSE;
+            
+        }
+        break;
+        
+    case VER_PLATFORM_WIN32_WINDOWS:
+        if (szVersion != NULL)
+        {
+            if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
+            {
+                strcpy(szVersion, "MS Windows 95 ");
+                if (osvi.szCSDVersion[1] == 'C')
+                    strcat(szVersion, "OSR2 ");
+            } 
+            
+            if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
+            {
+                strcpy(szVersion, "MS Windows 98 ");
+                if (osvi.szCSDVersion[1] == 'A')
+                    strcat(szVersion, "SE ");
+            } 
+            
+            if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
+            {
+                strcpy(szVersion, "MS Windows Me ");
+            }
+        }
+        if (dwVersion != NULL)
+            *dwVersion = OS_VERSION_WIN9X;
+        
+        break;
+        
+    case VER_PLATFORM_WIN32s:
+        if (szVersion != NULL)
+            strcpy(szVersion, "Microsoft Win32s ");
+        if (dwVersion != NULL)
+            *dwVersion = OS_VERSION_WIN9X;
+        break;
+    default:
+        return FALSE;
+        break;
+   }
+   return TRUE; 
+}
+
+static VOID ShowNotifyIcon(HWND hWnd, DWORD dwMessage)
+{
+    
+    NOTIFYICONDATA nid;
+    int  i = 0, n = 0;
+
+    ZeroMemory(&nid,sizeof(nid));
+    nid.cbSize = sizeof(NOTIFYICONDATA);
+    nid.hWnd = hWnd;
+    nid.uID = 0xFF;
+    nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
+    nid.uCallbackMessage = WM_TRAYMESSAGE;
+    
+    while (ap_stServices[i].szServiceName != NULL)
+    {    
+        if (ap_stServices[i].dwPid != 0)
+            ++n;
+        ++i;
+    }
+    if (dwMessage != NIM_DELETE)
+    {
+        if (n)
+            nid.hIcon = ap_icoRun;
+        else
+            nid.hIcon = ap_icoStop;
+    }
+    else
+        nid.hIcon = NULL;
+
+    sprintf(nid.szTip, "Running: %d Services", n);    
+    Shell_NotifyIcon(dwMessage, &nid);
+    
+}
+
+void ShowTryPopupMenu(HWND hWnd)
+{
+    /* create popup menu */
+    HMENU hMenu = CreatePopupMenu();
+    POINT pt;
+
+    if (hMenu)
+    {
+        AppendMenu(hMenu,  MF_STRING, IDM_ABOUT, "&About...");
+        AppendMenu(hMenu,  MF_STRING, IDM_RESTORE, "&Show Services...");
+        AppendMenu(hMenu,  MF_SEPARATOR, 0, "");
+        AppendMenu(hMenu,  MF_STRING, IDM_EXIT,  "&Exit...");
+
+        GetCursorPos(&pt);
+        SetForegroundWindow(NULL);
+        TrackPopupMenu(hMenu, TPM_LEFTALIGN|TPM_RIGHTBUTTON, pt.x, pt.y, 0, hWnd, NULL);
+    }
+}
+
+BOOL CenterWindow(HWND hwndChild)
+{
+   RECT    rChild, rWorkArea;
+   int     wChild, hChild;
+   int     xNew, yNew;
+   BOOL  bResult;
+
+   /* Get the Height and Width of the child window */
+   GetWindowRect(hwndChild, &rChild);
+   wChild = rChild.right - rChild.left;
+   hChild = rChild.bottom - rChild.top;
+
+   /* Get the limits of the 'workarea' */
+   bResult = SystemParametersInfo(
+      SPI_GETWORKAREA,  /* system parameter to query or set */
+      sizeof(RECT),
+      &rWorkArea,
+      0);
+   if (!bResult) {
+      rWorkArea.left = rWorkArea.top = 0;
+      rWorkArea.right = GetSystemMetrics(SM_CXSCREEN);
+      rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
+   }
+
+   /* Calculate new X position, then adjust for workarea */
+   xNew = (rWorkArea.right - wChild)/2;
+   yNew = (rWorkArea.bottom - hChild)/2;
+   return SetWindowPos (hwndChild, HWND_TOP, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
+}
+
+static void addItem(HWND hDlg, LPSTR lpStr, HBITMAP hBmp) 
+{ 
+    int nItem; 
+    nItem = SendMessage(hDlg, LB_ADDSTRING, 0, (LPARAM)lpStr); 
+    SendMessage(hDlg, LB_SETITEMDATA, nItem, (LPARAM)hBmp); 
+} 
+
+
+BOOL RunAndForgetConsole(LPTSTR szCmdLine,
+                         LPDWORD nRetValue,
+                         BOOL  showConsole)
+{
+    
+    
+    STARTUPINFO stInfo;
+    PROCESS_INFORMATION prInfo;
+    BOOL bResult;
+    ZeroMemory(&stInfo, sizeof(stInfo));
+    stInfo.cb = sizeof(stInfo);
+    stInfo.dwFlags = STARTF_USESHOWWINDOW;
+    stInfo.wShowWindow = showConsole ? SW_SHOWNORMAL : SW_HIDE;
+    
+    bResult = CreateProcess(NULL,
+        szCmdLine,
+        NULL,
+        NULL,
+        TRUE,
+        CREATE_NEW_CONSOLE ,
+        NULL,
+        NULL ,
+        &stInfo,
+        &prInfo);
+    if (nRetValue)
+        *nRetValue = GetLastError();
+    
+    CloseHandle(prInfo.hThread);
+    CloseHandle(prInfo.hProcess);
+    if (!bResult) 
+        return FALSE;
+    else
+        return TRUE;
+}
+
+
+BOOL ApacheManageService(LPCSTR szServiceName, LPCSTR szImagePath, DWORD dwCommand)
+{
+    
+    CHAR szBuf[MAX_PATH];
+    LPSTR sPos;
+    DWORD retCode;
+    BOOL  retValue;
+    BOOL  ntService = TRUE;
+    SC_HANDLE   schService;
+    SC_HANDLE   schSCManager;    
+    SERVICE_STATUS schSStatus;
+    LPSTR *args;
+    int   ticks;
+
+    if (ap_OSVersion == OS_VERSION_WIN9X)
+    {
+        sPos = strstr(szImagePath, "-k start");
+        if (sPos)
+        {
+            lstrcpyn(szBuf, szImagePath, sPos - szImagePath);
+            switch (dwCommand)
+            {
+            case SERVICE_CONTROL_STOP:
+                lstrcat(szBuf, " -k stop -n ");
+                break;
+            case SERVICE_CONTROL_CONTINUE:
+                lstrcat(szBuf, " -k start -n ");
+                break;
+            case SERVICE_APACHE_RESTART:
+                lstrcat(szBuf, " -k restart -n ");
+                break;
+            case SERVICE_CONTROL_SHUTDOWN:
+                lstrcat(szBuf, " -k uninstall -n ");
+                break;
+            default:
+                return FALSE;
+            }
+            lstrcat(szBuf, szServiceName);
+        }
+        else
+            return FALSE;
+        if (!RunAndForgetConsole(szBuf, &retCode, FALSE))
+        {
+            ErrorMessage(retCode);
+            return FALSE;
+        }
+    }
+    else
+    {
+        sPos = strstr(szImagePath, "--ntservice");
+        if (!sPos)
+        {
+            sPos = strstr(szImagePath, "-k runservice");
+            ntService = FALSE;
+        }
+        if (sPos)
+        {
+            lstrcpyn(szBuf, szImagePath, sPos - szImagePath);
+            if (dwCommand == SERVICE_CONTROL_SHUTDOWN)
+            {
+                lstrcat(szBuf, " -k uninstall -n ");
+                lstrcat(szBuf, szServiceName);
+                if (!RunAndForgetConsole(szBuf, &retCode, FALSE))
+                {
+                    ErrorMessage(retCode);
+                    return FALSE;
+                }
+                else
+                    return TRUE;
+            }
+        }
+        else
+            return FALSE;
+        schSCManager = OpenSCManager(
+            NULL,
+            NULL,
+            SC_MANAGER_ALL_ACCESS
+           );
+        if (!schSCManager)
+            return FALSE;
+        
+        schService = OpenService(schSCManager, szServiceName, SERVICE_ALL_ACCESS);
+        if (schService != NULL)
+        {
+            retValue = FALSE;
+            switch (dwCommand)
+            {
+                case SERVICE_CONTROL_STOP:
+                    if(ControlService(schService, SERVICE_CONTROL_STOP, &schSStatus)) 
+                    {
+                        Sleep(1000);
+                        while (QueryServiceStatus(schService, &schSStatus)) 
+                        {
+                            if (schSStatus.dwCurrentState == SERVICE_STOP_PENDING)
+                                Sleep(1000);
+                            else
+                                break;
+                        }
+                    }
+                    if (QueryServiceStatus(schService, &schSStatus))
+                    {
+                        if(schSStatus.dwCurrentState == SERVICE_STOPPED)
+                            retValue = TRUE;
+                    }
+                break;                
+                case SERVICE_CONTROL_CONTINUE:
+                    args = (char **)malloc(3 * sizeof(char*));
+                    args[0] = szBuf;
+                    if (ntService)
+                        args[1] = "--ntservice";
+                    else
+                    {
+                        args[1] = "-k";
+                        args[2] = "runservice";
+                    }
+                    if(StartService(schService, ntService ? 2 : 3, args)) 
+                    {
+                        Sleep(1000);
+                        while (QueryServiceStatus(schService, &schSStatus)) 
+                        {
+                            if (schSStatus.dwCurrentState == SERVICE_START_PENDING)
+                                Sleep(1000);
+                            else
+                                break;
+                        }
+                    }
+                    if (QueryServiceStatus(schService, &schSStatus))
+                    {
+                        if(schSStatus.dwCurrentState == SERVICE_RUNNING)
+                            retValue = TRUE;
+                    }
+                    /* is this OK to do? */
+                    free(args);
+                break;                
+                case SERVICE_APACHE_RESTART:
+                    if(ControlService(schService, SERVICE_APACHE_RESTART, &schSStatus)) 
+                    {
+                        ticks = 60;
+                        while(schSStatus.dwCurrentState == SERVICE_START_PENDING) 
+                        {
+                            Sleep(1000);
+                            if(!QueryServiceStatus(schService, &schSStatus))
+                            {
+                                CloseServiceHandle(schService);
+                                CloseServiceHandle(schSCManager);
+                                return FALSE;
+                            }
+                            if (!--ticks)
+                                break;
+                        }
+                    }
+                    if(schSStatus.dwCurrentState == SERVICE_RUNNING)
+                            retValue = TRUE;
+                break;                
+            }
+            CloseServiceHandle(schService);
+            CloseServiceHandle(schSCManager);
+            return retValue;
+            
+        }
+        else
+            ap_rescanServices = TRUE;
+        
+        CloseServiceHandle(schSCManager);
+        return FALSE;
+    }
+    
+    return FALSE;
+}
+
+BOOL IsServiceRunning(LPCSTR szServiceName, LPDWORD lpdwPid)
+{
+
+    DWORD dwPid, dwBytes;
+    HWND  hWnd;
+    SC_HANDLE   schService;
+    SC_HANDLE   schSCManager;    
+    SERVICE_STATUS schSStatus;
+    SERVICE_STATUS_PROCESS schSProcess;
+    HANDLE hAdvapi;
+    QUERYSERVICESTATUSEX pQueryServiceStatusEx = NULL;
+
+    if (ap_OSVersion == OS_VERSION_WIN9X)
+    {
+        hWnd = FindWindow("ApacheWin95ServiceMonitor", szServiceName);
+        if (hWnd && GetWindowThreadProcessId(hWnd, &dwPid))
+        {
+            *lpdwPid = dwPid;
+            return TRUE;
+        }
+        else
+            return FALSE;
+    }
+    else
+    {
+
+        dwPid = 0;
+        schSCManager = OpenSCManager(
+                            NULL,
+                            NULL,
+                            SC_MANAGER_ALL_ACCESS
+                           );
+        if (!schSCManager)
+            return FALSE;
+
+        schService = OpenService(schSCManager, szServiceName, SERVICE_QUERY_STATUS);
+        if (schService != NULL)
+        {
+            if (QueryServiceStatus(schService, &schSStatus))
+            {
+                
+                dwPid = schSStatus.dwCurrentState;
+                if (lpdwPid)
+                    *lpdwPid = 1;
+            }
+            if (ap_OSVersion == OS_VERSION_WIN2K)
+            {
+                hAdvapi = LoadLibrary("ADVAPI32.DLL");
+                if (hAdvapi != NULL)
+                    pQueryServiceStatusEx = (QUERYSERVICESTATUSEX)GetProcAddress(hAdvapi,
+                                                                    "QueryServiceStatusEx");
+                if (hAdvapi != NULL && pQueryServiceStatusEx != NULL)
+                {
+                    if (pQueryServiceStatusEx(schService, SC_STATUS_PROCESS_INFO,
+                                               (LPBYTE)&schSProcess, sizeof(SERVICE_STATUS_PROCESS), &dwBytes))
+                    {
+                        dwPid = schSProcess.dwCurrentState;
+                        if (lpdwPid)
+                            *lpdwPid = schSProcess.dwProcessId;
+                    }
+                }
+                if (hAdvapi != NULL)
+                    FreeLibrary(hAdvapi);
+            }
+            CloseServiceHandle(schService);
+            CloseServiceHandle(schSCManager);
+            return dwPid == SERVICE_RUNNING ? TRUE : FALSE;
+        }
+        else
+            ap_rescanServices = TRUE;
+
+        CloseServiceHandle(schSCManager);
+        return FALSE;
+
+    }
+
+    return FALSE;
+}
+
+BOOL FindRunningServices()
+{
+    int i = 0;
+    DWORD dwPid;
+    BOOL rv = FALSE;
+    while (ap_stServices[i].szServiceName != NULL)
+    {    
+        if (!IsServiceRunning(ap_stServices[i].szServiceName, &dwPid))
+            dwPid = 0;
+        if (ap_stServices[i].dwPid != dwPid)
+            rv = TRUE;
+        ap_stServices[i].dwPid = dwPid;
+        ++i;
+    }                        
+    return rv;
+}
+
+BOOL GetApacheServicesStatus()
+{
+
+    CHAR szKey[MAX_PATH];
+    CHAR achKey[MAX_PATH];
+    CHAR szImagePath[MAX_PATH];
+    CHAR szBuf[MAX_PATH];
+
+    HKEY hKey, hSubKey;
+    DWORD retCode, rv, dwKeyType;
+    DWORD dwBufLen = MAX_PATH;
+
+    int  i, stPos = 0;
+    ap_rescanServices = FALSE;
+
+    retCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                            "System\\CurrentControlSet\\Services\\",
+                            0, KEY_READ, &hKey);
+    if (retCode != ERROR_SUCCESS)
+    {
+        ErrorMessage(retCode);
+        return FALSE;
+    }
+    ap_ClearServicesSt();
+    for (i = 0, retCode = ERROR_SUCCESS; retCode == ERROR_SUCCESS; i++)
+    {
+
+        retCode = RegEnumKey(hKey, i, achKey, MAX_PATH);
+        if (retCode == ERROR_SUCCESS)
+        {
+            lstrcpy(szKey, "System\\CurrentControlSet\\Services\\");
+            lstrcat(szKey, achKey);
+
+            if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, 
+                KEY_QUERY_VALUE, &hSubKey) == ERROR_SUCCESS)
+            {
+                dwBufLen = MAX_PATH;
+                rv = RegQueryValueEx(hSubKey, "ImagePath", NULL,
+                                      &dwKeyType, szImagePath, &dwBufLen);
+
+                if (rv == ERROR_SUCCESS && (dwKeyType == REG_SZ  || dwKeyType == REG_EXPAND_SZ) && dwBufLen)
+                {
+                    lstrcpy(szBuf, szImagePath);
+                    CharLower(szBuf);
+                    if (strstr(szBuf, "\\apache.exe") != NULL)
+                    {
+                        ap_stServices[stPos].szServiceName = strdup(achKey);
+                        ap_stServices[stPos].szImagePath = strdup(szImagePath);
+                        dwBufLen = MAX_PATH;
+                        if (RegQueryValueEx(hSubKey, "Description", NULL,
+                                      &dwKeyType, szBuf, &dwBufLen) == ERROR_SUCCESS)
+                            ap_stServices[stPos].szDescription = strdup(szBuf);
+
+                        dwBufLen = MAX_PATH;
+                        if (RegQueryValueEx(hSubKey, "DisplayName", NULL,
+                                      &dwKeyType, szBuf, &dwBufLen) == ERROR_SUCCESS)
+                            ap_stServices[stPos].szDisplayName= strdup(szBuf);
+                        ++stPos;
+                        if (stPos >= MAX_APACHE_SERVICES)
+                            retCode = !ERROR_SUCCESS;
+                    }
+                }
+                RegCloseKey(hSubKey);
+            }
+        }
+    }
+    RegCloseKey(hKey);
+    FindRunningServices();
+    return TRUE;
+}
+
+LRESULT CALLBACK ServiceDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+
+    CHAR tchBuffer[MAX_PATH]; 
+    CHAR tsbBuffer[MAX_PATH];
+    CHAR szBuf[64];
+    HWND hListBox;
+    static HWND hStatusBar; 
+    TEXTMETRIC tm; 
+    int i, y; 
+    HDC hdcMem; 
+    LPMEASUREITEMSTRUCT lpmis; 
+    LPDRAWITEMSTRUCT lpdis; 
+    RECT rcBitmap; 
+    UINT nItem;
+
+    switch (message) 
+    { 
+        case WM_INITDIALOG: 
+            ShowWindow(hDlg, SW_HIDE);
+            ap_hServiceDlg = hDlg;
+            hbmpStart = LoadBitmap(ap_hInstance, MAKEINTRESOURCE(IDB_BMPRUN)); 
+            hbmpStop  = LoadBitmap(ap_hInstance, MAKEINTRESOURCE(IDB_BMPSTOP)); 
+
+            Button_Enable(GetDlgItem(hDlg, IDC_SSTART), FALSE);
+            Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), FALSE);
+            Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), FALSE);
+            Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), FALSE);
+            hListBox = GetDlgItem(hDlg, IDL_SERVICES); 
+            hStatusBar = CreateStatusWindow(SBT_TOOLTIPS | WS_CHILD | WS_VISIBLE,
+                                            "", hDlg, IDC_STATBAR);            
+            if (GetApacheServicesStatus())
+            {
+                i = 0;
+                while (ap_stServices[i].szServiceName != NULL)
+                {    
+                    addItem(hListBox, ap_stServices[i].szDisplayName, 
+                        ap_stServices[i].dwPid == 0 ? hbmpStop : hbmpStart);
+                    ++i;
+                }
+            }
+            CenterWindow(hDlg);
+            ShowWindow(hDlg, SW_SHOW);
+            SetFocus(hListBox); 
+            SendMessage(hListBox, LB_SETCURSEL, 0, 0); 
+            return TRUE;
+        break;
+        case WM_UPDATEMESSAGE:
+            hListBox = GetDlgItem(hDlg, IDL_SERVICES); 
+            SendMessage(hListBox, LB_RESETCONTENT, 0, 0); 
+            Button_Enable(GetDlgItem(hDlg, IDC_SSTART), FALSE);
+            Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), FALSE);
+            Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), FALSE);
+            Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), FALSE);
+            i = 0;
+            while (ap_stServices[i].szServiceName != NULL)
+            {    
+                addItem(hListBox, ap_stServices[i].szDisplayName, 
+                    ap_stServices[i].dwPid == 0 ? hbmpStop : hbmpStart);
+                ++i;
+            }
+            SendMessage(hListBox, LB_SETCURSEL, 0, 0); 
+            /* Dirty hack to bring the window to the foreground */
+             SetWindowPos(hDlg, HWND_TOPMOST, 0, 0, 0, 0,
+                                    SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
+            SetWindowPos(hDlg, HWND_NOTOPMOST, 0, 0, 0, 0,
+                                    SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
+            SetFocus(hListBox); 
+            return TRUE;
+        break;
+        case WM_MEASUREITEM: 
+            lpmis = (LPMEASUREITEMSTRUCT) lParam; 
+            lpmis->itemHeight = 16; 
+            return TRUE; 
+        case WM_DRAWITEM: 
+            lpdis = (LPDRAWITEMSTRUCT) lParam; 
+            if (lpdis->itemID == -1) 
+            { 
+                break; 
+            } 
+            switch (lpdis->itemAction) 
+            { 
+                case ODA_SELECT: 
+                case ODA_DRAWENTIRE: 
+                    hbmpPicture = (HBITMAP)SendMessage(lpdis->hwndItem, 
+                        LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0); 
+                    hdcMem = CreateCompatibleDC(lpdis->hDC); 
+                    hbmpOld = SelectObject(hdcMem, hbmpPicture); 
+                    BitBlt(lpdis->hDC, 
+                        lpdis->rcItem.left, lpdis->rcItem.top, 
+                        lpdis->rcItem.right - lpdis->rcItem.left, 
+                        lpdis->rcItem.bottom - lpdis->rcItem.top, 
+                        hdcMem, 0, 0, SRCCOPY); 
+                    SendMessage(lpdis->hwndItem, LB_GETTEXT, 
+                        lpdis->itemID, (LPARAM) tchBuffer); 
+                    GetTextMetrics(lpdis->hDC, &tm);  
+                    y = (lpdis->rcItem.bottom + lpdis->rcItem.top - 
+                        tm.tmHeight) / 2; 
+  
+                    SelectObject(hdcMem, hbmpOld); 
+                    DeleteDC(hdcMem); 
+                    rcBitmap.left = lpdis->rcItem.left + XBITMAP; 
+                    rcBitmap.top = lpdis->rcItem.top; 
+                    rcBitmap.right = lpdis->rcItem.right; 
+                    rcBitmap.bottom = lpdis->rcItem.top + YBITMAP; 
+
+                    if (lpdis->itemState & ODS_SELECTED) 
+                    { 
+                        if (hbmpPicture == hbmpStop)
+                        {
+                            Button_Enable(GetDlgItem(hDlg, IDC_SSTART), TRUE);
+                            Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), FALSE);
+                            Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), FALSE);
+                            Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), TRUE);
+                            
+                        }
+                        else if (hbmpPicture == hbmpStart) 
+                        {
+                            Button_Enable(GetDlgItem(hDlg, IDC_SSTART), FALSE);
+                            Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), TRUE);
+                            Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), TRUE);
+                            Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), FALSE);
+                            
+                        }
+                        i = 0;
+                        while (ap_stServices[i].szServiceName != NULL)
+                        {    
+                            if (lstrcmp(ap_stServices[i].szDisplayName, tchBuffer) == 0)
+                            {
+                                if (ap_stServices[i].szDescription)
+                                    lstrcpy(tsbBuffer, ap_stServices[i].szDescription); 
+                                else
+                                    lstrcpy(tsbBuffer, ap_stServices[i].szImagePath); 
+                                if (ap_stServices[i].dwPid != 0)
+                                {                
+                                    if (ap_stServices[i].dwPid & 0xFF000000)
+                                        sprintf(szBuf, "  PID : 0x%08X", ap_stServices[i].dwPid);
+                                    else
+                                        sprintf(szBuf, "  PID : %d", ap_stServices[i].dwPid);
+                                    lstrcat(tsbBuffer, szBuf);
+                                }
+                                SendMessage(hStatusBar, SB_SETTEXT, 0, (LPARAM)tsbBuffer);
+                                break;
+                            }
+                            ++i;
+                        }
+                        
+                        SetTextColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); 
+                        SetBkColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT)); 
+                        FillRect(lpdis->hDC, &rcBitmap, (HBRUSH)(COLOR_HIGHLIGHTTEXT)); 
+                    } 
+                    else
+                    {
+                       SetTextColor(lpdis->hDC, GetSysColor(COLOR_MENUTEXT)); 
+                       SetBkColor(lpdis->hDC, GetSysColor(COLOR_WINDOW)); 
+                       FillRect(lpdis->hDC, &rcBitmap, (HBRUSH)(COLOR_WINDOW+1)); 
+                    }
+                    TextOut(lpdis->hDC, 
+                        XBITMAP + 6, 
+                        y, 
+                        tchBuffer, 
+                        strlen(tchBuffer)); 
+                    break; 
+                case ODA_FOCUS: 
+                    break; 
+            } 
+            return TRUE;  
+        case WM_COMMAND: 
+            switch (LOWORD(wParam)) 
+            { 
+                case IDL_SERVICES:
+                    switch (HIWORD(wParam))
+                    {
+                        case LBN_DBLCLK:
+                            GetApacheServicesStatus();
+                            SendMessage(hDlg, WM_UPDATEMESSAGE, 0, 0);
+                            return TRUE;
+                    }
+                break;
+                case IDOK: 
+                    EndDialog(hDlg, TRUE); 
+                    return TRUE; 
+                break;
+                case IDC_SSTART: 
+                    Button_Enable(GetDlgItem(hDlg, IDC_SSTART), FALSE);
+                    hListBox = GetDlgItem(hDlg, IDL_SERVICES); 
+                    nItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0); 
+                    if (nItem != LB_ERR)
+                    {
+                        ApacheManageService(ap_stServices[nItem].szServiceName,
+                                             ap_stServices[nItem].szImagePath,
+                                             SERVICE_CONTROL_CONTINUE);
+                    }
+                    Button_Enable(GetDlgItem(hDlg, IDC_SSTART), TRUE);
+                    return TRUE;
+                break;
+                case IDC_SSTOP: 
+                    Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), FALSE);
+                    hListBox = GetDlgItem(hDlg, IDL_SERVICES); 
+                    nItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0); 
+                    if (nItem != LB_ERR)
+                    {
+                        ApacheManageService(ap_stServices[nItem].szServiceName,
+                                             ap_stServices[nItem].szImagePath,
+                                             SERVICE_CONTROL_STOP);
+                    }
+                    Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), TRUE);
+                    return TRUE;
+                break;
+                case IDC_SRESTART: 
+                    Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), FALSE);
+                    hListBox = GetDlgItem(hDlg, IDL_SERVICES); 
+                    nItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0); 
+                    if (nItem != LB_ERR)
+                    {
+                        ApacheManageService(ap_stServices[nItem].szServiceName,
+                                             ap_stServices[nItem].szImagePath,
+                                             SERVICE_APACHE_RESTART);
+                    }
+                    Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), TRUE);
+                    return TRUE;
+                break;
+                case IDC_SUNINSTALL: 
+                    Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), FALSE);
+                    hListBox = GetDlgItem(hDlg, IDL_SERVICES); 
+                    nItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0); 
+                    if (nItem != LB_ERR)
+                    {
+                        ApacheManageService(ap_stServices[nItem].szServiceName,
+                                             ap_stServices[nItem].szImagePath,
+                                             SERVICE_CONTROL_SHUTDOWN);
+                    }
+                    ap_rescanServices = TRUE;
+                    Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), TRUE);
+                    return TRUE;
+                break;
+             }
+        break;
+        case WM_SIZE:
+            switch (LOWORD(wParam)) 
+            { 
+                case SIZE_MINIMIZED:
+                    EndDialog(hDlg, TRUE); 
+                    return TRUE; 
+                break;
+            }
+        break;
+        case WM_ERASEBKGND:
+            
+            break;
+        case WM_CLOSE: 
+            EndDialog(hDlg, TRUE);
+            return TRUE;
+        case WM_DESTROY: 
+            DeleteObject(hbmpStart); 
+            DeleteObject(hbmpStop); 
+            return TRUE; 
+
+        default:
+            return FALSE;
+    }
+    return FALSE;
+}
+
+/* About Box from MS Generic Sample */
+LRESULT CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    static  HFONT hfontDlg;    /* Font for dialog text */
+    static  HFONT hFinePrint;  /* Font for 'fine print' in dialog */
+    DWORD   dwVerInfoSize;     /* Size of version information block */
+    LPSTR   lpVersion;         /* String pointer to 'version' text */
+    DWORD   dwVerHnd=0;        /* An 'ignored' parameter, always '0' */
+    UINT    uVersionLen;
+    WORD    wRootLen;
+    BOOL    bRetCode;
+    int     i;
+    char    szFullPath[256];
+    char    szResult[256];
+    char    szGetName[256];
+    char    szVersion[256];
+    DWORD dwResult;
+    
+    switch (message) {
+    case WM_INITDIALOG:
+        ShowWindow(hDlg, SW_HIDE);
+        ap_hwndAboutDlg = hDlg;
+        
+        hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            VARIABLE_PITCH | FF_SWISS, "");
+        hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            VARIABLE_PITCH | FF_SWISS, "");
+
+        CenterWindow(hDlg);
+        GetModuleFileName(ap_hInstance, szFullPath, sizeof(szFullPath));
+        
+        /* Now lets dive in and pull out the version information: */
+        dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd);
+        if (dwVerInfoSize) {
+            LPSTR   lpstrVffInfo;
+            HANDLE  hMem;
+            hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
+            lpstrVffInfo  = GlobalLock(hMem);
+            GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpstrVffInfo);
+            lstrcpy(szGetName, GetStringRes(IDS_VER_INFO_LANG));
+            
+            wRootLen = lstrlen(szGetName); /* Save this position */
+            
+            /* Set the title of the dialog: */
+            lstrcat(szGetName, "ProductName");
+            bRetCode = VerQueryValue((LPVOID)lpstrVffInfo,
+                (LPSTR)szGetName,
+                (LPVOID)&lpVersion,
+                (UINT *)&uVersionLen);
+            
+            /* Notice order of version and string... */
+            lstrcpy(szResult, "About ");
+            lstrcat(szResult, lpVersion);
+            
+            SetWindowText(hDlg, szResult);
+            
+            /* Walk through the dialog items that we want to replace: */
+            for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) {
+                GetDlgItemText(hDlg, i, szResult, sizeof(szResult));
+                szGetName[wRootLen] = (char)0;
+                lstrcat(szGetName, szResult);
+                uVersionLen   = 0;
+                lpVersion     = NULL;
+                bRetCode      =  VerQueryValue((LPVOID)lpstrVffInfo,
+                    (LPSTR)szGetName,
+                    (LPVOID)&lpVersion,
+                    (UINT *)&uVersionLen);
+                
+                if (bRetCode && uVersionLen && lpVersion) {
+                    /* Replace dialog item text with version info */
+                    lstrcpy(szResult, lpVersion);
+                    SetDlgItemText(hDlg, i, szResult);
+                }
+                else
+                {
+                    dwResult = GetLastError();
+                    
+                    wsprintf(szResult, GetStringRes(IDS_VERSION_ERROR), dwResult);
+                    SetDlgItemText(hDlg, i, szResult);
+                }
+                SendMessage(GetDlgItem(hDlg, i), WM_SETFONT,
+                    (UINT)((i==DLG_VERLAST)?hFinePrint:hfontDlg),
+                    TRUE);
+            }
+            
+            
+            GlobalUnlock(hMem);
+            GlobalFree(hMem);
+            
+        } 
+        
+        SendMessage(GetDlgItem(hDlg, IDC_LABEL), WM_SETFONT,
+            (WPARAM)hfontDlg,(LPARAM)TRUE);
+        if (!GetSystemOSVersion(szVersion, NULL))
+            strcpy(szVersion, "Unknown Version");
+        SetWindowText(GetDlgItem(hDlg, IDC_OSVERSION), szVersion);
+        ShowWindow(hDlg, SW_SHOW);
+        return (TRUE);
+        
+      case WM_COMMAND:
+          if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
+              EndDialog(hDlg, TRUE);
+              DeleteObject(hfontDlg);
+              DeleteObject(hFinePrint);
+              return (TRUE);
+          }
+          break;
+   }
+   
+   return FALSE;
+}
+
+VOID CALLBACK MainTimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
+{
+    if (ap_rescanServices)
+    {
+        GetApacheServicesStatus();
+        ShowNotifyIcon(hWnd, NIM_MODIFY);
+        if (ap_hServiceDlg)
+        {
+            SendMessage(ap_hServiceDlg, WM_UPDATEMESSAGE, 0, 0);
+
+        }
+    }
+    else if (FindRunningServices())
+    {
+        ShowNotifyIcon(hWnd, NIM_MODIFY);
+        if (ap_hServiceDlg)
+        {
+            SendMessage(ap_hServiceDlg, WM_UPDATEMESSAGE, 0, 0);
+
+        }
+    }
+}
+
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
+                          WPARAM wParam, LPARAM lParam)
+{
+    if (message == ap_uiTaskbarCreated)
+    {
+        /* reinstall tray icon */
+        ShowNotifyIcon(hWnd, NIM_ADD);
+        return DefWindowProc(hWnd, message, wParam, lParam);
+    }
+    switch (message) 
+    {
+        case WM_CREATE:
+            GetSystemOSVersion(NULL, &ap_OSVersion);
+            GetApacheServicesStatus();
+            ShowNotifyIcon(hWnd, NIM_ADD);
+            SetTimer(hWnd, 10, 1000, (TIMERPROC)MainTimerProc);
+              ap_hServiceDlg = NULL;                      
+        break;
+        case WM_QUIT:
+            ShowNotifyIcon(hWnd, NIM_DELETE);
+        break;
+        case WM_TRAYMESSAGE:
+            switch(lParam)
+            {
+                case WM_LBUTTONDBLCLK:
+                   if (!dlgServiceOn)
+                   {
+                       dlgServiceOn = TRUE;
+                       DialogBox(ap_hInstance, MAKEINTRESOURCE(IDD_APSRVMON_DIALOG),
+                             hWnd, (DLGPROC)ServiceDlgProc);
+                       dlgServiceOn = FALSE;
+                       ap_hServiceDlg = NULL;
+                   }
+                   else if (ap_hServiceDlg)
+                   {
+                       /* Dirty hack to bring the window to the foreground */
+                       SetWindowPos(ap_hServiceDlg, HWND_TOPMOST, 0, 0, 0, 0,
+                                    SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
+                       SetWindowPos(ap_hServiceDlg, HWND_NOTOPMOST, 0, 0, 0, 0,
+                                    SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
+                       SetFocus(ap_hServiceDlg);
+                   }
+                break;
+                case WM_RBUTTONUP:
+                    ShowTryPopupMenu(hWnd);
+                break;    
+            }
+            break;
+        case WM_COMMAND:
+            switch (LOWORD(wParam))
+            {
+               case IDM_RESTORE:
+                   if (!dlgServiceOn)
+                   {
+                       dlgServiceOn = TRUE;
+                       DialogBox(ap_hInstance, MAKEINTRESOURCE(IDD_APSRVMON_DIALOG),
+                             hWnd, (DLGPROC)ServiceDlgProc);
+                       dlgServiceOn = FALSE;
+                       ap_hServiceDlg = NULL;
+                   }
+                   else if (ap_hServiceDlg)
+                       SetFocus(ap_hServiceDlg);
+               break;
+               case IDM_ABOUT:
+                   if (!dlgAboutOn)
+                   {
+                      dlgAboutOn = TRUE;
+                      DialogBox(ap_hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX),
+                             hWnd, (DLGPROC)AboutDlgProc);
+                      dlgAboutOn = FALSE;
+                      ap_hwndAboutDlg = NULL;                      
+                   }
+                   else if (ap_hwndAboutDlg)
+                       SetFocus(ap_hwndAboutDlg);
+
+                break;
+                case IDM_EXIT:
+                    PostQuitMessage(0);
+                    return TRUE;
+                break;
+            }
+        default:
+            return DefWindowProc(hWnd, message, wParam, lParam);
+    }
+
+    return FALSE;
+}
+
+/* Create main invisible window */
+HWND CreateMainWindow(HINSTANCE hInstance)
+{
+    HWND       hWnd = NULL;
+    WNDCLASSEX wcex;
+
+    wcex.cbSize = sizeof(WNDCLASSEX); 
+
+    wcex.style          = CS_HREDRAW | CS_VREDRAW;
+    wcex.lpfnWndProc    = (WNDPROC)WndProc;
+    wcex.cbClsExtra     = 0;
+    wcex.cbWndExtra     = 0;
+    wcex.hInstance      = hInstance;
+    wcex.hIcon          = LoadIcon(hInstance, (LPCTSTR)IDI_APSRVMON);
+    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
+    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
+    wcex.lpszMenuName   = (LPCSTR)IDC_APSRVMON;
+    wcex.lpszClassName  = szWindowClass;
+    wcex.hIconSm        = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_APSMALL);
+
+    if (RegisterClassEx(&wcex))
+    {
+        hWnd = CreateWindow(szWindowClass, szTitle,
+                             0, 0, 0, 0, 0,
+                             NULL, NULL, hInstance, NULL);
+    }
+
+    return hWnd;
+
+}
+
+
+int WINAPI WinMain(HINSTANCE hInstance,
+                    HINSTANCE hPrevInstance,
+                    LPTSTR lpCmdLine,
+                    int nCmdShow)
+{
+    HWND    hwnd;
+    MSG     msg;
+    /* single instance mutex */
+    HANDLE hMutex = CreateMutex(NULL, FALSE, "APSRVMON_MUTEX");
+    if((hMutex == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS))
+    {
+        if (hMutex)
+            CloseHandle(hMutex);
+
+        return 0;
+    }
+
+    InitCommonControls();
+    ap_hInstance = hInstance;
+
+    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
+    LoadString(hInstance, IDC_APSRVMON,  szWindowClass, MAX_LOADSTRING);
+    ap_icoStop  = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICOSTOP));
+    ap_icoRun   = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICORUN));
+    ap_uiTaskbarCreated = RegisterWindowMessage("TaskbarCreated");
+
+    ZeroMemory(ap_stServices, sizeof(ST_APACHE_SERVICE) * MAX_APACHE_SERVICES);
+    hwnd = CreateMainWindow(hInstance);
+    if (hwnd != NULL)
+    {
+        while (GetMessage(&msg, NULL, 0, 0) == TRUE) 
+        {
+            TranslateMessage(&msg);
+            DispatchMessage(&msg);
+        }    
+        ap_ClearServicesSt();
+    }
+    CloseHandle(hMutex);
+    return 0;
+}
diff --git a/support/win32/ApacheMonitor.dsp b/support/win32/ApacheMonitor.dsp
new file mode 100644 (file)
index 0000000..8699a75
--- /dev/null
@@ -0,0 +1,143 @@
+# Microsoft Developer Studio Project File - Name="ApacheMonitor" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=ApacheMonitor - 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 "ApacheMonitor.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 "ApacheMonitor.mak" CFG="ApacheMonitor - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ApacheMonitor - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "ApacheMonitor - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ApacheMonitor - 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 "_WINDOWS" /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fd"Release/ApacheMonitor" /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 /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib comctl32.lib shell32.lib version.lib /nologo /subsystem:windows /map /machine:I386
+
+!ELSEIF  "$(CFG)" == "ApacheMonitor - 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 "_WINDOWS" /FD /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Fd"Debug/ApacheMonitor" /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 /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib comctl32.lib shell32.lib version.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ApacheMonitor - Win32 Release"
+# Name "ApacheMonitor - Win32 Debug"
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "*.rc,*.ico,*.bmp"
+# Begin Source File
+
+SOURCE=.\apache_header.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ApacheMonitor.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\ApacheMonitor.rc
+
+!IF  "$(CFG)" == "ApacheMonitor - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "ApacheMonitor - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\aprun.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\apsmall.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\apsrvmon.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\apstop.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\srun.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\sstop.bmp
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\ApacheMonitor.c
+# End Source File
+# End Target
+# End Project
diff --git a/support/win32/ApacheMonitor.h b/support/win32/ApacheMonitor.h
new file mode 100644 (file)
index 0000000..4bdfc15
--- /dev/null
@@ -0,0 +1,49 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by ApSrvmon.rc
+//
+#define IDD_APSRVMON_DIALOG             101
+#define IDD_ABOUTBOX                    102
+#define IDS_APP_TITLE                   103
+#define IDM_RESTORE                     104
+#define IDM_ABOUT                       105
+#define IDM_EXIT                        106
+#define IDI_APSRVMONM                   107
+#define IDI_APSRVMON                    108
+#define IDI_APSMALL                     109
+#define IDC_APSRVMON                    110
+#define IDS_VERSION_ERROR               111
+#define IDS_VER_INFO_LANG               112
+#define IDR_MAINFRAME                   128
+#define IDI_ICOSTOP                     129
+#define IDI_ICORUN                      130
+#define IDC_STATBAR                     134
+#define DLG_VERFIRST                    140
+#define IDC_COMPANY                     140
+#define IDC_FILEDESC                    141
+#define IDC_PRODVER                     142
+#define IDC_COPYRIGHT                   143
+#define IDC_OSVERSION                   144
+#define IDC_TRADEMARK                   145
+#define DLG_VERLAST                     145
+#define IDC_LABEL                       146
+#define IDB_BMPSTOP                     155
+#define IDB_BMPRUN                      156
+#define IDB_BMPHEADER                   158
+#define IDL_SERVICES                    1003
+#define IDC_SSTART                      1004
+#define IDC_SSTOP                       1005
+#define IDC_SRESTART                    1006
+#define IDC_SUNINSTALL                  1008
+#define IDC_STATIC                      -1
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        159
+#define _APS_NEXT_COMMAND_VALUE         32771
+#define _APS_NEXT_CONTROL_VALUE         1009
+#define _APS_NEXT_SYMED_VALUE           110
+#endif
+#endif
diff --git a/support/win32/ApacheMonitor.ico b/support/win32/ApacheMonitor.ico
new file mode 100644 (file)
index 0000000..cd28dc5
Binary files /dev/null and b/support/win32/ApacheMonitor.ico differ
diff --git a/support/win32/apache_header.bmp b/support/win32/apache_header.bmp
new file mode 100644 (file)
index 0000000..7b116fc
Binary files /dev/null and b/support/win32/apache_header.bmp differ
diff --git a/support/win32/aprun.ico b/support/win32/aprun.ico
new file mode 100644 (file)
index 0000000..dbd5832
Binary files /dev/null and b/support/win32/aprun.ico differ
diff --git a/support/win32/apstop.ico b/support/win32/apstop.ico
new file mode 100644 (file)
index 0000000..fba49ad
Binary files /dev/null and b/support/win32/apstop.ico differ
diff --git a/support/win32/srun.bmp b/support/win32/srun.bmp
new file mode 100644 (file)
index 0000000..90ecd46
Binary files /dev/null and b/support/win32/srun.bmp differ
diff --git a/support/win32/sstop.bmp b/support/win32/sstop.bmp
new file mode 100644 (file)
index 0000000..ba73d87
Binary files /dev/null and b/support/win32/sstop.bmp differ
diff --git a/support/win32/wintty.c b/support/win32/wintty.c
new file mode 100644 (file)
index 0000000..28c5979
--- /dev/null
@@ -0,0 +1,328 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 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.
+ * ====================================================================
+ */
+
+/* --------------------------------------------------------------------
+ *
+ * wintty : a Apache/WinNT support utility for monitoring and 
+ *          reflecting user feedback from the Apache process via
+ *          stdin/stdout, even as running within the service context.
+ *
+ * Originally contributed by William Rowe <wrowe@covalent.net>
+ *
+ * Note: this implementation is _very_ experimental, and error handling
+ * is far from complete.  Using it as a cgi or pipe process allows the
+ * programmer to discover if facilities such as reliable piped logs
+ * are working as expected, or answer operator prompts that would
+ * otherwise be discarded by the service process.
+ *
+ * Also note the isservice detection semantics, which far exceed any
+ * mechanism we have discovered thus far.
+ * 
+ * --------------------------------------------------------------------
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+const char *options = 
+"Syntax: wintty [opts] [-?]\n\n"
+"  opts: -c{haracter}   or -l{ine} input\n"
+"\t-q{uiet}       or -e{cho} input\n"
+"\topts: -u{nprocessed} or -p{rocessed} input\n"
+"\topts: -n{owrap}      or -w{rap} output lines\n"
+"\topts: -f{ormatted}   or -r{aw} output lines\n"
+"\topts: -v{erbose} error checking\n"
+"\topts: -? for this message\n\n";
+
+HANDLE herrout;
+BOOL verbose = FALSE;
+
+void printerr(char *fmt, ...) 
+{
+    char str[1024];
+    va_list args;
+    DWORD len;
+    if (!verbose)
+        return;
+    va_start(args, fmt);
+    wvsprintf(str, fmt, args);
+    WriteFile(herrout, str, len = strlen(str), &len, NULL);
+}
+
+DWORD WINAPI feedback(LPVOID pipeout);
+
+int main(int argc, char** argv)
+{
+    char str[1024], *contitle;
+    HANDLE hproc, thread;
+    HANDLE hwinsta, hsavewinsta;
+    HANDLE hdesk, hsavedesk;
+    HANDLE conin, conout;
+    HANDLE pipein, pipeout;
+    HANDLE hstdin, hstdout, hstderr;
+    DWORD conmode;
+    DWORD newinmode = 0, notinmode = 0;
+    DWORD newoutmode = 0, notoutmode = 0;
+    DWORD tid;
+    DWORD len;
+    BOOL isservice = FALSE;
+
+    while (--argc) {
+        ++argv;
+        if (**argv == '/' || **argv == '-') {
+            switch (tolower((*argv)[1])) {
+                case 'c':
+                    notinmode |= ENABLE_LINE_INPUT;          break;
+                case 'l':
+                    newinmode |= ENABLE_LINE_INPUT;          break;
+                case 'q':
+                    notinmode |= ENABLE_ECHO_INPUT;          break;
+                case 'e':
+                    newinmode |= ENABLE_ECHO_INPUT;          break;
+                case 'u':
+                    notinmode |= ENABLE_PROCESSED_INPUT;     break;
+                case 'p':
+                    newinmode |= ENABLE_PROCESSED_INPUT;     break;
+                case 'n':
+                    notoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
+                case 'w':
+                    newoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
+                case 'r':
+                    notoutmode |= ENABLE_PROCESSED_OUTPUT;   break;
+                case 'f':
+                    newoutmode |= ENABLE_PROCESSED_OUTPUT;   break;
+                case 'v':
+                    verbose = TRUE;
+                    break;
+                case 't':
+                    contitle = *(++argv);
+                    --argc;
+                    break;
+                case '?':
+                    printf(options);
+                    exit(1);
+               default:
+                    printf("wintty option %s not recognized, use -? for help.\n\n", *argv);
+                    exit(1);
+            }
+        }
+        else {
+            printf("wintty argument %s not understood, use -? for help.\n\n", *argv);
+            exit(1);
+        }
+    }
+
+    hproc = GetCurrentProcess();
+    herrout = hstderr = GetStdHandle(STD_ERROR_HANDLE);
+    if (!hstderr || hstderr == INVALID_HANDLE_VALUE) {
+        printerr("GetStdHandle(STD_ERROR_HANDLE) failed (%d)\n", GetLastError());
+    }
+    else if (!DuplicateHandle(hproc, hstderr,
+                         hproc, &herrout, 0, FALSE, 
+                         DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+        printerr("DupHandle(stderr) failed (%d)\n", GetLastError());
+    }
+
+    hstdin = GetStdHandle(STD_INPUT_HANDLE);
+    if (!hstdin || hstdin == INVALID_HANDLE_VALUE) {
+        printerr("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n", GetLastError());
+    }
+    else if (!DuplicateHandle(hproc, hstdin,
+                         hproc, &pipein, 0, FALSE, 
+                         DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+        printerr("DupHandle(stdin) failed (%d)\n", GetLastError());
+    }
+
+    hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
+    if (!hstdout || hstdout == INVALID_HANDLE_VALUE) {
+        printerr("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n", GetLastError());
+    }
+    else if (!DuplicateHandle(hproc, hstdout,
+                         hproc, &pipeout, 0, FALSE, 
+                         DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+        printerr("DupHandle(stdout) failed (%d)\n", GetLastError());
+    }
+
+    hsavewinsta = GetProcessWindowStation();
+    if (!hsavewinsta || hsavewinsta == INVALID_HANDLE_VALUE) {
+        printerr("GetProcWinSta() failed (%d)\n", GetLastError());
+    }
+    else if (!GetUserObjectInformation(hsavewinsta, UOI_NAME, str, sizeof(str), &len)) {
+        printerr("GetUserObjectInfo(GetProcWinSta) failed (%d)\n", GetLastError());
+        CloseHandle(hsavewinsta);
+    }
+    else if (strnicmp(str, "Service-", 8) == 0) {
+        isservice = TRUE;
+    }
+    else
+        CloseHandle(hsavewinsta);
+    SetLastError(0);
+
+    if (!FreeConsole())
+        printerr("DupHandle(stdout) failed (%d)\n", GetLastError());
+
+    if (isservice) {
+        hwinsta = OpenWindowStation("WinSta0", TRUE, 
+                            WINSTA_ACCESSCLIPBOARD     
+                          | WINSTA_ACCESSGLOBALATOMS  
+                          | WINSTA_ENUMDESKTOPS
+                          | WINSTA_ENUMERATE     
+                          | WINSTA_READATTRIBUTES  
+                          | WINSTA_READSCREEN
+                          | WINSTA_WRITEATTRIBUTES);
+        if (!hwinsta || hwinsta == INVALID_HANDLE_VALUE) {
+            printerr("OpenWinSta(WinSta0) failed (%d)\n", GetLastError());
+        }
+        else if (!SetProcessWindowStation(hwinsta)) {
+            printerr("SetProcWinSta(WinSta0) failed (%d)\n", GetLastError());
+        }
+       hsavedesk = GetThreadDesktop(GetCurrentThreadId());
+        hdesk = OpenDesktop("Default", 0, TRUE, 
+                            DESKTOP_READOBJECTS     
+                          | DESKTOP_CREATEWINDOW    
+                          | DESKTOP_CREATEMENU      
+                          | DESKTOP_HOOKCONTROL     
+                          | DESKTOP_JOURNALRECORD   
+                          | DESKTOP_JOURNALPLAYBACK 
+                          | DESKTOP_ENUMERATE       
+                          | DESKTOP_WRITEOBJECTS);
+        if (!hdesk || hdesk == INVALID_HANDLE_VALUE) {
+            printerr("OpenDesktop(Default) failed (%d)\n", GetLastError());
+        } 
+        else if (!SetThreadDesktop(hdesk)) {
+            printerr("SetThreadDesktop(Default) failed (%d)\n", GetLastError());
+        }
+    }
+
+    if (!AllocConsole()) {
+        printerr("AllocConsole(Default) failed (%d)\n", GetLastError());
+    }
+
+    if (contitle && !SetConsoleTitle(contitle)) {
+        printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
+    }
+
+    conout = GetStdHandle(STD_OUTPUT_HANDLE);
+    if (!conout || conout == INVALID_HANDLE_VALUE) {
+        printerr("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n", GetLastError());
+    }
+    else if (!GetConsoleMode(conout, &conmode)) {
+        printerr("GetConsoleMode(CONOUT) failed (%d)\n", GetLastError());
+    }
+    else if (!SetConsoleMode(conout, conmode = ((conmode | newoutmode) & ~notoutmode))) {
+        printerr("SetConsoleMode(CONOUT, 0x%x) failed (%d)\n", conmode, GetLastError());
+    }
+
+    conin = GetStdHandle(STD_INPUT_HANDLE);
+    if (!conin || conin == INVALID_HANDLE_VALUE) {
+        printerr("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n", GetLastError());
+    }
+    else if (!GetConsoleMode(conin, &conmode)) {
+        printerr("GetConsoleMode(CONOUT) failed (%d)\n", GetLastError());
+    }
+    else if (!SetConsoleMode(conin, conmode = ((conmode | newinmode) & ~notinmode))) {
+        printerr("SetConsoleMode(CONIN, 0x%x) failed (%d)\n", conmode, GetLastError());
+    }
+
+    thread = CreateThread(NULL, 0, feedback, (LPVOID)pipeout, 0, &tid);
+
+    while (ReadFile(pipein, str, sizeof(str), &len, NULL))
+        if (!len || !WriteFile(conout, str, len, &len, NULL))
+           break;
+
+    printerr("[EOF] from stdin (%d)\n", GetLastError());
+
+    CloseHandle(pipeout);
+    if (!GetConsoleTitle(str, sizeof(str))) {
+        printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
+    }
+    else {
+        strcat(str, " - [Finished]");
+        if (!SetConsoleTitle(str)) {
+            printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
+        }
+    }
+
+    WaitForSingleObject(thread, INFINITE);
+    FreeConsole();
+    CloseHandle(herrout);
+    if (isservice) {
+        if (!SetProcessWindowStation(hsavewinsta)) {
+            len = GetLastError();
+        }
+        if (!SetThreadDesktop(hsavedesk)) {
+            len = GetLastError();
+        }
+        CloseDesktop(hdesk);
+        CloseWindowStation(hwinsta);
+    }
+    return 0;
+}
+
+
+DWORD WINAPI feedback(LPVOID arg)
+{
+    HANDLE conin;
+    HANDLE pipeout = (HANDLE)arg;
+    char *str[1024];
+    DWORD len;
+
+    conin = GetStdHandle(STD_INPUT_HANDLE);
+    if (!conin) {
+        len = GetLastError();
+    }
+
+    while (ReadFile(conin, str, sizeof(str), &len, NULL))
+        if (!len || !WriteFile(pipeout, str, len, &len, NULL))
+            break;
+
+    printerr("[EOF] from Console (%d)\n", GetLastError());
+
+    return 0;
+}
diff --git a/support/win32/wintty.dsp b/support/win32/wintty.dsp
new file mode 100644 (file)
index 0000000..bf62b5e
--- /dev/null
@@ -0,0 +1,90 @@
+# Microsoft Developer Studio Project File - Name="wintty" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=wintty - 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 "wintty.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 "wintty.mak" CFG="wintty - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "wintty - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "wintty - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "wintty - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /I "../srclib/apr/include" /I "../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /Fd"Release/wintty" /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 advapi32.lib /nologo /subsystem:console /incremental:no /map /machine:I386
+# ADD LINK32 kernel32.lib user32.lib advapi32.lib /nologo /subsystem:console /incremental:no /map /machine:I386
+
+!ELSEIF  "$(CFG)" == "wintty - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /ZI /Od /I "../srclib/apr/include" /I "../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /Fd"Debug/wintty" /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 advapi32.lib /nologo /subsystem:console /incremental:no /map /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib advapi32.lib /nologo /subsystem:console /incremental:no /map /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "wintty - Win32 Release"
+# Name "wintty - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\wintty.c
+# End Source File
+# End Target
+# End Project