summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author(no author) <(no author)@unknown>2001-10-16 21:29:08 +0000
committer(no author) <(no author)@unknown>2001-10-16 21:29:08 +0000
commit517bf7127f1807ffeafea29bf3bafc33f1471acd (patch)
tree489237d958e3d03ae82584a2f8f704848628f918
parent5271b3826e7ca321b092d858e061ff01a00983a2 (diff)
downloadhttpd-517bf7127f1807ffeafea29bf3bafc33f1471acd.tar.gz
This commit was manufactured by cvs2svn to create branch 'avendor'.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/avendor@91505 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--build/.cvsignore1
-rw-r--r--build/make_exports.awk120
-rw-r--r--build/make_var_export.awk59
-rw-r--r--build/rules.mk.in264
-rw-r--r--build/win32/.cvsignore2
-rw-r--r--build/win32/apache.icobin0 -> 1078 bytes
-rw-r--r--build/win32/win32ver.awk108
-rw-r--r--docs/docroot/apache_pb.pngbin0 -> 1385 bytes
-rw-r--r--docs/docroot/apache_pb2.pngbin0 -> 1463 bytes
-rw-r--r--docs/docroot/index.html.cz.iso8859-251
-rw-r--r--docs/docroot/index.html.hr.iso8859-235
-rw-r--r--docs/docroot/index.html.nn48
-rw-r--r--docs/docroot/index.html.po.iso8859-240
-rw-r--r--docs/docroot/index.html.var34
-rw-r--r--docs/error/HTTP_BAD_GATEWAY.html.var66
-rw-r--r--docs/error/HTTP_BAD_REQUEST.html.var46
-rw-r--r--docs/error/HTTP_FORBIDDEN.html.var84
-rw-r--r--docs/error/HTTP_GONE.html.var100
-rw-r--r--docs/error/HTTP_INTERNAL_SERVER_ERROR.html.var86
-rw-r--r--docs/error/HTTP_LENGTH_REQUIRED.html.var37
-rw-r--r--docs/error/HTTP_METHOD_NOT_ALLOWED.html.var48
-rw-r--r--docs/error/HTTP_NOT_ACCEPTABLE.html.var49
-rw-r--r--docs/error/HTTP_NOT_FOUND.html.var113
-rw-r--r--docs/error/HTTP_NOT_IMPLEMENTED.html.var45
-rw-r--r--docs/error/HTTP_PRECONDITION_FAILED.html.var44
-rw-r--r--docs/error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var51
-rw-r--r--docs/error/HTTP_REQUEST_TIME_OUT.html.var48
-rw-r--r--docs/error/HTTP_REQUEST_URI_TOO_LARGE.html.var48
-rw-r--r--docs/error/HTTP_SERVICE_UNAVAILABLE.html.var56
-rw-r--r--docs/error/HTTP_UNAUTHORIZED.html.var84
-rw-r--r--docs/error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var45
-rw-r--r--docs/error/HTTP_VARIANT_ALSO_VARIES.html.var51
-rw-r--r--docs/error/contact.html.var30
-rw-r--r--docs/error/include/bottom.html12
-rw-r--r--docs/error/include/spacer.html1
-rw-r--r--docs/error/include/top.html12
-rw-r--r--docs/icons/a.pngbin0 -> 293 bytes
-rw-r--r--docs/icons/alert.black.pngbin0 -> 279 bytes
-rw-r--r--docs/icons/alert.red.pngbin0 -> 298 bytes
-rw-r--r--docs/icons/apache_pb.pngbin0 -> 1385 bytes
-rw-r--r--docs/icons/apache_pb2.pngbin0 -> 1463 bytes
-rw-r--r--docs/icons/back.pngbin0 -> 284 bytes
-rw-r--r--docs/icons/ball.gray.pngbin0 -> 277 bytes
-rw-r--r--docs/icons/ball.red.pngbin0 -> 265 bytes
-rw-r--r--docs/icons/binary.pngbin0 -> 296 bytes
-rw-r--r--docs/icons/binhex.pngbin0 -> 304 bytes
-rw-r--r--docs/icons/blank.pngbin0 -> 195 bytes
-rw-r--r--docs/icons/bomb.pngbin0 -> 356 bytes
-rw-r--r--docs/icons/box1.pngbin0 -> 308 bytes
-rw-r--r--docs/icons/box2.pngbin0 -> 322 bytes
-rw-r--r--docs/icons/broken.pngbin0 -> 305 bytes
-rw-r--r--docs/icons/burst.pngbin0 -> 314 bytes
-rw-r--r--docs/icons/c.pngbin0 -> 285 bytes
-rw-r--r--docs/icons/comp.blue.pngbin0 -> 313 bytes
-rw-r--r--docs/icons/comp.gray.pngbin0 -> 304 bytes
-rw-r--r--docs/icons/compressed.pngbin0 -> 315 bytes
-rw-r--r--docs/icons/continued.pngbin0 -> 272 bytes
-rw-r--r--docs/icons/dir.pngbin0 -> 272 bytes
-rw-r--r--docs/icons/down.pngbin0 -> 232 bytes
-rw-r--r--docs/icons/dvi.pngbin0 -> 290 bytes
-rw-r--r--docs/icons/f.pngbin0 -> 282 bytes
-rw-r--r--docs/icons/folder.open.pngbin0 -> 305 bytes
-rw-r--r--docs/icons/folder.pngbin0 -> 272 bytes
-rw-r--r--docs/icons/folder.sec.pngbin0 -> 290 bytes
-rw-r--r--docs/icons/forward.pngbin0 -> 284 bytes
-rw-r--r--docs/icons/generic.pngbin0 -> 260 bytes
-rw-r--r--docs/icons/generic.red.pngbin0 -> 262 bytes
-rw-r--r--docs/icons/generic.sec.pngbin0 -> 279 bytes
-rw-r--r--docs/icons/hand.right.pngbin0 -> 280 bytes
-rw-r--r--docs/icons/hand.up.pngbin0 -> 280 bytes
-rw-r--r--docs/icons/icon.sheet.pngbin0 -> 8898 bytes
-rw-r--r--docs/icons/image1.pngbin0 -> 307 bytes
-rw-r--r--docs/icons/image2.pngbin0 -> 355 bytes
-rw-r--r--docs/icons/image3.pngbin0 -> 323 bytes
-rw-r--r--docs/icons/index.pngbin0 -> 316 bytes
-rw-r--r--docs/icons/layout.pngbin0 -> 306 bytes
-rw-r--r--docs/icons/left.pngbin0 -> 235 bytes
-rw-r--r--docs/icons/link.pngbin0 -> 297 bytes
-rw-r--r--docs/icons/movie.pngbin0 -> 258 bytes
-rw-r--r--docs/icons/p.pngbin0 -> 284 bytes
-rw-r--r--docs/icons/patch.pngbin0 -> 295 bytes
-rw-r--r--docs/icons/pdf.pngbin0 -> 289 bytes
-rw-r--r--docs/icons/pie0.pngbin0 -> 242 bytes
-rw-r--r--docs/icons/pie1.pngbin0 -> 261 bytes
-rw-r--r--docs/icons/pie2.pngbin0 -> 253 bytes
-rw-r--r--docs/icons/pie3.pngbin0 -> 256 bytes
-rw-r--r--docs/icons/pie4.pngbin0 -> 239 bytes
-rw-r--r--docs/icons/pie5.pngbin0 -> 258 bytes
-rw-r--r--docs/icons/pie6.pngbin0 -> 253 bytes
-rw-r--r--docs/icons/pie7.pngbin0 -> 258 bytes
-rw-r--r--docs/icons/pie8.pngbin0 -> 233 bytes
-rw-r--r--docs/icons/portal.pngbin0 -> 303 bytes
-rw-r--r--docs/icons/ps.pngbin0 -> 287 bytes
-rw-r--r--docs/icons/quill.pngbin0 -> 315 bytes
-rw-r--r--docs/icons/right.pngbin0 -> 233 bytes
-rw-r--r--docs/icons/screw1.pngbin0 -> 312 bytes
-rw-r--r--docs/icons/screw2.pngbin0 -> 318 bytes
-rw-r--r--docs/icons/script.pngbin0 -> 275 bytes
-rw-r--r--docs/icons/small/back.pngbin0 -> 238 bytes
-rw-r--r--docs/icons/small/binary.pngbin0 -> 242 bytes
-rw-r--r--docs/icons/small/binhex.pngbin0 -> 248 bytes
-rw-r--r--docs/icons/small/blank.pngbin0 -> 90 bytes
-rw-r--r--docs/icons/small/broken.pngbin0 -> 254 bytes
-rw-r--r--docs/icons/small/burst.pngbin0 -> 194 bytes
-rw-r--r--docs/icons/small/comp1.pngbin0 -> 197 bytes
-rw-r--r--docs/icons/small/comp2.pngbin0 -> 194 bytes
-rw-r--r--docs/icons/small/compressed.pngbin0 -> 189 bytes
-rw-r--r--docs/icons/small/continued.pngbin0 -> 214 bytes
-rw-r--r--docs/icons/small/dir.pngbin0 -> 175 bytes
-rw-r--r--docs/icons/small/dir2.pngbin0 -> 161 bytes
-rw-r--r--docs/icons/small/doc.pngbin0 -> 269 bytes
-rw-r--r--docs/icons/small/forward.pngbin0 -> 244 bytes
-rw-r--r--docs/icons/small/generic.pngbin0 -> 182 bytes
-rw-r--r--docs/icons/small/generic2.pngbin0 -> 158 bytes
-rw-r--r--docs/icons/small/generic3.pngbin0 -> 147 bytes
-rw-r--r--docs/icons/small/image.pngbin0 -> 178 bytes
-rw-r--r--docs/icons/small/image2.pngbin0 -> 186 bytes
-rw-r--r--docs/icons/small/index.pngbin0 -> 206 bytes
-rw-r--r--docs/icons/small/key.pngbin0 -> 254 bytes
-rw-r--r--docs/icons/small/movie.pngbin0 -> 202 bytes
-rw-r--r--docs/icons/small/patch.pngbin0 -> 250 bytes
-rw-r--r--docs/icons/small/ps.pngbin0 -> 254 bytes
-rw-r--r--docs/icons/small/rainbow.pngbin0 -> 2418 bytes
-rw-r--r--docs/icons/small/sound.pngbin0 -> 176 bytes
-rw-r--r--docs/icons/small/sound2.pngbin0 -> 236 bytes
-rw-r--r--docs/icons/small/tar.pngbin0 -> 227 bytes
-rw-r--r--docs/icons/small/text.pngbin0 -> 202 bytes
-rw-r--r--docs/icons/small/transfer.pngbin0 -> 186 bytes
-rw-r--r--docs/icons/small/unknown.pngbin0 -> 226 bytes
-rw-r--r--docs/icons/small/uu.pngbin0 -> 217 bytes
-rw-r--r--docs/icons/sound1.pngbin0 -> 310 bytes
-rw-r--r--docs/icons/sound2.pngbin0 -> 297 bytes
-rw-r--r--docs/icons/sphere1.pngbin0 -> 326 bytes
-rw-r--r--docs/icons/sphere2.pngbin0 -> 322 bytes
-rw-r--r--docs/icons/tar.pngbin0 -> 261 bytes
-rw-r--r--docs/icons/tex.pngbin0 -> 295 bytes
-rw-r--r--docs/icons/text.pngbin0 -> 273 bytes
-rw-r--r--docs/icons/transfer.pngbin0 -> 319 bytes
-rw-r--r--docs/icons/unknown.pngbin0 -> 291 bytes
-rw-r--r--docs/icons/up.pngbin0 -> 234 bytes
-rw-r--r--docs/icons/uu.pngbin0 -> 280 bytes
-rw-r--r--docs/icons/uuencoded.pngbin0 -> 280 bytes
-rw-r--r--docs/icons/world1.pngbin0 -> 315 bytes
-rw-r--r--docs/icons/world2.pngbin0 -> 339 bytes
-rw-r--r--docs/manual/developer/documenting.html64
-rw-r--r--docs/manual/developer/request.html217
-rw-r--r--docs/manual/faq/index.html67
-rw-r--r--docs/manual/faq/support.html143
-rw-r--r--docs/manual/howto/auth.html318
-rw-r--r--docs/manual/howto/auth.html.en318
-rw-r--r--docs/manual/images/custom_errordocs.pngbin0 -> 17464 bytes
-rw-r--r--docs/manual/images/mod_rewrite_fig1.pngbin0 -> 1659 bytes
-rw-r--r--docs/manual/images/mod_rewrite_fig2.pngbin0 -> 1369 bytes
-rw-r--r--docs/manual/logs.html580
-rw-r--r--docs/manual/logs.html.en580
-rw-r--r--docs/manual/mod/mod_cgid.html101
-rw-r--r--docs/manual/mod/mod_suexec.html95
-rw-r--r--include/util_ldap.h275
-rw-r--r--include/util_time.h111
-rw-r--r--modules/experimental/cache_storage.c276
-rw-r--r--modules/experimental/cache_util.c206
-rw-r--r--modules/experimental/mod_auth_ldap.c862
-rw-r--r--modules/experimental/mod_cache.dsp123
-rw-r--r--modules/experimental/mod_case_filter_in.c131
-rw-r--r--modules/experimental/mod_mem_cache.c509
-rw-r--r--modules/experimental/util_ldap.c1105
-rw-r--r--modules/experimental/util_ldap_cache.c309
-rw-r--r--modules/experimental/util_ldap_cache.h214
-rw-r--r--modules/experimental/util_ldap_cache_mgr.c537
-rw-r--r--modules/generators/mod_cgi.h79
-rw-r--r--modules/mappers/mod_vhost_alias.dsp128
-rw-r--r--modules/metadata/mod_mime_magic.dsp128
-rw-r--r--modules/metadata/mod_unique_id.dsp128
-rw-r--r--modules/proxy/CHANGES134
-rw-r--r--modules/ssl/Makefile.in50
-rw-r--r--modules/ssl/config.m4160
-rw-r--r--modules/ssl/mod_ssl.dsp217
-rw-r--r--modules/test/Makefile.in3
-rw-r--r--modules/test/config.m49
-rw-r--r--modules/test/mod_optional_hook_export.c83
-rw-r--r--modules/test/mod_optional_hook_export.h62
-rw-r--r--modules/test/mod_optional_hook_import.c83
-rw-r--r--server/mpm/mpmt_os2/.cvsignore5
-rw-r--r--server/mpm/mpmt_os2/Makefile.in5
-rw-r--r--server/mpm/mpmt_os2/config5.m45
-rw-r--r--server/mpm/mpmt_os2/mpm.h74
-rw-r--r--server/mpm/mpmt_os2/mpm_default.h116
-rw-r--r--server/mpm/mpmt_os2/mpmt_os2.c587
-rw-r--r--server/mpm/mpmt_os2/mpmt_os2_child.c463
-rw-r--r--server/mpm/worker/.cvsignore5
-rw-r--r--server/mpm/worker/Makefile.in5
-rw-r--r--server/mpm/worker/config5.m45
-rw-r--r--server/mpm/worker/fdqueue.c161
-rw-r--r--server/mpm/worker/fdqueue.h95
-rw-r--r--server/mpm/worker/mpm.h78
-rw-r--r--server/mpm/worker/mpm_default.h154
-rw-r--r--server/mpm/worker/worker.c1660
-rw-r--r--server/util_time.c175
-rw-r--r--support/abs.dsp131
-rw-r--r--support/config.m445
-rwxr-xr-xsupport/list_hooks.pl77
-rw-r--r--support/win32/.cvsignore3
-rw-r--r--support/win32/ApacheMonitor.c1367
-rw-r--r--support/win32/ApacheMonitor.dsp143
-rw-r--r--support/win32/ApacheMonitor.h49
-rw-r--r--support/win32/ApacheMonitor.icobin0 -> 1078 bytes
-rw-r--r--support/win32/apache_header.bmpbin0 -> 6183 bytes
-rw-r--r--support/win32/aprun.icobin0 -> 318 bytes
-rw-r--r--support/win32/apstop.icobin0 -> 318 bytes
-rw-r--r--support/win32/srun.bmpbin0 -> 246 bytes
-rw-r--r--support/win32/sstop.bmpbin0 -> 246 bytes
-rw-r--r--support/win32/wintty.c328
-rw-r--r--support/win32/wintty.dsp90
213 files changed, 16149 insertions, 0 deletions
diff --git a/build/.cvsignore b/build/.cvsignore
new file mode 100644
index 0000000000..29d51bb51c
--- /dev/null
+++ b/build/.cvsignore
@@ -0,0 +1 @@
+rules.mk
diff --git a/build/make_exports.awk b/build/make_exports.awk
new file mode 100644
index 0000000000..27fd631eb9
--- /dev/null
+++ b/build/make_exports.awk
@@ -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
index 0000000000..62263f10e6
--- /dev/null
+++ b/build/make_var_export.awk
@@ -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
index 0000000000..659cbcf877
--- /dev/null
+++ b/build/rules.mk.in
@@ -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
index 0000000000..3a75560491
--- /dev/null
+++ b/build/win32/.cvsignore
@@ -0,0 +1,2 @@
+Apache.aps
+Apache.rc
diff --git a/build/win32/apache.ico b/build/win32/apache.ico
new file mode 100644
index 0000000000..5e8adcbfc5
--- /dev/null
+++ b/build/win32/apache.ico
Binary files differ
diff --git a/build/win32/win32ver.awk b/build/win32/win32ver.awk
new file mode 100644
index 0000000000..8862aa87f3
--- /dev/null
+++ b/build/win32/win32ver.awk
@@ -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
index 0000000000..eb99a8cd39
--- /dev/null
+++ b/docs/docroot/apache_pb.png
Binary files differ
diff --git a/docs/docroot/apache_pb2.png b/docs/docroot/apache_pb2.png
new file mode 100644
index 0000000000..28baa70fb8
--- /dev/null
+++ b/docs/docroot/apache_pb2.png
Binary files differ
diff --git a/docs/docroot/index.html.cz.iso8859-2 b/docs/docroot/index.html.cz.iso8859-2
new file mode 100644
index 0000000000..e0cd2ba610
--- /dev/null
+++ b/docs/docroot/index.html.cz.iso8859-2
@@ -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
index 0000000000..7af963d66f
--- /dev/null
+++ b/docs/docroot/index.html.hr.iso8859-2
@@ -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
index 0000000000..67db9570b0
--- /dev/null
+++ b/docs/docroot/index.html.nn
@@ -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
index 0000000000..8b030bd651
--- /dev/null
+++ b/docs/docroot/index.html.po.iso8859-2
@@ -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
index 0000000000..12693fff15
--- /dev/null
+++ b/docs/docroot/index.html.var
@@ -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
index 0000000000..c6f81de0a1
--- /dev/null
+++ b/docs/error/HTTP_BAD_GATEWAY.html.var
@@ -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
index 0000000000..938db8174d
--- /dev/null
+++ b/docs/error/HTTP_BAD_REQUEST.html.var
@@ -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
index 0000000000..9a30ceb9f1
--- /dev/null
+++ b/docs/error/HTTP_FORBIDDEN.html.var
@@ -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
index 0000000000..22aef7c674
--- /dev/null
+++ b/docs/error/HTTP_GONE.html.var
@@ -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
index 0000000000..742e235eb7
--- /dev/null
+++ b/docs/error/HTTP_INTERNAL_SERVER_ERROR.html.var
@@ -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
index 0000000000..24d6909c3c
--- /dev/null
+++ b/docs/error/HTTP_LENGTH_REQUIRED.html.var
@@ -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
index 0000000000..38d2bd8a05
--- /dev/null
+++ b/docs/error/HTTP_METHOD_NOT_ALLOWED.html.var
@@ -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
index 0000000000..fe4236fac7
--- /dev/null
+++ b/docs/error/HTTP_NOT_ACCEPTABLE.html.var
@@ -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
index 0000000000..8a7e8ee5fb
--- /dev/null
+++ b/docs/error/HTTP_NOT_FOUND.html.var
@@ -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
index 0000000000..ed3468eaf3
--- /dev/null
+++ b/docs/error/HTTP_NOT_IMPLEMENTED.html.var
@@ -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
index 0000000000..164bfcef6d
--- /dev/null
+++ b/docs/error/HTTP_PRECONDITION_FAILED.html.var
@@ -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
index 0000000000..af7ef260e0
--- /dev/null
+++ b/docs/error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var
@@ -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
index 0000000000..6295279a03
--- /dev/null
+++ b/docs/error/HTTP_REQUEST_TIME_OUT.html.var
@@ -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
index 0000000000..f34de5558c
--- /dev/null
+++ b/docs/error/HTTP_REQUEST_URI_TOO_LARGE.html.var
@@ -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
index 0000000000..d29f16fac8
--- /dev/null
+++ b/docs/error/HTTP_SERVICE_UNAVAILABLE.html.var
@@ -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
index 0000000000..4152b66146
--- /dev/null
+++ b/docs/error/HTTP_UNAUTHORIZED.html.var
@@ -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
index 0000000000..78c48bd2c4
--- /dev/null
+++ b/docs/error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var
@@ -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
index 0000000000..b558284f47
--- /dev/null
+++ b/docs/error/HTTP_VARIANT_ALSO_VARIES.html.var
@@ -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
index 0000000000..f39136b637
--- /dev/null
+++ b/docs/error/contact.html.var
@@ -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
index 0000000000..a3b23698d3
--- /dev/null
+++ b/docs/error/include/bottom.html
@@ -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
index 0000000000..70c5b25f4a
--- /dev/null
+++ b/docs/error/include/spacer.html
@@ -0,0 +1 @@
+</DL><DL><DD>
diff --git a/docs/error/include/top.html b/docs/error/include/top.html
new file mode 100644
index 0000000000..3e24e48615
--- /dev/null
+++ b/docs/error/include/top.html
@@ -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
index 0000000000..c1840256dc
--- /dev/null
+++ b/docs/icons/a.png
Binary files differ
diff --git a/docs/icons/alert.black.png b/docs/icons/alert.black.png
new file mode 100644
index 0000000000..af6b1246ad
--- /dev/null
+++ b/docs/icons/alert.black.png
Binary files differ
diff --git a/docs/icons/alert.red.png b/docs/icons/alert.red.png
new file mode 100644
index 0000000000..b9222fd524
--- /dev/null
+++ b/docs/icons/alert.red.png
Binary files differ
diff --git a/docs/icons/apache_pb.png b/docs/icons/apache_pb.png
new file mode 100644
index 0000000000..eb99a8cd39
--- /dev/null
+++ b/docs/icons/apache_pb.png
Binary files differ
diff --git a/docs/icons/apache_pb2.png b/docs/icons/apache_pb2.png
new file mode 100644
index 0000000000..28baa70fb8
--- /dev/null
+++ b/docs/icons/apache_pb2.png
Binary files differ
diff --git a/docs/icons/back.png b/docs/icons/back.png
new file mode 100644
index 0000000000..2d8d353bbc
--- /dev/null
+++ b/docs/icons/back.png
Binary files differ
diff --git a/docs/icons/ball.gray.png b/docs/icons/ball.gray.png
new file mode 100644
index 0000000000..7b756f2d82
--- /dev/null
+++ b/docs/icons/ball.gray.png
Binary files differ
diff --git a/docs/icons/ball.red.png b/docs/icons/ball.red.png
new file mode 100644
index 0000000000..05f3e50629
--- /dev/null
+++ b/docs/icons/ball.red.png
Binary files differ
diff --git a/docs/icons/binary.png b/docs/icons/binary.png
new file mode 100644
index 0000000000..c5119d1e1e
--- /dev/null
+++ b/docs/icons/binary.png
Binary files differ
diff --git a/docs/icons/binhex.png b/docs/icons/binhex.png
new file mode 100644
index 0000000000..eff532202d
--- /dev/null
+++ b/docs/icons/binhex.png
Binary files differ
diff --git a/docs/icons/blank.png b/docs/icons/blank.png
new file mode 100644
index 0000000000..3802c03c9c
--- /dev/null
+++ b/docs/icons/blank.png
Binary files differ
diff --git a/docs/icons/bomb.png b/docs/icons/bomb.png
new file mode 100644
index 0000000000..5261a0575e
--- /dev/null
+++ b/docs/icons/bomb.png
Binary files differ
diff --git a/docs/icons/box1.png b/docs/icons/box1.png
new file mode 100644
index 0000000000..c55fccf8dc
--- /dev/null
+++ b/docs/icons/box1.png
Binary files differ
diff --git a/docs/icons/box2.png b/docs/icons/box2.png
new file mode 100644
index 0000000000..26d14325d9
--- /dev/null
+++ b/docs/icons/box2.png
Binary files differ
diff --git a/docs/icons/broken.png b/docs/icons/broken.png
new file mode 100644
index 0000000000..e8fd150a33
--- /dev/null
+++ b/docs/icons/broken.png
Binary files differ
diff --git a/docs/icons/burst.png b/docs/icons/burst.png
new file mode 100644
index 0000000000..2329898f2a
--- /dev/null
+++ b/docs/icons/burst.png
Binary files differ
diff --git a/docs/icons/c.png b/docs/icons/c.png
new file mode 100644
index 0000000000..41593b36b3
--- /dev/null
+++ b/docs/icons/c.png
Binary files differ
diff --git a/docs/icons/comp.blue.png b/docs/icons/comp.blue.png
new file mode 100644
index 0000000000..60ff156deb
--- /dev/null
+++ b/docs/icons/comp.blue.png
Binary files differ
diff --git a/docs/icons/comp.gray.png b/docs/icons/comp.gray.png
new file mode 100644
index 0000000000..01538f8f31
--- /dev/null
+++ b/docs/icons/comp.gray.png
Binary files differ
diff --git a/docs/icons/compressed.png b/docs/icons/compressed.png
new file mode 100644
index 0000000000..de7276dbc0
--- /dev/null
+++ b/docs/icons/compressed.png
Binary files differ
diff --git a/docs/icons/continued.png b/docs/icons/continued.png
new file mode 100644
index 0000000000..8f656e27f2
--- /dev/null
+++ b/docs/icons/continued.png
Binary files differ
diff --git a/docs/icons/dir.png b/docs/icons/dir.png
new file mode 100644
index 0000000000..6b97905067
--- /dev/null
+++ b/docs/icons/dir.png
Binary files differ
diff --git a/docs/icons/down.png b/docs/icons/down.png
new file mode 100644
index 0000000000..be3904b045
--- /dev/null
+++ b/docs/icons/down.png
Binary files differ
diff --git a/docs/icons/dvi.png b/docs/icons/dvi.png
new file mode 100644
index 0000000000..19c417f227
--- /dev/null
+++ b/docs/icons/dvi.png
Binary files differ
diff --git a/docs/icons/f.png b/docs/icons/f.png
new file mode 100644
index 0000000000..c946f5b316
--- /dev/null
+++ b/docs/icons/f.png
Binary files differ
diff --git a/docs/icons/folder.open.png b/docs/icons/folder.open.png
new file mode 100644
index 0000000000..dd2d7e0cc2
--- /dev/null
+++ b/docs/icons/folder.open.png
Binary files differ
diff --git a/docs/icons/folder.png b/docs/icons/folder.png
new file mode 100644
index 0000000000..6b97905067
--- /dev/null
+++ b/docs/icons/folder.png
Binary files differ
diff --git a/docs/icons/folder.sec.png b/docs/icons/folder.sec.png
new file mode 100644
index 0000000000..833f59ac51
--- /dev/null
+++ b/docs/icons/folder.sec.png
Binary files differ
diff --git a/docs/icons/forward.png b/docs/icons/forward.png
new file mode 100644
index 0000000000..c5584a4c30
--- /dev/null
+++ b/docs/icons/forward.png
Binary files differ
diff --git a/docs/icons/generic.png b/docs/icons/generic.png
new file mode 100644
index 0000000000..0227cabb5c
--- /dev/null
+++ b/docs/icons/generic.png
Binary files differ
diff --git a/docs/icons/generic.red.png b/docs/icons/generic.red.png
new file mode 100644
index 0000000000..be63249beb
--- /dev/null
+++ b/docs/icons/generic.red.png
Binary files differ
diff --git a/docs/icons/generic.sec.png b/docs/icons/generic.sec.png
new file mode 100644
index 0000000000..0bd3d96bdc
--- /dev/null
+++ b/docs/icons/generic.sec.png
Binary files differ
diff --git a/docs/icons/hand.right.png b/docs/icons/hand.right.png
new file mode 100644
index 0000000000..93035c658a
--- /dev/null
+++ b/docs/icons/hand.right.png
Binary files differ
diff --git a/docs/icons/hand.up.png b/docs/icons/hand.up.png
new file mode 100644
index 0000000000..1405a6f156
--- /dev/null
+++ b/docs/icons/hand.up.png
Binary files differ
diff --git a/docs/icons/icon.sheet.png b/docs/icons/icon.sheet.png
new file mode 100644
index 0000000000..b875cb6b1c
--- /dev/null
+++ b/docs/icons/icon.sheet.png
Binary files differ
diff --git a/docs/icons/image1.png b/docs/icons/image1.png
new file mode 100644
index 0000000000..c1374fde33
--- /dev/null
+++ b/docs/icons/image1.png
Binary files differ
diff --git a/docs/icons/image2.png b/docs/icons/image2.png
new file mode 100644
index 0000000000..606d4fb87e
--- /dev/null
+++ b/docs/icons/image2.png
Binary files differ
diff --git a/docs/icons/image3.png b/docs/icons/image3.png
new file mode 100644
index 0000000000..701fb1e135
--- /dev/null
+++ b/docs/icons/image3.png
Binary files differ
diff --git a/docs/icons/index.png b/docs/icons/index.png
new file mode 100644
index 0000000000..9a0de35058
--- /dev/null
+++ b/docs/icons/index.png
Binary files differ
diff --git a/docs/icons/layout.png b/docs/icons/layout.png
new file mode 100644
index 0000000000..0a97c1c475
--- /dev/null
+++ b/docs/icons/layout.png
Binary files differ
diff --git a/docs/icons/left.png b/docs/icons/left.png
new file mode 100644
index 0000000000..d6e2404a81
--- /dev/null
+++ b/docs/icons/left.png
Binary files differ
diff --git a/docs/icons/link.png b/docs/icons/link.png
new file mode 100644
index 0000000000..4714d0ef40
--- /dev/null
+++ b/docs/icons/link.png
Binary files differ
diff --git a/docs/icons/movie.png b/docs/icons/movie.png
new file mode 100644
index 0000000000..5615180de8
--- /dev/null
+++ b/docs/icons/movie.png
Binary files differ
diff --git a/docs/icons/p.png b/docs/icons/p.png
new file mode 100644
index 0000000000..3fbe0e8801
--- /dev/null
+++ b/docs/icons/p.png
Binary files differ
diff --git a/docs/icons/patch.png b/docs/icons/patch.png
new file mode 100644
index 0000000000..808ed7865f
--- /dev/null
+++ b/docs/icons/patch.png
Binary files differ
diff --git a/docs/icons/pdf.png b/docs/icons/pdf.png
new file mode 100644
index 0000000000..516142bb47
--- /dev/null
+++ b/docs/icons/pdf.png
Binary files differ
diff --git a/docs/icons/pie0.png b/docs/icons/pie0.png
new file mode 100644
index 0000000000..12e0200c97
--- /dev/null
+++ b/docs/icons/pie0.png
Binary files differ
diff --git a/docs/icons/pie1.png b/docs/icons/pie1.png
new file mode 100644
index 0000000000..c44c793ed8
--- /dev/null
+++ b/docs/icons/pie1.png
Binary files differ
diff --git a/docs/icons/pie2.png b/docs/icons/pie2.png
new file mode 100644
index 0000000000..e0b7167d91
--- /dev/null
+++ b/docs/icons/pie2.png
Binary files differ
diff --git a/docs/icons/pie3.png b/docs/icons/pie3.png
new file mode 100644
index 0000000000..820a3c35fa
--- /dev/null
+++ b/docs/icons/pie3.png
Binary files differ
diff --git a/docs/icons/pie4.png b/docs/icons/pie4.png
new file mode 100644
index 0000000000..35490d857c
--- /dev/null
+++ b/docs/icons/pie4.png
Binary files differ
diff --git a/docs/icons/pie5.png b/docs/icons/pie5.png
new file mode 100644
index 0000000000..359b7d377f
--- /dev/null
+++ b/docs/icons/pie5.png
Binary files differ
diff --git a/docs/icons/pie6.png b/docs/icons/pie6.png
new file mode 100644
index 0000000000..4b293eae18
--- /dev/null
+++ b/docs/icons/pie6.png
Binary files differ
diff --git a/docs/icons/pie7.png b/docs/icons/pie7.png
new file mode 100644
index 0000000000..6bfa2d06ae
--- /dev/null
+++ b/docs/icons/pie7.png
Binary files differ
diff --git a/docs/icons/pie8.png b/docs/icons/pie8.png
new file mode 100644
index 0000000000..716cf2822b
--- /dev/null
+++ b/docs/icons/pie8.png
Binary files differ
diff --git a/docs/icons/portal.png b/docs/icons/portal.png
new file mode 100644
index 0000000000..937c0f87cd
--- /dev/null
+++ b/docs/icons/portal.png
Binary files differ
diff --git a/docs/icons/ps.png b/docs/icons/ps.png
new file mode 100644
index 0000000000..ccccf730b6
--- /dev/null
+++ b/docs/icons/ps.png
Binary files differ
diff --git a/docs/icons/quill.png b/docs/icons/quill.png
new file mode 100644
index 0000000000..b697770a88
--- /dev/null
+++ b/docs/icons/quill.png
Binary files differ
diff --git a/docs/icons/right.png b/docs/icons/right.png
new file mode 100644
index 0000000000..41f8529a84
--- /dev/null
+++ b/docs/icons/right.png
Binary files differ
diff --git a/docs/icons/screw1.png b/docs/icons/screw1.png
new file mode 100644
index 0000000000..11673ab97d
--- /dev/null
+++ b/docs/icons/screw1.png
Binary files differ
diff --git a/docs/icons/screw2.png b/docs/icons/screw2.png
new file mode 100644
index 0000000000..5d7d2cf65e
--- /dev/null
+++ b/docs/icons/screw2.png
Binary files differ
diff --git a/docs/icons/script.png b/docs/icons/script.png
new file mode 100644
index 0000000000..2520570a77
--- /dev/null
+++ b/docs/icons/script.png
Binary files differ
diff --git a/docs/icons/small/back.png b/docs/icons/small/back.png
new file mode 100644
index 0000000000..2257df2140
--- /dev/null
+++ b/docs/icons/small/back.png
Binary files differ
diff --git a/docs/icons/small/binary.png b/docs/icons/small/binary.png
new file mode 100644
index 0000000000..2e2e1b073d
--- /dev/null
+++ b/docs/icons/small/binary.png
Binary files differ
diff --git a/docs/icons/small/binhex.png b/docs/icons/small/binhex.png
new file mode 100644
index 0000000000..9deab419b6
--- /dev/null
+++ b/docs/icons/small/binhex.png
Binary files differ
diff --git a/docs/icons/small/blank.png b/docs/icons/small/blank.png
new file mode 100644
index 0000000000..86f57a504f
--- /dev/null
+++ b/docs/icons/small/blank.png
Binary files differ
diff --git a/docs/icons/small/broken.png b/docs/icons/small/broken.png
new file mode 100644
index 0000000000..79c998c8c3
--- /dev/null
+++ b/docs/icons/small/broken.png
Binary files differ
diff --git a/docs/icons/small/burst.png b/docs/icons/small/burst.png
new file mode 100644
index 0000000000..2b21436c78
--- /dev/null
+++ b/docs/icons/small/burst.png
Binary files differ
diff --git a/docs/icons/small/comp1.png b/docs/icons/small/comp1.png
new file mode 100644
index 0000000000..6d8c3459ed
--- /dev/null
+++ b/docs/icons/small/comp1.png
Binary files differ
diff --git a/docs/icons/small/comp2.png b/docs/icons/small/comp2.png
new file mode 100644
index 0000000000..57f7ad197b
--- /dev/null
+++ b/docs/icons/small/comp2.png
Binary files differ
diff --git a/docs/icons/small/compressed.png b/docs/icons/small/compressed.png
new file mode 100644
index 0000000000..43acd8b943
--- /dev/null
+++ b/docs/icons/small/compressed.png
Binary files differ
diff --git a/docs/icons/small/continued.png b/docs/icons/small/continued.png
new file mode 100644
index 0000000000..db17c42465
--- /dev/null
+++ b/docs/icons/small/continued.png
Binary files differ
diff --git a/docs/icons/small/dir.png b/docs/icons/small/dir.png
new file mode 100644
index 0000000000..9bd6256bdb
--- /dev/null
+++ b/docs/icons/small/dir.png
Binary files differ
diff --git a/docs/icons/small/dir2.png b/docs/icons/small/dir2.png
new file mode 100644
index 0000000000..836daf49ef
--- /dev/null
+++ b/docs/icons/small/dir2.png
Binary files differ
diff --git a/docs/icons/small/doc.png b/docs/icons/small/doc.png
new file mode 100644
index 0000000000..c560df21d3
--- /dev/null
+++ b/docs/icons/small/doc.png
Binary files differ
diff --git a/docs/icons/small/forward.png b/docs/icons/small/forward.png
new file mode 100644
index 0000000000..4ddbc61e14
--- /dev/null
+++ b/docs/icons/small/forward.png
Binary files differ
diff --git a/docs/icons/small/generic.png b/docs/icons/small/generic.png
new file mode 100644
index 0000000000..16374a12bb
--- /dev/null
+++ b/docs/icons/small/generic.png
Binary files differ
diff --git a/docs/icons/small/generic2.png b/docs/icons/small/generic2.png
new file mode 100644
index 0000000000..40d60c1df2
--- /dev/null
+++ b/docs/icons/small/generic2.png
Binary files differ
diff --git a/docs/icons/small/generic3.png b/docs/icons/small/generic3.png
new file mode 100644
index 0000000000..aa38963afa
--- /dev/null
+++ b/docs/icons/small/generic3.png
Binary files differ
diff --git a/docs/icons/small/image.png b/docs/icons/small/image.png
new file mode 100644
index 0000000000..d92f0a5fcc
--- /dev/null
+++ b/docs/icons/small/image.png
Binary files differ
diff --git a/docs/icons/small/image2.png b/docs/icons/small/image2.png
new file mode 100644
index 0000000000..4049bda561
--- /dev/null
+++ b/docs/icons/small/image2.png
Binary files differ
diff --git a/docs/icons/small/index.png b/docs/icons/small/index.png
new file mode 100644
index 0000000000..080453e215
--- /dev/null
+++ b/docs/icons/small/index.png
Binary files differ
diff --git a/docs/icons/small/key.png b/docs/icons/small/key.png
new file mode 100644
index 0000000000..1a45f67df3
--- /dev/null
+++ b/docs/icons/small/key.png
Binary files differ
diff --git a/docs/icons/small/movie.png b/docs/icons/small/movie.png
new file mode 100644
index 0000000000..7c126042c9
--- /dev/null
+++ b/docs/icons/small/movie.png
Binary files differ
diff --git a/docs/icons/small/patch.png b/docs/icons/small/patch.png
new file mode 100644
index 0000000000..c39f14435a
--- /dev/null
+++ b/docs/icons/small/patch.png
Binary files differ
diff --git a/docs/icons/small/ps.png b/docs/icons/small/ps.png
new file mode 100644
index 0000000000..5c604230d0
--- /dev/null
+++ b/docs/icons/small/ps.png
Binary files differ
diff --git a/docs/icons/small/rainbow.png b/docs/icons/small/rainbow.png
new file mode 100644
index 0000000000..175053cb43
--- /dev/null
+++ b/docs/icons/small/rainbow.png
Binary files differ
diff --git a/docs/icons/small/sound.png b/docs/icons/small/sound.png
new file mode 100644
index 0000000000..6e3e95d3d0
--- /dev/null
+++ b/docs/icons/small/sound.png
Binary files differ
diff --git a/docs/icons/small/sound2.png b/docs/icons/small/sound2.png
new file mode 100644
index 0000000000..bc46eb48fe
--- /dev/null
+++ b/docs/icons/small/sound2.png
Binary files differ
diff --git a/docs/icons/small/tar.png b/docs/icons/small/tar.png
new file mode 100644
index 0000000000..12f0347bf9
--- /dev/null
+++ b/docs/icons/small/tar.png
Binary files differ
diff --git a/docs/icons/small/text.png b/docs/icons/small/text.png
new file mode 100644
index 0000000000..b4e30f466d
--- /dev/null
+++ b/docs/icons/small/text.png
Binary files differ
diff --git a/docs/icons/small/transfer.png b/docs/icons/small/transfer.png
new file mode 100644
index 0000000000..324048170a
--- /dev/null
+++ b/docs/icons/small/transfer.png
Binary files differ
diff --git a/docs/icons/small/unknown.png b/docs/icons/small/unknown.png
new file mode 100644
index 0000000000..cad7e7a7aa
--- /dev/null
+++ b/docs/icons/small/unknown.png
Binary files differ
diff --git a/docs/icons/small/uu.png b/docs/icons/small/uu.png
new file mode 100644
index 0000000000..ef87c82ee6
--- /dev/null
+++ b/docs/icons/small/uu.png
Binary files differ
diff --git a/docs/icons/sound1.png b/docs/icons/sound1.png
new file mode 100644
index 0000000000..7a766be6cc
--- /dev/null
+++ b/docs/icons/sound1.png
Binary files differ
diff --git a/docs/icons/sound2.png b/docs/icons/sound2.png
new file mode 100644
index 0000000000..4511290939
--- /dev/null
+++ b/docs/icons/sound2.png
Binary files differ
diff --git a/docs/icons/sphere1.png b/docs/icons/sphere1.png
new file mode 100644
index 0000000000..2198ae89ec
--- /dev/null
+++ b/docs/icons/sphere1.png
Binary files differ
diff --git a/docs/icons/sphere2.png b/docs/icons/sphere2.png
new file mode 100644
index 0000000000..257632ba46
--- /dev/null
+++ b/docs/icons/sphere2.png
Binary files differ
diff --git a/docs/icons/tar.png b/docs/icons/tar.png
new file mode 100644
index 0000000000..6c40521ff8
--- /dev/null
+++ b/docs/icons/tar.png
Binary files differ
diff --git a/docs/icons/tex.png b/docs/icons/tex.png
new file mode 100644
index 0000000000..906622d384
--- /dev/null
+++ b/docs/icons/tex.png
Binary files differ
diff --git a/docs/icons/text.png b/docs/icons/text.png
new file mode 100644
index 0000000000..34d0edf86e
--- /dev/null
+++ b/docs/icons/text.png
Binary files differ
diff --git a/docs/icons/transfer.png b/docs/icons/transfer.png
new file mode 100644
index 0000000000..efaf17b682
--- /dev/null
+++ b/docs/icons/transfer.png
Binary files differ
diff --git a/docs/icons/unknown.png b/docs/icons/unknown.png
new file mode 100644
index 0000000000..7c241c383c
--- /dev/null
+++ b/docs/icons/unknown.png
Binary files differ
diff --git a/docs/icons/up.png b/docs/icons/up.png
new file mode 100644
index 0000000000..a69ea00c5b
--- /dev/null
+++ b/docs/icons/up.png
Binary files differ
diff --git a/docs/icons/uu.png b/docs/icons/uu.png
new file mode 100644
index 0000000000..b1d1a8579d
--- /dev/null
+++ b/docs/icons/uu.png
Binary files differ
diff --git a/docs/icons/uuencoded.png b/docs/icons/uuencoded.png
new file mode 100644
index 0000000000..b1d1a8579d
--- /dev/null
+++ b/docs/icons/uuencoded.png
Binary files differ
diff --git a/docs/icons/world1.png b/docs/icons/world1.png
new file mode 100644
index 0000000000..3a65c00d84
--- /dev/null
+++ b/docs/icons/world1.png
Binary files differ
diff --git a/docs/icons/world2.png b/docs/icons/world2.png
new file mode 100644
index 0000000000..9f8a3ea4b3
--- /dev/null
+++ b/docs/icons/world2.png
Binary files differ
diff --git a/docs/manual/developer/documenting.html b/docs/manual/developer/documenting.html
new file mode 100644
index 0000000000..8f8163f426
--- /dev/null
+++ b/docs/manual/developer/documenting.html
@@ -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
index 0000000000..6becc938a4
--- /dev/null
+++ b/docs/manual/developer/request.html
@@ -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
index 0000000000..90c19b7bb9
--- /dev/null
+++ b/docs/manual/faq/index.html
@@ -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
index 0000000000..95aa420132
--- /dev/null
+++ b/docs/manual/faq/support.html
@@ -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
index 0000000000..9311ffcf81
--- /dev/null
+++ b/docs/manual/howto/auth.html
@@ -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
index 0000000000..9311ffcf81
--- /dev/null
+++ b/docs/manual/howto/auth.html.en
@@ -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
index 0000000000..11553cbdee
--- /dev/null
+++ b/docs/manual/images/custom_errordocs.png
Binary files differ
diff --git a/docs/manual/images/mod_rewrite_fig1.png b/docs/manual/images/mod_rewrite_fig1.png
new file mode 100644
index 0000000000..f3a68d9e4c
--- /dev/null
+++ b/docs/manual/images/mod_rewrite_fig1.png
Binary files differ
diff --git a/docs/manual/images/mod_rewrite_fig2.png b/docs/manual/images/mod_rewrite_fig2.png
new file mode 100644
index 0000000000..bc141be769
--- /dev/null
+++ b/docs/manual/images/mod_rewrite_fig2.png
Binary files differ
diff --git a/docs/manual/logs.html b/docs/manual/logs.html
new file mode 100644
index 0000000000..d3ebf480c5
--- /dev/null
+++ b/docs/manual/logs.html
@@ -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
index 0000000000..d3ebf480c5
--- /dev/null
+++ b/docs/manual/logs.html.en
@@ -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
index 0000000000..396d608d5c
--- /dev/null
+++ b/docs/manual/mod/mod_cgid.html
@@ -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
index 0000000000..0910999f1a
--- /dev/null
+++ b/docs/manual/mod/mod_suexec.html
@@ -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
index 0000000000..10dc2eabe5
--- /dev/null
+++ b/include/util_ldap.h
@@ -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
index 0000000000..704d0e02d5
--- /dev/null
+++ b/include/util_time.h
@@ -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
index 0000000000..050628dad1
--- /dev/null
+++ b/modules/experimental/cache_storage.c
@@ -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
index 0000000000..cc37aa972c
--- /dev/null
+++ b/modules/experimental/cache_util.c
@@ -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
index 0000000000..6be891cf17
--- /dev/null
+++ b/modules/experimental/mod_auth_ldap.c
@@ -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
index 0000000000..f62455d62d
--- /dev/null
+++ b/modules/experimental/mod_cache.dsp
@@ -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
index 0000000000..e4ab97feab
--- /dev/null
+++ b/modules/experimental/mod_case_filter_in.c
@@ -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
index 0000000000..2d2781a1a2
--- /dev/null
+++ b/modules/experimental/mod_mem_cache.c
@@ -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
index 0000000000..4e9ca0e501
--- /dev/null
+++ b/modules/experimental/util_ldap.c
@@ -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
index 0000000000..9d9844ddd6
--- /dev/null
+++ b/modules/experimental/util_ldap_cache.c
@@ -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
index 0000000000..18790eb854
--- /dev/null
+++ b/modules/experimental/util_ldap_cache.h
@@ -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
index 0000000000..49eece8958
--- /dev/null
+++ b/modules/experimental/util_ldap_cache_mgr.c
@@ -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
index 0000000000..4eadfaa58a
--- /dev/null
+++ b/modules/generators/mod_cgi.h
@@ -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
index 0000000000..7a892bf83d
--- /dev/null
+++ b/modules/mappers/mod_vhost_alias.dsp
@@ -0,0 +1,128 @@
+# Microsoft Developer Studio Project File - Name="mod_vhost_alias" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_vhost_alias - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_vhost_alias.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_vhost_alias.mak" CFG="mod_vhost_alias - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_vhost_alias - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_vhost_alias - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_vhost_alias - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /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
index 0000000000..f831ae4ec8
--- /dev/null
+++ b/modules/metadata/mod_mime_magic.dsp
@@ -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
index 0000000000..3045d2fc23
--- /dev/null
+++ b/modules/metadata/mod_unique_id.dsp
@@ -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
index 0000000000..d03f68c932
--- /dev/null
+++ b/modules/proxy/CHANGES
@@ -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
index 0000000000..2207d48f3c
--- /dev/null
+++ b/modules/ssl/Makefile.in
@@ -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
index 0000000000..4eeefd7b79
--- /dev/null
+++ b/modules/ssl/config.m4
@@ -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
index 0000000000..c1f46dfb83
--- /dev/null
+++ b/modules/ssl/mod_ssl.dsp
@@ -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
index 0000000000..7c5c149d85
--- /dev/null
+++ b/modules/test/Makefile.in
@@ -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
index 0000000000..01bc0fa971
--- /dev/null
+++ b/modules/test/config.m4
@@ -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
index 0000000000..34ef97b543
--- /dev/null
+++ b/modules/test/mod_optional_hook_export.c
@@ -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
index 0000000000..7494078cdd
--- /dev/null
+++ b/modules/test/mod_optional_hook_export.h
@@ -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
index 0000000000..ebf0158610
--- /dev/null
+++ b/modules/test/mod_optional_hook_import.c
@@ -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
index 0000000000..dc53ac865d
--- /dev/null
+++ b/server/mpm/mpmt_os2/.cvsignore
@@ -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
index 0000000000..38e598edf3
--- /dev/null
+++ b/server/mpm/mpmt_os2/Makefile.in
@@ -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
index 0000000000..b27c296d2a
--- /dev/null
+++ b/server/mpm/mpmt_os2/config5.m4
@@ -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
index 0000000000..37d2f6c20c
--- /dev/null
+++ b/server/mpm/mpmt_os2/mpm.h
@@ -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
index 0000000000..100beb2e61
--- /dev/null
+++ b/server/mpm/mpmt_os2/mpm_default.h
@@ -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
index 0000000000..99806d1c8e
--- /dev/null
+++ b/server/mpm/mpmt_os2/mpmt_os2.c
@@ -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
index 0000000000..36a96ca939
--- /dev/null
+++ b/server/mpm/mpmt_os2/mpmt_os2_child.c
@@ -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
index 0000000000..84df257214
--- /dev/null
+++ b/server/mpm/worker/.cvsignore
@@ -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
index 0000000000..64a02436f3
--- /dev/null
+++ b/server/mpm/worker/Makefile.in
@@ -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
index 0000000000..52ab50e82c
--- /dev/null
+++ b/server/mpm/worker/config5.m4
@@ -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
index 0000000000..147a3b7a94
--- /dev/null
+++ b/server/mpm/worker/fdqueue.c
@@ -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
index 0000000000..b1cab2d018
--- /dev/null
+++ b/server/mpm/worker/fdqueue.h
@@ -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
index 0000000000..23ed27c1ea
--- /dev/null
+++ b/server/mpm/worker/mpm.h
@@ -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
index 0000000000..84c1304bfa
--- /dev/null
+++ b/server/mpm/worker/mpm_default.h
@@ -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
index 0000000000..9f969af51c
--- /dev/null
+++ b/server/mpm/worker/worker.c
@@ -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
index 0000000000..3db076bcea
--- /dev/null
+++ b/server/util_time.c
@@ -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
index 0000000000..cd8467a594
--- /dev/null
+++ b/support/abs.dsp
@@ -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
index 0000000000..ce377479b6
--- /dev/null
+++ b/support/config.m4
@@ -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
index 0000000000..c52c567142
--- /dev/null
+++ b/support/list_hooks.pl
@@ -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
index 0000000000..7658ff5525
--- /dev/null
+++ b/support/win32/.cvsignore
@@ -0,0 +1,3 @@
+Release
+Debug
+*.plg
diff --git a/support/win32/ApacheMonitor.c b/support/win32/ApacheMonitor.c
new file mode 100644
index 0000000000..156f896abc
--- /dev/null
+++ b/support/win32/ApacheMonitor.c
@@ -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
index 0000000000..8699a75baa
--- /dev/null
+++ b/support/win32/ApacheMonitor.dsp
@@ -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
index 0000000000..4bdfc15914
--- /dev/null
+++ b/support/win32/ApacheMonitor.h
@@ -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
index 0000000000..cd28dc520c
--- /dev/null
+++ b/support/win32/ApacheMonitor.ico
Binary files differ
diff --git a/support/win32/apache_header.bmp b/support/win32/apache_header.bmp
new file mode 100644
index 0000000000..7b116fc3da
--- /dev/null
+++ b/support/win32/apache_header.bmp
Binary files differ
diff --git a/support/win32/aprun.ico b/support/win32/aprun.ico
new file mode 100644
index 0000000000..dbd5832602
--- /dev/null
+++ b/support/win32/aprun.ico
Binary files differ
diff --git a/support/win32/apstop.ico b/support/win32/apstop.ico
new file mode 100644
index 0000000000..fba49ad292
--- /dev/null
+++ b/support/win32/apstop.ico
Binary files differ
diff --git a/support/win32/srun.bmp b/support/win32/srun.bmp
new file mode 100644
index 0000000000..90ecd46f17
--- /dev/null
+++ b/support/win32/srun.bmp
Binary files differ
diff --git a/support/win32/sstop.bmp b/support/win32/sstop.bmp
new file mode 100644
index 0000000000..ba73d87aec
--- /dev/null
+++ b/support/win32/sstop.bmp
Binary files differ
diff --git a/support/win32/wintty.c b/support/win32/wintty.c
new file mode 100644
index 0000000000..28c5979cce
--- /dev/null
+++ b/support/win32/wintty.c
@@ -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
index 0000000000..bf62b5e80c
--- /dev/null
+++ b/support/win32/wintty.dsp
@@ -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