]> granicus.if.org Git - apache/commitdiff
Apache 1.3.9 baseline for the Apache 2.0 repository.
authorRoy T. Fielding <fielding@apache.org>
Tue, 24 Aug 1999 06:46:03 +0000 (06:46 +0000)
committerRoy T. Fielding <fielding@apache.org>
Tue, 24 Aug 1999 06:46:03 +0000 (06:46 +0000)
Obtained from: Apache 1.3.9 (minus unused files), tag APACHE_1_3_9
Submitted by: Apache Group

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@83750 13f79535-47bb-0310-9956-ffa450edef68

118 files changed:
docs/man/ab.8 [new file with mode: 0644]
docs/man/apachectl.8 [new file with mode: 0644]
docs/man/apxs.8 [new file with mode: 0644]
docs/man/dbmmanage.1 [new file with mode: 0644]
docs/man/htdigest.1 [new file with mode: 0644]
docs/man/htpasswd.1 [new file with mode: 0644]
docs/man/httpd.8 [new file with mode: 0644]
docs/man/logresolve.8 [new file with mode: 0644]
docs/man/rotatelogs.8 [new file with mode: 0644]
docs/man/suexec.8 [new file with mode: 0644]
modules/aaa/.indent.pro [new file with mode: 0644]
modules/aaa/mod_auth_anon.dsp [new file with mode: 0644]
modules/arch/win32/mod_isapi.c [new file with mode: 0644]
modules/cache/.indent.pro [new file with mode: 0644]
modules/echo/.indent.pro [new file with mode: 0644]
modules/experimental/.indent.pro [new file with mode: 0644]
modules/filters/.indent.pro [new file with mode: 0644]
modules/generators/.indent.pro [new file with mode: 0644]
modules/generators/mod_info.dsp [new file with mode: 0644]
modules/generators/mod_status.dsp [new file with mode: 0644]
modules/http/.indent.pro [new file with mode: 0644]
modules/http/http_core.c [new file with mode: 0644]
modules/http/http_protocol.c [new file with mode: 0644]
modules/http/http_request.c [new file with mode: 0644]
modules/loggers/.indent.pro [new file with mode: 0644]
modules/mappers/.indent.pro [new file with mode: 0644]
modules/mappers/mod_rewrite.dsp [new file with mode: 0644]
modules/mappers/mod_speling.dsp [new file with mode: 0644]
modules/metadata/.indent.pro [new file with mode: 0644]
modules/metadata/mod_cern_meta.dsp [new file with mode: 0644]
modules/metadata/mod_expires.dsp [new file with mode: 0644]
modules/metadata/mod_headers.dsp [new file with mode: 0644]
modules/metadata/mod_usertrack.dsp [new file with mode: 0644]
modules/ssl/.indent.pro [new file with mode: 0644]
os/.indent.pro [new file with mode: 0644]
os/bs2000/.cvsignore [new file with mode: 0644]
os/bs2000/bs2login.c [new file with mode: 0644]
os/bs2000/ebcdic.c [new file with mode: 0644]
os/bs2000/ebcdic.h [new file with mode: 0644]
os/bs2000/os-inline.c [new file with mode: 0644]
os/bs2000/os.c [new file with mode: 0644]
os/bs2000/os.h [new file with mode: 0644]
os/os2/.cvsignore [new file with mode: 0644]
os/os2/os-inline.c [new file with mode: 0644]
os/os2/os.h [new file with mode: 0644]
os/os2/util_os2.c [new file with mode: 0644]
os/tpf/TPFExport [new file with mode: 0644]
os/tpf/ebcdic.c [new file with mode: 0644]
os/tpf/ebcdic.h [new file with mode: 0644]
os/tpf/os-inline.c [new file with mode: 0644]
os/tpf/os.c [new file with mode: 0644]
os/tpf/os.h [new file with mode: 0644]
os/tpf/samples/linkdll.jcl [new file with mode: 0644]
os/tpf/samples/loadset.jcl [new file with mode: 0644]
os/unix/.cvsignore [new file with mode: 0644]
os/unix/os-inline.c [new file with mode: 0644]
os/unix/os.h [new file with mode: 0644]
os/win32/.cvsignore [new file with mode: 0644]
os/win32/MakeModuleMak.cpp [new file with mode: 0644]
os/win32/Module.mak.tmpl [new file with mode: 0644]
os/win32/mod_isapi.c [new file with mode: 0644]
os/win32/modules.c [new file with mode: 0644]
os/win32/os.h [new file with mode: 0644]
os/win32/util_win32.c [new file with mode: 0644]
server/.cvsignore [new file with mode: 0644]
server/.indent.pro [new file with mode: 0644]
server/config.c [new file with mode: 0644]
server/gen_test_char.c [new file with mode: 0644]
server/gen_test_char.dsp [new file with mode: 0644]
server/gen_uri_delims.c [new file with mode: 0644]
server/gen_uri_delims.dsp [new file with mode: 0644]
server/log.c [new file with mode: 0644]
server/main.c [new file with mode: 0644]
server/mpm/winnt/registry.c [new file with mode: 0644]
server/mpm/winnt/service.c [new file with mode: 0644]
server/rfc1413.c [new file with mode: 0644]
server/util.c [new file with mode: 0644]
server/util_date.c [new file with mode: 0644]
server/util_md5.c [new file with mode: 0644]
server/util_script.c [new file with mode: 0644]
server/util_uri.c [new file with mode: 0644]
server/vhost.c [new file with mode: 0644]
srclib/.cvsignore [new file with mode: 0644]
support/.cvsignore [new file with mode: 0644]
support/.indent.pro [new file with mode: 0644]
support/README [new file with mode: 0644]
support/SHA1/README.sha1 [new file with mode: 0644]
support/SHA1/convert-sha1.pl [new file with mode: 0644]
support/SHA1/htpasswd-sha1.pl [new file with mode: 0644]
support/SHA1/ldif-sha1.example [new file with mode: 0644]
support/ab.c [new file with mode: 0644]
support/apxs.in [new file with mode: 0644]
support/dbmmanage [new file with mode: 0644]
support/htdigest.c [new file with mode: 0644]
support/htpasswd.c [new file with mode: 0644]
support/httpd.exp [new file with mode: 0644]
support/log_server_status [new file with mode: 0644]
support/logresolve.c [new file with mode: 0644]
support/logresolve.pl [new file with mode: 0644]
support/phf_abuse_log.cgi [new file with mode: 0644]
support/rotatelogs.c [new file with mode: 0644]
support/split-logfile [new file with mode: 0644]
support/suexec.c [new file with mode: 0644]
support/suexec.h [new file with mode: 0644]
test/.cvsignore [new file with mode: 0644]
test/.indent.pro [new file with mode: 0644]
test/README [new file with mode: 0644]
test/check_chunked [new file with mode: 0644]
test/cls.c [new file with mode: 0644]
test/tcpdumpscii.txt [new file with mode: 0644]
test/test-writev.c [new file with mode: 0644]
test/test_date.c [new file with mode: 0644]
test/test_find.c [new file with mode: 0644]
test/test_limits.c [new file with mode: 0644]
test/test_parser.c [new file with mode: 0644]
test/test_select.c [new file with mode: 0644]
test/time-sem.c [new file with mode: 0644]
test/zb.c [new file with mode: 0644]

diff --git a/docs/man/ab.8 b/docs/man/ab.8
new file mode 100644 (file)
index 0000000..5a06f36
--- /dev/null
@@ -0,0 +1,210 @@
+.TH ab 1 "March 1998"
+.\" $Id: ab.8,v 1.1 1999/08/24 06:45:53 fielding Exp $
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer. 
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\"    software must display the following acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\"    endorse or promote products derived from this software without
+.\"    prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\"    nor may "Apache" appear in their names without prior written
+.\"    permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\"    acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+ab \- Apache HTTP server benchmarking tool
+.SH SYNOPSIS
+.B ab 
+[
+.B \-k
+] [
+.BI \-n " requests"
+] [
+.BI \-t " timelimit"
+] [
+.BI \-c " concurrency"
+] [
+.BI \-p " POST file"
+] [
+.BI \-A " Authenticate username:password"
+] [
+.BI \-P " Proxy Authenticate username:password"
+] [
+.BI \-H " Custom header"
+] [
+.BI \-C " Cookie name=value"
+] [
+.BI \-T " content-type"
+] [
+.BI \-v " verbosity"
+]
+] [
+.BI \-w " output HTML"
+]
+] [
+.BI \-x " <table> attributes"
+]
+] [
+.BI \-y " <tr> attributes"
+]
+] [
+.BI \-z " <td> attributes"
+]
+.I [http://]hostname[:port]/path 
+
+.B ab
+[
+.B \-V
+] [
+.B \-h 
+]
+.PP
+.SH DESCRIPTION
+.B ab
+is a tool for benchmarking your Apache HyperText Transfer Protocol (HTTP)
+server. It is designed to give you an impression on how performant is your
+current Apache installation.  This especially shows you how much requests per
+time your Apache installation is capable to serve. 
+.PP
+.SH OPTIONS
+.TP 12
+.B \-k 
+Enable the HTTP KeepAlive feature, i.e. perform multiple requests within one
+HTTP session instead. Default is no KeepAlive.
+.TP 12
+.BI \-n " requests"
+Number of requests to perform for the benchmarking session.  The default is to
+just perform one single request which usually leads to not very representative
+benchmarking results.
+.TP 12
+.BI \-t " timelimit"
+Seconds to max. spend for benchmarking. This implies
+a 
+.B \-n 
+.B 50000
+internally. Use this to benchmark the server within a fixed total amount of
+time.  Per default there is no timelimit. 
+.TP 12
+.BI \-c " concurrency"
+Number of multiple requests per time to perform. 
+Default is one request per time.
+
+.TP 12
+.BI \-p " POST file"
+File containing data to POST.
+
+.TP 12
+.BI \-A " Authorization username:password"
+Supply BASIC Authentification credentials to the server. The username
+and password are separated by a single ':' and send on the wire uuencoded.
+The string is send regardless of wether the server needs it; (i.e. has
+send an 401. Authentifcation needed).
+
+.TP 12
+.BI \-p " Proxy-Authorization username:password"
+Supply BASIC Authentification credentials to a proxy en-route. The username
+and password are separated by a single ':' and send on the wire uuencoded.
+The string is send regardless of wether the proxy needs it; (i.e. has
+send an 407 Proxy authentifcation needed).
+
+.TP 12
+.BI \-C " Cookie name=value"
+Add a 'Cookie:' line to the request. The argument is typically in the form
+of a 'name=value' pair. This field is repeatable.
+
+.TP 12
+.BI \-p " Header string"
+Postfix extra headers to the request. The argument is typically in the form
+of a valid header line; containing a colon separated field value pair. (i.e. 
+'Accept-Encoding: zip/zop;8bit').
+
+.TP 12
+.BI \-T " content-type"
+Content-type header to use for POST data.
+
+.TP 12
+.B \-v 
+Set verbosity level - 4 and above prints information on headers, 3 and
+above prints response codes (404, 200, etc.), 2 and above prints
+warnings and info.
+
+.TP 12
+.BI \-w
+Print out results in HTML tables.  Default table is two columns wide,
+with a white background.
+.TP 12
+.BI \-x " attributes"
+String to use as attributes for <table>.  Attributes are inserted
+<table 
+.B here
+>
+.TP 12
+.BI \-y " attributes"
+String to use as attributes for <tr>.
+.TP 12
+.BI \-z " attributes"
+String to use as attributes for <td>.
+.TP 12
+.B \-V
+Display version number and exit.
+.TP 12
+.B \-h 
+Display usage information.
+.PD
+.SH BUGS
+There are various statically declared buffers of fixed length. Combined
+with the lazy parsing of the command line arguments, the response headers
+from the server and other external inputs this might bite you.
+.P
+It does not implement HTTP/1.x fully; only accepts some 'expected' forms
+of responses. The rather heavy use of 
+.BR strstr(3)
+shows up top in profile,
+which might indicate a performance problem; i.e. you would measure the
+.BR ab
+performance rather than the server's.
+
+.SH SEE ALSO
+.BR httpd(8)
+.
diff --git a/docs/man/apachectl.8 b/docs/man/apachectl.8
new file mode 100644 (file)
index 0000000..72300eb
--- /dev/null
@@ -0,0 +1,133 @@
+.TH apachectl 1 "September 1997"
+.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer. 
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\"    software must display the following acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\"    endorse or promote products derived from this software without
+.\"    prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\"    nor may "Apache" appear in their names without prior written
+.\"    permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\"    acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+apachectl \- Apache HTTP server control interface
+.SH SYNOPSIS
+.B apachectl 
+\fIcommand\fP [...]
+.SH DESCRIPTION
+.B apachectl
+is a front end to the Apache HyperText Transfer Protocol (HTTP) 
+server.  It is designed to help the administrator control the 
+functioning of the Apache 
+.B httpd
+daemon.  
+.PP
+.B NOTE: 
+If your Apache installation uses non-standard paths, you will need to 
+edit the 
+.B apachectl
+script to set the appropriate paths to your PID file and your 
+.B httpd
+binary.  See the comments in the script for details.
+.PP
+The 
+.B apachectl
+script returns a 0 exit value on success, and >0 if an error 
+occurs.  For more details, view the comments in the script.
+.PP
+Full documentation for Apache is available at 
+.B http://www.apache.org/
+.
+.SH OPTIONS
+The \fIcommand\fP can be any one or more of the following options:
+.TP 12
+.BI start
+Start the Apache daemon.  Gives an error if it is already running.
+.TP
+.BI stop
+Stops the Apache daemon.
+.TP
+.BI restart
+Restarts the Apache daemon by sending it a SIGHUP.  If the daemon
+is not running, it is started.
+This command automatically checks the configuration files via 
+.BI configtest
+before initiating the restart to make sure Apache doesn't die.
+.TP
+.BI fullstatus
+Displays a full status report from 
+.B mod_status. 
+For this to work, you need to have mod_status enabled on your server 
+and a text-based browser such as \fIlynx\fP available on your system.  The
+URL used to access the status report can be set by editing the
+.B STATUSURL
+variable in the script.
+.TP
+.BI status
+Displays a brief status report.  Similar to the fullstatus option,
+except that the list of requests currently being served is omitted.
+.TP
+.BI graceful
+Gracefully restarts the Apache daemon by sending it a SIGUSR1.  If
+the daemon is not running, it is started.  This differs from a
+normal restart in that currently open connections are not aborted.
+A side effect is that old log files will not be closed immediately.
+This means that if used in a log rotation script, a substantial delay may be
+necessary to ensure that the old log files are closed before processing them.
+This command automatically checks the configuration files via 
+.BI configtest
+before initiating the restart to make sure Apache doesn't die.
+.TP
+.BI configtest
+Run a configuration file syntax test. It parses the configuration
+files and either reports 
+.B "Syntax Ok"
+or detailed information about the particular syntax error.
+.TP
+.BI help
+Displays a short help message.
+.SH SEE ALSO
+.BR httpd(8)
+.
diff --git a/docs/man/apxs.8 b/docs/man/apxs.8
new file mode 100644 (file)
index 0000000..464766c
--- /dev/null
@@ -0,0 +1,459 @@
+.TH apxs 8 "April 1998"
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer. 
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\"    software must display the following acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\"    endorse or promote products derived from this software without
+.\"    prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\"    nor may "Apache" appear in their names without prior written
+.\"    permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\"    acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+apxs \- APache eXtenSion tool
+.SH SYNOPSIS
+.B apxs
+.B \-g
+[
+.BI \-S " name=value
+]
+.BI \-n " modname"
+
+.B apxs
+.B \-q
+[
+.BI \-S " name=value
+]
+.IR query " ..."
+
+.B apxs
+.B \-c
+[
+.BI \-S " name=value
+]
+[
+.BI \-o " dsofile"
+]
+[
+.BI \-I " incdir"
+]
+[
+.BI \-D " name=value"
+]
+[
+.BI \-L " libdir"
+]
+[
+.BI \-l " libname"
+]
+[
+.BI \-Wc, "compiler-flags"
+]
+[
+.BI \-Wl, "linker-flags"
+]
+.IR files " ..."
+
+.B apxs
+.B \-i
+[
+.BI \-S " name=value
+]
+[
+.BI \-n " modname"
+]
+[
+.B \-a
+]
+[
+.B \-A
+]
+.IR dsofile " ..."
+
+.B apxs
+.B \-e
+[
+.BI \-S " name=value
+]
+[
+.BI \-n " modname"
+]
+[
+.B \-a
+]
+[
+.B \-A
+]
+.IR dsofile " ..."
+.PP
+.SH DESCRIPTION
+.B apxs
+is a tool for building and installing extension modules for the Apache
+HyperText Transfer Protocol (HTTP) server. This is achieved by building a
+dynamic shared object (DSO) from one or more source or object
+.I files
+which then can be loaded into
+the Apache server under runtime via the
+.B LoadModule
+directive from
+.BR mod_so.
+
+So to use this extension mechanism your platform has
+to support the DSO feature and your
+Apache
+.B httpd
+binary has to be built with the
+.B mod_so
+module.
+The
+.B apxs
+tool automatically complains if this is not the case.
+You can check this yourself by manually running the command
+
+.nf
+  $ httpd -l
+.fi
+
+The module
+.B mod_so
+should be part of the displayed list.
+If these requirements are fulfilled you can easily extend
+your Apache server's functionality by installing your own
+modules with the DSO mechanism by the help of this
+.B apxs
+tool:
+
+.nf
+  $ apxs -i -a -c mod_foo.c
+  gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c
+  ld -Bshareable -o mod_foo.so mod_foo.o
+  cp mod_foo.so /path/to/apache/libexec/mod_foo.so
+  chmod 755 /path/to/apache/libexec/mod_foo.so
+  [activating module `foo' in /path/to/apache/etc/httpd.conf]
+  $ apachectl restart
+  /path/to/apache/sbin/apachectl restart: httpd not running, trying to start
+  [Tue Mar 31 11:27:55 1998] [debug] mod_so.c(303): loaded module foo_module
+  /path/to/apache/sbin/apachectl restart: httpd started
+  $ _
+.fi
+
+The arguments
+.I files
+can be any C source file (.c), a object file (.o) or
+even a library archive (.a). The
+.B apxs
+tool automatically recognizes these extensions and automtaically used the C
+source files for compilation while just using the object and archive files for
+the linking phase. But when using such pre-compiled objects make sure they are
+compiled for position independend code (PIC) to be able to use them for a
+dynamically loaded shared object.
+For instance with GCC you always just have to use
+.BR -fpic .
+For other
+C compilers consult its manual
+page or at watch for the flags
+.B apxs
+uses to compile the object files.
+
+For more details about DSO support in Apache read the documentation
+of
+.B mod_so
+or perhaps even read the
+.B src/modules/standard/mod_so.c
+source file.
+
+.PP
+.SH OPTIONS
+Common options:
+.TP 12
+.BI \-n " modname"
+This explicitly sets the module name for the
+.B \-i
+(install)
+and
+.B \-g
+(template generation) option. Use this to explicitly specify the module name.
+For option
+.B \-g
+this is required, for option
+.B \-i
+the
+.B apxs
+tool tries to determine the name from the source or (as a fallback) at least
+by guessing it from the filename.
+.PP
+Query options:
+.TP 12
+.B \-q 
+Performs a query for 
+.BR apxs 's
+knowledge about certain settings. The
+.I query
+parameters can be one or more of the following strings:
+.nf
+  CC              TARGET
+  CFLAGS          SBINDIR    
+  CFLAGS_SHLIB    INCLUDEDIR 
+  LD_SHLIB        LIBEXECDIR 
+  LDFLAGS_SHLIB   SYSCONFDIR 
+  LIBS_SHLIB
+.fi
+Use this for manually determining settings. For instance use
+.nf
+  INC=-I`apxs -q INCLUDEDIR`
+.fi
+inside your own Makefiles if you need manual access
+to Apache's C header files.
+.PP
+Configuration options:
+.TP 12
+.BI \-S " name=value"
+This option changes the apxs settings described above.
+.PP
+Template Generation options:
+.TP 12
+.B \-g
+This generates a subdirectory
+.I name
+(see option
+.BR \-n ")"
+and there two files: A sample module source file named
+.BI mod_ name.c
+which can be used as a template for creating your own modules or
+as a quick start for playing with the APXS mechanism.
+And a corresponding
+.B Makefile
+for even easier build and installing of this module.
+.PP
+DSO compilation options:
+.TP 12
+.B \-c
+This indicates the compilation operation. It first compiles the C source
+files (.c) of
+.I files
+into corresponding object files (.o) and then builds a dynamically shared object in
+.I dsofile
+by linking these object files plus the remaining
+object files (.o and .a) of
+.I files
+If no
+.B \-o
+option is specified
+the output file is guessed from the first filename in
+.I files
+and thus usually defaults to
+.BI mod_ name.so
+.TP 12
+.BI \-o " dsofile"
+Explicitly specifies the filename of the created dynamically shared object. If
+not specified and the name cannot be guessed from the
+.I files
+list, the fallback name
+.B mod_unknown.so
+is used.
+.TP 12
+.BI \-D " name=value"
+This option is directly passed through to the compilation command(s).
+Use this to add your own defines to the build process.
+.TP 12
+.BI \-I " incdir"
+This option is directly passed through to the compilation command(s).
+Use this to add your own include directories to search to the build process.
+.TP 12
+.BI \-L " libdir"
+This option is directly passed through to the linker command.
+Use this to add your own library directories to search to the build process.
+.TP 12
+.BI \-l " libname"
+This option is directly passed through to the linker command.
+Use this to add your own libraries to search to the build process.
+.TP 12
+.BI \-Wc, "compiler-flags"
+This option passes 
+.I compiler-flags
+as additional flags to the compiler command.
+Use this to add local compiler-specific options.
+.TP 12
+.BI \-Wl, "linker-flags"
+This option passes 
+.I linker-flags
+as additional flags to the linker command.
+Use this to add local linker-specific options.
+.PP
+DSO installation and configuration options:
+.TP 12
+.B \-i
+This indicates the installation operation and installs one or more
+dynamically shared objects into the
+server's
+.I libexec
+directory.
+.TP 12
+.B \-a
+This activates the module by automatically adding a corresponding
+.B LoadModule
+line to Apache's
+.B httpd.conf
+configuration file, or by enabling it if it already exists.
+.TP 12
+.B \-A
+Same as option
+.B \-a
+but the created 
+.B LoadModule
+directive is prefixed with a hash sign (#), i.e. the module is
+just prepared for later activation but initially disabled. 
+.TP 12
+.B \-e
+This indicates the editing operation, which can be used with the
+.B \-a
+and
+.B \-A
+options similarly to the
+.B \-i
+operation to edit Apache's
+.B httpd.conf
+configuration file without attempting to install the module.
+.PD
+.SH EXAMPLES
+Assume you have an Apache module named mod_foo.c available which should extend
+Apache's server functionality. To accomplish this you first have to compile
+the C source into a shared object suitable for loading into the Apache server
+under runtime via the following command:
+
+.nf
+  $ apxs -c mod_foo.c
+  gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c
+  ld -Bshareable -o mod_foo.so mod_foo.o
+  $ _
+.fi
+
+Then you have to update the Apache configuration by making sure a
+.B LoadModule
+directive is present to load this shared object. To simplify this
+step
+.B apxs
+provides an automatic way to install the shared object in its
+"libexec" directory and updating the
+.B httpd.conf
+file accordingly. This can be achieved by running:
+
+.nf
+  $ apxs -i -a mod_foo.c
+  cp mod_foo.so /path/to/apache/libexec/mod_foo.so
+  chmod 755 /path/to/apache/libexec/mod_foo.so
+  [activating module `foo' in /path/to/apache/etc/httpd.conf]
+  $ _
+.fi
+
+This way a line named
+
+.nf
+  LoadModule foo_module libexec/mod_foo.so
+.fi
+
+is added to the configuration file if still not present.
+If you want to have this this disabled per default use the
+.B \-A
+option, i.e.
+
+.nf
+  $ apxs -i -A mod_foo.c
+.fi
+
+For a quick test of the APXS mechanism you can create a sample Apache module
+template plus a corresponding Makefile via:
+
+.nf
+  $ apxs -g -n foo
+  Creating [DIR]  foo
+  Creating [FILE] foo/Makefile
+  Creating [FILE] foo/mod_foo.c
+  $ _
+.fi
+
+Then you can immediately compile this sample module into a shared object and
+load it into the Apache server:
+
+.nf
+  $ cd foo
+  $ make all reload
+  apxs -c mod_foo.c
+  gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c
+  ld -Bshareable -o mod_foo.so mod_foo.o
+  apxs -i -a -n "foo" mod_foo.so
+  cp mod_foo.so /path/to/apache/libexec/mod_foo.so
+  chmod 755 /path/to/apache/libexec/mod_foo.so
+  [activating module `foo' in /path/to/apache/etc/httpd.conf]
+  apachectl restart
+  /path/to/apache/sbin/apachectl restart: httpd not running, trying to start
+  [Tue Mar 31 11:27:55 1998] [debug] mod_so.c(303): loaded module foo_module
+  /path/to/apache/sbin/apachectl restart: httpd started
+  $ _
+.fi
+
+You can even use
+.B apxs
+to compile complex modules outside the Apache source tree, like PHP3:
+
+.nf
+  $ cd php3
+  $ ./configure --with-shared-apache=../apache-1.3
+  $ apxs -c -o libphp3.so mod_php3.c libmodphp3-so.a
+  gcc -fpic -DSHARED_MODULE -I/tmp/apache/include  -c mod_php3.c
+  ld -Bshareable -o libphp3.so mod_php3.o libmodphp3-so.a
+  $ _
+.fi
+
+because
+.B apxs
+automatically recognized C source files and object files.  Only C source files
+are compiled while remaining object files are used for the linking phase.
+
+.PD
+.SH SEE ALSO
+.BR apachectl(1),
+.BR httpd(8).
+.
diff --git a/docs/man/dbmmanage.1 b/docs/man/dbmmanage.1
new file mode 100644 (file)
index 0000000..06380ff
--- /dev/null
@@ -0,0 +1,171 @@
+.TH dbmmanage 1 "March 1998"
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer. 
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\"    software must display the following acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\"    endorse or promote products derived from this software without
+.\"    prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\"    nor may "Apache" appear in their names without prior written
+.\"    permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\"    acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+dbmmanage \- Create and update user authentication files in DBM format
+.SH SYNOPSIS
+.B dbmmanage
+.I filename
+[
+.I command
+] [
+.I username
+[
+.I encpasswd
+] ]
+.PP
+.SH DESCRIPTION
+.B dbmmanage
+is used to create and update the DBM format files used to store
+usernames and password for basic authentication of HTTP users.
+Resources available from the
+.B httpd
+Apache web server can be restricted to just the users listed
+in the files created by 
+.B dbmmanage.
+This program can only be used
+when the usernames are stored in a DBM file. To use a
+flat-file database see 
+\fBhtpasswd\fP.
+.PP
+This manual page only lists the command line arguments. For details of
+the directives necessary to configure user authentication in 
+.B httpd 
+see
+the Apache manual, which is part of the Apache distribution or can be
+found at http://www.apache.org/.
+.SH OPTIONS
+.IP \fB\fIfilename\fP
+The filename of the DBM format file. Usually without the 
+extension .db, .pag, or .dir.
+.IP \fB\fIcommand\fP
+This selects the operation to perform:
+.TP 12
+.B add
+Adds an entry for \fIusername\fP to \fIfilename\fP using the encrypted
+password \fIencpassword\fP.
+.TP 12
+.B adduser
+Asks for a password and then adds an entry for \fIusername\fP to
+\fIfilename\fP .
+.TP 12
+.B check
+Asks for a password and then checks if 
+\fIusername\fP is in \fIfilename\fP and if it's password matches
+the specified one.
+.TP 12
+.B delete
+Deletes the \fIusername\fP entry from \fIfilename\fP.
+.TP 12
+.B import
+Reads username:password entries (one per line) from STDIN and adds them to
+\fIfilename\fP. The passwords already has to be crypted.
+.TP 12
+.B update
+Same as the "adduser" command, except that it makes sure \fIusername\fP
+already exists in \fIfilename\fP.
+.TP 12
+.B view
+Just displays the complete contents of the DBM file.
+.IP \fB\fIusername\fP
+The user for which the update operation is performed.
+.PD
+.SH BUGS
+.PP
+One should be aware that there are a number of different DBM file
+formats in existance, and with all likelihood, libraries for more than
+one format may exist on your system.  The three primary examples are
+NDBM, the GNU project's GDBM, and Berkeley DB 2.  Unfortunately, all
+these libraries use different file formats, and you must make sure
+that the file format used by
+.I filename
+is the same format that 
+.B dbmmanage
+expects to see.  
+.B dbmmanage
+currently has no way of determining what type of DBM file it is
+looking at.  If used against the wrong format, 
+.dbmmanage
+will simply return nothing, or may create a different DBM file with a
+different name, or at worst, it may corrupt the DBM file if you were
+attempting to write to it.
+.PP
+.B dbmmanage
+has a list of DBM format preferences, defined by the 
+.B @AnyDBM::ISA
+array near the beginning of the program.  Since we prefer the Berkeley
+DB 2 file format, the order in which
+.B dbmmanage 
+will look for system libraries is Berkeley DB 2, then NDBM, and then
+GDBM.  The first library found will be the library
+.B dbmmanage
+will attempt to use for all DBM file transactions.  This ordering is
+slightly different than the standard 
+.B @AnyDBM::ISA
+ordering in perl, as well as the ordering used by the simple dbmopen()
+call in Perl, so if you use any other utilities to manage your DBM
+files, they must also follow this preference ordering.  Similar care
+must be taken if using programs in other languages, like C, to 
+access these files.
+.PP
+Apache's 
+.B mod_auth_db.c 
+module corresponds to Berkeley DB 2 library, while
+.B mod_auth_dbm.c
+corresponds to the NDBM library.  Also, one can usually use the 
+.B file
+program supplied with most Unix systems to see what format a DBM file is in.
+.PD
+.SH SEE ALSO
+.BR httpd(8)
+.
diff --git a/docs/man/htdigest.1 b/docs/man/htdigest.1
new file mode 100644 (file)
index 0000000..c719e45
--- /dev/null
@@ -0,0 +1,97 @@
+.TH htdigest 1 "March 1998"
+.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer. 
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\"    software must display the following acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\"    endorse or promote products derived from this software without
+.\"    prior written permission. For written permission, please contact
+.\"    apache@apache.org.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\"    nor may "Apache" appear in their names without prior written
+.\"    permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\"    acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+htdigest \- Create and update user authentication files
+.SH SYNOPSIS
+.B htdigest 
+[
+.B \-c
+] 
+.I passwdfile
+.I realm
+.I username
+.SH DESCRIPTION
+.B htdigest
+is used to create and update the flat-files used to store
+usernames, realm and password for digest authentication of HTTP users.
+Resources available from the
+.B httpd
+Apache web server can be restricted to just the users listed
+in the files created by 
+.B htdigest.
+.PP
+This manual page only lists the command line arguments. For details of
+the directives necessary to configure digest authentication in 
+.B httpd 
+see
+the Apache manual, which is part of the Apache distribution or can be
+found at http://www.apache.org/.
+.SH OPTIONS
+.IP \-c 
+Create the \fIpasswdfile\fP. If \fIpasswdfile\fP already exists, it
+is deleted first. 
+.IP \fB\fIpasswdfile\fP
+Name of the file to contain the username, realm and password. If \-c
+is given, this file is created if it does not already exist,
+or deleted and recreated if it does exist. 
+.IP \fB\fIrealm\fP
+The realm name to which the user name belongs to. 
+.IP \fB\fIusername\fP
+The user name to create or update in \fBpasswdfile\fP. If
+\fIusername\fP does not exist is this file, an entry is added. If it
+does exist, the password is changed.
+.SH SEE ALSO
+.BR httpd(8) 
+.
diff --git a/docs/man/htpasswd.1 b/docs/man/htpasswd.1
new file mode 100644 (file)
index 0000000..1687cde
--- /dev/null
@@ -0,0 +1,213 @@
+.TH htpasswd 1 "February 1997"
+.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer. 
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\"    software must display the following acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\"    endorse or promote products derived from this software without
+.\"    prior written permission. For written permission, please contact
+.\"    apache@apache.org.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\"    nor may "Apache" appear in their names without prior written
+.\"    permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\"    acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+htpasswd \- Create and update user authentication files
+.SH SYNOPSIS
+.B htpasswd 
+[
+.B \-c
+] 
+[
+.B \-m
+] 
+.I passwdfile
+.I username
+.br
+.B htpasswd
+.B \-b  
+[
+.B \-c
+] 
+[
+.B \-m
+.B \-d
+.B \-p
+.B \-s
+] 
+.I passwdfile
+.I username
+.I password
+.SH DESCRIPTION
+.B htpasswd
+is used to create and update the flat-files used to store
+usernames and password for basic authentication of HTTP users.
+If
+.B htpasswd
+cannot access a file, such as not being able to write to the output
+file or not being able to read the file in order to update it,
+it returns an error status and makes no changes.
+.PP
+Resources available from the
+.B httpd
+Apache web server can be restricted to just the users listed
+in the files created by 
+.B htpasswd.
+This program can only be used
+when the usernames are stored in a flat-file. To use a
+DBM database see 
+\fBdbmmanage\fP.
+.PP
+.B htpasswd
+encrypts passwords using either a version of MD5 modified for Apache,
+or the system's \fIcrypt()\fP routine.  Files managed by
+.B htpasswd
+may contain both types of passwords; some user records may have
+MD5-encrypted passwords while others in the same file may have passwords
+encrypted with \fIcrypt()\fP.
+.PP
+This manual page only lists the command line arguments. For details of
+the directives necessary to configure user authentication in 
+.B httpd 
+see
+the Apache manual, which is part of the Apache distribution or can be
+found at <URL:http://www.apache.org/>.
+.SH OPTIONS
+.IP \-b 
+Use batch mode; \fIi.e.\fP, get the password from the command line
+rather than prompting for it. \fBThis option should be used with
+extreme care, since the password is clearly visible on the command
+line.\fP
+.IP \-c 
+Create the \fIpasswdfile\fP. If \fIpasswdfile\fP already exists, it
+is rewritten and truncated.
+.IP \-m 
+Use MD5 encryption for passwords. On Windows and TPF, this is the default.
+.IP \-d
+Use crypt() encryption for passwords. The default on all platforms but
+Windows and TPF. Though possibly supported by
+.B htpasswd
+onm all platforms, it is not supported by the
+.B httpd
+server on Windows and TPF.
+.IP \-s
+Use SHA encryption for passwords. Faciliates migration from/to Netscape
+servers using the LDAP Directory Interchange Format (ldif).
+.IP \-p
+Use plaintext passwords. Though 
+.B htpasswd
+will support creation on all platofrms, the
+.B httpd
+deamon will only accept plain text passwords on Windows and TPF.
+.IP \fB\fIpasswdfile\fP
+Name of the file to contain the user name and password. If \-c
+is given, this file is created if it does not already exist,
+or rewritten and truncated if it does exist. 
+.IP \fB\fIusername\fP
+The username to create or update in \fBpasswdfile\fP. If
+\fIusername\fP does not exist in this file, an entry is added. If it
+does exist, the password is changed.
+.IP \fB\fIpassword\fP
+The plaintext password to be encrypted and stored in the file.  Only used
+with the \fI-b\fP flag.
+.SH EXIT STATUS
+.B htpasswd
+returns a zero status ("true") if the username and password have
+been successfully added or updated in the \fIpasswdfile\fP.
+.B htpasswd
+returns 1 if it encounters some problem accessing files, 2 if there
+was a syntax problem with the command line, 3 if the password was
+entered interactively and the verification entry didn't match, 4 if
+its operation was interrupted, 5 if a value is too long (username,
+filename, password, or final computed record), and 6 if the username
+contains illegal characters (see the \fBRESTRICTIONS\fP section).
+.SH EXAMPLES
+\fBhtpasswd /usr/local/etc/apache/.htpasswd-users jsmith\fP
+.IP
+Adds or modifies the password for user \fIjsmith\fP.  
+The user is prompted for the password.  If executed
+on a Windows system, the password will be encrypted using the
+modified Apache MD5 algorithm; otherwise, the system's
+\fIcrypt()\fP routine will be used.  If the file does not
+exist, 
+.B htpasswd
+will do nothing except return an error.
+.LP
+\fBhtpasswd -c /home/doe/public_html/.htpasswd jane\fP
+.IP
+Creates a new file and stores a record in it for user \fIjane\fP.
+The user is prompted for the password.
+If the file exists and cannot be read, or cannot be written,
+it is not altered and
+.B htpasswd
+will display a message and return an error status.
+.LP
+\fBhtpasswd -mb /usr/web/.htpasswd-all jones Pwd4Steve\fP
+.IP
+Encrypts the password from the command line (\fIPwd4Steve\fP) using
+the MD5 algorithm, and stores it in the specified file.
+.LP
+.SH SECURITY CONSIDERATIONS
+Web password files such as those managed by
+.B htpasswd
+should \fBnot\fP be within the Web server's URI space -- that is,
+they should not be fetchable with a browser.
+.PP
+The use of the \fI-b\fP option is discouraged, since when it is
+used the unencrypted password appears on the command line.
+.SH RESTRICTIONS
+On the Windows and MPE platforms, passwords encrypted with
+.B htpasswd
+are limited to no more than 255 characters in length.  Longer
+passwords will be truncated to 255 characters.
+.PP
+The MD5 algorithm used by
+.B htpasswd
+is specific to the Apache software; passwords encrypted using it will not be
+usable with other Web servers.
+.PP
+Usernames are limited to 255 bytes and may not include the character ':'.
+.SH SEE ALSO
+.BR httpd(8)
+and the scripts in support/SHA1 which come with the distribution.
diff --git a/docs/man/httpd.8 b/docs/man/httpd.8
new file mode 100644 (file)
index 0000000..ae320ba
--- /dev/null
@@ -0,0 +1,211 @@
+.TH httpd 8 "February 1997"
+.\" Copyright (c) 1995-1997 David Robinson. All rights reserved.
+.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer. 
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\"    software must display the following acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\"    endorse or promote products derived from this software without
+.\"    prior written permission. For written permission, please contact
+.\"    apache@apache.org.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\"    nor may "Apache" appear in their names without prior written
+.\"    permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\"    acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+httpd \- Apache hypertext transfer protocol server
+.SH SYNOPSIS
+.B httpd 
+[
+.B \-X
+] [
+.BI \-R " libexecdir"
+] [
+.BI \-d " serverroot"
+] [
+.BI \-f " config"
+] [
+.BI \-C " directive"
+] [
+.BI \-c " directive"
+] [
+.BI \-D " parameter"
+]
+
+.B httpd 
+[
+.B \-h
+] 
+[
+.B \-l
+] 
+[
+.B \-L
+] 
+[
+.B \-v
+] 
+[
+.B \-V
+] 
+[
+.B \-S
+] 
+[
+.B \-t
+] 
+[
+.B \-T
+]
+
+.SH DESCRIPTION
+.B httpd
+is the Apache HyperText Transfer Protocol (HTTP) server program. It is
+designed to be run as a standalone daemon process. When used like this
+it will create a pool of child processes to handle requests. To stop
+it, send a TERM signal to the initial (parent) process. The PID of
+this process is written to a file as given in the configuration file.
+Alternatively 
+.B httpd 
+may be invoked by the Internet daemon inetd(8) each
+time a connection to the HTTP service is made.
+.PP
+This manual page only lists the command line arguments. For details
+of the directives necessary to configure 
+.B httpd
+see the Apache manual,
+which is part of the Apache distribution or can be found at
+http://www.apache.org/. Paths in this manual may not reflect those
+compiled into 
+.B httpd.
+.SH OPTIONS
+.TP 12
+.BI \-R " libexecdir"
+This option is only available if Apache was built with
+the 
+.I SHARED_CORE
+rule enabled which forces the Apache core code to be
+placed into a dynamic shared object (DSO) file. This file
+is searched in a hardcoded path under ServerRoot per default. Use this
+option if you want to override it.
+.TP 12
+.BI \-d " serverroot"
+Set the initial value for the ServerRoot directive to \fIserverroot\fP. This
+can be overridden by the ServerRoot command in the configuration file. The
+default is \fB/usr/local/apache\fP.
+.TP
+.BI \-f " config"
+Execute the commands in the file \fIconfig\fP on startup. If \fIconfig\fP
+does not begin with a /, then it is taken to be a path relative to
+the ServerRoot. The default is \fBconf/httpd.conf\fP.
+.TP
+.BI \-C " directive"
+Process the configuration \fIdirective\fP before reading config files.
+.TP
+.BI \-c " directive"
+Process the configuration \fIdirective\fP after reading config files.
+.TP
+.BI \-D " parameter"
+Sets a configuration \fIparameter\fP which can be used with
+<IfDefine>...</IfDefine> sections in the configuration files
+to conditionally skip or process commands.
+.TP
+.B \-h
+Output a short summary of available command line options.
+.TP
+.B \-l
+Output a list of modules compiled into the server.
+.TP
+.B \-L
+Output a list of directives together with expected arguments and
+places where the directive is valid.
+.TP
+.B \-S
+Show the settings as parsed from the config file (currently only shows the
+virtualhost settings).
+.TP
+.B \-t
+Run syntax tests for configuration files only. The program immediately exits
+after these syntax parsing with either a return code of 0 (Syntax OK) or
+return code not equal to 0 (Syntax Error).
+.TP
+.B \-T
+Same as option 
+.B \-t
+but does not check the configured document roots. 
+.TP
+.B \-X
+Run in single-process mode, for internal debugging purposes only; the daemon
+does not detach from the terminal or fork any children. Do NOT use this mode
+to provide ordinary web service.
+.TP
+.B \-v
+Print the version of 
+.B httpd
+, and then exit.
+.TP
+.B \-V
+Print the version and build parameters of 
+.B httpd
+, and then exit.
+.SH FILES
+.PD 0
+.B /usr/local/apache/conf/httpd.conf
+.br
+.B /usr/local/apache/conf/srm.conf
+.br
+.B /usr/local/apache/conf/access.conf
+.br
+.B /usr/local/apache/conf/mime.types
+.br
+.B /usr/local/apache/conf/magic
+.br
+.B /usr/local/apache/logs/error_log
+.br
+.B /usr/local/apache/logs/access_log
+.br
+.B /usr/local/apache/logs/httpd.pid
+.PD
+.SH SEE ALSO
+.BR inetd (8).
diff --git a/docs/man/logresolve.8 b/docs/man/logresolve.8
new file mode 100644 (file)
index 0000000..4851269
--- /dev/null
@@ -0,0 +1,87 @@
+.TH logresolve 8 "March 1998"
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer. 
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\"    software must display the following acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\"    endorse or promote products derived from this software without
+.\"    prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\"    nor may "Apache" appear in their names without prior written
+.\"    permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\"    acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+logresolve \- resolve hostnames for IP-adresses in Apache logfiles
+.SH SYNOPSIS
+.B logresolve
+[
+.BI \-s " filename"
+] [
+.B \-c
+] < 
+.I access_log
+>
+.I access_log.new
+.PP
+.SH DESCRIPTION
+.B logresolve
+is a post-processing program to resolve IP-adresses in Apache's access
+logfiles.  To minimize impact on your nameserver, logresolve has its very own
+internal hash-table cache. This means that each IP number will only be looked
+up the first time it is found in the log file.
+.SH OPTIONS
+.TP 12
+.BI \-s " filename"
+Specifies a filename to record statistics.
+.TP 12
+.B \-c 
+This causes 
+.B logresolve 
+to apply some DNS checks: after finding the hostname from the IP address, it
+looks up the IP addresses for the hostname and checks that one of these
+matches the original address.
+.PD
+.SH SEE ALSO
+.BR httpd(8)
+.
diff --git a/docs/man/rotatelogs.8 b/docs/man/rotatelogs.8
new file mode 100644 (file)
index 0000000..aee02e1
--- /dev/null
@@ -0,0 +1,83 @@
+.TH rotatelogs 8 "March 1998"
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer. 
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\"    software must display the following acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\"    endorse or promote products derived from this software without
+.\"    prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\"    nor may "Apache" appear in their names without prior written
+.\"    permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\"    acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+rotatelogs \- rotate Apache logs without having to kill the server
+.SH SYNOPSIS
+.B rotatelogs
+.I logfile
+.I rotationtime
+.PP
+.SH DESCRIPTION
+.B rotatelogs
+is a simple program for use in conjunction with Apache's piped logfile
+feature which can be used like this:
+
+.fi
+   TransferLog "|rotatelogs /path/to/logs/access_log 86400"
+.mf
+
+This creates the files /path/to/logs/access_log.nnnn where nnnn is the system
+time at which the log nominally starts (this time will always be a multiple of
+the rotation time, so you can synchronize cron scripts with it).  At the end
+of each rotation time (here after 24 hours) a new log is started.
+.SH OPTIONS
+.IP \fB\fIlogfile\fP
+The path plus basename of the logfile. The suffix .nnnn is automatically
+added.
+.IP \fB\fIrotationtime\fP
+The rotation time in seconds.
+.PD
+.SH SEE ALSO
+.BR httpd(8)
+.
diff --git a/docs/man/suexec.8 b/docs/man/suexec.8
new file mode 100644 (file)
index 0000000..801d7ab
--- /dev/null
@@ -0,0 +1,70 @@
+.TH suexec 8 "March 1998"
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer. 
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\"    software must display the following acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\"    endorse or promote products derived from this software without
+.\"    prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\"    nor may "Apache" appear in their names without prior written
+.\"    permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\"    acknowledgment:
+.\"    "This product includes software developed by the Apache Group
+.\"    for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+suexec \- Switch User For Exec
+.SH SYNOPSIS
+No synopsis for usage, because this program
+is used internally by Apache only. 
+.PP
+.SH DESCRIPTION
+.B suexec
+is the "wrapper" support program for the suEXEC behaviour for Apache.
+It is run from within Apache automatically to switch the user when
+an external program has to be run under a different user. For more
+information about suEXEC see the document `Apache suEXEC Support'
+under http://www.apache.org/docs/suexec.html .
+.PD
+.SH SEE ALSO
+.BR httpd(8)
+.
diff --git a/modules/aaa/.indent.pro b/modules/aaa/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/modules/aaa/mod_auth_anon.dsp b/modules/aaa/mod_auth_anon.dsp
new file mode 100644 (file)
index 0000000..afe8cc7
--- /dev/null
@@ -0,0 +1,113 @@
+# Microsoft Developer Studio Project File - Name="ApacheModuleAuthAnon" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleAuthAnon - 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 "ApacheModuleAuthAnon.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 "ApacheModuleAuthAnon.mak"\
+ CFG="ApacheModuleAuthAnon - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ApacheModuleAuthAnon - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleAuthAnon - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ApacheModuleAuthAnon - 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 ".\ApacheModuleAuthAnonR"
+# PROP Intermediate_Dir ".\ApacheModuleAuthAnonR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "ApacheModuleAuthAnon - 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 ".\ApacheModuleAuthAnonD"
+# PROP Intermediate_Dir ".\ApacheModuleAuthAnonD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ApacheModuleAuthAnon - Win32 Release"
+# Name "ApacheModuleAuthAnon - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_auth_anon.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/modules/arch/win32/mod_isapi.c b/modules/arch/win32/mod_isapi.c
new file mode 100644 (file)
index 0000000..fa00270
--- /dev/null
@@ -0,0 +1,569 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * mod_isapi.c - Internet Server Application (ISA) module for Apache
+ * by Alexei Kosut <akosut@apache.org>
+ *
+ * This module implements Microsoft's ISAPI, allowing Apache (when running
+ * under Windows) to load Internet Server Applications (ISAPI extensions).
+ * It implements all of the ISAPI 2.0 specification, except for the 
+ * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI
+ * extensions that use only synchronous I/O and are compatible with the
+ * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should
+ * function as well).
+ *
+ * To load, simply place the ISA in a location in the document tree.
+ * Then add an "AddHandler isapi-isa dll" into your config file.
+ * You should now be able to load ISAPI DLLs just be reffering to their
+ * URLs. Make sure the ExecCGI option is active in the directory
+ * the ISA is in.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_log.h"
+#include "util_script.h"
+
+/* We use the exact same header file as the original */
+#include <HttpExt.h>
+
+/* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER,
+   define this to conform */
+#define RELAX_HEADER_RULE
+
+module isapi_module;
+
+/* Our "Connection ID" structure */
+
+typedef struct {
+    LPEXTENSION_CONTROL_BLOCK ecb;
+    request_rec *r;
+    int status;
+} isapi_cid;
+
+/* Declare the ISAPI functions */
+
+BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
+                              LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer);
+BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
+                        DWORD dwReserved);
+BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize);
+BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
+                                  LPVOID lpvBuffer, LPDWORD lpdwSize,
+                                  LPDWORD lpdwDataType);
+
+/*
+    The optimiser blows it totally here. What happens is that autos are addressed relative to the
+    stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
+    between setting isapi_entry and calling through it. We work around the problem by forcing it to
+    use frame pointers.
+*/
+#pragma optimize("y",off)
+
+int isapi_handler (request_rec *r) {
+    LPEXTENSION_CONTROL_BLOCK ecb =
+       ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
+    HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
+
+    HINSTANCE isapi_handle;
+    BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */
+    DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */
+    BOOL (*isapi_term)(DWORD); /* optional entry point 3 */
+
+    isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid));
+    table *e = r->subprocess_env;
+    int retval;
+
+    /* Use similar restrictions as CGIs */
+
+    if (!(ap_allow_options(r) & OPT_EXECCGI))
+       return FORBIDDEN;
+
+    if (r->finfo.st_mode == 0)
+       return NOT_FOUND;
+
+    if (S_ISDIR(r->finfo.st_mode))
+       return FORBIDDEN;
+
+    /* Load the module */
+
+    if (!(isapi_handle = LoadLibraryEx(r->filename, NULL,
+                                      LOAD_WITH_ALTERED_SEARCH_PATH))) {
+       ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+                   "Could not load DLL: %s", r->filename);
+       return SERVER_ERROR;
+    }
+
+    if (!(isapi_version =
+         (void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) {
+       ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+                   "DLL could not load GetExtensionVersion(): %s", r->filename);
+       FreeLibrary(isapi_handle);
+       return SERVER_ERROR;
+    }
+
+    if (!(isapi_entry =
+         (void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) {
+       ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+                   "DLL could not load HttpExtensionProc(): %s", r->filename);
+       FreeLibrary(isapi_handle);
+       return SERVER_ERROR;
+    }
+
+    isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension"));
+
+    /* Run GetExtensionVersion() */
+
+    if ((*isapi_version)(pVer) != TRUE) {
+       ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+                   "ISAPI GetExtensionVersion() failed: %s", r->filename);
+       FreeLibrary(isapi_handle);
+       return SERVER_ERROR;
+    }
+
+    /* Set up variables */
+    ap_add_common_vars(r);
+    ap_add_cgi_vars(r);
+
+    /* Set up connection ID */
+    ecb->ConnID = (HCONN)cid;
+    cid->ecb = ecb;
+    cid->r = r;
+    cid->status = 0;
+
+    ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK);
+    ecb->dwVersion = MAKELONG(0, 2);
+    ecb->dwHttpStatusCode = 0;
+    strcpy(ecb->lpszLogData, "");
+    ecb->lpszMethod = r->method;
+    ecb->lpszQueryString = ap_table_get(e, "QUERY_STRING");
+    ecb->lpszPathInfo = ap_table_get(e, "PATH_INFO");
+    ecb->lpszPathTranslated = ap_table_get(e, "PATH_TRANSLATED");
+    ecb->lpszContentType = ap_table_get(e, "CONTENT_TYPE");
+
+    /* Set up client input */
+    if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
+       if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+       FreeLibrary(isapi_handle);
+       return retval;
+    }
+
+    if (ap_should_client_block(r)) {
+       /* Unlike IIS, which limits this to 48k, we read the whole
+        * sucker in. I suppose this could be bad for memory if someone
+        * uploaded the complete works of Shakespeare. Well, WebSite
+        * does the same thing.
+        */
+       long to_read = atol(ap_table_get(e, "CONTENT_LENGTH"));
+       long read;
+
+       /* Actually, let's cap it at 48k, until we figure out what
+        * to do with this... we don't want a Content-Length: 1000000000
+        * taking out the machine.
+        */
+
+       if (to_read > 49152) {
+           if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+           FreeLibrary(isapi_handle);
+           return HTTP_REQUEST_ENTITY_TOO_LARGE;
+       }
+
+       ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read);
+
+       if ((read = ap_get_client_block(r, ecb->lpbData, to_read)) < 0) {
+           if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+           FreeLibrary(isapi_handle);
+           return SERVER_ERROR;
+       }
+
+       /* Although its not to spec, IIS seems to null-terminate
+        * its lpdData string. So we will too. To make sure
+        * cbAvailable matches cbTotalBytes, we'll up the latter
+        * and equalize them.
+        */
+       ecb->cbAvailable = ecb->cbTotalBytes = read + 1;
+       ecb->lpbData[read] = '\0';
+       }
+    else {
+       ecb->cbTotalBytes = 0;
+       ecb->cbAvailable = 0;
+       ecb->lpbData = NULL;
+    }
+
+    /* Set up the callbacks */
+
+    ecb->GetServerVariable = &GetServerVariable;
+    ecb->WriteClient = &WriteClient;
+    ecb->ReadClient = &ReadClient;
+    ecb->ServerSupportFunction = &ServerSupportFunction;
+
+    /* All right... try and load the sucker */
+    retval = (*isapi_entry)(ecb);
+
+    /* Set the status (for logging) */
+    if (ecb->dwHttpStatusCode)
+       r->status = ecb->dwHttpStatusCode;
+
+    /* Check for a log message - and log it */
+    if (ecb->lpszLogData && strcmp(ecb->lpszLogData, ""))
+       ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                   "%s: %s", ecb->lpszLogData, r->filename);
+
+    /* All done with the DLL... get rid of it */
+    if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+    FreeLibrary(isapi_handle);
+
+    switch(retval) {
+    case HSE_STATUS_SUCCESS:
+    case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
+       /* Ignore the keepalive stuff; Apache handles it just fine without
+        * the ISA's "advice".
+        */
+
+       if (cid->status) /* We have a special status to return */
+           return cid->status;
+
+       return OK;
+    case HSE_STATUS_PENDING:   /* We don't support this */
+       ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+                   "ISAPI asynchronous I/O not supported: %s", r->filename);
+    case HSE_STATUS_ERROR:
+    default:
+       return SERVER_ERROR;
+    }
+
+}
+#pragma optimize("",on)
+
+BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
+                              LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) {
+    request_rec *r = ((isapi_cid *)hConn)->r;
+    table *e = r->subprocess_env;
+    const char *result;
+    
+    /* Mostly, we just grab it from the environment, but there are
+     * a couple of special cases
+     */
+
+    if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
+       /* We don't support NT users, so this is always the same as
+        * REMOTE_USER
+        */
+       result = ap_table_get(e, "REMOTE_USER");
+    }
+    else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
+       /* Apache doesn't support secure requests inherently, so
+        * we have no way of knowing. We'll be conservative, and say
+        * all requests are insecure.
+        */
+       result = "0";
+    }
+    else if (!strcasecmp(lpszVariableName, "URL")) {
+       result = r->uri;
+    }
+    else {
+       result = ap_table_get(e, lpszVariableName);
+    }
+
+    if (result) {
+       if (strlen(result) > *lpdwSizeofBuffer) {
+           *lpdwSizeofBuffer = strlen(result);
+           SetLastError(ERROR_INSUFFICIENT_BUFFER);
+           return FALSE;
+       }
+       strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
+       return TRUE;
+    }
+
+    /* Didn't find it */
+    SetLastError(ERROR_INVALID_INDEX);
+    return FALSE;
+}
+
+BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
+                        DWORD dwReserved) {
+    request_rec *r = ((isapi_cid *)ConnID)->r;
+    int writ;  /* written, actually, but why shouldn't I make up words? */
+
+    /* We only support synchronous writing */
+    if (dwReserved && dwReserved != HSE_IO_SYNC) {
+       ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+                   "ISAPI asynchronous I/O not supported: %s", r->filename);
+       SetLastError(ERROR_INVALID_PARAMETER);
+       return FALSE;
+    }
+
+    if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
+       SetLastError(ERROR); /* XXX: Find the right error code */
+       return FALSE;
+    }
+   
+    *lpwdwBytes = writ;
+    return TRUE;
+}
+
+BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) {
+    /* Doesn't need to do anything; we've read all the data already */
+    return TRUE;
+}
+
+/* XXX: There is an O(n^2) attack possible here. */
+BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
+                                  LPVOID lpvBuffer, LPDWORD lpdwSize,
+                                  LPDWORD lpdwDataType) {
+    isapi_cid *cid = (isapi_cid *)hConn;
+    request_rec *subreq, *r = cid->r;
+    char *data;
+
+    switch (dwHSERequest) {
+    case HSE_REQ_SEND_URL_REDIRECT_RESP:
+       /* Set the status to be returned when the HttpExtensionProc()
+        * is done.
+        */
+       ap_table_set (r->headers_out, "Location", lpvBuffer);
+       cid->status = cid->r->status = cid->ecb->dwHttpStatusCode = REDIRECT;
+       return TRUE;
+
+    case HSE_REQ_SEND_URL:
+       /* Read any additional input */
+
+       if (r->remaining > 0) {
+           char argsbuffer[HUGE_STRING_LEN];
+
+           while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
+       }
+       
+       /* Reset the method to GET */
+       r->method = ap_pstrdup(r->pool, "GET");
+       r->method_number = M_GET;
+
+       /* Don't let anyone think there's still data */
+       ap_table_unset(r->headers_in, "Content-Length");
+       
+       ap_internal_redirect((char *)lpvBuffer, r);     
+       return TRUE;
+
+    case HSE_REQ_SEND_RESPONSE_HEADER:
+       r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK");
+       sscanf(r->status_line, "%d", &r->status);
+       cid->ecb->dwHttpStatusCode = r->status;
+
+       /* Now fill in the HTTP headers, and the rest of it. Ick.
+        * lpdwDataType contains a string that has headers (in MIME
+        * format), a blank like, then (possibly) data. We need
+        * to parse it.
+        *
+        * Easy case first:
+        */
+       if (!lpdwDataType) {
+           ap_send_http_header(r);
+           return TRUE;
+       }
+
+       /* Make a copy - don't disturb the original */
+       data = ap_pstrdup(r->pool, (char *)lpdwDataType);
+
+       /* We *should* break before this while loop ends */
+       while (*data) {
+           char *value, *lf = strchr(data, '\n');
+           int p;
+
+#ifdef RELAX_HEADER_RULE
+           if (lf)
+               *lf = '\0';
+#else
+           if (!lf) { /* Huh? Invalid data, I think */
+               ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                           "ISA sent invalid headers: %s", r->filename);
+               SetLastError(ERROR);    /* XXX: Find right error */
+               return FALSE;
+           }
+
+           /* Get rid of \n and \r */
+           *lf = '\0';
+#endif
+           p = strlen(data);
+           if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
+           
+           /* End of headers */
+           if (*data == '\0') {
+#ifdef RELAX_HEADER_RULE
+               if (lf)
+#endif
+                   data = lf + 1;      /* Reset data */
+               break;
+           }
+
+           if (!(value = strchr(data, ':'))) {
+               SetLastError(ERROR);    /* XXX: Find right error */
+               ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                           "ISA sent invalid headers", r->filename);
+               return FALSE;
+           }
+
+           *value++ = '\0';
+           while (*value && ap_isspace(*value)) ++value;
+
+           /* Check all the special-case headers. Similar to what
+            * ap_scan_script_header_err() does (see that function for
+            * more detail)
+            */
+
+           if (!strcasecmp(data, "Content-Type")) {
+               char *tmp;
+               /* Nuke trailing whitespace */
+               
+               char *endp = value + strlen(value) - 1;
+               while (endp > value && ap_isspace(*endp)) *endp-- = '\0';
+            
+               tmp = ap_pstrdup (r->pool, value);
+               ap_str_tolower(tmp);
+               r->content_type = tmp;
+           }
+           else if (!strcasecmp(data, "Content-Length")) {
+               ap_table_set(r->headers_out, data, value);
+           }
+           else if (!strcasecmp(data, "Transfer-Encoding")) {
+               ap_table_set(r->headers_out, data, value);
+           }
+           else if (!strcasecmp(data, "Set-Cookie")) {
+               ap_table_add(r->err_headers_out, data, value);
+           }
+           else {
+               ap_table_merge(r->err_headers_out, data, value);
+           }
+         
+           /* Reset data */
+#ifdef RELAX_HEADER_RULE
+           if (!lf) {
+               data += p;
+               break;
+           }
+#endif
+           data = lf + 1;
+       }
+       
+       /* All the headers should be set now */
+
+       ap_send_http_header(r);
+
+       /* Any data left should now be sent directly */
+       ap_rputs(data, r);
+
+       return TRUE;
+
+    case HSE_REQ_MAP_URL_TO_PATH:
+       /* Map a URL to a filename */
+       subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
+                                             *lpdwSize), r);
+
+       GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
+
+       /* IIS puts a trailing slash on directories, Apache doesn't */
+
+       if (S_ISDIR (subreq->finfo.st_mode)) {
+               int l = strlen((char *)lpvBuffer);
+
+               ((char *)lpvBuffer)[l] = '\\';
+               ((char *)lpvBuffer)[l + 1] = '\0';
+       }
+       
+       return TRUE;
+
+    case HSE_REQ_DONE_WITH_SESSION:
+       /* Do nothing... since we don't support async I/O, they'll
+        * return from HttpExtensionProc soon
+        */
+       return TRUE;
+
+    /* We don't support all this async I/O, Microsoft-specific stuff */
+    case HSE_REQ_IO_COMPLETION:
+    case HSE_REQ_TRANSMIT_FILE:
+       ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+                   "ISAPI asynchronous I/O not supported: %s", r->filename);
+    default:
+       SetLastError(ERROR_INVALID_PARAMETER);
+       return FALSE;
+    }
+}
+
+handler_rec isapi_handlers[] = {
+{ "isapi-isa", isapi_handler },
+{ NULL}
+};
+
+module isapi_module = {
+   STANDARD_MODULE_STUFF,
+   NULL,                       /* initializer */
+   NULL,                       /* create per-dir config */
+   NULL,                       /* merge per-dir config */
+   NULL,                       /* server config */
+   NULL,                       /* merge server config */
+   NULL,                       /* command table */
+   isapi_handlers,             /* handlers */
+   NULL,                       /* filename translation */
+   NULL,                       /* check_user_id */
+   NULL,                       /* check auth */
+   NULL,                       /* check access */
+   NULL,                       /* type_checker */
+   NULL,                       /* logger */
+   NULL                                /* header parser */
+};
diff --git a/modules/cache/.indent.pro b/modules/cache/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/modules/echo/.indent.pro b/modules/echo/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/modules/experimental/.indent.pro b/modules/experimental/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/modules/filters/.indent.pro b/modules/filters/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/modules/generators/.indent.pro b/modules/generators/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/modules/generators/mod_info.dsp b/modules/generators/mod_info.dsp
new file mode 100644 (file)
index 0000000..cf85646
--- /dev/null
@@ -0,0 +1,112 @@
+# Microsoft Developer Studio Project File - Name="ApacheModuleInfo" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleInfo - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleInfo.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleInfo.mak" CFG="ApacheModuleInfo - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ApacheModuleInfo - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleInfo - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ApacheModuleInfo - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\ApacheModuleInfoR"
+# PROP Intermediate_Dir ".\ApacheModuleInfoR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "ApacheModuleInfo - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\ApacheModuleInfoD"
+# PROP Intermediate_Dir ".\ApacheModuleInfoD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ApacheModuleInfo - Win32 Release"
+# Name "ApacheModuleInfo - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_info.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/modules/generators/mod_status.dsp b/modules/generators/mod_status.dsp
new file mode 100644 (file)
index 0000000..8bc9271
--- /dev/null
@@ -0,0 +1,113 @@
+# Microsoft Developer Studio Project File - Name="ApacheModuleStatus" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleStatus - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleStatus.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleStatus.mak"\
+ CFG="ApacheModuleStatus - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ApacheModuleStatus - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleStatus - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ApacheModuleStatus - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\ApacheModuleStatusR"
+# PROP Intermediate_Dir ".\ApacheModuleStatusR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "ApacheModuleStatus - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\ApacheModuleStatusD"
+# PROP Intermediate_Dir ".\ApacheModuleStatusD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ApacheModuleStatus - Win32 Release"
+# Name "ApacheModuleStatus - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_status.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/modules/http/.indent.pro b/modules/http/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/modules/http/http_core.c b/modules/http/http_core.c
new file mode 100644 (file)
index 0000000..e02d32e
--- /dev/null
@@ -0,0 +1,3214 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"     /* For index_of_response().  Grump. */
+#include "http_request.h"
+#include "http_conf_globals.h"
+#include "http_vhost.h"
+#include "http_main.h"         /* For the default_handler below... */
+#include "http_log.h"
+#include "rfc1413.h"
+#include "util_md5.h"
+#include "scoreboard.h"
+#include "fnmatch.h"
+
+#ifdef USE_MMAP_FILES
+#include <sys/mman.h>
+
+/* mmap support for static files based on ideas from John Heidemann's
+ * patch against 1.0.5.  See
+ * <http://www.isi.edu/~johnh/SOFTWARE/APACHE/index.html>.
+ */
+
+/* Files have to be at least this big before they're mmap()d.  This is to deal
+ * with systems where the expense of doing an mmap() and an munmap() outweighs
+ * the benefit for small files.  It shouldn't be set lower than 1.
+ */
+#ifndef MMAP_THRESHOLD
+#ifdef SUNOS4
+#define MMAP_THRESHOLD         (8*1024)
+#else
+#define MMAP_THRESHOLD         1
+#endif
+#endif
+#endif
+#ifndef MMAP_LIMIT
+#define MMAP_LIMIT              (4*1024*1024)
+#endif
+
+/* Server core module... This module provides support for really basic
+ * server operations, including options and commands which control the
+ * operation of other modules.  Consider this the bureaucracy module.
+ *
+ * The core module also defines handlers, etc., do handle just enough
+ * to allow a server with the core module ONLY to actually serve documents
+ * (though it slaps DefaultType on all of 'em); this was useful in testing,
+ * but may not be worth preserving.
+ *
+ * This file could almost be mod_core.c, except for the stuff which affects
+ * the http_conf_globals.
+ */
+
+static void *create_core_dir_config(pool *a, char *dir)
+{
+    core_dir_config *conf;
+
+    conf = (core_dir_config *)ap_pcalloc(a, sizeof(core_dir_config));
+    if (!dir || dir[strlen(dir) - 1] == '/') {
+        conf->d = dir;
+    }
+    else if (strncmp(dir, "proxy:", 6) == 0) {
+        conf->d = ap_pstrdup(a, dir);
+    }
+    else {
+        conf->d = ap_pstrcat(a, dir, "/", NULL);
+    }
+    conf->d_is_fnmatch = conf->d ? (ap_is_fnmatch(conf->d) != 0) : 0;
+    conf->d_components = conf->d ? ap_count_dirs(conf->d) : 0;
+
+    conf->opts = dir ? OPT_UNSET : OPT_UNSET|OPT_ALL;
+    conf->opts_add = conf->opts_remove = OPT_NONE;
+    conf->override = dir ? OR_UNSET : OR_UNSET|OR_ALL;
+
+    conf->content_md5 = 2;
+
+    conf->use_canonical_name = USE_CANONICAL_NAME_UNSET;
+
+    conf->hostname_lookups = HOSTNAME_LOOKUP_UNSET;
+    conf->do_rfc1413 = DEFAULT_RFC1413 | 2; /* set bit 1 to indicate default */
+    conf->satisfy = SATISFY_NOSPEC;
+
+#ifdef RLIMIT_CPU
+    conf->limit_cpu = NULL;
+#endif
+#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
+    conf->limit_mem = NULL;
+#endif
+#ifdef RLIMIT_NPROC
+    conf->limit_nproc = NULL;
+#endif
+
+    conf->limit_req_body = 0;
+    conf->sec = ap_make_array(a, 2, sizeof(void *));
+#ifdef WIN32
+    conf->script_interpreter_source = INTERPRETER_SOURCE_UNSET;
+#endif
+
+    conf->server_signature = srv_sig_unset;
+
+    return (void *)conf;
+}
+
+static void *merge_core_dir_configs(pool *a, void *basev, void *newv)
+{
+    core_dir_config *base = (core_dir_config *)basev;
+    core_dir_config *new = (core_dir_config *)newv;
+    core_dir_config *conf;
+    int i;
+  
+    conf = (core_dir_config *)ap_palloc(a, sizeof(core_dir_config));
+    memcpy((char *)conf, (const char *)base, sizeof(core_dir_config));
+    if (base->response_code_strings) {
+       conf->response_code_strings =
+           ap_palloc(a, sizeof(*conf->response_code_strings)
+                     * RESPONSE_CODES);
+       memcpy(conf->response_code_strings, base->response_code_strings,
+              sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+    }
+    
+    conf->d = new->d;
+    conf->d_is_fnmatch = new->d_is_fnmatch;
+    conf->d_components = new->d_components;
+    conf->r = new->r;
+    
+    if (new->opts & OPT_UNSET) {
+       /* there was no explicit setting of new->opts, so we merge
+        * preserve the invariant (opts_add & opts_remove) == 0
+        */
+       conf->opts_add = (conf->opts_add & ~new->opts_remove) | new->opts_add;
+       conf->opts_remove = (conf->opts_remove & ~new->opts_add)
+                           | new->opts_remove;
+       conf->opts = (conf->opts & ~conf->opts_remove) | conf->opts_add;
+        if ((base->opts & OPT_INCNOEXEC) && (new->opts & OPT_INCLUDES)) {
+            conf->opts = (conf->opts & ~OPT_INCNOEXEC) | OPT_INCLUDES;
+       }
+    }
+    else {
+       /* otherwise we just copy, because an explicit opts setting
+        * overrides all earlier +/- modifiers
+        */
+       conf->opts = new->opts;
+       conf->opts_add = new->opts_add;
+       conf->opts_remove = new->opts_remove;
+    }
+
+    if (!(new->override & OR_UNSET)) {
+        conf->override = new->override;
+    }
+    if (new->ap_default_type) {
+        conf->ap_default_type = new->ap_default_type;
+    }
+    
+    if (new->ap_auth_type) {
+        conf->ap_auth_type = new->ap_auth_type;
+    }
+    if (new->ap_auth_name) {
+        conf->ap_auth_name = new->ap_auth_name;
+    }
+    if (new->ap_requires) {
+        conf->ap_requires = new->ap_requires;
+    }
+
+    if (new->response_code_strings) {
+       if (conf->response_code_strings == NULL) {
+           conf->response_code_strings = ap_palloc(a,
+               sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+           memcpy(conf->response_code_strings, new->response_code_strings,
+                  sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+       }
+       else {
+           for (i = 0; i < RESPONSE_CODES; ++i) {
+               if (new->response_code_strings[i] != NULL) {
+                   conf->response_code_strings[i]
+                       = new->response_code_strings[i];
+               }
+           }
+       }
+    }
+    if (new->hostname_lookups != HOSTNAME_LOOKUP_UNSET) {
+       conf->hostname_lookups = new->hostname_lookups;
+    }
+    if ((new->do_rfc1413 & 2) == 0) {
+        conf->do_rfc1413 = new->do_rfc1413;
+    }
+    if ((new->content_md5 & 2) == 0) {
+        conf->content_md5 = new->content_md5;
+    }
+    if (new->use_canonical_name != USE_CANONICAL_NAME_UNSET) {
+       conf->use_canonical_name = new->use_canonical_name;
+    }
+
+#ifdef RLIMIT_CPU
+    if (new->limit_cpu) {
+        conf->limit_cpu = new->limit_cpu;
+    }
+#endif
+#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
+    if (new->limit_mem) {
+        conf->limit_mem = new->limit_mem;
+    }
+#endif
+#ifdef RLIMIT_NPROC    
+    if (new->limit_nproc) {
+        conf->limit_nproc = new->limit_nproc;
+    }
+#endif
+
+    if (new->limit_req_body) {
+        conf->limit_req_body = new->limit_req_body;
+    }
+    conf->sec = ap_append_arrays(a, base->sec, new->sec);
+
+    if (new->satisfy != SATISFY_NOSPEC) {
+        conf->satisfy = new->satisfy;
+    }
+
+#ifdef WIN32
+    if (new->script_interpreter_source != INTERPRETER_SOURCE_UNSET) {
+        conf->script_interpreter_source = new->script_interpreter_source;
+    }
+#endif
+
+    if (new->server_signature != srv_sig_unset) {
+       conf->server_signature = new->server_signature;
+    }
+
+    return (void*)conf;
+}
+
+static void *create_core_server_config(pool *a, server_rec *s)
+{
+    core_server_config *conf;
+    int is_virtual = s->is_virtual;
+  
+    conf = (core_server_config *)ap_pcalloc(a, sizeof(core_server_config));
+#ifdef GPROF
+    conf->gprof_dir = NULL;
+#endif
+    conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME;
+    conf->ap_document_root = is_virtual ? NULL : DOCUMENT_LOCATION;
+    conf->sec = ap_make_array(a, 40, sizeof(void *));
+    conf->sec_url = ap_make_array(a, 40, sizeof(void *));
+    
+    return (void *)conf;
+}
+
+static void *merge_core_server_configs(pool *p, void *basev, void *virtv)
+{
+    core_server_config *base = (core_server_config *)basev;
+    core_server_config *virt = (core_server_config *)virtv;
+    core_server_config *conf;
+
+    conf = (core_server_config *)ap_pcalloc(p, sizeof(core_server_config));
+    *conf = *virt;
+    if (!conf->access_name) {
+        conf->access_name = base->access_name;
+    }
+    if (!conf->ap_document_root) {
+        conf->ap_document_root = base->ap_document_root;
+    }
+    conf->sec = ap_append_arrays(p, base->sec, virt->sec);
+    conf->sec_url = ap_append_arrays(p, base->sec_url, virt->sec_url);
+
+    return conf;
+}
+
+/* Add per-directory configuration entry (for <directory> section);
+ * these are part of the core server config.
+ */
+
+CORE_EXPORT(void) ap_add_per_dir_conf(server_rec *s, void *dir_config)
+{
+    core_server_config *sconf = ap_get_module_config(s->module_config,
+                                                    &core_module);
+    void **new_space = (void **)ap_push_array(sconf->sec);
+    
+    *new_space = dir_config;
+}
+
+CORE_EXPORT(void) ap_add_per_url_conf(server_rec *s, void *url_config)
+{
+    core_server_config *sconf = ap_get_module_config(s->module_config,
+                                                    &core_module);
+    void **new_space = (void **)ap_push_array(sconf->sec_url);
+    
+    *new_space = url_config;
+}
+
+CORE_EXPORT(void) ap_add_file_conf(core_dir_config *conf, void *url_config)
+{
+    void **new_space = (void **)ap_push_array(conf->sec);
+    
+    *new_space = url_config;
+}
+
+/* core_reorder_directories reorders the directory sections such that the
+ * 1-component sections come first, then the 2-component, and so on, finally
+ * followed by the "special" sections.  A section is "special" if it's a regex,
+ * or if it doesn't start with / -- consider proxy: matching.  All movements
+ * are in-order to preserve the ordering of the sections from the config files.
+ * See directory_walk().
+ */
+
+#ifdef HAVE_DRIVE_LETTERS
+#define IS_SPECIAL(entry_core) \
+    ((entry_core)->r != NULL \
+       || ((entry_core)->d[0] != '/' && (entry_core)->d[1] != ':'))
+#else
+#define IS_SPECIAL(entry_core) \
+    ((entry_core)->r != NULL || (entry_core)->d[0] != '/')
+#endif
+
+/* We need to do a stable sort, qsort isn't stable.  So to make it stable
+ * we'll be maintaining the original index into the list, and using it
+ * as the minor key during sorting.  The major key is the number of
+ * components (where a "special" section has infinite components).
+ */
+struct reorder_sort_rec {
+    void *elt;
+    int orig_index;
+};
+
+static int reorder_sorter(const void *va, const void *vb)
+{
+    const struct reorder_sort_rec *a = va;
+    const struct reorder_sort_rec *b = vb;
+    core_dir_config *core_a;
+    core_dir_config *core_b;
+
+    core_a = (core_dir_config *)ap_get_module_config(a->elt, &core_module);
+    core_b = (core_dir_config *)ap_get_module_config(b->elt, &core_module);
+    if (IS_SPECIAL(core_a)) {
+       if (!IS_SPECIAL(core_b)) {
+           return 1;
+       }
+    }
+    else if (IS_SPECIAL(core_b)) {
+       return -1;
+    }
+    else {
+       /* we know they're both not special */
+       if (core_a->d_components < core_b->d_components) {
+           return -1;
+       }
+       else if (core_a->d_components > core_b->d_components) {
+           return 1;
+       }
+    }
+    /* Either they're both special, or they're both not special and have the
+     * same number of components.  In any event, we now have to compare
+     * the minor key. */
+    return a->orig_index - b->orig_index;
+}
+
+void ap_core_reorder_directories(pool *p, server_rec *s)
+{
+    core_server_config *sconf;
+    array_header *sec;
+    struct reorder_sort_rec *sortbin;
+    int nelts;
+    void **elts;
+    int i;
+    pool *tmp;
+
+    sconf = ap_get_module_config(s->module_config, &core_module);
+    sec = sconf->sec;
+    nelts = sec->nelts;
+    elts = (void **)sec->elts;
+
+    /* we have to allocate tmp space to do a stable sort */
+    tmp = ap_make_sub_pool(p);
+    sortbin = ap_palloc(tmp, sec->nelts * sizeof(*sortbin));
+    for (i = 0; i < nelts; ++i) {
+       sortbin[i].orig_index = i;
+       sortbin[i].elt = elts[i];
+    }
+
+    qsort(sortbin, nelts, sizeof(*sortbin), reorder_sorter);
+
+    /* and now copy back to the original array */
+    for (i = 0; i < nelts; ++i) {
+      elts[i] = sortbin[i].elt;
+    }
+
+    ap_destroy_pool(tmp);
+}
+
+/*****************************************************************
+ *
+ * There are some elements of the core config structures in which
+ * other modules have a legitimate interest (this is ugly, but necessary
+ * to preserve NCSA back-compatibility).  So, we have a bunch of accessors
+ * here...
+ */
+
+API_EXPORT(int) ap_allow_options(request_rec *r)
+{
+    core_dir_config *conf = 
+      (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); 
+
+    return conf->opts; 
+} 
+
+API_EXPORT(int) ap_allow_overrides(request_rec *r) 
+{ 
+    core_dir_config *conf;
+    conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                  &core_module); 
+
+    return conf->override; 
+} 
+
+API_EXPORT(const char *) ap_auth_type(request_rec *r)
+{
+    core_dir_config *conf;
+
+    conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                  &core_module); 
+    return conf->ap_auth_type;
+}
+
+API_EXPORT(const char *) ap_auth_name(request_rec *r)
+{
+    core_dir_config *conf;
+
+    conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                  &core_module); 
+    return conf->ap_auth_name;
+}
+
+API_EXPORT(const char *) ap_default_type(request_rec *r)
+{
+    core_dir_config *conf;
+
+    conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                  &core_module); 
+    return conf->ap_default_type 
+               ? conf->ap_default_type 
+               : DEFAULT_CONTENT_TYPE;
+}
+
+API_EXPORT(const char *) ap_document_root(request_rec *r) /* Don't use this! */
+{
+    core_server_config *conf;
+
+    conf = (core_server_config *)ap_get_module_config(r->server->module_config,
+                                                     &core_module); 
+    return conf->ap_document_root;
+}
+
+API_EXPORT(const array_header *) ap_requires(request_rec *r)
+{
+    core_dir_config *conf;
+
+    conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                  &core_module); 
+    return conf->ap_requires;
+}
+
+API_EXPORT(int) ap_satisfies(request_rec *r)
+{
+    core_dir_config *conf;
+
+    conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                  &core_module);
+
+    return conf->satisfy;
+}
+
+/* Should probably just get rid of this... the only code that cares is
+ * part of the core anyway (and in fact, it isn't publicised to other
+ * modules).
+ */
+
+char *ap_response_code_string(request_rec *r, int error_index)
+{
+    core_dir_config *conf;
+
+    conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                  &core_module); 
+
+    if (conf->response_code_strings == NULL) {
+       return NULL;
+    }
+    return conf->response_code_strings[error_index];
+}
+
+
+/* Code from Harald Hanche-Olsen <hanche@imf.unit.no> */
+static ap_inline void do_double_reverse (conn_rec *conn)
+{
+    struct hostent *hptr;
+
+    if (conn->double_reverse) {
+       /* already done */
+       return;
+    }
+    if (conn->remote_host == NULL || conn->remote_host[0] == '\0') {
+       /* single reverse failed, so don't bother */
+       conn->double_reverse = -1;
+       return;
+    }
+    hptr = gethostbyname(conn->remote_host);
+    if (hptr) {
+       char **haddr;
+
+       for (haddr = hptr->h_addr_list; *haddr; haddr++) {
+           if (((struct in_addr *)(*haddr))->s_addr
+               == conn->remote_addr.sin_addr.s_addr) {
+               conn->double_reverse = 1;
+               return;
+           }
+       }
+    }
+    conn->double_reverse = -1;
+}
+
+API_EXPORT(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config,
+                                           int type)
+{
+    struct in_addr *iaddr;
+    struct hostent *hptr;
+    int hostname_lookups;
+    int old_stat = SERVER_DEAD;        /* we shouldn't ever be in this state */
+
+    /* If we haven't checked the host name, and we want to */
+    if (dir_config) {
+       hostname_lookups =
+           ((core_dir_config *)ap_get_module_config(dir_config, &core_module))
+               ->hostname_lookups;
+       if (hostname_lookups == HOSTNAME_LOOKUP_UNSET) {
+           hostname_lookups = HOSTNAME_LOOKUP_OFF;
+       }
+    }
+    else {
+       /* the default */
+       hostname_lookups = HOSTNAME_LOOKUP_OFF;
+    }
+
+    if (type != REMOTE_NOLOOKUP
+       && conn->remote_host == NULL
+       && (type == REMOTE_DOUBLE_REV
+           || hostname_lookups != HOSTNAME_LOOKUP_OFF)) {
+       old_stat = ap_update_child_status(conn->child_num, SERVER_BUSY_DNS,
+                                         (request_rec*)NULL);
+       iaddr = &(conn->remote_addr.sin_addr);
+       hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), AF_INET);
+       if (hptr != NULL) {
+           conn->remote_host = ap_pstrdup(conn->pool, (void *)hptr->h_name);
+           ap_str_tolower(conn->remote_host);
+          
+           if (hostname_lookups == HOSTNAME_LOOKUP_DOUBLE) {
+               do_double_reverse(conn);
+               if (conn->double_reverse != 1) {
+                   conn->remote_host = NULL;
+               }
+           }
+       }
+       /* if failed, set it to the NULL string to indicate error */
+       if (conn->remote_host == NULL) {
+           conn->remote_host = "";
+       }
+    }
+    if (type == REMOTE_DOUBLE_REV) {
+       do_double_reverse(conn);
+       if (conn->double_reverse == -1) {
+           return NULL;
+       }
+    }
+    if (old_stat != SERVER_DEAD) {
+       (void)ap_update_child_status(conn->child_num, old_stat,
+                                    (request_rec*)NULL);
+    }
+
+/*
+ * Return the desired information; either the remote DNS name, if found,
+ * or either NULL (if the hostname was requested) or the IP address
+ * (if any identifier was requested).
+ */
+    if (conn->remote_host != NULL && conn->remote_host[0] != '\0') {
+       return conn->remote_host;
+    }
+    else {
+       if (type == REMOTE_HOST || type == REMOTE_DOUBLE_REV) {
+           return NULL;
+       }
+       else {
+           return conn->remote_ip;
+       }
+    }
+}
+
+API_EXPORT(const char *) ap_get_remote_logname(request_rec *r)
+{
+    core_dir_config *dir_conf;
+
+    if (r->connection->remote_logname != NULL) {
+       return r->connection->remote_logname;
+    }
+
+/* If we haven't checked the identity, and we want to */
+    dir_conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                      &core_module);
+
+    if (dir_conf->do_rfc1413 & 1) {
+       return ap_rfc1413(r->connection, r->server);
+    }
+    else {
+       return NULL;
+    }
+}
+
+/* There are two options regarding what the "name" of a server is.  The
+ * "canonical" name as defined by ServerName and Port, or the "client's
+ * name" as supplied by a possible Host: header or full URI.  We never
+ * trust the port passed in the client's headers, we always use the
+ * port of the actual socket.
+ *
+ * The DNS option to UseCanonicalName causes this routine to do a
+ * reverse lookup on the local IP address of the connectiona and use
+ * that for the ServerName. This makes its value more reliable while
+ * at the same time allowing Demon's magic virtual hosting to work.
+ * The assumption is that DNS lookups are sufficiently quick...
+ * -- fanf 1998-10-03
+ */
+API_EXPORT(const char *) ap_get_server_name(request_rec *r)
+{
+    conn_rec *conn = r->connection;
+    core_dir_config *d;
+
+    d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                               &core_module);
+
+    if (d->use_canonical_name == USE_CANONICAL_NAME_OFF) {
+        return r->hostname ? r->hostname : r->server->server_hostname;
+    }
+    if (d->use_canonical_name == USE_CANONICAL_NAME_DNS) {
+        if (conn->local_host == NULL) {
+           struct in_addr *iaddr;
+           struct hostent *hptr;
+            int old_stat;
+           old_stat = ap_update_child_status(conn->child_num,
+                                             SERVER_BUSY_DNS, r);
+           iaddr = &(conn->local_addr.sin_addr);
+           hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr),
+                                AF_INET);
+           if (hptr != NULL) {
+               conn->local_host = ap_pstrdup(conn->pool,
+                                             (void *)hptr->h_name);
+               ap_str_tolower(conn->local_host);
+           }
+           else {
+               conn->local_host = ap_pstrdup(conn->pool,
+                                             r->server->server_hostname);
+           }
+           (void) ap_update_child_status(conn->child_num, old_stat, r);
+       }
+       return conn->local_host;
+    }
+    /* default */
+    return r->server->server_hostname;
+}
+
+API_EXPORT(unsigned) ap_get_server_port(const request_rec *r)
+{
+    unsigned port;
+    core_dir_config *d =
+      (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+    
+    port = r->server->port ? r->server->port : ap_default_port(r);
+
+    if (d->use_canonical_name == USE_CANONICAL_NAME_OFF
+       || d->use_canonical_name == USE_CANONICAL_NAME_DNS) {
+        return r->hostname ? ntohs(r->connection->local_addr.sin_port)
+                          : port;
+    }
+    /* default */
+    return port;
+}
+
+API_EXPORT(char *) ap_construct_url(pool *p, const char *uri,
+                                   request_rec *r)
+{
+    unsigned port = ap_get_server_port(r);
+    const char *host = ap_get_server_name(r);
+
+    if (ap_is_default_port(port, r)) {
+       return ap_pstrcat(p, ap_http_method(r), "://", host, uri, NULL);
+    }
+    return ap_psprintf(p, "%s://%s:%u%s", ap_http_method(r), host, port, uri);
+}
+
+API_EXPORT(unsigned long) ap_get_limit_req_body(const request_rec *r)
+{
+    core_dir_config *d =
+      (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+    
+    return d->limit_req_body;
+}
+
+#ifdef WIN32
+static char* get_interpreter_from_win32_registry(pool *p, const char* ext) 
+{
+    char extension_path[] = "SOFTWARE\\Classes\\";
+    char executable_path[] = "\\SHELL\\OPEN\\COMMAND";
+
+    HKEY hkeyOpen;
+    DWORD type;
+    int size;
+    int result;
+    char *keyName;
+    char *buffer;
+    char *s;
+
+    if (!ext)
+        return NULL;
+    /* 
+     * Future optimization:
+     * When the registry is successfully searched, store the interpreter
+     * string in a table to make subsequent look-ups faster
+     */
+
+    /* Open the key associated with the script extension */
+    keyName = ap_pstrcat(p, extension_path, ext, NULL);
+
+    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, 
+                          &hkeyOpen);
+
+    if (result != ERROR_SUCCESS) 
+        return NULL;
+
+    /* Read to NULL buffer to find value size */
+    size = 0;
+    result = RegQueryValueEx(hkeyOpen, "", NULL, &type, NULL, &size);
+
+    if (result == ERROR_SUCCESS) {
+        buffer = ap_palloc(p, size);
+        result = RegQueryValueEx(hkeyOpen, "", NULL, &type, buffer, &size);
+    }
+
+    RegCloseKey(hkeyOpen);
+
+    if (result != ERROR_SUCCESS)
+        return NULL;
+
+    /* Open the key associated with the interpreter path */
+    keyName = ap_pstrcat(p, extension_path, buffer, executable_path, NULL);
+
+    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, 
+                          &hkeyOpen);
+
+    if (result != ERROR_SUCCESS)
+        return NULL;
+
+    /* Read to NULL buffer to find value size */
+    size = 0;
+    result = RegQueryValueEx(hkeyOpen, "", 0, &type, NULL, &size);
+
+    if (result == ERROR_SUCCESS) {
+        buffer = ap_palloc(p, size);
+        result = RegQueryValueEx(hkeyOpen, "", 0, &type, buffer, &size);
+    }
+
+    RegCloseKey(hkeyOpen);
+
+    if (result != ERROR_SUCCESS)
+        return NULL;
+
+    /*
+     * The canonical way shell command entries are entered in the Win32 
+     * registry is as follows:
+     *   shell [options] "%1"
+     * where
+     *   shell - full path name to interpreter or shell to run.
+     *           E.g., c:\usr\local\ntreskit\perl\bin\perl.exe
+     *   options - optional switches
+     *              E.g., \C
+     *   "%1" - Place holder for file to run the shell against. 
+     *          Typically quoted.
+     *
+     * If we find a %1 or a quoted %1, lop it off. 
+     */
+    if (buffer && *buffer) {
+        if ((s = strstr(buffer, "\"%1")))
+            *s = '\0';
+        else if ((s = strstr(buffer, "%1"))) 
+            *s = '\0';
+    }
+
+    return buffer;
+}
+
+API_EXPORT (file_type_e) ap_get_win32_interpreter(const  request_rec *r, 
+                                                  char** interpreter )
+{
+    HANDLE hFile;
+    DWORD nBytesRead;
+    BOOLEAN bResult;
+    char buffer[1024];
+    core_dir_config *d;
+    int i;
+    file_type_e fileType = eFileTypeUNKNOWN;
+    char *ext = NULL;
+    char *exename = NULL;
+
+    d = (core_dir_config *)ap_get_module_config(r->per_dir_config, 
+                                                &core_module);
+
+    /* Find the file extension */
+    exename = strrchr(r->filename, '/');
+    if (!exename) {
+        exename = strrchr(r->filename, '\\');
+    }
+    if (!exename) {
+        exename = r->filename;
+    }
+    else {
+        exename++;
+    }
+    ext = strrchr(exename, '.');
+
+    if (ext && (!strcasecmp(ext,".bat") || !strcasecmp(ext,".cmd"))) {
+        return eFileTypeEXE32;
+    }
+
+    /* If the file has an extension and it is not .com and not .exe and
+     * we've been instructed to search the registry, then do it!
+     */
+    if (ext && strcasecmp(ext,".exe") && strcasecmp(ext,".com") &&
+        d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY) {
+         /* Check the registry */
+        *interpreter = get_interpreter_from_win32_registry(r->pool, ext);
+        if (*interpreter)
+            return eFileTypeSCRIPT;
+        else {
+            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r->server,
+             "ScriptInterpreterSource config directive set to \"registry\".\n\t"
+             "Registry was searched but interpreter not found. Trying the shebang line.");
+        }
+    }        
+
+    /* Need to peek into the file figure out what it really is... */
+    hFile = CreateFile(r->filename, GENERIC_READ, FILE_SHARE_READ, NULL,
+                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE) {
+        return eFileTypeUNKNOWN;
+    }
+    bResult = ReadFile(hFile, (void*) &buffer, sizeof(buffer) - 1, 
+                       &nBytesRead, NULL);
+    if (!bResult || (nBytesRead == 0)) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                      "ReadFile(%s) failed", r->filename);
+        CloseHandle(hFile);
+        return eFileTypeUNKNOWN;
+    }
+    CloseHandle(hFile);
+    buffer[nBytesRead] = '\0';
+
+    /* Script or executable, that is the question... */
+    if ((buffer[0] == '#') && (buffer[1] == '!')) {
+        /* Assuming file is a script since it starts with a shebang */
+        fileType = eFileTypeSCRIPT;
+        for (i = 2; i < sizeof(buffer); i++) {
+            if ((buffer[i] == '\r')
+                || (buffer[i] == '\n')) {
+                break;
+            }
+        }
+        buffer[i] = '\0';
+        for (i = 2; buffer[i] == ' ' ; ++i)
+            ;
+        *interpreter = ap_pstrdup(r->pool, buffer + i ); 
+    }
+    else {
+        /* Not a script, is it an executable? */
+        IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)buffer;    
+        if ((nBytesRead >= sizeof(IMAGE_DOS_HEADER)) && (hdr->e_magic == IMAGE_DOS_SIGNATURE)) {
+            if (hdr->e_lfarlc < 0x40)
+                fileType = eFileTypeEXE16;
+            else
+                fileType = eFileTypeEXE32;
+        }
+        else
+            fileType = eFileTypeUNKNOWN;
+    }
+
+    return fileType;
+}
+#endif
+
+/*****************************************************************
+ *
+ * Commands... this module handles almost all of the NCSA httpd.conf
+ * commands, but most of the old srm.conf is in the the modules.
+ */
+
+static const char end_directory_section[] = "</Directory>";
+static const char end_directorymatch_section[] = "</DirectoryMatch>";
+static const char end_location_section[] = "</Location>";
+static const char end_locationmatch_section[] = "</LocationMatch>";
+static const char end_files_section[] = "</Files>";
+static const char end_filesmatch_section[] = "</FilesMatch>";
+static const char end_virtualhost_section[] = "</VirtualHost>";
+static const char end_ifmodule_section[] = "</IfModule>";
+static const char end_ifdefine_section[] = "</IfDefine>";
+
+
+API_EXPORT(const char *) ap_check_cmd_context(cmd_parms *cmd,
+                                             unsigned forbidden)
+{
+    const char *gt = (cmd->cmd->name[0] == '<'
+                     && cmd->cmd->name[strlen(cmd->cmd->name)-1] != '>')
+                         ? ">" : "";
+
+    if ((forbidden & NOT_IN_VIRTUALHOST) && cmd->server->is_virtual) {
+       return ap_pstrcat(cmd->pool, cmd->cmd->name, gt,
+                         " cannot occur within <VirtualHost> section", NULL);
+    }
+
+    if ((forbidden & NOT_IN_LIMIT) && cmd->limited != -1) {
+       return ap_pstrcat(cmd->pool, cmd->cmd->name, gt,
+                         " cannot occur within <Limit> section", NULL);
+    }
+
+    if ((forbidden & NOT_IN_DIR_LOC_FILE) == NOT_IN_DIR_LOC_FILE
+       && cmd->path != NULL) {
+       return ap_pstrcat(cmd->pool, cmd->cmd->name, gt,
+                         " cannot occur within <Directory/Location/Files> "
+                         "section", NULL);
+    }
+    
+    if (((forbidden & NOT_IN_DIRECTORY)
+        && (cmd->end_token == end_directory_section
+            || cmd->end_token == end_directorymatch_section)) 
+       || ((forbidden & NOT_IN_LOCATION)
+           && (cmd->end_token == end_location_section
+               || cmd->end_token == end_locationmatch_section)) 
+       || ((forbidden & NOT_IN_FILES)
+           && (cmd->end_token == end_files_section
+               || cmd->end_token == end_filesmatch_section))) {
+       return ap_pstrcat(cmd->pool, cmd->cmd->name, gt,
+                         " cannot occur within <", cmd->end_token+2,
+                         " section", NULL);
+    }
+
+    return NULL;
+}
+
+static const char *set_access_name(cmd_parms *cmd, void *dummy, char *arg)
+{
+    void *sconf = cmd->server->module_config;
+    core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+    const char *err = ap_check_cmd_context(cmd,
+                                          NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    conf->access_name = ap_pstrdup(cmd->pool, arg);
+    return NULL;
+}
+
+#ifdef GPROF
+static const char *set_gprof_dir(cmd_parms *cmd, void *dummy, char *arg)
+{
+    void *sconf = cmd->server->module_config;
+    core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+    const char *err = ap_check_cmd_context(cmd,
+                                          NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    conf->gprof_dir = ap_pstrdup(cmd->pool, arg);
+    return NULL;
+}
+#endif /*GPROF*/
+
+static const char *set_document_root(cmd_parms *cmd, void *dummy, char *arg)
+{
+    void *sconf = cmd->server->module_config;
+    core_server_config *conf = ap_get_module_config(sconf, &core_module);
+  
+    const char *err = ap_check_cmd_context(cmd,
+                                          NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    arg = ap_os_canonical_filename(cmd->pool, arg);
+    if (ap_configtestonly && ap_docrootcheck && !ap_is_directory(arg)) {
+       if (cmd->server->is_virtual) {
+           fprintf(stderr, "Warning: DocumentRoot [%s] does not exist\n",
+                   arg);
+       }
+       else {
+           return "DocumentRoot must be a directory";
+       }
+    }
+    
+    conf->ap_document_root = arg;
+    return NULL;
+}
+
+API_EXPORT(void) ap_custom_response(request_rec *r, int status, char *string)
+{
+    core_dir_config *conf = 
+       ap_get_module_config(r->per_dir_config, &core_module);
+    int idx;
+
+    if(conf->response_code_strings == NULL) {
+        conf->response_code_strings = 
+           ap_pcalloc(r->pool,
+                   sizeof(*conf->response_code_strings) * 
+                   RESPONSE_CODES);
+    }
+
+    idx = ap_index_of_response(status);
+
+    conf->response_code_strings[idx] = 
+       ((ap_is_url(string) || (*string == '/')) && (*string != '"')) ? 
+       ap_pstrdup(r->pool, string) : ap_pstrcat(r->pool, "\"", string, NULL);
+}
+
+static const char *set_error_document(cmd_parms *cmd, core_dir_config *conf,
+                                     char *line)
+{
+    int error_number, index_number, idx500;
+    char *w;
+                
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    /* 1st parameter should be a 3 digit number, which we recognize;
+     * convert it into an array index
+     */
+  
+    w = ap_getword_conf_nc(cmd->pool, &line);
+    error_number = atoi(w);
+
+    idx500 = ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR);
+
+    if (error_number == HTTP_INTERNAL_SERVER_ERROR) {
+        index_number = idx500;
+    }
+    else if ((index_number = ap_index_of_response(error_number)) == idx500) {
+        return ap_pstrcat(cmd->pool, "Unsupported HTTP response code ",
+                         w, NULL);
+    }
+
+    /* The entry should be ignored if it is a full URL for a 401 error */
+
+    if (error_number == 401 &&
+       line[0] != '/' && line[0] != '"') { /* Ignore it... */
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, cmd->server,
+                    "cannot use a full URL in a 401 ErrorDocument "
+                    "directive --- ignoring!");
+    }
+    else { /* Store it... */
+       if (conf->response_code_strings == NULL) {
+           conf->response_code_strings =
+               ap_pcalloc(cmd->pool,
+                          sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+        }
+        conf->response_code_strings[index_number] = ap_pstrdup(cmd->pool, line);
+    }   
+
+    return NULL;
+}
+
+/* access.conf commands...
+ *
+ * The *only* thing that can appear in access.conf at top level is a
+ * <Directory> section.  NB we need to have a way to cut the srm_command_loop
+ * invoked by dirsection (i.e., <Directory>) short when </Directory> is seen.
+ * We do that by returning an error, which dirsection itself recognizes and
+ * discards as harmless.  Cheesy, but it works.
+ */
+
+static const char *set_override(cmd_parms *cmd, core_dir_config *d,
+                               const char *l)
+{
+    char *w;
+  
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    d->override = OR_NONE;
+    while (l[0]) {
+        w = ap_getword_conf(cmd->pool, &l);
+       if (!strcasecmp(w, "Limit")) {
+           d->override |= OR_LIMIT;
+       }
+       else if (!strcasecmp(w, "Options")) {
+           d->override |= OR_OPTIONS;
+       }
+       else if (!strcasecmp(w, "FileInfo")) {
+            d->override |= OR_FILEINFO;
+       }
+       else if (!strcasecmp(w, "AuthConfig")) {
+           d->override |= OR_AUTHCFG;
+       }
+       else if (!strcasecmp(w, "Indexes")) {
+            d->override |= OR_INDEXES;
+       }
+       else if (!strcasecmp(w, "None")) {
+           d->override = OR_NONE;
+       }
+       else if (!strcasecmp(w, "All")) {
+           d->override = OR_ALL;
+       }
+       else {
+           return ap_pstrcat(cmd->pool, "Illegal override option ", w, NULL);
+       }
+       d->override &= ~OR_UNSET;
+    }
+
+    return NULL;
+}
+
+static const char *set_options(cmd_parms *cmd, core_dir_config *d,
+                              const char *l)
+{
+    allow_options_t opt;
+    int first = 1;
+    char action;
+
+    while (l[0]) {
+        char *w = ap_getword_conf(cmd->pool, &l);
+       action = '\0';
+
+       if (*w == '+' || *w == '-') {
+           action = *(w++);
+       }
+       else if (first) {
+           d->opts = OPT_NONE;
+            first = 0;
+        }
+           
+       if (!strcasecmp(w, "Indexes")) {
+           opt = OPT_INDEXES;
+       }
+       else if (!strcasecmp(w, "Includes")) {
+           opt = OPT_INCLUDES;
+       }
+       else if (!strcasecmp(w, "IncludesNOEXEC")) {
+           opt = (OPT_INCLUDES | OPT_INCNOEXEC);
+       }
+       else if (!strcasecmp(w, "FollowSymLinks")) {
+           opt = OPT_SYM_LINKS;
+       }
+       else if (!strcasecmp(w, "SymLinksIfOwnerMatch")) {
+           opt = OPT_SYM_OWNER;
+       }
+       else if (!strcasecmp(w, "execCGI")) {
+           opt = OPT_EXECCGI;
+       }
+       else if (!strcasecmp(w, "MultiViews")) {
+           opt = OPT_MULTI;
+       }
+       else if (!strcasecmp(w, "RunScripts")) { /* AI backcompat. Yuck */
+           opt = OPT_MULTI|OPT_EXECCGI;
+       }
+       else if (!strcasecmp(w, "None")) {
+           opt = OPT_NONE;
+       }
+       else if (!strcasecmp(w, "All")) {
+           opt = OPT_ALL;
+       }
+       else {
+           return ap_pstrcat(cmd->pool, "Illegal option ", w, NULL);
+       }
+
+       /* we ensure the invariant (d->opts_add & d->opts_remove) == 0 */
+       if (action == '-') {
+           d->opts_remove |= opt;
+           d->opts_add &= ~opt;
+           d->opts &= ~opt;
+       }
+       else if (action == '+') {
+           d->opts_add |= opt;
+           d->opts_remove &= ~opt;
+           d->opts |= opt;
+       }
+       else {
+           d->opts |= opt;
+       }
+    }
+
+    return NULL;
+}
+
+static const char *satisfy(cmd_parms *cmd, core_dir_config *c, char *arg)
+{
+    if (!strcasecmp(arg, "all")) {
+        c->satisfy = SATISFY_ALL;
+    }
+    else if (!strcasecmp(arg, "any")) {
+        c->satisfy = SATISFY_ANY;
+    }
+    else {
+        return "Satisfy either 'any' or 'all'.";
+    }
+    return NULL;
+}
+
+static const char *require(cmd_parms *cmd, core_dir_config *c, char *arg)
+{
+    require_line *r;
+  
+    if (!c->ap_requires) {
+        c->ap_requires = ap_make_array(cmd->pool, 2, sizeof(require_line));
+    }
+    r = (require_line *)ap_push_array(c->ap_requires);
+    r->requirement = ap_pstrdup(cmd->pool, arg);
+    r->method_mask = cmd->limited;
+    return NULL;
+}
+
+CORE_EXPORT_NONSTD(const char *) ap_limit_section(cmd_parms *cmd, void *dummy,
+                                                 const char *arg)
+{
+    const char *limited_methods = ap_getword(cmd->pool, &arg, '>');
+    void *tog = cmd->cmd->cmd_data;
+    int limited = 0;
+  
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    /* XXX: NB: Currently, we have no way of checking
+     * whether <Limit> or <LimitExcept> sections are closed properly.
+     * (If we would add a srm_command_loop() here we might...)
+     */
+    
+    while (limited_methods[0]) {
+        char *method = ap_getword_conf(cmd->pool, &limited_methods);
+        int  methnum = ap_method_number_of(method);
+
+        if (methnum == M_TRACE && !tog) {
+            return "TRACE cannot be controlled by <Limit>";
+        }
+        else if (methnum == M_INVALID) {
+            return ap_pstrcat(cmd->pool, "unknown method \"", method,
+                              "\" in <Limit", tog ? "Except>" : ">", NULL);
+        }
+        else {
+            limited |= (1 << methnum);
+        }
+    }
+
+    /* Killing two features with one function,
+     * if (tog == NULL) <Limit>, else <LimitExcept>
+     */
+    cmd->limited = tog ? ~limited : limited;
+    return NULL;
+}
+
+static const char *endlimit_section(cmd_parms *cmd, void *dummy, void *dummy2)
+{
+    void *tog = cmd->cmd->cmd_data;
+
+    if (cmd->limited == -1) {
+        return tog ? "</LimitExcept> unexpected" : "</Limit> unexpected";
+    }
+    
+    cmd->limited = -1;
+    return NULL;
+}
+
+/*
+ * When a section is not closed properly when end-of-file is reached,
+ * then an error message should be printed:
+ */
+static const char *missing_endsection(cmd_parms *cmd, int nest)
+{
+    if (nest < 2) {
+       return ap_psprintf(cmd->pool, "Missing %s directive at end-of-file",
+                          cmd->end_token);
+    }
+    return ap_psprintf(cmd->pool, "%d missing %s directives at end-of-file",
+                      nest, cmd->end_token);
+}
+
+/* We use this in <DirectoryMatch> and <FilesMatch>, to ensure that 
+ * people don't get bitten by wrong-cased regex matches
+ */
+
+#ifdef WIN32
+#define USE_ICASE REG_ICASE
+#else
+#define USE_ICASE 0
+#endif
+
+static const char *end_nested_section(cmd_parms *cmd, void *dummy)
+{
+    if (cmd->end_token == NULL) {
+        return ap_pstrcat(cmd->pool, cmd->cmd->name,
+                         " without matching <", cmd->cmd->name + 2, 
+                         " section", NULL);
+    }
+    /*
+     * This '!=' may look weird on a string comparison, but it's correct --
+     * it's been set up so that checking for two pointers to the same datum
+     * is valid here.  And faster.
+     */
+    if (cmd->cmd->name != cmd->end_token) {
+       return ap_pstrcat(cmd->pool, "Expected ", cmd->end_token, " but saw ",
+                         cmd->cmd->name, NULL);
+    }
+    return cmd->end_token;
+}
+
+/*
+ * Report a missing-'>' syntax error.
+ */
+static char *unclosed_directive(cmd_parms *cmd)
+{
+    return ap_pstrcat(cmd->pool, cmd->cmd->name,
+                     "> directive missing closing '>'", NULL);
+}
+
+static const char *dirsection(cmd_parms *cmd, void *dummy, const char *arg)
+{
+    const char *errmsg;
+    char *endp = strrchr(arg, '>');
+    int old_overrides = cmd->override;
+    char *old_path = cmd->path;
+    core_dir_config *conf;
+    void *new_dir_conf = ap_create_per_dir_config(cmd->pool);
+    regex_t *r = NULL;
+    const char *old_end_token;
+    const command_rec *thiscmd = cmd->cmd;
+
+    const char *err = ap_check_cmd_context(cmd,
+                                          NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (endp == NULL) {
+       return unclosed_directive(cmd);
+    }
+
+    *endp = '\0';
+
+    cmd->path = ap_getword_conf(cmd->pool, &arg);
+    cmd->override = OR_ALL|ACCESS_CONF;
+
+    if (thiscmd->cmd_data) { /* <DirectoryMatch> */
+       r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+    }
+    else if (!strcmp(cmd->path, "~")) {
+       cmd->path = ap_getword_conf(cmd->pool, &arg);
+       r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+    }
+    else {
+       /* Ensure that the pathname is canonical */
+       cmd->path = ap_os_canonical_filename(cmd->pool, cmd->path);
+    }
+
+    old_end_token = cmd->end_token;
+    cmd->end_token = thiscmd->cmd_data ? end_directorymatch_section : end_directory_section;
+    errmsg = ap_srm_command_loop(cmd, new_dir_conf);
+    if (errmsg == NULL) {
+       errmsg = missing_endsection(cmd, 1);
+    }
+    cmd->end_token = old_end_token;
+    if (errmsg != (thiscmd->cmd_data 
+                      ? end_directorymatch_section 
+                  : end_directory_section)) {
+       return errmsg;
+    }
+
+    conf = (core_dir_config *)ap_get_module_config(new_dir_conf, &core_module);
+    conf->r = r;
+
+    ap_add_per_dir_conf(cmd->server, new_dir_conf);
+
+    if (*arg != '\0') {
+       return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+                         "> arguments not (yet) supported.", NULL);
+    }
+
+    cmd->path = old_path;
+    cmd->override = old_overrides;
+
+    return NULL;
+}
+
+static const char *urlsection(cmd_parms *cmd, void *dummy, const char *arg)
+{
+    const char *errmsg;
+    char *endp = strrchr(arg, '>');
+    int old_overrides = cmd->override;
+    char *old_path = cmd->path;
+    core_dir_config *conf;
+    regex_t *r = NULL;
+    const char *old_end_token;
+    const command_rec *thiscmd = cmd->cmd;
+
+    void *new_url_conf = ap_create_per_dir_config(cmd->pool);
+
+    const char *err = ap_check_cmd_context(cmd,
+                                          NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (endp == NULL) {
+       return unclosed_directive(cmd);
+    }
+
+    *endp = '\0';
+
+    cmd->path = ap_getword_conf(cmd->pool, &arg);
+    cmd->override = OR_ALL|ACCESS_CONF;
+
+    if (thiscmd->cmd_data) { /* <LocationMatch> */
+       r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+    }
+    else if (!strcmp(cmd->path, "~")) {
+       cmd->path = ap_getword_conf(cmd->pool, &arg);
+       r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+    }
+
+    old_end_token = cmd->end_token;
+    cmd->end_token = thiscmd->cmd_data ? end_locationmatch_section
+                                       : end_location_section;
+    errmsg = ap_srm_command_loop(cmd, new_url_conf);
+    if (errmsg == NULL) {
+       errmsg = missing_endsection(cmd, 1);
+    }
+    cmd->end_token = old_end_token;
+    if (errmsg != (thiscmd->cmd_data 
+                      ? end_locationmatch_section 
+                      : end_location_section)) {
+       return errmsg;
+    }
+
+    conf = (core_dir_config *)ap_get_module_config(new_url_conf, &core_module);
+    conf->d = ap_pstrdup(cmd->pool, cmd->path);        /* No mangling, please */
+    conf->d_is_fnmatch = ap_is_fnmatch(conf->d) != 0;
+    conf->r = r;
+
+    ap_add_per_url_conf(cmd->server, new_url_conf);
+    
+    if (*arg != '\0') {
+       return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+                         "> arguments not (yet) supported.", NULL);
+    }
+
+    cmd->path = old_path;
+    cmd->override = old_overrides;
+
+    return NULL;
+}
+
+static const char *filesection(cmd_parms *cmd, core_dir_config *c,
+                              const char *arg)
+{
+    const char *errmsg;
+    char *endp = strrchr(arg, '>');
+    int old_overrides = cmd->override;
+    char *old_path = cmd->path;
+    core_dir_config *conf;
+    regex_t *r = NULL;
+    const char *old_end_token;
+    const command_rec *thiscmd = cmd->cmd;
+
+    void *new_file_conf = ap_create_per_dir_config(cmd->pool);
+
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_LOCATION);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (endp == NULL) {
+       return unclosed_directive(cmd);
+    }
+
+    *endp = '\0';
+
+    cmd->path = ap_getword_conf(cmd->pool, &arg);
+    /* Only if not an .htaccess file */
+    if (!old_path) {
+       cmd->override = OR_ALL|ACCESS_CONF;
+    }
+
+    if (thiscmd->cmd_data) { /* <FilesMatch> */
+        r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+    }
+    else if (!strcmp(cmd->path, "~")) {
+       cmd->path = ap_getword_conf(cmd->pool, &arg);
+       r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+    }
+    else {
+       /* Ensure that the pathname is canonical */
+       cmd->path = ap_os_canonical_filename(cmd->pool, cmd->path);
+    }
+
+    old_end_token = cmd->end_token;
+    cmd->end_token = thiscmd->cmd_data ? end_filesmatch_section : end_files_section;
+    errmsg = ap_srm_command_loop(cmd, new_file_conf);
+    if (errmsg == NULL) {
+       errmsg = missing_endsection(cmd, 1);
+    }
+    cmd->end_token = old_end_token;
+    if (errmsg != (thiscmd->cmd_data 
+                      ? end_filesmatch_section 
+                  : end_files_section)) {
+       return errmsg;
+    }
+
+    conf = (core_dir_config *)ap_get_module_config(new_file_conf,
+                                                  &core_module);
+    conf->d = cmd->path;
+    conf->d_is_fnmatch = ap_is_fnmatch(conf->d) != 0;
+    conf->r = r;
+
+    ap_add_file_conf(c, new_file_conf);
+
+    if (*arg != '\0') {
+       return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+                         "> arguments not (yet) supported.", NULL);
+    }
+
+    cmd->path = old_path;
+    cmd->override = old_overrides;
+
+    return NULL;
+}
+
+/* XXX: NB: Currently, we have no way of checking
+ * whether <IfModule> sections are closed properly.
+ * Extra (redundant, unpaired) </IfModule> directives are
+ * simply silently ignored.
+ */
+static const char *end_ifmod(cmd_parms *cmd, void *dummy)
+{
+    return NULL;
+}
+
+static const char *start_ifmod(cmd_parms *cmd, void *dummy, char *arg)
+{
+    char *endp = strrchr(arg, '>');
+    char l[MAX_STRING_LEN];
+    int not = (arg[0] == '!');
+    module *found;
+    int nest = 1;
+
+    if (endp == NULL) {
+       return unclosed_directive(cmd);
+    }
+
+    *endp = '\0';
+
+    if (not) {
+        arg++;
+    }
+
+    found = ap_find_linked_module(arg);
+
+    if ((!not && found) || (not && !found)) {
+        return NULL;
+    }
+
+    while (nest && !(ap_cfg_getline(l, MAX_STRING_LEN, cmd->config_file))) {
+        if (!strncasecmp(l, "<IfModule", 9)) {
+           nest++;
+       }
+       if (!strcasecmp(l, "</IfModule>")) {
+         nest--;
+       }
+    }
+
+    if (nest) {
+       cmd->end_token = end_ifmodule_section;
+       return missing_endsection(cmd, nest);
+    }
+    return NULL;
+}
+
+API_EXPORT(int) ap_exists_config_define(char *name)
+{
+    char **defines;
+    int i;
+
+    defines = (char **)ap_server_config_defines->elts;
+    for (i = 0; i < ap_server_config_defines->nelts; i++) {
+        if (strcmp(defines[i], name) == 0) {
+            return 1;
+       }
+    }
+    return 0;
+}
+
+static const char *end_ifdefine(cmd_parms *cmd, void *dummy) 
+{
+    return NULL;
+}
+
+static const char *start_ifdefine(cmd_parms *cmd, void *dummy, char *arg)
+{
+    char *endp;
+    char l[MAX_STRING_LEN];
+    int defined;
+    int not = 0;
+    int nest = 1;
+
+    endp = strrchr(arg, '>');
+    if (endp == NULL) {
+       return unclosed_directive(cmd);
+    }
+
+    *endp = '\0';
+
+    if (arg[0] == '!') {
+        not = 1;
+       arg++;
+    }
+
+    defined = ap_exists_config_define(arg);
+
+    if ((!not && defined) || (not && !defined)) {
+       return NULL;
+    }
+
+    while (nest && !(ap_cfg_getline(l, MAX_STRING_LEN, cmd->config_file))) {
+        if (!strncasecmp(l, "<IfDefine", 9)) {
+           nest++;
+       }
+       if (!strcasecmp(l, "</IfDefine>")) {
+           nest--;
+       }
+    }
+    if (nest) {
+       cmd->end_token = end_ifdefine_section;
+       return missing_endsection(cmd, nest);
+    }
+    return NULL;
+}
+
+/* httpd.conf commands... beginning with the <VirtualHost> business */
+
+static const char *virtualhost_section(cmd_parms *cmd, void *dummy, char *arg)
+{
+    server_rec *main_server = cmd->server, *s;
+    const char *errmsg;
+    char *endp = strrchr(arg, '>');
+    pool *p = cmd->pool, *ptemp = cmd->temp_pool;
+    const char *old_end_token;
+
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (endp == NULL) {
+       return unclosed_directive(cmd);
+    }
+
+    *endp = '\0';
+    
+    /* FIXME: There's another feature waiting to happen here -- since you
+       can now put multiple addresses/names on a single <VirtualHost>
+       you might want to use it to group common definitions and then
+       define other "subhosts" with their individual differences.  But
+       personally I'd rather just do it with a macro preprocessor. -djg */
+    if (main_server->is_virtual) {
+       return "<VirtualHost> doesn't nest!";
+    }
+    
+    errmsg = ap_init_virtual_host(p, arg, main_server, &s);
+    if (errmsg) {
+       return errmsg;
+    }
+
+    s->next = main_server->next;
+    main_server->next = s;
+
+    s->defn_name = cmd->config_file->name;
+    s->defn_line_number = cmd->config_file->line_number;
+
+    old_end_token = cmd->end_token;
+    cmd->end_token = end_virtualhost_section;
+    cmd->server = s;
+    errmsg = ap_srm_command_loop(cmd, s->lookup_defaults);
+    cmd->server = main_server;
+    if (errmsg == NULL) {
+       errmsg = missing_endsection(cmd, 1);
+    }
+    cmd->end_token = old_end_token;
+
+    if (s->srm_confname) {
+       ap_process_resource_config(s, s->srm_confname, p, ptemp);
+    }
+
+    if (s->access_confname) {
+       ap_process_resource_config(s, s->access_confname, p, ptemp);
+    }
+    
+    if (errmsg == end_virtualhost_section) {
+       return NULL;
+    }
+    return errmsg;
+}
+
+static const char *set_server_alias(cmd_parms *cmd, void *dummy,
+                                   const char *arg)
+{
+    if (!cmd->server->names) {
+       return "ServerAlias only used in <VirtualHost>";
+    }
+    while (*arg) {
+       char **item, *name = ap_getword_conf(cmd->pool, &arg);
+       if (ap_is_matchexp(name)) {
+           item = (char **)ap_push_array(cmd->server->wild_names);
+       }
+       else {
+           item = (char **)ap_push_array(cmd->server->names);
+       }
+       *item = name;
+    }
+    return NULL;
+}
+
+static const char *add_module_command(cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (!ap_add_named_module(arg)) {
+       return ap_pstrcat(cmd->pool, "Cannot add module via name '", arg, 
+                         "': not in list of loaded modules", NULL);
+    }
+    return NULL;
+}
+
+static const char *clear_module_list_command(cmd_parms *cmd, void *dummy)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_clear_module_list();
+    return NULL;
+}
+
+static const char *set_server_string_slot(cmd_parms *cmd, void *dummy,
+                                         char *arg)
+{
+    /* This one's pretty generic... */
+  
+    int offset = (int)(long)cmd->info;
+    char *struct_ptr = (char *)cmd->server;
+    
+    const char *err = ap_check_cmd_context(cmd, 
+                                          NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    *(char **)(struct_ptr + offset) = arg;
+    return NULL;
+}
+
+static const char *server_type(cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (!strcasecmp(arg, "inetd")) {
+        ap_standalone = 0;
+    }
+    else if (!strcasecmp(arg, "standalone")) {
+        ap_standalone = 1;
+    }
+    else {
+        return "ServerType must be either 'inetd' or 'standalone'";
+    }
+
+    return NULL;
+}
+
+static const char *server_port(cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    int port;
+
+    if (err != NULL) {
+       return err;
+    }
+    port = atoi(arg);
+    if (port <= 0 || port >= 65536) { /* 65536 == 1<<16 */
+       return ap_pstrcat(cmd->temp_pool, "The port number \"", arg, 
+                         "\" is outside the appropriate range "
+                         "(i.e., 1..65535).", NULL);
+    }
+    cmd->server->port = port;
+    return NULL;
+}
+
+static const char *set_signature_flag(cmd_parms *cmd, core_dir_config *d, 
+                                     char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (strcasecmp(arg, "On") == 0) {
+       d->server_signature = srv_sig_on;
+    }
+    else if (strcasecmp(arg, "Off") == 0) {
+        d->server_signature = srv_sig_off;
+    }
+    else if (strcasecmp(arg, "EMail") == 0) {
+       d->server_signature = srv_sig_withmail;
+    }
+    else {
+       return "ServerSignature: use one of: off | on | email";
+    }
+    return NULL;
+}
+
+static const char *set_send_buffer_size(cmd_parms *cmd, void *dummy, char *arg)
+{
+    int s = atoi(arg);
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (s < 512 && s != 0) {
+        return "SendBufferSize must be >= 512 bytes, or 0 for system default.";
+    }
+    cmd->server->send_buffer_size = s;
+    return NULL;
+}
+
+static const char *set_user(cmd_parms *cmd, void *dummy, char *arg)
+{
+#ifdef WIN32
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, cmd->server,
+                "User directive has no affect on Win32");
+    cmd->server->server_uid = ap_user_id = 1;
+#else
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (!cmd->server->is_virtual) {
+       ap_user_name = arg;
+       cmd->server->server_uid = ap_user_id = ap_uname2id(arg);
+    }
+    else {
+        if (ap_suexec_enabled) {
+           cmd->server->server_uid = ap_uname2id(arg);
+       }
+       else {
+           cmd->server->server_uid = ap_user_id;
+           fprintf(stderr,
+                   "Warning: User directive in <VirtualHost> "
+                   "requires SUEXEC wrapper.\n");
+       }
+    }
+#if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
+    if (cmd->server->server_uid == 0) {
+       fprintf(stderr,
+               "Error:\tApache has not been designed to serve pages while\n"
+               "\trunning as root.  There are known race conditions that\n"
+               "\twill allow any local user to read any file on the system.\n"
+               "\tIf you still desire to serve pages as root then\n"
+               "\tadd -DBIG_SECURITY_HOLE to the EXTRA_CFLAGS line in your\n"
+               "\tsrc/Configuration file and rebuild the server.  It is\n"
+               "\tstrongly suggested that you instead modify the User\n"
+               "\tdirective in your httpd.conf file to list a non-root\n"
+               "\tuser.\n");
+       exit (1);
+    }
+#endif
+#endif /* WIN32 */
+
+    return NULL;
+}
+
+static const char *set_group(cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (!cmd->server->is_virtual) {
+       cmd->server->server_gid = ap_group_id = ap_gname2id(arg);
+    }
+    else {
+        if (ap_suexec_enabled) {
+           cmd->server->server_gid = ap_gname2id(arg);
+       }
+       else {
+           cmd->server->server_gid = ap_group_id;
+           fprintf(stderr,
+                   "Warning: Group directive in <VirtualHost> requires "
+                   "SUEXEC wrapper.\n");
+       }
+    }
+
+    return NULL;
+}
+
+static const char *set_server_root(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+    if (err != NULL) {
+        return err;
+    }
+
+    arg = ap_os_canonical_filename(cmd->pool, arg);
+
+    if (!ap_is_directory(arg)) {
+        return "ServerRoot must be a valid directory";
+    }
+    ap_cpystrn(ap_server_root, arg,
+              sizeof(ap_server_root));
+    return NULL;
+}
+
+static const char *set_timeout(cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    cmd->server->timeout = atoi(arg);
+    return NULL;
+}
+
+static const char *set_keep_alive_timeout(cmd_parms *cmd, void *dummy,
+                                         char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    cmd->server->keep_alive_timeout = atoi(arg);
+    return NULL;
+}
+
+static const char *set_keep_alive(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    /* We've changed it to On/Off, but used to use numbers
+     * so we accept anything but "Off" or "0" as "On"
+     */
+    if (!strcasecmp(arg, "off") || !strcmp(arg, "0")) {
+       cmd->server->keep_alive = 0;
+    }
+    else {
+       cmd->server->keep_alive = 1;
+    }
+    return NULL;
+}
+
+static const char *set_keep_alive_max(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    cmd->server->keep_alive_max = atoi(arg);
+    return NULL;
+}
+
+static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (cmd->server->is_virtual) {
+       return "PidFile directive not allowed in <VirtualHost>";
+    }
+    ap_pid_fname = arg;
+    return NULL;
+}
+
+static const char *set_scoreboard(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_scoreboard_fname = arg;
+    return NULL;
+}
+
+static const char *set_lockfile(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_lock_fname = arg;
+    return NULL;
+}
+
+static const char *set_idcheck(cmd_parms *cmd, core_dir_config *d, int arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    d->do_rfc1413 = arg != 0;
+    return NULL;
+}
+
+static const char *set_hostname_lookups(cmd_parms *cmd, core_dir_config *d,
+                                       char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (!strcasecmp(arg, "on")) {
+       d->hostname_lookups = HOSTNAME_LOOKUP_ON;
+    }
+    else if (!strcasecmp(arg, "off")) {
+       d->hostname_lookups = HOSTNAME_LOOKUP_OFF;
+    }
+    else if (!strcasecmp(arg, "double")) {
+       d->hostname_lookups = HOSTNAME_LOOKUP_DOUBLE;
+    }
+    else {
+       return "parameter must be 'on', 'off', or 'double'";
+    }
+    return NULL;
+}
+
+static const char *set_serverpath(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    cmd->server->path = arg;
+    cmd->server->pathlen = strlen(arg);
+    return NULL;
+}
+
+static const char *set_content_md5(cmd_parms *cmd, core_dir_config *d, int arg)
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    d->content_md5 = arg != 0;
+    return NULL;
+}
+
+static const char *set_use_canonical_name(cmd_parms *cmd, core_dir_config *d, 
+                                         char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+    if (err != NULL) {
+       return err;
+    }
+
+    if (strcasecmp(arg, "on") == 0) {
+        d->use_canonical_name = USE_CANONICAL_NAME_ON;
+    }
+    else if (strcasecmp(arg, "off") == 0) {
+        d->use_canonical_name = USE_CANONICAL_NAME_OFF;
+    }
+    else if (strcasecmp(arg, "dns") == 0) {
+        d->use_canonical_name = USE_CANONICAL_NAME_DNS;
+    }
+    else {
+        return "parameter must be 'on', 'off', or 'dns'";
+    }
+    return NULL;
+}
+
+static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, char *arg) 
+{
+#ifdef WIN32
+    fprintf(stderr, "WARNING: StartServers has no effect on Win32\n");
+#else
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_daemons_to_start = atoi(arg);
+#endif
+    return NULL;
+}
+
+static const char *set_min_free_servers(cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_daemons_min_free = atoi(arg);
+    if (ap_daemons_min_free <= 0) {
+       fprintf(stderr, "WARNING: detected MinSpareServers set to non-positive.\n");
+       fprintf(stderr, "Resetting to 1 to avoid almost certain Apache failure.\n");
+       fprintf(stderr, "Please read the documentation.\n");
+       ap_daemons_min_free = 1;
+    }
+       
+    return NULL;
+}
+
+static const char *set_max_free_servers(cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_daemons_max_free = atoi(arg);
+    return NULL;
+}
+
+static const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_daemons_limit = atoi(arg);
+    if (ap_daemons_limit > HARD_SERVER_LIMIT) {
+       fprintf(stderr, "WARNING: MaxClients of %d exceeds compile time limit "
+           "of %d servers,\n", ap_daemons_limit, HARD_SERVER_LIMIT);
+       fprintf(stderr, " lowering MaxClients to %d.  To increase, please "
+           "see the\n", HARD_SERVER_LIMIT);
+       fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n");
+       ap_daemons_limit = HARD_SERVER_LIMIT;
+    } 
+    else if (ap_daemons_limit < 1) {
+       fprintf(stderr, "WARNING: Require MaxClients > 0, setting to 1\n");
+       ap_daemons_limit = 1;
+    }
+    return NULL;
+}
+
+static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_max_requests_per_child = atoi(arg);
+    return NULL;
+}
+
+static const char *set_threads(cmd_parms *cmd, void *dummy, char *arg) {
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_threads_per_child = atoi(arg);
+    if (ap_threads_per_child > HARD_SERVER_LIMIT) {
+        fprintf(stderr, "WARNING: ThreadsPerChild of %d exceeds compile time limit "
+                "of %d threads,\n", ap_threads_per_child, HARD_SERVER_LIMIT);
+        fprintf(stderr, " lowering ThreadsPerChild to %d.  To increase, please "
+                "see the\n", HARD_SERVER_LIMIT);
+        fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n");
+        ap_threads_per_child = HARD_SERVER_LIMIT;
+    } 
+    else if (ap_threads_per_child < 1) {
+       fprintf(stderr, "WARNING: Require ThreadsPerChild > 0, setting to 1\n");
+       ap_threads_per_child = 1;
+    }
+
+    return NULL;
+}
+
+static const char *set_excess_requests(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_excess_requests_per_child = atoi(arg);
+    return NULL;
+}
+
+
+#if defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS)
+static void set_rlimit(cmd_parms *cmd, struct rlimit **plimit, const char *arg,
+                       const char * arg2, int type)
+{
+    char *str;
+    struct rlimit *limit;
+    /* If your platform doesn't define rlim_t then typedef it in ap_config.h */
+    rlim_t cur = 0;
+    rlim_t max = 0;
+
+    *plimit = (struct rlimit *)ap_pcalloc(cmd->pool, sizeof(**plimit));
+    limit = *plimit;
+    if ((getrlimit(type, limit)) != 0) {
+       *plimit = NULL;
+       ap_log_error(APLOG_MARK, APLOG_ERR, cmd->server,
+                    "%s: getrlimit failed", cmd->cmd->name);
+       return;
+    }
+
+    if ((str = ap_getword_conf(cmd->pool, &arg))) {
+       if (!strcasecmp(str, "max")) {
+           cur = limit->rlim_max;
+       }
+       else {
+           cur = atol(str);
+       }
+    }
+    else {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server,
+                    "Invalid parameters for %s", cmd->cmd->name);
+       return;
+    }
+    
+    if (arg2 && (str = ap_getword_conf(cmd->pool, &arg2))) {
+       max = atol(str);
+    }
+
+    /* if we aren't running as root, cannot increase max */
+    if (geteuid()) {
+       limit->rlim_cur = cur;
+       if (max) {
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server,
+                        "Must be uid 0 to raise maximum %s", cmd->cmd->name);
+       }
+    }
+    else {
+        if (cur) {
+           limit->rlim_cur = cur;
+       }
+        if (max) {
+           limit->rlim_max = max;
+       }
+    }
+}
+#endif
+
+#if !defined (RLIMIT_CPU) || !(defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)) || !defined (RLIMIT_NPROC)
+static const char *no_set_limit(cmd_parms *cmd, core_dir_config *conf,
+                               char *arg, char *arg2)
+{
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server,
+               "%s not supported on this platform", cmd->cmd->name);
+    return NULL;
+}
+#endif
+
+#ifdef RLIMIT_CPU
+static const char *set_limit_cpu(cmd_parms *cmd, core_dir_config *conf, 
+                                char *arg, char *arg2)
+{
+    set_rlimit(cmd, &conf->limit_cpu, arg, arg2, RLIMIT_CPU);
+    return NULL;
+}
+#endif
+
+#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)
+static const char *set_limit_mem(cmd_parms *cmd, core_dir_config *conf, 
+                                char *arg, char * arg2)
+{
+#if defined(RLIMIT_AS)
+    set_rlimit(cmd, &conf->limit_mem, arg, arg2 ,RLIMIT_AS);
+#elif defined(RLIMIT_DATA)
+    set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_DATA);
+#elif defined(RLIMIT_VMEM)
+    set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_VMEM);
+#endif
+    return NULL;
+}
+#endif
+
+#ifdef RLIMIT_NPROC
+static const char *set_limit_nproc(cmd_parms *cmd, core_dir_config *conf,  
+                                  char *arg, char * arg2)
+{
+    set_rlimit(cmd, &conf->limit_nproc, arg, arg2, RLIMIT_NPROC);
+    return NULL;
+}
+#endif
+
+static const char *set_bind_address(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ap_bind_address.s_addr = ap_get_virthost_addr(arg, NULL);
+    return NULL;
+}
+
+static const char *set_listener(cmd_parms *cmd, void *dummy, char *ips)
+{
+    listen_rec *new;
+    char *ports;
+    unsigned short port;
+
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    ports = strchr(ips, ':');
+    if (ports != NULL) {
+       if (ports == ips) {
+           return "Missing IP address";
+       }
+       else if (ports[1] == '\0') {
+           return "Address must end in :<port-number>";
+       }
+       *(ports++) = '\0';
+    }
+    else {
+       ports = ips;
+    }
+
+    new=ap_pcalloc(cmd->pool, sizeof(listen_rec));
+    new->local_addr.sin_family = AF_INET;
+    if (ports == ips) { /* no address */
+       new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    }
+    else {
+       new->local_addr.sin_addr.s_addr = ap_get_virthost_addr(ips, NULL);
+    }
+    port = atoi(ports);
+    if (!port) {
+       return "Port must be numeric";
+    }
+    new->local_addr.sin_port = htons(port);
+    new->fd = -1;
+    new->used = 0;
+    new->next = ap_listeners;
+    ap_listeners = new;
+    return NULL;
+}
+
+static const char *set_listenbacklog(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    int b;
+
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    b = atoi(arg);
+    if (b < 1) {
+        return "ListenBacklog must be > 0";
+    }
+    ap_listenbacklog = b;
+    return NULL;
+}
+
+static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg) 
+{
+    struct stat finfo;
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    arg = ap_server_root_relative(cmd->pool, arg);
+    if ((stat(arg, &finfo) == -1) || !S_ISDIR(finfo.st_mode)) {
+       return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", arg, 
+                         " does not exist or is not a directory", NULL);
+    }
+    ap_cpystrn(ap_coredump_dir, arg, sizeof(ap_coredump_dir));
+    return NULL;
+}
+
+static const char *include_config (cmd_parms *cmd, void *dummy, char *name)
+{
+    name = ap_server_root_relative(cmd->pool, name);
+    
+    ap_process_resource_config(cmd->server, name, cmd->pool, cmd->temp_pool);
+
+    return NULL;
+}
+
+static const char *set_loglevel(cmd_parms *cmd, void *dummy, const char *arg) 
+{
+    char *str;
+    
+    const char *err = ap_check_cmd_context(cmd,
+                                          NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    if ((str = ap_getword_conf(cmd->pool, &arg))) {
+        if (!strcasecmp(str, "emerg")) {
+           cmd->server->loglevel = APLOG_EMERG;
+       }
+       else if (!strcasecmp(str, "alert")) {
+           cmd->server->loglevel = APLOG_ALERT;
+       }
+       else if (!strcasecmp(str, "crit")) {
+           cmd->server->loglevel = APLOG_CRIT;
+       }
+       else if (!strcasecmp(str, "error")) {
+           cmd->server->loglevel = APLOG_ERR;
+       }
+       else if (!strcasecmp(str, "warn")) {
+           cmd->server->loglevel = APLOG_WARNING;
+       }
+       else if (!strcasecmp(str, "notice")) {
+           cmd->server->loglevel = APLOG_NOTICE;
+       }
+       else if (!strcasecmp(str, "info")) {
+           cmd->server->loglevel = APLOG_INFO;
+       }
+       else if (!strcasecmp(str, "debug")) {
+           cmd->server->loglevel = APLOG_DEBUG;
+       }
+       else {
+            return "LogLevel requires level keyword: one of "
+                  "emerg/alert/crit/error/warn/notice/info/debug";
+       }
+    }
+    else {
+        return "LogLevel requires level keyword";
+    }
+
+    return NULL;
+}
+
+API_EXPORT(const char *) ap_psignature(const char *prefix, request_rec *r)
+{
+    char sport[20];
+    core_dir_config *conf;
+
+    conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                  &core_module);
+    if ((conf->server_signature == srv_sig_off)
+           || (conf->server_signature == srv_sig_unset)) {
+       return "";
+    }
+
+    ap_snprintf(sport, sizeof sport, "%u", (unsigned) ap_get_server_port(r));
+
+    if (conf->server_signature == srv_sig_withmail) {
+       return ap_pstrcat(r->pool, prefix, "<ADDRESS>" SERVER_BASEVERSION
+                         " Server at <A HREF=\"mailto:",
+                         r->server->server_admin, "\">",
+                         ap_get_server_name(r), "</A> Port ", sport,
+                         "</ADDRESS>\n", NULL);
+    }
+    return ap_pstrcat(r->pool, prefix, "<ADDRESS>" SERVER_BASEVERSION
+                     " Server at ", ap_get_server_name(r), " Port ", sport,
+                     "</ADDRESS>\n", NULL);
+}
+
+/*
+ * Load an authorisation realm into our location configuration, applying the
+ * usual rules that apply to realms.
+ */
+static const char *set_authname(cmd_parms *cmd, void *mconfig, char *word1)
+{
+    core_dir_config *aconfig = (core_dir_config *)mconfig;
+
+    aconfig->ap_auth_name = ap_escape_quotes(cmd->pool, word1);
+    return NULL;
+}
+
+#ifdef _OSD_POSIX /* BS2000 Logon Passwd file */
+static const char *set_bs2000_account(cmd_parms *cmd, void *dummy, char *name)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    return os_set_account(cmd->pool, name);
+}
+#endif /*_OSD_POSIX*/
+
+/*
+ * Handle a request to include the server's OS platform in the Server
+ * response header field (the ServerTokens directive).  Unfortunately
+ * this requires a new global in order to communicate the setting back to
+ * http_main so it can insert the information in the right place in the
+ * string.
+ */
+
+static const char *set_serv_tokens(cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (!strcasecmp(arg, "OS")) {
+        ap_server_tokens = SrvTk_OS;
+    }
+    else if (!strcasecmp(arg, "Min") || !strcasecmp(arg, "Minimal")) {
+        ap_server_tokens = SrvTk_MIN;
+    }
+    else {
+        ap_server_tokens = SrvTk_FULL;
+    }
+    return NULL;
+}
+
+static const char *set_limit_req_line(cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd,
+                                           NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    int lim;
+
+    if (err != NULL) {
+        return err;
+    }
+    lim = atoi(arg);
+    if (lim < 0) {
+        return ap_pstrcat(cmd->temp_pool, "LimitRequestLine \"", arg, 
+                          "\" must be a non-negative integer", NULL);
+    }
+    if (lim > DEFAULT_LIMIT_REQUEST_LINE) {
+        return ap_psprintf(cmd->temp_pool, "LimitRequestLine \"%s\" "
+                           "must not exceed the precompiled maximum of %d",
+                           arg, DEFAULT_LIMIT_REQUEST_LINE);
+    }
+    cmd->server->limit_req_line = lim;
+    return NULL;
+}
+
+static const char *set_limit_req_fieldsize(cmd_parms *cmd, void *dummy,
+                                           char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd,
+                                           NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    int lim;
+
+    if (err != NULL) {
+        return err;
+    }
+    lim = atoi(arg);
+    if (lim < 0) {
+        return ap_pstrcat(cmd->temp_pool, "LimitRequestFieldsize \"", arg, 
+                          "\" must be a non-negative integer (0 = no limit)",
+                          NULL);
+    }
+    if (lim > DEFAULT_LIMIT_REQUEST_FIELDSIZE) {
+        return ap_psprintf(cmd->temp_pool, "LimitRequestFieldsize \"%s\" "
+                          "must not exceed the precompiled maximum of %d",
+                           arg, DEFAULT_LIMIT_REQUEST_FIELDSIZE);
+    }
+    cmd->server->limit_req_fieldsize = lim;
+    return NULL;
+}
+
+static const char *set_limit_req_fields(cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd,
+                                           NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    int lim;
+
+    if (err != NULL) {
+        return err;
+    }
+    lim = atoi(arg);
+    if (lim < 0) {
+        return ap_pstrcat(cmd->temp_pool, "LimitRequestFields \"", arg, 
+                          "\" must be a non-negative integer (0 = no limit)",
+                          NULL);
+    }
+    cmd->server->limit_req_fields = lim;
+    return NULL;
+}
+
+static const char *set_limit_req_body(cmd_parms *cmd, core_dir_config *conf,
+                                      char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    /* WTF: If strtoul is not portable, then write a replacement.
+     *      Instead we have an idiotic define in httpd.h that prevents
+     *      it from being used even when it is available. Sheesh.
+     */
+    conf->limit_req_body = (unsigned long)strtol(arg, (char **)NULL, 10);
+    return NULL;
+}
+
+#ifdef WIN32
+static const char *set_interpreter_source(cmd_parms *cmd, core_dir_config *d,
+                                                char *arg)
+{
+    if (!strcasecmp(arg, "registry")) {
+        d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY;
+    } else if (!strcasecmp(arg, "script")) {
+        d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
+    } else {
+        d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
+    }
+    return NULL;
+}
+#endif
+
+/* Note --- ErrorDocument will now work from .htaccess files.  
+ * The AllowOverride of Fileinfo allows webmasters to turn it off
+ */
+
+static const command_rec core_cmds[] = {
+
+/* Old access config file commands */
+
+{ "<Directory", dirsection, NULL, RSRC_CONF, RAW_ARGS,
+  "Container for directives affecting resources located in the specified "
+  "directories" },
+{ end_directory_section, end_nested_section, NULL, ACCESS_CONF, NO_ARGS,
+  "Marks end of <Directory>" },
+{ "<Location", urlsection, NULL, RSRC_CONF, RAW_ARGS,
+  "Container for directives affecting resources accessed through the "
+  "specified URL paths" },
+{ end_location_section, end_nested_section, NULL, ACCESS_CONF, NO_ARGS,
+  "Marks end of <Location>" },
+{ "<VirtualHost", virtualhost_section, NULL, RSRC_CONF, RAW_ARGS,
+  "Container to map directives to a particular virtual host, takes one or "
+  "more host addresses" },
+{ end_virtualhost_section, end_nested_section, NULL, RSRC_CONF, NO_ARGS,
+  "Marks end of <VirtualHost>" },
+{ "<Files", filesection, NULL, OR_ALL, RAW_ARGS, "Container for directives "
+  "affecting files matching specified patterns" },
+{ end_files_section, end_nested_section, NULL, OR_ALL, NO_ARGS,
+  "Marks end of <Files>" },
+{ "<Limit", ap_limit_section, NULL, OR_ALL, RAW_ARGS, "Container for "
+  "authentication directives when accessed using specified HTTP methods" },
+{ "</Limit>", endlimit_section, NULL, OR_ALL, NO_ARGS,
+  "Marks end of <Limit>" },
+{ "<LimitExcept", ap_limit_section, (void*)1, OR_ALL, RAW_ARGS,
+  "Container for authentication directives to be applied when any HTTP "
+  "method other than those specified is used to access the resource" },
+{ "</LimitExcept>", endlimit_section, (void*)1, OR_ALL, NO_ARGS,
+  "Marks end of <LimitExcept>" },
+{ "<IfModule", start_ifmod, NULL, OR_ALL, TAKE1,
+  "Container for directives based on existance of specified modules" },
+{ end_ifmodule_section, end_ifmod, NULL, OR_ALL, NO_ARGS,
+  "Marks end of <IfModule>" },
+{ "<IfDefine", start_ifdefine, NULL, OR_ALL, TAKE1,
+  "Container for directives based on existance of command line defines" },
+{ end_ifdefine_section, end_ifdefine, NULL, OR_ALL, NO_ARGS,
+  "Marks end of <IfDefine>" },
+{ "<DirectoryMatch", dirsection, (void*)1, RSRC_CONF, RAW_ARGS,
+  "Container for directives affecting resources located in the "
+  "specified directories" },
+{ end_directorymatch_section, end_nested_section, NULL, ACCESS_CONF, NO_ARGS,
+  "Marks end of <DirectoryMatch>" },
+{ "<LocationMatch", urlsection, (void*)1, RSRC_CONF, RAW_ARGS,
+  "Container for directives affecting resources accessed through the "
+  "specified URL paths" },
+{ end_locationmatch_section, end_nested_section, NULL, ACCESS_CONF, NO_ARGS,
+  "Marks end of <LocationMatch>" },
+{ "<FilesMatch", filesection, (void*)1, OR_ALL, RAW_ARGS,
+  "Container for directives affecting files matching specified patterns" },
+{ end_filesmatch_section, end_nested_section, NULL, OR_ALL, NO_ARGS,
+  "Marks end of <FilesMatch>" },
+{ "AuthType", ap_set_string_slot,
+  (void*)XtOffsetOf(core_dir_config, ap_auth_type), OR_AUTHCFG, TAKE1,
+  "An HTTP authorization type (e.g., \"Basic\")" },
+{ "AuthName", set_authname, NULL, OR_AUTHCFG, TAKE1,
+  "The authentication realm (e.g. \"Members Only\")" },
+{ "Require", require, NULL, OR_AUTHCFG, RAW_ARGS,
+  "Selects which authenticated users or groups may access a protected space" },
+{ "Satisfy", satisfy, NULL, OR_AUTHCFG, TAKE1,
+  "access policy if both allow and require used ('all' or 'any')" },    
+#ifdef GPROF
+{ "GprofDir", set_gprof_dir, NULL, RSRC_CONF, TAKE1,
+  "Directory to plop gmon.out files" },
+#endif
+
+/* Old resource config file commands */
+  
+{ "AccessFileName", set_access_name, NULL, RSRC_CONF, RAW_ARGS,
+  "Name(s) of per-directory config files (default: .htaccess)" },
+{ "DocumentRoot", set_document_root, NULL, RSRC_CONF, TAKE1,
+  "Root directory of the document tree"  },
+{ "ErrorDocument", set_error_document, NULL, OR_FILEINFO, RAW_ARGS,
+  "Change responses for HTTP errors" },
+{ "AllowOverride", set_override, NULL, ACCESS_CONF, RAW_ARGS,
+  "Controls what groups of directives can be configured by per-directory "
+  "config files" },
+{ "Options", set_options, NULL, OR_OPTIONS, RAW_ARGS,
+  "Set a number of attributes for a given directory" },
+{ "DefaultType", ap_set_string_slot,
+  (void*)XtOffsetOf (core_dir_config, ap_default_type),
+  OR_FILEINFO, TAKE1, "the default MIME type for untypable files" },
+
+/* Old server config file commands */
+
+{ "ServerType", server_type, NULL, RSRC_CONF, TAKE1,
+  "'inetd' or 'standalone'"},
+{ "Port", server_port, NULL, RSRC_CONF, TAKE1, "A TCP port number"},
+{ "HostnameLookups", set_hostname_lookups, NULL, ACCESS_CONF|RSRC_CONF, TAKE1,
+  "\"on\" to enable, \"off\" to disable reverse DNS lookups, or \"double\" to "
+  "enable double-reverse DNS lookups" },
+{ "User", set_user, NULL, RSRC_CONF, TAKE1,
+  "Effective user id for this server"},
+{ "Group", set_group, NULL, RSRC_CONF, TAKE1,
+  "Effective group id for this server"},
+{ "ServerAdmin", set_server_string_slot,
+  (void *)XtOffsetOf (server_rec, server_admin), RSRC_CONF, TAKE1,
+  "The email address of the server administrator" },
+{ "ServerName", set_server_string_slot,
+  (void *)XtOffsetOf (server_rec, server_hostname), RSRC_CONF, TAKE1,
+  "The hostname of the server" },
+{ "ServerSignature", set_signature_flag, NULL, OR_ALL, TAKE1,
+  "En-/disable server signature (on|off|email)" },
+{ "ServerRoot", set_server_root, NULL, RSRC_CONF, TAKE1,
+  "Common directory of server-related files (logs, confs, etc.)" },
+{ "ErrorLog", set_server_string_slot,
+  (void *)XtOffsetOf (server_rec, error_fname), RSRC_CONF, TAKE1,
+  "The filename of the error log" },
+{ "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1,
+    "A file for logging the server process ID"},
+{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1,
+    "A file for Apache to maintain runtime process management information"},
+{ "LockFile", set_lockfile, NULL, RSRC_CONF, TAKE1,
+    "The lockfile used when Apache needs to lock the accept() call"},
+{ "AccessConfig", set_server_string_slot,
+  (void *)XtOffsetOf (server_rec, access_confname), RSRC_CONF, TAKE1,
+  "The filename of the access config file" },
+{ "ResourceConfig", set_server_string_slot,
+  (void *)XtOffsetOf (server_rec, srm_confname), RSRC_CONF, TAKE1,
+  "The filename of the resource config file" },
+{ "ServerAlias", set_server_alias, NULL, RSRC_CONF, RAW_ARGS,
+  "A name or names alternately used to access the server" },
+{ "ServerPath", set_serverpath, NULL, RSRC_CONF, TAKE1,
+  "The pathname the server can be reached at" },
+{ "Timeout", set_timeout, NULL, RSRC_CONF, TAKE1, "Timeout duration (sec)" },
+{ "KeepAliveTimeout", set_keep_alive_timeout, NULL, RSRC_CONF, TAKE1,
+  "Keep-Alive timeout duration (sec)"},
+{ "MaxKeepAliveRequests", set_keep_alive_max, NULL, RSRC_CONF, TAKE1,
+  "Maximum number of Keep-Alive requests per connection, or 0 for infinite" },
+{ "KeepAlive", set_keep_alive, NULL, RSRC_CONF, TAKE1,
+  "Whether persistent connections should be On or Off" },
+{ "IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF, FLAG,
+  "Enable identd (RFC 1413) user lookups - SLOW" },
+{ "ContentDigest", set_content_md5, NULL, OR_OPTIONS,
+  FLAG, "whether or not to send a Content-MD5 header with each request" },
+{ "UseCanonicalName", set_use_canonical_name, NULL,
+  RSRC_CONF, TAKE1,
+  "How to work out the ServerName : Port when constructing URLs" },
+{ "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1,
+  "Number of child processes launched at server startup" },
+{ "MinSpareServers", set_min_free_servers, NULL, RSRC_CONF, TAKE1,
+  "Minimum number of idle children, to handle request spikes" },
+{ "MaxSpareServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1,
+  "Maximum number of idle children" },
+{ "MaxServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1,
+  "Deprecated equivalent to MaxSpareServers" },
+{ "ServersSafetyLimit", set_server_limit, NULL, RSRC_CONF, TAKE1,
+  "Deprecated equivalent to MaxClients" },
+{ "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1,
+  "Maximum number of children alive at the same time" },
+{ "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1,
+  "Maximum number of requests a particular child serves before dying." },
+{ "RLimitCPU",
+#ifdef RLIMIT_CPU
+  set_limit_cpu, (void*)XtOffsetOf(core_dir_config, limit_cpu),
+#else
+  no_set_limit, NULL,
+#endif
+  OR_ALL, TAKE12, "Soft/hard limits for max CPU usage in seconds" },
+{ "RLimitMEM",
+#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined (RLIMIT_AS)
+  set_limit_mem, (void*)XtOffsetOf(core_dir_config, limit_mem),
+#else
+  no_set_limit, NULL,
+#endif
+  OR_ALL, TAKE12, "Soft/hard limits for max memory usage per process" },
+{ "RLimitNPROC",
+#ifdef RLIMIT_NPROC
+  set_limit_nproc, (void*)XtOffsetOf(core_dir_config, limit_nproc),
+#else
+  no_set_limit, NULL,
+#endif
+   OR_ALL, TAKE12, "soft/hard limits for max number of processes per uid" },
+{ "BindAddress", set_bind_address, NULL, RSRC_CONF, TAKE1,
+  "'*', a numeric IP address, or the name of a host with a unique IP address"},
+{ "Listen", set_listener, NULL, RSRC_CONF, TAKE1,
+  "A port number or a numeric IP address and a port number"},
+{ "SendBufferSize", set_send_buffer_size, NULL, RSRC_CONF, TAKE1,
+  "Send buffer size in bytes"},
+{ "AddModule", add_module_command, NULL, RSRC_CONF, ITERATE,
+  "The name of a module" },
+{ "ClearModuleList", clear_module_list_command, NULL, RSRC_CONF, NO_ARGS, 
+  NULL },
+{ "ThreadsPerChild", set_threads, NULL, RSRC_CONF, TAKE1,
+  "Number of threads a child creates" },
+{ "ExcessRequestsPerChild", set_excess_requests, NULL, RSRC_CONF, TAKE1,
+  "Maximum number of requests a particular child serves after it is ready "
+  "to die." },
+{ "ListenBacklog", set_listenbacklog, NULL, RSRC_CONF, TAKE1,
+  "Maximum length of the queue of pending connections, as used by listen(2)" },
+{ "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1,
+  "The location of the directory Apache changes to before dumping core" },
+{ "Include", include_config, NULL, (RSRC_CONF | ACCESS_CONF), TAKE1,
+  "Name of the config file to be included" },
+{ "LogLevel", set_loglevel, NULL, RSRC_CONF, TAKE1,
+  "Level of verbosity in error logging" },
+{ "NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF, TAKE1,
+  "A numeric IP address:port, or the name of a host" },
+#ifdef _OSD_POSIX
+{ "BS2000Account", set_bs2000_account, NULL, RSRC_CONF, TAKE1,
+  "Name of server User's bs2000 logon account name" },
+#endif
+#ifdef WIN32
+{ "ScriptInterpreterSource", set_interpreter_source, NULL, OR_FILEINFO, TAKE1,
+  "Where to find interpreter to run Win32 scripts (Registry or script shebang line)" },
+#endif
+{ "ServerTokens", set_serv_tokens, NULL, RSRC_CONF, TAKE1,
+  "Determine tokens displayed in the Server: header - Min(imal), OS or Full" },
+{ "LimitRequestLine", set_limit_req_line, NULL, RSRC_CONF, TAKE1,
+  "Limit on maximum size of an HTTP request line"},
+{ "LimitRequestFieldsize", set_limit_req_fieldsize, NULL, RSRC_CONF, TAKE1,
+  "Limit on maximum size of an HTTP request header field"},
+{ "LimitRequestFields", set_limit_req_fields, NULL, RSRC_CONF, TAKE1,
+  "Limit (0 = unlimited) on max number of header fields in a request message"},
+{ "LimitRequestBody", set_limit_req_body,
+  (void*)XtOffsetOf(core_dir_config, limit_req_body),
+  OR_ALL, TAKE1,
+  "Limit (in bytes) on maximum size of request message body" },
+{ NULL }
+};
+
+/*****************************************************************
+ *
+ * Core handlers for various phases of server operation...
+ */
+
+static int core_translate(request_rec *r)
+{
+    void *sconf = r->server->module_config;
+    core_server_config *conf = ap_get_module_config(sconf, &core_module);
+  
+    if (r->proxyreq) {
+        return HTTP_FORBIDDEN;
+    }
+    if ((r->uri[0] != '/') && strcmp(r->uri, "*")) {
+       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                    "Invalid URI in request %s", r->the_request);
+       return BAD_REQUEST;
+    }
+    
+    if (r->server->path 
+       && !strncmp(r->uri, r->server->path, r->server->pathlen)
+       && (r->server->path[r->server->pathlen - 1] == '/'
+           || r->uri[r->server->pathlen] == '/'
+           || r->uri[r->server->pathlen] == '\0')) {
+        r->filename = ap_pstrcat(r->pool, conf->ap_document_root,
+                                (r->uri + r->server->pathlen), NULL);
+    }
+    else {
+       /*
+         * Make sure that we do not mess up the translation by adding two
+         * /'s in a row.  This happens under windows when the document
+         * root ends with a /
+         */
+        if ((conf->ap_document_root[strlen(conf->ap_document_root)-1] == '/')
+           && (*(r->uri) == '/')) {
+           r->filename = ap_pstrcat(r->pool, conf->ap_document_root, r->uri+1,
+                                    NULL);
+       }
+       else {
+           r->filename = ap_pstrcat(r->pool, conf->ap_document_root, r->uri,
+                                    NULL);
+       }
+    }
+
+    return OK;
+}
+
+static int do_nothing(request_rec *r) { return OK; }
+
+#ifdef USE_MMAP_FILES
+struct mmap_rec {
+    void *mm;
+    size_t length;
+};
+
+static void mmap_cleanup(void *mmv)
+{
+    struct mmap_rec *mmd = mmv;
+
+    if (munmap(mmd->mm, mmd->length) == -1) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, NULL,
+                     "Failed to munmap memory of length %ld at 0x%lx",
+                     (long) mmd->length, (long) mmd->mm);
+    }
+}
+#endif
+
+/*
+ * Default handler for MIME types without other handlers.  Only GET
+ * and OPTIONS at this point... anyone who wants to write a generic
+ * handler for PUT or POST is free to do so, but it seems unwise to provide
+ * any defaults yet... So, for now, we assume that this will always be
+ * the last handler called and return 405 or 501.
+ */
+
+static int default_handler(request_rec *r)
+{
+    core_dir_config *d =
+      (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+    int rangestatus, errstatus;
+    FILE *f;
+#ifdef USE_MMAP_FILES
+    caddr_t mm;
+#endif
+#ifdef CHARSET_EBCDIC
+    int convert_flag;
+#endif
+
+    /* This handler has no use for a request body (yet), but we still
+     * need to read and discard it if the client sent one.
+     */
+    if ((errstatus = ap_discard_request_body(r)) != OK) {
+        return errstatus;
+    }
+
+    r->allowed |= (1 << M_GET) | (1 << M_OPTIONS);
+
+    if (r->method_number == M_INVALID) {
+       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                   "Invalid method in request %s", r->the_request);
+       return NOT_IMPLEMENTED;
+    }
+    if (r->method_number == M_OPTIONS) {
+        return ap_send_http_options(r);
+    }
+    if (r->method_number == M_PUT) {
+        return METHOD_NOT_ALLOWED;
+    }
+
+    if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) {
+       char *emsg;
+
+       emsg = "File does not exist: ";
+       if (r->path_info == NULL) {
+           emsg = ap_pstrcat(r->pool, emsg, r->filename, NULL);
+       }
+       else {
+           emsg = ap_pstrcat(r->pool, emsg, r->filename, r->path_info, NULL);
+       }
+       ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, "%s", emsg);
+       return HTTP_NOT_FOUND;
+    }
+    if (r->method_number != M_GET) {
+        return METHOD_NOT_ALLOWED;
+    }
+       
+#if defined(OS2) || defined(WIN32)
+    /* Need binary mode for OS/2 */
+    f = ap_pfopen(r->pool, r->filename, "rb");
+#else
+    f = ap_pfopen(r->pool, r->filename, "r");
+#endif
+
+    if (f == NULL) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                    "file permissions deny server access: %s", r->filename);
+        return FORBIDDEN;
+    }
+       
+    ap_update_mtime(r, r->finfo.st_mtime);
+    ap_set_last_modified(r);
+    ap_set_etag(r);
+    ap_table_setn(r->headers_out, "Accept-Ranges", "bytes");
+    if (((errstatus = ap_meets_conditions(r)) != OK)
+       || (errstatus = ap_set_content_length(r, r->finfo.st_size))) {
+        return errstatus;
+    }
+
+#ifdef USE_MMAP_FILES
+    ap_block_alarms();
+    if ((r->finfo.st_size >= MMAP_THRESHOLD)
+       && (r->finfo.st_size < MMAP_LIMIT)
+       && (!r->header_only || (d->content_md5 & 1))) {
+       /* we need to protect ourselves in case we die while we've got the
+        * file mmapped */
+       mm = mmap(NULL, r->finfo.st_size, PROT_READ, MAP_PRIVATE,
+                 fileno(f), 0);
+       if (mm == (caddr_t)-1) {
+           ap_log_rerror(APLOG_MARK, APLOG_CRIT, r,
+                        "default_handler: mmap failed: %s", r->filename);
+       }
+    }
+    else {
+       mm = (caddr_t)-1;
+    }
+
+    if (mm == (caddr_t)-1) {
+       ap_unblock_alarms();
+#endif
+
+#ifdef CHARSET_EBCDIC
+       /* To make serving of "raw ASCII text" files easy (they serve faster
+        * since they don't have to be converted from EBCDIC), a new
+        * "magic" type prefix was invented: text/x-ascii-{plain,html,...}
+        * If we detect one of these content types here, we simply correct
+        * the type to the real text/{plain,html,...} type. Otherwise, we
+        * set a flag that translation is required later on.
+        */
+       convert_flag = ap_checkconv(r);
+       if (d->content_md5 & 1) {
+           ap_table_setn(r->headers_out, "Content-MD5",
+                         ap_md5digest(r->pool, f, convert_flag));
+       }
+#else
+       if (d->content_md5 & 1) {
+           ap_table_setn(r->headers_out, "Content-MD5",
+                         ap_md5digest(r->pool, f));
+       }
+#endif /* CHARSET_EBCDIC */
+
+       rangestatus = ap_set_byterange(r);
+
+       ap_send_http_header(r);
+       
+       if (!r->header_only) {
+           if (!rangestatus) {
+               ap_send_fd(f, r);
+           }
+           else {
+               long offset, length;
+               while (ap_each_byterange(r, &offset, &length)) {
+                   /*
+                    * Non zero returns are more portable than checking
+                    * for a return of -1.
+                    */
+                   if (fseek(f, offset, SEEK_SET)) {
+                       ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                             "Failed to fseek for byterange (%ld, %ld)",
+                             offset, length);
+                   }
+                   else {
+                       ap_send_fd_length(f, r, length);
+                   }
+               }
+           }
+       }
+
+#ifdef USE_MMAP_FILES
+    }
+    else {
+       struct mmap_rec *mmd;
+
+       mmd = ap_palloc(r->pool, sizeof(*mmd));
+       mmd->mm = mm;
+       mmd->length = r->finfo.st_size;
+       ap_register_cleanup(r->pool, (void *)mmd, mmap_cleanup, mmap_cleanup);
+       ap_unblock_alarms();
+
+       if (d->content_md5 & 1) {
+           AP_MD5_CTX context;
+           
+           ap_MD5Init(&context);
+           ap_MD5Update(&context, (void *)mm, (unsigned int)r->finfo.st_size);
+           ap_table_setn(r->headers_out, "Content-MD5",
+                         ap_md5contextTo64(r->pool, &context));
+       }
+
+       rangestatus = ap_set_byterange(r);
+       ap_send_http_header(r);
+       
+       if (!r->header_only) {
+           if (!rangestatus) {
+               ap_send_mmap(mm, r, 0, r->finfo.st_size);
+           }
+           else {
+               long offset, length;
+               while (ap_each_byterange(r, &offset, &length)) {
+                   ap_send_mmap(mm, r, offset, length);
+               }
+           }
+       }
+    }
+#endif
+
+    ap_pfclose(r->pool, f);
+    return OK;
+}
+
+static const handler_rec core_handlers[] = {
+{ "*/*", default_handler },
+{ "default-handler", default_handler },
+{ NULL, NULL }
+};
+
+API_VAR_EXPORT module core_module = {
+    STANDARD_MODULE_STUFF,
+    NULL,                      /* initializer */
+    create_core_dir_config,    /* create per-directory config structure */
+    merge_core_dir_configs,    /* merge per-directory config structures */
+    create_core_server_config, /* create per-server config structure */
+    merge_core_server_configs, /* merge per-server config structures */
+    core_cmds,                 /* command table */
+    core_handlers,             /* handlers */
+    core_translate,            /* translate_handler */
+    NULL,                      /* check_user_id */
+    NULL,                      /* check auth */
+    do_nothing,                        /* check access */
+    do_nothing,                        /* type_checker */
+    NULL,                      /* pre-run fixups */
+    NULL,                      /* logger */
+    NULL,                      /* header parser */
+    NULL,                      /* child_init */
+    NULL,                      /* child_exit */
+    NULL                       /* post_read_request */
+};
diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c
new file mode 100644 (file)
index 0000000..3177931
--- /dev/null
@@ -0,0 +1,2763 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_protocol.c --- routines which directly communicate with the client.
+ *
+ * Code originally by Rob McCool; much redone by Robert S. Thau
+ * and the Apache Group.
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_request.h"
+#include "http_vhost.h"
+#include "http_log.h"           /* For errors detected in basic auth common
+                                 * support code... */
+#include "util_date.h"          /* For parseHTTPdate and BAD_DATE */
+#include <stdarg.h>
+#include "http_conf_globals.h"
+
+#define SET_BYTES_SENT(r) \
+  do { if (r->sent_bodyct) \
+          ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
+  } while (0)
+
+
+static int parse_byterange(char *range, long clength, long *start, long *end)
+{
+    char *dash = strchr(range, '-');
+
+    if (!dash)
+        return 0;
+
+    if ((dash == range)) {
+        /* In the form "-5" */
+        *start = clength - atol(dash + 1);
+        *end = clength - 1;
+    }
+    else {
+        *dash = '\0';
+        dash++;
+        *start = atol(range);
+        if (*dash)
+            *end = atol(dash);
+        else                    /* "5-" */
+            *end = clength - 1;
+    }
+
+    if (*start < 0)
+       *start = 0;
+
+    if (*end >= clength)
+        *end = clength - 1;
+
+    if (*start > *end)
+       return 0;
+
+    return (*start > 0 || *end < clength - 1);
+}
+
+static int internal_byterange(int, long *, request_rec *, const char **, long *,
+                              long *);
+
+API_EXPORT(int) ap_set_byterange(request_rec *r)
+{
+    const char *range, *if_range, *match;
+    long range_start, range_end;
+
+    if (!r->clength || r->assbackwards)
+        return 0;
+
+    /* Check for Range request-header (HTTP/1.1) or Request-Range for
+     * backwards-compatibility with second-draft Luotonen/Franks
+     * byte-ranges (e.g. Netscape Navigator 2-3).
+     *
+     * We support this form, with Request-Range, and (farther down) we
+     * send multipart/x-byteranges instead of multipart/byteranges for
+     * Request-Range based requests to work around a bug in Netscape
+     * Navigator 2-3 and MSIE 3.
+     */
+
+    if (!(range = ap_table_get(r->headers_in, "Range")))
+        range = ap_table_get(r->headers_in, "Request-Range");
+
+    if (!range || strncasecmp(range, "bytes=", 6)) {
+        return 0;
+    }
+
+    /* Check the If-Range header for Etag or Date.
+     * Note that this check will return false (as required) if either
+     * of the two etags are weak.
+     */
+    if ((if_range = ap_table_get(r->headers_in, "If-Range"))) {
+        if (if_range[0] == '"') {
+            if (!(match = ap_table_get(r->headers_out, "Etag")) ||
+                (strcmp(if_range, match) != 0))
+                return 0;
+        }
+        else if (!(match = ap_table_get(r->headers_out, "Last-Modified")) ||
+                 (strcmp(if_range, match) != 0))
+            return 0;
+    }
+
+    if (!strchr(range, ',')) {
+        /* A single range */
+        if (!parse_byterange(ap_pstrdup(r->pool, range + 6), r->clength,
+                             &range_start, &range_end))
+            return 0;
+
+        r->byterange = 1;
+
+        ap_table_setn(r->headers_out, "Content-Range",
+           ap_psprintf(r->pool, "bytes %ld-%ld/%ld",
+               range_start, range_end, r->clength));
+        ap_table_setn(r->headers_out, "Content-Length",
+           ap_psprintf(r->pool, "%ld", range_end - range_start + 1));
+    }
+    else {
+        /* a multiple range */
+        const char *r_range = ap_pstrdup(r->pool, range + 6);
+        long tlength = 0;
+
+        r->byterange = 2;
+        r->boundary = ap_psprintf(r->pool, "%lx%lx",
+                               r->request_time, (long) getpid());
+        while (internal_byterange(0, &tlength, r, &r_range, NULL, NULL));
+        ap_table_setn(r->headers_out, "Content-Length",
+           ap_psprintf(r->pool, "%ld", tlength));
+    }
+
+    r->status = PARTIAL_CONTENT;
+    r->range = range + 6;
+
+    return 1;
+}
+
+API_EXPORT(int) ap_each_byterange(request_rec *r, long *offset, long *length)
+{
+    return internal_byterange(1, NULL, r, &r->range, offset, length);
+}
+
+/* If this function is called with realreq=1, it will spit out
+ * the correct headers for a byterange chunk, and set offset and
+ * length to the positions they should be.
+ *
+ * If it is called with realreq=0, it will add to tlength the length
+ * it *would* have used with realreq=1.
+ *
+ * Either case will return 1 if it should be called again, and 0
+ * when done.
+ */
+static int internal_byterange(int realreq, long *tlength, request_rec *r,
+                              const char **r_range, long *offset, long *length)
+{
+    long range_start, range_end;
+    char *range;
+
+    if (!**r_range) {
+        if (r->byterange > 1) {
+            if (realreq)
+                ap_rvputs(r, "\015\012--", r->boundary, "--\015\012", NULL);
+            else
+                *tlength += 4 + strlen(r->boundary) + 4;
+        }
+        return 0;
+    }
+
+    range = ap_getword(r->pool, r_range, ',');
+    if (!parse_byterange(range, r->clength, &range_start, &range_end))
+        /* Skip this one */
+        return internal_byterange(realreq, tlength, r, r_range, offset,
+                                  length);
+
+    if (r->byterange > 1) {
+        const char *ct = r->content_type ? r->content_type : ap_default_type(r);
+        char ts[MAX_STRING_LEN];
+
+        ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end,
+                    r->clength);
+        if (realreq)
+            ap_rvputs(r, "\015\012--", r->boundary, "\015\012Content-type: ",
+                   ct, "\015\012Content-range: bytes ", ts, "\015\012\015\012",
+                   NULL);
+        else
+            *tlength += 4 + strlen(r->boundary) + 16 + strlen(ct) + 23 +
+                        strlen(ts) + 4;
+    }
+
+    if (realreq) {
+        *offset = range_start;
+        *length = range_end - range_start + 1;
+    }
+    else {
+        *tlength += range_end - range_start + 1;
+    }
+    return 1;
+}
+
+API_EXPORT(int) ap_set_content_length(request_rec *r, long clength)
+{
+    r->clength = clength;
+    ap_table_setn(r->headers_out, "Content-Length", ap_psprintf(r->pool, "%ld", clength));
+    return 0;
+}
+
+API_EXPORT(int) ap_set_keepalive(request_rec *r)
+{
+    int ka_sent = 0;
+    int wimpy = ap_find_token(r->pool,
+                           ap_table_get(r->headers_out, "Connection"), "close");
+    const char *conn = ap_table_get(r->headers_in, "Connection");
+
+    /* The following convoluted conditional determines whether or not
+     * the current connection should remain persistent after this response
+     * (a.k.a. HTTP Keep-Alive) and whether or not the output message
+     * body should use the HTTP/1.1 chunked transfer-coding.  In English,
+     *
+     *   IF  we have not marked this connection as errored;
+     *   and the response body has a defined length due to the status code
+     *       being 304 or 204, the request method being HEAD, already
+     *       having defined Content-Length or Transfer-Encoding: chunked, or
+     *       the request version being HTTP/1.1 and thus capable of being set
+     *       as chunked [we know the (r->chunked = 1) side-effect is ugly];
+     *   and the server configuration enables keep-alive;
+     *   and the server configuration has a reasonable inter-request timeout;
+     *   and there is no maximum # requests or the max hasn't been reached;
+     *   and the response status does not require a close;
+     *   and the response generator has not already indicated close;
+     *   and the client did not request non-persistence (Connection: close);
+     *   and    we haven't been configured to ignore the buggy twit
+     *       or they're a buggy twit coming through a HTTP/1.1 proxy
+     *   and    the client is requesting an HTTP/1.0-style keep-alive
+     *       or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
+     *   THEN we can be persistent, which requires more headers be output.
+     *
+     * Note that the condition evaluation order is extremely important.
+     */
+    if ((r->connection->keepalive != -1) &&
+        ((r->status == HTTP_NOT_MODIFIED) ||
+         (r->status == HTTP_NO_CONTENT) ||
+         r->header_only ||
+         ap_table_get(r->headers_out, "Content-Length") ||
+         ap_find_last_token(r->pool,
+                         ap_table_get(r->headers_out, "Transfer-Encoding"),
+                         "chunked") ||
+         ((r->proto_num >= HTTP_VERSION(1,1)) &&
+         (r->chunked = 1))) && /* THIS CODE IS CORRECT, see comment above. */
+        r->server->keep_alive &&
+        (r->server->keep_alive_timeout > 0) &&
+        ((r->server->keep_alive_max == 0) ||
+         (r->server->keep_alive_max > r->connection->keepalives)) &&
+        !ap_status_drops_connection(r->status) &&
+        !wimpy &&
+        !ap_find_token(r->pool, conn, "close") &&
+        (!ap_table_get(r->subprocess_env, "nokeepalive") ||
+         ap_table_get(r->headers_in, "Via")) &&
+        ((ka_sent = ap_find_token(r->pool, conn, "keep-alive")) ||
+         (r->proto_num >= HTTP_VERSION(1,1)))
+       ) {
+        int left = r->server->keep_alive_max - r->connection->keepalives;
+
+        r->connection->keepalive = 1;
+        r->connection->keepalives++;
+
+        /* If they sent a Keep-Alive token, send one back */
+        if (ka_sent) {
+            if (r->server->keep_alive_max)
+               ap_table_setn(r->headers_out, "Keep-Alive",
+                   ap_psprintf(r->pool, "timeout=%d, max=%d",
+                            r->server->keep_alive_timeout, left));
+            else
+               ap_table_setn(r->headers_out, "Keep-Alive",
+                   ap_psprintf(r->pool, "timeout=%d",
+                            r->server->keep_alive_timeout));
+            ap_table_mergen(r->headers_out, "Connection", "Keep-Alive");
+        }
+
+        return 1;
+    }
+
+    /* Otherwise, we need to indicate that we will be closing this
+     * connection immediately after the current response.
+     *
+     * We only really need to send "close" to HTTP/1.1 clients, but we
+     * always send it anyway, because a broken proxy may identify itself
+     * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
+     * to a HTTP/1.1 client. Better safe than sorry.
+     */
+    if (!wimpy)
+       ap_table_mergen(r->headers_out, "Connection", "close");
+
+    r->connection->keepalive = 0;
+
+    return 0;
+}
+
+/*
+ * Return the latest rational time from a request/mtime (modification time)
+ * pair.  We return the mtime unless it's in the future, in which case we
+ * return the current time.  We use the request time as a reference in order
+ * to limit the number of calls to time().  We don't check for futurosity
+ * unless the mtime is at least as new as the reference.
+ */
+API_EXPORT(time_t) ap_rationalize_mtime(request_rec *r, time_t mtime)
+{
+    time_t now;
+
+    /* For all static responses, it's almost certain that the file was
+     * last modified before the beginning of the request.  So there's
+     * no reason to call time(NULL) again.  But if the response has been
+     * created on demand, then it might be newer than the time the request
+     * started.  In this event we really have to call time(NULL) again
+     * so that we can give the clients the most accurate Last-Modified.  If we
+     * were given a time in the future, we return the current time - the
+     * Last-Modified can't be in the future.
+     */
+    now = (mtime < r->request_time) ? r->request_time : time(NULL);
+    return (mtime > now) ? now : mtime;
+}
+
+API_EXPORT(int) ap_meets_conditions(request_rec *r)
+{
+    const char *etag = ap_table_get(r->headers_out, "ETag");
+    const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
+    time_t mtime;
+
+    /* Check for conditional requests --- note that we only want to do
+     * this if we are successful so far and we are not processing a
+     * subrequest or an ErrorDocument.
+     *
+     * The order of the checks is important, since ETag checks are supposed
+     * to be more accurate than checks relative to the modification time.
+     * However, not all documents are guaranteed to *have* ETags, and some
+     * might have Last-Modified values w/o ETags, so this gets a little
+     * complicated.
+     */
+
+    if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
+        return OK;
+    }
+
+    mtime = (r->mtime != 0) ? r->mtime : time(NULL);
+
+    /* If an If-Match request-header field was given
+     * AND the field value is not "*" (meaning match anything)
+     * AND if our strong ETag does not match any entity tag in that field,
+     *     respond with a status of 412 (Precondition Failed).
+     */
+    if ((if_match = ap_table_get(r->headers_in, "If-Match")) != NULL) {
+        if (if_match[0] != '*' &&
+            (etag == NULL || etag[0] == 'W' ||
+             !ap_find_list_item(r->pool, if_match, etag))) {
+            return HTTP_PRECONDITION_FAILED;
+        }
+    }
+    else {
+        /* Else if a valid If-Unmodified-Since request-header field was given
+         * AND the requested resource has been modified since the time
+         * specified in this field, then the server MUST
+         *     respond with a status of 412 (Precondition Failed).
+         */
+        if_unmodified = ap_table_get(r->headers_in, "If-Unmodified-Since");
+        if (if_unmodified != NULL) {
+            time_t ius = ap_parseHTTPdate(if_unmodified);
+
+            if ((ius != BAD_DATE) && (mtime > ius)) {
+                return HTTP_PRECONDITION_FAILED;
+            }
+        }
+    }
+
+    /* If an If-None-Match request-header field was given
+     * AND the field value is "*" (meaning match anything)
+     *     OR our ETag matches any of the entity tags in that field, fail.
+     *
+     * If the request method was GET or HEAD, failure means the server
+     *    SHOULD respond with a 304 (Not Modified) response.
+     * For all other request methods, failure means the server MUST
+     *    respond with a status of 412 (Precondition Failed).
+     *
+     * GET or HEAD allow weak etag comparison, all other methods require
+     * strong comparison.  We can only use weak if it's not a range request.
+     */
+    if_nonematch = ap_table_get(r->headers_in, "If-None-Match");
+    if (if_nonematch != NULL) {
+        if (r->method_number == M_GET) {
+            if (if_nonematch[0] == '*')
+                return HTTP_NOT_MODIFIED;
+            if (etag != NULL) {
+                if (ap_table_get(r->headers_in, "Range")) {
+                    if (etag[0] != 'W' &&
+                        ap_find_list_item(r->pool, if_nonematch, etag)) {
+                        return HTTP_NOT_MODIFIED;
+                    }
+                }
+                else if (strstr(if_nonematch, etag)) {
+                    return HTTP_NOT_MODIFIED;
+                }
+            }
+        }
+        else if (if_nonematch[0] == '*' ||
+                 (etag != NULL &&
+                  ap_find_list_item(r->pool, if_nonematch, etag))) {
+            return HTTP_PRECONDITION_FAILED;
+        }
+    }
+    /* Else if a valid If-Modified-Since request-header field was given
+     * AND it is a GET or HEAD request
+     * AND the requested resource has not been modified since the time
+     * specified in this field, then the server MUST
+     *    respond with a status of 304 (Not Modified).
+     * A date later than the server's current request time is invalid.
+     */
+    else if ((r->method_number == M_GET)
+             && ((if_modified_since =
+                  ap_table_get(r->headers_in, "If-Modified-Since")) != NULL)) {
+        time_t ims = ap_parseHTTPdate(if_modified_since);
+
+        if ((ims >= mtime) && (ims <= r->request_time)) {
+            return HTTP_NOT_MODIFIED;
+        }
+    }
+    return OK;
+}
+
+/*
+ * Construct an entity tag (ETag) from resource information.  If it's a real
+ * file, build in some of the file characteristics.  If the modification time
+ * is newer than (request-time minus 1 second), mark the ETag as weak - it
+ * could be modified again in as short an interval.  We rationalize the
+ * modification time we're given to keep it from being in the future.
+ */
+API_EXPORT(char *) ap_make_etag(request_rec *r, int force_weak)
+{
+    char *etag;
+    char *weak;
+
+    /*
+     * Make an ETag header out of various pieces of information. We use
+     * the last-modified date and, if we have a real file, the
+     * length and inode number - note that this doesn't have to match
+     * the content-length (i.e. includes), it just has to be unique
+     * for the file.
+     *
+     * If the request was made within a second of the last-modified date,
+     * we send a weak tag instead of a strong one, since it could
+     * be modified again later in the second, and the validation
+     * would be incorrect.
+     */
+    
+    weak = ((r->request_time - r->mtime > 1) && !force_weak) ? "" : "W/";
+
+    if (r->finfo.st_mode != 0) {
+        etag = ap_psprintf(r->pool,
+                    "%s\"%lx-%lx-%lx\"", weak,
+                    (unsigned long) r->finfo.st_ino,
+                    (unsigned long) r->finfo.st_size,
+                    (unsigned long) r->mtime);
+    }
+    else {
+        etag = ap_psprintf(r->pool, "%s\"%lx\"", weak,
+                    (unsigned long) r->mtime);
+    }
+
+    return etag;
+}
+
+API_EXPORT(void) ap_set_etag(request_rec *r)
+{
+    char *etag;
+    char *variant_etag, *vlv;
+    int vlv_weak;
+
+    if (!r->vlist_validator) {
+        etag = ap_make_etag(r, 0);
+    }
+    else {
+        /* If we have a variant list validator (vlv) due to the
+         * response being negotiated, then we create a structured
+         * entity tag which merges the variant etag with the variant
+         * list validator (vlv).  This merging makes revalidation
+         * somewhat safer, ensures that caches which can deal with
+         * Vary will (eventually) be updated if the set of variants is
+         * changed, and is also a protocol requirement for transparent
+         * content negotiation.
+         */
+
+        /* if the variant list validator is weak, we make the whole
+         * structured etag weak.  If we would not, then clients could
+         * have problems merging range responses if we have different
+         * variants with the same non-globally-unique strong etag.
+         */
+
+        vlv = r->vlist_validator;
+        vlv_weak = (vlv[0] == 'W');
+               
+        variant_etag = ap_make_etag(r, vlv_weak);
+
+        /* merge variant_etag and vlv into a structured etag */
+
+        variant_etag[strlen(variant_etag) - 1] = '\0';
+        if (vlv_weak)
+            vlv += 3;
+        else
+            vlv++;
+        etag = ap_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
+    }
+
+    ap_table_setn(r->headers_out, "ETag", etag);
+}
+
+/*
+ * This function sets the Last-Modified output header field to the value
+ * of the mtime field in the request structure - rationalized to keep it from
+ * being in the future.
+ */
+API_EXPORT(void) ap_set_last_modified(request_rec *r)
+{
+    time_t mod_time = ap_rationalize_mtime(r, r->mtime);
+
+    ap_table_setn(r->headers_out, "Last-Modified",
+              ap_gm_timestr_822(r->pool, mod_time));
+}
+
+/* Get the method number associated with the given string, assumed to
+ * contain an HTTP method.  Returns M_INVALID if not recognized.
+ *
+ * This is the first step toward placing method names in a configurable
+ * list.  Hopefully it (and other routines) can eventually be moved to
+ * something like a mod_http_methods.c, complete with config stuff.
+ */
+API_EXPORT(int) ap_method_number_of(const char *method)
+{
+    switch (*method) {
+        case 'H':
+           if (strcmp(method, "HEAD") == 0)
+               return M_GET;   /* see header_only in request_rec */
+           break;
+        case 'G':
+           if (strcmp(method, "GET") == 0)
+               return M_GET;
+           break;
+        case 'P':
+           if (strcmp(method, "POST") == 0)
+               return M_POST;
+           if (strcmp(method, "PUT") == 0)
+               return M_PUT;
+           if (strcmp(method, "PATCH") == 0)
+               return M_PATCH;
+           if (strcmp(method, "PROPFIND") == 0)
+               return M_PROPFIND;
+           if (strcmp(method, "PROPPATCH") == 0)
+               return M_PROPPATCH;
+           break;
+        case 'D':
+           if (strcmp(method, "DELETE") == 0)
+               return M_DELETE;
+           break;
+        case 'C':
+           if (strcmp(method, "CONNECT") == 0)
+               return M_CONNECT;
+           if (strcmp(method, "COPY") == 0)
+               return M_COPY;
+           break;
+        case 'M':
+           if (strcmp(method, "MKCOL") == 0)
+               return M_MKCOL;
+           if (strcmp(method, "MOVE") == 0)
+               return M_MOVE;
+           break;
+        case 'O':
+           if (strcmp(method, "OPTIONS") == 0)
+               return M_OPTIONS;
+           break;
+        case 'T':
+           if (strcmp(method, "TRACE") == 0)
+               return M_TRACE;
+           break;
+        case 'L':
+           if (strcmp(method, "LOCK") == 0)
+               return M_LOCK;
+           break;
+        case 'U':
+           if (strcmp(method, "UNLOCK") == 0)
+               return M_UNLOCK;
+           break;
+    }
+    return M_INVALID;
+}
+
+/* Get a line of protocol input, including any continuation lines
+ * caused by MIME folding (or broken clients) if fold != 0, and place it
+ * in the buffer s, of size n bytes, without the ending newline.
+ *
+ * Returns -1 on error, or the length of s.
+ *
+ * Note: Because bgets uses 1 char for newline and 1 char for NUL,
+ *       the most we can get is (n - 2) actual characters if it
+ *       was ended by a newline, or (n - 1) characters if the line
+ *       length exceeded (n - 1).  So, if the result == (n - 1),
+ *       then the actual input line exceeded the buffer length,
+ *       and it would be a good idea for the caller to puke 400 or 414.
+ */
+static int getline(char *s, int n, BUFF *in, int fold)
+{
+    char *pos, next;
+    int retval;
+    int total = 0;
+
+    pos = s;
+
+    do {
+        retval = ap_bgets(pos, n, in);     /* retval == -1 if error, 0 if EOF */
+
+        if (retval <= 0)
+            return ((retval < 0) && (total == 0)) ? -1 : total;
+
+        /* retval is the number of characters read, not including NUL      */
+
+        n -= retval;            /* Keep track of how much of s is full     */
+        pos += (retval - 1);    /* and where s ends                        */
+        total += retval;        /* and how long s has become               */
+
+        if (*pos == '\n') {     /* Did we get a full line of input?        */
+            /*
+             * Trim any extra trailing spaces or tabs except for the first
+             * space or tab at the beginning of a blank string.  This makes
+             * it much easier to check field values for exact matches, and
+             * saves memory as well.  Terminate string at end of line.
+             */
+            while (pos > (s + 1) && (*(pos - 1) == ' ' || *(pos - 1) == '\t')) {
+                --pos;          /* trim extra trailing spaces or tabs      */
+                --total;        /* but not one at the beginning of line    */
+                ++n;
+            }
+            *pos = '\0';
+            --total;
+            ++n;
+        }
+        else
+            return total;       /* if not, input line exceeded buffer size */
+
+        /* Continue appending if line folding is desired and
+         * the last line was not empty and we have room in the buffer and
+         * the next line begins with a continuation character.
+         */
+    } while (fold && (retval != 1) && (n > 1)
+                  && (ap_blookc(&next, in) == 1)
+                  && ((next == ' ') || (next == '\t')));
+
+    return total;
+}
+
+/* parse_uri: break apart the uri
+ * Side Effects:
+ * - sets r->args to rest after '?' (or NULL if no '?')
+ * - sets r->uri to request uri (without r->args part)
+ * - sets r->hostname (if not set already) from request (scheme://host:port)
+ */
+CORE_EXPORT(void) ap_parse_uri(request_rec *r, const char *uri)
+{
+    int status = HTTP_OK;
+
+    r->unparsed_uri = ap_pstrdup(r->pool, uri);
+
+    if (r->method_number == M_CONNECT) {
+       status = ap_parse_hostinfo_components(r->pool, uri, &r->parsed_uri);
+    } else {
+       /* Simple syntax Errors in URLs are trapped by parse_uri_components(). */
+       status = ap_parse_uri_components(r->pool, uri, &r->parsed_uri);
+    }
+
+    if (ap_is_HTTP_SUCCESS(status)) {
+       /* if it has a scheme we may need to do absoluteURI vhost stuff */
+       if (r->parsed_uri.scheme
+           && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))) {
+           r->hostname = r->parsed_uri.hostname;
+       } else if (r->method_number == M_CONNECT) {
+           r->hostname = r->parsed_uri.hostname;
+       }
+       r->args = r->parsed_uri.query;
+       r->uri = r->parsed_uri.path ? r->parsed_uri.path
+                                   : ap_pstrdup(r->pool, "/");
+#if defined(OS2) || defined(WIN32)
+       /* Handle path translations for OS/2 and plug security hole.
+        * This will prevent "http://www.wherever.com/..\..\/" from
+        * returning a directory for the root drive.
+        */
+       {
+           char *x;
+
+           for (x = r->uri; (x = strchr(x, '\\')) != NULL; )
+               *x = '/';
+       }
+#endif  /* OS2 || WIN32 */
+    }
+    else {
+       r->args = NULL;
+       r->hostname = NULL;
+       r->status = status;             /* set error status */
+       r->uri = ap_pstrdup(r->pool, uri);
+    }
+}
+
+static int read_request_line(request_rec *r)
+{
+    char l[DEFAULT_LIMIT_REQUEST_LINE + 2]; /* getline's two extra for \n\0 */
+    const char *ll = l;
+    const char *uri;
+    conn_rec *conn = r->connection;
+    int major = 1, minor = 0;   /* Assume HTTP/1.0 if non-"HTTP" protocol */
+    int len;
+
+    /* Read past empty lines until we get a real request line,
+     * a read error, the connection closes (EOF), or we timeout.
+     *
+     * We skip empty lines because browsers have to tack a CRLF on to the end
+     * of POSTs to support old CERN webservers.  But note that we may not
+     * have flushed any previous response completely to the client yet.
+     * We delay the flush as long as possible so that we can improve
+     * performance for clients that are pipelining requests.  If a request
+     * is pipelined then we won't block during the (implicit) read() below.
+     * If the requests aren't pipelined, then the client is still waiting
+     * for the final buffer flush from us, and we will block in the implicit
+     * read().  B_SAFEREAD ensures that the BUFF layer flushes if it will
+     * have to block during a read.
+     */
+    ap_bsetflag(conn->client, B_SAFEREAD, 1);
+    while ((len = getline(l, sizeof(l), conn->client, 0)) <= 0) {
+        if ((len < 0) || ap_bgetflag(conn->client, B_EOF)) {
+            ap_bsetflag(conn->client, B_SAFEREAD, 0);
+           /* this is a hack to make sure that request time is set,
+            * it's not perfect, but it's better than nothing 
+            */
+           r->request_time = time(0);
+            return 0;
+        }
+    }
+    /* we've probably got something to do, ignore graceful restart requests */
+#ifdef SIGUSR1
+    signal(SIGUSR1, SIG_IGN);
+#endif
+
+    ap_bsetflag(conn->client, B_SAFEREAD, 0);
+
+    r->request_time = time(NULL);
+    r->the_request = ap_pstrdup(r->pool, l);
+    r->method = ap_getword_white(r->pool, &ll);
+    uri = ap_getword_white(r->pool, &ll);
+
+    /* Provide quick information about the request method as soon as known */
+
+    r->method_number = ap_method_number_of(r->method);
+    if (r->method_number == M_GET && r->method[0] == 'H') {
+        r->header_only = 1;
+    }
+
+    ap_parse_uri(r, uri);
+
+    /* getline returns (size of max buffer - 1) if it fills up the
+     * buffer before finding the end-of-line.  This is only going to
+     * happen if it exceeds the configured limit for a request-line.
+     */
+    if (len > r->server->limit_req_line) {
+        r->status    = HTTP_REQUEST_URI_TOO_LARGE;
+        r->proto_num = HTTP_VERSION(1,0);
+        r->protocol  = ap_pstrdup(r->pool, "HTTP/1.0");
+        return 0;
+    }
+
+    r->assbackwards = (ll[0] == '\0');
+    r->protocol = ap_pstrdup(r->pool, ll[0] ? ll : "HTTP/0.9");
+
+    if (2 == sscanf(r->protocol, "HTTP/%u.%u", &major, &minor)
+      && minor < HTTP_VERSION(1,0))    /* don't allow HTTP/0.1000 */
+       r->proto_num = HTTP_VERSION(major, minor);
+    else
+       r->proto_num = HTTP_VERSION(1,0);
+
+    return 1;
+}
+
+static void get_mime_headers(request_rec *r)
+{
+    char field[DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2]; /* getline's two extra */
+    conn_rec *c = r->connection;
+    char *value;
+    char *copy;
+    int len;
+    unsigned int fields_read = 0;
+    table *tmp_headers;
+
+    /* We'll use ap_overlap_tables later to merge these into r->headers_in. */
+    tmp_headers = ap_make_table(r->pool, 50);
+
+    /*
+     * Read header lines until we get the empty separator line, a read error,
+     * the connection closes (EOF), reach the server limit, or we timeout.
+     */
+    while ((len = getline(field, sizeof(field), c->client, 1)) > 0) {
+
+        if (r->server->limit_req_fields &&
+            (++fields_read > r->server->limit_req_fields)) {
+            r->status = HTTP_BAD_REQUEST;
+            ap_table_setn(r->notes, "error-notes",
+                          "The number of request header fields exceeds "
+                          "this server's limit.<P>\n");
+            return;
+        }
+        /* getline returns (size of max buffer - 1) if it fills up the
+         * buffer before finding the end-of-line.  This is only going to
+         * happen if it exceeds the configured limit for a field size.
+         */
+        if (len > r->server->limit_req_fieldsize) {
+            r->status = HTTP_BAD_REQUEST;
+            ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
+                "Size of a request header field exceeds server limit.<P>\n"
+                "<PRE>\n", field, "</PRE>\n", NULL));
+            return;
+        }
+        copy = ap_palloc(r->pool, len + 1);
+        memcpy(copy, field, len + 1);
+
+        if (!(value = strchr(copy, ':'))) {     /* Find the colon separator */
+            r->status = HTTP_BAD_REQUEST;       /* or abort the bad request */
+            ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
+                "Request header field is missing colon separator.<P>\n"
+                "<PRE>\n", copy, "</PRE>\n", NULL));
+            return;
+        }
+
+        *value = '\0';
+        ++value;
+        while (*value == ' ' || *value == '\t')
+            ++value;            /* Skip to start of value   */
+
+       ap_table_addn(tmp_headers, copy, value);
+    }
+
+    ap_overlap_tables(r->headers_in, tmp_headers, AP_OVERLAP_TABLES_MERGE);
+}
+
+request_rec *ap_read_request(conn_rec *conn)
+{
+    request_rec *r;
+    pool *p;
+    const char *expect;
+    int access_status;
+
+    p = ap_make_sub_pool(conn->pool);
+    r = ap_pcalloc(p, sizeof(request_rec));
+    r->pool            = p;
+    r->connection      = conn;
+    conn->server       = conn->base_server;
+    r->server          = conn->server;
+
+    conn->keptalive    = conn->keepalive == 1;
+    conn->keepalive    = 0;
+
+    conn->user         = NULL;
+    conn->ap_auth_type    = NULL;
+
+    r->headers_in      = ap_make_table(r->pool, 50);
+    r->subprocess_env  = ap_make_table(r->pool, 50);
+    r->headers_out     = ap_make_table(r->pool, 12);
+    r->err_headers_out = ap_make_table(r->pool, 5);
+    r->notes           = ap_make_table(r->pool, 5);
+
+    r->request_config  = ap_create_request_config(r->pool);
+    r->per_dir_config  = r->server->lookup_defaults;
+
+    r->sent_bodyct     = 0;                      /* bytect isn't for body */
+
+    r->read_length     = 0;
+    r->read_body       = REQUEST_NO_BODY;
+
+    r->status          = HTTP_REQUEST_TIME_OUT;  /* Until we get a request */
+    r->the_request     = NULL;
+
+#ifdef CHARSET_EBCDIC
+    ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1);
+#endif
+
+    /* Get the request... */
+
+    ap_keepalive_timeout("read request line", r);
+    if (!read_request_line(r)) {
+        ap_kill_timeout(r);
+        if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
+
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                         "request failed: URI too long");
+            ap_send_error_response(r, 0);
+            ap_log_transaction(r);
+            return r;
+        }
+        return NULL;
+    }
+    if (!r->assbackwards) {
+        ap_hard_timeout("read request headers", r);
+        get_mime_headers(r);
+        ap_kill_timeout(r);
+        if (r->status != HTTP_REQUEST_TIME_OUT) {
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                         "request failed: error reading the headers");
+            ap_send_error_response(r, 0);
+            ap_log_transaction(r);
+            return r;
+        }
+    }
+    else {
+        ap_kill_timeout(r);
+
+        if (r->header_only) {
+            /*
+             * Client asked for headers only with HTTP/0.9, which doesn't send
+             * headers! Have to dink things just to make sure the error message
+             * comes through...
+             */
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                          "client sent invalid HTTP/0.9 request: HEAD %s",
+                          r->uri);
+            r->header_only = 0;
+            r->status = HTTP_BAD_REQUEST;
+            ap_send_error_response(r, 0);
+            ap_log_transaction(r);
+            return r;
+        }
+    }
+
+    r->status = HTTP_OK;                         /* Until further notice. */
+
+    /* update what we think the virtual host is based on the headers we've
+     * now read
+     */
+    ap_update_vhost_from_headers(r);
+
+    /* we may have switched to another server */
+    r->per_dir_config = r->server->lookup_defaults;
+
+    conn->keptalive = 0;        /* We now have a request to play with */
+
+    if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1,1))) ||
+        ((r->proto_num == HTTP_VERSION(1,1)) &&
+         !ap_table_get(r->headers_in, "Host"))) {
+        /*
+         * Client sent us an HTTP/1.1 or later request without telling us the
+         * hostname, either with a full URL or a Host: header. We therefore
+         * need to (as per the 1.1 spec) send an error.  As a special case,
+         * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
+         * a Host: header, and the server MUST respond with 400 if it doesn't.
+         */
+        r->status = HTTP_BAD_REQUEST;
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                      "client sent HTTP/1.1 request without hostname "
+                      "(see RFC2068 section 9, and 14.23): %s", r->uri);
+        ap_send_error_response(r, 0);
+        ap_log_transaction(r);
+        return r;
+    }
+    if (((expect = ap_table_get(r->headers_in, "Expect")) != NULL) &&
+        (expect[0] != '\0')) {
+        /*
+         * The Expect header field was added to HTTP/1.1 after RFC 2068
+         * as a means to signal when a 100 response is desired and,
+         * unfortunately, to signal a poor man's mandatory extension that
+         * the server must understand or return 417 Expectation Failed.
+         */
+        if (strcasecmp(expect, "100-continue") == 0) {
+            r->expecting_100 = 1;
+        }
+        else {
+            r->status = HTTP_EXPECTATION_FAILED;
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
+                          "client sent an unrecognized expectation value of "
+                          "Expect: %s", expect);
+            ap_send_error_response(r, 0);
+            (void) ap_discard_request_body(r);
+            ap_log_transaction(r);
+            return r;
+        }
+    }
+
+    if ((access_status = ap_run_post_read_request(r))) {
+        ap_die(access_status, r);
+        ap_log_transaction(r);
+        return NULL;
+    }
+
+    return r;
+}
+
+/*
+ * A couple of other functions which initialize some of the fields of
+ * a request structure, as appropriate for adjuncts of one kind or another
+ * to a request in progress.  Best here, rather than elsewhere, since
+ * *someone* has to set the protocol-specific fields...
+ */
+
+void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r)
+{
+    rnew->the_request     = r->the_request;  /* Keep original request-line */
+
+    rnew->assbackwards    = 1;   /* Don't send headers from this. */
+    rnew->no_local_copy   = 1;   /* Don't try to send USE_LOCAL_COPY for a
+                                  * fragment. */
+    rnew->method          = "GET";
+    rnew->method_number   = M_GET;
+    rnew->protocol        = "INCLUDED";
+
+    rnew->status          = HTTP_OK;
+
+    rnew->headers_in      = r->headers_in;
+    rnew->subprocess_env  = ap_copy_table(rnew->pool, r->subprocess_env);
+    rnew->headers_out     = ap_make_table(rnew->pool, 5);
+    rnew->err_headers_out = ap_make_table(rnew->pool, 5);
+    rnew->notes           = ap_make_table(rnew->pool, 5);
+
+    rnew->expecting_100   = r->expecting_100;
+    rnew->read_length     = r->read_length;
+    rnew->read_body       = REQUEST_NO_BODY;
+
+    rnew->main = (request_rec *) r;
+}
+
+void ap_finalize_sub_req_protocol(request_rec *sub)
+{
+    SET_BYTES_SENT(sub->main);
+}
+
+/*
+ * Support for the Basic authentication protocol, and a bit for Digest.
+ */
+
+API_EXPORT(void) ap_note_auth_failure(request_rec *r)
+{
+    if (!strcasecmp(ap_auth_type(r), "Basic"))
+        ap_note_basic_auth_failure(r);
+    else if (!strcasecmp(ap_auth_type(r), "Digest"))
+        ap_note_digest_auth_failure(r);
+}
+
+API_EXPORT(void) ap_note_basic_auth_failure(request_rec *r)
+{
+    if (strcasecmp(ap_auth_type(r), "Basic"))
+        ap_note_auth_failure(r);
+    else
+        ap_table_setn(r->err_headers_out,
+                  r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+                  ap_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"",
+                          NULL));
+}
+
+API_EXPORT(void) ap_note_digest_auth_failure(request_rec *r)
+{
+    ap_table_setn(r->err_headers_out,
+           r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+           ap_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%lu\"",
+               ap_auth_name(r), r->request_time));
+}
+
+API_EXPORT(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
+{
+    const char *auth_line = ap_table_get(r->headers_in,
+                                      r->proxyreq ? "Proxy-Authorization"
+                                                  : "Authorization");
+    const char *t;
+
+    if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
+        return DECLINED;
+
+    if (!ap_auth_name(r)) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
+                   r, "need AuthName: %s", r->uri);
+        return SERVER_ERROR;
+    }
+
+    if (!auth_line) {
+        ap_note_basic_auth_failure(r);
+        return AUTH_REQUIRED;
+    }
+
+    if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
+        /* Client tried to authenticate using wrong auth scheme */
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                    "client used wrong authentication scheme: %s", r->uri);
+        ap_note_basic_auth_failure(r);
+        return AUTH_REQUIRED;
+    }
+
+    /* CHARSET_EBCDIC Issue's here ?!? Compare with 32/9 instead
+     * as we are operating on an octed stream ?
+     */
+    while (*auth_line== ' ' || *auth_line== '\t')
+        auth_line++;
+
+    t = ap_pbase64decode(r->pool, auth_line);
+    /* Note that this allocation has to be made from r->connection->pool
+     * because it has the lifetime of the connection.  The other allocations
+     * are temporary and can be tossed away any time.
+     */
+    r->connection->user = ap_getword_nulls (r->connection->pool, &t, ':');
+    r->connection->ap_auth_type = "Basic";
+
+    *pw = t;
+
+    return OK;
+}
+
+/* New Apache routine to map status codes into array indicies
+ *  e.g.  100 -> 0,  101 -> 1,  200 -> 2 ...
+ * The number of status lines must equal the value of RESPONSE_CODES (httpd.h)
+ * and must be listed in order.
+ */
+
+#ifdef UTS21
+/* The second const triggers an assembler bug on UTS 2.1.
+ * Another workaround is to move some code out of this file into another,
+ *   but this is easier.  Dave Dykstra, 3/31/99 
+ */
+static const char * status_lines[RESPONSE_CODES] =
+#else
+static const char * const status_lines[RESPONSE_CODES] =
+#endif
+{
+    "100 Continue",
+    "101 Switching Protocols",
+    "102 Processing",
+#define LEVEL_200  3
+    "200 OK",
+    "201 Created",
+    "202 Accepted",
+    "203 Non-Authoritative Information",
+    "204 No Content",
+    "205 Reset Content",
+    "206 Partial Content",
+    "207 Multi-Status",
+#define LEVEL_300 11
+    "300 Multiple Choices",
+    "301 Moved Permanently",
+    "302 Found",
+    "303 See Other",
+    "304 Not Modified",
+    "305 Use Proxy",
+    "306 unused",
+    "307 Temporary Redirect",
+#define LEVEL_400 19
+    "400 Bad Request",
+    "401 Authorization Required",
+    "402 Payment Required",
+    "403 Forbidden",
+    "404 Not Found",
+    "405 Method Not Allowed",
+    "406 Not Acceptable",
+    "407 Proxy Authentication Required",
+    "408 Request Time-out",
+    "409 Conflict",
+    "410 Gone",
+    "411 Length Required",
+    "412 Precondition Failed",
+    "413 Request Entity Too Large",
+    "414 Request-URI Too Large",
+    "415 Unsupported Media Type",
+    "416 Requested Range Not Satisfiable",
+    "417 Expectation Failed",
+    "418 unused",
+    "419 unused",
+    "420 unused",
+    "421 unused",
+    "422 Unprocessable Entity",
+    "423 Locked",
+    "424 Failed Dependency",
+#define LEVEL_500 44
+    "500 Internal Server Error",
+    "501 Method Not Implemented",
+    "502 Bad Gateway",
+    "503 Service Temporarily Unavailable",
+    "504 Gateway Time-out",
+    "505 HTTP Version Not Supported",
+    "506 Variant Also Negotiates",
+    "507 Insufficient Storage",
+    "508 unused",
+    "509 unused",
+    "510 Not Extended"
+};
+
+/* The index is found by its offset from the x00 code of each level.
+ * Although this is fast, it will need to be replaced if some nutcase
+ * decides to define a high-numbered code before the lower numbers.
+ * If that sad event occurs, replace the code below with a linear search
+ * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
+ */
+API_EXPORT(int) ap_index_of_response(int status)
+{
+    static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
+    LEVEL_500, RESPONSE_CODES};
+    int i, pos;
+
+    if (status < 100)           /* Below 100 is illegal for HTTP status */
+        return LEVEL_500;
+
+    for (i = 0; i < 5; i++) {
+        status -= 100;
+        if (status < 100) {
+            pos = (status + shortcut[i]);
+            if (pos < shortcut[i + 1])
+                return pos;
+            else
+                return LEVEL_500;       /* status unknown (falls in gap) */
+        }
+    }
+    return LEVEL_500;           /* 600 or above is also illegal */
+}
+
+/* Send a single HTTP header field to the client.  Note that this function
+ * is used in calls to table_do(), so their interfaces are co-dependent.
+ * In other words, don't change this one without checking table_do in alloc.c.
+ * It returns true unless there was a write error of some kind.
+ */
+API_EXPORT_NONSTD(int) ap_send_header_field(request_rec *r,
+    const char *fieldname, const char *fieldval)
+{
+    return (0 < ap_rvputs(r, fieldname, ": ", fieldval, "\015\012", NULL));
+}
+
+API_EXPORT(void) ap_basic_http_header(request_rec *r)
+{
+    char *protocol;
+#ifdef CHARSET_EBCDIC
+    int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII);
+#endif /*CHARSET_EBCDIC*/
+
+    if (r->assbackwards)
+        return;
+
+    if (!r->status_line)
+        r->status_line = status_lines[ap_index_of_response(r->status)];
+
+    /* mod_proxy is only HTTP/1.0, so avoid sending HTTP/1.1 error response;
+     * kluge around broken browsers when indicated by force-response-1.0
+     */
+    if (r->proxyreq
+        || (r->proto_num == HTTP_VERSION(1,0)
+            && ap_table_get(r->subprocess_env, "force-response-1.0"))) {
+
+        protocol = "HTTP/1.0";
+        r->connection->keepalive = -1;
+    }
+    else
+        protocol = SERVER_PROTOCOL;
+
+#ifdef CHARSET_EBCDIC
+    ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
+#endif /*CHARSET_EBCDIC*/
+
+    /* Output the HTTP/1.x Status-Line and the Date and Server fields */
+
+    ap_rvputs(r, protocol, " ", r->status_line, "\015\012", NULL);
+
+    ap_send_header_field(r, "Date", ap_gm_timestr_822(r->pool, r->request_time));
+    ap_send_header_field(r, "Server", ap_get_server_version());
+
+    ap_table_unset(r->headers_out, "Date");        /* Avoid bogosity */
+    ap_table_unset(r->headers_out, "Server");
+#ifdef CHARSET_EBCDIC
+    if (!convert)
+        ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert);
+#endif /*CHARSET_EBCDIC*/
+}
+
+/* Navigator versions 2.x, 3.x and 4.0 betas up to and including 4.0b2
+ * have a header parsing bug.  If the terminating \r\n occur starting
+ * at offset 256, 257 or 258 of output then it will not properly parse
+ * the headers.  Curiously it doesn't exhibit this problem at 512, 513.
+ * We are guessing that this is because their initial read of a new request
+ * uses a 256 byte buffer, and subsequent reads use a larger buffer.
+ * So the problem might exist at different offsets as well.
+ *
+ * This should also work on keepalive connections assuming they use the
+ * same small buffer for the first read of each new request.
+ *
+ * At any rate, we check the bytes written so far and, if we are about to
+ * tickle the bug, we instead insert a bogus padding header.  Since the bug
+ * manifests as a broken image in Navigator, users blame the server.  :(
+ * It is more expensive to check the User-Agent than it is to just add the
+ * bytes, so we haven't used the BrowserMatch feature here.
+ */
+static void terminate_header(BUFF *client)
+{
+    long int bs;
+
+    ap_bgetopt(client, BO_BYTECT, &bs);
+    if (bs >= 255 && bs <= 257)
+        ap_bputs("X-Pad: avoid browser bug\015\012", client);
+
+    ap_bputs("\015\012", client);  /* Send the terminating empty line */
+}
+
+/* Build the Allow field-value from the request handler method mask.
+ * Note that we always allow TRACE, since it is handled below.
+ */
+static char *make_allow(request_rec *r)
+{
+    return 2 + ap_pstrcat(r->pool,
+                   (r->allowed & (1 << M_GET))       ? ", GET, HEAD" : "",
+                   (r->allowed & (1 << M_POST))      ? ", POST"      : "",
+                   (r->allowed & (1 << M_PUT))       ? ", PUT"       : "",
+                   (r->allowed & (1 << M_DELETE))    ? ", DELETE"    : "",
+                   (r->allowed & (1 << M_CONNECT))   ? ", CONNECT"   : "",
+                   (r->allowed & (1 << M_OPTIONS))   ? ", OPTIONS"   : "",
+                   (r->allowed & (1 << M_PATCH))     ? ", PATCH"     : "",
+                   (r->allowed & (1 << M_PROPFIND))  ? ", PROPFIND"  : "",
+                   (r->allowed & (1 << M_PROPPATCH)) ? ", PROPPATCH" : "",
+                   (r->allowed & (1 << M_MKCOL))     ? ", MKCOL"     : "",
+                   (r->allowed & (1 << M_COPY))      ? ", COPY"      : "",
+                   (r->allowed & (1 << M_MOVE))      ? ", MOVE"      : "",
+                   (r->allowed & (1 << M_LOCK))      ? ", LOCK"      : "",
+                   (r->allowed & (1 << M_UNLOCK))    ? ", UNLOCK"    : "",
+                   ", TRACE",
+                   NULL);
+}
+
+API_EXPORT(int) ap_send_http_trace(request_rec *r)
+{
+    int rv;
+
+    /* Get the original request */
+    while (r->prev)
+        r = r->prev;
+
+    if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY)))
+        return rv;
+
+    ap_hard_timeout("send TRACE", r);
+
+    r->content_type = "message/http";
+    ap_send_http_header(r);
+
+    /* Now we recreate the request, and echo it back */
+
+    ap_rvputs(r, r->the_request, "\015\012", NULL);
+
+    ap_table_do((int (*) (void *, const char *, const char *))
+                ap_send_header_field, (void *) r, r->headers_in, NULL);
+    ap_rputs("\015\012", r);
+
+    ap_kill_timeout(r);
+    return OK;
+}
+
+int ap_send_http_options(request_rec *r)
+{
+    const long int zero = 0L;
+
+    if (r->assbackwards)
+        return DECLINED;
+
+    ap_hard_timeout("send OPTIONS", r);
+
+    ap_basic_http_header(r);
+
+    ap_table_setn(r->headers_out, "Content-Length", "0");
+    ap_table_setn(r->headers_out, "Allow", make_allow(r));
+    ap_set_keepalive(r);
+
+    ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field,
+             (void *) r, r->headers_out, NULL);
+
+    terminate_header(r->connection->client);
+
+    ap_kill_timeout(r);
+    ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
+
+    return OK;
+}
+
+/*
+ * Here we try to be compatible with clients that want multipart/x-byteranges
+ * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
+ * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
+ * that the browser supports an older protocol. We also check User-Agent
+ * for Microsoft Internet Explorer 3, which needs this as well.
+ */
+static int use_range_x(request_rec *r)
+{
+    const char *ua;
+    return (ap_table_get(r->headers_in, "Request-Range") ||
+            ((ua = ap_table_get(r->headers_in, "User-Agent"))
+             && strstr(ua, "MSIE 3")));
+}
+
+/* This routine is called by ap_table_do and merges all instances of
+ * the passed field values into a single array that will be further
+ * processed by some later routine.  Originally intended to help split
+ * and recombine multiple Vary fields, though it is generic to any field
+ * consisting of comma/space-separated tokens.
+ */
+static int uniq_field_values(void *d, const char *key, const char *val)
+{
+    array_header *values;
+    char *start;
+    char *e;
+    char **strpp;
+    int  i;
+
+    values = (array_header *)d;
+
+    e = ap_pstrdup(values->pool, val);
+
+    do {
+        /* Find a non-empty fieldname */
+
+        while (*e == ',' || ap_isspace(*e)) {
+            ++e;
+        }
+        if (*e == '\0') {
+            break;
+        }
+        start = e;
+        while (*e != '\0' && *e != ',' && !ap_isspace(*e)) {
+            ++e;
+        }
+        if (*e != '\0') {
+            *e++ = '\0';
+        }
+
+        /* Now add it to values if it isn't already represented.
+         * Could be replaced by a ap_array_strcasecmp() if we had one.
+         */
+        for (i = 0, strpp = (char **) values->elts; i < values->nelts;
+             ++i, ++strpp) {
+            if (*strpp && strcasecmp(*strpp, start) == 0) {
+                break;
+            }
+        }
+        if (i == values->nelts) {  /* if not found */
+           *(char **)ap_push_array(values) = start;
+        }
+    } while (*e != '\0');
+
+    return 1;
+}
+
+/*
+ * Since some clients choke violently on multiple Vary fields, or
+ * Vary fields with duplicate tokens, combine any multiples and remove
+ * any duplicates.
+ */
+static void fixup_vary(request_rec *r)
+{
+    array_header *varies;
+
+    varies = ap_make_array(r->pool, 5, sizeof(char *));
+
+    /* Extract all Vary fields from the headers_out, separate each into
+     * its comma-separated fieldname values, and then add them to varies
+     * if not already present in the array.
+     */
+    ap_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
+               (void *) varies, r->headers_out, "Vary", NULL);
+
+    /* If we found any, replace old Vary fields with unique-ified value */
+
+    if (varies->nelts > 0) {
+       ap_table_setn(r->headers_out, "Vary",
+                     ap_array_pstrcat(r->pool, varies, ','));
+    }
+}
+
+API_EXPORT(void) ap_send_http_header(request_rec *r)
+{
+    int i;
+    const long int zero = 0L;
+#ifdef CHARSET_EBCDIC
+    int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII);
+#endif /*CHARSET_EBCDIC*/
+
+    if (r->assbackwards) {
+        if (!r->main)
+            ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
+        r->sent_bodyct = 1;
+        return;
+    }
+
+    /*
+     * Now that we are ready to send a response, we need to combine the two
+     * header field tables into a single table.  If we don't do this, our
+     * later attempts to set or unset a given fieldname might be bypassed.
+     */
+    if (!ap_is_empty_table(r->err_headers_out))
+        r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out,
+                                        r->headers_out);
+
+    /*
+     * Remove the 'Vary' header field if the client can't handle it.
+     * Since this will have nasty effects on HTTP/1.1 caches, force
+     * the response into HTTP/1.0 mode.
+     */
+    if (ap_table_get(r->subprocess_env, "force-no-vary") != NULL) {
+       ap_table_unset(r->headers_out, "Vary");
+       r->proto_num = HTTP_VERSION(1,0);
+       ap_table_set(r->subprocess_env, "force-response-1.0", "1");
+    }
+    else {
+       fixup_vary(r);
+    }
+
+    ap_hard_timeout("send headers", r);
+
+    ap_basic_http_header(r);
+
+#ifdef CHARSET_EBCDIC
+    ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
+#endif /*CHARSET_EBCDIC*/
+
+    ap_set_keepalive(r);
+
+    if (r->chunked) {
+        ap_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
+        ap_table_unset(r->headers_out, "Content-Length");
+    }
+
+    if (r->byterange > 1)
+        ap_table_setn(r->headers_out, "Content-Type",
+                  ap_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
+                          "byteranges; boundary=", r->boundary, NULL));
+    else if (r->content_type)
+        ap_table_setn(r->headers_out, "Content-Type", r->content_type);
+    else
+        ap_table_setn(r->headers_out, "Content-Type", ap_default_type(r));
+
+    if (r->content_encoding)
+        ap_table_setn(r->headers_out, "Content-Encoding", r->content_encoding);
+
+    if (r->content_languages && r->content_languages->nelts) {
+        for (i = 0; i < r->content_languages->nelts; ++i) {
+            ap_table_mergen(r->headers_out, "Content-Language",
+                        ((char **) (r->content_languages->elts))[i]);
+        }
+    }
+    else if (r->content_language)
+        ap_table_setn(r->headers_out, "Content-Language", r->content_language);
+
+    /*
+     * Control cachability for non-cachable responses if not already set by
+     * some other part of the server configuration.
+     */
+    if (r->no_cache && !ap_table_get(r->headers_out, "Expires"))
+        ap_table_addn(r->headers_out, "Expires",
+                  ap_gm_timestr_822(r->pool, r->request_time));
+
+    /* Send the entire table of header fields, terminated by an empty line. */
+
+    ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field,
+             (void *) r, r->headers_out, NULL);
+
+    terminate_header(r->connection->client);
+
+    ap_kill_timeout(r);
+
+    ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
+    r->sent_bodyct = 1;         /* Whatever follows is real body stuff... */
+
+    /* Set buffer flags for the body */
+    if (r->chunked)
+        ap_bsetflag(r->connection->client, B_CHUNK, 1);
+#ifdef CHARSET_EBCDIC
+    if (!convert)
+        ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert);
+#endif /*CHARSET_EBCDIC*/
+}
+
+/* finalize_request_protocol is called at completion of sending the
+ * response.  It's sole purpose is to send the terminating protocol
+ * information for any wrappers around the response message body
+ * (i.e., transfer encodings).  It should have been named finalize_response.
+ */
+API_EXPORT(void) ap_finalize_request_protocol(request_rec *r)
+{
+    if (r->chunked && !r->connection->aborted) {
+        /*
+         * Turn off chunked encoding --- we can only do this once.
+         */
+        r->chunked = 0;
+        ap_bsetflag(r->connection->client, B_CHUNK, 0);
+
+        ap_soft_timeout("send ending chunk", r);
+        ap_rputs("0\015\012", r);
+        /* If we had footer "headers", we'd send them now */
+        ap_rputs("\015\012", r);
+        ap_kill_timeout(r);
+    }
+}
+
+/* Here we deal with getting the request message body from the client.
+ * Whether or not the request contains a body is signaled by the presence
+ * of a non-zero Content-Length or by a Transfer-Encoding: chunked.
+ *
+ * Note that this is more complicated than it was in Apache 1.1 and prior
+ * versions, because chunked support means that the module does less.
+ *
+ * The proper procedure is this:
+ *
+ * 1. Call setup_client_block() near the beginning of the request
+ *    handler. This will set up all the necessary properties, and will
+ *    return either OK, or an error code. If the latter, the module should
+ *    return that error code. The second parameter selects the policy to
+ *    apply if the request message indicates a body, and how a chunked
+ *    transfer-coding should be interpreted. Choose one of
+ *
+ *    REQUEST_NO_BODY          Send 413 error if message has any body
+ *    REQUEST_CHUNKED_ERROR    Send 411 error if body without Content-Length
+ *    REQUEST_CHUNKED_DECHUNK  If chunked, remove the chunks for me.
+ *    REQUEST_CHUNKED_PASS     Pass the chunks to me without removal.
+ *
+ *    In order to use the last two options, the caller MUST provide a buffer
+ *    large enough to hold a chunk-size line, including any extensions.
+ *
+ * 2. When you are ready to read a body (if any), call should_client_block().
+ *    This will tell the module whether or not to read input. If it is 0,
+ *    the module should assume that there is no message body to read.
+ *    This step also sends a 100 Continue response to HTTP/1.1 clients,
+ *    so should not be called until the module is *definitely* ready to
+ *    read content. (otherwise, the point of the 100 response is defeated).
+ *    Never call this function more than once.
+ *
+ * 3. Finally, call get_client_block in a loop. Pass it a buffer and its size.
+ *    It will put data into the buffer (not necessarily a full buffer), and
+ *    return the length of the input block. When it is done reading, it will
+ *    return 0 if EOF, or -1 if there was an error.
+ *    If an error occurs on input, we force an end to keepalive.
+ */
+
+API_EXPORT(int) ap_setup_client_block(request_rec *r, int read_policy)
+{
+    const char *tenc = ap_table_get(r->headers_in, "Transfer-Encoding");
+    const char *lenp = ap_table_get(r->headers_in, "Content-Length");
+    unsigned long max_body;
+
+    r->read_body = read_policy;
+    r->read_chunked = 0;
+    r->remaining = 0;
+
+    if (tenc) {
+        if (strcasecmp(tenc, "chunked")) {
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                        "Unknown Transfer-Encoding %s", tenc);
+            return HTTP_NOT_IMPLEMENTED;
+        }
+        if (r->read_body == REQUEST_CHUNKED_ERROR) {
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                        "chunked Transfer-Encoding forbidden: %s", r->uri);
+            return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED;
+        }
+
+        r->read_chunked = 1;
+    }
+    else if (lenp) {
+        const char *pos = lenp;
+
+        while (ap_isdigit(*pos) || ap_isspace(*pos))
+            ++pos;
+        if (*pos != '\0') {
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                        "Invalid Content-Length %s", lenp);
+            return HTTP_BAD_REQUEST;
+        }
+
+        r->remaining = atol(lenp);
+    }
+
+    if ((r->read_body == REQUEST_NO_BODY) &&
+        (r->read_chunked || (r->remaining > 0))) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                    "%s with body is not allowed for %s", r->method, r->uri);
+        return HTTP_REQUEST_ENTITY_TOO_LARGE;
+    }
+
+    max_body = ap_get_limit_req_body(r);
+    if (max_body && (r->remaining > max_body)) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+          "Request content-length of %s is larger than the configured "
+          "limit of %lu", lenp, max_body);
+        return HTTP_REQUEST_ENTITY_TOO_LARGE;
+    }
+
+    return OK;
+}
+
+API_EXPORT(int) ap_should_client_block(request_rec *r)
+{
+    /* First check if we have already read the request body */
+
+    if (r->read_length || (!r->read_chunked && (r->remaining <= 0)))
+        return 0;
+
+    if (r->expecting_100 && r->proto_num >= HTTP_VERSION(1,1)) {
+        /* sending 100 Continue interim response */
+        ap_rvputs(r, SERVER_PROTOCOL, " ", status_lines[0], "\015\012\015\012",
+                  NULL);
+        ap_rflush(r);
+    }
+
+    return 1;
+}
+
+static long get_chunk_size(char *b)
+{
+    long chunksize = 0;
+
+    while (ap_isxdigit(*b)) {
+        int xvalue = 0;
+
+        if (*b >= '0' && *b <= '9')
+            xvalue = *b - '0';
+        else if (*b >= 'A' && *b <= 'F')
+            xvalue = *b - 'A' + 0xa;
+        else if (*b >= 'a' && *b <= 'f')
+            xvalue = *b - 'a' + 0xa;
+
+        chunksize = (chunksize << 4) | xvalue;
+        ++b;
+    }
+
+    return chunksize;
+}
+
+/* get_client_block is called in a loop to get the request message body.
+ * This is quite simple if the client includes a content-length
+ * (the normal case), but gets messy if the body is chunked. Note that
+ * r->remaining is used to maintain state across calls and that
+ * r->read_length is the total number of bytes given to the caller
+ * across all invocations.  It is messy because we have to be careful not
+ * to read past the data provided by the client, since these reads block.
+ * Returns 0 on End-of-body, -1 on error or premature chunk end.
+ *
+ * Reading the chunked encoding requires a buffer size large enough to
+ * hold a chunk-size line, including any extensions. For now, we'll leave
+ * that to the caller, at least until we can come up with a better solution.
+ */
+API_EXPORT(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz)
+{
+    int c;
+    long len_read, len_to_read;
+    long chunk_start = 0;
+    unsigned long max_body;
+
+    if (!r->read_chunked) {     /* Content-length read */
+        len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
+        len_read = ap_bread(r->connection->client, buffer, len_to_read);
+        if (len_read <= 0) {
+            if (len_read < 0)
+                r->connection->keepalive = -1;
+            return len_read;
+        }
+        r->read_length += len_read;
+        r->remaining -= len_read;
+        return len_read;
+    }
+
+    /*
+     * Handle chunked reading Note: we are careful to shorten the input
+     * bufsiz so that there will always be enough space for us to add a CRLF
+     * (if necessary).
+     */
+    if (r->read_body == REQUEST_CHUNKED_PASS)
+        bufsiz -= 2;
+    if (bufsiz <= 0)
+        return -1;              /* Cannot read chunked with a small buffer */
+
+    /* Check to see if we have already read too much request data.
+     * For efficiency reasons, we only check this at the top of each
+     * caller read pass, since the limit exists just to stop infinite
+     * length requests and nobody cares if it goes over by one buffer.
+     */
+    max_body = ap_get_limit_req_body(r);
+    if (max_body && (r->read_length > max_body)) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+            "Chunked request body is larger than the configured limit of %lu",
+            max_body);
+        r->connection->keepalive = -1;
+        return -1;
+    }
+
+    if (r->remaining == 0) {    /* Start of new chunk */
+
+        chunk_start = getline(buffer, bufsiz, r->connection->client, 0);
+        if ((chunk_start <= 0) || (chunk_start >= (bufsiz - 1))
+            || !ap_isxdigit(*buffer)) {
+            r->connection->keepalive = -1;
+            return -1;
+        }
+
+        len_to_read = get_chunk_size(buffer);
+
+        if (len_to_read == 0) { /* Last chunk indicated, get footers */
+            if (r->read_body == REQUEST_CHUNKED_DECHUNK) {
+                get_mime_headers(r);
+                ap_snprintf(buffer, bufsiz, "%ld", r->read_length);
+                ap_table_unset(r->headers_in, "Transfer-Encoding");
+                ap_table_setn(r->headers_in, "Content-Length",
+                    ap_pstrdup(r->pool, buffer));
+                return 0;
+            }
+            r->remaining = -1;  /* Indicate footers in-progress */
+        }
+        else {
+            r->remaining = len_to_read;
+        }
+        if (r->read_body == REQUEST_CHUNKED_PASS) {
+            buffer[chunk_start++] = CR; /* Restore chunk-size line end  */
+            buffer[chunk_start++] = LF;
+            buffer += chunk_start;      /* and pass line on to caller   */
+            bufsiz -= chunk_start;
+        }
+        else {
+            /* REQUEST_CHUNKED_DECHUNK -- do not include the length of the
+             * header in the return value
+             */
+            chunk_start = 0;
+        }
+    }
+                                /* When REQUEST_CHUNKED_PASS, we are */
+    if (r->remaining == -1) {   /* reading footers until empty line  */
+        len_read = chunk_start;
+
+        while ((bufsiz > 1) && ((len_read =
+                  getline(buffer, bufsiz, r->connection->client, 1)) > 0)) {
+
+            if (len_read != (bufsiz - 1)) {
+                buffer[len_read++] = CR;        /* Restore footer line end  */
+                buffer[len_read++] = LF;
+            }
+            chunk_start += len_read;
+            buffer += len_read;
+            bufsiz -= len_read;
+        }
+        if (len_read < 0) {
+            r->connection->keepalive = -1;
+            return -1;
+        }
+
+        if (len_read == 0) {    /* Indicates an empty line */
+            buffer[0] = CR;
+            buffer[1] = LF;
+            chunk_start += 2;
+            r->remaining = -2;
+        }
+        r->read_length += chunk_start;
+        return chunk_start;
+    }
+                                /* When REQUEST_CHUNKED_PASS, we     */
+    if (r->remaining == -2) {   /* finished footers when last called */
+        r->remaining = 0;       /* so now we must signal EOF         */
+        return 0;
+    }
+
+    /* Otherwise, we are in the midst of reading a chunk of data */
+
+    len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
+
+    len_read = ap_bread(r->connection->client, buffer, len_to_read);
+    if (len_read <= 0) {
+        r->connection->keepalive = -1;
+        return -1;
+    }
+
+    r->remaining -= len_read;
+
+    if (r->remaining == 0) {    /* End of chunk, get trailing CRLF */
+        if ((c = ap_bgetc(r->connection->client)) == CR) {
+            c = ap_bgetc(r->connection->client);
+        }
+        if (c != LF) {
+            r->connection->keepalive = -1;
+            return -1;
+        }
+        if (r->read_body == REQUEST_CHUNKED_PASS) {
+            buffer[len_read++] = CR;
+            buffer[len_read++] = LF;
+        }
+    }
+    r->read_length += (chunk_start + len_read);
+
+    return (chunk_start + len_read);
+}
+
+/* In HTTP/1.1, any method can have a body.  However, most GET handlers
+ * wouldn't know what to do with a request body if they received one.
+ * This helper routine tests for and reads any message body in the request,
+ * simply discarding whatever it receives.  We need to do this because
+ * failing to read the request body would cause it to be interpreted
+ * as the next request on a persistent connection.
+ *
+ * Since we return an error status if the request is malformed, this
+ * routine should be called at the beginning of a no-body handler, e.g.,
+ *
+ *    if ((retval = ap_discard_request_body(r)) != OK)
+ *        return retval;
+ */
+API_EXPORT(int) ap_discard_request_body(request_rec *r)
+{
+    int rv;
+
+    if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_PASS)))
+        return rv;
+
+    /* In order to avoid sending 100 Continue when we already know the
+     * final response status, and yet not kill the connection if there is
+     * no request body to be read, we need to duplicate the test from
+     * ap_should_client_block() here negated rather than call it directly.
+     */
+    if ((r->read_length == 0) && (r->read_chunked || (r->remaining > 0))) {
+        char dumpbuf[HUGE_STRING_LEN];
+
+        if (r->expecting_100) {
+            r->connection->keepalive = -1;
+            return OK;
+        }
+        ap_hard_timeout("reading request body", r);
+        while ((rv = ap_get_client_block(r, dumpbuf, HUGE_STRING_LEN)) > 0)
+            continue;
+        ap_kill_timeout(r);
+
+        if (rv < 0)
+            return HTTP_BAD_REQUEST;
+    }
+    return OK;
+}
+
+/*
+ * Send the body of a response to the client.
+ */
+API_EXPORT(long) ap_send_fd(FILE *f, request_rec *r)
+{
+    return ap_send_fd_length(f, r, -1);
+}
+
+API_EXPORT(long) ap_send_fd_length(FILE *f, request_rec *r, long length)
+{
+    char buf[IOBUFSIZE];
+    long total_bytes_sent = 0;
+    register int n, w, o, len;
+
+    if (length == 0)
+        return 0;
+
+    ap_soft_timeout("send body", r);
+
+    while (!r->connection->aborted) {
+        if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length)
+            len = length - total_bytes_sent;
+        else
+            len = IOBUFSIZE;
+
+        while ((n = fread(buf, sizeof(char), len, f)) < 1
+               && ferror(f) && errno == EINTR && !r->connection->aborted)
+            continue;
+
+        if (n < 1) {
+            break;
+        }
+        o = 0;
+
+        while (n && !r->connection->aborted) {
+            w = ap_bwrite(r->connection->client, &buf[o], n);
+            if (w > 0) {
+                ap_reset_timeout(r); /* reset timeout after successful write */
+                total_bytes_sent += w;
+                n -= w;
+                o += w;
+            }
+            else if (w < 0) {
+                if (!r->connection->aborted) {
+                    ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                     "client stopped connection before send body completed");
+                    ap_bsetflag(r->connection->client, B_EOUT, 1);
+                    r->connection->aborted = 1;
+                }
+                break;
+            }
+        }
+    }
+
+    ap_kill_timeout(r);
+    SET_BYTES_SENT(r);
+    return total_bytes_sent;
+}
+
+/*
+ * Send the body of a response to the client.
+ */
+API_EXPORT(long) ap_send_fb(BUFF *fb, request_rec *r)
+{
+    return ap_send_fb_length(fb, r, -1);
+}
+
+API_EXPORT(long) ap_send_fb_length(BUFF *fb, request_rec *r, long length)
+{
+    char buf[IOBUFSIZE];
+    long total_bytes_sent = 0;
+    register int n, w, o, len, fd;
+    fd_set fds;
+
+    if (length == 0)
+        return 0;
+
+    /* Make fb unbuffered and non-blocking */
+    ap_bsetflag(fb, B_RD, 0);
+#ifndef TPF    
+    ap_bnonblock(fb, B_RD);
+#endif
+    fd = ap_bfileno(fb, B_RD);
+#ifdef CHECK_FD_SETSIZE
+    if (fd >= FD_SETSIZE) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+           "send body: filedescriptor (%u) larger than FD_SETSIZE (%u) "
+           "found, you probably need to rebuild Apache with a "
+           "larger FD_SETSIZE", fd, FD_SETSIZE);
+       return 0;
+    }
+#endif
+
+    ap_soft_timeout("send body", r);
+
+    FD_ZERO(&fds);
+    while (!r->connection->aborted) {
+#ifdef NDELAY_PIPE_RETURNS_ZERO
+       /* Contributed by dwd@bell-labs.com for UTS 2.1.2, where the fcntl */
+       /*   O_NDELAY flag causes read to return 0 when there's nothing */
+       /*   available when reading from a pipe.  That makes it tricky */
+       /*   to detect end-of-file :-(.  This stupid bug is even documented */
+       /*   in the read(2) man page where it says that everything but */
+       /*   pipes return -1 and EAGAIN.  That makes it a feature, right? */
+       int afterselect = 0;
+#endif
+        if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length)
+            len = length - total_bytes_sent;
+        else
+            len = IOBUFSIZE;
+
+        do {
+            n = ap_bread(fb, buf, len);
+#ifdef NDELAY_PIPE_RETURNS_ZERO
+           if ((n > 0) || (n == 0 && afterselect))
+               break;
+#else
+            if (n >= 0)
+                break;
+#endif
+            if (r->connection->aborted)
+                break;
+            if (n < 0 && errno != EAGAIN)
+                break;
+
+            /* we need to block, so flush the output first */
+            if (ap_bflush(r->connection->client) < 0) {
+                ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                    "client stopped connection before send body completed");
+                ap_bsetflag(r->connection->client, B_EOUT, 1);
+                r->connection->aborted = 1;
+                break;
+            }
+            FD_SET(fd, &fds);
+            /*
+             * we don't care what select says, we might as well loop back
+             * around and try another read
+             */
+            ap_select(fd + 1, &fds, NULL, NULL, NULL);
+#ifdef NDELAY_PIPE_RETURNS_ZERO
+           afterselect = 1;
+#endif
+        } while (!r->connection->aborted);
+
+        if (n < 1 || r->connection->aborted) {
+            break;
+        }
+        o = 0;
+
+        while (n && !r->connection->aborted) {
+            w = ap_bwrite(r->connection->client, &buf[o], n);
+            if (w > 0) {
+                ap_reset_timeout(r); /* reset timeout after successful write */
+                total_bytes_sent += w;
+                n -= w;
+                o += w;
+            }
+            else if (w < 0) {
+                if (!r->connection->aborted) {
+                    ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                       "client stopped connection before send body completed");
+                    ap_bsetflag(r->connection->client, B_EOUT, 1);
+                    r->connection->aborted = 1;
+                }
+                break;
+            }
+        }
+    }
+
+    ap_kill_timeout(r);
+    SET_BYTES_SENT(r);
+    return total_bytes_sent;
+}
+
+
+
+/* The code writes MMAP_SEGMENT_SIZE bytes at a time.  This is due to Apache's
+ * timeout model, which is a timeout per-write rather than a time for the
+ * entire transaction to complete.  Essentially this should be small enough
+ * so that in one Timeout period, your slowest clients should be reasonably
+ * able to receive this many bytes.
+ *
+ * To take advantage of zero-copy TCP under Solaris 2.6 this should be a
+ * multiple of 16k.  (And you need a SunATM2.0 network card.)
+ */
+#ifndef MMAP_SEGMENT_SIZE
+#define MMAP_SEGMENT_SIZE       32768
+#endif
+
+/* send data from an in-memory buffer */
+API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset,
+                             size_t length)
+{
+    size_t total_bytes_sent = 0;
+    int n, w;
+
+    if (length == 0)
+        return 0;
+
+    ap_soft_timeout("send mmap", r);
+
+    length += offset;
+    while (!r->connection->aborted && offset < length) {
+        if (length - offset > MMAP_SEGMENT_SIZE) {
+            n = MMAP_SEGMENT_SIZE;
+        }
+        else {
+            n = length - offset;
+        }
+
+        while (n && !r->connection->aborted) {
+            w = ap_bwrite(r->connection->client, (char *) mm + offset, n);
+            if (w > 0) {
+                ap_reset_timeout(r); /* reset timeout after successful write */
+                total_bytes_sent += w;
+                n -= w;
+                offset += w;
+            }
+            else if (w < 0) {
+                if (!r->connection->aborted) {
+                    ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                       "client stopped connection before send mmap completed");
+                    ap_bsetflag(r->connection->client, B_EOUT, 1);
+                    r->connection->aborted = 1;
+                }
+                break;
+            }
+        }
+    }
+
+    ap_kill_timeout(r);
+    SET_BYTES_SENT(r);
+    return total_bytes_sent;
+}
+
+API_EXPORT(int) ap_rputc(int c, request_rec *r)
+{
+    if (r->connection->aborted)
+        return EOF;
+
+    if (ap_bputc(c, r->connection->client) < 0) {
+        if (!r->connection->aborted) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                "client stopped connection before rputc completed");
+            ap_bsetflag(r->connection->client, B_EOUT, 1);
+            r->connection->aborted = 1;
+        }
+        return EOF;
+    }
+    SET_BYTES_SENT(r);
+    return c;
+}
+
+API_EXPORT(int) ap_rputs(const char *str, request_rec *r)
+{
+    int rcode;
+
+    if (r->connection->aborted)
+        return EOF;
+    
+    rcode = ap_bputs(str, r->connection->client);
+    if (rcode < 0) {
+        if (!r->connection->aborted) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                "client stopped connection before rputs completed");
+            ap_bsetflag(r->connection->client, B_EOUT, 1);
+            r->connection->aborted = 1;
+        }
+        return EOF;
+    }
+    SET_BYTES_SENT(r);
+    return rcode;
+}
+
+API_EXPORT(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
+{
+    int n;
+
+    if (r->connection->aborted)
+        return -1;
+
+    n = ap_bwrite(r->connection->client, buf, nbyte);
+    if (n < 0) {
+        if (!r->connection->aborted) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                "client stopped connection before rwrite completed");
+            ap_bsetflag(r->connection->client, B_EOUT, 1);
+            r->connection->aborted = 1;
+        }
+        return -1;
+    }
+    SET_BYTES_SENT(r);
+    return n;
+}
+
+API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list ap)
+{
+    int n;
+
+    if (r->connection->aborted)
+        return -1;
+
+    n = ap_vbprintf(r->connection->client, fmt, ap);
+
+    if (n < 0) {
+        if (!r->connection->aborted) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                "client stopped connection before vrprintf completed");
+            ap_bsetflag(r->connection->client, B_EOUT, 1);
+            r->connection->aborted = 1;
+        }
+        return -1;
+    }
+    SET_BYTES_SENT(r);
+    return n;
+}
+
+API_EXPORT(int) ap_rprintf(request_rec *r, const char *fmt,...)
+{
+    va_list vlist;
+    int n;
+
+    if (r->connection->aborted)
+        return -1;
+
+    va_start(vlist, fmt);
+    n = ap_vbprintf(r->connection->client, fmt, vlist);
+    va_end(vlist);
+
+    if (n < 0) {
+        if (!r->connection->aborted) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                "client stopped connection before rprintf completed");
+            ap_bsetflag(r->connection->client, B_EOUT, 1);
+            r->connection->aborted = 1;
+        }
+        return -1;
+    }
+    SET_BYTES_SENT(r);
+    return n;
+}
+
+API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r,...)
+{
+    va_list args;
+    int i, j, k;
+    const char *x;
+    BUFF *fb = r->connection->client;
+
+    if (r->connection->aborted)
+        return EOF;
+
+    va_start(args, r);
+    for (k = 0;;) {
+        x = va_arg(args, const char *);
+        if (x == NULL)
+            break;
+        j = strlen(x);
+        i = ap_bwrite(fb, x, j);
+        if (i != j) {
+            va_end(args);
+            if (!r->connection->aborted) {
+                ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                    "client stopped connection before rvputs completed");
+                ap_bsetflag(r->connection->client, B_EOUT, 1);
+                r->connection->aborted = 1;
+            }
+            return EOF;
+        }
+        k += i;
+    }
+    va_end(args);
+
+    SET_BYTES_SENT(r);
+    return k;
+}
+
+API_EXPORT(int) ap_rflush(request_rec *r)
+{
+    if (ap_bflush(r->connection->client) < 0) {
+        if (!r->connection->aborted) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+                "client stopped connection before rflush completed");
+            ap_bsetflag(r->connection->client, B_EOUT, 1);
+            r->connection->aborted = 1;
+        }
+        return EOF;
+    }
+    return 0;
+}
+
+/* We should have named this send_canned_response, since it is used for any
+ * response that can be generated by the server from the request record.
+ * This includes all 204 (no content), 3xx (redirect), 4xx (client error),
+ * and 5xx (server error) messages that have not been redirected to another
+ * handler via the ErrorDocument feature.
+ */
+void ap_send_error_response(request_rec *r, int recursive_error)
+{
+    int status = r->status;
+    int idx = ap_index_of_response(status);
+    char *custom_response;
+    const char *location = ap_table_get(r->headers_out, "Location");
+
+    /*
+     * It's possible that the Location field might be in r->err_headers_out
+     * instead of r->headers_out; use the latter if possible, else the
+     * former.
+     */
+    if (location == NULL) {
+       location = ap_table_get(r->err_headers_out, "Location");
+    }
+    /* We need to special-case the handling of 204 and 304 responses,
+     * since they have specific HTTP requirements and do not include a
+     * message body.  Note that being assbackwards here is not an option.
+     */
+    if (status == HTTP_NOT_MODIFIED) {
+        if (!ap_is_empty_table(r->err_headers_out))
+            r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out,
+                                               r->headers_out);
+        ap_hard_timeout("send 304", r);
+
+        ap_basic_http_header(r);
+        ap_set_keepalive(r);
+
+        ap_table_do((int (*)(void *, const char *, const char *)) ap_send_header_field,
+                    (void *) r, r->headers_out,
+                    "Connection",
+                    "Keep-Alive",
+                    "ETag",
+                    "Content-Location",
+                    "Expires",
+                    "Cache-Control",
+                    "Vary",
+                    "Warning",
+                    "WWW-Authenticate",
+                    "Proxy-Authenticate",
+                    NULL);
+
+        terminate_header(r->connection->client);
+
+        ap_kill_timeout(r);
+        return;
+    }
+
+    if (status == HTTP_NO_CONTENT) {
+        ap_send_http_header(r);
+        ap_finalize_request_protocol(r);
+        return;
+    }
+
+    if (!r->assbackwards) {
+        table *tmp = r->headers_out;
+
+        /* For all HTTP/1.x responses for which we generate the message,
+         * we need to avoid inheriting the "normal status" header fields
+         * that may have been set by the request handler before the
+         * error or redirect, except for Location on external redirects.
+         */
+        r->headers_out = r->err_headers_out;
+        r->err_headers_out = tmp;
+        ap_clear_table(r->err_headers_out);
+
+        if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
+            if ((location != NULL) && *location) {
+               ap_table_setn(r->headers_out, "Location", location);
+            }
+            else {
+                location = "";   /* avoids coredump when printing, below */
+            }
+        }
+
+        r->content_language = NULL;
+        r->content_languages = NULL;
+        r->content_encoding = NULL;
+        r->clength = 0;
+        r->content_type = "text/html";
+
+        if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED))
+            ap_table_setn(r->headers_out, "Allow", make_allow(r));
+
+        ap_send_http_header(r);
+
+        if (r->header_only) {
+            ap_finalize_request_protocol(r);
+            ap_rflush(r);
+            return;
+        }
+    }
+
+    ap_hard_timeout("send error body", r);
+
+    if ((custom_response = ap_response_code_string(r, idx))) {
+        /*
+         * We have a custom response output. This should only be
+         * a text-string to write back. But if the ErrorDocument
+         * was a local redirect and the requested resource failed
+         * for any reason, the custom_response will still hold the
+         * redirect URL. We don't really want to output this URL
+         * as a text message, so first check the custom response
+         * string to ensure that it is a text-string (using the
+         * same test used in ap_die(), i.e. does it start with a ").
+         * If it doesn't, we've got a recursive error, so find
+         * the original error and output that as well.
+         */
+        if (custom_response[0] == '\"') {
+            ap_rputs(custom_response + 1, r);
+            ap_kill_timeout(r);
+            ap_finalize_request_protocol(r);
+            ap_rflush(r);
+            return;
+        }
+        /*
+         * Redirect failed, so get back the original error
+         */
+        while (r->prev && (r->prev->status != HTTP_OK))
+            r = r->prev;
+    }
+    {
+        const char *title = status_lines[idx];
+        const char *h1;
+        const char *error_notes;
+
+        /* Accept a status_line set by a module, but only if it begins
+         * with the 3 digit status code
+         */
+        if (r->status_line != NULL
+            && strlen(r->status_line) > 4       /* long enough */
+            && ap_isdigit(r->status_line[0])
+            && ap_isdigit(r->status_line[1])
+            && ap_isdigit(r->status_line[2])
+            && ap_isspace(r->status_line[3])
+            && ap_isalnum(r->status_line[4])) {
+            title = r->status_line;
+        }
+
+        /* folks decided they didn't want the error code in the H1 text */
+        h1 = &title[4];
+
+        ap_rvputs(r,
+                  DOCTYPE_HTML_2_0
+                  "<HTML><HEAD>\n<TITLE>", title,
+                  "</TITLE>\n</HEAD><BODY>\n<H1>", h1, "</H1>\n",
+                  NULL);
+
+       switch (status) {
+       case HTTP_MOVED_PERMANENTLY:
+       case HTTP_MOVED_TEMPORARILY:
+       case HTTP_TEMPORARY_REDIRECT:
+           ap_rvputs(r, "The document has moved <A HREF=\"",
+                     ap_escape_html(r->pool, location), "\">here</A>.<P>\n",
+                     NULL);
+           break;
+       case HTTP_SEE_OTHER:
+           ap_rvputs(r, "The answer to your request is located <A HREF=\"",
+                     ap_escape_html(r->pool, location), "\">here</A>.<P>\n",
+                     NULL);
+           break;
+       case HTTP_USE_PROXY:
+           ap_rvputs(r, "This resource is only accessible "
+                     "through the proxy\n",
+                     ap_escape_html(r->pool, location),
+                     "<BR>\nYou will need to ",
+                     "configure your client to use that proxy.<P>\n", NULL);
+           break;
+       case HTTP_PROXY_AUTHENTICATION_REQUIRED:
+       case AUTH_REQUIRED:
+           ap_rputs("This server could not verify that you\n"
+                    "are authorized to access the document\n"
+                    "requested.  Either you supplied the wrong\n"
+                    "credentials (e.g., bad password), or your\n"
+                    "browser doesn't understand how to supply\n"
+                    "the credentials required.<P>\n", r);
+           break;
+       case BAD_REQUEST:
+           ap_rputs("Your browser sent a request that "
+                    "this server could not understand.<P>\n", r);
+           if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+               ap_rvputs(r, error_notes, "<P>\n", NULL);
+           }
+           break;
+       case HTTP_FORBIDDEN:
+           ap_rvputs(r, "You don't have permission to access ",
+                     ap_escape_html(r->pool, r->uri),
+                     "\non this server.<P>\n", NULL);
+           break;
+       case NOT_FOUND:
+           ap_rvputs(r, "The requested URL ",
+                     ap_escape_html(r->pool, r->uri),
+                     " was not found on this server.<P>\n", NULL);
+           break;
+       case METHOD_NOT_ALLOWED:
+           ap_rvputs(r, "The requested method ", r->method,
+                     " is not allowed "
+                     "for the URL ", ap_escape_html(r->pool, r->uri),
+                     ".<P>\n", NULL);
+           break;
+       case NOT_ACCEPTABLE:
+           ap_rvputs(r,
+                     "An appropriate representation of the "
+                     "requested resource ",
+                     ap_escape_html(r->pool, r->uri),
+                     " could not be found on this server.<P>\n", NULL);
+           /* fall through */
+       case MULTIPLE_CHOICES:
+           {
+               const char *list;
+               if ((list = ap_table_get(r->notes, "variant-list")))
+                   ap_rputs(list, r);
+           }
+           break;
+       case LENGTH_REQUIRED:
+           ap_rvputs(r, "A request of the requested method ", r->method,
+                     " requires a valid Content-length.<P>\n", NULL);
+           if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+               ap_rvputs(r, error_notes, "<P>\n", NULL);
+           }
+           break;
+       case PRECONDITION_FAILED:
+           ap_rvputs(r, "The precondition on the request for the URL ",
+                     ap_escape_html(r->pool, r->uri),
+                     " evaluated to false.<P>\n", NULL);
+           break;
+       case HTTP_NOT_IMPLEMENTED:
+           ap_rvputs(r, ap_escape_html(r->pool, r->method), " to ",
+                     ap_escape_html(r->pool, r->uri),
+                     " not supported.<P>\n", NULL);
+           if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+               ap_rvputs(r, error_notes, "<P>\n", NULL);
+           }
+           break;
+       case BAD_GATEWAY:
+           ap_rputs("The proxy server received an invalid\015\012"
+                    "response from an upstream server.<P>\015\012", r);
+           if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+               ap_rvputs(r, error_notes, "<P>\n", NULL);
+           }
+           break;
+       case VARIANT_ALSO_VARIES:
+           ap_rvputs(r, "A variant for the requested resource\n<PRE>\n",
+                     ap_escape_html(r->pool, r->uri),
+                     "\n</PRE>\nis itself a negotiable resource. "
+                     "This indicates a configuration error.<P>\n", NULL);
+           break;
+       case HTTP_REQUEST_TIME_OUT:
+           ap_rputs("I'm tired of waiting for your request.\n", r);
+           break;
+       case HTTP_GONE:
+           ap_rvputs(r, "The requested resource<BR>",
+                     ap_escape_html(r->pool, r->uri),
+                     "<BR>\nis no longer available on this server ",
+                     "and there is no forwarding address.\n",
+                     "Please remove all references to this resource.\n",
+                     NULL);
+           break;
+       case HTTP_REQUEST_ENTITY_TOO_LARGE:
+           ap_rvputs(r, "The requested resource<BR>",
+                     ap_escape_html(r->pool, r->uri), "<BR>\n",
+                     "does not allow request data with ", r->method,
+                     " requests, or the amount of data provided in\n",
+                     "the request exceeds the capacity limit.\n", NULL);
+           break;
+       case HTTP_REQUEST_URI_TOO_LARGE:
+           ap_rputs("The requested URL's length exceeds the capacity\n"
+                    "limit for this server.<P>\n", r);
+           if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+               ap_rvputs(r, error_notes, "<P>\n", NULL);
+           }
+           break;
+       case HTTP_UNSUPPORTED_MEDIA_TYPE:
+           ap_rputs("The supplied request data is not in a format\n"
+                    "acceptable for processing by this resource.\n", r);
+           break;
+       case HTTP_RANGE_NOT_SATISFIABLE:
+           ap_rputs("None of the range-specifier values in the Range\n"
+                    "request-header field overlap the current extent\n"
+                    "of the selected resource.\n", r);
+           break;
+       case HTTP_EXPECTATION_FAILED:
+           ap_rvputs(r, "The expectation given in the Expect request-header"
+                     "\nfield could not be met by this server.<P>\n"
+                     "The client sent<PRE>\n    Expect: ",
+                     ap_table_get(r->headers_in, "Expect"), "\n</PRE>\n"
+                     "but we only allow the 100-continue expectation.\n",
+                     NULL);
+           break;
+       case HTTP_UNPROCESSABLE_ENTITY:
+           ap_rputs("The server understands the media type of the\n"
+                    "request entity, but was unable to process the\n"
+                    "contained instructions.\n", r);
+           break;
+       case HTTP_LOCKED:
+           ap_rputs("The requested resource is currently locked.\n"
+                    "The lock must be released or proper identification\n"
+                    "given before the method can be applied.\n", r);
+           break;
+       case HTTP_FAILED_DEPENDENCY:
+           ap_rputs("The method could not be performed on the resource\n"
+                    "because the requested action depended on another\n"
+                    "action and that other action failed.\n", r);
+           break;
+       case HTTP_INSUFFICIENT_STORAGE:
+           ap_rputs("The method could not be performed on the resource\n"
+                    "because the server is unable to store the\n"
+                    "representation needed to successfully complete the\n"
+                    "request.  There is insufficient free space left in\n"
+                    "your storage allocation.\n", r);
+           break;
+       case HTTP_SERVICE_UNAVAILABLE:
+           ap_rputs("The server is temporarily unable to service your\n"
+                    "request due to maintenance downtime or capacity\n"
+                    "problems. Please try again later.\n", r);
+           break;
+       case HTTP_GATEWAY_TIME_OUT:
+           ap_rputs("The proxy server did not receive a timely response\n"
+                    "from the upstream server.\n", r);
+           break;
+       case HTTP_NOT_EXTENDED:
+           ap_rputs("A mandatory extension policy in the request is not\n"
+                    "accepted by the server for this resource.\n", r);
+           break;
+       default:            /* HTTP_INTERNAL_SERVER_ERROR */
+           /*
+            * This comparison to expose error-notes could be modified to
+            * use a configuration directive and export based on that 
+            * directive.  For now "*" is used to designate an error-notes
+            * that is totally safe for any user to see (ie lacks paths,
+            * database passwords, etc.)
+            */
+           if (((error_notes = ap_table_get(r->notes, "error-notes")) != NULL)
+               && (h1 = ap_table_get(r->notes, "verbose-error-to")) != NULL
+               && (strcmp(h1, "*") == 0)) {
+               ap_rvputs(r, error_notes, "<P>\n", NULL);
+           }
+           else {
+               ap_rvputs(r, "The server encountered an internal error or\n"
+                    "misconfiguration and was unable to complete\n"
+                    "your request.<P>\n"
+                    "Please contact the server administrator,\n ",
+                    ap_escape_html(r->pool, r->server->server_admin),
+                    " and inform them of the time the error occurred,\n"
+                    "and anything you might have done that may have\n"
+                    "caused the error.<P>\n"
+                    "More information about this error may be available\n"
+                    "in the server error log.<P>\n", NULL);
+           }
+        /*
+         * It would be nice to give the user the information they need to
+         * fix the problem directly since many users don't have access to
+         * the error_log (think University sites) even though they can easily
+         * get this error by misconfiguring an htaccess file.  However, the
+         * error notes tend to include the real file pathname in this case,
+         * which some people consider to be a breach of privacy.  Until we
+         * can figure out a way to remove the pathname, leave this commented.
+         *
+         * if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+         *     ap_rvputs(r, error_notes, "<P>\n", NULL);
+         * }
+         */
+           break;
+       }
+
+        if (recursive_error) {
+            ap_rvputs(r, "<P>Additionally, a ",
+                      status_lines[ap_index_of_response(recursive_error)],
+                      "\nerror was encountered while trying to use an "
+                      "ErrorDocument to handle the request.\n", NULL);
+        }
+        ap_rputs(ap_psignature("<HR>\n", r), r);
+        ap_rputs("</BODY></HTML>\n", r);
+    }
+    ap_kill_timeout(r);
+    ap_finalize_request_protocol(r);
+    ap_rflush(r);
+}
diff --git a/modules/http/http_request.c b/modules/http/http_request.c
new file mode 100644 (file)
index 0000000..bc4d45f
--- /dev/null
@@ -0,0 +1,1374 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_request.c: functions to get and process requests
+ *
+ * Rob McCool 3/21/93
+ *
+ * Thoroughly revamped by rst for Apache.  NB this file reads
+ * best from the bottom up.
+ *
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_conf_globals.h" /* for ap_extended_status */
+#include "http_log.h"
+#include "http_main.h"
+#include "scoreboard.h"
+#include "fnmatch.h"
+
+/*****************************************************************
+ *
+ * Getting and checking directory configuration.  Also checks the
+ * FollowSymlinks and FollowSymOwner stuff, since this is really the
+ * only place that can happen (barring a new mid_dir_walk callout).
+ *
+ * We can't do it as an access_checker module function which gets
+ * called with the final per_dir_config, since we could have a directory
+ * with FollowSymLinks disabled, which contains a symlink to another
+ * with a .htaccess file which turns FollowSymLinks back on --- and
+ * access in such a case must be denied.  So, whatever it is that
+ * checks FollowSymLinks needs to know the state of the options as
+ * they change, all the way down.
+ */
+
+/*
+ * We don't want people able to serve up pipes, or unix sockets, or other
+ * scary things.  Note that symlink tests are performed later.
+ */
+static int check_safe_file(request_rec *r)
+{
+    if (r->finfo.st_mode == 0         /* doesn't exist */
+        || S_ISDIR(r->finfo.st_mode)
+        || S_ISREG(r->finfo.st_mode)
+        || S_ISLNK(r->finfo.st_mode)) {
+        return OK;
+    }
+    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                "object is not a file, directory or symlink: %s",
+                r->filename);
+    return HTTP_FORBIDDEN;
+}
+
+
+static int check_symlinks(char *d, int opts)
+{
+#if defined(OS2) || defined(WIN32)
+    /* OS/2 doesn't have symlinks */
+    return OK;
+#else
+    struct stat lfi, fi;
+    char *lastp;
+    int res;
+
+    if (opts & OPT_SYM_LINKS)
+        return OK;
+
+    /*
+     * Strip trailing '/', if any, off what we're checking; trailing slashes
+     * make some systems follow symlinks to directories even in lstat().
+     * After we've done the lstat, put it back.  Also, don't bother checking
+     * '/' at all...
+     * 
+     * Note that we don't have to worry about multiple slashes here because of
+     * no2slash() below...
+     */
+
+    lastp = d + strlen(d) - 1;
+    if (lastp == d)
+        return OK;              /* Root directory, '/' */
+
+    if (*lastp == '/')
+        *lastp = '\0';
+    else
+        lastp = NULL;
+
+    res = lstat(d, &lfi);
+
+    if (lastp)
+        *lastp = '/';
+
+    /*
+     * Note that we don't reject accesses to nonexistent files (multiviews or
+     * the like may cons up a way to run the transaction anyway)...
+     */
+
+    if (!(res >= 0) || !S_ISLNK(lfi.st_mode))
+        return OK;
+
+    /* OK, it's a symlink.  May still be OK with OPT_SYM_OWNER */
+
+    if (!(opts & OPT_SYM_OWNER))
+        return HTTP_FORBIDDEN;
+
+    if (stat(d, &fi) < 0)
+        return HTTP_FORBIDDEN;
+
+    return (fi.st_uid == lfi.st_uid) ? OK : HTTP_FORBIDDEN;
+
+#endif
+}
+
+/* Dealing with the file system to get PATH_INFO
+ */
+static int get_path_info(request_rec *r)
+{
+    char *cp;
+    char *path = r->filename;
+    char *end = &path[strlen(path)];
+    char *last_cp = NULL;
+    int rv;
+#ifdef HAVE_DRIVE_LETTERS
+    char bStripSlash=1;
+#endif
+
+    if (r->finfo.st_mode) {
+       /* assume path_info already set */
+       return OK;
+    }
+
+#ifdef HAVE_DRIVE_LETTERS
+    /* If the directory is x:\, then we don't want to strip
+     * the trailing slash since x: is not a valid directory.
+     */
+    if (strlen(path) == 3 && path[1] == ':' && path[2] == '/')
+        bStripSlash = 0;
+
+
+    /* If UNC name == //machine/share/, do not 
+     * advance over the trailing slash.  Any other
+     * UNC name is OK to strip the slash.
+     */
+    cp = end;
+    if (strlen(path) > 2 && path[0] == '/' && path[1] == '/' && 
+        path[2] != '/' && cp[-1] == '/') {
+        char *p;
+        int iCount=0;
+        p = path;
+        while (p = strchr(p,'/')) {
+            p++;
+            iCount++;
+        }
+    
+        if (iCount == 4)
+            bStripSlash = 0;
+    }
+
+    if (bStripSlash)
+#endif
+        /* Advance over trailing slashes ... NOT part of filename 
+         * if file is not a UNC name (Win32 only).
+         */
+        for (cp = end; cp > path && cp[-1] == '/'; --cp)
+            continue;
+
+
+    while (cp > path) {
+
+        /* See if the pathname ending here exists... */
+
+        *cp = '\0';
+
+        /* We must not stat() filenames that may cause os-specific system
+         * problems, such as "/file/aux" on DOS-abused filesystems.
+         * So pretend that they do not exist by returning an ENOENT error.
+         * This will force us to drop that part of the path and keep
+         * looking back for a "real" file that exists, while still allowing
+         * the "invalid" path parts within the PATH_INFO.
+         */
+        if (!ap_os_is_filename_valid(path)) {
+            errno = ENOENT;
+            rv = -1;
+        }
+        else {
+            errno = 0;
+            rv = stat(path, &r->finfo);
+        }
+
+        if (cp != end)
+            *cp = '/';
+
+        if (!rv) {
+
+            /*
+             * Aha!  Found something.  If it was a directory, we will search
+             * contents of that directory for a multi_match, so the PATH_INFO
+             * argument starts with the component after that.
+             */
+
+            if (S_ISDIR(r->finfo.st_mode) && last_cp) {
+                r->finfo.st_mode = 0;   /* No such file... */
+                cp = last_cp;
+            }
+
+            r->path_info = ap_pstrdup(r->pool, cp);
+            *cp = '\0';
+            return OK;
+        }
+       /* must set this to zero, some stat()s may have corrupted it
+        * even if they returned an error.
+        */
+       r->finfo.st_mode = 0;
+#if defined(ENOENT) && defined(ENOTDIR)
+        if (errno == ENOENT || errno == ENOTDIR) {
+            last_cp = cp;
+
+            while (--cp > path && *cp != '/')
+                continue;
+
+            while (cp > path && cp[-1] == '/')
+                --cp;
+        }
+        else {
+#if defined(EACCES)
+            if (errno != EACCES)
+#endif
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                            "access to %s failed", r->uri);
+            return HTTP_FORBIDDEN;
+        }
+#else
+#error ENOENT || ENOTDIR not defined; please see the
+#error comments at this line in the source for a workaround.
+        /*
+         * If ENOENT || ENOTDIR is not defined in one of the your OS's
+         * include files, Apache does not know how to check to see why the
+         * stat() of the index file failed; there are cases where it can fail
+         * even though the file exists.  This means that it is possible for
+         * someone to get a directory listing of a directory even though
+         * there is an index (eg. index.html) file in it.  If you do not have
+         * a problem with this, delete the above #error lines and start the
+         * compile again.  If you need to do this, please submit a bug report
+         * from http://www.apache.org/bug_report.html letting us know that
+         * you needed to do this.  Please be sure to include the operating
+         * system you are using.
+         */
+       last_cp = cp;
+
+       while (--cp > path && *cp != '/')
+           continue;
+
+       while (cp > path && cp[-1] == '/')
+           --cp;
+#endif  /* ENOENT && ENOTDIR */
+    }
+    return OK;
+}
+
+static int directory_walk(request_rec *r)
+{
+    core_server_config *sconf = ap_get_module_config(r->server->module_config,
+                                                  &core_module);
+    void *per_dir_defaults = r->server->lookup_defaults;
+    void **sec = (void **) sconf->sec->elts;
+    int num_sec = sconf->sec->nelts;
+    char *test_filename;
+    char *test_dirname;
+    int res;
+    unsigned i, num_dirs, iStart;
+    int j, test_filename_len;
+
+    /*
+     * Are we dealing with a file? If not, we can (hopefuly) safely assume we
+     * have a handler that doesn't require one, but for safety's sake, and so
+     * we have something find_types() can get something out of, fake one. But
+     * don't run through the directory entries.
+     */
+
+    if (r->filename == NULL) {
+        r->filename = ap_pstrdup(r->pool, r->uri);
+        r->finfo.st_mode = 0;   /* Not really a file... */
+        r->per_dir_config = per_dir_defaults;
+
+        return OK;
+    }
+
+    /*
+     * Go down the directory hierarchy.  Where we have to check for symlinks,
+     * do so.  Where a .htaccess file has permission to override anything,
+     * try to find one.  If either of these things fails, we could poke
+     * around, see why, and adjust the lookup_rec accordingly --- this might
+     * save us a call to get_path_info (with the attendant stat()s); however,
+     * for the moment, that's not worth the trouble.
+     *
+     * Fake filenames (i.e. proxy:) only match Directory sections.
+     */
+
+    if (!ap_os_is_path_absolute(r->filename))
+    {
+        void *this_conf, *entry_config;
+        core_dir_config *entry_core;
+        char *entry_dir;
+
+        for (j = 0; j < num_sec; ++j) {
+
+            entry_config = sec[j];
+
+            entry_core = (core_dir_config *)
+                ap_get_module_config(entry_config, &core_module);
+            entry_dir = entry_core->d;
+
+            this_conf = NULL;
+            if (entry_core->r) {
+                if (!ap_regexec(entry_core->r, r->filename, 0, NULL, 0))
+                    this_conf = entry_config;
+            }
+            else if (entry_core->d_is_fnmatch) {
+                if (!ap_fnmatch(entry_dir, r->filename, 0))
+                    this_conf = entry_config;
+            }
+            else if (!strncmp(r->filename, entry_dir, strlen(entry_dir)))
+                this_conf = entry_config;
+
+            if (this_conf)
+                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                                         per_dir_defaults,
+                                                         this_conf);
+        }
+
+        r->per_dir_config = per_dir_defaults;
+
+        return OK;
+    }
+
+    r->filename   = ap_os_case_canonical_filename(r->pool, r->filename);
+
+    res = get_path_info(r);
+    if (res != OK) {
+        return res;
+    }
+
+    r->filename   = ap_os_canonical_filename(r->pool, r->filename);
+
+    test_filename = ap_pstrdup(r->pool, r->filename);
+
+    ap_no2slash(test_filename);
+    num_dirs = ap_count_dirs(test_filename);
+
+    if (!ap_os_is_filename_valid(r->filename)) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                      "Filename is not valid: %s", r->filename);
+        return HTTP_FORBIDDEN;
+    }
+
+    if ((res = check_safe_file(r))) {
+        return res;
+    }
+
+    test_filename_len = strlen(test_filename);
+    if (test_filename[test_filename_len - 1] == '/')
+        --num_dirs;
+
+    if (S_ISDIR(r->finfo.st_mode))
+        ++num_dirs;
+
+    /*
+     * We will use test_dirname as scratch space while we build directory
+     * names during the walk.  Profiling shows directory_walk to be a busy
+     * function so we try to avoid allocating lots of extra memory here.
+     * We need 2 extra bytes, one for trailing \0 and one because
+     * make_dirstr_prefix will add potentially one extra /.
+     */
+    test_dirname = ap_palloc(r->pool, test_filename_len + 2);
+
+    iStart = 1;
+#ifdef WIN32
+    /* If the name is a UNC name, then do not walk through the
+     * machine and share name (e.g. \\machine\share\)
+     */
+    if (num_dirs > 3 && test_filename[0] == '/' && test_filename[1] == '/')
+        iStart = 4;
+#endif
+
+    /* j keeps track of which section we're on, see core_reorder_directories */
+    j = 0;
+    for (i = iStart; i <= num_dirs; ++i) {
+        int overrides_here;
+        core_dir_config *core_dir = (core_dir_config *)
+            ap_get_module_config(per_dir_defaults, &core_module);
+
+        /*
+         * XXX: this could be made faster by only copying the next component
+         * rather than copying the entire thing all over.
+         */
+        ap_make_dirstr_prefix(test_dirname, test_filename, i);
+
+        /*
+         * Do symlink checks first, because they are done with the
+         * permissions appropriate to the *parent* directory...
+         */
+
+        if ((res = check_symlinks(test_dirname, core_dir->opts))) {
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                        "Symbolic link not allowed: %s", test_dirname);
+            return res;
+        }
+
+        /*
+         * Begin *this* level by looking for matching <Directory> sections
+         * from access.conf.
+         */
+
+        for (; j < num_sec; ++j) {
+            void *entry_config = sec[j];
+            core_dir_config *entry_core;
+            char *entry_dir;
+            void *this_conf;
+
+            entry_core = (core_dir_config *)
+                         ap_get_module_config(entry_config, &core_module);
+            entry_dir = entry_core->d;
+
+            if (entry_core->r
+               || !ap_os_is_path_absolute(entry_dir)
+                || entry_core->d_components > i)
+                break;
+
+            this_conf = NULL;
+            if (entry_core->d_is_fnmatch) {
+                if (!ap_fnmatch(entry_dir, test_dirname, FNM_PATHNAME)) {
+                    this_conf = entry_config;
+                }
+            }
+            else if (!strcmp(test_dirname, entry_dir))
+                this_conf = entry_config;
+
+            if (this_conf) {
+                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                                         per_dir_defaults,
+                                                         this_conf);
+                core_dir = (core_dir_config *)
+                           ap_get_module_config(per_dir_defaults, &core_module);
+            }
+        }
+        overrides_here = core_dir->override;
+
+        /* If .htaccess files are enabled, check for one. */
+
+        if (overrides_here) {
+            void *htaccess_conf = NULL;
+
+            res = ap_parse_htaccess(&htaccess_conf, r, overrides_here,
+                                 ap_pstrdup(r->pool, test_dirname),
+                                 sconf->access_name);
+            if (res)
+                return res;
+
+            if (htaccess_conf) {
+                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                                           per_dir_defaults,
+                                                           htaccess_conf);
+               r->per_dir_config = per_dir_defaults;
+           }
+        }
+    }
+
+    /*
+     * There's two types of IS_SPECIAL sections (see http_core.c), and we've
+     * already handled the proxy:-style stuff.  Now we'll deal with the
+     * regexes.
+     */
+    for (; j < num_sec; ++j) {
+        void *entry_config = sec[j];
+        core_dir_config *entry_core;
+
+        entry_core = (core_dir_config *)
+                     ap_get_module_config(entry_config, &core_module);
+
+        if (entry_core->r) {
+            if (!ap_regexec(entry_core->r, test_dirname, 0, NULL, REG_NOTEOL)) {
+                per_dir_defaults =
+                    ap_merge_per_dir_configs(r->pool, per_dir_defaults,
+                                          entry_config);
+            }
+        }
+    }
+    r->per_dir_config = per_dir_defaults;
+
+    /*
+     * Symlink permissions are determined by the parent.  If the request is
+     * for a directory then applying the symlink test here would use the
+     * permissions of the directory as opposed to its parent.  Consider a
+     * symlink pointing to a dir with a .htaccess disallowing symlinks.  If
+     * you access /symlink (or /symlink/) you would get a 403 without this
+     * S_ISDIR test.  But if you accessed /symlink/index.html, for example,
+     * you would *not* get the 403.
+     */
+    if (!S_ISDIR(r->finfo.st_mode)
+        && (res = check_symlinks(r->filename, ap_allow_options(r)))) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                    "Symbolic link not allowed: %s", r->filename);
+        return res;
+    }
+    return OK;                  /* Can only "fail" if access denied by the
+                                 * symlink goop. */
+}
+
+static int location_walk(request_rec *r)
+{
+    core_server_config *sconf = ap_get_module_config(r->server->module_config,
+                                                  &core_module);
+    void *per_dir_defaults = r->per_dir_config;
+    void **url = (void **) sconf->sec_url->elts;
+    int len, num_url = sconf->sec_url->nelts;
+    char *test_location;
+    void *this_conf, *entry_config;
+    core_dir_config *entry_core;
+    char *entry_url;
+    int j;
+
+    if (!num_url) {
+       return OK;
+    }
+
+    /* Location and LocationMatch differ on their behaviour w.r.t. multiple
+     * slashes.  Location matches multiple slashes with a single slash,
+     * LocationMatch doesn't.  An exception, for backwards brokenness is
+     * absoluteURIs... in which case neither match multiple slashes.
+     */
+    if (r->uri[0] != '/') {
+       test_location = r->uri;
+    }
+    else {
+       test_location = ap_pstrdup(r->pool, r->uri);
+       ap_no2slash(test_location);
+    }
+
+    /* Go through the location entries, and check for matches. */
+
+    /* we apply the directive sections in some order;
+     * should really try them with the most general first.
+     */
+    for (j = 0; j < num_url; ++j) {
+
+       entry_config = url[j];
+
+       entry_core = (core_dir_config *)
+           ap_get_module_config(entry_config, &core_module);
+       entry_url = entry_core->d;
+
+       len = strlen(entry_url);
+
+       this_conf = NULL;
+
+       if (entry_core->r) {
+           if (!ap_regexec(entry_core->r, r->uri, 0, NULL, 0))
+               this_conf = entry_config;
+       }
+       else if (entry_core->d_is_fnmatch) {
+           if (!ap_fnmatch(entry_url, test_location, FNM_PATHNAME)) {
+               this_conf = entry_config;
+           }
+       }
+       else if (!strncmp(test_location, entry_url, len) &&
+                   (entry_url[len - 1] == '/' ||
+               test_location[len] == '/' || test_location[len] == '\0'))
+           this_conf = entry_config;
+
+       if (this_conf)
+           per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                           per_dir_defaults, this_conf);
+    }
+    r->per_dir_config = per_dir_defaults;
+
+    return OK;
+}
+
+static int file_walk(request_rec *r)
+{
+    core_dir_config *conf = ap_get_module_config(r->per_dir_config, &core_module);
+    void *per_dir_defaults = r->per_dir_config;
+    void **file = (void **) conf->sec->elts;
+    int num_files = conf->sec->nelts;
+    char *test_file;
+
+    /* get the basename */
+    test_file = strrchr(r->filename, '/');
+    if (test_file == NULL) {
+       test_file = r->filename;
+    }
+    else {
+       ++test_file;
+    }
+
+    /* Go through the file entries, and check for matches. */
+
+    if (num_files) {
+        void *this_conf, *entry_config;
+        core_dir_config *entry_core;
+        char *entry_file;
+        int j;
+
+        /* we apply the directive sections in some order;
+         * should really try them with the most general first.
+         */
+        for (j = 0; j < num_files; ++j) {
+
+            entry_config = file[j];
+
+            entry_core = (core_dir_config *)
+                         ap_get_module_config(entry_config, &core_module);
+            entry_file = entry_core->d;
+
+            this_conf = NULL;
+
+            if (entry_core->r) {
+                if (!ap_regexec(entry_core->r, test_file, 0, NULL, 0))
+                    this_conf = entry_config;
+            }
+            else if (entry_core->d_is_fnmatch) {
+                if (!ap_fnmatch(entry_file, test_file, FNM_PATHNAME)) {
+                    this_conf = entry_config;
+                }
+            }
+            else if (!strcmp(test_file, entry_file)) {
+                this_conf = entry_config;
+           }
+
+            if (this_conf)
+                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                                         per_dir_defaults,
+                                                         this_conf);
+        }
+        r->per_dir_config = per_dir_defaults;
+    }
+    return OK;
+}
+
+/*****************************************************************
+ *
+ * The sub_request mechanism.
+ *
+ * Fns to look up a relative URI from, e.g., a map file or SSI document.
+ * These do all access checks, etc., but don't actually run the transaction
+ * ... use run_sub_req below for that.  Also, be sure to use destroy_sub_req
+ * as appropriate if you're likely to be creating more than a few of these.
+ * (An early Apache version didn't destroy the sub_reqs used in directory
+ * indexing.  The result, when indexing a directory with 800-odd files in
+ * it, was massively excessive storage allocation).
+ *
+ * Note more manipulation of protocol-specific vars in the request
+ * structure...
+ */
+
+static request_rec *make_sub_request(const request_rec *r)
+{
+    pool *rrp = ap_make_sub_pool(r->pool);
+    request_rec *rr = ap_pcalloc(rrp, sizeof(request_rec));
+
+    rr->pool = rrp;
+    return rr;
+}
+
+API_EXPORT(request_rec *) ap_sub_req_method_uri(const char *method,
+                                                const char *new_file,
+                                                const request_rec *r)
+{
+    request_rec *rnew;
+    int res;
+    char *udir;
+
+    rnew = make_sub_request(r);
+    rnew->hostname       = r->hostname;
+    rnew->request_time   = r->request_time;
+    rnew->connection     = r->connection;
+    rnew->server         = r->server;
+    rnew->request_config = ap_create_request_config(rnew->pool);
+    rnew->htaccess       = r->htaccess;
+    rnew->per_dir_config = r->server->lookup_defaults;
+
+    ap_set_sub_req_protocol(rnew, r);
+
+    /* would be nicer to pass "method" to ap_set_sub_req_protocol */
+    rnew->method = method;
+    rnew->method_number = ap_method_number_of(method);
+
+    if (new_file[0] == '/')
+        ap_parse_uri(rnew, new_file);
+    else {
+        udir = ap_make_dirstr_parent(rnew->pool, r->uri);
+        udir = ap_escape_uri(rnew->pool, udir);    /* re-escape it */
+        ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_file));
+    }
+
+    res = ap_unescape_url(rnew->uri);
+    if (res) {
+        rnew->status = res;
+        return rnew;
+    }
+
+    ap_getparents(rnew->uri);
+
+    if ((res = location_walk(rnew))) {
+        rnew->status = res;
+        return rnew;
+    }
+
+    res = ap_translate_name(rnew);
+    if (res) {
+        rnew->status = res;
+        return rnew;
+    }
+
+    /*
+     * We could be clever at this point, and avoid calling directory_walk,
+     * etc. However, we'd need to test that the old and new filenames contain
+     * the same directory components, so it would require duplicating the
+     * start of translate_name. Instead we rely on the cache of .htaccess
+     * results.
+     *
+     * NB: directory_walk() clears the per_dir_config, so we don't inherit
+     * from location_walk() above
+     */
+
+    if ((res = directory_walk(rnew))
+        || (res = file_walk(rnew))
+        || (res = location_walk(rnew))
+        || ((ap_satisfies(rnew) == SATISFY_ALL
+             || ap_satisfies(rnew) == SATISFY_NOSPEC)
+            ? ((res = ap_check_access(rnew))
+               || (ap_some_auth_required(rnew)
+                   && ((res = ap_check_user_id(rnew))
+                       || (res = ap_check_auth(rnew)))))
+            : ((res = ap_check_access(rnew))
+               && (!ap_some_auth_required(rnew)
+                   || ((res = ap_check_user_id(rnew))
+                       || (res = ap_check_auth(rnew)))))
+           )
+        || (res = ap_find_types(rnew))
+        || (res = ap_run_fixups(rnew))
+       ) {
+        rnew->status = res;
+    }
+    return rnew;
+}
+
+API_EXPORT(request_rec *) ap_sub_req_lookup_uri(const char *new_file,
+                                                const request_rec *r)
+{
+    return ap_sub_req_method_uri("GET", new_file, r);
+}
+
+API_EXPORT(request_rec *) ap_sub_req_lookup_file(const char *new_file,
+                                              const request_rec *r)
+{
+    request_rec *rnew;
+    int res;
+    char *fdir;
+
+    rnew = make_sub_request(r);
+    rnew->hostname       = r->hostname;
+    rnew->request_time   = r->request_time;
+    rnew->connection     = r->connection;
+    rnew->server         = r->server;
+    rnew->request_config = ap_create_request_config(rnew->pool);
+    rnew->htaccess       = r->htaccess;
+
+    ap_set_sub_req_protocol(rnew, r);
+    fdir = ap_make_dirstr_parent(rnew->pool, r->filename);
+
+    /*
+     * Check for a special case... if there are no '/' characters in new_file
+     * at all, then we are looking at a relative lookup in the same
+     * directory. That means we won't have to redo directory_walk, and we may
+     * not even have to redo access checks.
+     */
+
+    if (strchr(new_file, '/') == NULL) {
+        char *udir = ap_make_dirstr_parent(rnew->pool, r->uri);
+
+        rnew->uri = ap_make_full_path(rnew->pool, udir, new_file);
+        rnew->filename = ap_make_full_path(rnew->pool, fdir, new_file);
+        ap_parse_uri(rnew, rnew->uri);    /* fill in parsed_uri values */
+        if (stat(rnew->filename, &rnew->finfo) < 0) {
+            rnew->finfo.st_mode = 0;
+        }
+
+        if ((res = check_safe_file(rnew))) {
+            rnew->status = res;
+            return rnew;
+        }
+
+        rnew->per_dir_config = r->per_dir_config;
+
+        /*
+         * no matter what, if it's a subdirectory, we need to re-run
+         * directory_walk
+         */
+        if (S_ISDIR(rnew->finfo.st_mode)) {
+            res = directory_walk(rnew);
+            if (!res) {
+                res = file_walk(rnew);
+            }
+        }
+        else {
+            if ((res = check_symlinks(rnew->filename, ap_allow_options(rnew)))) {
+                ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, rnew,
+                            "Symbolic link not allowed: %s", rnew->filename);
+                rnew->status = res;
+                return rnew;
+            }
+            /*
+             * do a file_walk, if it doesn't change the per_dir_config then
+             * we know that we don't have to redo all the access checks
+             */
+            if ((res = file_walk(rnew))) {
+                rnew->status = res;
+                return rnew;
+            }
+            if (rnew->per_dir_config == r->per_dir_config) {
+                if ((res = ap_find_types(rnew)) || (res = ap_run_fixups(rnew))) {
+                    rnew->status = res;
+                }
+                return rnew;
+            }
+        }
+    }
+    else {
+       /* XXX: @@@: What should be done with the parsed_uri values? */
+       ap_parse_uri(rnew, new_file);   /* fill in parsed_uri values */
+        /*
+         * XXX: this should be set properly like it is in the same-dir case
+         * but it's actually sometimes to impossible to do it... because the
+         * file may not have a uri associated with it -djg
+         */
+        rnew->uri = "INTERNALLY GENERATED file-relative req";
+        rnew->filename = ((ap_os_is_path_absolute(new_file)) ?
+                          ap_pstrdup(rnew->pool, new_file) :
+                          ap_make_full_path(rnew->pool, fdir, new_file));
+        rnew->per_dir_config = r->server->lookup_defaults;
+        res = directory_walk(rnew);
+        if (!res) {
+            res = file_walk(rnew);
+        }
+    }
+
+    if (res
+        || ((ap_satisfies(rnew) == SATISFY_ALL
+             || ap_satisfies(rnew) == SATISFY_NOSPEC)
+            ? ((res = ap_check_access(rnew))
+               || (ap_some_auth_required(rnew)
+                   && ((res = ap_check_user_id(rnew))
+                       || (res = ap_check_auth(rnew)))))
+            : ((res = ap_check_access(rnew))
+               && (!ap_some_auth_required(rnew)
+                   || ((res = ap_check_user_id(rnew))
+                       || (res = ap_check_auth(rnew)))))
+           )
+        || (res = ap_find_types(rnew))
+        || (res = ap_run_fixups(rnew))
+       ) {
+        rnew->status = res;
+    }
+    return rnew;
+}
+
+API_EXPORT(int) ap_run_sub_req(request_rec *r)
+{
+#ifndef CHARSET_EBCDIC
+    int retval = ap_invoke_handler(r);
+#else /*CHARSET_EBCDIC*/
+    /* Save the EBCDIC conversion setting of the caller across subrequests */
+    int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII);
+    int retval  = ap_invoke_handler(r);
+    ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert);
+#endif /*CHARSET_EBCDIC*/
+    ap_finalize_sub_req_protocol(r);
+    return retval;
+}
+
+API_EXPORT(void) ap_destroy_sub_req(request_rec *r)
+{
+    /* Reclaim the space */
+    ap_destroy_pool(r->pool);
+}
+
+/*****************************************************************
+ *
+ * Mainline request processing...
+ */
+
+API_EXPORT(void) ap_die(int type, request_rec *r)
+{
+    int error_index = ap_index_of_response(type);
+    char *custom_response = ap_response_code_string(r, error_index);
+    int recursive_error = 0;
+
+    if (type == DONE) {
+        ap_finalize_request_protocol(r);
+        return;
+    }
+
+    /*
+     * The following takes care of Apache redirects to custom response URLs
+     * Note that if we are already dealing with the response to some other
+     * error condition, we just report on the original error, and give up on
+     * any attempt to handle the other thing "intelligently"...
+     */
+
+    if (r->status != HTTP_OK) {
+        recursive_error = type;
+
+        while (r->prev && (r->prev->status != HTTP_OK))
+            r = r->prev;        /* Get back to original error */
+
+        type = r->status;
+        custom_response = NULL; /* Do NOT retry the custom thing! */
+    }
+
+    r->status = type;
+
+    /*
+     * This test is done here so that none of the auth modules needs to know
+     * about proxy authentication.  They treat it like normal auth, and then
+     * we tweak the status.
+     */
+    if (r->status == AUTH_REQUIRED && r->proxyreq) {
+        r->status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
+    }
+
+    /*
+     * If we want to keep the connection, be sure that the request body
+     * (if any) has been read.
+     */
+    if ((r->status != HTTP_NOT_MODIFIED) && (r->status != HTTP_NO_CONTENT)
+        && !ap_status_drops_connection(r->status)
+        && r->connection && (r->connection->keepalive != -1)) {
+
+        (void) ap_discard_request_body(r);
+    }
+
+    /*
+     * Two types of custom redirects --- plain text, and URLs. Plain text has
+     * a leading '"', so the URL code, here, is triggered on its absence
+     */
+
+    if (custom_response && custom_response[0] != '"') {
+
+        if (ap_is_url(custom_response)) {
+            /*
+             * The URL isn't local, so lets drop through the rest of this
+             * apache code, and continue with the usual REDIRECT handler.
+             * But note that the client will ultimately see the wrong
+             * status...
+             */
+            r->status = REDIRECT;
+            ap_table_setn(r->headers_out, "Location", custom_response);
+        }
+        else if (custom_response[0] == '/') {
+            const char *error_notes;
+            r->no_local_copy = 1;       /* Do NOT send USE_LOCAL_COPY for
+                                         * error documents! */
+            /*
+             * This redirect needs to be a GET no matter what the original
+             * method was.
+             */
+            ap_table_setn(r->subprocess_env, "REQUEST_METHOD", r->method);
+
+           /*
+            * Provide a special method for modules to communicate
+            * more informative (than the plain canned) messages to us.
+            * Propagate them to ErrorDocuments via the ERROR_NOTES variable:
+            */
+            if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+               ap_table_setn(r->subprocess_env, "ERROR_NOTES", error_notes);
+           }
+            r->method = ap_pstrdup(r->pool, "GET");
+            r->method_number = M_GET;
+            ap_internal_redirect(custom_response, r);
+            return;
+        }
+        else {
+            /*
+             * Dumb user has given us a bad url to redirect to --- fake up
+             * dying with a recursive server error...
+             */
+            recursive_error = SERVER_ERROR;
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                        "Invalid error redirection directive: %s",
+                        custom_response);
+        }
+    }
+    ap_send_error_response(r, recursive_error);
+}
+
+static void decl_die(int status, char *phase, request_rec *r)
+{
+    if (status == DECLINED) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, r,
+                    "configuration error:  couldn't %s: %s", phase, r->uri);
+        ap_die(SERVER_ERROR, r);
+    }
+    else
+        ap_die(status, r);
+}
+
+API_EXPORT(int) ap_some_auth_required(request_rec *r)
+{
+    /* Is there a require line configured for the type of *this* req? */
+
+    const array_header *reqs_arr = ap_requires(r);
+    require_line *reqs;
+    int i;
+
+    if (!reqs_arr)
+        return 0;
+
+    reqs = (require_line *) reqs_arr->elts;
+
+    for (i = 0; i < reqs_arr->nelts; ++i)
+        if (reqs[i].method_mask & (1 << r->method_number))
+            return 1;
+
+    return 0;
+}
+
+static void process_request_internal(request_rec *r)
+{
+    int access_status;
+
+    /* Ignore embedded %2F's in path for proxy requests */
+    if (!r->proxyreq && r->parsed_uri.path) {
+       access_status = ap_unescape_url(r->parsed_uri.path);
+       if (access_status) {
+           ap_die(access_status, r);
+           return;
+       }
+    }
+
+    ap_getparents(r->uri);     /* OK --- shrinking transformations... */
+
+    if ((access_status = location_walk(r))) {
+        ap_die(access_status, r);
+        return;
+    }
+
+    if ((access_status = ap_translate_name(r))) {
+        decl_die(access_status, "translate", r);
+        return;
+    }
+
+    if (!r->proxyreq) {
+       /*
+        * We don't want TRACE to run through the normal handler set, we
+        * handle it specially.
+        */
+       if (r->method_number == M_TRACE) {
+           if ((access_status = ap_send_http_trace(r)))
+               ap_die(access_status, r);
+           else
+               ap_finalize_request_protocol(r);
+           return;
+       }
+    }
+
+    if (r->proto_num > HTTP_VERSION(1,0) && ap_table_get(r->subprocess_env, "downgrade-1.0")) {
+        r->proto_num = HTTP_VERSION(1,0);
+    }
+
+    /*
+     * NB: directory_walk() clears the per_dir_config, so we don't inherit
+     * from location_walk() above
+     */
+
+    if ((access_status = directory_walk(r))) {
+        ap_die(access_status, r);
+        return;
+    }
+
+    if ((access_status = file_walk(r))) {
+        ap_die(access_status, r);
+        return;
+    }
+
+    if ((access_status = location_walk(r))) {
+        ap_die(access_status, r);
+        return;
+    }
+
+    if ((access_status = ap_header_parse(r))) {
+        ap_die(access_status, r);
+        return;
+    }
+
+    switch (ap_satisfies(r)) {
+    case SATISFY_ALL:
+    case SATISFY_NOSPEC:
+        if ((access_status = ap_check_access(r)) != 0) {
+            decl_die(access_status, "check access", r);
+            return;
+        }
+        if (ap_some_auth_required(r)) {
+            if (((access_status = ap_check_user_id(r)) != 0) || !ap_auth_type(r)) {
+                decl_die(access_status, ap_auth_type(r)
+                   ? "check user.  No user file?"
+                   : "perform authentication. AuthType not set!", r);
+                return;
+            }
+            if (((access_status = ap_check_auth(r)) != 0) || !ap_auth_type(r)) {
+                decl_die(access_status, ap_auth_type(r)
+                   ? "check access.  No groups file?"
+                   : "perform authentication. AuthType not set!", r);
+                return;
+            }
+        }
+        break;
+    case SATISFY_ANY:
+        if (((access_status = ap_check_access(r)) != 0) || !ap_auth_type(r)) {
+            if (!ap_some_auth_required(r)) {
+                decl_die(access_status, ap_auth_type(r)
+                   ? "check access"
+                   : "perform authentication. AuthType not set!", r);
+                return;
+            }
+            if (((access_status = ap_check_user_id(r)) != 0) || !ap_auth_type(r)) {
+                decl_die(access_status, ap_auth_type(r)
+                   ? "check user.  No user file?"
+                   : "perform authentication. AuthType not set!", r);
+                return;
+            }
+            if (((access_status = ap_check_auth(r)) != 0) || !ap_auth_type(r)) {
+                decl_die(access_status, ap_auth_type(r)
+                   ? "check access.  No groups file?"
+                   : "perform authentication. AuthType not set!", r);
+                return;
+            }
+        }
+        break;
+    }
+
+    if (! (r->proxyreq 
+          && r->parsed_uri.scheme != NULL
+          && strcmp(r->parsed_uri.scheme, "http") == 0) ) {
+       if ((access_status = ap_find_types(r)) != 0) {
+           decl_die(access_status, "find types", r);
+           return;
+       }
+    }
+
+    if ((access_status = ap_run_fixups(r)) != 0) {
+        ap_die(access_status, r);
+        return;
+    }
+
+    if ((access_status = ap_invoke_handler(r)) != 0) {
+        ap_die(access_status, r);
+        return;
+    }
+
+    /* Take care of little things that need to happen when we're done */
+    ap_finalize_request_protocol(r);
+}
+
+void ap_process_request(request_rec *r)
+{
+    int old_stat;
+
+    if (ap_extended_status)
+       ap_time_process_request(r->connection->child_num, START_PREQUEST);
+
+    process_request_internal(r);
+
+    old_stat = ap_update_child_status(r->connection->child_num,
+                                   SERVER_BUSY_LOG, r);
+
+    /*
+     * We want to flush the last packet if this isn't a pipelining connection
+     * *before* we start into logging.  Suppose that the logging causes a DNS
+     * lookup to occur, which may have a high latency.  If we hold off on
+     * this packet, then it'll appear like the link is stalled when really
+     * it's the application that's stalled.
+     */
+    ap_bhalfduplex(r->connection->client);
+    ap_log_transaction(r);
+
+    (void) ap_update_child_status(r->connection->child_num, old_stat, r);
+    if (ap_extended_status)
+       ap_time_process_request(r->connection->child_num, STOP_PREQUEST);
+}
+
+static table *rename_original_env(pool *p, table *t)
+{
+    array_header *env_arr = ap_table_elts(t);
+    table_entry *elts = (table_entry *) env_arr->elts;
+    table *new = ap_make_table(p, env_arr->nalloc);
+    int i;
+
+    for (i = 0; i < env_arr->nelts; ++i) {
+        if (!elts[i].key)
+            continue;
+        ap_table_setn(new, ap_pstrcat(p, "REDIRECT_", elts[i].key, NULL),
+                  elts[i].val);
+    }
+
+    return new;
+}
+
+static request_rec *internal_internal_redirect(const char *new_uri, request_rec *r)
+{
+    int access_status;
+    request_rec *new = (request_rec *) ap_pcalloc(r->pool, sizeof(request_rec));
+
+    new->connection = r->connection;
+    new->server     = r->server;
+    new->pool       = r->pool;
+
+    /*
+     * A whole lot of this really ought to be shared with http_protocol.c...
+     * another missing cleanup.  It's particularly inappropriate to be
+     * setting header_only, etc., here.
+     */
+
+    new->method          = r->method;
+    new->method_number   = r->method_number;
+    ap_parse_uri(new, new_uri);
+    new->request_config = ap_create_request_config(r->pool);
+    new->per_dir_config = r->server->lookup_defaults;
+
+    new->prev = r;
+    r->next   = new;
+
+    /* Inherit the rest of the protocol info... */
+
+    new->the_request = r->the_request;
+
+    new->allowed         = r->allowed;
+
+    new->status          = r->status;
+    new->assbackwards    = r->assbackwards;
+    new->header_only     = r->header_only;
+    new->protocol        = r->protocol;
+    new->proto_num       = r->proto_num;
+    new->hostname        = r->hostname;
+    new->request_time    = r->request_time;
+    new->main            = r->main;
+
+    new->headers_in      = r->headers_in;
+    new->headers_out     = ap_make_table(r->pool, 12);
+    new->err_headers_out = r->err_headers_out;
+    new->subprocess_env  = rename_original_env(r->pool, r->subprocess_env);
+    new->notes           = ap_make_table(r->pool, 5);
+
+    new->htaccess        = r->htaccess;
+    new->no_cache        = r->no_cache;
+    new->expecting_100  = r->expecting_100;
+    new->no_local_copy   = r->no_local_copy;
+    new->read_length     = r->read_length;     /* We can only read it once */
+    new->vlist_validator = r->vlist_validator;
+
+    ap_table_setn(new->subprocess_env, "REDIRECT_STATUS",
+       ap_psprintf(r->pool, "%d", r->status));
+
+    /*
+     * XXX: hmm.  This is because mod_setenvif and mod_unique_id really need
+     * to do their thing on internal redirects as well.  Perhaps this is a
+     * misnamed function.
+     */
+    if ((access_status = ap_run_post_read_request(new))) {
+        ap_die(access_status, new);
+        return NULL;
+    }
+
+    return new;
+}
+
+API_EXPORT(void) ap_internal_redirect(const char *new_uri, request_rec *r)
+{
+    request_rec *new = internal_internal_redirect(new_uri, r);
+    process_request_internal(new);
+}
+
+/* This function is designed for things like actions or CGI scripts, when
+ * using AddHandler, and you want to preserve the content type across
+ * an internal redirect.
+ */
+API_EXPORT(void) ap_internal_redirect_handler(const char *new_uri, request_rec *r)
+{
+    request_rec *new = internal_internal_redirect(new_uri, r);
+    if (r->handler)
+        new->content_type = r->content_type;
+    process_request_internal(new);
+}
+
+/*
+ * Is it the initial main request, which we only get *once* per HTTP request?
+ */
+API_EXPORT(int) ap_is_initial_req(request_rec *r)
+{
+    return
+        (r->main == NULL)       /* otherwise, this is a sub-request */
+        &&
+        (r->prev == NULL);      /* otherwise, this is an internal redirect */
+}
+
+/*
+ * Function to set the r->mtime field to the specified value if it's later
+ * than what's already there.
+ */
+API_EXPORT(time_t) ap_update_mtime(request_rec *r, time_t dependency_mtime)
+{
+    if (r->mtime < dependency_mtime) {
+        r->mtime = dependency_mtime;
+    }
+    return r->mtime;
+}
diff --git a/modules/loggers/.indent.pro b/modules/loggers/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/modules/mappers/.indent.pro b/modules/mappers/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/modules/mappers/mod_rewrite.dsp b/modules/mappers/mod_rewrite.dsp
new file mode 100644 (file)
index 0000000..4a0ffd1
--- /dev/null
@@ -0,0 +1,117 @@
+# Microsoft Developer Studio Project File - Name="ApacheModuleRewrite" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleRewrite - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleRewrite.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleRewrite.mak"\
+ CFG="ApacheModuleRewrite - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ApacheModuleRewrite - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleRewrite - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ApacheModuleRewrite - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\ApacheModuleRewriteR"
+# PROP Intermediate_Dir ".\ApacheModuleRewriteR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "NO_DBM_REWRITEMAP" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "ApacheModuleRewrite - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\ApacheModuleRewriteD"
+# PROP Intermediate_Dir ".\ApacheModuleRewriteD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "NO_DBM_REWRITEMAP" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ApacheModuleRewrite - Win32 Release"
+# Name "ApacheModuleRewrite - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_rewrite.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\passwd.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/modules/mappers/mod_speling.dsp b/modules/mappers/mod_speling.dsp
new file mode 100644 (file)
index 0000000..499a72f
--- /dev/null
@@ -0,0 +1,113 @@
+# Microsoft Developer Studio Project File - Name="ApacheModuleSpeling" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleSpeling - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleSpeling.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleSpeling.mak"\
+ CFG="ApacheModuleSpeling - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ApacheModuleSpeling - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleSpeling - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ApacheModuleSpeling - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\ApacheModuleSpelingR"
+# PROP Intermediate_Dir ".\ApacheModuleSpelingR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "ApacheModuleSpeling - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\ApacheModuleSpelingD"
+# PROP Intermediate_Dir ".\ApacheModuleSpelingD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ApacheModuleSpeling - Win32 Release"
+# Name "ApacheModuleSpeling - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_speling.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/modules/metadata/.indent.pro b/modules/metadata/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/modules/metadata/mod_cern_meta.dsp b/modules/metadata/mod_cern_meta.dsp
new file mode 100644 (file)
index 0000000..3564c0e
--- /dev/null
@@ -0,0 +1,113 @@
+# Microsoft Developer Studio Project File - Name="ApacheModuleCERNMeta" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleCERNMeta - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleCERNMeta.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleCERNMeta.mak"\
+ CFG="ApacheModuleCERNMeta - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ApacheModuleCERNMeta - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleCERNMeta - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ApacheModuleCERNMeta - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\ApacheModuleCERNMetaR"
+# PROP Intermediate_Dir ".\ApacheModuleCERNMetaR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "ApacheModuleCERNMeta - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\ApacheModuleCERNMetaD"
+# PROP Intermediate_Dir ".\ApacheModuleCERNMetaD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ApacheModuleCERNMeta - Win32 Release"
+# Name "ApacheModuleCERNMeta - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_cern_meta.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/modules/metadata/mod_expires.dsp b/modules/metadata/mod_expires.dsp
new file mode 100644 (file)
index 0000000..0b2fe90
--- /dev/null
@@ -0,0 +1,113 @@
+# Microsoft Developer Studio Project File - Name="ApacheModuleExpires" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleExpires - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleExpires.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleExpires.mak"\
+ CFG="ApacheModuleExpires - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ApacheModuleExpires - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleExpires - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ApacheModuleExpires - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\ApacheModuleExpiresR"
+# PROP Intermediate_Dir ".\ApacheModuleExpiresR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "ApacheModuleExpires - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\ApacheModuleExpiresD"
+# PROP Intermediate_Dir ".\ApacheModuleExpiresD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ApacheModuleExpires - Win32 Release"
+# Name "ApacheModuleExpires - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_expires.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/modules/metadata/mod_headers.dsp b/modules/metadata/mod_headers.dsp
new file mode 100644 (file)
index 0000000..1dc7602
--- /dev/null
@@ -0,0 +1,113 @@
+# Microsoft Developer Studio Project File - Name="ApacheModuleHeaders" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleHeaders - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleHeaders.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleHeaders.mak"\
+ CFG="ApacheModuleHeaders - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ApacheModuleHeaders - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleHeaders - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ApacheModuleHeaders - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\ApacheModuleHeadersR"
+# PROP Intermediate_Dir ".\ApacheModuleHeadersR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "ApacheModuleHeaders - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\ApacheModuleHeadersD"
+# PROP Intermediate_Dir ".\ApacheModuleHeadersD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ApacheModuleHeaders - Win32 Release"
+# Name "ApacheModuleHeaders - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_headers.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/modules/metadata/mod_usertrack.dsp b/modules/metadata/mod_usertrack.dsp
new file mode 100644 (file)
index 0000000..e3aee55
--- /dev/null
@@ -0,0 +1,113 @@
+# Microsoft Developer Studio Project File - Name="ApacheModuleUserTrack" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleUserTrack - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleUserTrack.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "ApacheModuleUserTrack.mak"\
+ CFG="ApacheModuleUserTrack - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ApacheModuleUserTrack - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleUserTrack - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ApacheModuleUserTrack - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\ApacheModuleUserTrackR"
+# PROP Intermediate_Dir ".\ApacheModuleUserTrackR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "ApacheModuleUserTrack - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\ApacheModuleUserTrackD"
+# PROP Intermediate_Dir ".\ApacheModuleUserTrackD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ApacheModuleUserTrack - Win32 Release"
+# Name "ApacheModuleUserTrack - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_usertrack.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/modules/ssl/.indent.pro b/modules/ssl/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/os/.indent.pro b/os/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/os/bs2000/.cvsignore b/os/bs2000/.cvsignore
new file mode 100644 (file)
index 0000000..f3c7a7c
--- /dev/null
@@ -0,0 +1 @@
+Makefile
diff --git a/os/bs2000/bs2login.c b/os/bs2000/bs2login.c
new file mode 100644 (file)
index 0000000..32eb1ae
--- /dev/null
@@ -0,0 +1,297 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+#ifdef _OSD_POSIX
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include <ctype.h>
+#include <sys/utsname.h>
+
+#define ACCT_LEN 8
+#define USER_LEN 8
+
+static const char *bs2000_account = NULL;
+typedef enum
+{
+    bs2_unknown,     /* not initialized yet. */
+    bs2_noFORK,      /* no fork() because -X flag was specified */
+    bs2_FORK,        /* only fork() because uid != 0 */
+    bs2_FORK_RINI,   /* prior to A17, regular fork() and _rini() was used. */
+    bs2_RFORK_RINI,  /* for A17, use of _rfork() and _rini() was required */
+    bs2_UFORK        /* As of A18, the new ufork() is used. */
+} bs2_ForkType;
+
+static bs2_ForkType forktype = bs2_unknown;
+
+
+static void ap_pad(char *dest, size_t size, char ch)
+{
+    int i = strlen(dest); /* Leave space for trailing '\0' */
+    
+    while (i < size-1)
+       dest[i++] = ch;
+
+    dest[size-1] = '\0';       /* Guarantee for trailing '\0' */
+}
+
+static void ap_str_toupper(char *str)
+{
+    while (*str) {
+       *str = ap_toupper(*str);
+       ++str;
+    }
+}
+
+/* Determine the method for forking off a child in such a way as to
+ * set both the POSIX and BS2000 user id's to the unprivileged user.
+ */
+static bs2_ForkType os_forktype(void)
+{
+    struct utsname os_version;
+
+    /* have we checked the OS version before? If yes return the previous
+     * result - the OS release isn't going to change suddenly!
+     */
+    if (forktype != bs2_unknown) {
+       return forktype;
+    }
+
+    /* If the user is unprivileged, use the normal fork() only. */
+    if (getuid() != 0) {
+       return forktype = bs2_FORK;
+    }
+
+    if (uname(&os_version) < 0)
+    {
+       ap_log_error(APLOG_MARK, APLOG_ALERT, NULL,
+                    "uname() failed - aborting.");
+       exit(APEXIT_CHILDFATAL);
+    }
+
+    /*
+     * Old BS2000/OSD versions (before XPG4 SPEC1170) don't work with Apache.
+     * Anyway, simply return a fork().
+     */
+    if (strcmp(os_version.release, "01.0A") == 0 ||
+       strcmp(os_version.release, "02.0A") == 0 ||
+       strcmp(os_version.release, "02.1A") == 0)
+    {
+       ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL,
+                    "Error: unsupported OS version. "
+                    "You may encounter problems.");
+       forktype = bs2_FORK;
+    }
+
+    /* The following versions are special:
+     * OS versions before A17 needs regular fork() and _rini().
+     * A17 requires _rfork() and _rini(),
+     * and later versions need ufork().
+     */
+    else if (strcmp(os_version.release, "01.1A") == 0 ||
+            strcmp(os_version.release, "03.0A") == 0 ||
+            strcmp(os_version.release, "03.1A") == 0 ||
+            strcmp(os_version.release, "04.0A") == 0)
+    {
+        if (strcmp (os_version.version, "A18") >= 0)
+            forktype = bs2_UFORK;
+
+       else if (strcmp (os_version.version, "A17") < 0)
+            forktype = bs2_FORK_RINI;
+
+       else
+           forktype = bs2_RFORK_RINI;
+    }
+
+    /* All later OS versions will hopefully use ufork() only  ;-) */
+    else
+        forktype = bs2_UFORK;
+
+    return forktype;
+}
+
+
+
+/* This routine is called by http_core for the BS2000Account directive */
+/* It stores the account name for later use */
+const char *os_set_account(pool *p, const char *account)
+{
+    char account_temp[ACCT_LEN+1];
+
+    ap_cpystrn(account_temp, account, sizeof account_temp);
+
+    /* Make account all upper case */
+    ap_str_toupper(account_temp);
+
+    /* Pad to length 8 */
+    ap_pad(account_temp, sizeof account_temp, ' ');
+
+    bs2000_account = ap_pstrdup(p, account_temp);
+    return NULL;
+}
+
+/* This routine complements the setuid() call: it causes the BS2000 job
+ * environment to be switched to the target user's user id.
+ * That is important if CGI scripts try to execute native BS2000 commands.
+ */
+int os_init_job_environment(server_rec *server, const char *user_name, int one_process)
+{
+    _rini_struct            inittask; 
+    char                    username[USER_LEN+1];
+    int                     save_errno;
+    bs2_ForkType            type = os_forktype();
+
+    /* We can be sure that no change to uid==0 is possible because of
+     * the checks in http_core.c:set_user()
+     */
+
+    /* The _rini() function works only after a prior _rfork().
+     * In the case of one_process, it would fail.
+     */
+    if (one_process) {
+
+       type = forktype = bs2_noFORK;
+
+       ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server,
+                    "The debug mode of Apache should only "
+                    "be started by an unprivileged user!");
+       return 0;
+    }
+
+    /* If no _rini() is required, then return quickly. */
+    if (type != bs2_RFORK_RINI && type != bs2_FORK_RINI)
+       return 0;
+
+    /* An Account is required for _rini() */
+    if (bs2000_account == NULL)
+    {
+       ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server,
+                    "No BS2000Account configured - cannot switch to User %s",
+                    user_name);
+       exit(APEXIT_CHILDFATAL);
+    }
+
+    ap_cpystrn(username, user_name, sizeof username);
+
+    /* Make user name all upper case */
+    ap_str_toupper(username);
+
+    /* Pad to length 8 */
+    ap_pad(username, sizeof username, ' ');
+
+    inittask.username       = username;
+    inittask.account        = bs2000_account;
+    inittask.processor_name = "        ";
+
+    /* Switch to the new logon user (setuid() and setgid() are done later) */
+    /* Only the super user can switch identities. */
+    if (_rini(&inittask) != 0) {
+
+       ap_log_error(APLOG_MARK, APLOG_ALERT, server,
+                    "_rini: BS2000 auth failed for user \"%s\" acct \"%s\"",
+                    inittask.username, inittask.account);
+
+       exit(APEXIT_CHILDFATAL);
+    }
+
+    return 0;
+}
+
+/* BS2000 requires a "special" version of fork() before a setuid()/_rini() call */
+pid_t os_fork(const char *user)
+{
+    pid_t pid;
+    char  username[USER_LEN+1];
+
+    switch (os_forktype()) {
+      case bs2_FORK:
+      case bs2_FORK_RINI:
+       pid = fork();
+       break;
+
+      case bs2_RFORK_RINI:
+       pid = _rfork();
+       break;
+
+      case bs2_UFORK:
+       ap_cpystrn(username, user, sizeof username);
+
+       /* Make user name all upper case - for some versions of ufork() */
+       ap_str_toupper(username);
+
+       pid = ufork(username);
+       if (pid == -1 && errno == EPERM) {
+           ap_log_error(APLOG_MARK, APLOG_EMERG,
+                        NULL, "ufork: Possible mis-configuration "
+                        "for user %s - Aborting.", user);
+           exit(1);
+       }
+       break;
+
+      default:
+       pid = 0;
+       break;
+    }
+
+    return pid;
+}
+
+#else /* _OSD_POSIX */
+void bs2login_is_not_here()
+{
+}
+#endif /* _OSD_POSIX */
diff --git a/os/bs2000/ebcdic.c b/os/bs2000/ebcdic.c
new file mode 100644 (file)
index 0000000..0303fc6
--- /dev/null
@@ -0,0 +1,252 @@
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+
+#ifdef CHARSET_EBCDIC
+#include "ap_config.h"
+#include "ebcdic.h"
+/*
+          Initial Port for  Apache-1.3 by <Martin.Kraemer@Mch.SNI.De>
+
+"BS2000 OSD" is a POSIX on a main frame. It is made by Siemens AG, Germany.
+Within the POSIX subsystem, the same character set was chosen as in
+"native BS2000", namely EBCDIC.
+
+EBCDIC Table. (Yes, in EBCDIC, the letters 'a'..'z' are not contiguous!)
+This table is bijective, i.e. there are no ambigous or duplicate characters
+00    00 01 02 03 85 09 86 7f  87 8d 8e 0b 0c 0d 0e 0f  *................*
+10    10 11 12 13 8f 0a 08 97  18 19 9c 9d 1c 1d 1e 1f  *................*
+20    80 81 82 83 84 92 17 1b  88 89 8a 8b 8c 05 06 07  *................*
+30    90 91 16 93 94 95 96 04  98 99 9a 9b 14 15 9e 1a  *................*
+40    20 a0 e2 e4 e0 e1 e3 e5  e7 f1 60 2e 3c 28 2b 7c  * .........`.<(+|*
+50    26 e9 ea eb e8 ed ee ef  ec df 21 24 2a 29 3b 9f  *&.........!$*);.*
+60    2d 2f c2 c4 c0 c1 c3 c5  c7 d1 5e 2c 25 5f 3e 3f  *-/........^,%_>?*
+70    f8 c9 ca cb c8 cd ce cf  cc a8 3a 23 40 27 3d 22  *..........:#@'="*
+80    d8 61 62 63 64 65 66 67  68 69 ab bb f0 fd fe b1  *.abcdefghi......*
+90    b0 6a 6b 6c 6d 6e 6f 70  71 72 aa ba e6 b8 c6 a4  *.jklmnopqr......*
+a0    b5 af 73 74 75 76 77 78  79 7a a1 bf d0 dd de ae  *..stuvwxyz......*
+b0    a2 a3 a5 b7 a9 a7 b6 bc  bd be ac 5b 5c 5d b4 d7  *...........[\]..*
+c0    f9 41 42 43 44 45 46 47  48 49 ad f4 f6 f2 f3 f5  *.ABCDEFGHI......*
+d0    a6 4a 4b 4c 4d 4e 4f 50  51 52 b9 fb fc db fa ff  *.JKLMNOPQR......*
+e0    d9 f7 53 54 55 56 57 58  59 5a b2 d4 d6 d2 d3 d5  *..STUVWXYZ......*
+f0    30 31 32 33 34 35 36 37  38 39 b3 7b dc 7d da 7e  *0123456789.{.}.~*
+*/
+
+/* The bijective ebcdic-to-ascii table: */
+const unsigned char os_toascii_strictly[256] = {
+/*00*/ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f,
+       0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/
+/*10*/ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97,
+       0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/
+/*20*/ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b,
+       0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................*/
+/*30*/ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04,
+       0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................*/
+/*40*/ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5,
+       0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+|*/
+/*50*/ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef,
+       0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);.*/
+/*60*/ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5,
+       0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /*-/........^,%_>?*/
+/*70*/ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf,
+       0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'="*/
+/*80*/ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+       0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi......*/
+/*90*/ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+       0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr......*/
+/*a0*/ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+       0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz......*/
+/*b0*/ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc,
+       0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\]..*/
+/*c0*/ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+       0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI......*/
+/*d0*/ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+       0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR......*/
+/*e0*/ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+       0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ......*/
+/*f0*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+       0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e  /*0123456789.{.}.~*/
+};
+
+/* This table is (almost) identical to the previous one. The only difference
+ * is the fact that it maps every EBCDIC *except 0x0A* to its ASCII
+ * equivalent. The reason for this table is simple: Throughout the
+ * server, protocol strings are used in the form
+ *  "Content-Type: text/plain\015\012". Now all the characters in the string
+ * are stored as EBCDIC, only the semantics of \012 is completely
+ * different from LF (look it up in the table above). \015 happens to be
+ * mapped to \015 anyway, so there's no special case for it.
+ * 
+ * In THIS table, EBCDIC-\012 is mapped to ASCII-\012.
+ * This table is therefore used wherever an EBCDIC to ASCII conversion is
+ * needed in the server.
+ */
+/* ebcdic-to-ascii with \012 mapped to ASCII-\n */
+const unsigned char os_toascii[256] = {
+/*00*/ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f,
+       0x87, 0x8d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/
+/*10*/ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97,
+       0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/
+/*20*/ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b,
+       0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................*/
+/*30*/ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04,
+       0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................*/
+/*40*/ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5,
+       0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+|*/
+/*50*/ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef,
+       0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);.*/
+/*60*/ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5,
+       0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /*-/........^,%_>?*/
+/*70*/ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf,
+       0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'="*/
+/*80*/ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+       0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi......*/
+/*90*/ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+       0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr......*/
+/*a0*/ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+       0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz......*/
+/*b0*/ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc,
+       0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\]..*/
+/*c0*/ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+       0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI......*/
+/*d0*/ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+       0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR......*/
+/*e0*/ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+       0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ......*/
+/*f0*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+       0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e  /*0123456789.{.}.~*/
+};
+
+/* The ascii-to-ebcdic table:
+00    00 01 02 03 37 2d 2e 2f  16 05 15 0b 0c 0d 0e 0f  *................*
+10    10 11 12 13 3c 3d 32 26  18 19 3f 27 1c 1d 1e 1f  *................*
+20    40 5a 7f 7b 5b 6c 50 7d  4d 5d 5c 4e 6b 60 4b 61  * !"#$%&'()*+,-./
+30    f0 f1 f2 f3 f4 f5 f6 f7  f8 f9 7a 5e 4c 7e 6e 6f  *0123456789:;<=>?*
+40    7c c1 c2 c3 c4 c5 c6 c7  c8 c9 d1 d2 d3 d4 d5 d6  *@ABCDEFGHIJKLMNO*
+50    d7 d8 d9 e2 e3 e4 e5 e6  e7 e8 e9 bb bc bd 6a 6d  *PQRSTUVWXYZ[\]^_*
+60    4a 81 82 83 84 85 86 87  88 89 91 92 93 94 95 96  *`abcdefghijklmno*
+70    97 98 99 a2 a3 a4 a5 a6  a7 a8 a9 fb 4f fd ff 07  *pqrstuvwxyz{|}~.*
+80    20 21 22 23 24 04 06 08  28 29 2a 2b 2c 09 0a 14  *................*
+90    30 31 25 33 34 35 36 17  38 39 3a 3b 1a 1b 3e 5f  *................*
+a0    41 aa b0 b1 9f b2 d0 b5  79 b4 9a 8a ba ca af a1  *................*
+b0    90 8f ea fa be a0 b6 b3  9d da 9b 8b b7 b8 b9 ab  *................*
+c0    64 65 62 66 63 67 9e 68  74 71 72 73 78 75 76 77  *................*
+d0    ac 69 ed ee eb ef ec bf  80 e0 fe dd fc ad ae 59  *................*
+e0    44 45 42 46 43 47 9c 48  54 51 52 53 58 55 56 57  *................*
+f0    8c 49 cd ce cb cf cc e1  70 c0 de db dc 8d 8e df  *................*
+*/
+const unsigned char os_toebcdic[256] = {
+/*00*/  0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f,
+       0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,  /*................*/
+/*10*/  0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26,
+       0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f,  /*................*/
+/*20*/  0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d,
+       0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61,  /* !"#$%&'()*+,-./ */
+/*30*/  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+       0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f,  /*0123456789:;<=>?*/
+/*40*/  0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+       0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,  /*@ABCDEFGHIJKLMNO*/
+/*50*/  0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+       0xe7, 0xe8, 0xe9, 0xbb, 0xbc, 0xbd, 0x6a, 0x6d,  /*PQRSTUVWXYZ[\]^_*/
+/*60*/  0x4a, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+       0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,  /*`abcdefghijklmno*/
+/*70*/  0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+       0xa7, 0xa8, 0xa9, 0xfb, 0x4f, 0xfd, 0xff, 0x07,  /*pqrstuvwxyz{|}~.*/
+/*80*/  0x20, 0x21, 0x22, 0x23, 0x24, 0x04, 0x06, 0x08,
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x14,  /*................*/
+/*90*/  0x30, 0x31, 0x25, 0x33, 0x34, 0x35, 0x36, 0x17,
+       0x38, 0x39, 0x3a, 0x3b, 0x1a, 0x1b, 0x3e, 0x5f,  /*................*/
+/*a0*/  0x41, 0xaa, 0xb0, 0xb1, 0x9f, 0xb2, 0xd0, 0xb5,
+       0x79, 0xb4, 0x9a, 0x8a, 0xba, 0xca, 0xaf, 0xa1,  /*................*/
+/*b0*/  0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3,
+       0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab,  /*................*/
+/*c0*/  0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68,
+       0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77,  /*................*/
+/*d0*/  0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf,
+       0x80, 0xe0, 0xfe, 0xdd, 0xfc, 0xad, 0xae, 0x59,  /*................*/
+/*e0*/  0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48,
+       0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57,  /*................*/
+/*f0*/  0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1,
+       0x70, 0xc0, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf   /*................*/
+};
+
+/* Translate a memory block from EBCDIC (host charset) to ASCII (net charset)
+ * dest and srce may be identical, or separate memory blocks, but
+ * should not overlap.
+ */
+void
+ebcdic2ascii(unsigned char *dest, const unsigned char *srce, size_t count)
+{
+       while (count-- != 0) {
+               *dest++ = os_toascii[*srce++];
+       }
+}
+void
+ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count)
+{
+       while (count-- != 0) {
+               *dest++ = os_toascii_strictly[*srce++];
+       }
+}
+void
+ascii2ebcdic(unsigned char *dest, const unsigned char *srce, size_t count)
+{
+       while (count-- != 0) {
+               *dest++ = os_toebcdic[*srce++];
+       }
+}
+#endif /*CHARSET_EBCDIC*/
diff --git a/os/bs2000/ebcdic.h b/os/bs2000/ebcdic.h
new file mode 100644 (file)
index 0000000..267f978
--- /dev/null
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+
+extern const unsigned char os_toascii[256];
+extern const unsigned char os_toebcdic[256];
+void ebcdic2ascii(unsigned char *dest, const unsigned char *srce, size_t count);
+void ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count);
+void ascii2ebcdic(unsigned char *dest, const unsigned char *srce, size_t count);
+
diff --git a/os/bs2000/os-inline.c b/os/bs2000/os-inline.c
new file mode 100644 (file)
index 0000000..e58917a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * This file contains functions which can be inlined if the compiler
+ * has an "inline" modifier. Because of this, this file is both a
+ * header file and a compilable module.
+ *
+ * Only inlineable functions should be defined in here. They must all
+ * include the INLINE modifier. 
+ *
+ * If the compiler supports inline, this file will be #included as a
+ * header file from os.h to create all the inline function
+ * definitions. INLINE will be defined to whatever is required on
+ * function definitions to make them inline declarations.
+ *
+ * If the compiler does not support inline, this file will be compiled
+ * as a normal C file into libos.a (along with os.c). In this case
+ * INLINE will _not_ be set so we can use this to test if we are
+ * compiling this source file.  
+ */
+
+#ifndef INLINE
+#define INLINE
+
+/* Anything required only when compiling */
+#include "ap_config.h"
+
+#endif
+
+INLINE int ap_os_is_path_absolute(const char *file)
+{
+  return (file && file[0] == '/' ? 1 : 0);
+}
diff --git a/os/bs2000/os.c b/os/bs2000/os.c
new file mode 100644 (file)
index 0000000..c33810a
--- /dev/null
@@ -0,0 +1,103 @@
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * This file will include OS specific functions which are not inlineable.
+ * Any inlineable functions should be defined in os-inline.c instead.
+ */
+
+#include "httpd.h"
+#include "http_core.h"
+#include "os.h"
+
+/* Check the Content-Type to decide if conversion is needed */
+int ap_checkconv(struct request_rec *r)
+{
+    int convert_to_ascii;
+    const char *type;
+
+    /* To make serving of "raw ASCII text" files easy (they serve faster 
+     * since they don't have to be converted from EBCDIC), a new
+     * "magic" type prefix was invented: text/x-ascii-{plain,html,...}
+     * If we detect one of these content types here, we simply correct
+     * the type to the real text/{plain,html,...} type. Otherwise, we
+     * set a flag that translation is required later on.
+     */
+
+    type = (r->content_type == NULL) ? ap_default_type(r) : r->content_type;
+
+    /* If no content type is set then treat it as (ebcdic) text/plain */
+    convert_to_ascii = (type == NULL);
+
+    /* Conversion is applied to text/ files only, if ever. */
+    if (type && (strncasecmp(type, "text/", 5) == 0 ||
+                strncasecmp(type, "message/", 8) == 0)) {
+       if (strncasecmp(type, ASCIITEXT_MAGIC_TYPE_PREFIX,
+                       sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1) == 0)
+           r->content_type = ap_pstrcat(r->pool, "text/",
+                                        type+sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1,
+                                        NULL);
+        else
+           /* translate EBCDIC to ASCII */
+           convert_to_ascii = 1;
+    }
+    /* Enable conversion if it's a text document */
+    ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert_to_ascii);
+
+    return convert_to_ascii;
+}
+
diff --git a/os/bs2000/os.h b/os/bs2000/os.h
new file mode 100644 (file)
index 0000000..1ede081
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef APACHE_OS_H
+#define APACHE_OS_H
+
+#define PLATFORM "BS2000"
+
+/*
+ * This file in included in all Apache source code. It contains definitions
+ * of facilities available on _this_ operating system (HAVE_* macros),
+ * and prototypes of OS specific functions defined in os.c or os-inline.c
+ */
+
+#if !defined(INLINE) && defined(USE_GNU_INLINE)
+/* Compiler supports inline, so include the inlineable functions as
+ * part of the header
+ */
+#define INLINE extern ap_inline
+
+INLINE int ap_os_is_path_absolute(const char *file);
+
+#include "os-inline.c"
+#endif
+
+#ifndef INLINE
+/* Compiler does not support inline, so prototype the inlineable functions
+ * as normal
+ */
+extern int ap_os_is_path_absolute(const char *file);
+#endif
+
+/* Other ap_os_ routines not used by this platform */
+
+#define ap_os_is_filename_valid(f)          (1)
+#define ap_os_kill(pid, sig)                kill(pid, sig)
+
+#if !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE)
+typedef struct {           
+    char    *username;     
+    char    *account;      
+    char    *processor_name;
+}  _rini_struct;           
+
+extern int _rini(_rini_struct *);
+#endif /* !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE) */
+
+/* Sorry if this is ugly, but the include order doesn't allow me
+ * to use request_rec here... */
+struct request_rec;
+extern int ap_checkconv(struct request_rec *r);
+extern pid_t os_fork(const char *user);
+
+#endif /*! APACHE_OS_H*/
diff --git a/os/os2/.cvsignore b/os/os2/.cvsignore
new file mode 100644 (file)
index 0000000..f3c7a7c
--- /dev/null
@@ -0,0 +1 @@
+Makefile
diff --git a/os/os2/os-inline.c b/os/os2/os-inline.c
new file mode 100644 (file)
index 0000000..e3ff9f2
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * This file contains functions which can be inlined if the compiler
+ * has an "inline" modifier. Because of this, this file is both a
+ * header file and a compilable module.
+ *
+ * Only inlineable functions should be defined in here. They must all
+ * include the INLINE modifier. 
+ *
+ * If the compiler supports inline, this file will be #included as a
+ * header file from os.h to create all the inline function
+ * definitions. INLINE will be defined to whatever is required on
+ * function definitions to make them inline declarations.
+ *
+ * If the compiler does not support inline, this file will be compiled
+ * as a normal C file into libos.a (along with os.c). In this case
+ * INLINE will _not_ be set so we can use this to test if we are
+ * compiling this source file.  
+ */
+
+#ifndef INLINE
+#define INLINE
+
+/* Anything required only when compiling */
+#include "ap_config.h"
+
+#endif
+
+INLINE int ap_os_is_path_absolute(const char *file)
+{
+  /* For now, just do the same check that http_request.c and mod_alias.c
+   * do. 
+   */
+  return file && (file[0] == '/' || file[1] == ':');
+}
diff --git a/os/os2/os.h b/os/os2/os.h
new file mode 100644 (file)
index 0000000..d717f1f
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef APACHE_OS_H
+#define APACHE_OS_H
+
+#define PLATFORM "OS/2"
+#define HAVE_CANONICAL_FILENAME
+#define HAVE_DRIVE_LETTERS
+
+/*
+ * This file in included in all Apache source code. It contains definitions
+ * of facilities available on _this_ operating system (HAVE_* macros),
+ * and prototypes of OS specific functions defined in os.c or os-inline.c
+ */
+
+#if defined(__GNUC__) && !defined(INLINE)
+/* Compiler supports inline, so include the inlineable functions as
+ * part of the header
+ */
+#define INLINE extern __inline__
+
+INLINE int ap_os_is_path_absolute(const char *file);
+
+#include "os-inline.c"
+#endif
+
+#ifndef INLINE
+/* Compiler does not support inline, so prototype the inlineable functions
+ * as normal
+ */
+extern int ap_os_is_path_absolute(const char *file);
+#endif
+
+/* FIXME: the following should be implemented on this platform */
+#define ap_os_is_filename_valid(f)         (1)
+
+/* Use a specialized kill() function */
+int ap_os_kill(int pid, int sig);
+
+/* Maps an OS error code to an error message */
+char *ap_os_error_message(int err);
+
+/* OS/2 doesn't have symlinks so S_ISLNK is always false */
+#define S_ISLNK(m) 0
+
+/* Dynamic loading functions */
+#define     ap_os_dso_handle_t  unsigned long
+void        ap_os_dso_init(void);
+ap_os_dso_handle_t ap_os_dso_load(const char *);
+void        ap_os_dso_unload(ap_os_dso_handle_t);
+void *      ap_os_dso_sym(ap_os_dso_handle_t, const char *);
+const char *ap_os_dso_error(void);
+
+#endif   /* ! APACHE_OS_H */
diff --git a/os/os2/util_os2.c b/os/os2/util_os2.c
new file mode 100644 (file)
index 0000000..820ea61
--- /dev/null
@@ -0,0 +1,96 @@
+#define INCL_DOS
+#define INCL_DOSERRORS
+#include <os2.h>
+#include "httpd.h"
+#include "http_log.h"
+
+
+API_EXPORT(char *)ap_os_canonical_filename(pool *pPool, const char *szFile)
+{
+    char buf[HUGE_STRING_LEN];
+    char buf2[HUGE_STRING_LEN];
+    int rc, len; 
+    char *pos;
+    
+/* Remove trailing slash unless it's a root directory */
+    strcpy(buf, szFile);
+    len = strlen(buf);
+    
+    if (len > 3 && buf[len-1] == '/')
+        buf[--len] = 0;
+      
+    rc = DosQueryPathInfo(buf, FIL_QUERYFULLNAME, buf2, HUGE_STRING_LEN);
+
+    if (rc) {
+        if ( rc != ERROR_INVALID_NAME ) {
+            ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL, "OS/2 error %d for file %s", rc, szFile);
+            return ap_pstrdup(pPool, "");
+        } else {
+            return ap_pstrdup(pPool, szFile);
+        }
+    }
+
+    strlwr(buf2);
+    
+/* Switch backslashes to forward */
+    for (pos=buf2; *pos; pos++)
+        if (*pos == '\\')
+            *pos = '/';
+    
+    return ap_pstrdup(pPool, buf2);
+}
+
+
+
+int ap_os_kill(pid_t pid, int sig)
+{
+/* SIGTERM's don't work too well in OS/2 (only affects other EMX programs).
+   CGIs may not be, esp. REXX scripts, so use a native call instead */
+   
+    int rc;
+    
+    if ( sig == SIGTERM ) {
+        rc = DosSendSignalException( pid, XCPT_SIGNAL_BREAK );
+        
+        if ( rc ) {
+            errno = ESRCH;
+            rc = -1;
+        }
+    } else {
+        rc = kill(pid, sig);
+    }
+    
+    return rc;
+}
+
+
+
+char *ap_os_error_message(int err)
+{
+  static char result[200];
+  char message[HUGE_STRING_LEN];
+  ULONG len;
+  char *pos;
+  int c;
+  
+  if (DosGetMessage(NULL, 0, message, HUGE_STRING_LEN, err, "OSO001.MSG", &len) == 0) {
+      len--;
+      message[len] = 0;
+      pos = result;
+  
+      if (len >= sizeof(result))
+        len = sizeof(result-1);
+
+      for (c=0; c<len; c++) {
+          while (isspace(message[c]) && isspace(message[c+1])) /* skip multiple whitespace */
+              c++;
+          *(pos++) = isspace(message[c]) ? ' ' : message[c];
+      }
+  
+      *pos = 0;
+  } else {
+      sprintf(result, "OS/2 error %d", err);
+  }
+  
+  return result;
+}
diff --git a/os/tpf/TPFExport b/os/tpf/TPFExport
new file mode 100644 (file)
index 0000000..449ebf2
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+echo " Setting TPF/c89 environment variables"
+export _C89_CCMODE=1
+# replace the following with the location of your TPF include files
+export _C89_INCDIRS="/u/tpf41/currentmaint/include /u/tpf41/currentmaint/include/oco" 
+export TPF=YES
+echo "Done"
diff --git a/os/tpf/ebcdic.c b/os/tpf/ebcdic.c
new file mode 100644 (file)
index 0000000..be029f4
--- /dev/null
@@ -0,0 +1,221 @@
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+
+#ifdef CHARSET_EBCDIC
+#include "ap_config.h"
+#include "ebcdic.h"
+/*
+This code does basic character mapping for IBM's TPF operating system.
+It is a modified version of <Martin.Kraemer@Mch.SNI.De>'s code for
+the BS2000 (apache/src/os/bs2000/ebcdic.c).
+*/
+
+/*
+Bijective EBCDIC (character set IBM-1047) to US-ASCII table:
+This table is bijective - there are no ambigous or duplicate characters.
+*/
+const unsigned char os_toascii_strictly[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, /* 00-0f:           */
+    0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */
+    0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, /* 10-1f:           */
+    0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, /* 20-2f:           */
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* ................ */
+    0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, /* 30-3f:           */
+    0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* ................ */
+    0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, /* 40-4f:           */
+    0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /*  ...........<(+| */
+    0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, /* 50-5f:           */
+    0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* &.........!$*);^ */
+    0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, /* 60-6f:           */
+    0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* -/.........,%_>? */
+    0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, /* 70-7f:           */
+    0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* .........`:#@'=" */
+    0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 80-8f:           */
+    0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* .abcdefghi...... */
+    0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* 90-9f:           */
+    0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* .jklmnopqr...... */
+    0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* a0-af:           */
+    0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, /* .~stuvwxyz...[.. */
+    0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, /* b0-bf:           */
+    0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, /* .............].. */
+    0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* c0-cf:           */
+    0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* {ABCDEFGHI...... */
+    0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* d0-df:           */
+    0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, /* }JKLMNOPQR...... */
+    0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* e0-ef:           */
+    0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* \.STUVWXYZ...... */
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* f0-ff:           */
+    0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f  /* 0123456789...... */
+};
+
+/*
+Server EBCDIC (character set IBM-1047) to US-ASCII table:
+This table is a copy of the os_toascii_strictly bijective table above.
+The only change is that hex 0a (\012 octal) is mapped to hex 0a
+(ASCII's line feed) instead of hex 8e.  This is done because throughout
+Apache, protocol string definitions hardcode the linefeed as \012 (octal):
+"Content-Type: text/plain\015\012".  Without this kludge all protocol
+string definitions would need to be changed from ...\012 to ...\025.
+*/
+const unsigned char os_toascii[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, /* 00-0f:           */
+    0x87, 0x8d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */
+    0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, /* 10-1f:           */
+    0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, /* 20-2f:           */
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* ................ */
+    0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, /* 30-3f:           */
+    0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* ................ */
+    0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, /* 40-4f:           */
+    0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /*  ...........<(+| */
+    0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, /* 50-5f:           */
+    0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* &.........!$*);^ */
+    0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, /* 60-6f:           */
+    0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* -/.........,%_>? */
+    0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, /* 70-7f:           */
+    0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* .........`:#@'=" */
+    0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 80-8f:           */
+    0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* .abcdefghi...... */
+    0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* 90-9f:           */
+    0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* .jklmnopqr...... */
+    0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* a0-af:           */
+    0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, /* .~stuvwxyz...[.. */
+    0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, /* b0-bf:           */
+    0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, /* .............].. */
+    0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* c0-cf:           */
+    0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* {ABCDEFGHI...... */
+    0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* d0-df:           */
+    0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, /* }JKLMNOPQR...... */
+    0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* e0-ef:           */
+    0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* \.STUVWXYZ...... */
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* f0-ff:           */
+    0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f  /* 0123456789...... */
+};
+
+/*
+The US-ASCII to EBCDIC (character set IBM-1047) table:
+This table is bijective (no ambiguous or duplicate characters)
+*/
+const unsigned char os_toebcdic[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, /* 00-0f:           */
+    0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */
+    0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, /* 10-1f:           */
+    0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */
+    0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, /* 20-2f:           */
+    0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /*  !"#$%&'()*+,-./ */
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 30-3f:           */
+    0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /* 0123456789:;<=>? */
+    0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 40-4f:           */
+    0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /* @ABCDEFGHIJKLMNO */
+    0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, /* 50-5f:           */
+    0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, /* PQRSTUVWXYZ[\]^_ */
+    0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 60-6f:           */
+    0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /* `abcdefghijklmno */
+    0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, /* 70-7f:           */
+    0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, /* pqrstuvwxyz{|}~. */
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x04, 0x06, 0x08, /* 80-8f:           */
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x14, /* ................ */
+    0x30, 0x31, 0x25, 0x33, 0x34, 0x35, 0x36, 0x17, /* 90-9f:           */
+    0x38, 0x39, 0x3a, 0x3b, 0x1a, 0x1b, 0x3e, 0xff, /* ................ */
+    0x41, 0xaa, 0x4a, 0xb1, 0x9f, 0xb2, 0x6a, 0xb5, /* a0-af:           */
+    0xbb, 0xb4, 0x9a, 0x8a, 0xb0, 0xca, 0xaf, 0xbc, /* ................ */
+    0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3, /* b0-bf:           */
+    0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, /* ................ */
+    0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68, /* c0-cf:           */
+    0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, /* ................ */
+    0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf, /* d0-df:           */
+    0x80, 0xfd, 0xfe, 0xfb, 0xfc, 0xba, 0xae, 0x59, /* ................ */
+    0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48, /* e0-ef:           */
+    0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, /* ................ */
+    0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1, /* f0-ff:           */
+    0x70, 0xdd, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf  /* ................ */
+};
+
+/* Translate a memory block from EBCDIC (host charset) to ASCII (net charset)
+ * dest and srce may be identical, or separate memory blocks, but
+ * should not overlap.
+ */
+void
+ebcdic2ascii(void *dest, const void *srce, size_t count)
+{
+    unsigned char *udest = dest;
+    const unsigned char *usrce = srce;
+    while (count-- != 0) {
+        *udest++ = os_toascii[*usrce++];
+    }
+}
+void
+ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count)
+{
+    while (count-- != 0) {
+        *dest++ = os_toascii_strictly[*srce++];
+    }
+}
+void
+ascii2ebcdic(void *dest, const void *srce, size_t count)
+{
+        unsigned char *udest = dest;
+        const unsigned char *usrce = srce;
+
+        while (count-- != 0) {
+                *udest++ = os_toebcdic[*usrce++];
+    }
+}
+#endif /*CHARSET_EBCDIC*/
diff --git a/os/tpf/ebcdic.h b/os/tpf/ebcdic.h
new file mode 100644 (file)
index 0000000..e9c4120
--- /dev/null
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+
+extern const unsigned char os_toascii[256];
+extern const unsigned char os_toebcdic[256];
+void ebcdic2ascii(void *dest, const void *srce, size_t count);
+void ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count);
+void ascii2ebcdic(void *dest, const void *srce, size_t count);
+
diff --git a/os/tpf/os-inline.c b/os/tpf/os-inline.c
new file mode 100644 (file)
index 0000000..e58917a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * This file contains functions which can be inlined if the compiler
+ * has an "inline" modifier. Because of this, this file is both a
+ * header file and a compilable module.
+ *
+ * Only inlineable functions should be defined in here. They must all
+ * include the INLINE modifier. 
+ *
+ * If the compiler supports inline, this file will be #included as a
+ * header file from os.h to create all the inline function
+ * definitions. INLINE will be defined to whatever is required on
+ * function definitions to make them inline declarations.
+ *
+ * If the compiler does not support inline, this file will be compiled
+ * as a normal C file into libos.a (along with os.c). In this case
+ * INLINE will _not_ be set so we can use this to test if we are
+ * compiling this source file.  
+ */
+
+#ifndef INLINE
+#define INLINE
+
+/* Anything required only when compiling */
+#include "ap_config.h"
+
+#endif
+
+INLINE int ap_os_is_path_absolute(const char *file)
+{
+  return (file && file[0] == '/' ? 1 : 0);
+}
diff --git a/os/tpf/os.c b/os/tpf/os.c
new file mode 100644 (file)
index 0000000..e468232
--- /dev/null
@@ -0,0 +1,414 @@
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * This file will include OS specific functions which are not inlineable.
+ * Any inlineable functions should be defined in os-inline.c instead.
+ */
+
+#include "httpd.h"
+#include "http_core.h"
+#include "os.h"
+#include "scoreboard.h"
+#include "http_log.h"
+#include "http_conf_globals.h"
+
+static FILE *sock_fp;
+
+/* Check the Content-Type to decide if conversion is needed */
+int ap_checkconv(struct request_rec *r)
+{
+    int convert_to_ascii;
+    const char *type;
+
+    /* To make serving of "raw ASCII text" files easy (they serve faster 
+     * since they don't have to be converted from EBCDIC), a new
+     * "magic" type prefix was invented: text/x-ascii-{plain,html,...}
+     * If we detect one of these content types here, we simply correct
+     * the type to the real text/{plain,html,...} type. Otherwise, we
+     * set a flag that translation is required later on.
+     */
+
+    type = (r->content_type == NULL) ? ap_default_type(r) : r->content_type;
+
+    /* If no content type is set then treat it as (ebcdic) text/plain */
+    convert_to_ascii = (type == NULL);
+
+    /* Conversion is applied to text/ files only, if ever. */
+    if (type && (strncasecmp(type, "text/", 5) == 0 ||
+                strncasecmp(type, "message/", 8) == 0)) {
+       if (strncasecmp(type, ASCIITEXT_MAGIC_TYPE_PREFIX,
+                        sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1) == 0){
+           r->content_type = ap_pstrcat(r->pool, "text/",
+                   type+sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1, NULL);
+            if (r->method_number == M_PUT)
+                   ap_bsetflag(r->connection->client, B_ASCII2EBCDIC, 0);
+            }
+
+        else
+           /* translate EBCDIC to ASCII */
+           convert_to_ascii = 1;
+    }
+    else{
+           if (r->method_number == M_PUT)
+               ap_bsetflag(r->connection->client, B_ASCII2EBCDIC, 0);
+               /* don't translate non-text files to EBCDIC */
+    }
+    /* Enable conversion if it's a text document */
+    ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert_to_ascii);
+
+    return convert_to_ascii;
+}
+
+int tpf_select(int maxfds, fd_set *reads, fd_set *writes, fd_set *excepts, struct timeval *tv)
+{
+/* We're going to force our way through select.  We're only interested reads and TPF allows
+   2billion+ socket descriptors for we don't want an fd_set that big.  Just assume that maxfds-1
+   contains the socket descriptor we're interested in.  If it's 0, leave it alone. */
+
+    int sockets[1];
+    int no_reads = 0;
+    int no_writes = 0;
+    int no_excepts = 0;
+    int timeout = 0;
+    int rv;
+    
+    if(maxfds) {
+        if(tv)
+            timeout = tv->tv_sec * 1000 + tv->tv_usec;
+        sockets[0] = maxfds-1;
+        no_reads++;
+    }
+        else
+        sockets[0] = 0;
+        
+    ap_check_signals();
+    rv = select(sockets, no_reads, no_writes, no_excepts, timeout);
+    ap_check_signals();
+    
+    return rv;
+
+}
+
+int tpf_accept(int sockfd, struct sockaddr *peer, int *paddrlen)
+{
+    int socks[1];
+    int rv;
+
+    ap_check_signals();
+    socks[0] = sockfd;
+    rv = select(socks, 1, 0, 0, 1000);
+    errno = sock_errno();
+    if(rv>0) {
+        ap_check_signals();
+        rv = accept(sockfd, peer, paddrlen);
+        errno = sock_errno();
+    }    
+    return rv;
+}
+   
+/* the getpass function is not usable on TPF */
+char *getpass(const char* prompt)
+{
+    errno = EIO;
+    return((char *)NULL);
+}
+
+#ifndef __PIPE_
+int pipe(int fildes[2])
+{
+    errno = ENOSYS;
+    return(-1);
+}
+#endif
+  
+/* fork and exec functions are not defined on
+   TPF due to the implementation of tpf_fork() */
+pid_t fork(void)
+{
+    errno = ENOSYS;
+    return(-1);
+}
+
+int execl(const char *path, const char *arg0, ...)
+{
+    errno = ENOSYS;
+    return(-1);
+}
+
+int execle(const char *path, const char *arg0, ...)
+{
+    errno = ENOSYS;
+    return(-1);
+}
+
+int execve(const char *path, char *const argv[], char *const envp[])
+{
+    errno = ENOSYS;
+    return(-1);
+}
+
+int execvp(const char *file, char *const argv[])
+{
+    errno = ENOSYS;
+    return(-1);
+}
+
+
+
+int ap_tpf_spawn_child(pool *p, int (*func) (void *, child_info *),
+                       void *data, enum kill_conditions kill_how,
+                       int *pipe_in, int *pipe_out, int *pipe_err,
+                       int out_fds[], int in_fds[], int err_fds[])
+
+{
+
+   int                      i, temp_out, temp_in, temp_err, save_errno, pid, result=0;
+   int                      fd_flags_out, fd_flags_in, fd_flags_err;
+   struct tpf_fork_input    fork_input;
+   TPF_FORK_CHILD           *cld = (TPF_FORK_CHILD *) data;
+   array_header             *env_arr = ap_table_elts ((array_header *) cld->subprocess_env);
+   table_entry              *elts = (table_entry *) env_arr->elts;
+
+
+
+   if (func) {
+      if (result=func(data, NULL)) {
+          return 0;                    /* error from child function */
+      }
+   }
+
+   if (pipe_out) {
+      fd_flags_out = fcntl(out_fds[0], F_GETFD);
+      fcntl(out_fds[0], F_SETFD, FD_CLOEXEC);
+      temp_out = dup(STDOUT_FILENO);
+      fcntl(temp_out, F_SETFD, FD_CLOEXEC);
+      dup2(out_fds[1], STDOUT_FILENO);
+   }
+
+
+   if (pipe_in) {
+      fd_flags_in = fcntl(in_fds[1], F_GETFD);
+      fcntl(in_fds[1], F_SETFD, FD_CLOEXEC);
+      temp_in = dup(STDIN_FILENO);
+      fcntl(temp_in, F_SETFD, FD_CLOEXEC);
+      dup2(in_fds[0], STDIN_FILENO);
+   }
+
+   if (pipe_err) {
+      fd_flags_err = fcntl(err_fds[0], F_GETFD);
+      fcntl(err_fds[0], F_SETFD, FD_CLOEXEC);
+      temp_err = dup(STDERR_FILENO);
+      fcntl(temp_err, F_SETFD, FD_CLOEXEC);
+      dup2(err_fds[1], STDERR_FILENO);
+   }
+
+   if (cld->subprocess_env) {
+      for (i = 0; i < env_arr->nelts; ++i) {
+           if (!elts[i].key)
+               continue;
+           setenv (elts[i].key, elts[i].val, 1);
+       }
+   }
+
+   fork_input.program = (const char*) cld->filename;
+   fork_input.prog_type = cld->prog_type;
+   fork_input.istream = TPF_FORK_IS_BALANCE;
+   fork_input.ebw_data_length = 0;
+   fork_input.ebw_data = NULL;
+   fork_input.parm_data = NULL;
+
+
+   if ((pid = tpf_fork(&fork_input)) < 0) {
+       save_errno = errno;
+       if (pipe_out) {
+           close(out_fds[0]);
+       }
+       if (pipe_in) {
+           close(in_fds[1]);
+       }
+       if (pipe_err) {
+           close(err_fds[0]);
+       }
+       errno = save_errno;
+       pid = 0;
+   }
+
+   if (cld->subprocess_env) {
+       for (i = 0; i < env_arr->nelts; ++i) {
+            if (!elts[i].key)
+                continue;
+            unsetenv (elts[i].key);
+       }
+   }
+
+   if (pipe_out) {
+       close(out_fds[1]);
+       dup2(temp_out, STDOUT_FILENO);
+       close(temp_out);
+       fcntl(out_fds[0], F_SETFD, fd_flags_out);
+   }
+
+   if (pipe_in) {
+       close(in_fds[0]);
+       dup2(temp_in, STDIN_FILENO);
+       close(temp_in);
+       fcntl(in_fds[1], F_SETFD, fd_flags_in);
+   }
+
+
+   if (pipe_err) {
+       close(err_fds[1]);
+       dup2(temp_err, STDERR_FILENO);
+       close(temp_err);
+       fcntl(err_fds[0], F_SETFD, fd_flags_err);
+   }
+
+
+   if (pid) {
+
+       ap_note_subprocess(p, pid, kill_how);
+
+       if (pipe_out) {
+          *pipe_out = out_fds[0];
+       }
+       if (pipe_in) {
+          *pipe_in = in_fds[1];
+       }
+       if (pipe_err) {
+          *pipe_err = err_fds[0];
+       }
+   }
+
+   return pid;
+
+}
+
+pid_t os_fork(server_rec *s, int slot)
+{
+    struct tpf_fork_input fork_input;
+    APACHE_TPF_INPUT input_parms;
+    int count;
+    listen_rec *lr;
+
+    fflush(stdin);
+    if (dup2(fileno(sock_fp), STDIN_FILENO) == -1)
+        ap_log_error(APLOG_MARK, APLOG_CRIT, s,
+        "unable to replace stdin with sock device driver");
+    fflush(stdout);
+    if (dup2(fileno(sock_fp), STDOUT_FILENO) == -1)
+        ap_log_error(APLOG_MARK, APLOG_CRIT, s,
+        "unable to replace stdout with sock device driver");
+    input_parms.generation = ap_my_generation;
+#ifdef SCOREBOARD_FILE
+    input_parms.scoreboard_fd = scoreboard_fd;
+#else /* must be USE_TPF_SCOREBOARD or USE_SHMGET_SCOREBOARD */
+    input_parms.scoreboard_heap = ap_scoreboard_image;
+#endif
+
+    lr = ap_listeners;
+    count = 0;
+    do {
+        input_parms.listeners[count] = lr->fd;
+        lr = lr->next;
+        count++;
+    } while(lr != ap_listeners);
+
+    input_parms.slot = slot;
+    input_parms.restart_time = ap_restart_time;
+    fork_input.ebw_data = &input_parms;
+    fork_input.program = ap_server_argv0;
+    fork_input.prog_type = TPF_FORK_NAME;
+    fork_input.istream = TPF_FORK_IS_BALANCE;
+    fork_input.ebw_data_length = sizeof(input_parms);
+    fork_input.parm_data = "-x";
+    return tpf_fork(&fork_input);
+}
+
+int os_check_server(char *server) {
+    #ifndef USE_TPF_DAEMON
+    int rv;
+    int *current_acn;
+    if((rv = inetd_getServerStatus(server)) == INETD_SERVER_STATUS_INACTIVE)
+        return 1;
+    else {
+        current_acn = (int *)cinfc_fast(CINFC_CMMACNUM);
+        if(ecbp2()->ce2acn != *current_acn)
+            return 1;
+    }
+    #endif
+    return 0;
+}
+
+void os_note_additional_cleanups(pool *p, int sd) {
+    char sockfilename[50];
+    /* write the socket to file so that TPF socket device driver will close socket in case
+       we happen to abend. */
+    sprintf(sockfilename, "/dev/tpf.socket.file/%.8X", sd);
+    sock_fp = fopen(sockfilename, "r+");
+    ap_note_cleanups_for_file(p, sock_fp);  /* arrange to close on exec or restart */
+    fcntl(sd,F_SETFD,FD_CLOEXEC);
+}
+
+void os_tpf_child(APACHE_TPF_INPUT *input_parms) {
+    tpf_child = 1;
+    ap_my_generation = input_parms->generation;
+    ap_restart_time = input_parms->restart_time;
+}
+
+
diff --git a/os/tpf/os.h b/os/tpf/os.h
new file mode 100644 (file)
index 0000000..ff65322
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef APACHE_OS_H
+#define APACHE_OS_H
+
+#define PLATFORM "TPF"
+
+#ifdef errno
+#undef errno
+#endif
+
+/*
+ * This file in included in all Apache source code. It contains definitions
+ * of facilities available on _this_ operating system (HAVE_* macros),
+ * and prototypes of OS specific functions defined in os.c or os-inline.c
+ */
+
+#include "ap_config.h"
+
+#if !defined(INLINE) && defined(USE_GNU_INLINE)
+/* Compiler supports inline, so include the inlineable functions as
+ * part of the header
+ */
+#define INLINE extern ap_inline
+#include "os-inline.c"
+#endif
+
+#ifndef INLINE
+/* Compiler does not support inline, so prototype the inlineable functions
+ * as normal
+ */
+extern int ap_os_is_path_absolute(const char *f);
+#endif
+
+/* Other ap_os_ routines not used by this platform */
+
+#define ap_os_is_filename_valid(f)          (1)
+#define ap_os_kill(pid, sig)                kill(pid, sig)
+
+/* Sorry if this is ugly, but the include order doesn't allow me
+ * to use request_rec here... */
+struct request_rec;
+extern int ap_checkconv(struct request_rec *r);
+#include <strings.h>
+#ifndef __strings_h
+
+#define FD_SETSIZE    2048 
+typedef long fd_mask;
+
+#define NBBY    8    /* number of bits in a byte */
+#define NFDBITS (sizeof(fd_mask) * NBBY)
+#define  howmany(x, y)  (((x)+((y)-1))/(y))
+
+typedef struct fd_set { 
+        fd_mask fds_bits [howmany(FD_SETSIZE, NFDBITS)];
+} fd_set; 
+
+#define FD_CLR(n, p)((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p)((p)->fds_bits[(n)/NFDBITS] & (1 <<((n) % NFDBITS)))
+#define  FD_ZERO(p)   memset((char *)(p), 0, sizeof(*(p)))
+#endif
+    
+#ifdef FD_SET
+#undef FD_SET
+#define FD_SET(n, p) (0)
+#endif
+
+#define  RESOURCE_KEY ((void*) 0xC1C2C1C3)
+
+/* TPF doesn't have, or need, tzset (it is used in mod_expires.c) */
+#define tzset()
+
+#include <i$netd.h>
+struct apache_input {
+    INETD_SERVER_INPUT  inetd_server;
+    void                *scoreboard_heap;   /* scoreboard system heap address */
+    int                 scoreboard_fd;      /* scoreboard file descriptor */
+    int                 slot;               /* child number */
+    int                 generation;         /* server generation number */
+    int                 listeners[10];
+    time_t              restart_time;
+};
+
+typedef struct apache_input APACHE_TPF_INPUT;
+
+typedef struct tpf_fork_child {
+     char  *filename;
+     enum { FORK_NAME = 1, FORK_FILE = 2 } prog_type;
+     void  *subprocess_env;
+}TPF_FORK_CHILD;
+
+int tpf_accept(int sockfd, struct sockaddr *peer, int *paddrlen);
+extern int tpf_child;
+
+struct server_rec;
+pid_t os_fork(struct server_rec *s, int slot);
+int os_check_server(char *server);
+char *getpass(const char *prompt);
+extern char *ap_server_argv0;
+extern int scoreboard_fd;
+#include <signal.h>
+#ifndef SIGPIPE
+#define SIGPIPE 14
+#endif
+#ifdef NSIG
+#undef NSIG
+#endif
+#endif /*! APACHE_OS_H*/
diff --git a/os/tpf/samples/linkdll.jcl b/os/tpf/samples/linkdll.jcl
new file mode 100644 (file)
index 0000000..6bcd17c
--- /dev/null
@@ -0,0 +1,121 @@
+//APACH JOB MSGLEVEL=(1,1),CLASS=A,MSGCLASS=A\r
+/*ROUTE PRINT XXXXXX.XXXXXX\r
+/*ROUTE PUNCH XXXXXX.XXXXXX\r
+/*NOTIFY XXXXXX.XXXXXX\r
+//CCLE JCLLIB ORDER=(SYS1.CBC.SCBCPRC,SYS1.CEE.SCEEPROC)\r
+//PRELINK EXEC EDCPL,COND.LKED=(0,NE),\r
+// PPARM='OMVS,DLLNAME(pppp)',\r
+// LREGSIZ='2048K',\r
+// LPARM='AMODE=31,RMODE=ANY,LIST,XREF'\r
+//PLKED.SYSLIB DD DISP=SHR,DSN=FSE0000.DEVP.STUB.OB\r
+//             DD DISP=SHR,DSN=FSE0000.DEVP.CLIB.OB\r
+//             DD DISP=SHR,DSN=ACP.CLIB.RLSE46.WEB\r
+//             DD DISP=SHR,DSN=ACP.STUB.RLSE46.WEB\r
+//             DD DISP=SHR,DSN=ACP.CLIB.RLSE40\r
+//             DD DISP=SHR,DSN=ACP.STUB.RLSE40\r
+//PLKED.SYSDEFSD DD DSN=APA0000.DEVP.IMPORTS.DSD(ppppvv),DISP=SHR\r
+//PLKED.DSD DD DSN=APA0000.DEVP.IMPORTS.DSD,DISP=SHR\r
+//PLKED.OBJLIB DD DISP=SHR,DSN=FSE0000.DEVP.TEST.OB\r
+//             DD DISP=SHR,DSN=ACP.OBJ.RLSE46.WEB\r
+//             DD DISP=SHR,DSN=ACP.OBJ.INTG98.NBS\r
+//             DD DISP=SHR,DSN=ACP.MAIN.SYST.OBBSS\r
+//             DD DISP=SHR,DSN=ACP.DF.MAIN.SYST.OBBSS\r
+//             DD DISP=SHR,DSN=ACP.OBJ.RLSE40.BSS\r
+//PLKED.OBJ1   DD PATH='/usr/local/apache/src/ap/ap_cpystrn.o'\r
+//PLKED.OBJ2   DD PATH='/usr/local/apache/src/ap/ap_execve.o'\r
+//PLKED.OBJ3   DD PATH='/usr/local/apache/src/ap/ap_signal.o'\r
+//PLKED.OBJ4   DD PATH='/usr/local/apache/src/ap/ap_slack.o'\r
+//PLKED.OBJ5   DD PATH='/usr/local/apache/src/ap/ap_snprintf.o'\r
+//PLKED.OBJ6   DD PATH='/usr/local/apache/src/ap/ap_strings.o'\r
+//PLKED.OBJ7   DD PATH='/usr/local/apache/src/os/tpf/ebcdic.o'\r
+//PLKED.OBJ8   DD PATH='/usr/local/apache/src/os/tpf/os.o'\r
+//PLKED.OBJ9   DD PATH='/usr/local/apache/src/os/tpf/os-inline.o'\r
+//PLKED.OBJ10  DD PATH='/usr/local/apache/src/regex/regcomp.o'\r
+//PLKED.OBJ11  DD PATH='/usr/local/apache/src/regex/regerror.o'\r
+//PLKED.OBJ12  DD PATH='/usr/local/apache/src/regex/regexec.o'\r
+//PLKED.OBJ13  DD PATH='/usr/local/apache/src/regex/regfree.o'\r
+//PLKED.OBJ14  DD PATH='/usr/local/apache/src/main/alloc.o'\r
+//PLKED.OBJ15  DD PATH='/usr/local/apache/src/main/buff.o'\r
+//PLKED.OBJ16  DD PATH='/usr/local/apache/src/main/fnmatch.o'\r
+//PLKED.OBJ17  DD PATH='/usr/local/apache/src/main/http_config.o'\r
+//PLKED.OBJ18  DD PATH='/usr/local/apache/src/main/http_core.o'\r
+//PLKED.OBJ19  DD PATH='/usr/local/apache/src/main/http_log.o'\r
+//PLKED.OBJ20  DD PATH='/usr/local/apache/src/main/http_main.o'\r
+//PLKED.OBJ21  DD PATH='/usr/local/apache/src/main/http_protocol.o'\r
+//PLKED.OBJ22  DD PATH='/usr/local/apache/src/main/http_request.o'\r
+//PLKED.OBJ23  DD PATH='/usr/local/apache/src/main/http_vhost.o'\r
+//PLKED.OBJ24  DD PATH='/usr/local/apache/src/main/md5c.o'\r
+//PLKED.OBJ25  DD PATH='/usr/local/apache/src/main/rfc1413.o'\r
+//PLKED.OBJ26  DD PATH='/usr/local/apache/src/main/util.o'\r
+//PLKED.OBJ27  DD PATH='/usr/local/apache/src/main/util_date.o'\r
+//PLKED.OBJ28  DD PATH='/usr/local/apache/src/main/util_md5.o'\r
+//PLKED.OBJ29  DD PATH='/usr/local/apache/src/main/util_script.o'\r
+//PLKED.OBJ30  DD PATH='/usr/local/apache/src/main/util_uri.o'\r
+//PLKED.OBJ31  DD PATH='/usr/local/apache/src/modules.o'\r
+//PLKED.OBJ32  DD PATH='/usr/local/apache/src/buildmark.o'\r
+//PLKED.OBJ33  DD PATH='/usr/local/apache/src/modules/standard/mod_auto\\r
+//             index.o'\r
+//PLKED.OBJ34  DD PATH='/usr/local/apache/src/modules/standard/mod_dir.\\r
+//             o'\r
+//PLKED.OBJ35  DD PATH='/usr/local/apache/src/modules/standard/mod_mime\\r
+//             .o'\r
+//PLKED.OBJ36  DD PATH='/usr/local/apache/src/modules/standard/mod_sete\\r
+//             nvif.o'\r
+//PLKED.OBJ37  DD PATH='/usr/local/apache/src/modules/standard/mod_alia\\r
+//             s.o'\r
+//PLKED.OBJ38  DD PATH='/usr/local/apache/src/modules/standard/mod_acce\\r
+//             ss.o'\r
+//PLKED.OBJ39  DD PATH='/usr/local/apache/src/modules/standard/mod_user\\r
+//             dir.o'\r
+//PLKED.OBJ40  DD PATH='/usr/local/apache/src/modules/standard/mod_spel\\r
+//             ing.o'\r
+//PLKED.OBJ41  DD PATH='/usr/local/apache/src/modules/standard/mod_nego\\r
+//             tiation.o'\r
+//PLKED.SYSIN DD *\r
+ ORDER @@DLMHDR\r
+ INCLUDE OBJLIB(CSTRTD40)\r
+ INCLUDE OBJ1\r
+ INCLUDE OBJ2\r
+ INCLUDE OBJ3\r
+ INCLUDE OBJ4\r
+ INCLUDE OBJ5\r
+ INCLUDE OBJ6\r
+ INCLUDE OBJ7\r
+ INCLUDE OBJ8\r
+ INCLUDE OBJ9\r
+ INCLUDE OBJ10\r
+ INCLUDE OBJ11\r
+ INCLUDE OBJ12\r
+ INCLUDE OBJ13\r
+ INCLUDE OBJ14\r
+ INCLUDE OBJ15\r
+ INCLUDE OBJ16\r
+ INCLUDE OBJ17\r
+ INCLUDE OBJ18\r
+ INCLUDE OBJ19\r
+ INCLUDE OBJ20\r
+ INCLUDE OBJ21\r
+ INCLUDE OBJ22\r
+ INCLUDE OBJ23\r
+ INCLUDE OBJ24\r
+ INCLUDE OBJ25\r
+ INCLUDE OBJ26\r
+ INCLUDE OBJ27\r
+ INCLUDE OBJ28\r
+ INCLUDE OBJ29\r
+ INCLUDE OBJ30\r
+ INCLUDE OBJ31\r
+ INCLUDE OBJ32\r
+ INCLUDE OBJ33\r
+ INCLUDE OBJ34\r
+ INCLUDE OBJ35\r
+ INCLUDE OBJ36\r
+ INCLUDE OBJ37\r
+ INCLUDE OBJ38\r
+ INCLUDE OBJ39\r
+ INCLUDE OBJ40\r
+ INCLUDE OBJ41\r
+/*\r
+//*** WARNING *** NEVER change .LK to .OB in SYSLMOD!!!\r
+//LKED.SYSLMOD DD DISP=OLD,DSN=xxxxxx.xxxx(ppppvv)\r
+//\r
diff --git a/os/tpf/samples/loadset.jcl b/os/tpf/samples/loadset.jcl
new file mode 100644 (file)
index 0000000..c0134d4
--- /dev/null
@@ -0,0 +1,58 @@
+//OLDRWEB JOB MSGLEVEL=1,CLASS=A,MSGCLASS=S\r
+//JOBCAT DD DSN=ICFCAT.ESAWK2,DISP=SHR\r
+/*ROUTE PRINT xxxxxx.xxxxxxx\r
+/*ROUTE PUNCH xxxxxx.xxxxxxx\r
+//TLDR   EXEC  PGM=TPFLDRCA,REGION=8M,\r
+//             PARM='OLDR,SYS=ACP,CLMSIZE=8000000'\r
+//STEPLIB  DD  DSN=ACP.LINK.RLSE46.WEB,DISP=SHR\r
+//         DD  DSN=ACP.LINK.RLSE40.BSS,DISP=SHR\r
+//         DD  DSN=VIS0000.DEVP.TEST.LK,DISP=SHR\r
+//         DD  DSN=SYS1.CEE.SCEERUN,DISP=SHR\r
+//SALTB    DD  DSN=ACP.SALTBL.RLSE46.WEB,DISP=SHR\r
+//         DD  DSN=ACP.SALTBL.INTG46.WEB,DISP=SHR\r
+//OBJLIB   DD  DSN=FSE0000.DEVP.TEST.OB,DISP=SHR\r
+//         DD  DSN=APA0000.DEVP.TEST.OB,DISP=SHR\r
+//         DD  DSN=ACP.DRVE.TEST.OB,DISP=SHR\r
+//         DD  DSN=ACP.OBJ.RLSE46.WEB,DISP=SHR\r
+//         DD  DSN=ACP.OBJ.INTG36.DRV,DISP=SHR\r
+//         DD  DSN=ACP.OBJ.INTG46.WEB,DISP=SHR\r
+//         DD  DSN=ACP.OBJ.INTG40.BSS,DISP=SHR\r
+//LOADMOD  DD  DSN=FSE0000.DEVP.TEST.LK,DISP=SHR\r
+//         DD  DSN=APA0000.DEVP.TEST.LK,DISP=SHR\r
+//         DD  DSN=CWEISS.LINK,DISP=SHR\r
+//         DD  DSN=ACP.DRVE.TEST.LK,DISP=SHR\r
+//         DD  DSN=ACP.LINK.RLSE46.WEB,DISP=SHR\r
+//         DD  DSN=ACP.LINK.INTG98.NBS,DISP=SHR\r
+//         DD  DSN=ACP.LINK.INTG46.WEB,DISP=SHR\r
+//         DD  DSN=ACP.LINK.INTG36.DRV,DISP=SHR\r
+//         DD  DSN=ACP.LINK.INTG40.BSS,DISP=SHR\r
+//LOADSUM  DD  DSN=&&LOADSUM,DISP=(NEW,PASS),UNIT=SYSDA,\r
+//             LRECL=133,SPACE=(TRK,(10,10)),RECFM=FBA\r
+//CPRTEMP  DD  UNIT=SYSDA,\r
+//             DSN=&&CPRTEMP,SPACE=(TRK,(100,20)),\r
+//             DCB=(RECFM=FB,BLKSIZE=4095,LRECL=4095),\r
+//             DISP=(NEW,DELETE)\r
+//PROGTEMP DD  UNIT=SYSDA,\r
+//             DSN=&&PRTEMP,SPACE=(TRK,(100,20)),\r
+//             DCB=(RECFM=FB,BLKSIZE=4095,LRECL=4095),\r
+//             DISP=(NEW,DELETE)\r
+//OUTPUT   DD  DSN=&&VRDROUT,DISP=(NEW,PASS),UNIT=SYSDA,\r
+//             DCB=(RECFM=F,BLKSIZE=4095,LRECL=4095)\r
+//SYSUDUMP DD  DUMMY\r
+//SYSABEND DD  DUMMY\r
+//SYSOUT   DD  SYSOUT=A\r
+//SYSPRINT DD  SYSOUT=A\r
+//PRINTER  DD  SYSOUT=A\r
+//CEEDUMP  DD  SYSOUT=A\r
+//SYSIN    DD  *\r
+SYSID=BSS\r
+PATVERS=NONE\r
+SALVERS=40\r
+LOADER   LOADSET  lllllll\r
+LOADER   CALL  PROG ppppvv\r
+/*\r
+//TRANSMIT EXEC PGM=IKJEFT01,\r
+//  PARM='TRANSMIT xxxxxx.xxxxxx DDNAME(SYSTSIN) NOLOG NONOTIFY SEQ'\r
+//SYSTSIN  DD  UNIT=SYSDA,\r
+//             DSN=&&VRDROUT,DISP=(OLD,DELETE)\r
+//SYSTSPRT DD  DUMMY\r
diff --git a/os/unix/.cvsignore b/os/unix/.cvsignore
new file mode 100644 (file)
index 0000000..f3c7a7c
--- /dev/null
@@ -0,0 +1 @@
+Makefile
diff --git a/os/unix/os-inline.c b/os/unix/os-inline.c
new file mode 100644 (file)
index 0000000..54ff49a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * This file contains functions which can be inlined if the compiler
+ * has an "inline" modifier. Because of this, this file is both a
+ * header file and a compilable module.
+ *
+ * Only inlineable functions should be defined in here. They must all
+ * include the INLINE modifier. 
+ *
+ * If the compiler supports inline, this file will be #included as a
+ * header file from os.h to create all the inline function
+ * definitions. INLINE will be defined to whatever is required on
+ * function definitions to make them inline declarations.
+ *
+ * If the compiler does not support inline, this file will be compiled
+ * as a normal C file into libos.a (along with os.c). In this case
+ * INLINE will _not_ be set so we can use this to test if we are
+ * compiling this source file.  
+ */
+
+#ifndef INLINE
+#define INLINE
+
+/* Anything required only when compiling */
+#include "ap_config.h"
+
+#endif
+
+INLINE int ap_os_is_path_absolute(const char *file)
+{
+  return file[0] == '/';
+}
diff --git a/os/unix/os.h b/os/unix/os.h
new file mode 100644 (file)
index 0000000..6689dfa
--- /dev/null
@@ -0,0 +1,147 @@
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+#ifndef APACHE_OS_H
+#define APACHE_OS_H
+
+#include "ap_config.h"
+
+#ifndef PLATFORM
+#define PLATFORM "Unix"
+#endif
+
+/*
+ * This file in included in all Apache source code. It contains definitions
+ * of facilities available on _this_ operating system (HAVE_* macros),
+ * and prototypes of OS specific functions defined in os.c or os-inline.c
+ */
+
+#if !defined(INLINE) && defined(USE_GNU_INLINE)
+/* Compiler supports inline, so include the inlineable functions as
+ * part of the header
+ */
+#define INLINE extern ap_inline
+
+INLINE int ap_os_is_path_absolute(const char *file);
+
+#include "os-inline.c"
+
+#else
+
+/* Compiler does not support inline, so prototype the inlineable functions
+ * as normal
+ */
+extern int ap_os_is_path_absolute(const char *file);
+#endif
+
+/* Other ap_os_ routines not used by this platform */
+
+#define ap_os_is_filename_valid(f)          (1)
+#define ap_os_kill(pid, sig)                kill(pid, sig)
+
+/*
+ *  Abstraction layer for loading
+ *  Apache modules under run-time via 
+ *  dynamic shared object (DSO) mechanism
+ */
+
+#ifdef HAVE_DL_H
+#include <dl.h>
+#endif
+
+/*
+ * Do not use native AIX DSO support
+ */
+#ifdef AIX
+#undef HAVE_DLFCN_H
+#endif
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#else
+void *dlopen(const char *, int);
+int dlclose(void *);
+void *dlsym(void *, const char *);
+const char *dlerror(void);
+#endif
+
+/* probably on an older system that doesn't support RTLD_NOW or RTLD_LAZY.
+ * The below define is a lie since we are really doing RTLD_LAZY since the
+ * system doesn't support RTLD_NOW.
+ */
+#ifndef RTLD_NOW
+#define RTLD_NOW 1
+#endif
+
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+
+#if (defined(__FreeBSD__) ||\
+     defined(__OpenBSD__) ||\
+     defined(__NetBSD__)     ) && !defined(__ELF__)
+#define DLSYM_NEEDS_UNDERSCORE
+#endif
+
+#define     ap_os_dso_handle_t  void *
+void        ap_os_dso_init(void);
+void *      ap_os_dso_load(const char *);
+void        ap_os_dso_unload(void *);
+void *      ap_os_dso_sym(void *, const char *);
+const char *ap_os_dso_error(void);
+
+#endif /* !APACHE_OS_H */
diff --git a/os/win32/.cvsignore b/os/win32/.cvsignore
new file mode 100644 (file)
index 0000000..1756445
--- /dev/null
@@ -0,0 +1,27 @@
+*.mdp
+*.ncb
+*.opt
+*.plg
+*.dsw
+ApacheModuleAuthAnonD
+ApacheModuleAuthAnonR
+ApacheModuleCERNMetaD
+ApacheModuleCERNMetaR
+ApacheModuleDigestD
+ApacheModuleDigestR
+ApacheModuleExpiresD
+ApacheModuleExpiresR
+ApacheModuleHeadersD
+ApacheModuleHeadersR
+ApacheModuleInfoD
+ApacheModuleInfoR
+ApacheModuleRewriteD
+ApacheModuleRewriteR
+ApacheModuleSpelingD
+ApacheModuleSpelingR
+ApacheModuleStatusD
+ApacheModuleStatusR
+ApacheModuleUserTrackD
+ApacheModuleUserTrackR
+ApacheOSR
+ApacheOSD
diff --git a/os/win32/MakeModuleMak.cpp b/os/win32/MakeModuleMak.cpp
new file mode 100644 (file)
index 0000000..9ab9f27
--- /dev/null
@@ -0,0 +1,59 @@
+#include <fstream.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+void MakeMake(const char *szModule,const char *szSource)
+    {
+    ifstream ifs("Module.mak.tmpl",ios::nocreate);
+    assert(ifs.good());
+    
+    char buf[1024];
+    sprintf(buf,"%s.mak",szModule);
+    ofstream ofs(buf,ios::trunc);
+    for( ; ; )
+       {
+       ifs.getline(buf,sizeof buf);
+       if(ifs.eof())
+           break;
+       for(char *s=buf ; *s ; )
+           {
+           char *p=strchr(s,'%');
+           if(!p)
+               {
+               ofs << s << '\n';
+               break;
+               }
+           if(!strncmp(p,"%Module%",8))
+               {
+               ofs.write(s,p-s);
+               ofs << szModule;
+               s=p+8;
+               }
+           else if(!strncmp(p,"%Source%",8))
+               {
+               ofs.write(s,p-s);
+               ofs << szSource;
+               s=p+8;
+               }
+           else
+               {
+               ofs.write(s,p-s+1);
+               s=p+1;
+               }
+           }
+       }
+    }
+
+void main(int argc,char **argv)
+    {
+    if(argc < 2 || (argc%2) != 1)
+       {
+       cerr << argv[0] << " [<module name> <source file>]+\n";
+       exit(1);
+       }
+    for(int n=1 ; n < argc ; n+=2)
+       MakeMake(argv[n],argv[n+1]);
+    }
+
diff --git a/os/win32/Module.mak.tmpl b/os/win32/Module.mak.tmpl
new file mode 100644 (file)
index 0000000..162456f
--- /dev/null
@@ -0,0 +1,230 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+!IF "$(CFG)" == ""
+CFG=%Module% - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to %Module% - Win32\
+ Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "%Module% - Win32 Release" && "$(CFG)" !=\
+ "%Module% - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "%Module%.mak"\
+ CFG="%Module% - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "%Module% - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "%Module% - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "%Module% - Win32 Debug"
+MTL=mktyplib.exe
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "%Module% - 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 "%Module%R"
+# PROP Intermediate_Dir "%Module%R"
+# PROP Target_Dir ""
+OUTDIR=.\%Module%R
+INTDIR=.\%Module%R
+
+ALL : "$(OUTDIR)\%Module%.dll"
+
+CLEAN : 
+       -@erase "$(INTDIR)\%Source%.obj"
+       -@erase "$(OUTDIR)\%Module%.dll"
+       -@erase "$(OUTDIR)\%Module%.exp"
+       -@erase "$(OUTDIR)\%Module%.lib"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\regex" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "..\regex" /D "WIN32" /D "NDEBUG" /D\
+ "_WINDOWS" /Fp"$(INTDIR)/%Module%.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\%Module%R/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32 
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/%Module%.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+LINK32_FLAGS=..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib\
+ winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib\
+ uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll\
+ /incremental:no /pdb:"$(OUTDIR)/%Module%.pdb" /machine:I386\
+ /out:"$(OUTDIR)/%Module%.dll"\
+ /implib:"$(OUTDIR)/%Module%.lib" 
+LINK32_OBJS= \
+       "$(INTDIR)\%Source%.obj"
+
+"$(OUTDIR)\%Module%.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "%Module% - 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 "%Module%D"
+# PROP Intermediate_Dir "%Module%D"
+# PROP Target_Dir ""
+OUTDIR=.\%Module%D
+INTDIR=.\%Module%D
+
+ALL : "$(OUTDIR)\%Module%.dll"
+
+CLEAN : 
+       -@erase "$(INTDIR)\%Source%.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\%Module%.dll"
+       -@erase "$(OUTDIR)\%Module%.exp"
+       -@erase "$(OUTDIR)\%Module%.ilk"
+       -@erase "$(OUTDIR)\%Module%.lib"
+       -@erase "$(OUTDIR)\%Module%.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\regex" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\regex" /D "WIN32" /D "_DEBUG"\
+ /D "_WINDOWS" /Fp"$(INTDIR)/%Module%.pch" /YX /Fo"$(INTDIR)/"\
+ /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\%Module%D/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32 
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/%Module%.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+LINK32_FLAGS=..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib\
+ winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib\
+ uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll\
+ /incremental:yes /pdb:"$(OUTDIR)/%Module%.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/%Module%.dll"\
+ /implib:"$(OUTDIR)/%Module%.lib" 
+LINK32_OBJS= \
+       "$(INTDIR)\%Source%.obj"
+
+"$(OUTDIR)\%Module%.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "%Module% - Win32 Release"
+# Name "%Module% - Win32 Debug"
+
+!IF  "$(CFG)" == "%Module% - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "%Module% - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=\work\apache\src\%Source%.c
+DEP_CPP_MOD_A=\
+       "..\alloc.h"\
+       "..\buff.h"\
+       "..\conf.h"\
+       "..\http_config.h"\
+       "..\http_core.h"\
+       "..\http_log.h"\
+       "..\http_request.h"\
+       "..\httpd.h"\
+       "..\regex\regex.h"\
+       "..\ap_mmn.h"\
+       ".\readdir.h"\
+       {$(INCLUDE)}"\sys\stat.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+NODEP_CPP_MOD_A=\
+       "..\sfio.h"\
+       
+
+"$(INTDIR)\%Source%.obj" : $(SOURCE) $(DEP_CPP_MOD_A) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/os/win32/mod_isapi.c b/os/win32/mod_isapi.c
new file mode 100644 (file)
index 0000000..fa00270
--- /dev/null
@@ -0,0 +1,569 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * mod_isapi.c - Internet Server Application (ISA) module for Apache
+ * by Alexei Kosut <akosut@apache.org>
+ *
+ * This module implements Microsoft's ISAPI, allowing Apache (when running
+ * under Windows) to load Internet Server Applications (ISAPI extensions).
+ * It implements all of the ISAPI 2.0 specification, except for the 
+ * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI
+ * extensions that use only synchronous I/O and are compatible with the
+ * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should
+ * function as well).
+ *
+ * To load, simply place the ISA in a location in the document tree.
+ * Then add an "AddHandler isapi-isa dll" into your config file.
+ * You should now be able to load ISAPI DLLs just be reffering to their
+ * URLs. Make sure the ExecCGI option is active in the directory
+ * the ISA is in.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_log.h"
+#include "util_script.h"
+
+/* We use the exact same header file as the original */
+#include <HttpExt.h>
+
+/* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER,
+   define this to conform */
+#define RELAX_HEADER_RULE
+
+module isapi_module;
+
+/* Our "Connection ID" structure */
+
+typedef struct {
+    LPEXTENSION_CONTROL_BLOCK ecb;
+    request_rec *r;
+    int status;
+} isapi_cid;
+
+/* Declare the ISAPI functions */
+
+BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
+                              LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer);
+BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
+                        DWORD dwReserved);
+BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize);
+BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
+                                  LPVOID lpvBuffer, LPDWORD lpdwSize,
+                                  LPDWORD lpdwDataType);
+
+/*
+    The optimiser blows it totally here. What happens is that autos are addressed relative to the
+    stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
+    between setting isapi_entry and calling through it. We work around the problem by forcing it to
+    use frame pointers.
+*/
+#pragma optimize("y",off)
+
+int isapi_handler (request_rec *r) {
+    LPEXTENSION_CONTROL_BLOCK ecb =
+       ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
+    HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
+
+    HINSTANCE isapi_handle;
+    BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */
+    DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */
+    BOOL (*isapi_term)(DWORD); /* optional entry point 3 */
+
+    isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid));
+    table *e = r->subprocess_env;
+    int retval;
+
+    /* Use similar restrictions as CGIs */
+
+    if (!(ap_allow_options(r) & OPT_EXECCGI))
+       return FORBIDDEN;
+
+    if (r->finfo.st_mode == 0)
+       return NOT_FOUND;
+
+    if (S_ISDIR(r->finfo.st_mode))
+       return FORBIDDEN;
+
+    /* Load the module */
+
+    if (!(isapi_handle = LoadLibraryEx(r->filename, NULL,
+                                      LOAD_WITH_ALTERED_SEARCH_PATH))) {
+       ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+                   "Could not load DLL: %s", r->filename);
+       return SERVER_ERROR;
+    }
+
+    if (!(isapi_version =
+         (void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) {
+       ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+                   "DLL could not load GetExtensionVersion(): %s", r->filename);
+       FreeLibrary(isapi_handle);
+       return SERVER_ERROR;
+    }
+
+    if (!(isapi_entry =
+         (void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) {
+       ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+                   "DLL could not load HttpExtensionProc(): %s", r->filename);
+       FreeLibrary(isapi_handle);
+       return SERVER_ERROR;
+    }
+
+    isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension"));
+
+    /* Run GetExtensionVersion() */
+
+    if ((*isapi_version)(pVer) != TRUE) {
+       ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+                   "ISAPI GetExtensionVersion() failed: %s", r->filename);
+       FreeLibrary(isapi_handle);
+       return SERVER_ERROR;
+    }
+
+    /* Set up variables */
+    ap_add_common_vars(r);
+    ap_add_cgi_vars(r);
+
+    /* Set up connection ID */
+    ecb->ConnID = (HCONN)cid;
+    cid->ecb = ecb;
+    cid->r = r;
+    cid->status = 0;
+
+    ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK);
+    ecb->dwVersion = MAKELONG(0, 2);
+    ecb->dwHttpStatusCode = 0;
+    strcpy(ecb->lpszLogData, "");
+    ecb->lpszMethod = r->method;
+    ecb->lpszQueryString = ap_table_get(e, "QUERY_STRING");
+    ecb->lpszPathInfo = ap_table_get(e, "PATH_INFO");
+    ecb->lpszPathTranslated = ap_table_get(e, "PATH_TRANSLATED");
+    ecb->lpszContentType = ap_table_get(e, "CONTENT_TYPE");
+
+    /* Set up client input */
+    if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
+       if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+       FreeLibrary(isapi_handle);
+       return retval;
+    }
+
+    if (ap_should_client_block(r)) {
+       /* Unlike IIS, which limits this to 48k, we read the whole
+        * sucker in. I suppose this could be bad for memory if someone
+        * uploaded the complete works of Shakespeare. Well, WebSite
+        * does the same thing.
+        */
+       long to_read = atol(ap_table_get(e, "CONTENT_LENGTH"));
+       long read;
+
+       /* Actually, let's cap it at 48k, until we figure out what
+        * to do with this... we don't want a Content-Length: 1000000000
+        * taking out the machine.
+        */
+
+       if (to_read > 49152) {
+           if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+           FreeLibrary(isapi_handle);
+           return HTTP_REQUEST_ENTITY_TOO_LARGE;
+       }
+
+       ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read);
+
+       if ((read = ap_get_client_block(r, ecb->lpbData, to_read)) < 0) {
+           if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+           FreeLibrary(isapi_handle);
+           return SERVER_ERROR;
+       }
+
+       /* Although its not to spec, IIS seems to null-terminate
+        * its lpdData string. So we will too. To make sure
+        * cbAvailable matches cbTotalBytes, we'll up the latter
+        * and equalize them.
+        */
+       ecb->cbAvailable = ecb->cbTotalBytes = read + 1;
+       ecb->lpbData[read] = '\0';
+       }
+    else {
+       ecb->cbTotalBytes = 0;
+       ecb->cbAvailable = 0;
+       ecb->lpbData = NULL;
+    }
+
+    /* Set up the callbacks */
+
+    ecb->GetServerVariable = &GetServerVariable;
+    ecb->WriteClient = &WriteClient;
+    ecb->ReadClient = &ReadClient;
+    ecb->ServerSupportFunction = &ServerSupportFunction;
+
+    /* All right... try and load the sucker */
+    retval = (*isapi_entry)(ecb);
+
+    /* Set the status (for logging) */
+    if (ecb->dwHttpStatusCode)
+       r->status = ecb->dwHttpStatusCode;
+
+    /* Check for a log message - and log it */
+    if (ecb->lpszLogData && strcmp(ecb->lpszLogData, ""))
+       ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                   "%s: %s", ecb->lpszLogData, r->filename);
+
+    /* All done with the DLL... get rid of it */
+    if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+    FreeLibrary(isapi_handle);
+
+    switch(retval) {
+    case HSE_STATUS_SUCCESS:
+    case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
+       /* Ignore the keepalive stuff; Apache handles it just fine without
+        * the ISA's "advice".
+        */
+
+       if (cid->status) /* We have a special status to return */
+           return cid->status;
+
+       return OK;
+    case HSE_STATUS_PENDING:   /* We don't support this */
+       ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+                   "ISAPI asynchronous I/O not supported: %s", r->filename);
+    case HSE_STATUS_ERROR:
+    default:
+       return SERVER_ERROR;
+    }
+
+}
+#pragma optimize("",on)
+
+BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
+                              LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) {
+    request_rec *r = ((isapi_cid *)hConn)->r;
+    table *e = r->subprocess_env;
+    const char *result;
+    
+    /* Mostly, we just grab it from the environment, but there are
+     * a couple of special cases
+     */
+
+    if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
+       /* We don't support NT users, so this is always the same as
+        * REMOTE_USER
+        */
+       result = ap_table_get(e, "REMOTE_USER");
+    }
+    else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
+       /* Apache doesn't support secure requests inherently, so
+        * we have no way of knowing. We'll be conservative, and say
+        * all requests are insecure.
+        */
+       result = "0";
+    }
+    else if (!strcasecmp(lpszVariableName, "URL")) {
+       result = r->uri;
+    }
+    else {
+       result = ap_table_get(e, lpszVariableName);
+    }
+
+    if (result) {
+       if (strlen(result) > *lpdwSizeofBuffer) {
+           *lpdwSizeofBuffer = strlen(result);
+           SetLastError(ERROR_INSUFFICIENT_BUFFER);
+           return FALSE;
+       }
+       strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
+       return TRUE;
+    }
+
+    /* Didn't find it */
+    SetLastError(ERROR_INVALID_INDEX);
+    return FALSE;
+}
+
+BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
+                        DWORD dwReserved) {
+    request_rec *r = ((isapi_cid *)ConnID)->r;
+    int writ;  /* written, actually, but why shouldn't I make up words? */
+
+    /* We only support synchronous writing */
+    if (dwReserved && dwReserved != HSE_IO_SYNC) {
+       ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+                   "ISAPI asynchronous I/O not supported: %s", r->filename);
+       SetLastError(ERROR_INVALID_PARAMETER);
+       return FALSE;
+    }
+
+    if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
+       SetLastError(ERROR); /* XXX: Find the right error code */
+       return FALSE;
+    }
+   
+    *lpwdwBytes = writ;
+    return TRUE;
+}
+
+BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) {
+    /* Doesn't need to do anything; we've read all the data already */
+    return TRUE;
+}
+
+/* XXX: There is an O(n^2) attack possible here. */
+BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
+                                  LPVOID lpvBuffer, LPDWORD lpdwSize,
+                                  LPDWORD lpdwDataType) {
+    isapi_cid *cid = (isapi_cid *)hConn;
+    request_rec *subreq, *r = cid->r;
+    char *data;
+
+    switch (dwHSERequest) {
+    case HSE_REQ_SEND_URL_REDIRECT_RESP:
+       /* Set the status to be returned when the HttpExtensionProc()
+        * is done.
+        */
+       ap_table_set (r->headers_out, "Location", lpvBuffer);
+       cid->status = cid->r->status = cid->ecb->dwHttpStatusCode = REDIRECT;
+       return TRUE;
+
+    case HSE_REQ_SEND_URL:
+       /* Read any additional input */
+
+       if (r->remaining > 0) {
+           char argsbuffer[HUGE_STRING_LEN];
+
+           while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
+       }
+       
+       /* Reset the method to GET */
+       r->method = ap_pstrdup(r->pool, "GET");
+       r->method_number = M_GET;
+
+       /* Don't let anyone think there's still data */
+       ap_table_unset(r->headers_in, "Content-Length");
+       
+       ap_internal_redirect((char *)lpvBuffer, r);     
+       return TRUE;
+
+    case HSE_REQ_SEND_RESPONSE_HEADER:
+       r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK");
+       sscanf(r->status_line, "%d", &r->status);
+       cid->ecb->dwHttpStatusCode = r->status;
+
+       /* Now fill in the HTTP headers, and the rest of it. Ick.
+        * lpdwDataType contains a string that has headers (in MIME
+        * format), a blank like, then (possibly) data. We need
+        * to parse it.
+        *
+        * Easy case first:
+        */
+       if (!lpdwDataType) {
+           ap_send_http_header(r);
+           return TRUE;
+       }
+
+       /* Make a copy - don't disturb the original */
+       data = ap_pstrdup(r->pool, (char *)lpdwDataType);
+
+       /* We *should* break before this while loop ends */
+       while (*data) {
+           char *value, *lf = strchr(data, '\n');
+           int p;
+
+#ifdef RELAX_HEADER_RULE
+           if (lf)
+               *lf = '\0';
+#else
+           if (!lf) { /* Huh? Invalid data, I think */
+               ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                           "ISA sent invalid headers: %s", r->filename);
+               SetLastError(ERROR);    /* XXX: Find right error */
+               return FALSE;
+           }
+
+           /* Get rid of \n and \r */
+           *lf = '\0';
+#endif
+           p = strlen(data);
+           if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
+           
+           /* End of headers */
+           if (*data == '\0') {
+#ifdef RELAX_HEADER_RULE
+               if (lf)
+#endif
+                   data = lf + 1;      /* Reset data */
+               break;
+           }
+
+           if (!(value = strchr(data, ':'))) {
+               SetLastError(ERROR);    /* XXX: Find right error */
+               ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                           "ISA sent invalid headers", r->filename);
+               return FALSE;
+           }
+
+           *value++ = '\0';
+           while (*value && ap_isspace(*value)) ++value;
+
+           /* Check all the special-case headers. Similar to what
+            * ap_scan_script_header_err() does (see that function for
+            * more detail)
+            */
+
+           if (!strcasecmp(data, "Content-Type")) {
+               char *tmp;
+               /* Nuke trailing whitespace */
+               
+               char *endp = value + strlen(value) - 1;
+               while (endp > value && ap_isspace(*endp)) *endp-- = '\0';
+            
+               tmp = ap_pstrdup (r->pool, value);
+               ap_str_tolower(tmp);
+               r->content_type = tmp;
+           }
+           else if (!strcasecmp(data, "Content-Length")) {
+               ap_table_set(r->headers_out, data, value);
+           }
+           else if (!strcasecmp(data, "Transfer-Encoding")) {
+               ap_table_set(r->headers_out, data, value);
+           }
+           else if (!strcasecmp(data, "Set-Cookie")) {
+               ap_table_add(r->err_headers_out, data, value);
+           }
+           else {
+               ap_table_merge(r->err_headers_out, data, value);
+           }
+         
+           /* Reset data */
+#ifdef RELAX_HEADER_RULE
+           if (!lf) {
+               data += p;
+               break;
+           }
+#endif
+           data = lf + 1;
+       }
+       
+       /* All the headers should be set now */
+
+       ap_send_http_header(r);
+
+       /* Any data left should now be sent directly */
+       ap_rputs(data, r);
+
+       return TRUE;
+
+    case HSE_REQ_MAP_URL_TO_PATH:
+       /* Map a URL to a filename */
+       subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
+                                             *lpdwSize), r);
+
+       GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
+
+       /* IIS puts a trailing slash on directories, Apache doesn't */
+
+       if (S_ISDIR (subreq->finfo.st_mode)) {
+               int l = strlen((char *)lpvBuffer);
+
+               ((char *)lpvBuffer)[l] = '\\';
+               ((char *)lpvBuffer)[l + 1] = '\0';
+       }
+       
+       return TRUE;
+
+    case HSE_REQ_DONE_WITH_SESSION:
+       /* Do nothing... since we don't support async I/O, they'll
+        * return from HttpExtensionProc soon
+        */
+       return TRUE;
+
+    /* We don't support all this async I/O, Microsoft-specific stuff */
+    case HSE_REQ_IO_COMPLETION:
+    case HSE_REQ_TRANSMIT_FILE:
+       ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+                   "ISAPI asynchronous I/O not supported: %s", r->filename);
+    default:
+       SetLastError(ERROR_INVALID_PARAMETER);
+       return FALSE;
+    }
+}
+
+handler_rec isapi_handlers[] = {
+{ "isapi-isa", isapi_handler },
+{ NULL}
+};
+
+module isapi_module = {
+   STANDARD_MODULE_STUFF,
+   NULL,                       /* initializer */
+   NULL,                       /* create per-dir config */
+   NULL,                       /* merge per-dir config */
+   NULL,                       /* server config */
+   NULL,                       /* merge server config */
+   NULL,                       /* command table */
+   isapi_handlers,             /* handlers */
+   NULL,                       /* filename translation */
+   NULL,                       /* check_user_id */
+   NULL,                       /* check auth */
+   NULL,                       /* check access */
+   NULL,                       /* type_checker */
+   NULL,                       /* logger */
+   NULL                                /* header parser */
+};
diff --git a/os/win32/modules.c b/os/win32/modules.c
new file mode 100644 (file)
index 0000000..d55d3d8
--- /dev/null
@@ -0,0 +1,72 @@
+/* modules.c --- major modules compiled into Apache for Win32.
+ * Only insert an entry for a module if it must be compiled into
+ * the core server
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+
+extern module core_module;
+extern module so_module;
+extern module mime_module;
+extern module access_module;
+extern module auth_module;
+extern module negotiation_module;
+extern module includes_module;
+extern module autoindex_module;
+extern module dir_module;
+extern module cgi_module;
+extern module userdir_module;
+extern module alias_module;
+extern module env_module;
+extern module config_log_module;
+extern module asis_module;
+extern module imap_module;
+extern module action_module;
+extern module setenvif_module;
+extern module isapi_module;
+
+module *ap_prelinked_modules[] = {
+  &core_module,
+  &so_module,
+  &mime_module,
+  &access_module,
+  &auth_module,
+  &negotiation_module,
+  &includes_module,
+  &autoindex_module,
+  &dir_module,
+  &cgi_module,
+  &userdir_module,
+  &alias_module,
+  &env_module,
+  &config_log_module,
+  &asis_module,
+  &imap_module,
+  &action_module,
+  &setenvif_module,
+  &isapi_module,
+  NULL
+};
+module *ap_preloaded_modules[] = {
+  &core_module,
+  &so_module,
+  &mime_module,
+  &access_module,
+  &auth_module,
+  &negotiation_module,
+  &includes_module,
+  &autoindex_module,
+  &dir_module,
+  &cgi_module,
+  &userdir_module,
+  &alias_module,
+  &env_module,
+  &config_log_module,
+  &asis_module,
+  &imap_module,
+  &action_module,
+  &setenvif_module,
+  &isapi_module,
+  NULL
+};
diff --git a/os/win32/os.h b/os/win32/os.h
new file mode 100644 (file)
index 0000000..80ffd7e
--- /dev/null
@@ -0,0 +1,126 @@
+#ifndef APACHE_OS_H
+#define APACHE_OS_H
+
+#define PLATFORM "Win32"
+
+/*
+ * This file in included in all Apache source code. It contains definitions
+ * of facilities available on _this_ operating system (HAVE_* macros),
+ * and prototypes of OS specific functions defined in os.c
+ */
+
+/* temporarily replace crypt */
+/* char *crypt(const char *pw, const char *salt); */
+#define crypt(buf,salt)            (buf)
+
+/* Although DIR_TYPE is dirent (see nt/readdir.h) we need direct.h for
+   chdir() */
+#include <direct.h>
+
+#define STATUS
+/*#define WIN32_LEAN_AND_MEAN Now defined in project files */
+#ifndef STRICT
+ #define STRICT
+#endif
+#define CASE_BLIND_FILESYSTEM
+#define NO_WRITEV
+#define NO_SETSID
+#define NO_USE_SIGACTION
+#define NO_TIMES
+#define NO_GETTIMEOFDAY
+//#define NEED_PROCESS_H    although we do, this is specially handled in ap_config.h
+#define USE_LONGJMP
+#define HAVE_MMAP
+#define USE_MMAP_SCOREBOARD
+#define MULTITHREAD
+#define HAVE_CANONICAL_FILENAME
+#define HAVE_DRIVE_LETTERS
+typedef int uid_t;
+typedef int gid_t;
+typedef int pid_t;
+typedef int mode_t;
+typedef char * caddr_t;
+
+/*
+Define export types. API_EXPORT_NONSTD is a nasty hack to avoid having to declare
+every configuration function as __stdcall.
+*/
+
+#ifdef SHARED_MODULE
+# define API_VAR_EXPORT                __declspec(dllimport)
+# define API_EXPORT(type)    __declspec(dllimport) type __stdcall
+# define API_EXPORT_NONSTD(type)    __declspec(dllimport) type
+#else
+# define API_VAR_EXPORT                __declspec(dllexport)
+# define API_EXPORT(type)    __declspec(dllexport) type __stdcall
+# define API_EXPORT_NONSTD(type)    __declspec(dllexport) type
+#endif
+#define MODULE_VAR_EXPORT   __declspec(dllexport)
+
+#define strcasecmp(s1, s2) stricmp(s1, s2)
+#define strncasecmp(s1, s2, n) strnicmp(s1, s2, n)
+#define lstat(x, y) stat(x, y)
+#define S_ISLNK(m) (0)
+#define S_ISREG(m) ((m & _S_IFREG) == _S_IFREG)
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
+#endif
+#ifndef S_ISREG
+#define S_ISREG(m)      (((m)&(S_IFREG)) == (S_IFREG))
+#endif
+#define STDIN_FILENO  0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#define JMP_BUF jmp_buf
+#define sleep(t) Sleep(t*1000)
+#define O_CREAT _O_CREAT
+#define O_RDWR _O_RDWR
+#define SIGPIPE 17
+/* Seems Windows is not a subgenius */
+#define NO_SLACK
+#include <stddef.h>
+
+#define NO_OTHER_CHILD
+#define NO_RELIABLE_PIPED_LOGS
+
+__inline int ap_os_is_path_absolute(const char *file)
+{
+  /* For now, just do the same check that http_request.c and mod_alias.c
+   * do. 
+   */
+  return file[0] == '/' || file[1] == ':';
+}
+
+#define stat(f,ps)  os_stat(f,ps)
+API_EXPORT(int) os_stat(const char *szPath,struct stat *pStat);
+
+API_EXPORT(int) os_strftime(char *s, size_t max, const char *format, const struct tm *tm);
+
+#define _spawnv(mode,cmdname,argv)         os_spawnv(mode,cmdname,argv)
+#define spawnv(mode,cmdname,argv)          os_spawnv(mode,cmdname,argv)
+API_EXPORT(int) os_spawnv(int mode,const char *cmdname,const char *const *argv);
+#define _spawnve(mode,cmdname,argv,envp)    os_spawnve(mode,cmdname,argv,envp)
+#define spawnve(mode,cmdname,argv,envp)            os_spawnve(mode,cmdname,argv,envp)
+API_EXPORT(int) os_spawnve(int mode,const char *cmdname,const char *const *argv,const char *const *envp);
+#define _spawnle                           os_spawnle
+#define spawnle                                    os_spawnle
+API_EXPORT(int) os_spawnle(int mode,const char *cmdname,...);
+
+/* OS-dependent filename routines in util_win32.c */
+
+API_EXPORT(int) ap_os_is_filename_valid(const char *file);
+
+/* Abstractions for dealing with shared object files (DLLs on Win32).
+ * These are used by mod_so.c
+ */
+#define ap_os_dso_handle_t  HINSTANCE
+#define ap_os_dso_init()
+#define ap_os_dso_load(l)   LoadLibraryEx(l, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)
+#define ap_os_dso_unload(l) FreeLibrary(l)
+#define ap_os_dso_sym(h,s)  GetProcAddress(h,s)
+#define ap_os_dso_error()   "" /* for now */
+
+/* Other ap_os_ routines not used by this platform */
+#define ap_os_kill(pid, sig)                kill(pid, sig)
+
+#endif   /* ! APACHE_OS_H */
diff --git a/os/win32/util_win32.c b/os/win32/util_win32.c
new file mode 100644 (file)
index 0000000..348e0f9
--- /dev/null
@@ -0,0 +1,677 @@
+#include <windows.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include "httpd.h"
+#include "http_log.h"
+
+/* Returns TRUE if the input string is a string
+ * of one or more '.' characters.
+ */
+static BOOL OnlyDots(char *pString)
+{
+    char *c;
+
+    if (*pString == '\0')
+        return FALSE;
+
+    for (c = pString;*c;c++)
+        if (*c != '.')
+            return FALSE;
+
+    return TRUE;
+}
+
+/* Accepts as input a pathname, and tries to match it to an 
+ * existing path and return the pathname in the case that
+ * is present on the existing path.  This routine also
+ * converts alias names to long names.
+ */
+API_EXPORT(char *) ap_os_systemcase_filename(pool *pPool, 
+                                             const char *szFile)
+{
+    char buf[HUGE_STRING_LEN];
+    char *pInputName;
+    char *p, *q;
+    BOOL bDone = FALSE;
+    BOOL bFileExists = TRUE;
+    HANDLE hFind;
+    WIN32_FIND_DATA wfd;
+
+    if (!szFile || strlen(szFile) == 0 || strlen(szFile) >= sizeof(buf))
+        return ap_pstrdup(pPool, "");
+
+    buf[0] = '\0';
+    pInputName = ap_pstrdup(pPool, szFile);
+
+    /* First convert all slashes to \ so Win32 calls work OK */
+    for (p = pInputName; *p; p++) {
+        if (*p == '/')
+            *p = '\\';
+    }
+    
+    p = pInputName;
+    /* If there is drive information, copy it over. */ 
+    if (pInputName[1] == ':') {
+        buf[0] = tolower(*p++);
+        buf[1] = *p++;
+        buf[2] = '\0';
+
+        /* If all we have is a drive letter, then we are done */
+        if (strlen(pInputName) == 2)
+            bDone = TRUE;
+    }
+    
+    q = p;
+    if (*p == '\\') {
+        p++;
+        if (*p == '\\')  /* Possible UNC name */
+        {
+            p++;
+            /* Get past the machine name.  FindFirstFile */
+            /* will not find a machine name only */
+            p = strchr(p, '\\'); 
+            if (p)
+            {
+                p++;
+                /* Get past the share name.  FindFirstFile */
+                /* will not find a \\machine\share name only */
+                p = strchr(p, '\\'); 
+                if (p) {
+                    strncat(buf,q,p-q);
+                    q = p;
+                    p++;
+                }
+            }
+
+            if (!p)
+                p = q;
+        }
+    }
+
+    p = strchr(p, '\\');
+
+    while (!bDone) {
+        if (p)
+            *p = '\0';
+
+        if (strchr(q, '*') || strchr(q, '?'))
+            bFileExists = FALSE;
+
+        /* If the path exists so far, call FindFirstFile
+         * again.  However, if this portion of the path contains
+         * only '.' charaters, skip the call to FindFirstFile
+         * since it will convert '.' and '..' to actual names.
+         * Note: in the call to OnlyDots, we may have to skip
+         *       a leading slash.
+         */
+        if (bFileExists && !OnlyDots((*q == '.' ? q : q+1))) {            
+            hFind = FindFirstFile(pInputName, &wfd);
+            
+            if (hFind == INVALID_HANDLE_VALUE) {
+                bFileExists = FALSE;
+            }
+            else {
+                FindClose(hFind);
+
+                if (*q == '\\')
+                    strcat(buf,"\\");
+                strcat(buf, wfd.cFileName);
+            }
+        }
+        
+        if (!bFileExists || OnlyDots((*q == '.' ? q : q+1))) {
+            strcat(buf, q);
+        }
+        
+        if (p) {
+            q = p;
+            *p++ = '\\';
+            p = strchr(p, '\\');
+        }
+        else {
+            bDone = TRUE;
+        }
+    }
+    
+    /* First convert all slashes to / so server code handles it ok */
+    for (p = buf; *p; p++) {
+        if (*p == '\\')
+            *p = '/';
+    }
+
+    return ap_pstrdup(pPool, buf);
+}
+
+
+/*  Perform canonicalization with the exception that the
+ *  input case is preserved.
+ */
+API_EXPORT(char *) ap_os_case_canonical_filename(pool *pPool, 
+                                                 const char *szFile)
+{
+    char *pNewStr;
+    char *s;
+    char *p; 
+    char *q;
+
+    if (szFile == NULL || strlen(szFile) == 0)
+        return ap_pstrdup(pPool, "");
+
+    pNewStr = ap_pstrdup(pPool, szFile);
+
+    /*  Change all '\' characters to '/' characters.
+     *  While doing this, remove any trailing '.'.
+     *  Also, blow away any directories with 3 or
+     *  more '.'
+     */
+    for (p = pNewStr,s = pNewStr; *s; s++,p++) {
+        if (*s == '\\' || *s == '/') {
+
+            q = p;
+            while (p > pNewStr && *(p-1) == '.')
+                p--;
+
+            if (p == pNewStr && q-p <= 2 && *p == '.')
+                p = q;
+            else if (p > pNewStr && p < q && *(p-1) == '/') {
+                if (q-p > 2)
+                    p--;
+                else
+                    p = q;
+            }
+
+            *p = '/';
+        }
+        else {
+            *p = *s;
+        }
+    }
+    *p = '\0';
+
+    /*  Blow away any final trailing '.' since on Win32
+     *  foo.bat == foo.bat. == foo.bat... etc.
+     *  Also blow away any trailing spaces since
+     *  "filename" == "filename "
+     */
+    q = p;
+    while (p > pNewStr && (*(p-1) == '.' || *(p-1) == ' '))
+        p--;
+    if ((p > pNewStr) ||
+        (p == pNewStr && q-p > 2))
+        *p = '\0';
+        
+
+    /*  One more security issue to deal with.  Win32 allows
+     *  you to create long filenames.  However, alias filenames
+     *  are always created so that the filename will
+     *  conform to 8.3 rules.  According to the Microsoft
+     *  Developer's network CD (1/98) 
+     *  "Automatically generated aliases are composed of the 
+     *   first six characters of the filename plus ~n 
+     *   (where n is a number) and the first three characters 
+     *   after the last period."
+     *  Here, we attempt to detect and decode these names.
+     */
+    p = strchr(pNewStr, '~');
+    if (p != NULL) {
+        char *pConvertedName, *pQstr, *pPstr;
+        char buf[HUGE_STRING_LEN];
+        /* We potentially have a short name.  Call 
+         * ap_os_systemcase_filename to examine the filesystem
+         * and possibly extract the long name.
+         */
+        pConvertedName = ap_os_systemcase_filename(pPool, pNewStr);
+
+        /* Since we want to preserve the incoming case as much
+         * as we can, compare for differences in the string and
+         * only substitute in the path names that changed.
+         */
+        if (stricmp(pNewStr, pConvertedName)) {
+            buf[0] = '\0';
+
+            q = pQstr = pConvertedName;
+            p = pPstr = pNewStr;
+            do {
+                q = strchr(q,'/');
+                p = strchr(p,'/');
+
+                if (p != NULL) {
+                    *q = '\0';
+                    *p = '\0';
+                }
+
+                if (stricmp(pQstr, pPstr)) 
+                    strcat(buf, pQstr);   /* Converted name */
+                else 
+                    strcat(buf, pPstr);   /* Original name  */
+
+
+                if (p != NULL) {
+                    pQstr = q;
+                    pPstr = p;
+                    *q++ = '/';
+                    *p++ = '/';
+                }
+
+            } while (p != NULL); 
+
+            pNewStr = ap_pstrdup(pPool, buf);
+        }
+    }
+
+
+    return pNewStr;
+}
+
+/*  Perform complete canonicalization.
+ */
+API_EXPORT(char *) ap_os_canonical_filename(pool *pPool, const char *szFile)
+{
+    char *pNewName;
+    pNewName = ap_os_case_canonical_filename(pPool, szFile);
+    strlwr(pNewName);
+    return pNewName;
+}
+
+/* Win95 doesn't like trailing /s. NT and Unix don't mind. This works 
+ * around the problem.
+ * Errr... except if it is UNC and we are referring to the root of 
+ * the UNC, we MUST have a trailing \ and we can't use /s. Jeez. 
+ * Not sure if this refers to all UNCs or just roots,
+ * but I'm going to fix it for all cases for now. (Ben)
+ */
+
+#undef stat
+API_EXPORT(int) os_stat(const char *szPath, struct stat *pStat)
+{
+    int n;
+    
+    if (strlen(szPath) == 0) {
+        return -1;
+    }
+
+    if (szPath[0] == '/' && szPath[1] == '/') {
+       char buf[_MAX_PATH];
+       char *s;
+       int nSlashes = 0;
+
+       ap_assert(strlen(szPath) < _MAX_PATH);
+       strcpy(buf, szPath);
+       for (s = buf; *s; ++s) {
+           if (*s == '/') {
+               *s = '\\';
+               ++nSlashes;
+           }
+       }
+       /* then we need to add one more to get \\machine\share\ */
+       if (nSlashes == 3) {
+           *s++ = '\\';
+       }
+       *s = '\0';
+       return stat(buf, pStat);
+    }
+
+    /*
+     * Below removes the trailing /, however, do not remove
+     * it in the case of 'x:/' or stat will fail
+     */
+    n = strlen(szPath);
+    if ((szPath[n - 1] == '\\' || szPath[n - 1] == '/') &&
+        !(n == 3 && szPath[1] == ':')) {
+        char buf[_MAX_PATH];
+        
+        ap_assert(n < _MAX_PATH);
+        strcpy(buf, szPath);
+        buf[n - 1] = '\0';
+        
+        return stat(buf, pStat);
+    }
+    return stat(szPath, pStat);
+}
+
+/* Fix two really crap problems with Win32 spawn[lv]e*:
+ *
+ *  1. Win32 doesn't deal with spaces in argv.
+ *  2. Win95 doesn't like / in cmdname.
+ */
+
+#undef _spawnv
+API_EXPORT(int) os_spawnv(int mode, const char *cmdname,
+                         const char *const *argv)
+{
+    int n;
+    char **aszArgs;
+    const char *szArg;
+    char *szCmd;
+    char *s;
+    
+    szCmd = _alloca(strlen(cmdname)+1);
+    strcpy(szCmd, cmdname);
+    for (s = szCmd; *s; ++s) {
+        if (*s == '/') {
+            *s = '\\';
+       }
+    }
+
+    for (n = 0; argv[n]; ++n)
+        ;
+
+    aszArgs = _alloca((n + 1) * sizeof(const char *));
+
+    for (n = 0; szArg = argv[n]; ++n) {
+        if (strchr(szArg, ' ')) {
+            int l = strlen(szArg);
+
+            aszArgs[n] = _alloca(l + 2 + 1);
+            aszArgs[n][0] = '"';
+            strcpy(&aszArgs[n][1], szArg);
+            aszArgs[n][l + 1] = '"';
+            aszArgs[n][l + 2] = '\0';
+        }
+        else {
+            aszArgs[n] = (char *)szArg;
+        }
+    }
+
+    aszArgs[n] = NULL;
+
+    return _spawnv(mode, szCmd, aszArgs);
+}
+
+#undef _spawnve
+API_EXPORT(int) os_spawnve(int mode, const char *cmdname,
+                          const char *const *argv, const char *const *envp)
+{
+    int n;
+    char **aszArgs;
+    const char *szArg;
+    char *szCmd;
+    char *s;
+    
+    szCmd = _alloca(strlen(cmdname)+1);
+    strcpy(szCmd, cmdname);
+    for (s = szCmd; *s; ++s) {
+        if (*s == '/') {
+            *s = '\\';
+       }
+    }
+    
+    for (n = 0; argv[n]; ++n)
+        ;
+
+    aszArgs = _alloca((n + 1)*sizeof(const char *));
+
+    for (n = 0; szArg = argv[n]; ++n){
+        if (strchr(szArg, ' ')) {
+            int l = strlen(szArg);
+
+            aszArgs[n] = _alloca(l + 2 + 1);
+            aszArgs[n][0] = '"';
+            strcpy(&aszArgs[n][1], szArg);
+            aszArgs[n][l + 1] = '"';
+            aszArgs[n][l + 2] = '\0';
+        }
+        else {
+            aszArgs[n] = (char *)szArg;
+        }
+    }
+
+    aszArgs[n] = NULL;
+
+    return _spawnve(mode, szCmd, aszArgs, envp);
+}
+
+API_EXPORT(int) os_spawnle(int mode, const char *cmdname, ...)
+{
+    int n;
+    va_list vlist;
+    char **aszArgs;
+    const char *szArg;
+    const char *const *aszEnv;
+    char *szCmd;
+    char *s;
+    
+    szCmd = _alloca(strlen(cmdname)+1);
+    strcpy(szCmd, cmdname);
+    for (s = szCmd; *s; ++s) {
+        if (*s == '/') {
+            *s = '\\';
+       }
+    }
+
+    va_start(vlist, cmdname);
+    for (n = 0; va_arg(vlist, const char *); ++n)
+        ;
+    va_end(vlist);
+
+    aszArgs = _alloca((n + 1) * sizeof(const char *));
+
+    va_start(vlist, cmdname);
+    for (n = 0; szArg = va_arg(vlist, const char *); ++n) {
+        if (strchr(szArg, ' ')) {
+            int l = strlen(szArg);
+
+            aszArgs[n] = _alloca(l + 2 + 1);
+            aszArgs[n][0] = '"';
+            strcpy(&aszArgs[n][1], szArg);
+            aszArgs[n][l + 1] = '"';
+            aszArgs[n][l + 2] = '\0';
+        }
+        else {
+            aszArgs[n] = (char *)szArg;
+        }
+    }
+
+    aszArgs[n] = NULL;
+
+    aszEnv = va_arg(vlist, const char *const *);
+    va_end(vlist);
+    
+    return _spawnve(mode, szCmd, aszArgs, aszEnv);
+}
+
+#undef strftime
+
+/* Partial replacement for strftime. This adds certain expandos to the
+ * Windows version
+ */
+
+API_EXPORT(int) os_strftime(char *s, size_t max, const char *format,
+                            const struct tm *tm) {
+   /* If the new format string is bigger than max, the result string probably
+    * won't fit anyway. When %-expandos are added, made sure the padding below
+    * is enough.
+    */
+    char *new_format = (char *) _alloca(max + 11);
+    size_t i, j, format_length = strlen(format);
+    int return_value;
+    int length_written;
+
+    for (i = 0, j = 0; (i < format_length && j < max);) {
+        if (format[i] != '%') {
+            new_format[j++] = format[i++];
+            continue;
+        }
+        switch (format[i+1]) {
+            case 'D':
+                /* Is this locale dependent? Shouldn't be...
+                   Also note the year 2000 exposure here */
+                memcpy(new_format + j, "%m/%d/%y", 8);
+                i += 2;
+                j += 8;
+                break;
+            case 'r':
+                memcpy(new_format + j, "%I:%M:%S %p", 11);
+                i += 2;
+                j += 11;
+                break;
+            case 'T':
+                memcpy(new_format + j, "%H:%M:%S", 8);
+                i += 2;
+                j += 8;
+                break;
+            case 'e':
+                length_written = ap_snprintf(new_format + j, max - j, "%2d",
+                    tm->tm_mday);
+                j = (length_written == -1) ? max : (j + length_written);
+                i += 2;
+                break;
+            default:
+                /* We know we can advance two characters forward here. */
+                new_format[j++] = format[i++];
+                new_format[j++] = format[i++];
+        }
+    }
+    if (j >= max) {
+        *s = '\0';  /* Defensive programming, okay since output is undefined */
+        return_value = 0;
+    } else {
+        new_format[j] = '\0';
+        return_value = strftime(s, max, new_format, tm);
+    }
+    return return_value;
+}
+
+/*
+ * ap_os_is_filename_valid is given a filename, and returns 0 if the filename
+ * is not valid for use on this system. On Windows, this means it fails any
+ * of the tests below. Otherwise returns 1.
+ *
+ * Test for filename validity on Win32. This is of tests come in part from
+ * the MSDN article at "Technical Articles, Windows Platform, Base Services,
+ * Guidelines, Making Room for Long Filenames" although the information
+ * in MSDN about filename testing is incomplete or conflicting. There is a
+ * similar set of tests in "Technical Articles, Windows Platform, Base Services,
+ * Guidelines, Moving Unix Applications to Windows NT".
+ *
+ * The tests are:
+ *
+ * 1) total path length greater than MAX_PATH
+ *
+ * 2) anything using the octets 0-31 or characters " < > | :
+ *    (these are reserved for Windows use in filenames. In addition
+ *     each file system has its own additional characters that are
+ *     invalid. See KB article Q100108 for more details).
+ *
+ * 3) anything ending in "." (no matter how many)
+ *    (filename doc, doc. and doc... all refer to the same file)
+ *
+ * 4) any segment in which the basename (before first period) matches
+ *    one of the DOS device names
+ *    (the list comes from KB article Q100108 although some people
+ *     reports that additional names such as "COM5" are also special
+ *     devices).
+ *
+ * If the path fails ANY of these tests, the result must be to deny access.
+ */
+
+API_EXPORT(int) ap_os_is_filename_valid(const char *file)
+{
+    const char *segstart;
+    unsigned int seglength;
+    const char *pos;
+    static const char * const invalid_characters = "?\"<>*|:";
+    static const char * const invalid_filenames[] = { 
+       "CON", "AUX", "COM1", "COM2", "COM3", 
+       "COM4", "LPT1", "LPT2", "LPT3", "PRN", "NUL", NULL 
+    };
+
+    /* Test 1 */
+    if (strlen(file) > MAX_PATH) {
+       /* Path too long for Windows. Note that this test is not valid
+        * if the path starts with //?/ or \\?\. */
+       return 0;
+    }
+
+    pos = file;
+
+    /* Skip any leading non-path components. This can be either a
+     * drive letter such as C:, or a UNC path such as \\SERVER\SHARE\.
+     * We continue and check the rest of the path based on the rules above.
+     * This means we could eliminate valid filenames from servers which
+     * are not running NT (such as Samba).
+     */
+
+    if (pos[0] && pos[1] == ':') {
+       /* Skip leading drive letter */
+       pos += 2;
+    }
+    else {
+       if ((pos[0] == '\\' || pos[0] == '/') &&
+           (pos[1] == '\\' || pos[1] == '/')) {
+           /* Is a UNC, so skip the server name and share name */
+           pos += 2;
+           while (*pos && *pos != '/' && *pos != '\\')
+               pos++;
+           if (!*pos) {
+               /* No share name */
+               return 0;
+           }
+           pos++;      /* Move to start of share name */
+           while (*pos && *pos != '/' && *pos != '\\')
+               pos++;
+           if (!*pos) {
+               /* No path information */
+               return 0;
+           }
+       }
+    }
+
+    while (*pos) {
+       unsigned int idx;
+       unsigned int baselength;
+
+       while (*pos == '/' || *pos == '\\') {
+           pos++;
+       }
+       if (*pos == '\0') {
+           break;
+       }
+       segstart = pos; /* start of segment */
+       while (*pos && *pos != '/' && *pos != '\\') {
+           pos++;
+       }
+       seglength = pos - segstart;
+       /* 
+        * Now we have a segment of the path, starting at position "segstart"
+        * and length "seglength"
+        */
+
+       /* Test 2 */
+       for (idx = 0; idx < seglength; idx++) {
+           if ((segstart[idx] > 0 && segstart[idx] < 32) ||
+               strchr(invalid_characters, segstart[idx])) {
+               return 0;
+           }
+       }
+
+       /* Test 3 */
+       if (segstart[seglength-1] == '.') {
+           return 0;
+       }
+
+       /* Test 4 */
+       for (baselength = 0; baselength < seglength; baselength++) {
+           if (segstart[baselength] == '.') {
+               break;
+           }
+       }
+
+       /* baselength is the number of characters in the base path of
+        * the segment (which could be the same as the whole segment length,
+        * if it does not include any dot characters). */
+       if (baselength == 3 || baselength == 4) {
+           for (idx = 0; invalid_filenames[idx]; idx++) {
+               if (strlen(invalid_filenames[idx]) == baselength &&
+                   !strnicmp(invalid_filenames[idx], segstart, baselength)) {
+                   return 0;
+               }
+           }
+       }
+    }
+
+    return 1;
+}
diff --git a/server/.cvsignore b/server/.cvsignore
new file mode 100644 (file)
index 0000000..f8193cb
--- /dev/null
@@ -0,0 +1,13 @@
+Makefile
+uri_delims.h
+gen_uri_delims
+test_char.h
+gen_test_char
+gen_uri_delims_R
+gen_test_char_R
+gen_uri_delims_D
+gen_uri_delims.ilk
+gen_uri_delims.pdb
+gen_test_char_D
+gen_test_char.ilk
+gen_test_char.pdb
diff --git a/server/.indent.pro b/server/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/server/config.c b/server/config.c
new file mode 100644 (file)
index 0000000..8980f98
--- /dev/null
@@ -0,0 +1,1641 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_config.c: once was auxillary functions for reading httpd's config
+ * file and converting filenames into a namespace
+ *
+ * Rob McCool 
+ * 
+ * Wall-to-wall rewrite for Apache... commands which are part of the
+ * server core can now be found next door in "http_core.c".  Now contains
+ * general command loop, and functions which do bookkeeping for the new
+ * Apache config stuff (modules and configuration vectors).
+ *
+ * rst
+ *
+ */
+
+#define CORE_PRIVATE
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"          /* for errors in parse_htaccess */
+#include "http_request.h"      /* for default_handler (see invoke_handler) */
+#include "http_conf_globals.h" /* Sigh... */
+#include "http_vhost.h"
+#include "explain.h"
+
+DEF_Explain
+
+/****************************************************************
+ *
+ * We begin with the functions which deal with the linked list
+ * of modules which control just about all of the server operation.
+ */
+
+/* total_modules is the number of modules that have been linked
+ * into the server.
+ */
+static int total_modules = 0;
+/* dynamic_modules is the number of modules that have been added
+ * after the pre-loaded ones have been set up. It shouldn't be larger
+ * than DYNAMIC_MODULE_LIMIT.
+ */
+static int dynamic_modules = 0;
+API_VAR_EXPORT module *top_module = NULL;
+API_VAR_EXPORT module **ap_loaded_modules=NULL;
+
+typedef int (*handler_func) (request_rec *);
+typedef void *(*dir_maker_func) (pool *, char *);
+typedef void *(*merger_func) (pool *, void *, void *);
+
+/* Dealing with config vectors.  These are associated with per-directory,
+ * per-server, and per-request configuration, and have a void* pointer for
+ * each modules.  The nature of the structure pointed to is private to the
+ * module in question... the core doesn't (and can't) know.  However, there
+ * are defined interfaces which allow it to create instances of its private
+ * per-directory and per-server structures, and to merge the per-directory
+ * structures of a directory and its subdirectory (producing a new one in
+ * which the defaults applying to the base directory have been properly
+ * overridden).
+ */
+
+#ifndef ap_get_module_config
+API_EXPORT(void *) ap_get_module_config(void *conf_vector, module *m)
+{
+    void **confv = (void **) conf_vector;
+    return confv[m->module_index];
+}
+#endif
+
+#ifndef ap_set_module_config
+API_EXPORT(void) ap_set_module_config(void *conf_vector, module *m, void *val)
+{
+    void **confv = (void **) conf_vector;
+    confv[m->module_index] = val;
+}
+#endif
+
+static void *create_empty_config(pool *p)
+{
+    void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) *
+                                   (total_modules + DYNAMIC_MODULE_LIMIT));
+    return (void *) conf_vector;
+}
+
+static void *create_default_per_dir_config(pool *p)
+{
+    void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) * (total_modules + DYNAMIC_MODULE_LIMIT));
+    module *modp;
+
+    for (modp = top_module; modp; modp = modp->next) {
+       dir_maker_func df = modp->create_dir_config;
+
+       if (df)
+           conf_vector[modp->module_index] = (*df) (p, NULL);
+    }
+
+    return (void *) conf_vector;
+}
+
+void *
+     ap_merge_per_dir_configs(pool *p, void *base, void *new)
+{
+    void **conf_vector = (void **) ap_palloc(p, sizeof(void *) * total_modules);
+    void **base_vector = (void **) base;
+    void **new_vector = (void **) new;
+    module *modp;
+
+    for (modp = top_module; modp; modp = modp->next) {
+       merger_func df = modp->merge_dir_config;
+       int i = modp->module_index;
+
+       if (df && new_vector[i])
+           conf_vector[i] = (*df) (p, base_vector[i], new_vector[i]);
+       else
+           conf_vector[i] = new_vector[i] ? new_vector[i] : base_vector[i];
+    }
+
+    return (void *) conf_vector;
+}
+
+static void *create_server_config(pool *p, server_rec *s)
+{
+    void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) * (total_modules + DYNAMIC_MODULE_LIMIT));
+    module *modp;
+
+    for (modp = top_module; modp; modp = modp->next) {
+       if (modp->create_server_config)
+           conf_vector[modp->module_index] = (*modp->create_server_config) (p, s);
+    }
+
+    return (void *) conf_vector;
+}
+
+static void merge_server_configs(pool *p, void *base, void *virt)
+{
+    /* Can reuse the 'virt' vector for the spine of it, since we don't
+     * have to deal with the moral equivalent of .htaccess files here...
+     */
+
+    void **base_vector = (void **) base;
+    void **virt_vector = (void **) virt;
+    module *modp;
+
+    for (modp = top_module; modp; modp = modp->next) {
+       merger_func df = modp->merge_server_config;
+       int i = modp->module_index;
+
+       if (!virt_vector[i])
+           virt_vector[i] = base_vector[i];
+       else if (df)
+           virt_vector[i] = (*df) (p, base_vector[i], virt_vector[i]);
+    }
+}
+
+void *ap_create_request_config(pool *p)
+{
+    return create_empty_config(p);
+}
+
+CORE_EXPORT(void *) ap_create_per_dir_config(pool *p)
+{
+    return create_empty_config(p);
+}
+
+#ifdef EXPLAIN
+
+struct {
+    int offset;
+    char *method;
+} aMethods[] =
+
+{
+#define m(meth)        { XtOffsetOf(module,meth),#meth }
+    m(translate_handler),
+    m(ap_check_user_id),
+    m(auth_checker),
+    m(type_checker),
+    m(fixer_upper),
+    m(logger),
+    { -1, "?" },
+#undef m
+};
+
+char *ShowMethod(module *modp, int offset)
+{
+    int n;
+    static char buf[200];
+
+    for (n = 0; aMethods[n].offset >= 0; ++n)
+       if (aMethods[n].offset == offset)
+           break;
+    ap_snprintf(buf, sizeof(buf), "%s:%s", modp->name, aMethods[n].method);
+    return buf;
+}
+#else
+#define ShowMethod(modp,offset)
+#endif
+
+/****************************************************************
+ *
+ * Dispatch through the modules to find handlers for various phases
+ * of request handling.  These are invoked by http_request.c to actually
+ * do the dirty work of slogging through the module structures.
+ */
+
+/*
+ * Optimized run_method routines.  The observation here is that many modules
+ * have NULL for most of the methods.  So we build optimized lists of
+ * everything.  If you think about it, this is really just like a sparse array
+ * implementation to avoid scanning the zero entries.
+ */
+static const int method_offsets[] =
+{
+    XtOffsetOf(module, translate_handler),
+    XtOffsetOf(module, ap_check_user_id),
+    XtOffsetOf(module, auth_checker),
+    XtOffsetOf(module, access_checker),
+    XtOffsetOf(module, type_checker),
+    XtOffsetOf(module, fixer_upper),
+    XtOffsetOf(module, logger),
+    XtOffsetOf(module, header_parser),
+    XtOffsetOf(module, post_read_request)
+};
+#define NMETHODS       (sizeof (method_offsets)/sizeof (method_offsets[0]))
+
+static struct {
+    int translate_handler;
+    int ap_check_user_id;
+    int auth_checker;
+    int access_checker;
+    int type_checker;
+    int fixer_upper;
+    int logger;
+    int header_parser;
+    int post_read_request;
+} offsets_into_method_ptrs;
+
+/*
+ * This is just one big array of method_ptrs.  It's constructed such that,
+ * for example, method_ptrs[ offsets_into_method_ptrs.logger ] is the first
+ * logger function.  You go one-by-one from there until you hit a NULL.
+ * This structure was designed to hopefully maximize cache-coolness.
+ */
+static handler_func *method_ptrs;
+
+/* routine to reconstruct all these shortcuts... called after every
+ * add_module.
+ * XXX: this breaks if modules dink with their methods pointers
+ */
+static void build_method_shortcuts(void)
+{
+    module *modp;
+    int how_many_ptrs;
+    int i;
+    int next_ptr;
+    handler_func fp;
+
+    if (method_ptrs) {
+       /* free up any previous set of method_ptrs */
+       free(method_ptrs);
+    }
+
+    /* first we count how many functions we have */
+    how_many_ptrs = 0;
+    for (modp = top_module; modp; modp = modp->next) {
+       for (i = 0; i < NMETHODS; ++i) {
+           if (*(handler_func *) (method_offsets[i] + (char *) modp)) {
+               ++how_many_ptrs;
+           }
+       }
+    }
+    method_ptrs = malloc((how_many_ptrs + NMETHODS) * sizeof(handler_func));
+    if (method_ptrs == NULL) {
+       fprintf(stderr, "Ouch!  Out of memory in build_method_shortcuts()!\n");
+    }
+    next_ptr = 0;
+    for (i = 0; i < NMETHODS; ++i) {
+       /* XXX: This is an itsy bit presumptuous about the alignment
+        * constraints on offsets_into_method_ptrs.  I can't remember if
+        * ANSI says this has to be true... -djg */
+       ((int *) &offsets_into_method_ptrs)[i] = next_ptr;
+       for (modp = top_module; modp; modp = modp->next) {
+           fp = *(handler_func *) (method_offsets[i] + (char *) modp);
+           if (fp) {
+               method_ptrs[next_ptr++] = fp;
+           }
+       }
+       method_ptrs[next_ptr++] = NULL;
+    }
+}
+
+
+static int run_method(request_rec *r, int offset, int run_all)
+{
+    int i;
+
+    for (i = offset; method_ptrs[i]; ++i) {
+       handler_func mod_handler = method_ptrs[i];
+
+       if (mod_handler) {
+           int result;
+
+           result = (*mod_handler) (r);
+
+           if (result != DECLINED && (!run_all || result != OK))
+               return result;
+       }
+    }
+
+    return run_all ? OK : DECLINED;
+}
+
+int ap_translate_name(request_rec *r)
+{
+    return run_method(r, offsets_into_method_ptrs.translate_handler, 0);
+}
+
+int ap_check_access(request_rec *r)
+{
+    return run_method(r, offsets_into_method_ptrs.access_checker, 1);
+}
+
+int ap_find_types(request_rec *r)
+{
+    return run_method(r, offsets_into_method_ptrs.type_checker, 0);
+}
+
+int ap_run_fixups(request_rec *r)
+{
+    return run_method(r, offsets_into_method_ptrs.fixer_upper, 1);
+}
+
+int ap_log_transaction(request_rec *r)
+{
+    return run_method(r, offsets_into_method_ptrs.logger, 1);
+}
+
+int ap_header_parse(request_rec *r)
+{
+    return run_method(r, offsets_into_method_ptrs.header_parser, 1);
+}
+
+int ap_run_post_read_request(request_rec *r)
+{
+    return run_method(r, offsets_into_method_ptrs.post_read_request, 1);
+}
+
+/* Auth stuff --- anything that defines one of these will presumably
+ * want to define something for the other.  Note that check_auth is
+ * separate from check_access to make catching some config errors easier.
+ */
+
+int ap_check_user_id(request_rec *r)
+{
+    return run_method(r, offsets_into_method_ptrs.ap_check_user_id, 0);
+}
+
+int ap_check_auth(request_rec *r)
+{
+    return run_method(r, offsets_into_method_ptrs.auth_checker, 0);
+}
+
+/*
+ * For speed/efficiency we generate a compact list of all the handlers
+ * and wildcard handlers.  This means we won't have to scan the entire
+ * module list looking for handlers... where we'll find a whole whack
+ * of NULLs.
+ */
+typedef struct {
+    handler_rec hr;
+    size_t len;
+} fast_handler_rec;
+
+static fast_handler_rec *handlers;
+static fast_handler_rec *wildhandlers;
+
+static void init_handlers(pool *p)
+{
+    module *modp;
+    int nhandlers = 0;
+    int nwildhandlers = 0;
+    const handler_rec *handp;
+    fast_handler_rec *ph, *pw;
+    char *starp;
+
+    for (modp = top_module; modp; modp = modp->next) {
+       if (!modp->handlers)
+           continue;
+       for (handp = modp->handlers; handp->content_type; ++handp) {
+           if (strchr(handp->content_type, '*')) {
+                nwildhandlers ++;
+            } else {
+                nhandlers ++;
+            }
+        }
+    }
+    ph = handlers = ap_palloc(p, sizeof(*ph)*(nhandlers + 1));
+    pw = wildhandlers = ap_palloc(p, sizeof(*pw)*(nwildhandlers + 1));
+    for (modp = top_module; modp; modp = modp->next) {
+       if (!modp->handlers)
+           continue;
+       for (handp = modp->handlers; handp->content_type; ++handp) {
+           if ((starp = strchr(handp->content_type, '*'))) {
+                pw->hr.content_type = handp->content_type;
+                pw->hr.handler = handp->handler;
+               pw->len = starp - handp->content_type;
+                pw ++;
+            } else {
+                ph->hr.content_type = handp->content_type;
+                ph->hr.handler = handp->handler;
+               ph->len = strlen(handp->content_type);
+                ph ++;
+            }
+        }
+    }
+    pw->hr.content_type = NULL;
+    pw->hr.handler = NULL;
+    ph->hr.content_type = NULL;
+    ph->hr.handler = NULL;
+}
+
+int ap_invoke_handler(request_rec *r)
+{
+    fast_handler_rec *handp;
+    const char *handler;
+    char *p;
+    size_t handler_len;
+    int result = HTTP_INTERNAL_SERVER_ERROR;
+
+    if (r->handler) {
+       handler = r->handler;
+       handler_len = strlen(handler);
+    }
+    else {
+       handler = r->content_type ? r->content_type : ap_default_type(r);
+       if ((p = strchr(handler, ';')) != NULL) { /* MIME type arguments */
+           while (p > handler && p[-1] == ' ')
+               --p;            /* strip trailing spaces */
+           handler_len = p - handler;
+       }
+       else {
+           handler_len = strlen(handler);
+       }
+    }
+
+    /* Pass one --- direct matches */
+
+    for (handp = handlers; handp->hr.content_type; ++handp) {
+       if (handler_len == handp->len
+           && !strncmp(handler, handp->hr.content_type, handler_len)) {
+            result = (*handp->hr.handler) (r);
+
+            if (result != DECLINED)
+                return result;
+        }
+    }
+
+    if (result == HTTP_INTERNAL_SERVER_ERROR && r->handler) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
+            "handler \"%s\" not found for: %s", r->handler, r->filename);
+    }
+
+    /* Pass two --- wildcard matches */
+
+    for (handp = wildhandlers; handp->hr.content_type; ++handp) {
+       if (handler_len >= handp->len
+           && !strncmp(handler, handp->hr.content_type, handp->len)) {
+             result = (*handp->hr.handler) (r);
+
+             if (result != DECLINED)
+                 return result;
+         }
+    }
+
+    return HTTP_INTERNAL_SERVER_ERROR;
+}
+
+/* One-time setup for precompiled modules --- NOT to be done on restart */
+
+API_EXPORT(void) ap_add_module(module *m)
+{
+    /* This could be called from an AddModule httpd.conf command,
+     * after the file has been linked and the module structure within it
+     * teased out...
+     */
+
+    if (m->version != MODULE_MAGIC_NUMBER_MAJOR) {
+       fprintf(stderr, "%s: module \"%s\" is not compatible with this "
+               "version of Apache.\n", ap_server_argv0, m->name);
+       fprintf(stderr, "Please contact the vendor for the correct version.\n");
+       exit(1);
+    }
+
+    if (m->next == NULL) {
+       m->next = top_module;
+       top_module = m;
+    }
+    if (m->module_index == -1) {
+       m->module_index = total_modules++;
+       dynamic_modules++;
+
+       if (dynamic_modules > DYNAMIC_MODULE_LIMIT) {
+           fprintf(stderr, "%s: module \"%s\" could not be loaded, because"
+                   " the dynamic\n", ap_server_argv0, m->name);
+           fprintf(stderr, "module limit was reached. Please increase "
+                   "DYNAMIC_MODULE_LIMIT and recompile.\n");
+           exit(1);
+       }
+    }
+
+    /* Some C compilers put a complete path into __FILE__, but we want
+     * only the filename (e.g. mod_includes.c). So check for path
+     * components (Unix and DOS), and remove them.
+     */
+
+    if (strrchr(m->name, '/'))
+       m->name = 1 + strrchr(m->name, '/');
+    if (strrchr(m->name, '\\'))
+       m->name = 1 + strrchr(m->name, '\\');
+
+#ifdef _OSD_POSIX /* __FILE__="*POSIX(/home/martin/apache/src/modules/standard/mod_info.c)" */
+    /* We cannot fix the string in-place, because it's const */
+    if (m->name[strlen(m->name)-1]==')') {
+       char *tmp = strdup(m->name);    /* FIXME:memory leak, albeit a small one */
+       tmp[strlen(tmp)-1] = '\0';
+       m->name = tmp;
+    }
+#endif /*_OSD_POSIX*/
+}
+
+/* 
+ * remove_module undoes what add_module did. There are some caveats:
+ * when the module is removed, its slot is lost so all the current
+ * per-dir and per-server configurations are invalid. So we should
+ * only ever call this function when you are invalidating almost
+ * all our current data. I.e. when doing a restart.
+ */
+
+API_EXPORT(void) ap_remove_module(module *m)
+{
+    module *modp;
+
+    modp = top_module;
+    if (modp == m) {
+       /* We are the top module, special case */
+       top_module = modp->next;
+       m->next = NULL;
+    }
+    else {
+       /* Not the top module, find use. When found modp will
+        * point to the module _before_ us in the list
+        */
+
+       while (modp && modp->next != m) {
+           modp = modp->next;
+       }
+       if (!modp) {
+           /* Uh-oh, this module doesn't exist */
+           ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL,
+               "Cannot remove module %s: not found in module list",
+               m->name);
+           return;
+       }
+       /* Eliminate us from the module list */
+       modp->next = modp->next->next;
+    }
+
+    m->module_index = -1;      /* simulate being unloaded, should
+                                * be unnecessary */
+    dynamic_modules--;
+    total_modules--;
+}
+
+API_EXPORT(void) ap_add_loaded_module(module *mod)
+{
+    module **m;
+
+    /* 
+     *  Add module pointer to top of chained module list 
+     */
+    ap_add_module(mod);
+
+    /* 
+     *  And module pointer to list of loaded modules 
+     *
+     *  Notes: 1. ap_add_module() would already complain if no more space
+     *            exists for adding a dynamically loaded module
+     *         2. ap_add_module() accepts double-inclusion, so we have
+     *            to accept this, too.
+     */
+    for (m = ap_loaded_modules; *m != NULL; m++)
+        ;
+    *m++ = mod;
+    *m = NULL;
+}
+
+API_EXPORT(void) ap_remove_loaded_module(module *mod)
+{
+    module **m;
+    module **m2;
+    int done;
+
+    /* 
+     *  Remove module pointer from chained module list 
+     */
+    ap_remove_module(mod);
+
+    /* 
+     *  Remove module pointer from list of loaded modules
+     *
+     *  Note: 1. We cannot determine if the module was successfully
+     *           removed by ap_remove_module().
+     *        2. We have not to complain explicity when the module
+     *           is not found because ap_remove_module() did it
+     *           for us already.
+     */
+    for (m = m2 = ap_loaded_modules, done = 0; *m2 != NULL; m2++) {
+        if (*m2 == mod && done == 0)
+            done = 1;
+        else
+            *m++ = *m2;
+    }
+    *m = NULL;
+}
+
+void ap_setup_prelinked_modules()
+{
+    module **m;
+    module **m2;
+
+    /*
+     *  Initialise total_modules variable and module indices
+     */
+    total_modules = 0;
+    for (m = ap_preloaded_modules; *m != NULL; m++)
+        (*m)->module_index = total_modules++;
+
+    /* 
+     *  Initialise list of loaded modules
+     */
+    ap_loaded_modules = (module **)malloc(
+        sizeof(module *)*(total_modules+DYNAMIC_MODULE_LIMIT+1));
+    if (ap_loaded_modules == NULL) {
+       fprintf(stderr, "Ouch!  Out of memory in ap_setup_prelinked_modules()!\n");
+    }
+    for (m = ap_preloaded_modules, m2 = ap_loaded_modules; *m != NULL; )
+        *m2++ = *m++;
+    *m2 = NULL;
+
+    /*
+     *   Initialize chain of linked (=activate) modules
+     */
+    for (m = ap_prelinked_modules; *m != NULL; m++)
+        ap_add_module(*m);
+}
+
+API_EXPORT(const char *) ap_find_module_name(module *m)
+{
+    return m->name;
+}
+
+API_EXPORT(module *) ap_find_linked_module(const char *name)
+{
+    module *modp;
+
+    for (modp = top_module; modp; modp = modp->next) {
+       if (strcmp(modp->name, name) == 0)
+           return modp;
+    }
+    return NULL;
+}
+
+/* Add a named module.  Returns 1 if module found, 0 otherwise.  */
+API_EXPORT(int) ap_add_named_module(const char *name)
+{
+    module *modp;
+    int i = 0;
+
+    for (modp = ap_loaded_modules[i]; modp; modp = ap_loaded_modules[++i]) {
+       if (strcmp(modp->name, name) == 0) {
+           /* Only add modules that are not already enabled.  */
+           if (modp->next == NULL) {
+               ap_add_module(modp);
+           }
+           return 1;
+       }
+    }
+
+    return 0;
+}
+
+/* Clear the internal list of modules, in preparation for starting over. */
+API_EXPORT(void) ap_clear_module_list()
+{
+    module **m = &top_module;
+    module **next_m;
+
+    while (*m) {
+       next_m = &((*m)->next);
+       *m = NULL;
+       m = next_m;
+    }
+
+    /* This is required; so we add it always.  */
+    ap_add_named_module("http_core.c");
+}
+
+/*****************************************************************
+ *
+ * Resource, access, and .htaccess config files now parsed by a common
+ * command loop.
+ *
+ * Let's begin with the basics; parsing the line and
+ * invoking the function...
+ */
+
+static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms,
+                           void *mconfig, const char *args)
+{
+    char *w, *w2, *w3;
+    const char *errmsg;
+
+    if ((parms->override & cmd->req_override) == 0)
+       return ap_pstrcat(parms->pool, cmd->name, " not allowed here", NULL);
+
+    parms->info = cmd->cmd_data;
+    parms->cmd = cmd;
+
+    switch (cmd->args_how) {
+    case RAW_ARGS:
+       return ((const char *(*)(cmd_parms *, void *, const char *))
+               (cmd->func)) (parms, mconfig, args);
+
+    case NO_ARGS:
+       if (*args != 0)
+           return ap_pstrcat(parms->pool, cmd->name, " takes no arguments",
+                          NULL);
+
+       return ((const char *(*)(cmd_parms *, void *))
+               (cmd->func)) (parms, mconfig);
+
+    case TAKE1:
+       w = ap_getword_conf(parms->pool, &args);
+
+       if (*w == '\0' || *args != 0)
+           return ap_pstrcat(parms->pool, cmd->name, " takes one argument",
+                           cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+       return ((const char *(*)(cmd_parms *, void *, const char *))
+               (cmd->func)) (parms, mconfig, w);
+
+    case TAKE2:
+
+       w = ap_getword_conf(parms->pool, &args);
+       w2 = ap_getword_conf(parms->pool, &args);
+
+       if (*w == '\0' || *w2 == '\0' || *args != 0)
+           return ap_pstrcat(parms->pool, cmd->name, " takes two arguments",
+                           cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+       return ((const char *(*)(cmd_parms *, void *, const char *,
+                       const char *)) (cmd->func)) (parms, mconfig, w, w2);
+
+    case TAKE12:
+
+       w = ap_getword_conf(parms->pool, &args);
+       w2 = ap_getword_conf(parms->pool, &args);
+
+       if (*w == '\0' || *args != 0)
+           return ap_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments",
+                           cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+       return ((const char *(*)(cmd_parms *, void *, const char *,
+                           const char *)) (cmd->func)) (parms, mconfig, w,
+                                                           *w2 ? w2 : NULL);
+
+    case TAKE3:
+
+       w = ap_getword_conf(parms->pool, &args);
+       w2 = ap_getword_conf(parms->pool, &args);
+       w3 = ap_getword_conf(parms->pool, &args);
+
+       if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0)
+           return ap_pstrcat(parms->pool, cmd->name, " takes three arguments",
+                           cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+       return ((const char *(*)(cmd_parms *, void *, const char *,
+                           const char *, const char *)) (cmd->func)) (parms,
+                                                       mconfig, w, w2, w3);
+
+    case TAKE23:
+
+       w = ap_getword_conf(parms->pool, &args);
+       w2 = ap_getword_conf(parms->pool, &args);
+       w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
+
+       if (*w == '\0' || *w2 == '\0' || *args != 0)
+           return ap_pstrcat(parms->pool, cmd->name,
+                           " takes two or three arguments",
+                           cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+       return ((const char *(*)(cmd_parms *, void *, const char *,
+                           const char *, const char *)) (cmd->func)) (parms,
+                                                       mconfig, w, w2, w3);
+
+    case TAKE123:
+
+       w = ap_getword_conf(parms->pool, &args);
+       w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
+       w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
+
+       if (*w == '\0' || *args != 0)
+           return ap_pstrcat(parms->pool, cmd->name,
+                           " takes one, two or three arguments",
+                           cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+       return ((const char *(*)(cmd_parms *, void *, const char *,
+                           const char *, const char *)) (cmd->func)) (parms,
+                                                       mconfig, w, w2, w3);
+
+    case TAKE13:
+
+       w = ap_getword_conf(parms->pool, &args);
+       w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
+       w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
+
+       if (*w == '\0' || (*w2 && !w3) || *args != 0)
+           return ap_pstrcat(parms->pool, cmd->name,
+                           " takes one or three arguments",
+                           cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+       return ((const char *(*)(cmd_parms *, void *, const char *,
+                           const char *, const char *)) (cmd->func)) (parms,
+                                                       mconfig, w, w2, w3);
+
+    case ITERATE:
+
+       while (*(w = ap_getword_conf(parms->pool, &args)) != '\0')
+       if   ((errmsg = ((const char *(*)(cmd_parms *, void *,
+                       const char *)) (cmd->func)) (parms, mconfig, w)))
+                   return errmsg;
+
+       return NULL;
+
+    case ITERATE2:
+
+       w = ap_getword_conf(parms->pool, &args);
+
+       if (*w == '\0' || *args == 0)
+           return ap_pstrcat(parms->pool, cmd->name,
+                           " requires at least two arguments",
+                           cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+
+       while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0')
+           if   ((errmsg = ((const char *(*)(cmd_parms *, void *,
+                           const char *, const char *)) (cmd->func)) (parms,
+                                                           mconfig, w, w2)))
+                       return errmsg;
+
+       return NULL;
+
+    case FLAG:
+
+       w = ap_getword_conf(parms->pool, &args);
+
+       if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off")))
+           return ap_pstrcat(parms->pool, cmd->name, " must be On or Off",
+                           NULL);
+
+       return ((const char *(*)(cmd_parms *, void *, int))
+               (cmd->func)) (parms, mconfig, strcasecmp(w, "off") != 0);
+
+    default:
+
+       return ap_pstrcat(parms->pool, cmd->name,
+                   " is improperly configured internally (server bug)",
+                       NULL);
+    }
+}
+
+CORE_EXPORT(const command_rec *) ap_find_command(const char *name, const command_rec *cmds)
+{
+    while (cmds->name)
+       if (!strcasecmp(name, cmds->name))
+           return cmds;
+       else
+           ++cmds;
+
+    return NULL;
+}
+
+CORE_EXPORT(const command_rec *) ap_find_command_in_modules(const char *cmd_name, module **mod)
+{
+    const command_rec *cmdp;
+    module *modp;
+
+    for (modp = *mod; modp; modp = modp->next)
+       if (modp->cmds && (cmdp = ap_find_command(cmd_name, modp->cmds))) {
+           *mod = modp;
+           return cmdp;
+       }
+
+    return NULL;
+}
+
+CORE_EXPORT(void *) ap_set_config_vectors(cmd_parms *parms, void *config, module *mod)
+{
+    void *mconfig = ap_get_module_config(config, mod);
+    void *sconfig = ap_get_module_config(parms->server->module_config, mod);
+
+    if (!mconfig && mod->create_dir_config) {
+       mconfig = (*mod->create_dir_config) (parms->pool, parms->path);
+       ap_set_module_config(config, mod, mconfig);
+    }
+
+    if (!sconfig && mod->create_server_config) {
+       sconfig = (*mod->create_server_config) (parms->pool, parms->server);
+       ap_set_module_config(parms->server->module_config, mod, sconfig);
+    }
+    return mconfig;
+}
+
+CORE_EXPORT(const char *) ap_handle_command(cmd_parms *parms, void *config, const char *l)
+{
+    void *oldconfig;
+    const char *args, *cmd_name, *retval;
+    const command_rec *cmd;
+    module *mod = top_module;
+
+    if ((l[0] == '#') || (!l[0]))
+       return NULL;
+
+    args = l;
+    cmd_name = ap_getword_conf(parms->temp_pool, &args);
+    if (*cmd_name == '\0')
+       return NULL;
+
+    oldconfig = parms->context;
+    parms->context = config;
+    do {
+       if (!(cmd = ap_find_command_in_modules(cmd_name, &mod))) {
+            errno = EINVAL;
+            return ap_pstrcat(parms->pool, "Invalid command '", cmd_name,
+                           "', perhaps mis-spelled or defined by a module "
+                           "not included in the server configuration", NULL);
+       }
+       else {
+           void *mconfig = ap_set_config_vectors(parms,config, mod);
+
+           retval = invoke_cmd(cmd, parms, mconfig, args);
+           mod = mod->next;    /* Next time around, skip this one */
+       }
+    } while (retval && !strcmp(retval, DECLINE_CMD));
+    parms->context = oldconfig;
+
+    return retval;
+}
+
+API_EXPORT(const char *) ap_srm_command_loop(cmd_parms *parms, void *config)
+{
+    char l[MAX_STRING_LEN];
+
+    while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) {
+       const char *errmsg = ap_handle_command(parms, config, l);
+        if (errmsg) {
+           return errmsg;
+       }
+    }
+
+    return NULL;
+}
+
+/*
+ * Generic command functions...
+ */
+
+API_EXPORT_NONSTD(const char *) ap_set_string_slot(cmd_parms *cmd,
+                                               char *struct_ptr, char *arg)
+{
+    /* This one's pretty generic... */
+
+    int offset = (int) (long) cmd->info;
+    *(char **) (struct_ptr + offset) = arg;
+    return NULL;
+}
+
+API_EXPORT_NONSTD(const char *) ap_set_string_slot_lower(cmd_parms *cmd,
+                                               char *struct_ptr, char *arg)
+{
+    /* This one's pretty generic... */
+
+    int offset = (int) (long) cmd->info;
+    ap_str_tolower(arg);
+    *(char **) (struct_ptr + offset) = arg;
+    return NULL;
+}
+
+API_EXPORT_NONSTD(const char *) ap_set_flag_slot(cmd_parms *cmd,
+                                             char *struct_ptr, int arg)
+{
+    /* This one's pretty generic too... */
+
+    int offset = (int) (long) cmd->info;
+    *(int *) (struct_ptr + offset) = arg ? 1 : 0;
+    return NULL;
+}
+
+API_EXPORT_NONSTD(const char *) ap_set_file_slot(cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+    /* Prepend server_root to relative arg.
+       This allows .htaccess to be independent of server_root,
+       so the server can be moved or mirrored with less pain.  */
+    char *p;
+    int offset = (int) (long) cmd->info;
+    if (ap_os_is_path_absolute(arg))
+       p = arg;
+    else
+       p = ap_make_full_path(cmd->pool, ap_server_root, arg);
+    *(char **) (struct_ptr + offset) = p;
+    return NULL;
+}
+
+/*****************************************************************
+ *
+ * Reading whole config files...
+ */
+
+static cmd_parms default_parms =
+{NULL, 0, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+API_EXPORT(char *) ap_server_root_relative(pool *p, char *file)
+{
+    if(ap_os_is_path_absolute(file))
+       return file;
+    return ap_make_full_path(p, ap_server_root, file);
+}
+
+
+/* This structure and the following functions are needed for the
+ * table-based config file reading. They are passed to the
+ * cfg_open_custom() routine.
+ */
+
+/* Structure to be passed to cfg_open_custom(): it contains an
+ * index which is incremented from 0 to nelts on each call to
+ * cfg_getline() (which in turn calls arr_elts_getstr())
+ * and an array_header pointer for the string array.
+ */
+typedef struct {
+    array_header *array;
+    int curr_idx;
+} arr_elts_param_t;
+
+
+/* arr_elts_getstr() returns the next line from the string array. */
+static void *arr_elts_getstr(void *buf, size_t bufsiz, void *param)
+{
+    arr_elts_param_t *arr_param = (arr_elts_param_t *) param;
+
+    /* End of array reached? */
+    if (++arr_param->curr_idx > arr_param->array->nelts)
+        return NULL;
+
+    /* return the line */
+    ap_cpystrn(buf, ((char **) arr_param->array->elts)[arr_param->curr_idx - 1], bufsiz);
+
+    return buf;
+}
+
+
+/* arr_elts_close(): dummy close routine (makes sure no more lines can be read) */
+static int arr_elts_close(void *param)
+{
+    arr_elts_param_t *arr_param = (arr_elts_param_t *) param;
+    arr_param->curr_idx = arr_param->array->nelts;
+    return 0;
+}
+
+static void process_command_config(server_rec *s, array_header *arr, pool *p,
+                                   pool *ptemp)
+{
+    const char *errmsg;
+    cmd_parms parms;
+    arr_elts_param_t arr_parms;
+
+    arr_parms.curr_idx = 0;
+    arr_parms.array = arr;
+
+    parms = default_parms;
+    parms.pool = p;
+    parms.temp_pool = ptemp;
+    parms.server = s;
+    parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT);
+    parms.config_file = ap_pcfg_open_custom(p, "-c/-C directives",
+                                         &arr_parms, NULL,
+                                         arr_elts_getstr, arr_elts_close);
+
+    errmsg = ap_srm_command_loop(&parms, s->lookup_defaults);
+
+    if (errmsg) {
+        fprintf(stderr, "Syntax error in -C/-c directive:\n%s\n", errmsg);
+        exit(1);
+    }
+
+    ap_cfg_closefile(parms.config_file);
+}
+
+void ap_process_resource_config(server_rec *s, char *fname, pool *p, pool *ptemp)
+{
+    const char *errmsg;
+    cmd_parms parms;
+    struct stat finfo;
+
+    fname = ap_server_root_relative(p, fname);
+
+    if (!(strcmp(fname, ap_server_root_relative(p, RESOURCE_CONFIG_FILE))) ||
+       !(strcmp(fname, ap_server_root_relative(p, ACCESS_CONFIG_FILE)))) {
+       if (stat(fname, &finfo) == -1)
+           return;
+    }
+
+    /* don't require conf/httpd.conf if we have a -C or -c switch */
+    if((ap_server_pre_read_config->nelts || ap_server_post_read_config->nelts) &&
+       !(strcmp(fname, ap_server_root_relative(p, SERVER_CONFIG_FILE)))) {
+       if (stat(fname, &finfo) == -1)
+           return;
+    }
+
+    /* GCC's initialization extensions are soooo nice here... */
+
+    parms = default_parms;
+    parms.pool = p;
+    parms.temp_pool = ptemp;
+    parms.server = s;
+    parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT);
+
+    if (!(parms.config_file = ap_pcfg_openfile(p,fname))) {
+       perror("fopen");
+       fprintf(stderr, "%s: could not open document config file %s\n",
+               ap_server_argv0, fname);
+       exit(1);
+    }
+
+    errmsg = ap_srm_command_loop(&parms, s->lookup_defaults);
+
+    if (errmsg) {
+       fprintf(stderr, "Syntax error on line %d of %s:\n",
+               parms.config_file->line_number, parms.config_file->name);
+       fprintf(stderr, "%s\n", errmsg);
+       exit(1);
+    }
+
+    ap_cfg_closefile(parms.config_file);
+}
+
+
+int ap_parse_htaccess(void **result, request_rec *r, int override,
+                  const char *d, const char *access_name)
+{
+    configfile_t *f = NULL;
+    cmd_parms parms;
+    const char *errmsg;
+    char *filename = NULL;
+    const struct htaccess_result *cache;
+    struct htaccess_result *new;
+    void *dc = NULL;
+
+/* firstly, search cache */
+    for (cache = r->htaccess; cache != NULL; cache = cache->next)
+       if (cache->override == override && strcmp(cache->dir, d) == 0) {
+           if (cache->htaccess != NULL)
+               *result = cache->htaccess;
+           return OK;
+       }
+
+    parms = default_parms;
+    parms.override = override;
+    parms.pool = r->pool;
+    parms.temp_pool = r->pool;
+    parms.server = r->server;
+    parms.path = ap_pstrdup(r->pool, d);
+
+    /* loop through the access names and find the first one */
+
+    while (access_name[0]) {
+        filename = ap_make_full_path(r->pool, d,
+                                     ap_getword_conf(r->pool, &access_name));
+
+        if ((f = ap_pcfg_openfile(r->pool, filename)) != NULL) {
+
+            dc = ap_create_per_dir_config(r->pool);
+
+            parms.config_file = f;
+
+            errmsg = ap_srm_command_loop(&parms, dc);
+
+            ap_cfg_closefile(f);
+
+            if (errmsg) {
+                ap_log_rerror(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, r,
+                              "%s: %s", filename, errmsg);
+                return HTTP_INTERNAL_SERVER_ERROR;
+            }
+            *result = dc;
+            break;
+        }
+        else if (errno != ENOENT && errno != ENOTDIR) {
+            ap_log_rerror(APLOG_MARK, APLOG_CRIT, r,
+                          "%s pcfg_openfile: unable to check htaccess file, "
+                          "ensure it is readable",
+                          filename);
+            ap_table_setn(r->notes, "error-notes",
+                          "Server unable to read htaccess file, denying "
+                          "access to be safe");
+            return HTTP_FORBIDDEN;
+        }
+    }
+
+/* cache it */
+    new = ap_palloc(r->pool, sizeof(struct htaccess_result));
+    new->dir = parms.path;
+    new->override = override;
+    new->htaccess = dc;
+/* add to head of list */
+    new->next = r->htaccess;
+    r->htaccess = new;
+
+    return OK;
+}
+
+
+CORE_EXPORT(const char *) ap_init_virtual_host(pool *p, const char *hostname,
+                             server_rec *main_server, server_rec **ps)
+{
+    server_rec *s = (server_rec *) ap_pcalloc(p, sizeof(server_rec));
+
+#ifdef RLIMIT_NOFILE
+    struct rlimit limits;
+
+    getrlimit(RLIMIT_NOFILE, &limits);
+    if (limits.rlim_cur < limits.rlim_max) {
+       limits.rlim_cur += 2;
+       if (setrlimit(RLIMIT_NOFILE, &limits) < 0) {
+           perror("setrlimit(RLIMIT_NOFILE)");
+           fprintf(stderr, "Cannot exceed hard limit for open files");
+       }
+    }
+#endif
+
+    s->server_admin = NULL;
+    s->server_hostname = NULL;
+    s->error_fname = NULL;
+    s->srm_confname = NULL;
+    s->access_confname = NULL;
+    s->timeout = 0;
+    s->keep_alive_timeout = 0;
+    s->keep_alive = -1;
+    s->keep_alive_max = -1;
+    s->error_log = main_server->error_log;
+    s->loglevel = main_server->loglevel;
+    /* useful default, otherwise we get a port of 0 on redirects */
+    s->port = main_server->port;
+    s->next = NULL;
+
+    s->is_virtual = 1;
+    s->names = ap_make_array(p, 4, sizeof(char **));
+    s->wild_names = ap_make_array(p, 4, sizeof(char **));
+
+    s->module_config = create_empty_config(p);
+    s->lookup_defaults = ap_create_per_dir_config(p);
+
+    s->server_uid = ap_user_id;
+    s->server_gid = ap_group_id;
+
+    s->limit_req_line = main_server->limit_req_line;
+    s->limit_req_fieldsize = main_server->limit_req_fieldsize;
+    s->limit_req_fields = main_server->limit_req_fields;
+
+    *ps = s;
+
+    return ap_parse_vhost_addrs(p, hostname, s);
+}
+
+
+static void fixup_virtual_hosts(pool *p, server_rec *main_server)
+{
+    server_rec *virt;
+
+    for (virt = main_server->next; virt; virt = virt->next) {
+       merge_server_configs(p, main_server->module_config,
+                            virt->module_config);
+
+       virt->lookup_defaults =
+           ap_merge_per_dir_configs(p, main_server->lookup_defaults,
+                                 virt->lookup_defaults);
+
+       if (virt->server_admin == NULL)
+           virt->server_admin = main_server->server_admin;
+
+       if (virt->srm_confname == NULL)
+           virt->srm_confname = main_server->srm_confname;
+
+       if (virt->access_confname == NULL)
+           virt->access_confname = main_server->access_confname;
+
+       if (virt->timeout == 0)
+           virt->timeout = main_server->timeout;
+
+       if (virt->keep_alive_timeout == 0)
+           virt->keep_alive_timeout = main_server->keep_alive_timeout;
+
+       if (virt->keep_alive == -1)
+           virt->keep_alive = main_server->keep_alive;
+
+       if (virt->keep_alive_max == -1)
+           virt->keep_alive_max = main_server->keep_alive_max;
+
+       if (virt->send_buffer_size == 0)
+           virt->send_buffer_size = main_server->send_buffer_size;
+
+       /* XXX: this is really something that should be dealt with by a
+        * post-config api phase */
+       ap_core_reorder_directories(p, virt);
+    }
+    ap_core_reorder_directories(p, main_server);
+}
+
+/*****************************************************************
+ *
+ * Getting *everything* configured... 
+ */
+
+static void init_config_globals(pool *p)
+{
+    /* ServerRoot, server_confname set in httpd.c */
+
+    ap_standalone = 1;
+    ap_user_name = DEFAULT_USER;
+    ap_user_id = ap_uname2id(DEFAULT_USER);
+    ap_group_id = ap_gname2id(DEFAULT_GROUP);
+    ap_daemons_to_start = DEFAULT_START_DAEMON;
+    ap_daemons_min_free = DEFAULT_MIN_FREE_DAEMON;
+    ap_daemons_max_free = DEFAULT_MAX_FREE_DAEMON;
+    ap_daemons_limit = HARD_SERVER_LIMIT;
+    ap_pid_fname = DEFAULT_PIDLOG;
+    ap_scoreboard_fname = DEFAULT_SCOREBOARD;
+    ap_lock_fname = DEFAULT_LOCKFILE;
+    ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
+    ap_bind_address.s_addr = htonl(INADDR_ANY);
+    ap_listeners = NULL;
+    ap_listenbacklog = DEFAULT_LISTENBACKLOG;
+    ap_extended_status = 0;
+
+    /* Global virtual host hash bucket pointers.  Init to null. */
+    ap_init_vhost_config(p);
+
+    ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+}
+
+static server_rec *init_server_config(pool *p)
+{
+    server_rec *s = (server_rec *) ap_pcalloc(p, sizeof(server_rec));
+
+    s->port = 0;
+    s->server_admin = DEFAULT_ADMIN;
+    s->server_hostname = NULL;
+    s->error_fname = DEFAULT_ERRORLOG;
+    s->error_log = stderr;
+    s->loglevel = DEFAULT_LOGLEVEL;
+    s->srm_confname = RESOURCE_CONFIG_FILE;
+    s->access_confname = ACCESS_CONFIG_FILE;
+    s->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE;
+    s->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE;
+    s->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS;
+    s->timeout = DEFAULT_TIMEOUT;
+    s->keep_alive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
+    s->keep_alive_max = DEFAULT_KEEPALIVE;
+    s->keep_alive = 1;
+    s->next = NULL;
+    s->addrs = ap_pcalloc(p, sizeof(server_addr_rec));
+    /* NOT virtual host; don't match any real network interface */
+    s->addrs->host_addr.s_addr = htonl(INADDR_ANY);
+    s->addrs->host_port = 0;   /* matches any port */
+    s->addrs->virthost = "";   /* must be non-NULL */
+    s->names = s->wild_names = NULL;
+
+    s->module_config = create_server_config(p, s);
+    s->lookup_defaults = create_default_per_dir_config(p);
+
+    return s;
+}
+
+
+static void default_listeners(pool *p, server_rec *s)
+{
+    listen_rec *new;
+
+    if (ap_listeners != NULL) {
+       return;
+    }
+    /* allocate a default listener */
+    new = ap_pcalloc(p, sizeof(listen_rec));
+    new->local_addr.sin_family = AF_INET;
+    new->local_addr.sin_addr = ap_bind_address;
+    new->local_addr.sin_port = htons(s->port ? s->port : DEFAULT_HTTP_PORT);
+    new->fd = -1;
+    new->used = 0;
+    new->next = NULL;
+    ap_listeners = new;
+}
+
+
+server_rec *ap_read_config(pool *p, pool *ptemp, char *confname)
+{
+    server_rec *s = init_server_config(p);
+
+    init_config_globals(p);
+
+    /* All server-wide config files now have the SAME syntax... */
+
+    process_command_config(s, ap_server_pre_read_config, p, ptemp);
+
+    ap_process_resource_config(s, confname, p, ptemp);
+    ap_process_resource_config(s, s->srm_confname, p, ptemp);
+    ap_process_resource_config(s, s->access_confname, p, ptemp);
+
+    process_command_config(s, ap_server_post_read_config, p, ptemp);
+
+    fixup_virtual_hosts(p, s);
+    default_listeners(p, s);
+    ap_fini_vhost_config(p, s);
+
+    return s;
+}
+
+void ap_single_module_configure(pool *p, server_rec *s, module *m)
+{
+    if (m->create_server_config)
+        ap_set_module_config(s->module_config, m,
+                             (*m->create_server_config)(p, s));
+    if (m->create_dir_config)
+        ap_set_module_config(s->lookup_defaults, m,
+                             (*m->create_dir_config)(p, NULL));
+}
+
+void ap_init_modules(pool *p, server_rec *s)
+{
+    module *m;
+
+    for (m = top_module; m; m = m->next)
+       if (m->init)
+           (*m->init) (s, p);
+    build_method_shortcuts();
+    init_handlers(p);
+}
+
+void ap_child_init_modules(pool *p, server_rec *s)
+{
+    module *m;
+
+    for (m = top_module; m; m = m->next)
+       if (m->child_init)
+           (*m->child_init) (s, p);
+}
+
+void ap_child_exit_modules(pool *p, server_rec *s)
+{
+    module *m;
+
+#ifdef SIGHUP
+    signal(SIGHUP, SIG_IGN);
+#endif
+#ifdef SIGUSR1
+    signal(SIGUSR1, SIG_IGN);
+#endif
+
+    for (m = top_module; m; m = m->next)
+       if (m->child_exit)
+           (*m->child_exit) (s, p);
+
+}
+
+/********************************************************************
+ * Configuration directives are restricted in terms of where they may
+ * appear in the main configuration files and/or .htaccess files according
+ * to the bitmask req_override in the command_rec structure.
+ * If any of the overrides set in req_override are also allowed in the
+ * context in which the command is read, then the command is allowed.
+ * The context is determined as follows:
+ *
+ *    inside *.conf --> override = (RSRC_CONF|OR_ALL)&~(OR_AUTHCFG|OR_LIMIT);
+ *    within <Directory> or <Location> --> override = OR_ALL|ACCESS_CONF;
+ *    within .htaccess --> override = AllowOverride for current directory;
+ *
+ * the result is, well, a rather confusing set of possibilities for when
+ * a particular directive is allowed to be used.  This procedure prints
+ * in English where the given (pc) directive can be used.
+ */
+static void show_overrides(const command_rec *pc, module *pm)
+{
+    int n = 0;
+
+    printf("\tAllowed in *.conf ");
+    if ((pc->req_override & (OR_OPTIONS | OR_FILEINFO | OR_INDEXES)) ||
+       ((pc->req_override & RSRC_CONF) &&
+        ((pc->req_override & (ACCESS_CONF | OR_AUTHCFG | OR_LIMIT)))))
+       printf("anywhere");
+    else if (pc->req_override & RSRC_CONF)
+       printf("only outside <Directory>, <Files> or <Location>");
+    else
+       printf("only inside <Directory>, <Files> or <Location>");
+
+    /* Warn if the directive is allowed inside <Directory> or .htaccess
+     * but module doesn't support per-dir configuration */
+
+    if ((pc->req_override & (OR_ALL | ACCESS_CONF)) && !pm->create_dir_config)
+       printf(" [no per-dir config]");
+
+    if (pc->req_override & OR_ALL) {
+       printf(" and in .htaccess\n\twhen AllowOverride");
+
+       if ((pc->req_override & OR_ALL) == OR_ALL)
+           printf(" isn't None");
+       else {
+           printf(" includes ");
+
+           if (pc->req_override & OR_AUTHCFG) {
+               if (n++)
+                   printf(" or ");
+               printf("AuthConfig");
+           }
+           if (pc->req_override & OR_LIMIT) {
+               if (n++)
+                   printf(" or ");
+               printf("Limit");
+           }
+           if (pc->req_override & OR_OPTIONS) {
+               if (n++)
+                   printf(" or ");
+               printf("Options");
+           }
+           if (pc->req_override & OR_FILEINFO) {
+               if (n++)
+                   printf(" or ");
+               printf("FileInfo");
+           }
+           if (pc->req_override & OR_INDEXES) {
+               if (n++)
+                   printf(" or ");
+               printf("Indexes");
+           }
+       }
+    }
+    printf("\n");
+}
+
+/* Show the preloaded configuration directives, the help string explaining
+ * the directive arguments, in what module they are handled, and in
+ * what parts of the configuration they are allowed.  Used for httpd -h.
+ */
+void ap_show_directives()
+{
+    const command_rec *pc;
+    int n;
+
+    for (n = 0; ap_loaded_modules[n]; ++n)
+       for (pc = ap_loaded_modules[n]->cmds; pc && pc->name; ++pc) {
+           printf("%s (%s)\n", pc->name, ap_loaded_modules[n]->name);
+           if (pc->errmsg)
+               printf("\t%s\n", pc->errmsg);
+           show_overrides(pc, ap_loaded_modules[n]);
+       }
+}
+
+/* Show the preloaded module names.  Used for httpd -l. */
+void ap_show_modules()
+{
+    int n;
+
+    printf("Compiled-in modules:\n");
+    for (n = 0; ap_loaded_modules[n]; ++n)
+       printf("  %s\n", ap_loaded_modules[n]->name);
+}
diff --git a/server/gen_test_char.c b/server/gen_test_char.c
new file mode 100644 (file)
index 0000000..dc33a73
--- /dev/null
@@ -0,0 +1,62 @@
+/* we need some of the portability definitions... for strchr */
+#include "httpd.h"
+
+/* A bunch of functions in util.c scan strings looking for certain characters.
+ * To make that more efficient we encode a lookup table.
+ */
+#define T_ESCAPE_SHELL_CMD     (0x01)
+#define T_ESCAPE_PATH_SEGMENT  (0x02)
+#define T_OS_ESCAPE_PATH       (0x04)
+#define T_HTTP_TOKEN_STOP      (0x08)
+
+int main(int argc, char *argv[])
+{
+    unsigned c;
+    unsigned char flags;
+
+    printf(
+"/* this file is automatically generated by gen_test_char, do not edit */\n"
+"#define T_ESCAPE_SHELL_CMD    (%u)\n"
+"#define T_ESCAPE_PATH_SEGMENT (%u)\n"
+"#define T_OS_ESCAPE_PATH      (%u)\n"
+"#define T_HTTP_TOKEN_STOP     (%u)\n"
+"\n"
+"static const unsigned char test_char_table[256] = {\n"
+"    0,",
+       T_ESCAPE_SHELL_CMD,
+       T_ESCAPE_PATH_SEGMENT,
+       T_OS_ESCAPE_PATH,
+       T_HTTP_TOKEN_STOP);
+
+    /* we explicitly dealt with NUL above
+     * in case some strchr() do bogosity with it */
+
+    for (c = 1; c < 256; ++c) {
+       flags = 0;
+       if (c % 20 == 0)
+           printf("\n    ");
+
+       /* escape_shell_cmd */
+       if (strchr("&;`'\"|*?~<>^()[]{}$\\\n", c)) {
+           flags |= T_ESCAPE_SHELL_CMD;
+       }
+
+       if (!ap_isalnum(c) && !strchr("$-_.+!*'(),:@&=~", c)) {
+           flags |= T_ESCAPE_PATH_SEGMENT;
+       }
+
+       if (!ap_isalnum(c) && !strchr("$-_.+!*'(),:@&=/~", c)) {
+           flags |= T_OS_ESCAPE_PATH;
+       }
+
+       /* these are the "tspecials" from RFC2068 */
+       if (ap_iscntrl(c) || strchr(" \t()<>@,;:\\/[]?={}", c)) {
+           flags |= T_HTTP_TOKEN_STOP;
+       }
+       printf("%u%c", flags, (c < 255) ? ',' : ' ');
+
+    }
+    printf("\n};\n");
+
+    return 0;
+}
diff --git a/server/gen_test_char.dsp b/server/gen_test_char.dsp
new file mode 100644 (file)
index 0000000..c2630ce
--- /dev/null
@@ -0,0 +1,105 @@
+# Microsoft Developer Studio Project File - Name="gen_test_char" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=gen_test_char - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "gen_test_char.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "gen_test_char.mak" CFG="gen_test_char - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "gen_test_char - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "gen_test_char - Win32 Debug" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "gen_test_char - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "gen_test"
+# PROP BASE Intermediate_Dir "gen_test"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "gen_test_char_R"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /machine:I386
+# Begin Special Build Tool
+SOURCE=$(InputPath)
+PostBuild_Desc=Create test_char.h
+PostBuild_Cmds=.\gen_test_char > test_char.h
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "gen_test_char - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "gen_tes0"
+# PROP BASE Intermediate_Dir "gen_tes0"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "gen_test_char_D"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# Begin Special Build Tool
+SOURCE=$(InputPath)
+PostBuild_Desc=Create test_char.h
+PostBuild_Cmds=.\gen_test_char > test_char.h
+# End Special Build Tool
+
+!ENDIF 
+
+# Begin Target
+
+# Name "gen_test_char - Win32 Release"
+# Name "gen_test_char - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\gen_test_char.c
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/server/gen_uri_delims.c b/server/gen_uri_delims.c
new file mode 100644 (file)
index 0000000..15a73fb
--- /dev/null
@@ -0,0 +1,31 @@
+#include <stdio.h>
+
+/* generate a table of 256 values, where certain characters are
+ * marked "interesting"... for the uri parsing process.
+ */
+
+int main(int argc, char *argv[])
+{
+    int i;
+    char *value;
+
+    printf("/* this file is automatically generated by "
+           "gen_uri_delims, do not edit */\n");
+    printf("static const unsigned char uri_delims[256] = {");
+    for (i = 0; i < 256; ++i) {
+       if (i % 20 == 0)
+           printf("\n    ");
+       switch (i) {
+       case ':':       value = "T_COLON";      break;
+       case '/':       value = "T_SLASH";      break;
+       case '?':       value = "T_QUESTION";   break;
+       case '#':       value = "T_HASH";       break;
+       case '\0':      value = "T_NUL";        break;
+       default:        value = "0";            break;
+       }
+       printf("%s%c", value, (i < 255) ? ',' : ' ');
+    }
+    printf("\n};\n");
+
+    return 0;
+}
diff --git a/server/gen_uri_delims.dsp b/server/gen_uri_delims.dsp
new file mode 100644 (file)
index 0000000..566dd83
--- /dev/null
@@ -0,0 +1,105 @@
+# Microsoft Developer Studio Project File - Name="gen_uri_delims" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=gen_uri_delims - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "gen_uri_delims.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "gen_uri_delims.mak" CFG="gen_uri_delims - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "gen_uri_delims - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "gen_uri_delims - Win32 Debug" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "gen_uri_delims - 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 ""
+# PROP Intermediate_Dir "gen_uri_delims_R"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /machine:I386
+# Begin Special Build Tool
+SOURCE=$(InputPath)
+PostBuild_Desc=Create uri_delims.h
+PostBuild_Cmds=.\gen_uri_delims > uri_delims.h
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "gen_uri_delims - 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 ""
+# PROP Intermediate_Dir "gen_uri_delims_D"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# Begin Special Build Tool
+SOURCE=$(InputPath)
+PostBuild_Desc=Create uri_delims.h
+PostBuild_Cmds=.\gen_uri_delims > uri_delims.h
+# End Special Build Tool
+
+!ENDIF 
+
+# Begin Target
+
+# Name "gen_uri_delims - Win32 Release"
+# Name "gen_uri_delims - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\gen_uri_delims.c
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/server/log.c b/server/log.c
new file mode 100644 (file)
index 0000000..7a1e0ea
--- /dev/null
@@ -0,0 +1,777 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_log.c: Dealing with the logs and errors
+ * 
+ * Rob McCool
+ * 
+ */
+
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_conf_globals.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_main.h"
+
+#include <stdarg.h>
+
+typedef struct {
+       char    *t_name;
+       int     t_val;
+} TRANS;
+
+#ifdef HAVE_SYSLOG
+
+static const TRANS facilities[] = {
+    {"auth",   LOG_AUTH},
+#ifdef LOG_AUTHPRIV
+    {"authpriv",LOG_AUTHPRIV},
+#endif
+#ifdef LOG_CRON
+    {"cron",   LOG_CRON},
+#endif
+#ifdef LOG_DAEMON
+    {"daemon", LOG_DAEMON},
+#endif
+#ifdef LOG_FTP
+    {"ftp",    LOG_FTP},
+#endif
+#ifdef LOG_KERN
+    {"kern",   LOG_KERN},
+#endif
+#ifdef LOG_LPR
+    {"lpr",    LOG_LPR},
+#endif
+#ifdef LOG_MAIL
+    {"mail",   LOG_MAIL},
+#endif
+#ifdef LOG_NEWS
+    {"news",   LOG_NEWS},
+#endif
+#ifdef LOG_SYSLOG
+    {"syslog", LOG_SYSLOG},
+#endif
+#ifdef LOG_USER
+    {"user",   LOG_USER},
+#endif
+#ifdef LOG_UUCP
+    {"uucp",   LOG_UUCP},
+#endif
+#ifdef LOG_LOCAL0
+    {"local0", LOG_LOCAL0},
+#endif
+#ifdef LOG_LOCAL1
+    {"local1", LOG_LOCAL1},
+#endif
+#ifdef LOG_LOCAL2
+    {"local2", LOG_LOCAL2},
+#endif
+#ifdef LOG_LOCAL3
+    {"local3", LOG_LOCAL3},
+#endif
+#ifdef LOG_LOCAL4
+    {"local4", LOG_LOCAL4},
+#endif
+#ifdef LOG_LOCAL5
+    {"local5", LOG_LOCAL5},
+#endif
+#ifdef LOG_LOCAL6
+    {"local6", LOG_LOCAL6},
+#endif
+#ifdef LOG_LOCAL7
+    {"local7", LOG_LOCAL7},
+#endif
+    {NULL,             -1},
+};
+#endif
+
+static const TRANS priorities[] = {
+    {"emerg",  APLOG_EMERG},
+    {"alert",  APLOG_ALERT},
+    {"crit",   APLOG_CRIT},
+    {"error",  APLOG_ERR},
+    {"warn",   APLOG_WARNING},
+    {"notice", APLOG_NOTICE},
+    {"info",   APLOG_INFO},
+    {"debug",  APLOG_DEBUG},
+    {NULL,     -1},
+};
+
+static int error_log_child(void *cmd, child_info *pinfo)
+{
+    /* Child process code for 'ErrorLog "|..."';
+     * may want a common framework for this, since I expect it will
+     * be common for other foo-loggers to want this sort of thing...
+     */
+    int child_pid = 0;
+
+    ap_cleanup_for_exec();
+#ifdef SIGHUP
+    /* No concept of a child process on Win32 */
+    signal(SIGHUP, SIG_IGN);
+#endif /* ndef SIGHUP */
+#if defined(WIN32)
+    child_pid = spawnl(_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+    return(child_pid);
+#elif defined(OS2)
+    /* For OS/2 we need to use a '/' and spawn the child rather than exec as
+     * we haven't forked */
+    child_pid = spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+    return(child_pid);
+#else    
+    execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif    
+    exit(1);
+    /* NOT REACHED */
+    return(child_pid);
+}
+
+static void open_error_log(server_rec *s, pool *p)
+{
+    char *fname;
+
+    if (*s->error_fname == '|') {
+       FILE *dummy;
+#ifdef TPF
+        TPF_FORK_CHILD cld;
+        cld.filename = s->error_fname+1;
+        cld.subprocess_env = NULL;
+        cld.prog_type = FORK_NAME;
+        if (!ap_spawn_child(p, NULL, &cld,
+                            kill_after_timeout, &dummy, NULL, NULL)) {
+#else
+       if (!ap_spawn_child(p, error_log_child, (void *)(s->error_fname+1),
+                           kill_after_timeout, &dummy, NULL, NULL)) {
+#endif /* TPF */
+           perror("ap_spawn_child");
+           fprintf(stderr, "Couldn't fork child for ErrorLog process\n");
+           exit(1);
+       }
+
+       s->error_log = dummy;
+    }
+
+#ifdef HAVE_SYSLOG
+    else if (!strncasecmp(s->error_fname, "syslog", 6)) {
+       if ((fname = strchr(s->error_fname, ':'))) {
+           const TRANS *fac;
+
+           fname++;
+           for (fac = facilities; fac->t_name; fac++) {
+               if (!strcasecmp(fname, fac->t_name)) {
+                   openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID,
+                           fac->t_val);
+                   s->error_log = NULL;
+                   return;
+               }
+           }
+       }
+       else
+           openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID, LOG_LOCAL7);
+
+       s->error_log = NULL;
+    }
+#endif
+    else {
+       fname = ap_server_root_relative(p, s->error_fname);
+        if (!(s->error_log = ap_pfopen(p, fname, "a"))) {
+            perror("fopen");
+            fprintf(stderr, "%s: could not open error log file %s.\n",
+                   ap_server_argv0, fname);
+            exit(1);
+       }
+    }
+}
+
+void ap_open_logs(server_rec *s_main, pool *p)
+{
+    server_rec *virt, *q;
+    int replace_stderr;
+
+    open_error_log(s_main, p);
+
+    replace_stderr = 1;
+    if (s_main->error_log) {
+       /* replace stderr with this new log */
+       fflush(stderr);
+       if (dup2(fileno(s_main->error_log), STDERR_FILENO) == -1) {
+           ap_log_error(APLOG_MARK, APLOG_CRIT, s_main,
+               "unable to replace stderr with error_log");
+       } else {
+           replace_stderr = 0;
+       }
+    }
+    /* note that stderr may still need to be replaced with something
+     * because it points to the old error log, or back to the tty
+     * of the submitter.
+     */
+    if (replace_stderr && freopen("/dev/null", "w", stderr) == NULL) {
+       ap_log_error(APLOG_MARK, APLOG_CRIT, s_main,
+           "unable to replace stderr with /dev/null");
+    }
+
+    for (virt = s_main->next; virt; virt = virt->next) {
+       if (virt->error_fname)
+       {
+           for (q=s_main; q != virt; q = q->next)
+               if (q->error_fname != NULL &&
+                   strcmp(q->error_fname, virt->error_fname) == 0)
+                   break;
+           if (q == virt)
+               open_error_log(virt, p);
+           else 
+               virt->error_log = q->error_log;
+       }
+       else
+           virt->error_log = s_main->error_log;
+    }
+}
+
+API_EXPORT(void) ap_error_log2stderr(server_rec *s) {
+    if (   s->error_log != NULL
+        && fileno(s->error_log) != STDERR_FILENO)
+        dup2(fileno(s->error_log), STDERR_FILENO);
+}
+
+static void log_error_core(const char *file, int line, int level,
+                          const server_rec *s, const request_rec *r,
+                          const char *fmt, va_list args)
+{
+    char errstr[MAX_STRING_LEN];
+    size_t len;
+    int save_errno = errno;
+    FILE *logf;
+
+    if (s == NULL) {
+       /*
+        * If we are doing stderr logging (startup), don't log messages that are
+        * above the default server log level unless it is a startup/shutdown
+        * notice
+        */
+       if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) &&
+           ((level & APLOG_LEVELMASK) > DEFAULT_LOGLEVEL))
+           return;
+       logf = stderr;
+    }
+    else if (s->error_log) {
+       /*
+        * If we are doing normal logging, don't log messages that are
+        * above the server log level unless it is a startup/shutdown notice
+        */
+       if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) &&
+           ((level & APLOG_LEVELMASK) > s->loglevel))
+           return;
+       logf = s->error_log;
+    }
+#ifdef TPF
+    else if (tpf_child) {
+    /*
+     * If we are doing normal logging, don't log messages that are
+     * above the server log level unless it is a startup/shutdown notice
+     */
+    if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) &&
+        ((level & APLOG_LEVELMASK) > s->loglevel))
+        return;
+    logf = stderr;
+    }
+#endif /* TPF */
+    else {
+       /*
+        * If we are doing syslog logging, don't log messages that are
+        * above the server log level (including a startup/shutdown notice)
+        */
+       if ((level & APLOG_LEVELMASK) > s->loglevel)
+           return;
+       logf = NULL;
+    }
+
+    if (logf) {
+       len = ap_snprintf(errstr, sizeof(errstr), "[%s] ", ap_get_time());
+    } else {
+       len = 0;
+    }
+
+    len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+           "[%s] ", priorities[level & APLOG_LEVELMASK].t_name);
+
+#ifndef TPF
+    if (file && (level & APLOG_LEVELMASK) == APLOG_DEBUG) {
+#ifdef _OSD_POSIX
+       char tmp[256];
+       char *e = strrchr(file, '/');
+
+       /* In OSD/POSIX, the compiler returns for __FILE__
+        * a string like: __FILE__="*POSIX(/usr/include/stdio.h)"
+        * (it even returns an absolute path for sources in
+        * the current directory). Here we try to strip this
+        * down to the basename.
+        */
+       if (e != NULL && e[1] != '\0') {
+           ap_snprintf(tmp, sizeof(tmp), "%s", &e[1]);
+           e = &tmp[strlen(tmp)-1];
+           if (*e == ')')
+               *e = '\0';
+           file = tmp;
+       }
+#endif /*_OSD_POSIX*/
+       len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+               "%s(%d): ", file, line);
+    }
+#endif /* TPF */
+    if (r) {
+       /* XXX: TODO: add a method of selecting whether logged client
+        * addresses are in dotted quad or resolved form... dotted
+        * quad is the most secure, which is why I'm implementing it
+        * first. -djg
+        */
+       len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+               "[client %s] ", r->connection->remote_ip);
+    }
+    if (!(level & APLOG_NOERRNO)
+       && (save_errno != 0)
+#ifdef WIN32
+       && !(level & APLOG_WIN32ERROR)
+#endif
+       ) {
+       len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+               "(%d)%s: ", save_errno, strerror(save_errno));
+    }
+#ifdef WIN32
+    if (level & APLOG_WIN32ERROR) {
+       int nChars;
+       int nErrorCode;
+
+       nErrorCode = GetLastError();
+       len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+           "(%d)", nErrorCode);
+
+       nChars = FormatMessage( 
+           FORMAT_MESSAGE_FROM_SYSTEM,
+           NULL,
+           nErrorCode,
+           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+           (LPTSTR) errstr + len,
+           sizeof(errstr) - len,
+           NULL 
+       );
+       len += nChars;
+       if (nChars == 0) {
+           /* Um, error occurred, but we can't recurse to log it again
+            * (and it would probably only fail anyway), so lets just
+            * log the numeric value.
+            */
+           nErrorCode = GetLastError();
+           len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+                              "(FormatMessage failed with code %d): ",
+                              nErrorCode);
+       }
+       else {
+           /* FormatMessage put the message in the buffer, but it may
+            * have appended a newline (\r\n). So remove it and use
+            * ": " instead like the Unix errors. The error may also
+            * end with a . before the return - if so, trash it.
+            */
+           if (len > 1 && errstr[len-2] == '\r' && errstr[len-1] == '\n') {
+               if (len > 2 && errstr[len-3] == '.')
+                   len--;
+               errstr[len-2] = ':';
+               errstr[len-1] = ' ';
+           }
+       }
+    }
+#endif
+
+    len += ap_vsnprintf(errstr + len, sizeof(errstr) - len, fmt, args);
+
+    /* NULL if we are logging to syslog */
+    if (logf) {
+       fputs(errstr, logf);
+       fputc('\n', logf);
+       fflush(logf);
+    }
+#ifdef HAVE_SYSLOG
+    else {
+       syslog(level & APLOG_LEVELMASK, "%s", errstr);
+    }
+#endif
+}
+    
+API_EXPORT(void) ap_log_error(const char *file, int line, int level,
+                             const server_rec *s, const char *fmt, ...)
+{
+    va_list args;
+
+    va_start(args, fmt);
+    log_error_core(file, line, level, s, NULL, fmt, args);
+    va_end(args);
+}
+
+API_EXPORT(void) ap_log_rerror(const char *file, int line, int level,
+                              const request_rec *r, const char *fmt, ...)
+{
+    va_list args;
+
+    va_start(args, fmt);
+    log_error_core(file, line, level, r->server, r, fmt, args);
+    /*
+     * IF the error level is 'warning' or more severe,
+     * AND there isn't already error text associated with this request,
+     * THEN make the message text available to ErrorDocument and
+     * other error processors.  This can be disabled by stuffing
+     * something, even an empty string, into the "error-notes" cell
+     * before calling this routine.
+     */
+    va_end(args);
+    va_start(args,fmt); 
+    if (((level & APLOG_LEVELMASK) <= APLOG_WARNING)
+       && (ap_table_get(r->notes, "error-notes") == NULL)) {
+       ap_table_setn(r->notes, "error-notes",
+                     ap_pvsprintf(r->pool, fmt, args));
+    }
+    va_end(args);
+}
+
+void ap_log_pid(pool *p, char *fname)
+{
+    FILE *pid_file;
+    struct stat finfo;
+    static pid_t saved_pid = -1;
+    pid_t mypid;
+
+    if (!fname) 
+       return;
+
+    fname = ap_server_root_relative(p, fname);
+    mypid = getpid();
+    if (mypid != saved_pid && stat(fname, &finfo) == 0) {
+      /* USR1 and HUP call this on each restart.
+       * Only warn on first time through for this pid.
+       *
+       * XXX: Could just write first time through too, although
+       *      that may screw up scripts written to do something
+       *      based on the last modification time of the pid file.
+       */
+      ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+                  ap_psprintf(p,
+                              "pid file %s overwritten -- Unclean shutdown of previous Apache run?",
+                              fname)
+                  );
+    }
+
+    if(!(pid_file = fopen(fname, "w"))) {
+       perror("fopen");
+        fprintf(stderr, "%s: could not log pid to file %s\n",
+               ap_server_argv0, fname);
+        exit(1);
+    }
+    fprintf(pid_file, "%ld\n", (long)mypid);
+    fclose(pid_file);
+    saved_pid = mypid;
+}
+
+API_EXPORT(void) ap_log_error_old(const char *err, server_rec *s)
+{
+    ap_log_error(APLOG_MARK, APLOG_ERR, s, "%s", err);
+}
+
+API_EXPORT(void) ap_log_unixerr(const char *routine, const char *file,
+                             const char *msg, server_rec *s)
+{
+    ap_log_error(file, 0, APLOG_ERR, s, "%s", msg);
+}
+
+API_EXPORT(void) ap_log_printf(const server_rec *s, const char *fmt, ...)
+{
+    va_list args;
+    
+    va_start(args, fmt);
+    log_error_core(APLOG_MARK, APLOG_ERR, s, NULL, fmt, args);
+    va_end(args);
+}
+
+API_EXPORT(void) ap_log_reason(const char *reason, const char *file, request_rec *r) 
+{
+    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+               "access to %s failed for %s, reason: %s",
+               file,
+               ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME),
+               reason);
+}
+
+API_EXPORT(void) ap_log_assert(const char *szExp, const char *szFile, int nLine)
+{
+    fprintf(stderr, "[%s] file %s, line %d, assertion \"%s\" failed\n",
+           ap_get_time(), szFile, nLine, szExp);
+#ifndef WIN32
+    /* unix assert does an abort leading to a core dump */
+    abort();
+#else
+    exit(1);
+#endif
+}
+
+/* piped log support */
+
+#ifndef NO_RELIABLE_PIPED_LOGS
+/* forward declaration */
+static void piped_log_maintenance(int reason, void *data, ap_wait_t status);
+
+static int piped_log_spawn(piped_log *pl)
+{
+    int pid;
+
+    ap_block_alarms();
+    pid = fork();
+    if (pid == 0) {
+       /* XXX: this needs porting to OS2 and WIN32 */
+       /* XXX: need to check what open fds the logger is actually passed,
+        * XXX: and CGIs for that matter ... cleanup_for_exec *should*
+        * XXX: close all the relevant stuff, but hey, it could be broken. */
+       RAISE_SIGSTOP(PIPED_LOG_SPAWN);
+       /* we're now in the child */
+       close(STDIN_FILENO);
+       dup2(pl->fds[0], STDIN_FILENO);
+
+       ap_cleanup_for_exec();
+       signal(SIGCHLD, SIG_DFL);       /* for HPUX */
+       signal(SIGHUP, SIG_IGN);
+       execl(SHELL_PATH, SHELL_PATH, "-c", pl->program, NULL);
+       fprintf(stderr,
+           "piped_log_spawn: unable to exec %s -c '%s': %s\n",
+           SHELL_PATH, pl->program, strerror (errno));
+       exit(1);
+    }
+    if (pid == -1) {
+       fprintf(stderr,
+           "piped_log_spawn: unable to fork(): %s\n", strerror (errno));
+       ap_unblock_alarms();
+       return -1;
+    }
+    ap_unblock_alarms();
+    pl->pid = pid;
+    ap_register_other_child(pid, piped_log_maintenance, pl, pl->fds[1]);
+    return 0;
+}
+
+
+static void piped_log_maintenance(int reason, void *data, ap_wait_t status)
+{
+    piped_log *pl = data;
+
+    switch (reason) {
+    case OC_REASON_DEATH:
+    case OC_REASON_LOST:
+       pl->pid = -1;
+       ap_unregister_other_child(pl);
+       if (pl->program == NULL) {
+           /* during a restart */
+           break;
+       }
+       if (piped_log_spawn(pl) == -1) {
+           /* what can we do?  This could be the error log we're having
+            * problems opening up... */
+           fprintf(stderr,
+               "piped_log_maintenance: unable to respawn '%s': %s\n",
+               pl->program, strerror(errno));
+       }
+       break;
+    
+    case OC_REASON_UNWRITABLE:
+       if (pl->pid != -1) {
+           kill(pl->pid, SIGTERM);
+       }
+       break;
+    
+    case OC_REASON_RESTART:
+       pl->program = NULL;
+       if (pl->pid != -1) {
+           kill(pl->pid, SIGTERM);
+       }
+       break;
+
+    case OC_REASON_UNREGISTER:
+       break;
+    }
+}
+
+
+static void piped_log_cleanup(void *data)
+{
+    piped_log *pl = data;
+
+    if (pl->pid != -1) {
+       kill(pl->pid, SIGTERM);
+    }
+    ap_unregister_other_child(pl);
+    close(pl->fds[0]);
+    close(pl->fds[1]);
+}
+
+
+static void piped_log_cleanup_for_exec(void *data)
+{
+    piped_log *pl = data;
+
+    close(pl->fds[0]);
+    close(pl->fds[1]);
+}
+
+
+API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program)
+{
+    piped_log *pl;
+
+    pl = ap_palloc(p, sizeof (*pl));
+    pl->p = p;
+    pl->program = ap_pstrdup(p, program);
+    pl->pid = -1;
+    ap_block_alarms ();
+    if (pipe(pl->fds) == -1) {
+       int save_errno = errno;
+       ap_unblock_alarms();
+       errno = save_errno;
+       return NULL;
+    }
+    ap_register_cleanup(p, pl, piped_log_cleanup, piped_log_cleanup_for_exec);
+    if (piped_log_spawn(pl) == -1) {
+       int save_errno = errno;
+       ap_kill_cleanup(p, pl, piped_log_cleanup);
+       close(pl->fds[0]);
+       close(pl->fds[1]);
+       ap_unblock_alarms();
+       errno = save_errno;
+       return NULL;
+    }
+    ap_unblock_alarms();
+    return pl;
+}
+
+API_EXPORT(void) ap_close_piped_log(piped_log *pl)
+{
+    ap_block_alarms();
+    piped_log_cleanup(pl);
+    ap_kill_cleanup(pl->p, pl, piped_log_cleanup);
+    ap_unblock_alarms();
+}
+
+#else
+static int piped_log_child(void *cmd, child_info *pinfo)
+{
+    /* Child process code for 'TransferLog "|..."';
+     * may want a common framework for this, since I expect it will
+     * be common for other foo-loggers to want this sort of thing...
+     */
+    int child_pid = 1;
+
+    ap_cleanup_for_exec();
+#ifdef SIGHUP
+    signal(SIGHUP, SIG_IGN);
+#endif
+#if defined(WIN32)
+    child_pid = spawnl(_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+    return(child_pid);
+#elif defined(OS2)
+    /* For OS/2 we need to use a '/' and spawn the child rather than exec as
+     * we haven't forked */
+    child_pid = spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+    return(child_pid);
+#else
+    execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+    perror("exec");
+    fprintf(stderr, "Exec of shell for logging failed!!!\n");
+    return(child_pid);
+}
+
+
+API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program)
+{
+    piped_log *pl;
+    FILE *dummy;
+#ifdef TPF
+    TPF_FORK_CHILD cld;
+    cld.filename = (char *)program;
+    cld.subprocess_env = NULL;
+    cld.prog_type = FORK_NAME;
+
+    if (!ap_spawn_child (p, NULL, &cld,
+      kill_after_timeout, &dummy, NULL, NULL)){
+#else
+    if (!ap_spawn_child(p, piped_log_child, (void *)program,
+                       kill_after_timeout, &dummy, NULL, NULL)) {
+#endif /* TPF */
+       perror("ap_spawn_child");
+       fprintf(stderr, "Couldn't fork child for piped log process\n");
+       exit (1);
+    }
+    pl = ap_palloc(p, sizeof (*pl));
+    pl->p = p;
+    pl->write_f = dummy;
+
+    return pl;
+}
+
+
+API_EXPORT(void) ap_close_piped_log(piped_log *pl)
+{
+    ap_pfclose(pl->p, pl->write_f);
+}
+#endif
diff --git a/server/main.c b/server/main.c
new file mode 100644 (file)
index 0000000..5bd372b
--- /dev/null
@@ -0,0 +1,6669 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * httpd.c: simple http daemon for answering WWW file requests
+ *
+ * 
+ * 03-21-93  Rob McCool wrote original code (up to NCSA HTTPd 1.3)
+ * 
+ * 03-06-95  blong
+ *  changed server number for child-alone processes to 0 and changed name
+ *   of processes
+ *
+ * 03-10-95  blong
+ *      Added numerous speed hacks proposed by Robert S. Thau (rst@ai.mit.edu) 
+ *      including set group before fork, and call gettime before to fork
+ *      to set up libraries.
+ *
+ * 04-14-95  rst / rh
+ *      Brandon's code snarfed from NCSA 1.4, but tinkered to work with the
+ *      Apache server, and also to have child processes do accept() directly.
+ *
+ * April-July '95 rst
+ *      Extensive rework for Apache.
+ */
+
+#ifndef SHARED_CORE_BOOTSTRAP
+#ifndef SHARED_CORE_TIESTATIC
+
+#ifdef SHARED_CORE
+#define REALMAIN ap_main
+int ap_main(int argc, char *argv[]);
+#else
+#define REALMAIN main
+#endif
+
+#define CORE_PRIVATE
+
+#include "httpd.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_config.h"       /* for read_config */
+#include "http_protocol.h"     /* for read_request */
+#include "http_request.h"      /* for process_request */
+#include "http_conf_globals.h"
+#include "http_core.h"         /* for get_remote_host */
+#include "http_vhost.h"
+#include "util_script.h"       /* to force util_script.c linking */
+#include "util_uri.h"
+#include "scoreboard.h"
+#include "multithread.h"
+#include <sys/stat.h>
+#ifdef USE_SHMGET_SCOREBOARD
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+#ifdef SecureWare
+#include <sys/security.h>
+#include <sys/audit.h>
+#include <prot.h>
+#endif
+#ifdef WIN32
+#include "../os/win32/getopt.h"
+#elif !defined(BEOS) && !defined(TPF)
+#include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_BSTRING_H
+#include <bstring.h>           /* for IRIX, FD_SET calls bzero() */
+#endif
+
+#ifdef MULTITHREAD
+/* special debug stuff -- PCS */
+
+/* Set this non-zero if you are prepared to put up with more than one log entry per second */
+#define SEVERELY_VERBOSE           0
+
+  /* APD1() to APD5() are macros to help us debug. They can either
+   * log to the screen or the error_log file. In release builds, these
+   * macros do nothing. In debug builds, they send messages at priority
+   * "debug" to the error log file, or if DEBUG_TO_CONSOLE is defined,
+   * to the console.
+   */
+
+# ifdef _DEBUG
+#  ifndef DEBUG_TO_CONSOLE
+#   define APD1(a) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a)
+#   define APD2(a,b) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b)
+#   define APD3(a,b,c) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c)
+#   define APD4(a,b,c,d) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c,d)
+#   define APD5(a,b,c,d,e) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c,d,e)
+#  else
+#   define APD1(a) printf("%s\n",a)
+#   define APD2(a,b) do { printf(a,b);putchar('\n'); } while(0);
+#   define APD3(a,b,c) do { printf(a,b,c);putchar('\n'); } while(0);
+#   define APD4(a,b,c,d) do { printf(a,b,c,d);putchar('\n'); } while(0);
+#   define APD5(a,b,c,d,e) do { printf(a,b,c,d,e);putchar('\n'); } while(0);
+#  endif
+# else /* !_DEBUG */
+#  define APD1(a) 
+#  define APD2(a,b) 
+#  define APD3(a,b,c) 
+#  define APD4(a,b,c,d) 
+#  define APD5(a,b,c,d,e) 
+# endif /* _DEBUG */
+#endif /* MULTITHREAD */
+
+/* This next function is never used. It is here to ensure that if we
+ * make all the modules into shared libraries that core httpd still
+ * includes the full Apache API. Without this function the objects in
+ * main/util_script.c would not be linked into a minimal httpd.
+ * And the extra prototype is to make gcc -Wmissing-prototypes quiet.
+ */
+extern void ap_force_library_loading(void);
+void ap_force_library_loading(void) {
+    ap_add_cgi_vars(NULL);
+}
+
+#include "explain.h"
+
+#if !defined(max)
+#define max(a,b)        (a > b ? a : b)
+#endif
+
+#ifdef WIN32
+#include "../os/win32/service.h"
+#include "../os/win32/registry.h"
+#define DEFAULTSERVICENAME "Apache"
+#define PATHSEPARATOR '\\'
+#else
+#define PATHSEPARATOR '/'
+#endif
+
+
+#ifdef MINT
+long _stksize = 32768;
+#endif
+
+#ifdef USE_OS2_SCOREBOARD
+    /* Add MMAP style functionality to OS/2 */
+#define INCL_DOSMEMMGR
+#define INCL_DOSEXCEPTIONS
+#define INCL_DOSSEMAPHORES
+#include <os2.h>
+#include <umalloc.h>
+#include <stdio.h>
+caddr_t create_shared_heap(const char *, size_t);
+caddr_t get_shared_heap(const char *);
+#endif
+
+DEF_Explain
+
+/* Defining GPROF when compiling uses the moncontrol() function to
+ * disable gprof profiling in the parent, and enable it only for
+ * request processing in children (or in one_process mode).  It's
+ * absolutely required to get useful gprof results under linux
+ * because the profile itimers and such are disabled across a
+ * fork().  It's probably useful elsewhere as well.
+ */
+#ifdef GPROF
+extern void moncontrol(int);
+#define MONCONTROL(x) moncontrol(x)
+#else
+#define MONCONTROL(x)
+#endif
+
+#ifndef MULTITHREAD
+/* this just need to be anything non-NULL */
+void *ap_dummy_mutex = &ap_dummy_mutex;
+#endif
+
+/*
+ * Actual definitions of config globals... here because this is
+ * for the most part the only code that acts on 'em.  (Hmmm... mod_main.c?)
+ */
+
+int ap_standalone=0;
+int ap_configtestonly=0;
+int ap_docrootcheck=1;
+uid_t ap_user_id=0;
+char *ap_user_name=NULL;
+gid_t ap_group_id=0;
+#ifdef MULTIPLE_GROUPS
+gid_t group_id_list[NGROUPS_MAX];
+#endif
+int ap_max_requests_per_child=0;
+int ap_threads_per_child=0;
+int ap_excess_requests_per_child=0;
+char *ap_pid_fname=NULL;
+char *ap_scoreboard_fname=NULL;
+char *ap_lock_fname;
+char *ap_server_argv0=NULL;
+struct in_addr ap_bind_address;
+int ap_daemons_to_start=0;
+int ap_daemons_min_free=0;
+int ap_daemons_max_free=0;
+int ap_daemons_limit=0;
+time_t ap_restart_time=0;
+int ap_suexec_enabled = 0;
+int ap_listenbacklog;
+int ap_dump_settings = 0;
+API_VAR_EXPORT int ap_extended_status = 0;
+
+/*
+ * The max child slot ever assigned, preserved across restarts.  Necessary
+ * to deal with MaxClients changes across SIGUSR1 restarts.  We use this
+ * value to optimize routines that have to scan the entire scoreboard.
+ */
+static int max_daemons_limit = -1;
+
+/*
+ * During config time, listeners is treated as a NULL-terminated list.
+ * child_main previously would start at the beginning of the list each time
+ * through the loop, so a socket early on in the list could easily starve out
+ * sockets later on in the list.  The solution is to start at the listener
+ * after the last one processed.  But to do that fast/easily in child_main it's
+ * way more convenient for listeners to be a ring that loops back on itself.
+ * The routine setup_listeners() is called after config time to both open up
+ * the sockets and to turn the NULL-terminated list into a ring that loops back
+ * on itself.
+ *
+ * head_listener is used by each child to keep track of what they consider
+ * to be the "start" of the ring.  It is also set by make_child to ensure
+ * that new children also don't starve any sockets.
+ *
+ * Note that listeners != NULL is ensured by read_config().
+ */
+listen_rec *ap_listeners;
+static listen_rec *head_listener;
+
+API_VAR_EXPORT char ap_server_root[MAX_STRING_LEN]="";
+char ap_server_confname[MAX_STRING_LEN]="";
+char ap_coredump_dir[MAX_STRING_LEN];
+
+array_header *ap_server_pre_read_config;
+array_header *ap_server_post_read_config;
+array_header *ap_server_config_defines;
+
+/* *Non*-shared http_main globals... */
+
+static server_rec *server_conf;
+static JMP_BUF APACHE_TLS jmpbuffer;
+static int sd;
+static fd_set listenfds;
+static int listenmaxfd;
+static pid_t pgrp;
+
+/* one_process --- debugging mode variable; can be set from the command line
+ * with the -X flag.  If set, this gets you the child_main loop running
+ * in the process which originally started up (no detach, no make_child),
+ * which is a pretty nice debugging environment.  (You'll get a SIGHUP
+ * early in standalone_main; just continue through.  This is the server
+ * trying to kill off any child processes which it might have lying
+ * around --- Apache doesn't keep track of their pids, it just sends
+ * SIGHUP to the process group, ignoring it in the root process.
+ * Continue through and you'll be fine.).
+ */
+
+static int one_process = 0;
+
+/* set if timeouts are to be handled by the children and not by the parent.
+ * i.e. child_timeouts = !standalone || one_process.
+ */
+static int child_timeouts;
+
+#ifdef DEBUG_SIGSTOP
+int raise_sigstop_flags;
+#endif
+
+#ifndef NO_OTHER_CHILD
+/* used to maintain list of children which aren't part of the scoreboard */
+typedef struct other_child_rec other_child_rec;
+struct other_child_rec {
+    other_child_rec *next;
+    int pid;
+    void (*maintenance) (int, void *, ap_wait_t);
+    void *data;
+    int write_fd;
+};
+static other_child_rec *other_children;
+#endif
+
+static pool *pglobal;          /* Global pool */
+static pool *pconf;            /* Pool for config stuff */
+static pool *plog;             /* Pool for error-logging files */
+static pool *ptrans;           /* Pool for per-transaction stuff */
+static pool *pchild;           /* Pool for httpd child stuff */
+static pool *pcommands;        /* Pool for -C and -c switches */
+
+static int APACHE_TLS my_pid;  /* it seems silly to call getpid all the time */
+#ifndef MULTITHREAD
+static int my_child_num;
+#endif
+
+#ifdef TPF
+int tpf_child = 0;
+char tpf_server_name[INETD_SERVNAME_LENGTH+1];
+#endif /* TPF */
+
+scoreboard *ap_scoreboard_image = NULL;
+
+/*
+ * Pieces for managing the contents of the Server response header
+ * field.
+ */
+static char *server_version = NULL;
+static int version_locked = 0;
+
+/* Global, alas, so http_core can talk to us */
+enum server_token_type ap_server_tokens = SrvTk_FULL;
+
+/*
+ * This routine is called when the pconf pool is vacuumed.  It resets the
+ * server version string to a known value and [re]enables modifications
+ * (which are disabled by configuration completion). 
+ */
+static void reset_version(void *dummy)
+{
+    version_locked = 0;
+    ap_server_tokens = SrvTk_FULL;
+    server_version = NULL;
+}
+
+API_EXPORT(const char *) ap_get_server_version(void)
+{
+    return (server_version ? server_version : SERVER_BASEVERSION);
+}
+
+API_EXPORT(void) ap_add_version_component(const char *component)
+{
+    if (! version_locked) {
+        /*
+         * If the version string is null, register our cleanup to reset the
+         * pointer on pool destruction. We also know that, if NULL,
+        * we are adding the original SERVER_BASEVERSION string.
+         */
+        if (server_version == NULL) {
+           ap_register_cleanup(pconf, NULL, (void (*)(void *))reset_version, 
+                               ap_null_cleanup);
+           server_version = ap_pstrdup(pconf, component);
+       }
+       else {
+           /*
+            * Tack the given component identifier to the end of
+            * the existing string.
+            */
+           server_version = ap_pstrcat(pconf, server_version, " ",
+                                       component, NULL);
+       }
+    }
+}
+
+/*
+ * This routine adds the real server base identity to the version string,
+ * and then locks out changes until the next reconfig.
+ */
+static void ap_set_version(void)
+{
+    if (ap_server_tokens == SrvTk_MIN) {
+       ap_add_version_component(SERVER_BASEVERSION);
+    }
+    else {
+       ap_add_version_component(SERVER_BASEVERSION " (" PLATFORM ")");
+    }
+    /*
+     * Lock the server_version string if we're not displaying
+     * the full set of tokens
+     */
+    if (ap_server_tokens != SrvTk_FULL) {
+       version_locked++;
+    }
+}
+
+static APACHE_TLS int volatile exit_after_unblock = 0;
+
+#ifdef GPROF
+/* 
+ * change directory for gprof to plop the gmon.out file
+ * configure in httpd.conf:
+ * GprofDir logs/   -> $ServerRoot/logs/gmon.out
+ * GprofDir logs/%  -> $ServerRoot/logs/gprof.$pid/gmon.out
+ */
+static void chdir_for_gprof(void)
+{
+    core_server_config *sconf = 
+       ap_get_module_config(server_conf->module_config, &core_module);    
+    char *dir = sconf->gprof_dir;
+
+    if(dir) {
+       char buf[512];
+       int len = strlen(sconf->gprof_dir) - 1;
+       if(*(dir + len) == '%') {
+           dir[len] = '\0';
+           ap_snprintf(buf, sizeof(buf), "%sgprof.%d", dir, (int)getpid());
+       } 
+       dir = ap_server_root_relative(pconf, buf[0] ? buf : dir);
+       if(mkdir(dir, 0755) < 0 && errno != EEXIST) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+                        "gprof: error creating directory %s", dir);
+       }
+    }
+    else {
+       dir = ap_server_root_relative(pconf, "logs");
+    }
+
+    chdir(dir);
+}
+#else
+#define chdir_for_gprof()
+#endif
+
+/* a clean exit from a child with proper cleanup */
+static void clean_child_exit(int code) __attribute__ ((noreturn));
+static void clean_child_exit(int code)
+{
+    if (pchild) {
+       ap_child_exit_modules(pchild, server_conf);
+       ap_destroy_pool(pchild);
+    }
+    chdir_for_gprof();
+    exit(code);
+}
+
+#if defined(USE_FCNTL_SERIALIZED_ACCEPT) || defined(USE_FLOCK_SERIALIZED_ACCEPT)
+static void expand_lock_fname(pool *p)
+{
+    /* XXXX possibly bogus cast */
+    ap_lock_fname = ap_psprintf(p, "%s.%lu",
+       ap_server_root_relative(p, ap_lock_fname), (unsigned long)getpid());
+}
+#endif
+
+#if defined (USE_USLOCK_SERIALIZED_ACCEPT)
+
+#include <ulocks.h>
+
+static ulock_t uslock = NULL;
+
+#define accept_mutex_child_init(x)
+
+static void accept_mutex_init(pool *p)
+{
+    ptrdiff_t old;
+    usptr_t *us;
+
+
+    /* default is 8, allocate enough for all the children plus the parent */
+    if ((old = usconfig(CONF_INITUSERS, HARD_SERVER_LIMIT + 1)) == -1) {
+       perror("usconfig(CONF_INITUSERS)");
+       exit(-1);
+    }
+
+    if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) {
+       perror("usconfig(CONF_LOCKTYPE)");
+       exit(-1);
+    }
+    if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) {
+       perror("usconfig(CONF_ARENATYPE)");
+       exit(-1);
+    }
+    if ((us = usinit("/dev/zero")) == NULL) {
+       perror("usinit");
+       exit(-1);
+    }
+
+    if ((uslock = usnewlock(us)) == NULL) {
+       perror("usnewlock");
+       exit(-1);
+    }
+}
+
+static void accept_mutex_on(void)
+{
+    switch (ussetlock(uslock)) {
+    case 1:
+       /* got lock */
+       break;
+    case 0:
+       fprintf(stderr, "didn't get lock\n");
+       clean_child_exit(APEXIT_CHILDFATAL);
+    case -1:
+       perror("ussetlock");
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+}
+
+static void accept_mutex_off(void)
+{
+    if (usunsetlock(uslock) == -1) {
+       perror("usunsetlock");
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+}
+
+#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT)
+
+/* This code probably only works on Solaris ... but it works really fast
+ * on Solaris.  Note that pthread mutexes are *NOT* released when a task
+ * dies ... the task has to free it itself.  So we block signals and
+ * try to be nice about releasing the mutex.
+ */
+
+#include <pthread.h>
+
+static pthread_mutex_t *accept_mutex = (void *)(caddr_t) -1;
+static int have_accept_mutex;
+static sigset_t accept_block_mask;
+static sigset_t accept_previous_mask;
+
+static void accept_mutex_child_cleanup(void *foo)
+{
+    if (accept_mutex != (void *)(caddr_t)-1
+       && have_accept_mutex) {
+       pthread_mutex_unlock(accept_mutex);
+    }
+}
+
+static void accept_mutex_child_init(pool *p)
+{
+    ap_register_cleanup(p, NULL, accept_mutex_child_cleanup, ap_null_cleanup);
+}
+
+static void accept_mutex_cleanup(void *foo)
+{
+    if (accept_mutex != (void *)(caddr_t)-1
+       && munmap((caddr_t) accept_mutex, sizeof(*accept_mutex))) {
+       perror("munmap");
+    }
+    accept_mutex = (void *)(caddr_t)-1;
+}
+
+static void accept_mutex_init(pool *p)
+{
+    pthread_mutexattr_t mattr;
+    int fd;
+
+    fd = open("/dev/zero", O_RDWR);
+    if (fd == -1) {
+       perror("open(/dev/zero)");
+       exit(APEXIT_INIT);
+    }
+    accept_mutex = (pthread_mutex_t *) mmap((caddr_t) 0, sizeof(*accept_mutex),
+                                PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    if (accept_mutex == (void *) (caddr_t) - 1) {
+       perror("mmap");
+       exit(APEXIT_INIT);
+    }
+    close(fd);
+    if ((errno = pthread_mutexattr_init(&mattr))) {
+       perror("pthread_mutexattr_init");
+       exit(APEXIT_INIT);
+    }
+    if ((errno = pthread_mutexattr_setpshared(&mattr,
+                                               PTHREAD_PROCESS_SHARED))) {
+       perror("pthread_mutexattr_setpshared");
+       exit(APEXIT_INIT);
+    }
+    if ((errno = pthread_mutex_init(accept_mutex, &mattr))) {
+       perror("pthread_mutex_init");
+       exit(APEXIT_INIT);
+    }
+    sigfillset(&accept_block_mask);
+    sigdelset(&accept_block_mask, SIGHUP);
+    sigdelset(&accept_block_mask, SIGTERM);
+    sigdelset(&accept_block_mask, SIGUSR1);
+    ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+}
+
+static void accept_mutex_on(void)
+{
+    int err;
+
+    if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
+       perror("sigprocmask(SIG_BLOCK)");
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+    if ((err = pthread_mutex_lock(accept_mutex))) {
+       errno = err;
+       perror("pthread_mutex_lock");
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+    have_accept_mutex = 1;
+}
+
+static void accept_mutex_off(void)
+{
+    int err;
+
+    if ((err = pthread_mutex_unlock(accept_mutex))) {
+       errno = err;
+       perror("pthread_mutex_unlock");
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+    /* There is a slight race condition right here... if we were to die right
+     * now, we'd do another pthread_mutex_unlock.  Now, doing that would let
+     * another process into the mutex.  pthread mutexes are designed to be
+     * fast, as such they don't have protection for things like testing if the
+     * thread owning a mutex is actually unlocking it (or even any way of
+     * testing who owns the mutex).
+     *
+     * If we were to unset have_accept_mutex prior to releasing the mutex
+     * then the race could result in the server unable to serve hits.  Doing
+     * it this way means that the server can continue, but an additional
+     * child might be in the critical section ... at least it's still serving
+     * hits.
+     */
+    have_accept_mutex = 0;
+    if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
+       perror("sigprocmask(SIG_SETMASK)");
+       clean_child_exit(1);
+    }
+}
+
+#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+
+#ifdef NEED_UNION_SEMUN
+/* it makes no sense, but this isn't defined on solaris */
+union semun {
+    long val;
+    struct semid_ds *buf;
+    ushort *array;
+};
+
+#endif
+
+static int sem_id = -1;
+static struct sembuf op_on;
+static struct sembuf op_off;
+
+/* We get a random semaphore ... the lame sysv semaphore interface
+ * means we have to be sure to clean this up or else we'll leak
+ * semaphores.
+ */
+static void accept_mutex_cleanup(void *foo)
+{
+    union semun ick;
+
+    if (sem_id < 0)
+       return;
+    /* this is ignored anyhow */
+    ick.val = 0;
+    semctl(sem_id, 0, IPC_RMID, ick);
+}
+
+#define accept_mutex_child_init(x)
+
+static void accept_mutex_init(pool *p)
+{
+    union semun ick;
+    struct semid_ds buf;
+
+    /* acquire the semaphore */
+    sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
+    if (sem_id < 0) {
+       perror("semget");
+       exit(APEXIT_INIT);
+    }
+    ick.val = 1;
+    if (semctl(sem_id, 0, SETVAL, ick) < 0) {
+       perror("semctl(SETVAL)");
+       exit(APEXIT_INIT);
+    }
+    if (!getuid()) {
+       /* restrict it to use only by the appropriate user_id ... not that this
+        * stops CGIs from acquiring it and dinking around with it.
+        */
+       buf.sem_perm.uid = ap_user_id;
+       buf.sem_perm.gid = ap_group_id;
+       buf.sem_perm.mode = 0600;
+       ick.buf = &buf;
+       if (semctl(sem_id, 0, IPC_SET, ick) < 0) {
+           perror("semctl(IPC_SET)");
+           exit(APEXIT_INIT);
+       }
+    }
+    ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+
+    /* pre-initialize these */
+    op_on.sem_num = 0;
+    op_on.sem_op = -1;
+    op_on.sem_flg = SEM_UNDO;
+    op_off.sem_num = 0;
+    op_off.sem_op = 1;
+    op_off.sem_flg = SEM_UNDO;
+}
+
+static void accept_mutex_on(void)
+{
+    while (semop(sem_id, &op_on, 1) < 0) {
+       if (errno != EINTR) {
+           perror("accept_mutex_on");
+           clean_child_exit(APEXIT_CHILDFATAL);
+       }
+    }
+}
+
+static void accept_mutex_off(void)
+{
+    while (semop(sem_id, &op_off, 1) < 0) {
+       if (errno != EINTR) {
+           perror("accept_mutex_off");
+           clean_child_exit(APEXIT_CHILDFATAL);
+       }
+    }
+}
+
+#elif defined(USE_FCNTL_SERIALIZED_ACCEPT)
+static struct flock lock_it;
+static struct flock unlock_it;
+
+static int lock_fd = -1;
+
+#define accept_mutex_child_init(x)
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+static void accept_mutex_init(pool *p)
+{
+
+    lock_it.l_whence = SEEK_SET;       /* from current point */
+    lock_it.l_start = 0;               /* -"- */
+    lock_it.l_len = 0;                 /* until end of file */
+    lock_it.l_type = F_WRLCK;          /* set exclusive/write lock */
+    lock_it.l_pid = 0;                 /* pid not actually interesting */
+    unlock_it.l_whence = SEEK_SET;     /* from current point */
+    unlock_it.l_start = 0;             /* -"- */
+    unlock_it.l_len = 0;               /* until end of file */
+    unlock_it.l_type = F_UNLCK;                /* set exclusive/write lock */
+    unlock_it.l_pid = 0;               /* pid not actually interesting */
+
+    expand_lock_fname(p);
+    lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0644);
+    if (lock_fd == -1) {
+       perror("open");
+       fprintf(stderr, "Cannot open lock file: %s\n", ap_lock_fname);
+       exit(APEXIT_INIT);
+    }
+    unlink(ap_lock_fname);
+}
+
+static void accept_mutex_on(void)
+{
+    int ret;
+
+    while ((ret = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) {
+       /* nop */
+    }
+
+    if (ret < 0) {
+       ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+                   "fcntl: F_SETLKW: Error getting accept lock, exiting!  "
+                   "Perhaps you need to use the LockFile directive to place "
+                   "your lock file on a local disk!");
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+}
+
+static void accept_mutex_off(void)
+{
+    int ret;
+
+    while ((ret = fcntl(lock_fd, F_SETLKW, &unlock_it)) < 0 && errno == EINTR) {
+       /* nop */
+    }
+    if (ret < 0) {
+       ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+                   "fcntl: F_SETLKW: Error freeing accept lock, exiting!  "
+                   "Perhaps you need to use the LockFile directive to place "
+                   "your lock file on a local disk!");
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+}
+
+#elif defined(USE_FLOCK_SERIALIZED_ACCEPT)
+
+static int lock_fd = -1;
+
+static void accept_mutex_cleanup(void *foo)
+{
+    unlink(ap_lock_fname);
+}
+
+/*
+ * Initialize mutex lock.
+ * Done by each child at it's birth
+ */
+static void accept_mutex_child_init(pool *p)
+{
+
+    lock_fd = ap_popenf(p, ap_lock_fname, O_WRONLY, 0600);
+    if (lock_fd == -1) {
+       ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+                   "Child cannot open lock file: %s", ap_lock_fname);
+       clean_child_exit(APEXIT_CHILDINIT);
+    }
+}
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+static void accept_mutex_init(pool *p)
+{
+    expand_lock_fname(p);
+    unlink(ap_lock_fname);
+    lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0600);
+    if (lock_fd == -1) {
+       ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+                   "Parent cannot open lock file: %s", ap_lock_fname);
+       exit(APEXIT_INIT);
+    }
+    ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+}
+
+static void accept_mutex_on(void)
+{
+    int ret;
+
+    while ((ret = flock(lock_fd, LOCK_EX)) < 0 && errno == EINTR)
+       continue;
+
+    if (ret < 0) {
+       ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+                   "flock: LOCK_EX: Error getting accept lock. Exiting!");
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+}
+
+static void accept_mutex_off(void)
+{
+    if (flock(lock_fd, LOCK_UN) < 0) {
+       ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+                   "flock: LOCK_UN: Error freeing accept lock. Exiting!");
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+}
+
+#elif defined(USE_OS2SEM_SERIALIZED_ACCEPT)
+
+static HMTX lock_sem = -1;
+
+static void accept_mutex_cleanup(void *foo)
+{
+    DosReleaseMutexSem(lock_sem);
+    DosCloseMutexSem(lock_sem);
+}
+
+/*
+ * Initialize mutex lock.
+ * Done by each child at it's birth
+ */
+static void accept_mutex_child_init(pool *p)
+{
+    int rc = DosOpenMutexSem(NULL, &lock_sem);
+
+    if (rc != 0) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+                   "Child cannot open lock semaphore, rc=%d", rc);
+       clean_child_exit(APEXIT_CHILDINIT);
+    } else {
+        ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+    }
+}
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+static void accept_mutex_init(pool *p)
+{
+    int rc = DosCreateMutexSem(NULL, &lock_sem, DC_SEM_SHARED, FALSE);
+
+    if (rc != 0) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+                   "Parent cannot create lock semaphore, rc=%d", rc);
+       exit(APEXIT_INIT);
+    }
+
+    ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+}
+
+static void accept_mutex_on(void)
+{
+    int rc = DosRequestMutexSem(lock_sem, SEM_INDEFINITE_WAIT);
+
+    if (rc != 0) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+                   "OS2SEM: Error %d getting accept lock. Exiting!", rc);
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+}
+
+static void accept_mutex_off(void)
+{
+    int rc = DosReleaseMutexSem(lock_sem);
+    
+    if (rc != 0) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+                   "OS2SEM: Error %d freeing accept lock. Exiting!", rc);
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+}
+
+#elif defined(USE_TPF_CORE_SERIALIZED_ACCEPT)
+
+static int tpf_core_held;
+
+static void accept_mutex_cleanup(void *foo)
+{
+    if(tpf_core_held)
+        coruc(RESOURCE_KEY);
+}
+
+#define accept_mutex_init(x)
+
+static void accept_mutex_child_init(pool *p)
+{
+    ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+    tpf_core_held = 0;
+}
+
+static void accept_mutex_on(void)
+{
+    corhc(RESOURCE_KEY);
+    tpf_core_held = 1;
+    ap_check_signals();
+}
+
+static void accept_mutex_off(void)
+{
+    coruc(RESOURCE_KEY);
+    tpf_core_held = 0;
+    ap_check_signals();
+}
+
+#else
+/* Default --- no serialization.  Other methods *could* go here,
+ * as #elifs...
+ */
+#if !defined(MULTITHREAD)
+/* Multithreaded systems don't complete between processes for
+ * the sockets. */
+#define NO_SERIALIZED_ACCEPT
+#define accept_mutex_child_init(x)
+#define accept_mutex_init(x)
+#define accept_mutex_on()
+#define accept_mutex_off()
+#endif
+#endif
+
+/* On some architectures it's safe to do unserialized accept()s in the single
+ * Listen case.  But it's never safe to do it in the case where there's
+ * multiple Listen statements.  Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+ * when it's safe in the single Listen case.
+ */
+#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+#define SAFE_ACCEPT(stmt) do {if(ap_listeners->next != ap_listeners) {stmt;}} while(0)
+#else
+#define SAFE_ACCEPT(stmt) do {stmt;} while(0)
+#endif
+
+static void usage(char *bin)
+{
+    char pad[MAX_STRING_LEN];
+    unsigned i;
+
+    for (i = 0; i < strlen(bin); i++)
+       pad[i] = ' ';
+    pad[i] = '\0';
+#ifdef SHARED_CORE
+    fprintf(stderr, "Usage: %s [-R directory] [-D name] [-d directory] [-f file]\n", bin);
+#else
+    fprintf(stderr, "Usage: %s [-D name] [-d directory] [-f file]\n", bin);
+#endif
+    fprintf(stderr, "       %s [-C \"directive\"] [-c \"directive\"]\n", pad);
+    fprintf(stderr, "       %s [-v] [-V] [-h] [-l] [-L] [-S] [-t] [-T]\n", pad);
+#ifdef WIN32
+    fprintf(stderr, "       %s [-n service] [-k signal] [-i] [-u]\n", pad);
+#endif
+    fprintf(stderr, "Options:\n");
+#ifdef SHARED_CORE
+    fprintf(stderr, "  -R directory     : specify an alternate location for shared object files\n");
+#endif
+    fprintf(stderr, "  -D name          : define a name for use in <IfDefine name> directives\n");
+    fprintf(stderr, "  -d directory     : specify an alternate initial ServerRoot\n");
+    fprintf(stderr, "  -f file          : specify an alternate ServerConfigFile\n");
+    fprintf(stderr, "  -C \"directive\"   : process directive before reading config files\n");
+    fprintf(stderr, "  -c \"directive\"   : process directive after  reading config files\n");
+    fprintf(stderr, "  -v               : show version number\n");
+    fprintf(stderr, "  -V               : show compile settings\n");
+    fprintf(stderr, "  -h               : list available command line options (this page)\n");
+    fprintf(stderr, "  -l               : list compiled-in modules\n");
+    fprintf(stderr, "  -L               : list available configuration directives\n");
+    fprintf(stderr, "  -S               : show parsed settings (currently only vhost settings)\n");
+    fprintf(stderr, "  -t               : run syntax check for config files (with docroot check)\n");
+    fprintf(stderr, "  -T               : run syntax check for config files (without docroot check)\n");
+#ifdef WIN32
+    fprintf(stderr, "  -n name          : set service name and use its ServerConfigFile\n");
+    fprintf(stderr, "  -k shutdown      : tell running Apache to shutdown\n");
+    fprintf(stderr, "  -k restart       : tell running Apache to do a graceful restart\n");
+    fprintf(stderr, "  -k start         : tell Apache to start\n");
+    fprintf(stderr, "  -i               : install an Apache service\n");
+    fprintf(stderr, "  -u               : uninstall an Apache service\n");
+#endif
+    exit(1);
+}
+
+/*****************************************************************
+ *
+ * Timeout handling.  DISTINCTLY not thread-safe, but all this stuff
+ * has to change for threads anyway.  Note that this code allows only
+ * one timeout in progress at a time...
+ */
+
+static APACHE_TLS conn_rec *volatile current_conn;
+static APACHE_TLS request_rec *volatile timeout_req;
+static APACHE_TLS const char *volatile timeout_name = NULL;
+static APACHE_TLS int volatile alarms_blocked = 0;
+static APACHE_TLS int volatile alarm_pending = 0;
+
+static void timeout(int sig)
+{
+    void *dirconf;
+
+    if (alarms_blocked) {
+       alarm_pending = 1;
+       return;
+    }
+    if (exit_after_unblock) {
+       clean_child_exit(0);
+    }
+
+    if (!current_conn) {
+       ap_longjmp(jmpbuffer, 1);
+    }
+
+    if (timeout_req != NULL)
+       dirconf = timeout_req->per_dir_config;
+    else
+       dirconf = current_conn->server->lookup_defaults;
+    if (!current_conn->keptalive) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,
+                    current_conn->server, "[client %s] %s timed out",
+                    current_conn->remote_ip,
+                    timeout_name ? timeout_name : "request");
+    }
+
+    if (timeout_req) {
+       /* Someone has asked for this transaction to just be aborted
+        * if it times out...
+        */
+
+       request_rec *log_req = timeout_req;
+       request_rec *save_req = timeout_req;
+
+       /* avoid looping... if ap_log_transaction started another
+        * timer (say via rfc1413.c) we could loop...
+        */
+       timeout_req = NULL;
+
+       while (log_req->main || log_req->prev) {
+           /* Get back to original request... */
+           if (log_req->main)
+               log_req = log_req->main;
+           else
+               log_req = log_req->prev;
+       }
+
+       if (!current_conn->keptalive) {
+           /* in some cases we come here before setting the time */
+           if (log_req->request_time == 0) {
+             log_req->request_time = time(0);
+           }
+           ap_log_transaction(log_req);
+       }
+
+       ap_bsetflag(save_req->connection->client, B_EOUT, 1);
+       ap_bclose(save_req->connection->client);
+
+       if (!ap_standalone)
+           exit(0);
+
+       ap_longjmp(jmpbuffer, 1);
+    }
+    else {                     /* abort the connection */
+       ap_bsetflag(current_conn->client, B_EOUT, 1);
+       ap_bclose(current_conn->client);
+       current_conn->aborted = 1;
+    }
+}
+
+#ifndef TPF
+/*
+ * These two called from alloc.c to protect its critical sections...
+ * Note that they can nest (as when destroying the sub_pools of a pool
+ * which is itself being cleared); we have to support that here.
+ */
+
+API_EXPORT(void) ap_block_alarms(void)
+{
+    ++alarms_blocked;
+}
+
+API_EXPORT(void) ap_unblock_alarms(void)
+{
+    --alarms_blocked;
+    if (alarms_blocked == 0) {
+       if (exit_after_unblock) {
+           /* We have a couple race conditions to deal with here, we can't
+            * allow a timeout that comes in this small interval to allow
+            * the child to jump back to the main loop.  Instead we block
+            * alarms again, and then note that exit_after_unblock is
+            * being dealt with.  We choose this way to solve this so that
+            * the common path through unblock_alarms() is really short.
+            */
+           ++alarms_blocked;
+           exit_after_unblock = 0;
+           clean_child_exit(0);
+       }
+       if (alarm_pending) {
+           alarm_pending = 0;
+           timeout(0);
+       }
+    }
+}
+#endif /* TPF */
+
+static APACHE_TLS void (*volatile alarm_fn) (int) = NULL;
+#ifdef WIN32
+static APACHE_TLS unsigned int alarm_expiry_time = 0;
+#endif /* WIN32 */
+
+#ifndef WIN32
+static void alrm_handler(int sig)
+{
+    if (alarm_fn) {
+       (*alarm_fn) (sig);
+    }
+}
+#endif
+
+unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x)
+{
+    unsigned int old;
+
+#ifdef WIN32
+    old = alarm_expiry_time;
+    if (old)
+       old -= time(0);
+    if (x == 0) {
+       alarm_fn = NULL;
+       alarm_expiry_time = 0;
+    }
+    else {
+       alarm_fn = fn;
+       alarm_expiry_time = time(NULL) + x;
+    }
+#else
+    if (alarm_fn && x && fn != alarm_fn) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL,
+           "ap_set_callback_and_alarm: possible nested timer!");
+    }
+    alarm_fn = fn;
+#ifndef OPTIMIZE_TIMEOUTS
+    old = alarm(x);
+#else
+    if (child_timeouts) {
+       old = alarm(x);
+    }
+    else {
+       /* Just note the timeout in our scoreboard, no need to call the system.
+        * We also note that the virtual time has gone forward.
+        */
+       ap_check_signals();
+       old = ap_scoreboard_image->servers[my_child_num].timeout_len;
+       ap_scoreboard_image->servers[my_child_num].timeout_len = x;
+       ++ap_scoreboard_image->servers[my_child_num].cur_vtime;
+    }
+#endif
+#endif
+    return (old);
+}
+
+
+#ifdef WIN32
+API_EXPORT(int) ap_check_alarm(void)
+{
+    if (alarm_expiry_time) {
+       unsigned int t;
+
+       t = time(NULL);
+       if (t >= alarm_expiry_time) {
+           alarm_expiry_time = 0;
+           (*alarm_fn) (0);
+           return (-1);
+       }
+       else {
+           return (alarm_expiry_time - t);
+       }
+    }
+    else
+       return (0);
+}
+#endif /* WIN32 */
+
+
+
+/* reset_timeout (request_rec *) resets the timeout in effect,
+ * as long as it hasn't expired already.
+ */
+
+API_EXPORT(void) ap_reset_timeout(request_rec *r)
+{
+    int i;
+
+    if (timeout_name) {                /* timeout has been set */
+       i = ap_set_callback_and_alarm(alarm_fn, r->server->timeout);
+       if (i == 0)             /* timeout already expired, so set it back to 0 */
+           ap_set_callback_and_alarm(alarm_fn, 0);
+    }
+}
+
+
+
+
+void ap_keepalive_timeout(char *name, request_rec *r)
+{
+    unsigned int to;
+
+    timeout_req = r;
+    timeout_name = name;
+
+    if (r->connection->keptalive)
+       to = r->server->keep_alive_timeout;
+    else
+       to = r->server->timeout;
+    ap_set_callback_and_alarm(timeout, to);
+
+}
+
+API_EXPORT(void) ap_hard_timeout(char *name, request_rec *r)
+{
+    timeout_req = r;
+    timeout_name = name;
+
+    ap_set_callback_and_alarm(timeout, r->server->timeout);
+
+}
+
+API_EXPORT(void) ap_soft_timeout(char *name, request_rec *r)
+{
+    timeout_name = name;
+
+    ap_set_callback_and_alarm(timeout, r->server->timeout);
+
+}
+
+API_EXPORT(void) ap_kill_timeout(request_rec *dummy)
+{
+    ap_check_signals();
+    ap_set_callback_and_alarm(NULL, 0);
+    timeout_req = NULL;
+    timeout_name = NULL;
+}
+
+
+/*
+ * More machine-dependent networking gooo... on some systems,
+ * you've got to be *really* sure that all the packets are acknowledged
+ * before closing the connection, since the client will not be able
+ * to see the last response if their TCP buffer is flushed by a RST
+ * packet from us, which is what the server's TCP stack will send
+ * if it receives any request data after closing the connection.
+ *
+ * In an ideal world, this function would be accomplished by simply
+ * setting the socket option SO_LINGER and handling it within the
+ * server's TCP stack while the process continues on to the next request.
+ * Unfortunately, it seems that most (if not all) operating systems
+ * block the server process on close() when SO_LINGER is used.
+ * For those that don't, see USE_SO_LINGER below.  For the rest,
+ * we have created a home-brew lingering_close.
+ *
+ * Many operating systems tend to block, puke, or otherwise mishandle
+ * calls to shutdown only half of the connection.  You should define
+ * NO_LINGCLOSE in ap_config.h if such is the case for your system.
+ */
+#ifndef MAX_SECS_TO_LINGER
+#define MAX_SECS_TO_LINGER 30
+#endif
+
+#ifdef USE_SO_LINGER
+#define NO_LINGCLOSE           /* The two lingering options are exclusive */
+
+static void sock_enable_linger(int s)
+{
+    struct linger li;
+
+    li.l_onoff = 1;
+    li.l_linger = MAX_SECS_TO_LINGER;
+
+    if (setsockopt(s, SOL_SOCKET, SO_LINGER,
+                  (char *) &li, sizeof(struct linger)) < 0) {
+       ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+                   "setsockopt: (SO_LINGER)");
+       /* not a fatal error */
+    }
+}
+
+#else
+#define sock_enable_linger(s)  /* NOOP */
+#endif /* USE_SO_LINGER */
+
+#ifndef NO_LINGCLOSE
+
+/* Special version of timeout for lingering_close */
+
+static void lingerout(int sig)
+{
+    if (alarms_blocked) {
+       alarm_pending = 1;
+       return;
+    }
+
+    if (!current_conn) {
+       ap_longjmp(jmpbuffer, 1);
+    }
+    ap_bsetflag(current_conn->client, B_EOUT, 1);
+    current_conn->aborted = 1;
+}
+
+static void linger_timeout(void)
+{
+    timeout_name = "lingering close";
+
+    ap_set_callback_and_alarm(lingerout, MAX_SECS_TO_LINGER);
+}
+
+/* Since many clients will abort a connection instead of closing it,
+ * attempting to log an error message from this routine will only
+ * confuse the webmaster.  There doesn't seem to be any portable way to
+ * distinguish between a dropped connection and something that might be
+ * worth logging.
+ */
+static void lingering_close(request_rec *r)
+{
+    char dummybuf[512];
+    struct timeval tv;
+    fd_set lfds;
+    int select_rv;
+    int lsd;
+
+    /* Prevent a slow-drip client from holding us here indefinitely */
+
+    linger_timeout();
+
+    /* Send any leftover data to the client, but never try to again */
+
+    if (ap_bflush(r->connection->client) == -1) {
+       ap_kill_timeout(r);
+       ap_bclose(r->connection->client);
+       return;
+    }
+    ap_bsetflag(r->connection->client, B_EOUT, 1);
+
+    /* Close our half of the connection --- send the client a FIN */
+
+    lsd = r->connection->client->fd;
+
+    if ((shutdown(lsd, 1) != 0) || r->connection->aborted) {
+       ap_kill_timeout(r);
+       ap_bclose(r->connection->client);
+       return;
+    }
+
+    /* Set up to wait for readable data on socket... */
+
+    FD_ZERO(&lfds);
+
+    /* Wait for readable data or error condition on socket;
+     * slurp up any data that arrives...  We exit when we go for an
+     * interval of tv length without getting any more data, get an error
+     * from select(), get an error or EOF on a read, or the timer expires.
+     */
+
+    do {
+       /* We use a 2 second timeout because current (Feb 97) browsers
+        * fail to close a connection after the server closes it.  Thus,
+        * to avoid keeping the child busy, we are only lingering long enough
+        * for a client that is actively sending data on a connection.
+        * This should be sufficient unless the connection is massively
+        * losing packets, in which case we might have missed the RST anyway.
+        * These parameters are reset on each pass, since they might be
+        * changed by select.
+        */
+       FD_SET(lsd, &lfds);
+       tv.tv_sec = 2;
+       tv.tv_usec = 0;
+
+       select_rv = ap_select(lsd + 1, &lfds, NULL, NULL, &tv);
+
+    } while ((select_rv > 0) &&
+             (read(lsd, dummybuf, sizeof dummybuf) > 0));
+
+    /* Should now have seen final ack.  Safe to finally kill socket */
+
+    ap_bclose(r->connection->client);
+
+    ap_kill_timeout(r);
+}
+#endif /* ndef NO_LINGCLOSE */
+
+/*****************************************************************
+ * dealing with other children
+ */
+
+#ifndef NO_OTHER_CHILD
+API_EXPORT(void) ap_register_other_child(int pid,
+                      void (*maintenance) (int reason, void *, ap_wait_t status),
+                         void *data, int write_fd)
+{
+    other_child_rec *ocr;
+
+    ocr = ap_palloc(pconf, sizeof(*ocr));
+    ocr->pid = pid;
+    ocr->maintenance = maintenance;
+    ocr->data = data;
+    ocr->write_fd = write_fd;
+    ocr->next = other_children;
+    other_children = ocr;
+}
+
+/* note that since this can be called by a maintenance function while we're
+ * scanning the other_children list, all scanners should protect themself
+ * by loading ocr->next before calling any maintenance function.
+ */
+API_EXPORT(void) ap_unregister_other_child(void *data)
+{
+    other_child_rec **pocr, *nocr;
+
+    for (pocr = &other_children; *pocr; pocr = &(*pocr)->next) {
+       if ((*pocr)->data == data) {
+           nocr = (*pocr)->next;
+           (*(*pocr)->maintenance) (OC_REASON_UNREGISTER, (*pocr)->data, -1);
+           *pocr = nocr;
+           /* XXX: um, well we've just wasted some space in pconf ? */
+           return;
+       }
+    }
+}
+
+/* test to ensure that the write_fds are all still writable, otherwise
+ * invoke the maintenance functions as appropriate */
+static void probe_writable_fds(void)
+{
+    fd_set writable_fds;
+    int fd_max;
+    other_child_rec *ocr, *nocr;
+    struct timeval tv;
+    int rc;
+
+    if (other_children == NULL)
+       return;
+
+    fd_max = 0;
+    FD_ZERO(&writable_fds);
+    do {
+       for (ocr = other_children; ocr; ocr = ocr->next) {
+           if (ocr->write_fd == -1)
+               continue;
+           FD_SET(ocr->write_fd, &writable_fds);
+           if (ocr->write_fd > fd_max) {
+               fd_max = ocr->write_fd;
+           }
+       }
+       if (fd_max == 0)
+           return;
+
+       tv.tv_sec = 0;
+       tv.tv_usec = 0;
+       rc = ap_select(fd_max + 1, NULL, &writable_fds, NULL, &tv);
+    } while (rc == -1 && errno == EINTR);
+
+    if (rc == -1) {
+       /* XXX: uhh this could be really bad, we could have a bad file
+        * descriptor due to a bug in one of the maintenance routines */
+       ap_log_unixerr("probe_writable_fds", "select",
+                   "could not probe writable fds", server_conf);
+       return;
+    }
+    if (rc == 0)
+       return;
+
+    for (ocr = other_children; ocr; ocr = nocr) {
+       nocr = ocr->next;
+       if (ocr->write_fd == -1)
+           continue;
+       if (FD_ISSET(ocr->write_fd, &writable_fds))
+           continue;
+       (*ocr->maintenance) (OC_REASON_UNWRITABLE, ocr->data, -1);
+    }
+}
+
+/* possibly reap an other_child, return 0 if yes, -1 if not */
+static int reap_other_child(int pid, ap_wait_t status)
+{
+    other_child_rec *ocr, *nocr;
+
+    for (ocr = other_children; ocr; ocr = nocr) {
+       nocr = ocr->next;
+       if (ocr->pid != pid)
+           continue;
+       ocr->pid = -1;
+       (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status);
+       return 0;
+    }
+    return -1;
+}
+#endif
+
+/*****************************************************************
+ *
+ * Dealing with the scoreboard... a lot of these variables are global
+ * only to avoid getting clobbered by the longjmp() that happens when
+ * a hard timeout expires...
+ *
+ * We begin with routines which deal with the file itself... 
+ */
+
+#ifdef MULTITHREAD
+/*
+ * In the multithreaded mode, have multiple threads - not multiple
+ * processes that need to talk to each other. Just use a simple
+ * malloc. But let the routines that follow, think that you have
+ * shared memory (so they use memcpy etc.)
+ */
+
+static void reinit_scoreboard(pool *p)
+{
+    ap_assert(!ap_scoreboard_image);
+    ap_scoreboard_image = (scoreboard *) malloc(SCOREBOARD_SIZE);
+    if (ap_scoreboard_image == NULL) {
+       fprintf(stderr, "Ouch!  Out of memory reiniting scoreboard!\n");
+    }
+    memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE);
+}
+
+void cleanup_scoreboard(void)
+{
+    ap_assert(ap_scoreboard_image);
+    free(ap_scoreboard_image);
+    ap_scoreboard_image = NULL;
+}
+
+API_EXPORT(void) ap_sync_scoreboard_image(void)
+{
+}
+
+
+#else /* MULTITHREAD */
+#if defined(USE_OS2_SCOREBOARD)
+
+/* The next two routines are used to access shared memory under OS/2.  */
+/* This requires EMX v09c to be installed.                           */
+
+caddr_t create_shared_heap(const char *name, size_t size)
+{
+    ULONG rc;
+    void *mem;
+    Heap_t h;
+
+    rc = DosAllocSharedMem(&mem, name, size,
+                          PAG_COMMIT | PAG_READ | PAG_WRITE);
+    if (rc != 0)
+       return NULL;
+    h = _ucreate(mem, size, !_BLOCK_CLEAN, _HEAP_REGULAR | _HEAP_SHARED,
+                NULL, NULL);
+    if (h == NULL)
+       DosFreeMem(mem);
+    return (caddr_t) h;
+}
+
+caddr_t get_shared_heap(const char *Name)
+{
+
+    PVOID BaseAddress;         /* Pointer to the base address of
+                                  the shared memory object */
+    ULONG AttributeFlags;      /* Flags describing characteristics
+                                  of the shared memory object */
+    APIRET rc;                 /* Return code */
+
+    /* Request read and write access to */
+    /*   the shared memory object       */
+    AttributeFlags = PAG_WRITE | PAG_READ;
+
+    rc = DosGetNamedSharedMem(&BaseAddress, Name, AttributeFlags);
+
+    if (rc != 0) {
+       printf("DosGetNamedSharedMem error: return code = %ld", rc);
+       return 0;
+    }
+
+    return BaseAddress;
+}
+
+static void setup_shared_mem(pool *p)
+{
+    caddr_t m;
+
+    int rc;
+
+    m = (caddr_t) create_shared_heap("\\SHAREMEM\\SCOREBOARD", SCOREBOARD_SIZE);
+    if (m == 0) {
+       fprintf(stderr, "%s: Could not create OS/2 Shared memory pool.\n",
+               ap_server_argv0);
+       exit(APEXIT_INIT);
+    }
+
+    rc = _uopen((Heap_t) m);
+    if (rc != 0) {
+       fprintf(stderr,
+               "%s: Could not uopen() newly created OS/2 Shared memory pool.\n",
+               ap_server_argv0);
+    }
+    ap_scoreboard_image = (scoreboard *) m;
+    ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+    caddr_t m;
+    int rc;
+
+    m = (caddr_t) get_shared_heap("\\SHAREMEM\\SCOREBOARD");
+    if (m == 0) {
+       fprintf(stderr, "%s: Could not find existing OS/2 Shared memory pool.\n",
+               ap_server_argv0);
+       exit(APEXIT_INIT);
+    }
+
+    rc = _uopen((Heap_t) m);
+    ap_scoreboard_image = (scoreboard *) m;
+}
+
+#elif defined(USE_POSIX_SCOREBOARD)
+#include <sys/mman.h>
+/* 
+ * POSIX 1003.4 style
+ *
+ * Note 1: 
+ * As of version 4.23A, shared memory in QNX must reside under /dev/shmem,
+ * where no subdirectories allowed.
+ *
+ * POSIX shm_open() and shm_unlink() will take care about this issue,
+ * but to avoid confusion, I suggest to redefine scoreboard file name
+ * in httpd.conf to cut "logs/" from it. With default setup actual name
+ * will be "/dev/shmem/logs.apache_status". 
+ * 
+ * If something went wrong and Apache did not unlinked this object upon
+ * exit, you can remove it manually, using "rm -f" command.
+ * 
+ * Note 2:
+ * <sys/mman.h> in QNX defines MAP_ANON, but current implementation 
+ * does NOT support BSD style anonymous mapping. So, the order of 
+ * conditional compilation is important: 
+ * this #ifdef section must be ABOVE the next one (BSD style).
+ *
+ * I tested this stuff and it works fine for me, but if it provides 
+ * trouble for you, just comment out USE_MMAP_SCOREBOARD in QNX section
+ * of ap_config.h
+ *
+ * June 5, 1997, 
+ * Igor N. Kovalenko -- infoh@mail.wplus.net
+ */
+
+static void cleanup_shared_mem(void *d)
+{
+    shm_unlink(ap_scoreboard_fname);
+}
+
+static void setup_shared_mem(pool *p)
+{
+    char buf[512];
+    caddr_t m;
+    int fd;
+
+    fd = shm_open(ap_scoreboard_fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+    if (fd == -1) {
+       ap_snprintf(buf, sizeof(buf), "%s: could not open(create) scoreboard",
+                   ap_server_argv0);
+       perror(buf);
+       exit(APEXIT_INIT);
+    }
+    if (ltrunc(fd, (off_t) SCOREBOARD_SIZE, SEEK_SET) == -1) {
+       ap_snprintf(buf, sizeof(buf), "%s: could not ltrunc scoreboard",
+                   ap_server_argv0);
+       perror(buf);
+       shm_unlink(ap_scoreboard_fname);
+       exit(APEXIT_INIT);
+    }
+    if ((m = (caddr_t) mmap((caddr_t) 0,
+                           (size_t) SCOREBOARD_SIZE, PROT_READ | PROT_WRITE,
+                           MAP_SHARED, fd, (off_t) 0)) == (caddr_t) - 1) {
+       ap_snprintf(buf, sizeof(buf), "%s: cannot mmap scoreboard",
+                   ap_server_argv0);
+       perror(buf);
+       shm_unlink(ap_scoreboard_fname);
+       exit(APEXIT_INIT);
+    }
+    close(fd);
+    ap_register_cleanup(p, NULL, cleanup_shared_mem, ap_null_cleanup);
+    ap_scoreboard_image = (scoreboard *) m;
+    ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+}
+
+#elif defined(USE_MMAP_SCOREBOARD)
+
+static void setup_shared_mem(pool *p)
+{
+    caddr_t m;
+
+#if defined(MAP_ANON)
+/* BSD style */
+#ifdef CONVEXOS11
+    /*
+     * 9-Aug-97 - Jeff Venters (venters@convex.hp.com)
+     * ConvexOS maps address space as follows:
+     *   0x00000000 - 0x7fffffff : Kernel
+     *   0x80000000 - 0xffffffff : User
+     * Start mmapped area 1GB above start of text.
+     *
+     * Also, the length requires a pointer as the actual length is
+     * returned (rounded up to a page boundary).
+     */
+    {
+       unsigned len = SCOREBOARD_SIZE;
+
+       m = mmap((caddr_t) 0xC0000000, &len,
+                PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, NOFD, 0);
+    }
+#elif defined(MAP_TMPFILE)
+    {
+       char mfile[] = "/tmp/apache_shmem_XXXX";
+       int fd = mkstemp(mfile);
+       if (fd == -1) {
+           perror("open");
+           fprintf(stderr, "%s: Could not open %s\n", ap_server_argv0, mfile);
+           exit(APEXIT_INIT);
+       }
+       m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
+               PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+       if (m == (caddr_t) - 1) {
+           perror("mmap");
+           fprintf(stderr, "%s: Could not mmap %s\n", ap_server_argv0, mfile);
+           exit(APEXIT_INIT);
+       }
+       close(fd);
+       unlink(mfile);
+    }
+#else
+    m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
+            PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
+#endif
+    if (m == (caddr_t) - 1) {
+       perror("mmap");
+       fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0);
+       exit(APEXIT_INIT);
+    }
+#else
+/* Sun style */
+    int fd;
+
+    fd = open("/dev/zero", O_RDWR);
+    if (fd == -1) {
+       perror("open");
+       fprintf(stderr, "%s: Could not open /dev/zero\n", ap_server_argv0);
+       exit(APEXIT_INIT);
+    }
+    m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
+            PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    if (m == (caddr_t) - 1) {
+       perror("mmap");
+       fprintf(stderr, "%s: Could not mmap /dev/zero\n", ap_server_argv0);
+       exit(APEXIT_INIT);
+    }
+    close(fd);
+#endif
+    ap_scoreboard_image = (scoreboard *) m;
+    ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+}
+
+#elif defined(USE_SHMGET_SCOREBOARD)
+static key_t shmkey = IPC_PRIVATE;
+static int shmid = -1;
+
+static void setup_shared_mem(pool *p)
+{
+    struct shmid_ds shmbuf;
+#ifdef MOVEBREAK
+    char *obrk;
+#endif
+
+    if ((shmid = shmget(shmkey, SCOREBOARD_SIZE, IPC_CREAT | SHM_R | SHM_W)) == -1) {
+#ifdef LINUX
+       if (errno == ENOSYS) {
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+                        "Your kernel was built without CONFIG_SYSVIPC\n"
+                        "%s: Please consult the Apache FAQ for details",
+                        ap_server_argv0);
+       }
+#endif
+       ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+                   "could not call shmget");
+       exit(APEXIT_INIT);
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+               "created shared memory segment #%d", shmid);
+
+#ifdef MOVEBREAK
+    /*
+     * Some SysV systems place the shared segment WAY too close
+     * to the dynamic memory break point (sbrk(0)). This severely
+     * limits the use of malloc/sbrk in the program since sbrk will
+     * refuse to move past that point.
+     *
+     * To get around this, we move the break point "way up there",
+     * attach the segment and then move break back down. Ugly
+     */
+    if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) {
+       ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+           "sbrk() could not move break");
+    }
+#endif
+
+#define BADSHMAT       ((scoreboard *)(-1))
+    if ((ap_scoreboard_image = (scoreboard *) shmat(shmid, 0, 0)) == BADSHMAT) {
+       ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, "shmat error");
+       /*
+        * We exit below, after we try to remove the segment
+        */
+    }
+    else {                     /* only worry about permissions if we attached the segment */
+       if (shmctl(shmid, IPC_STAT, &shmbuf) != 0) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+               "shmctl() could not stat segment #%d", shmid);
+       }
+       else {
+           shmbuf.shm_perm.uid = ap_user_id;
+           shmbuf.shm_perm.gid = ap_group_id;
+           if (shmctl(shmid, IPC_SET, &shmbuf) != 0) {
+               ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+                   "shmctl() could not set segment #%d", shmid);
+           }
+       }
+    }
+    /*
+     * We must avoid leaving segments in the kernel's
+     * (small) tables.
+     */
+    if (shmctl(shmid, IPC_RMID, NULL) != 0) {
+       ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+               "shmctl: IPC_RMID: could not remove shared memory segment #%d",
+               shmid);
+    }
+    if (ap_scoreboard_image == BADSHMAT)       /* now bailout */
+       exit(APEXIT_INIT);
+
+#ifdef MOVEBREAK
+    if (obrk == (char *) -1)
+       return;                 /* nothing else to do */
+    if (sbrk(-(MOVEBREAK)) == (char *) -1) {
+       ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+           "sbrk() could not move break back");
+    }
+#endif
+    ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+}
+
+#elif defined(USE_TPF_SCOREBOARD)
+
+static void cleanup_scoreboard_heap()
+{
+    int rv;
+    rv = rsysc(ap_scoreboard_image, SCOREBOARD_FRAMES, SCOREBOARD_NAME);
+    if(rv == RSYSC_ERROR) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+            "rsysc() could not release scoreboard system heap");
+    }
+}
+
+static void setup_shared_mem(pool *p)
+{
+    cinfc(CINFC_WRITE, CINFC_CMMCTK2);
+    ap_scoreboard_image = (scoreboard *) gsysc(SCOREBOARD_FRAMES, SCOREBOARD_NAME);
+
+    if (!ap_scoreboard_image) {
+        fprintf(stderr, "httpd: Could not create scoreboard system heap storage.\n");
+        exit(APEXIT_INIT);
+    }
+
+    ap_register_cleanup(p, NULL, cleanup_scoreboard_heap, ap_null_cleanup);
+    ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+    cinfc(CINFC_WRITE, CINFC_CMMCTK2);
+}
+
+#else
+#define SCOREBOARD_FILE
+static scoreboard _scoreboard_image;
+static int scoreboard_fd = -1;
+
+/* XXX: things are seriously screwed if we ever have to do a partial
+ * read or write ... we could get a corrupted scoreboard
+ */
+static int force_write(int fd, void *buffer, int bufsz)
+{
+    int rv, orig_sz = bufsz;
+
+    do {
+       rv = write(fd, buffer, bufsz);
+       if (rv > 0) {
+           buffer = (char *) buffer + rv;
+           bufsz -= rv;
+       }
+    } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR));
+
+    return rv < 0 ? rv : orig_sz - bufsz;
+}
+
+static int force_read(int fd, void *buffer, int bufsz)
+{
+    int rv, orig_sz = bufsz;
+
+    do {
+       rv = read(fd, buffer, bufsz);
+       if (rv > 0) {
+           buffer = (char *) buffer + rv;
+           bufsz -= rv;
+       }
+    } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR));
+
+    return rv < 0 ? rv : orig_sz - bufsz;
+}
+
+static void cleanup_scoreboard_file(void *foo)
+{
+    unlink(ap_scoreboard_fname);
+}
+
+void reopen_scoreboard(pool *p)
+{
+    if (scoreboard_fd != -1)
+       ap_pclosef(p, scoreboard_fd);
+
+#ifdef TPF
+    ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname);
+#endif /* TPF */
+    scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0666);
+    if (scoreboard_fd == -1) {
+       perror(ap_scoreboard_fname);
+       fprintf(stderr, "Cannot open scoreboard file:\n");
+       clean_child_exit(1);
+    }
+}
+#endif
+
+/* Called by parent process */
+static void reinit_scoreboard(pool *p)
+{
+    int running_gen = 0;
+    if (ap_scoreboard_image)
+       running_gen = ap_scoreboard_image->global.running_generation;
+
+#ifndef SCOREBOARD_FILE
+    if (ap_scoreboard_image == NULL) {
+       setup_shared_mem(p);
+    }
+    memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE);
+    ap_scoreboard_image->global.running_generation = running_gen;
+#else
+    ap_scoreboard_image = &_scoreboard_image;
+    ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname);
+
+    scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0644);
+    if (scoreboard_fd == -1) {
+       perror(ap_scoreboard_fname);
+       fprintf(stderr, "Cannot open scoreboard file:\n");
+       exit(APEXIT_INIT);
+    }
+    ap_register_cleanup(p, NULL, cleanup_scoreboard_file, ap_null_cleanup);
+
+    memset((char *) ap_scoreboard_image, 0, sizeof(*ap_scoreboard_image));
+    ap_scoreboard_image->global.running_generation = running_gen;
+    force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image));
+#endif
+}
+
+/* Routines called to deal with the scoreboard image
+ * --- note that we do *not* need write locks, since update_child_status
+ * only updates a *single* record in place, and only one process writes to
+ * a given scoreboard slot at a time (either the child process owning that
+ * slot, or the parent, noting that the child has died).
+ *
+ * As a final note --- setting the score entry to getpid() is always safe,
+ * since when the parent is writing an entry, it's only noting SERVER_DEAD
+ * anyway.
+ */
+
+ap_inline void ap_sync_scoreboard_image(void)
+{
+#ifdef SCOREBOARD_FILE
+    lseek(scoreboard_fd, 0L, 0);
+    force_read(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image));
+#endif
+}
+
+#endif /* MULTITHREAD */
+
+API_EXPORT(int) ap_exists_scoreboard_image(void)
+{
+    return (ap_scoreboard_image ? 1 : 0);
+}
+
+static ap_inline void put_scoreboard_info(int child_num,
+                                      short_score *new_score_rec)
+{
+#ifdef SCOREBOARD_FILE
+    lseek(scoreboard_fd, (long) child_num * sizeof(short_score), 0);
+    force_write(scoreboard_fd, new_score_rec, sizeof(short_score));
+#endif
+}
+
+/* a clean exit from the parent with proper cleanup */
+static void clean_parent_exit(int code) __attribute__((noreturn));
+static void clean_parent_exit(int code)
+{
+    /* Clear the pool - including any registered cleanups */
+    ap_destroy_pool(pglobal);
+    exit(code);
+}
+
+int ap_update_child_status(int child_num, int status, request_rec *r)
+{
+    int old_status;
+    short_score *ss;
+
+    if (child_num < 0)
+       return -1;
+
+    ap_check_signals();
+
+    ap_sync_scoreboard_image();
+    ss = &ap_scoreboard_image->servers[child_num];
+    old_status = ss->status;
+    ss->status = status;
+#ifdef OPTIMIZE_TIMEOUTS
+    ++ss->cur_vtime;
+#endif
+
+    if (ap_extended_status) {
+#ifndef OPTIMIZE_TIMEOUTS
+       ss->last_used = time(NULL);
+#endif
+       if (status == SERVER_READY || status == SERVER_DEAD) {
+           /*
+            * Reset individual counters
+            */
+           if (status == SERVER_DEAD) {
+               ss->my_access_count = 0L;
+               ss->my_bytes_served = 0L;
+           }
+           ss->conn_count = (unsigned short) 0;
+           ss->conn_bytes = (unsigned long) 0;
+       }
+       if (r) {
+           conn_rec *c = r->connection;
+           ap_cpystrn(ss->client, ap_get_remote_host(c, r->per_dir_config,
+                                 REMOTE_NOLOOKUP), sizeof(ss->client));
+           if (r->the_request == NULL) {
+                   ap_cpystrn(ss->request, "NULL", sizeof(ss->request));
+           } else if (r->parsed_uri.password == NULL) {
+                   ap_cpystrn(ss->request, r->the_request, sizeof(ss->request));
+           } else {
+               /* Don't reveal the password in the server-status view */
+                   ap_cpystrn(ss->request, ap_pstrcat(r->pool, r->method, " ",
+                                              ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD),
+                                              r->assbackwards ? NULL : " ", r->protocol, NULL),
+                                      sizeof(ss->request));
+           }
+           ss->vhostrec =  r->server;
+       }
+    }
+    if (status == SERVER_STARTING && r == NULL) {
+       /* clean up the slot's vhostrec pointer (maybe re-used)
+        * and mark the slot as belonging to a new generation.
+        */
+       ss->vhostrec = NULL;
+       ap_scoreboard_image->parent[child_num].generation = ap_my_generation;
+#ifdef SCOREBOARD_FILE
+       lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[child_num]), 0);
+       force_write(scoreboard_fd, &ap_scoreboard_image->parent[child_num],
+           sizeof(parent_score));
+#endif
+    }
+    put_scoreboard_info(child_num, ss);
+
+    return old_status;
+}
+
+static void update_scoreboard_global(void)
+{
+#ifdef SCOREBOARD_FILE
+    lseek(scoreboard_fd,
+         (char *) &ap_scoreboard_image->global -(char *) ap_scoreboard_image, 0);
+    force_write(scoreboard_fd, &ap_scoreboard_image->global,
+               sizeof ap_scoreboard_image->global);
+#endif
+}
+
+void ap_time_process_request(int child_num, int status)
+{
+    short_score *ss;
+#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES)
+    struct tms tms_blk;
+#endif
+
+    if (child_num < 0)
+       return;
+
+    ap_sync_scoreboard_image();
+    ss = &ap_scoreboard_image->servers[child_num];
+
+    if (status == START_PREQUEST) {
+#if defined(NO_GETTIMEOFDAY)
+#ifndef NO_TIMES
+       if ((ss->start_time = times(&tms_blk)) == -1)
+#endif /* NO_TIMES */
+           ss->start_time = (clock_t) 0;
+#else
+       if (gettimeofday(&ss->start_time, (struct timezone *) 0) < 0)
+           ss->start_time.tv_sec =
+               ss->start_time.tv_usec = 0L;
+#endif
+    }
+    else if (status == STOP_PREQUEST) {
+#if defined(NO_GETTIMEOFDAY)
+#ifndef NO_TIMES
+       if ((ss->stop_time = times(&tms_blk)) == -1)
+#endif
+           ss->stop_time = ss->start_time = (clock_t) 0;
+#else
+       if (gettimeofday(&ss->stop_time, (struct timezone *) 0) < 0)
+           ss->stop_time.tv_sec =
+               ss->stop_time.tv_usec =
+               ss->start_time.tv_sec =
+               ss->start_time.tv_usec = 0L;
+#endif
+
+    }
+
+    put_scoreboard_info(child_num, ss);
+}
+
+static void increment_counts(int child_num, request_rec *r)
+{
+    long int bs = 0;
+    short_score *ss;
+
+    ap_sync_scoreboard_image();
+    ss = &ap_scoreboard_image->servers[child_num];
+
+    if (r->sent_bodyct)
+       ap_bgetopt(r->connection->client, BO_BYTECT, &bs);
+
+#ifndef NO_TIMES
+    times(&ss->times);
+#endif
+    ss->access_count++;
+    ss->my_access_count++;
+    ss->conn_count++;
+    ss->bytes_served += (unsigned long) bs;
+    ss->my_bytes_served += (unsigned long) bs;
+    ss->conn_bytes += (unsigned long) bs;
+
+    put_scoreboard_info(child_num, ss);
+}
+
+static int find_child_by_pid(int pid)
+{
+    int i;
+
+    for (i = 0; i < max_daemons_limit; ++i)
+       if (ap_scoreboard_image->parent[i].pid == pid)
+           return i;
+
+    return -1;
+}
+
+static void reclaim_child_processes(int terminate)
+{
+#ifndef MULTITHREAD
+    int i, status;
+    long int waittime = 1024 * 16;     /* in usecs */
+    struct timeval tv;
+    int waitret, tries;
+    int not_dead_yet;
+#ifndef NO_OTHER_CHILD
+    other_child_rec *ocr, *nocr;
+#endif
+
+    ap_sync_scoreboard_image();
+
+    for (tries = terminate ? 4 : 1; tries <= 9; ++tries) {
+       /* don't want to hold up progress any more than 
+        * necessary, but we need to allow children a few moments to exit.
+        * Set delay with an exponential backoff.
+        */
+       tv.tv_sec = waittime / 1000000;
+       tv.tv_usec = waittime % 1000000;
+       waittime = waittime * 4;
+       ap_select(0, NULL, NULL, NULL, &tv);
+
+       /* now see who is done */
+       not_dead_yet = 0;
+       for (i = 0; i < max_daemons_limit; ++i) {
+           int pid = ap_scoreboard_image->parent[i].pid;
+
+           if (pid == my_pid || pid == 0)
+               continue;
+
+           waitret = waitpid(pid, &status, WNOHANG);
+           if (waitret == pid || waitret == -1) {
+               ap_scoreboard_image->parent[i].pid = 0;
+               continue;
+           }
+           ++not_dead_yet;
+           switch (tries) {
+           case 1:     /*  16ms */
+           case 2:     /*  82ms */
+               break;
+           case 3:     /* 344ms */
+               /* perhaps it missed the SIGHUP, lets try again */
+               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING,
+                           server_conf,
+                   "child process %d did not exit, sending another SIGHUP",
+                           pid);
+               kill(pid, SIGHUP);
+               waittime = 1024 * 16;
+               break;
+           case 4:     /*  16ms */
+           case 5:     /*  82ms */
+           case 6:     /* 344ms */
+               break;
+           case 7:     /* 1.4sec */
+               /* ok, now it's being annoying */
+               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING,
+                           server_conf,
+                  "child process %d still did not exit, sending a SIGTERM",
+                           pid);
+               kill(pid, SIGTERM);
+               break;
+           case 8:     /*  6 sec */
+               /* die child scum */
+               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+                  "child process %d still did not exit, sending a SIGKILL",
+                           pid);
+               kill(pid, SIGKILL);
+               break;
+           case 9:     /* 14 sec */
+               /* gave it our best shot, but alas...  If this really 
+                * is a child we are trying to kill and it really hasn't
+                * exited, we will likely fail to bind to the port
+                * after the restart.
+                */
+               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+                           "could not make child process %d exit, "
+                           "attempting to continue anyway", pid);
+               break;
+           }
+       }
+#ifndef NO_OTHER_CHILD
+       for (ocr = other_children; ocr; ocr = nocr) {
+           nocr = ocr->next;
+           if (ocr->pid == -1)
+               continue;
+
+           waitret = waitpid(ocr->pid, &status, WNOHANG);
+           if (waitret == ocr->pid) {
+               ocr->pid = -1;
+               (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status);
+           }
+           else if (waitret == 0) {
+               (*ocr->maintenance) (OC_REASON_RESTART, ocr->data, -1);
+               ++not_dead_yet;
+           }
+           else if (waitret == -1) {
+               /* uh what the heck? they didn't call unregister? */
+               ocr->pid = -1;
+               (*ocr->maintenance) (OC_REASON_LOST, ocr->data, -1);
+           }
+       }
+#endif
+       if (!not_dead_yet) {
+           /* nothing left to wait for */
+           break;
+       }
+    }
+#endif /* ndef MULTITHREAD */
+}
+
+
+#if defined(NEED_WAITPID)
+/*
+   Systems without a real waitpid sometimes lose a child's exit while waiting
+   for another.  Search through the scoreboard for missing children.
+ */
+int reap_children(ap_wait_t *status)
+{
+    int n, pid;
+
+    for (n = 0; n < max_daemons_limit; ++n) {
+        ap_sync_scoreboard_image();
+       if (ap_scoreboard_image->servers[n].status != SERVER_DEAD &&
+               kill((pid = ap_scoreboard_image->parent[n].pid), 0) == -1) {
+           ap_update_child_status(n, SERVER_DEAD, NULL);
+           /* just mark it as having a successful exit status */
+           bzero((char *) status, sizeof(ap_wait_t));
+           return(pid);
+       }
+    }
+    return 0;
+}
+#endif
+
+/* Finally, this routine is used by the caretaker process to wait for
+ * a while...
+ */
+
+/* number of calls to wait_or_timeout between writable probes */
+#ifndef INTERVAL_OF_WRITABLE_PROBES
+#define INTERVAL_OF_WRITABLE_PROBES 10
+#endif
+static int wait_or_timeout_counter;
+
+static int wait_or_timeout(ap_wait_t *status)
+{
+#ifdef WIN32
+#define MAXWAITOBJ MAXIMUM_WAIT_OBJECTS
+    HANDLE h[MAXWAITOBJ];
+    int e[MAXWAITOBJ];
+    int round, pi, hi, rv, err;
+    for (round = 0; round <= (HARD_SERVER_LIMIT - 1) / MAXWAITOBJ + 1; round++) {
+       hi = 0;
+       for (pi = round * MAXWAITOBJ;
+            (pi < (round + 1) * MAXWAITOBJ) && (pi < HARD_SERVER_LIMIT);
+            pi++) {
+           if (ap_scoreboard_image->servers[pi].status != SERVER_DEAD) {
+               e[hi] = pi;
+               h[hi++] = (HANDLE) ap_scoreboard_image->parent[pi].pid;
+           }
+
+       }
+       if (hi > 0) {
+           rv = WaitForMultipleObjects(hi, h, FALSE, 10000);
+           if (rv == -1)
+               err = GetLastError();
+           if ((WAIT_OBJECT_0 <= (unsigned int) rv) && ((unsigned int) rv < (WAIT_OBJECT_0 + hi)))
+               return (ap_scoreboard_image->parent[e[rv - WAIT_OBJECT_0]].pid);
+           else if ((WAIT_ABANDONED_0 <= (unsigned int) rv) && ((unsigned int) rv < (WAIT_ABANDONED_0 + hi)))
+               return (ap_scoreboard_image->parent[e[rv - WAIT_ABANDONED_0]].pid);
+
+       }
+    }
+    return (-1);
+
+#else /* WIN32 */
+    struct timeval tv;
+    int ret;
+
+    ++wait_or_timeout_counter;
+    if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
+       wait_or_timeout_counter = 0;
+#ifndef NO_OTHER_CHILD
+       probe_writable_fds();
+#endif
+    }
+    ret = waitpid(-1, status, WNOHANG);
+    if (ret == -1 && errno == EINTR) {
+       return -1;
+    }
+    if (ret > 0) {
+       return ret;
+    }
+#ifdef NEED_WAITPID
+    if ((ret = reap_children(status)) > 0) {
+       return ret;
+    }
+#endif
+    tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000;
+    tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000;
+    ap_select(0, NULL, NULL, NULL, &tv);
+    return -1;
+#endif /* WIN32 */
+}
+
+
+#if defined(NSIG)
+#define NumSIG NSIG
+#elif defined(_NSIG)
+#define NumSIG _NSIG
+#elif defined(__NSIG)
+#define NumSIG __NSIG
+#else
+#define NumSIG 32   /* for 1998's unixes, this is still a good assumption */
+#endif
+
+#ifdef SYS_SIGLIST /* platform has sys_siglist[] */
+#define INIT_SIGLIST()  /*nothing*/
+#else /* platform has no sys_siglist[], define our own */
+#define SYS_SIGLIST ap_sys_siglist
+#define INIT_SIGLIST() siglist_init();
+
+const char *ap_sys_siglist[NumSIG];
+
+static void siglist_init(void)
+{
+    int sig;
+
+    ap_sys_siglist[0] = "Signal 0";
+#ifdef SIGHUP
+    ap_sys_siglist[SIGHUP] = "Hangup";
+#endif
+#ifdef SIGINT
+    ap_sys_siglist[SIGINT] = "Interrupt";
+#endif
+#ifdef SIGQUIT
+    ap_sys_siglist[SIGQUIT] = "Quit";
+#endif
+#ifdef SIGILL
+    ap_sys_siglist[SIGILL] = "Illegal instruction";
+#endif
+#ifdef SIGTRAP
+    ap_sys_siglist[SIGTRAP] = "Trace/BPT trap";
+#endif
+#ifdef SIGIOT
+    ap_sys_siglist[SIGIOT] = "IOT instruction";
+#endif
+#ifdef SIGABRT
+    ap_sys_siglist[SIGABRT] = "Abort";
+#endif
+#ifdef SIGEMT
+    ap_sys_siglist[SIGEMT] = "Emulator trap";
+#endif
+#ifdef SIGFPE
+    ap_sys_siglist[SIGFPE] = "Arithmetic exception";
+#endif
+#ifdef SIGKILL
+    ap_sys_siglist[SIGKILL] = "Killed";
+#endif
+#ifdef SIGBUS
+    ap_sys_siglist[SIGBUS] = "Bus error";
+#endif
+#ifdef SIGSEGV
+    ap_sys_siglist[SIGSEGV] = "Segmentation fault";
+#endif
+#ifdef SIGSYS
+    ap_sys_siglist[SIGSYS] = "Bad system call";
+#endif
+#ifdef SIGPIPE
+    ap_sys_siglist[SIGPIPE] = "Broken pipe";
+#endif
+#ifdef SIGALRM
+    ap_sys_siglist[SIGALRM] = "Alarm clock";
+#endif
+#ifdef SIGTERM
+    ap_sys_siglist[SIGTERM] = "Terminated";
+#endif
+#ifdef SIGUSR1
+    ap_sys_siglist[SIGUSR1] = "User defined signal 1";
+#endif
+#ifdef SIGUSR2
+    ap_sys_siglist[SIGUSR2] = "User defined signal 2";
+#endif
+#ifdef SIGCLD
+    ap_sys_siglist[SIGCLD] = "Child status change";
+#endif
+#ifdef SIGCHLD
+    ap_sys_siglist[SIGCHLD] = "Child status change";
+#endif
+#ifdef SIGPWR
+    ap_sys_siglist[SIGPWR] = "Power-fail restart";
+#endif
+#ifdef SIGWINCH
+    ap_sys_siglist[SIGWINCH] = "Window changed";
+#endif
+#ifdef SIGURG
+    ap_sys_siglist[SIGURG] = "urgent socket condition";
+#endif
+#ifdef SIGPOLL
+    ap_sys_siglist[SIGPOLL] = "Pollable event occurred";
+#endif
+#ifdef SIGIO
+    ap_sys_siglist[SIGIO] = "socket I/O possible";
+#endif
+#ifdef SIGSTOP
+    ap_sys_siglist[SIGSTOP] = "Stopped (signal)";
+#endif
+#ifdef SIGTSTP
+    ap_sys_siglist[SIGTSTP] = "Stopped";
+#endif
+#ifdef SIGCONT
+    ap_sys_siglist[SIGCONT] = "Continued";
+#endif
+#ifdef SIGTTIN
+    ap_sys_siglist[SIGTTIN] = "Stopped (tty input)";
+#endif
+#ifdef SIGTTOU
+    ap_sys_siglist[SIGTTOU] = "Stopped (tty output)";
+#endif
+#ifdef SIGVTALRM
+    ap_sys_siglist[SIGVTALRM] = "virtual timer expired";
+#endif
+#ifdef SIGPROF
+    ap_sys_siglist[SIGPROF] = "profiling timer expired";
+#endif
+#ifdef SIGXCPU
+    ap_sys_siglist[SIGXCPU] = "exceeded cpu limit";
+#endif
+#ifdef SIGXFSZ
+    ap_sys_siglist[SIGXFSZ] = "exceeded file size limit";
+#endif
+    for (sig=0; sig < sizeof(ap_sys_siglist)/sizeof(ap_sys_siglist[0]); ++sig)
+        if (ap_sys_siglist[sig] == NULL)
+            ap_sys_siglist[sig] = "";
+}
+#endif /* platform has sys_siglist[] */
+
+
+/* handle all varieties of core dumping signals */
+static void sig_coredump(int sig)
+{
+    chdir(ap_coredump_dir);
+    signal(sig, SIG_DFL);
+#ifndef WIN32
+    kill(getpid(), sig);
+#else
+    raise(sig);
+#endif
+    /* At this point we've got sig blocked, because we're still inside
+     * the signal handler.  When we leave the signal handler it will
+     * be unblocked, and we'll take the signal... and coredump or whatever
+     * is appropriate for this particular Unix.  In addition the parent
+     * will see the real signal we received -- whereas if we called
+     * abort() here, the parent would only see SIGABRT.
+     */
+}
+
+/*****************************************************************
+ * Connection structures and accounting...
+ */
+
+static void just_die(int sig)
+{                              /* SIGHUP to child process??? */
+    /* if alarms are blocked we have to wait to die otherwise we might
+     * end up with corruption in alloc.c's internal structures */
+    if (alarms_blocked) {
+       exit_after_unblock = 1;
+    }
+    else {
+       clean_child_exit(0);
+    }
+}
+
+static int volatile usr1_just_die = 1;
+static int volatile deferred_die;
+
+static void usr1_handler(int sig)
+{
+    if (usr1_just_die) {
+       just_die(sig);
+    }
+    deferred_die = 1;
+}
+
+/* volatile just in case */
+static int volatile shutdown_pending;
+static int volatile restart_pending;
+static int volatile is_graceful;
+ap_generation_t volatile ap_my_generation=0;
+
+#ifdef WIN32
+/*
+ * Signalling Apache on NT.
+ *
+ * Under Unix, Apache can be told to shutdown or restart by sending various
+ * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so
+ * we use "events" instead. The parent apache process goes into a loop
+ * where it waits forever for a set of events. Two of those events are
+ * called
+ *
+ *    apPID_shutdown
+ *    apPID_restart
+ *
+ * (where PID is the PID of the apache parent process). When one of these
+ * is signalled, the Apache parent performs the appropriate action. The events
+ * can become signalled through internal Apache methods (e.g. if the child
+ * finds a fatal error and needs to kill its parent), via the service
+ * control manager (the control thread will signal the shutdown event when
+ * requested to stop the Apache service), from the -k Apache command line,
+ * or from any external program which finds the Apache PID from the
+ * httpd.pid file.
+ *
+ * The signal_parent() function, below, is used to signal one of these events.
+ * It can be called by any child or parent process, since it does not
+ * rely on global variables.
+ *
+ * On entry, type gives the event to signal. 0 means shutdown, 1 means 
+ * graceful restart.
+ */
+
+static void signal_parent(int type)
+{
+    HANDLE e;
+    char *signal_name;
+    extern char signal_shutdown_name[];
+    extern char signal_restart_name[];
+
+    /* after updating the shutdown_pending or restart flags, we need
+     * to wake up the parent process so it can see the changes. The
+     * parent will normally be waiting for either a child process
+     * to die, or for a signal on the "spache-signal" event. So set the
+     * "apache-signal" event here.
+     */
+
+    if (one_process) {
+       return;
+    }
+
+    switch(type) {
+    case 0: signal_name = signal_shutdown_name; break;
+    case 1: signal_name = signal_restart_name; break;
+    default: return;
+    }
+
+    APD2("signal_parent signalling event \"%s\"", signal_name);
+
+    e = OpenEvent(EVENT_ALL_ACCESS, FALSE, signal_name);
+    if (!e) {
+       /* Um, problem, can't signal the parent, which means we can't
+        * signal ourselves to die. Ignore for now...
+        */
+       ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+           "OpenEvent on %s event", signal_name);
+       return;
+    }
+    if (SetEvent(e) == 0) {
+       /* Same problem as above */
+       ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+           "SetEvent on %s event", signal_name);
+       CloseHandle(e);
+       return;
+    }
+    CloseHandle(e);
+}
+#endif
+
+/*
+ * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
+ * functions to initiate shutdown or restart without relying on signals. 
+ * Previously this was initiated in sig_term() and restart() signal handlers, 
+ * but we want to be able to start a shutdown/restart from other sources --
+ * e.g. on Win32, from the service manager. Now the service manager can
+ * call ap_start_shutdown() or ap_start_restart() as appropiate.  Note that
+ * these functions can also be called by the child processes, since global
+ * variables are no longer used to pass on the required action to the parent.
+ */
+
+void ap_start_shutdown(void)
+{
+#ifndef WIN32
+    if (shutdown_pending == 1) {
+       /* Um, is this _probably_ not an error, if the user has
+        * tried to do a shutdown twice quickly, so we won't
+        * worry about reporting it.
+        */
+       return;
+    }
+    shutdown_pending = 1;
+#else
+    signal_parent(0);      /* get the parent process to wake up */
+#endif
+}
+
+/* do a graceful restart if graceful == 1 */
+void ap_start_restart(int graceful)
+{
+#ifndef WIN32
+    if (restart_pending == 1) {
+       /* Probably not an error - don't bother reporting it */
+       return;
+    }
+    restart_pending = 1;
+    is_graceful = graceful;
+#else
+    signal_parent(1);      /* get the parent process to wake up */
+#endif /* WIN32 */
+}
+
+static void sig_term(int sig)
+{
+    ap_start_shutdown();
+}
+
+static void restart(int sig)
+{
+#ifndef WIN32
+    ap_start_restart(sig == SIGUSR1);
+#else
+    ap_start_restart(1);
+#endif
+}
+
+static void set_signals(void)
+{
+#ifndef NO_USE_SIGACTION
+    struct sigaction sa;
+
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;
+
+    if (!one_process) {
+       sa.sa_handler = sig_coredump;
+#if defined(SA_ONESHOT)
+       sa.sa_flags = SA_ONESHOT;
+#elif defined(SA_RESETHAND)
+       sa.sa_flags = SA_RESETHAND;
+#endif
+       if (sigaction(SIGSEGV, &sa, NULL) < 0)
+           ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGSEGV)");
+#ifdef SIGBUS
+       if (sigaction(SIGBUS, &sa, NULL) < 0)
+           ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGBUS)");
+#endif
+#ifdef SIGABORT
+       if (sigaction(SIGABORT, &sa, NULL) < 0)
+           ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABORT)");
+#endif
+#ifdef SIGABRT
+       if (sigaction(SIGABRT, &sa, NULL) < 0)
+           ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABRT)");
+#endif
+#ifdef SIGILL
+       if (sigaction(SIGILL, &sa, NULL) < 0)
+           ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGILL)");
+#endif
+       sa.sa_flags = 0;
+    }
+    sa.sa_handler = sig_term;
+    if (sigaction(SIGTERM, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGTERM)");
+#ifdef SIGINT
+    if (sigaction(SIGINT, &sa, NULL) < 0)
+        ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGINT)");
+#endif
+#ifdef SIGXCPU
+    sa.sa_handler = SIG_DFL;
+    if (sigaction(SIGXCPU, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXCPU)");
+#endif
+#ifdef SIGXFSZ
+    sa.sa_handler = SIG_DFL;
+    if (sigaction(SIGXFSZ, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXFSZ)");
+#endif
+#ifdef SIGPIPE
+    sa.sa_handler = SIG_IGN;
+    if (sigaction(SIGPIPE, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGPIPE)");
+#endif
+
+    /* we want to ignore HUPs and USR1 while we're busy processing one */
+    sigaddset(&sa.sa_mask, SIGHUP);
+    sigaddset(&sa.sa_mask, SIGUSR1);
+    sa.sa_handler = restart;
+    if (sigaction(SIGHUP, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGHUP)");
+    if (sigaction(SIGUSR1, &sa, NULL) < 0)
+       ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGUSR1)");
+#else
+    if (!one_process) {
+       signal(SIGSEGV, sig_coredump);
+#ifdef SIGBUS
+       signal(SIGBUS, sig_coredump);
+#endif /* SIGBUS */
+#ifdef SIGABORT
+       signal(SIGABORT, sig_coredump);
+#endif /* SIGABORT */
+#ifdef SIGABRT
+       signal(SIGABRT, sig_coredump);
+#endif /* SIGABRT */
+#ifdef SIGILL
+       signal(SIGILL, sig_coredump);
+#endif /* SIGILL */
+#ifdef SIGXCPU
+       signal(SIGXCPU, SIG_DFL);
+#endif /* SIGXCPU */
+#ifdef SIGXFSZ
+       signal(SIGXFSZ, SIG_DFL);
+#endif /* SIGXFSZ */
+    }
+
+    signal(SIGTERM, sig_term);
+#ifdef SIGHUP
+    signal(SIGHUP, restart);
+#endif /* SIGHUP */
+#ifdef SIGUSR1
+    signal(SIGUSR1, restart);
+#endif /* SIGUSR1 */
+#ifdef SIGPIPE
+    signal(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+
+#endif
+}
+
+
+/*****************************************************************
+ * Here follows a long bunch of generic server bookkeeping stuff...
+ */
+
+static void detach(void)
+{
+#if !defined(WIN32)
+    int x;
+
+    chdir("/");
+#if !defined(MPE) && !defined(OS2) && !defined(TPF)
+/* Don't detach for MPE because child processes can't survive the death of
+   the parent. */
+    if ((x = fork()) > 0)
+       exit(0);
+    else if (x == -1) {
+       perror("fork");
+       fprintf(stderr, "%s: unable to fork new process\n", ap_server_argv0);
+       exit(1);
+    }
+    RAISE_SIGSTOP(DETACH);
+#endif
+#ifndef NO_SETSID
+    if ((pgrp = setsid()) == -1) {
+       perror("setsid");
+       fprintf(stderr, "%s: setsid failed\n", ap_server_argv0);
+       exit(1);
+    }
+#elif defined(NEXT) || defined(NEWSOS)
+    if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) {
+       perror("setpgrp");
+       fprintf(stderr, "%s: setpgrp or getpgrp failed\n", ap_server_argv0);
+       exit(1);
+    }
+#elif defined(OS2) || defined(TPF)
+    /* OS/2 and TPF don't support process group IDs */
+    pgrp = getpid();
+#elif defined(MPE)
+    /* MPE uses negative pid for process group */
+    pgrp = -getpid();
+#else
+    if ((pgrp = setpgrp(getpid(), 0)) == -1) {
+       perror("setpgrp");
+       fprintf(stderr, "%s: setpgrp failed\n", ap_server_argv0);
+       exit(1);
+    }
+#endif
+
+    /* close out the standard file descriptors */
+    if (freopen("/dev/null", "r", stdin) == NULL) {
+       fprintf(stderr, "%s: unable to replace stdin with /dev/null: %s\n",
+               ap_server_argv0, strerror(errno));
+       /* continue anyhow -- note we can't close out descriptor 0 because we
+        * have nothing to replace it with, and if we didn't have a descriptor
+        * 0 the next file would be created with that value ... leading to
+        * havoc.
+        */
+    }
+    if (freopen("/dev/null", "w", stdout) == NULL) {
+       fprintf(stderr, "%s: unable to replace stdout with /dev/null: %s\n",
+               ap_server_argv0, strerror(errno));
+    }
+    /* stderr is a tricky one, we really want it to be the error_log,
+     * but we haven't opened that yet.  So leave it alone for now and it'll
+     * be reopened moments later.
+     */
+#endif /* ndef WIN32 */
+}
+
+/* Set group privileges.
+ *
+ * Note that we use the username as set in the config files, rather than
+ * the lookup of to uid --- the same uid may have multiple passwd entries,
+ * with different sets of groups for each.
+ */
+
+static void set_group_privs(void)
+{
+#ifndef WIN32
+    if (!geteuid()) {
+       char *name;
+
+       /* Get username if passed as a uid */
+
+       if (ap_user_name[0] == '#') {
+           struct passwd *ent;
+           uid_t uid = atoi(&ap_user_name[1]);
+
+           if ((ent = getpwuid(uid)) == NULL) {
+               ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+                        "getpwuid: couldn't determine user name from uid %u, "
+                        "you probably need to modify the User directive",
+                        (unsigned)uid);
+               clean_child_exit(APEXIT_CHILDFATAL);
+           }
+
+           name = ent->pw_name;
+       }
+       else
+           name = ap_user_name;
+
+#if !defined(OS2) && !defined(TPF)
+       /* OS/2 and TPF don't support groups. */
+
+       /*
+        * Set the GID before initgroups(), since on some platforms
+        * setgid() is known to zap the group list.
+        */
+       if (setgid(ap_group_id) == -1) {
+           ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+                       "setgid: unable to set group id to Group %u",
+                       (unsigned)ap_group_id);
+           clean_child_exit(APEXIT_CHILDFATAL);
+       }
+
+       /* Reset `groups' attributes. */
+
+       if (initgroups(name, ap_group_id) == -1) {
+           ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+                       "initgroups: unable to set groups for User %s "
+                       "and Group %u", name, (unsigned)ap_group_id);
+           clean_child_exit(APEXIT_CHILDFATAL);
+       }
+#ifdef MULTIPLE_GROUPS
+       if (getgroups(NGROUPS_MAX, group_id_list) == -1) {
+           ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+                       "getgroups: unable to get group list");
+           clean_child_exit(APEXIT_CHILDFATAL);
+       }
+#endif /* MULTIPLE_GROUPS */
+#endif /* !defined(OS2) && !defined(TPF) */
+    }
+#endif /* ndef WIN32 */
+}
+
+/* check to see if we have the 'suexec' setuid wrapper installed */
+static int init_suexec(void)
+{
+#ifndef WIN32
+    struct stat wrapper;
+
+    if ((stat(SUEXEC_BIN, &wrapper)) != 0)
+       return (ap_suexec_enabled);
+
+    if ((wrapper.st_mode & S_ISUID) && wrapper.st_uid == 0) {
+       ap_suexec_enabled = 1;
+    }
+#endif /* ndef WIN32 */
+    return (ap_suexec_enabled);
+}
+
+/*****************************************************************
+ * Connection structures and accounting...
+ */
+
+
+static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout,
+                            const struct sockaddr_in *remaddr,
+                            const struct sockaddr_in *saddr,
+                            int child_num)
+{
+    conn_rec *conn = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec));
+
+    /* Got a connection structure, so initialize what fields we can
+     * (the rest are zeroed out by pcalloc).
+     */
+
+    conn->child_num = child_num;
+
+    conn->pool = p;
+    conn->local_addr = *saddr;
+    conn->local_ip = ap_pstrdup(conn->pool,
+                               inet_ntoa(conn->local_addr.sin_addr));
+    conn->server = server; /* just a guess for now */
+    ap_update_vhost_given_ip(conn);
+    conn->base_server = conn->server;
+    conn->client = inout;
+
+    conn->remote_addr = *remaddr;
+    conn->remote_ip = ap_pstrdup(conn->pool,
+                             inet_ntoa(conn->remote_addr.sin_addr));
+
+    return conn;
+}
+
+#if defined(TCP_NODELAY) && !defined(MPE) && !defined(TPF)
+static void sock_disable_nagle(int s)
+{
+    /* The Nagle algorithm says that we should delay sending partial
+     * packets in hopes of getting more data.  We don't want to do
+     * this; we are not telnet.  There are bad interactions between
+     * persistent connections and Nagle's algorithm that have very severe
+     * performance penalties.  (Failing to disable Nagle is not much of a
+     * problem with simple HTTP.)
+     *
+     * In spite of these problems, failure here is not a shooting offense.
+     */
+    int just_say_no = 1;
+
+    if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no,
+                  sizeof(int)) < 0) {
+       ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+                   "setsockopt: (TCP_NODELAY)");
+    }
+}
+
+#else
+#define sock_disable_nagle(s)  /* NOOP */
+#endif
+
+
+static int make_sock(pool *p, const struct sockaddr_in *server)
+{
+    int s;
+    int one = 1;
+    char addr[512];
+
+    if (server->sin_addr.s_addr != htonl(INADDR_ANY))
+       ap_snprintf(addr, sizeof(addr), "address %s port %d",
+               inet_ntoa(server->sin_addr), ntohs(server->sin_port));
+    else
+       ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port));
+
+    /* note that because we're about to slack we don't use psocket */
+    ap_block_alarms();
+    if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+       ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+                   "make_sock: failed to get a socket for %s", addr);
+       ap_unblock_alarms();
+       exit(1);
+    }
+
+    /* Solaris (probably versions 2.4, 2.5, and 2.5.1 with various levels
+     * of tcp patches) has some really weird bugs where if you dup the
+     * socket now it breaks things across SIGHUP restarts.  It'll either
+     * be unable to bind, or it won't respond.
+     */
+#if defined (SOLARIS2) && SOLARIS2 < 260
+#define WORKAROUND_SOLARIS_BUG
+#endif
+
+    /* PR#1282 Unixware 1.x appears to have the same problem as solaris */
+#if defined (UW) && UW < 200
+#define WORKAROUND_SOLARIS_BUG
+#endif
+
+    /* PR#1973 NCR SVR4 systems appear to have the same problem */
+#if defined (MPRAS)
+#define WORKAROUND_SOLARIS_BUG
+#endif
+
+#ifndef WORKAROUND_SOLARIS_BUG
+#ifndef BEOS /* this won't work for BeOS sockets!! */
+    s = ap_slack(s, AP_SLACK_HIGH);
+#endif
+
+    ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */
+#ifdef TPF
+    os_note_additional_cleanups(p, s);
+#endif /* TPF */
+#endif
+
+#ifndef MPE
+/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */
+#ifndef _OSD_POSIX
+    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) {
+       ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+                   "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr);
+#ifdef BEOS
+    closesocket(s);
+#else
+       close(s);
+#endif
+       ap_unblock_alarms();
+       return -1;
+    }
+#endif /*_OSD_POSIX*/
+    one = 1;
+#ifdef SO_KEEPALIVE
+    if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) {
+       ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+                   "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr);
+#ifdef BEOS
+    closesocket(s);
+#else
+       close(s);
+#endif
+
+       ap_unblock_alarms();
+       return -1;
+    }
+#endif
+#endif
+
+    sock_disable_nagle(s);
+    sock_enable_linger(s);
+
+    /*
+     * To send data over high bandwidth-delay connections at full
+     * speed we must force the TCP window to open wide enough to keep the
+     * pipe full.  The default window size on many systems
+     * is only 4kB.  Cross-country WAN connections of 100ms
+     * at 1Mb/s are not impossible for well connected sites.
+     * If we assume 100ms cross-country latency,
+     * a 4kB buffer limits throughput to 40kB/s.
+     *
+     * To avoid this problem I've added the SendBufferSize directive
+     * to allow the web master to configure send buffer size.
+     *
+     * The trade-off of larger buffers is that more kernel memory
+     * is consumed.  YMMV, know your customers and your network!
+     *
+     * -John Heidemann <johnh@isi.edu> 25-Oct-96
+     *
+     * If no size is specified, use the kernel default.
+     */
+#ifndef BEOS                   /* BeOS does not support SO_SNDBUF */
+    if (server_conf->send_buffer_size) {
+       if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
+               (char *) &server_conf->send_buffer_size, sizeof(int)) < 0) {
+           ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+                       "make_sock: failed to set SendBufferSize for %s, "
+                       "using default", addr);
+           /* not a fatal error */
+       }
+    }
+#endif
+
+#ifdef MPE
+/* MPE requires CAP=PM and GETPRIVMODE to bind to ports less than 1024 */
+    if (ntohs(server->sin_port) < 1024)
+       GETPRIVMODE();
+#endif
+
+    if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) {
+       ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+           "make_sock: could not bind to %s", addr);
+#ifdef MPE
+       if (ntohs(server->sin_port) < 1024)
+           GETUSERMODE();
+#endif
+
+#ifdef BEOS
+    closesocket(s);
+#else
+       close(s);
+#endif
+       ap_unblock_alarms();
+       exit(1);
+    }
+#ifdef MPE
+    if (ntohs(server->sin_port) < 1024)
+       GETUSERMODE();
+#endif
+
+    if (listen(s, ap_listenbacklog) == -1) {
+       ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+           "make_sock: unable to listen for connections on %s", addr);
+#ifdef BEOS
+    closesocket(s);
+#else
+       close(s);
+#endif
+       ap_unblock_alarms();
+       exit(1);
+    }
+
+#ifdef WORKAROUND_SOLARIS_BUG
+    s = ap_slack(s, AP_SLACK_HIGH);
+
+    ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */
+#endif
+    ap_unblock_alarms();
+
+#ifdef CHECK_FD_SETSIZE
+    /* protect various fd_sets */
+    if (s >= FD_SETSIZE) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+           "make_sock: problem listening on %s, filedescriptor (%u) "
+           "larger than FD_SETSIZE (%u) "
+           "found, you probably need to rebuild Apache with a "
+           "larger FD_SETSIZE", addr, s, FD_SETSIZE);
+#ifdef BEOS
+    closesocket(s);
+#else
+       close(s);
+#endif
+       return -1;
+    }
+#endif
+
+    return s;
+}
+
+
+/*
+ * During a restart we keep track of the old listeners here, so that we
+ * can re-use the sockets.  We have to do this because we won't be able
+ * to re-open the sockets ("Address already in use").
+ *
+ * Unlike the listeners ring, old_listeners is a NULL terminated list.
+ *
+ * copy_listeners() makes the copy, find_listener() finds an old listener
+ * and close_unused_listener() cleans up whatever wasn't used.
+ */
+static listen_rec *old_listeners;
+
+/* unfortunately copy_listeners may be called before listeners is a ring */
+static void copy_listeners(pool *p)
+{
+    listen_rec *lr;
+
+    ap_assert(old_listeners == NULL);
+    if (ap_listeners == NULL) {
+       return;
+    }
+    lr = ap_listeners;
+    do {
+       listen_rec *nr = malloc(sizeof *nr);
+       if (nr == NULL) {
+           fprintf(stderr, "Ouch!  malloc failed in copy_listeners()\n");
+           exit(1);
+       }
+       *nr = *lr;
+       ap_kill_cleanups_for_socket(p, nr->fd);
+       nr->next = old_listeners;
+       ap_assert(!nr->used);
+       old_listeners = nr;
+       lr = lr->next;
+    } while (lr && lr != ap_listeners);
+}
+
+
+static int find_listener(listen_rec *lr)
+{
+    listen_rec *or;
+
+    for (or = old_listeners; or; or = or->next) {
+       if (!memcmp(&or->local_addr, &lr->local_addr, sizeof(or->local_addr))) {
+           or->used = 1;
+           return or->fd;
+       }
+    }
+    return -1;
+}
+
+
+static void close_unused_listeners(void)
+{
+    listen_rec *or, *next;
+
+    for (or = old_listeners; or; or = next) {
+       next = or->next;
+       if (!or->used)
+           closesocket(or->fd);
+       free(or);
+    }
+    old_listeners = NULL;
+}
+
+
+/* open sockets, and turn the listeners list into a singly linked ring */
+static void setup_listeners(pool *p)
+{
+    listen_rec *lr;
+    int fd;
+
+    listenmaxfd = -1;
+    FD_ZERO(&listenfds);
+    lr = ap_listeners;
+    for (;;) {
+       fd = find_listener(lr);
+       if (fd < 0) {
+           fd = make_sock(p, &lr->local_addr);
+       }
+       else {
+           ap_note_cleanups_for_socket(p, fd);
+       }
+       if (fd >= 0) {
+           FD_SET(fd, &listenfds);
+           if (fd > listenmaxfd)
+               listenmaxfd = fd;
+       }
+       lr->fd = fd;
+       if (lr->next == NULL)
+           break;
+       lr = lr->next;
+    }
+    /* turn the list into a ring */
+    lr->next = ap_listeners;
+    head_listener = ap_listeners;
+    close_unused_listeners();
+
+#ifdef NO_SERIALIZED_ACCEPT
+    /* warn them about the starvation problem if they're using multiple
+     * sockets
+     */
+    if (ap_listeners->next != ap_listeners) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, NULL,
+                   "You cannot use multiple Listens safely on your system, "
+                   "proceeding anyway.  See src/PORTING, search for "
+                   "SERIALIZED_ACCEPT.");
+    }
+#endif
+}
+
+
+/*
+ * Find a listener which is ready for accept().  This advances the
+ * head_listener global.
+ */
+static ap_inline listen_rec *find_ready_listener(fd_set * main_fds)
+{
+    listen_rec *lr;
+
+    lr = head_listener;
+    do {
+       if (FD_ISSET(lr->fd, main_fds)) {
+           head_listener = lr->next;
+           return (lr);
+       }
+       lr = lr->next;
+    } while (lr != head_listener);
+    return NULL;
+}
+
+
+#ifdef WIN32
+static int s_iInitCount = 0;
+
+static int AMCSocketInitialize(void)
+{
+    int iVersionRequested;
+    WSADATA wsaData;
+    int err;
+
+    if (s_iInitCount > 0) {
+       s_iInitCount++;
+       return (0);
+    }
+    else if (s_iInitCount < 0)
+       return (s_iInitCount);
+
+    /* s_iInitCount == 0. Do the initailization */
+    iVersionRequested = MAKEWORD(1, 1);
+    err = WSAStartup((WORD) iVersionRequested, &wsaData);
+    if (err) {
+       s_iInitCount = -1;
+       return (s_iInitCount);
+    }
+    if (LOBYTE(wsaData.wVersion) != 1 ||
+       HIBYTE(wsaData.wVersion) != 1) {
+       s_iInitCount = -2;
+       WSACleanup();
+       return (s_iInitCount);
+    }
+
+    s_iInitCount++;
+    return (s_iInitCount);
+
+}
+
+
+static void AMCSocketCleanup(void)
+{
+    if (--s_iInitCount == 0)
+       WSACleanup();
+    return;
+}
+#endif
+
+static void show_compile_settings(void)
+{
+    printf("Server version: %s\n", ap_get_server_version());
+    printf("Server built:   %s\n", ap_get_server_built());
+    printf("Server's Module Magic Number: %u:%u\n",
+          MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);
+    printf("Server compiled with....\n");
+#ifdef BIG_SECURITY_HOLE
+    printf(" -D BIG_SECURITY_HOLE\n");
+#endif
+#ifdef SECURITY_HOLE_PASS_AUTHORIZATION
+    printf(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n");
+#endif
+#ifdef HAVE_MMAP
+    printf(" -D HAVE_MMAP\n");
+#endif
+#ifdef HAVE_SHMGET
+    printf(" -D HAVE_SHMGET\n");
+#endif
+#ifdef USE_MMAP_SCOREBOARD
+    printf(" -D USE_MMAP_SCOREBOARD\n");
+#endif
+#ifdef USE_SHMGET_SCOREBOARD
+    printf(" -D USE_SHMGET_SCOREBOARD\n");
+#endif
+#ifdef USE_OS2_SCOREBOARD
+    printf(" -D USE_OS2_SCOREBOARD\n");
+#endif
+#ifdef USE_POSIX_SCOREBOARD
+    printf(" -D USE_POSIX_SCOREBOARD\n");
+#endif
+#ifdef USE_MMAP_FILES
+    printf(" -D USE_MMAP_FILES\n");
+#ifdef MMAP_SEGMENT_SIZE
+       printf(" -D MMAP_SEGMENT_SIZE=%ld\n",(long)MMAP_SEGMENT_SIZE);
+#endif
+#endif /*USE_MMAP_FILES*/
+#ifdef NO_WRITEV
+    printf(" -D NO_WRITEV\n");
+#endif
+#ifdef NO_LINGCLOSE
+    printf(" -D NO_LINGCLOSE\n");
+#endif
+#ifdef USE_FCNTL_SERIALIZED_ACCEPT
+    printf(" -D USE_FCNTL_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef USE_FLOCK_SERIALIZED_ACCEPT
+    printf(" -D USE_FLOCK_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef USE_USLOCK_SERIALIZED_ACCEPT
+    printf(" -D USE_USLOCK_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef USE_SYSVSEM_SERIALIZED_ACCEPT
+    printf(" -D USE_SYSVSEM_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef USE_PTHREAD_SERIALIZED_ACCEPT
+    printf(" -D USE_PTHREAD_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+    printf(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n");
+#endif
+#ifdef NO_OTHER_CHILD
+    printf(" -D NO_OTHER_CHILD\n");
+#endif
+#ifdef NO_RELIABLE_PIPED_LOGS
+    printf(" -D NO_RELIABLE_PIPED_LOGS\n");
+#endif
+#ifdef BUFFERED_LOGS
+    printf(" -D BUFFERED_LOGS\n");
+#ifdef PIPE_BUF
+       printf(" -D PIPE_BUF=%ld\n",(long)PIPE_BUF);
+#endif
+#endif
+#ifdef MULTITHREAD
+    printf(" -D MULTITHREAD\n");
+#endif
+#ifdef CHARSET_EBCDIC
+    printf(" -D CHARSET_EBCDIC\n");
+#endif
+#ifdef NEED_HASHBANG_EMUL
+    printf(" -D NEED_HASHBANG_EMUL\n");
+#endif
+#ifdef SHARED_CORE
+    printf(" -D SHARED_CORE\n");
+#endif
+
+/* This list displays the compiled-in default paths: */
+#ifdef HTTPD_ROOT
+    printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n");
+#endif
+#ifdef SUEXEC_BIN
+    printf(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n");
+#endif
+#if defined(SHARED_CORE) && defined(SHARED_CORE_DIR)
+    printf(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n");
+#endif
+#ifdef DEFAULT_PIDLOG
+    printf(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n");
+#endif
+#ifdef DEFAULT_SCOREBOARD
+    printf(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n");
+#endif
+#ifdef DEFAULT_LOCKFILE
+    printf(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n");
+#endif
+#ifdef DEFAULT_XFERLOG
+    printf(" -D DEFAULT_XFERLOG=\"" DEFAULT_XFERLOG "\"\n");
+#endif
+#ifdef DEFAULT_ERRORLOG
+    printf(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n");
+#endif
+#ifdef TYPES_CONFIG_FILE
+    printf(" -D TYPES_CONFIG_FILE=\"" TYPES_CONFIG_FILE "\"\n");
+#endif
+#ifdef SERVER_CONFIG_FILE
+    printf(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n");
+#endif
+#ifdef ACCESS_CONFIG_FILE
+    printf(" -D ACCESS_CONFIG_FILE=\"" ACCESS_CONFIG_FILE "\"\n");
+#endif
+#ifdef RESOURCE_CONFIG_FILE
+    printf(" -D RESOURCE_CONFIG_FILE=\"" RESOURCE_CONFIG_FILE "\"\n");
+#endif
+}
+
+
+/* Some init code that's common between win32 and unix... well actually
+ * some of it is #ifdef'd but was duplicated before anyhow.  This stuff
+ * is still a mess.
+ */
+static void common_init(void)
+{
+    INIT_SIGLIST()
+#ifdef AUX3
+    (void) set42sig();
+#endif
+
+#ifdef WIN32
+    /* Initialize the stupid sockets */
+    AMCSocketInitialize();
+#endif /* WIN32 */
+
+    pglobal = ap_init_alloc();
+    pconf = ap_make_sub_pool(pglobal);
+    plog = ap_make_sub_pool(pglobal);
+    ptrans = ap_make_sub_pool(pconf);
+
+    ap_util_init();
+    ap_util_uri_init();
+
+    pcommands = ap_make_sub_pool(NULL);
+    ap_server_pre_read_config  = ap_make_array(pcommands, 1, sizeof(char *));
+    ap_server_post_read_config = ap_make_array(pcommands, 1, sizeof(char *));
+    ap_server_config_defines   = ap_make_array(pcommands, 1, sizeof(char *));
+}
+
+#ifndef MULTITHREAD
+/*****************************************************************
+ * Child process main loop.
+ * The following vars are static to avoid getting clobbered by longjmp();
+ * they are really private to child_main.
+ */
+
+static int srv;
+static int csd;
+static int dupped_csd;
+static int requests_this_child;
+static fd_set main_fds;
+
+API_EXPORT(void) ap_child_terminate(request_rec *r)
+{
+    r->connection->keepalive = 0;
+    requests_this_child = ap_max_requests_per_child = 1;
+}
+
+static void child_main(int child_num_arg)
+{
+    NET_SIZE_T clen;
+    struct sockaddr sa_server;
+    struct sockaddr sa_client;
+    listen_rec *lr;
+
+    /* All of initialization is a critical section, we don't care if we're
+     * told to HUP or USR1 before we're done initializing.  For example,
+     * we could be half way through child_init_modules() when a restart
+     * signal arrives, and we'd have no real way to recover gracefully
+     * and exit properly.
+     *
+     * I suppose a module could take forever to initialize, but that would
+     * be either a broken module, or a broken configuration (i.e. network
+     * problems, file locking problems, whatever). -djg
+     */
+    ap_block_alarms();
+
+    my_pid = getpid();
+    csd = -1;
+    dupped_csd = -1;
+    my_child_num = child_num_arg;
+    requests_this_child = 0;
+
+    /* Get a sub pool for global allocations in this child, so that
+     * we can have cleanups occur when the child exits.
+     */
+    pchild = ap_make_sub_pool(pconf);
+
+    /* needs to be done before we switch UIDs so we have permissions */
+    reopen_scoreboard(pchild);
+    SAFE_ACCEPT(accept_mutex_child_init(pchild));
+
+    set_group_privs();
+#ifdef MPE
+    /* Only try to switch if we're running as MANAGER.SYS */
+    if (geteuid() == 1 && ap_user_id > 1) {
+       GETPRIVMODE();
+       if (setuid(ap_user_id) == -1) {
+           GETUSERMODE();
+           ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+                       "setuid: unable to change to uid: %d", ap_user_id);
+           exit(1);
+       }
+       GETUSERMODE();
+    }
+#else
+    /* Only try to switch if we're running as root */
+    if (!geteuid() && (
+#ifdef _OSD_POSIX
+       os_init_job_environment(server_conf, ap_user_name, one_process) != 0 || 
+#endif
+       setuid(ap_user_id) == -1)) {
+       ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+                   "setuid: unable to change to uid: %ld", (long) ap_user_id);
+       clean_child_exit(APEXIT_CHILDFATAL);
+    }
+#endif
+
+    ap_child_init_modules(pchild, server_conf);
+
+    /* done with the initialization critical section */
+    ap_unblock_alarms();
+
+    (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL);
+
+    /*
+     * Setup the jump buffers so that we can return here after a timeout 
+     */
+    ap_setjmp(jmpbuffer);
+#ifndef OS2
+#ifdef SIGURG
+    signal(SIGURG, timeout);
+#endif
+#endif
+    signal(SIGALRM, alrm_handler);
+#ifdef TPF
+    signal(SIGHUP, just_die);
+    signal(SIGTERM, just_die);
+    signal(SIGUSR1, just_die);
+#endif /* TPF */
+
+#ifdef OS2
+/* Stop Ctrl-C/Ctrl-Break signals going to child processes */
+    {
+        unsigned long ulTimes;
+        DosSetSignalExceptionFocus(0, &ulTimes);
+    }
+#endif
+
+    while (1) {
+       BUFF *conn_io;
+       request_rec *r;
+
+       /* Prepare to receive a SIGUSR1 due to graceful restart so that
+        * we can exit cleanly.  Since we're between connections right
+        * now it's the right time to exit, but we might be blocked in a
+        * system call when the graceful restart request is made. */
+       usr1_just_die = 1;
+       signal(SIGUSR1, usr1_handler);
+
+       /*
+        * (Re)initialize this child to a pre-connection state.
+        */
+
+       ap_kill_timeout(0);     /* Cancel any outstanding alarms. */
+       current_conn = NULL;
+
+       ap_clear_pool(ptrans);
+
+       ap_sync_scoreboard_image();
+       if (ap_scoreboard_image->global.running_generation != ap_my_generation) {
+           clean_child_exit(0);
+       }
+
+#ifndef WIN32
+       if ((ap_max_requests_per_child > 0
+            && requests_this_child++ >= ap_max_requests_per_child)) {
+           clean_child_exit(0);
+       }
+#else
+       ++requests_this_child;
+#endif
+
+       (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL);
+
+       /*
+        * Wait for an acceptable connection to arrive.
+        */
+
+       /* Lock around "accept", if necessary */
+       SAFE_ACCEPT(accept_mutex_on());
+
+       for (;;) {
+           if (ap_listeners->next != ap_listeners) {
+               /* more than one socket */
+               memcpy(&main_fds, &listenfds, sizeof(fd_set));
+               srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, NULL);
+
+               if (srv < 0 && errno != EINTR) {
+                   /* Single Unix documents select as returning errnos
+                    * EBADF, EINTR, and EINVAL... and in none of those
+                    * cases does it make sense to continue.  In fact
+                    * on Linux 2.0.x we seem to end up with EFAULT
+                    * occasionally, and we'd loop forever due to it.
+                    */
+                   ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)");
+                   clean_child_exit(1);
+               }
+
+               if (srv <= 0)
+                   continue;
+
+               lr = find_ready_listener(&main_fds);
+               if (lr == NULL)
+                   continue;
+               sd = lr->fd;
+           }
+           else {
+               /* only one socket, just pretend we did the other stuff */
+               sd = ap_listeners->fd;
+           }
+
+           /* if we accept() something we don't want to die, so we have to
+            * defer the exit
+            */
+           deferred_die = 0;
+           usr1_just_die = 0;
+           for (;;) {
+               clen = sizeof(sa_client);
+               csd = ap_accept(sd, &sa_client, &clen);
+               if (csd >= 0 || errno != EINTR)
+                   break;
+               if (deferred_die) {
+                   /* we didn't get a socket, and we were told to die */
+                   clean_child_exit(0);
+               }
+           }
+
+           if (csd >= 0)
+               break;          /* We have a socket ready for reading */
+           else {
+
+               /* Our old behaviour here was to continue after accept()
+                * errors.  But this leads us into lots of troubles
+                * because most of the errors are quite fatal.  For
+                * example, EMFILE can be caused by slow descriptor
+                * leaks (say in a 3rd party module, or libc).  It's
+                * foolish for us to continue after an EMFILE.  We also
+                * seem to tickle kernel bugs on some platforms which
+                * lead to never-ending loops here.  So it seems best
+                * to just exit in most cases.
+                */
+                switch (errno) {
+#ifdef EPROTO
+                   /* EPROTO on certain older kernels really means
+                    * ECONNABORTED, so we need to ignore it for them.
+                    * See discussion in new-httpd archives nh.9701
+                    * search for EPROTO.
+                    *
+                    * Also see nh.9603, search for EPROTO:
+                    * There is potentially a bug in Solaris 2.x x<6,
+                    * and other boxes that implement tcp sockets in
+                    * userland (i.e. on top of STREAMS).  On these
+                    * systems, EPROTO can actually result in a fatal
+                    * loop.  See PR#981 for example.  It's hard to
+                    * handle both uses of EPROTO.
+                    */
+                case EPROTO:
+#endif
+#ifdef ECONNABORTED
+                case ECONNABORTED:
+#endif
+                   /* Linux generates the rest of these, other tcp
+                    * stacks (i.e. bsd) tend to hide them behind
+                    * getsockopt() interfaces.  They occur when
+                    * the net goes sour or the client disconnects
+                    * after the three-way handshake has been done
+                    * in the kernel but before userland has picked
+                    * up the socket.
+                    */
+#ifdef ECONNRESET
+                case ECONNRESET:
+#endif
+#ifdef ETIMEDOUT
+                case ETIMEDOUT:
+#endif
+#ifdef EHOSTUNREACH
+               case EHOSTUNREACH:
+#endif
+#ifdef ENETUNREACH
+               case ENETUNREACH:
+#endif
+                    break;
+#ifdef ENETDOWN
+               case ENETDOWN:
+                    /*
+                     * When the network layer has been shut down, there
+                     * is not much use in simply exiting: the parent
+                     * would simply re-create us (and we'd fail again).
+                     * Use the CHILDFATAL code to tear the server down.
+                     * @@@ Martin's idea for possible improvement:
+                     * A different approach would be to define
+                     * a new APEXIT_NETDOWN exit code, the reception
+                     * of which would make the parent shutdown all
+                     * children, then idle-loop until it detected that
+                     * the network is up again, and restart the children.
+                     * Ben Hyde noted that temporary ENETDOWN situations
+                     * occur in mobile IP.
+                     */
+                   ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+                       "accept: giving up.");
+                   clean_child_exit(APEXIT_CHILDFATAL);
+#endif /*ENETDOWN*/
+
+#ifdef TPF
+               case EINACT:
+                   ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+                       "offload device inactive");
+                   clean_child_exit(APEXIT_CHILDFATAL);
+                   break;
+               default:
+                   ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+                       "select/accept error (%u)", errno);
+                   clean_child_exit(APEXIT_CHILDFATAL);
+#else
+               default:
+                   ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+                               "accept: (client socket)");
+                   clean_child_exit(1);
+#endif
+               }
+           }
+
+           /* go around again, safe to die */
+           usr1_just_die = 1;
+           if (deferred_die) {
+               /* ok maybe not, see ya later */
+               clean_child_exit(0);
+           }
+           /* or maybe we missed a signal, you never know on systems
+            * without reliable signals
+            */
+           ap_sync_scoreboard_image();
+           if (ap_scoreboard_image->global.running_generation != ap_my_generation) {
+               clean_child_exit(0);
+           }
+       }
+
+       SAFE_ACCEPT(accept_mutex_off());        /* unlock after "accept" */
+
+#ifdef TPF
+       if (csd == 0)                       /* 0 is invalid socket for TPF */
+           continue;
+#endif
+
+       /* We've got a socket, let's at least process one request off the
+        * socket before we accept a graceful restart request.
+        */
+       signal(SIGUSR1, SIG_IGN);
+
+       ap_note_cleanups_for_fd(ptrans, csd);
+
+       /* protect various fd_sets */
+#ifdef CHECK_FD_SETSIZE
+       if (csd >= FD_SETSIZE) {
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+               "[csd] filedescriptor (%u) larger than FD_SETSIZE (%u) "
+               "found, you probably need to rebuild Apache with a "
+               "larger FD_SETSIZE", csd, FD_SETSIZE);
+           continue;
+       }
+#endif
+
+       /*
+        * We now have a connection, so set it up with the appropriate
+        * socket options, file descriptors, and read/write buffers.
+        */
+
+       clen = sizeof(sa_server);
+       if (getsockname(csd, &sa_server, &clen) < 0) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "getsockname");
+           continue;
+       }
+
+       sock_disable_nagle(csd);
+
+       (void) ap_update_child_status(my_child_num, SERVER_BUSY_READ,
+                                  (request_rec *) NULL);
+
+       conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
+
+#ifdef B_SFIO
+       (void) sfdisc(conn_io->sf_in, SF_POPDISC);
+       sfdisc(conn_io->sf_in, bsfio_new(conn_io->pool, conn_io));
+       sfsetbuf(conn_io->sf_in, NULL, 0);
+
+       (void) sfdisc(conn_io->sf_out, SF_POPDISC);
+       sfdisc(conn_io->sf_out, bsfio_new(conn_io->pool, conn_io));
+       sfsetbuf(conn_io->sf_out, NULL, 0);
+#endif
+
+       dupped_csd = csd;
+#if defined(NEED_DUPPED_CSD)
+       if ((dupped_csd = dup(csd)) < 0) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+                       "dup: couldn't duplicate csd");
+           dupped_csd = csd;   /* Oh well... */
+       }
+       ap_note_cleanups_for_fd(ptrans, dupped_csd);
+
+       /* protect various fd_sets */
+#ifdef CHECK_FD_SETSIZE
+       if (dupped_csd >= FD_SETSIZE) {
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+               "[dupped_csd] filedescriptor (%u) larger than FD_SETSIZE (%u) "
+               "found, you probably need to rebuild Apache with a "
+               "larger FD_SETSIZE", dupped_csd, FD_SETSIZE);
+           continue;
+       }
+#endif
+#endif
+       ap_bpushfd(conn_io, csd, dupped_csd);
+
+       current_conn = new_connection(ptrans, server_conf, conn_io,
+                                         (struct sockaddr_in *) &sa_client,
+                                         (struct sockaddr_in *) &sa_server,
+                                         my_child_num);
+
+       /*
+        * Read and process each request found on our connection
+        * until no requests are left or we decide to close.
+        */
+
+       while ((r = ap_read_request(current_conn)) != NULL) {
+
+           /* read_request_line has already done a
+            * signal (SIGUSR1, SIG_IGN);
+            */
+
+           (void) ap_update_child_status(my_child_num, SERVER_BUSY_WRITE, r);
+
+           /* process the request if it was read without error */
+
+           if (r->status == HTTP_OK)
+               ap_process_request(r);
+
+           if(ap_extended_status)
+               increment_counts(my_child_num, r);
+
+           if (!current_conn->keepalive || current_conn->aborted)
+               break;
+
+           ap_destroy_pool(r->pool);
+           (void) ap_update_child_status(my_child_num, SERVER_BUSY_KEEPALIVE,
+                                      (request_rec *) NULL);
+
+           ap_sync_scoreboard_image();
+           if (ap_scoreboard_image->global.running_generation != ap_my_generation) {
+               ap_bclose(conn_io);
+               clean_child_exit(0);
+           }
+
+           /* In case we get a graceful restart while we're blocked
+            * waiting for the request.
+            *
+            * XXX: This isn't perfect, we might actually read the
+            * request and then just die without saying anything to
+            * the client.  This can be fixed by using deferred_die
+            * but you have to teach buff.c about it so that it can handle
+            * the EINTR properly.
+            *
+            * In practice though browsers (have to) expect keepalive
+            * connections to close before receiving a response because
+            * of network latencies and server timeouts.
+            */
+           usr1_just_die = 1;
+           signal(SIGUSR1, usr1_handler);
+       }
+
+       /*
+        * Close the connection, being careful to send out whatever is still
+        * in our buffers.  If possible, try to avoid a hard close until the
+        * client has ACKed our FIN and/or has stopped sending us data.
+        */
+
+#ifdef NO_LINGCLOSE
+       ap_bclose(conn_io);     /* just close it */
+#else
+       if (r && r->connection
+           && !r->connection->aborted
+           && r->connection->client
+           && (r->connection->client->fd >= 0)) {
+
+           lingering_close(r);
+       }
+       else {
+           ap_bsetflag(conn_io, B_EOUT, 1);
+           ap_bclose(conn_io);
+       }
+#endif
+    }
+}
+
+#ifdef TPF
+static void reset_tpf_listeners(APACHE_TPF_INPUT *input_parms)
+{
+    int count;
+    listen_rec *lr;
+
+    count = 0;
+    listenmaxfd = -1;
+    FD_ZERO(&listenfds);
+    lr = ap_listeners;
+
+    for(;;) {
+        lr->fd = input_parms->listeners[count];
+        if(lr->fd >= 0) {
+            FD_SET(lr->fd, &listenfds);
+            if(lr->fd > listenmaxfd)
+                listenmaxfd = lr->fd;
+        }
+        if(lr->next == NULL)
+            break;
+        lr = lr->next;
+        count++;
+    }
+    lr->next = ap_listeners;
+    head_listener = ap_listeners;
+    close_unused_listeners();
+}
+
+#endif /* TPF */
+
+static int make_child(server_rec *s, int slot, time_t now)
+{
+    int pid;
+
+    if (slot + 1 > max_daemons_limit) {
+       max_daemons_limit = slot + 1;
+    }
+
+    if (one_process) {
+       signal(SIGHUP, just_die);
+       signal(SIGINT, just_die);
+#ifdef SIGQUIT
+       signal(SIGQUIT, SIG_DFL);
+#endif
+       signal(SIGTERM, just_die);
+       child_main(slot);
+    }
+
+    /* avoid starvation */
+    head_listener = head_listener->next;
+
+    Explain1("Starting new child in slot %d", slot);
+    (void) ap_update_child_status(slot, SERVER_STARTING, (request_rec *) NULL);
+
+
+#ifdef _OSD_POSIX
+    /* BS2000 requires a "special" version of fork() before a setuid() call */
+    if ((pid = os_fork(ap_user_name)) == -1) {
+#elif defined(TPF)
+    if ((pid = os_fork(s, slot)) == -1) {
+#else
+    if ((pid = fork()) == -1) {
+#endif
+       ap_log_error(APLOG_MARK, APLOG_ERR, s, "fork: Unable to fork new process");
+
+       /* fork didn't succeed. Fix the scoreboard or else
+        * it will say SERVER_STARTING forever and ever
+        */
+       (void) ap_update_child_status(slot, SERVER_DEAD, (request_rec *) NULL);
+
+       /* In case system resources are maxxed out, we don't want
+          Apache running away with the CPU trying to fork over and
+          over and over again. */
+       sleep(10);
+
+       return -1;
+    }
+
+    if (!pid) {
+#ifdef AIX_BIND_PROCESSOR
+/* by default AIX binds to a single processor
+ * this bit unbinds children which will then bind to another cpu
+ */
+#include <sys/processor.h>
+       int status = bindprocessor(BINDPROCESS, (int)getpid(), 
+                                  PROCESSOR_CLASS_ANY);
+       if (status != OK) {
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf,
+                       "processor unbind failed %d", status);
+       }
+#endif
+       RAISE_SIGSTOP(MAKE_CHILD);
+       MONCONTROL(1);
+       /* Disable the restart signal handlers and enable the just_die stuff.
+        * Note that since restart() just notes that a restart has been
+        * requested there's no race condition here.
+        */
+       signal(SIGHUP, just_die);
+       signal(SIGUSR1, just_die);
+       signal(SIGTERM, just_die);
+       child_main(slot);
+    }
+
+#ifdef OPTIMIZE_TIMEOUTS
+    ap_scoreboard_image->parent[slot].last_rtime = now;
+#endif
+    ap_scoreboard_image->parent[slot].pid = pid;
+#ifdef SCOREBOARD_FILE
+    lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[slot]), 0);
+    force_write(scoreboard_fd, &ap_scoreboard_image->parent[slot],
+               sizeof(parent_score));
+#endif
+
+    return 0;
+}
+
+
+/* start up a bunch of children */
+static void startup_children(int number_to_start)
+{
+    int i;
+    time_t now = time(0);
+
+    for (i = 0; number_to_start && i < ap_daemons_limit; ++i) {
+       if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) {
+           continue;
+       }
+       if (make_child(server_conf, i, now) < 0) {
+           break;
+       }
+       --number_to_start;
+    }
+}
+
+
+/*
+ * idle_spawn_rate is the number of children that will be spawned on the
+ * next maintenance cycle if there aren't enough idle servers.  It is
+ * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
+ * without the need to spawn.
+ */
+static int idle_spawn_rate = 1;
+#ifndef MAX_SPAWN_RATE
+#define MAX_SPAWN_RATE (32)
+#endif
+static int hold_off_on_exponential_spawning;
+
+static void perform_idle_server_maintenance(void)
+{
+    int i;
+    int to_kill;
+    int idle_count;
+    short_score *ss;
+    time_t now = time(0);
+    int free_length;
+    int free_slots[MAX_SPAWN_RATE];
+    int last_non_dead;
+    int total_non_dead;
+
+    /* initialize the free_list */
+    free_length = 0;
+
+    to_kill = -1;
+    idle_count = 0;
+    last_non_dead = -1;
+    total_non_dead = 0;
+
+    ap_sync_scoreboard_image();
+    for (i = 0; i < ap_daemons_limit; ++i) {
+       int status;
+
+       if (i >= max_daemons_limit && free_length == idle_spawn_rate)
+           break;
+       ss = &ap_scoreboard_image->servers[i];
+       status = ss->status;
+       if (status == SERVER_DEAD) {
+           /* try to keep children numbers as low as possible */
+           if (free_length < idle_spawn_rate) {
+               free_slots[free_length] = i;
+               ++free_length;
+           }
+       }
+       else {
+           /* We consider a starting server as idle because we started it
+            * at least a cycle ago, and if it still hasn't finished starting
+            * then we're just going to swamp things worse by forking more.
+            * So we hopefully won't need to fork more if we count it.
+            * This depends on the ordering of SERVER_READY and SERVER_STARTING.
+            */
+           if (status <= SERVER_READY) {
+               ++ idle_count;
+               /* always kill the highest numbered child if we have to...
+                * no really well thought out reason ... other than observing
+                * the server behaviour under linux where lower numbered children
+                * tend to service more hits (and hence are more likely to have
+                * their data in cpu caches).
+                */
+               to_kill = i;
+           }
+
+           ++total_non_dead;
+           last_non_dead = i;
+#ifdef OPTIMIZE_TIMEOUTS
+           if (ss->timeout_len) {
+               /* if it's a live server, with a live timeout then
+                * start checking its timeout */
+               parent_score *ps = &ap_scoreboard_image->parent[i];
+               if (ss->cur_vtime != ps->last_vtime) {
+                   /* it has made progress, so update its last_rtime,
+                    * last_vtime */
+                   ps->last_rtime = now;
+                   ps->last_vtime = ss->cur_vtime;
+               }
+               else if (ps->last_rtime + ss->timeout_len < now) {
+                   /* no progress, and the timeout length has been exceeded */
+                   ss->timeout_len = 0;
+                   kill(ps->pid, SIGALRM);
+               }
+           }
+#endif
+       }
+    }
+    max_daemons_limit = last_non_dead + 1;
+    if (idle_count > ap_daemons_max_free) {
+       /* kill off one child... we use SIGUSR1 because that'll cause it to
+        * shut down gracefully, in case it happened to pick up a request
+        * while we were counting
+        */
+       kill(ap_scoreboard_image->parent[to_kill].pid, SIGUSR1);
+       idle_spawn_rate = 1;
+    }
+    else if (idle_count < ap_daemons_min_free) {
+       /* terminate the free list */
+       if (free_length == 0) {
+           /* only report this condition once */
+           static int reported = 0;
+
+           if (!reported) {
+               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+                           "server reached MaxClients setting, consider"
+                           " raising the MaxClients setting");
+               reported = 1;
+           }
+           idle_spawn_rate = 1;
+       }
+       else {
+           if (idle_spawn_rate >= 8) {
+               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+                   "server seems busy, (you may need "
+                   "to increase StartServers, or Min/MaxSpareServers), "
+                   "spawning %d children, there are %d idle, and "
+                   "%d total children", idle_spawn_rate,
+                   idle_count, total_non_dead);
+           }
+           for (i = 0; i < free_length; ++i) {
+#ifdef TPF
+        if(make_child(server_conf, free_slots[i], now) == -1) {
+            if(free_length == 1) {
+                shutdown_pending = 1;
+                ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+                "No active child processes: shutting down");
+            }
+        }
+#else
+               make_child(server_conf, free_slots[i], now);
+#endif /* TPF */
+           }
+           /* the next time around we want to spawn twice as many if this
+            * wasn't good enough, but not if we've just done a graceful
+            */
+           if (hold_off_on_exponential_spawning) {
+               --hold_off_on_exponential_spawning;
+           }
+           else if (idle_spawn_rate < MAX_SPAWN_RATE) {
+               idle_spawn_rate *= 2;
+           }
+       }
+    }
+    else {
+       idle_spawn_rate = 1;
+    }
+}
+
+
+static void process_child_status(int pid, ap_wait_t status)
+{
+    /* Child died... if it died due to a fatal error,
+       * we should simply bail out.
+       */
+    if ((WIFEXITED(status)) &&
+       WEXITSTATUS(status) == APEXIT_CHILDFATAL) {
+       ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server_conf,
+                       "Child %d returned a Fatal error... \n"
+                       "Apache is exiting!",
+                       pid);
+       exit(APEXIT_CHILDFATAL);
+    }
+    if (WIFSIGNALED(status)) {
+       switch (WTERMSIG(status)) {
+       case SIGTERM:
+       case SIGHUP:
+       case SIGUSR1:
+       case SIGKILL:
+           break;
+       default:
+#ifdef SYS_SIGLIST
+#ifdef WCOREDUMP
+           if (WCOREDUMP(status)) {
+               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
+                            server_conf,
+                            "child pid %d exit signal %s (%d), "
+                            "possible coredump in %s",
+                            pid, (WTERMSIG(status) >= NumSIG) ? "" : 
+                            SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status),
+                            ap_coredump_dir);
+           }
+           else {
+#endif
+               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
+                            server_conf,
+                            "child pid %d exit signal %s (%d)", pid,
+                            SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status));
+#ifdef WCOREDUMP
+           }
+#endif
+#else
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
+                        server_conf,
+                        "child pid %d exit signal %d",
+                        pid, WTERMSIG(status));
+#endif
+       }
+    }
+}
+
+
+/*****************************************************************
+ * Executive routines.
+ */
+
+#ifndef STANDALONE_MAIN
+#define STANDALONE_MAIN standalone_main
+
+static void standalone_main(int argc, char **argv)
+{
+    int remaining_children_to_start;
+
+#ifdef OS2
+    printf("%s \n", ap_get_server_version());
+#endif
+
+    ap_standalone = 1;
+
+    is_graceful = 0;
+
+    if (!one_process) {
+       detach();
+    }
+    else {
+       MONCONTROL(1);
+    }
+
+    my_pid = getpid();
+
+    do {
+       copy_listeners(pconf);
+       if (!is_graceful) {
+           ap_restart_time = time(NULL);
+       }
+#ifdef SCOREBOARD_FILE
+       else if (scoreboard_fd != -1) {
+           ap_kill_cleanup(pconf, NULL, cleanup_scoreboard_file);
+           ap_kill_cleanups_for_fd(pconf, scoreboard_fd);
+       }
+#endif
+       ap_clear_pool(pconf);
+       ptrans = ap_make_sub_pool(pconf);
+
+       server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+       setup_listeners(pconf);
+       ap_clear_pool(plog);
+       ap_open_logs(server_conf, plog);
+       ap_log_pid(pconf, ap_pid_fname);
+       ap_set_version();       /* create our server_version string */
+       ap_init_modules(pconf, server_conf);
+       version_locked++;       /* no more changes to server_version */
+       SAFE_ACCEPT(accept_mutex_init(pconf));
+       if (!is_graceful) {
+           reinit_scoreboard(pconf);
+       }
+#ifdef SCOREBOARD_FILE
+       else {
+           ap_scoreboard_fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
+           ap_note_cleanups_for_fd(pconf, scoreboard_fd);
+       }
+#endif
+
+       set_signals();
+
+       if (ap_daemons_max_free < ap_daemons_min_free + 1)      /* Don't thrash... */
+           ap_daemons_max_free = ap_daemons_min_free + 1;
+
+       /* If we're doing a graceful_restart then we're going to see a lot
+        * of children exiting immediately when we get into the main loop
+        * below (because we just sent them SIGUSR1).  This happens pretty
+        * rapidly... and for each one that exits we'll start a new one until
+        * we reach at least daemons_min_free.  But we may be permitted to
+        * start more than that, so we'll just keep track of how many we're
+        * supposed to start up without the 1 second penalty between each fork.
+        */
+       remaining_children_to_start = ap_daemons_to_start;
+       if (remaining_children_to_start > ap_daemons_limit) {
+           remaining_children_to_start = ap_daemons_limit;
+       }
+       if (!is_graceful) {
+           startup_children(remaining_children_to_start);
+           remaining_children_to_start = 0;
+       }
+       else {
+           /* give the system some time to recover before kicking into
+            * exponential mode */
+           hold_off_on_exponential_spawning = 10;
+       }
+
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+                   "%s configured -- resuming normal operations",
+                   ap_get_server_version());
+       if (ap_suexec_enabled) {
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+                        "suEXEC mechanism enabled (wrapper: %s)", SUEXEC_BIN);
+       }
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+                   "Server built: %s", ap_get_server_built());
+       restart_pending = shutdown_pending = 0;
+
+       while (!restart_pending && !shutdown_pending) {
+           int child_slot;
+           ap_wait_t status;
+           int pid = wait_or_timeout(&status);
+
+           /* XXX: if it takes longer than 1 second for all our children
+            * to start up and get into IDLE state then we may spawn an
+            * extra child
+            */
+           if (pid >= 0) {
+               process_child_status(pid, status);
+               /* non-fatal death... note that it's gone in the scoreboard. */
+               ap_sync_scoreboard_image();
+               child_slot = find_child_by_pid(pid);
+               Explain2("Reaping child %d slot %d", pid, child_slot);
+               if (child_slot >= 0) {
+                   (void) ap_update_child_status(child_slot, SERVER_DEAD,
+                                              (request_rec *) NULL);
+                   if (remaining_children_to_start
+                       && child_slot < ap_daemons_limit) {
+                       /* we're still doing a 1-for-1 replacement of dead
+                        * children with new children
+                        */
+                       make_child(server_conf, child_slot, time(0));
+                       --remaining_children_to_start;
+                   }
+#ifndef NO_OTHER_CHILD
+               }
+               else if (reap_other_child(pid, status) == 0) {
+                   /* handled */
+#endif
+               }
+               else if (is_graceful) {
+                   /* Great, we've probably just lost a slot in the
+                    * scoreboard.  Somehow we don't know about this
+                    * child.
+                    */
+                   ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf,
+                               "long lost child came home! (pid %d)", pid);
+               }
+               /* Don't perform idle maintenance when a child dies,
+                * only do it when there's a timeout.  Remember only a
+                * finite number of children can die, and it's pretty
+                * pathological for a lot to die suddenly.
+                */
+               continue;
+           }
+           else if (remaining_children_to_start) {
+               /* we hit a 1 second timeout in which none of the previous
+                * generation of children needed to be reaped... so assume
+                * they're all done, and pick up the slack if any is left.
+                */
+               startup_children(remaining_children_to_start);
+               remaining_children_to_start = 0;
+               /* In any event we really shouldn't do the code below because
+                * few of the servers we just started are in the IDLE state
+                * yet, so we'd mistakenly create an extra server.
+                */
+               continue;
+           }
+
+           perform_idle_server_maintenance();
+#ifdef TPF
+        shutdown_pending = os_check_server(tpf_server_name);
+        ap_check_signals();
+        sleep(1);
+#endif /*TPF */
+       }
+
+       if (shutdown_pending) {
+           /* Time to gracefully shut down:
+            * Kill child processes, tell them to call child_exit, etc...
+            */
+           if (ap_killpg(pgrp, SIGTERM) < 0) {
+               ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGTERM");
+           }
+           reclaim_child_processes(1);         /* Start with SIGTERM */
+
+           /* cleanup pid file on normal shutdown */
+           {
+               const char *pidfile = NULL;
+               pidfile = ap_server_root_relative (pconf, ap_pid_fname);
+               if ( pidfile != NULL && unlink(pidfile) == 0)
+                   ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,
+                                server_conf,
+                                "removed PID file %s (pid=%ld)",
+                                pidfile, (long)getpid());
+           }
+
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+                       "caught SIGTERM, shutting down");
+           clean_parent_exit(0);
+       }
+
+       /* we've been told to restart */
+       signal(SIGHUP, SIG_IGN);
+       signal(SIGUSR1, SIG_IGN);
+
+       if (one_process) {
+           /* not worth thinking about */
+           clean_parent_exit(0);
+       }
+
+       /* advance to the next generation */
+       /* XXX: we really need to make sure this new generation number isn't in
+        * use by any of the children.
+        */
+       ++ap_my_generation;
+       ap_scoreboard_image->global.running_generation = ap_my_generation;
+       update_scoreboard_global();
+
+       if (is_graceful) {
+#ifndef SCOREBOARD_FILE
+           int i;
+#endif
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+                       "SIGUSR1 received.  Doing graceful restart");
+
+           /* kill off the idle ones */
+           if (ap_killpg(pgrp, SIGUSR1) < 0) {
+               ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGUSR1");
+           }
+#ifndef SCOREBOARD_FILE
+           /* This is mostly for debugging... so that we know what is still
+            * gracefully dealing with existing request.  But we can't really
+            * do it if we're in a SCOREBOARD_FILE because it'll cause
+            * corruption too easily.
+            */
+           ap_sync_scoreboard_image();
+           for (i = 0; i < ap_daemons_limit; ++i) {
+               if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) {
+                   ap_scoreboard_image->servers[i].status = SERVER_GRACEFUL;
+               }
+           }
+#endif
+       }
+       else {
+           /* Kill 'em off */
+           if (ap_killpg(pgrp, SIGHUP) < 0) {
+               ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGHUP");
+           }
+           reclaim_child_processes(0);         /* Not when just starting up */
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+                       "SIGHUP received.  Attempting to restart");
+       }
+    } while (restart_pending);
+
+    /*add_common_vars(NULL);*/
+}                              /* standalone_main */
+#else
+/* prototype */
+void STANDALONE_MAIN(int argc, char **argv);
+#endif /* STANDALONE_MAIN */
+
+extern char *optarg;
+extern int optind;
+
+int REALMAIN(int argc, char *argv[])
+{
+    int c;
+    int sock_in;
+    int sock_out;
+    char *s;
+    
+#ifdef SecureWare
+    if (set_auth_parameters(argc, argv) < 0)
+       perror("set_auth_parameters");
+    if (getluid() < 0)
+       if (setluid(getuid()) < 0)
+           perror("setluid");
+    if (setreuid(0, 0) < 0)
+       perror("setreuid");
+#endif
+
+#ifdef SOCKS
+    SOCKSinit(argv[0]);
+#endif
+
+#ifdef TPF
+    APACHE_TPF_INPUT input_parms;
+    ecbptr()->ebrout = PRIMECRAS;
+    input_parms = * (APACHE_TPF_INPUT *)(&(ecbptr()->ebw000));
+#endif
+
+    MONCONTROL(0);
+
+    common_init();
+    
+    if ((s = strrchr(argv[0], PATHSEPARATOR)) != NULL) {
+       ap_server_argv0 = ++s;
+    }
+    else {
+       ap_server_argv0 = argv[0];
+    }
+    
+    ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root));
+    ap_cpystrn(ap_server_confname, SERVER_CONFIG_FILE, sizeof(ap_server_confname));
+
+    ap_setup_prelinked_modules();
+
+    while ((c = getopt(argc, argv,
+                                   "D:C:c:xXd:f:vVlLR:StTh"
+#ifdef DEBUG_SIGSTOP
+                                   "Z:"
+#endif
+                       )) != -1) {
+       char **new;
+       switch (c) {
+       case 'c':
+           new = (char **)ap_push_array(ap_server_post_read_config);
+           *new = ap_pstrdup(pcommands, optarg);
+           break;
+       case 'C':
+           new = (char **)ap_push_array(ap_server_pre_read_config);
+           *new = ap_pstrdup(pcommands, optarg);
+           break;
+       case 'D':
+           new = (char **)ap_push_array(ap_server_config_defines);
+           *new = ap_pstrdup(pcommands, optarg);
+           break;
+       case 'd':
+           ap_cpystrn(ap_server_root, optarg, sizeof(ap_server_root));
+           break;
+       case 'f':
+           ap_cpystrn(ap_server_confname, optarg, sizeof(ap_server_confname));
+           break;
+       case 'v':
+           ap_set_version();
+           printf("Server version: %s\n", ap_get_server_version());
+           printf("Server built:   %s\n", ap_get_server_built());
+           exit(0);
+       case 'V':
+           ap_set_version();
+           show_compile_settings();
+           exit(0);
+       case 'l':
+           ap_show_modules();
+           exit(0);
+       case 'L':
+           ap_show_directives();
+           exit(0);
+       case 'X':
+           ++one_process;      /* Weird debugging mode. */
+           break;
+#ifdef TPF
+       case 'x':
+           os_tpf_child(&input_parms);
+           set_signals();
+           break;
+#endif
+#ifdef DEBUG_SIGSTOP
+       case 'Z':
+           raise_sigstop_flags = atoi(optarg);
+           break;
+#endif
+#ifdef SHARED_CORE
+       case 'R':
+           /* just ignore this option here, because it has only
+            * effect when SHARED_CORE is used and then it was
+            * already handled in the Shared Core Bootstrap
+            * program.
+            */
+           break;
+#endif
+       case 'S':
+           ap_dump_settings = 1;
+           break;
+       case 't':
+           ap_configtestonly = 1;
+           ap_docrootcheck = 1;
+           break;
+       case 'T':
+           ap_configtestonly = 1;
+           ap_docrootcheck = 0;
+           break;
+       case 'h':
+           usage(argv[0]);
+       case '?':
+           usage(argv[0]);
+       }
+    }
+
+    ap_suexec_enabled = init_suexec();
+    server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+
+    if (ap_configtestonly) {
+        fprintf(stderr, "Syntax OK\n");
+        exit(0);
+    }
+    if (ap_dump_settings) {
+        exit(0);
+    }
+
+    child_timeouts = !ap_standalone || one_process;
+
+#ifndef TPF
+    if (ap_standalone) {
+       ap_open_logs(server_conf, plog);
+       ap_set_version();
+       ap_init_modules(pconf, server_conf);
+       version_locked++;
+       STANDALONE_MAIN(argc, argv);
+    }
+#else
+    if (ap_standalone) {
+        if(!tpf_child) {
+            memcpy(tpf_server_name, input_parms.inetd_server.servname, INETD_SERVNAME_LENGTH);
+            tpf_server_name[INETD_SERVNAME_LENGTH+1] = '\0';
+            ap_open_logs(server_conf, pconf);
+        }
+        ap_set_version();
+        ap_init_modules(pconf, server_conf);
+        version_locked++;
+        if(tpf_child) {
+            copy_listeners(pconf);
+            reset_tpf_listeners(&input_parms);
+            server_conf->error_log = NULL;
+#ifdef SCOREBOARD_FILE
+            scoreboard_fd = input_parms.scoreboard_fd;
+            ap_scoreboard_image = &_scoreboard_image;
+#else /* must be USE_TPF_SCOREBOARD or USE_SHMGET_SCOREBOARD */
+            ap_scoreboard_image = (scoreboard *)input_parms.scoreboard_heap;
+#endif
+            child_main(input_parms.slot);
+        }
+        else
+            STANDALONE_MAIN(argc, argv);
+    }
+#endif
+    else {
+       conn_rec *conn;
+       request_rec *r;
+       struct sockaddr sa_server, sa_client;
+       BUFF *cio;
+       NET_SIZE_T l;
+
+       ap_set_version();
+       /* Yes this is called twice. */
+       ap_init_modules(pconf, server_conf);
+       version_locked++;
+       ap_open_logs(server_conf, plog);
+       ap_init_modules(pconf, server_conf);
+       set_group_privs();
+
+#ifdef MPE
+       /* Only try to switch if we're running as MANAGER.SYS */
+       if (geteuid() == 1 && ap_user_id > 1) {
+           GETPRIVMODE();
+           if (setuid(ap_user_id) == -1) {
+               GETUSERMODE();
+               ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+                           "setuid: unable to change to uid: %d", ap_user_id);
+               exit(1);
+           }
+           GETUSERMODE();
+       }
+#else
+       /* Only try to switch if we're running as root */
+       if (!geteuid() && setuid(ap_user_id) == -1) {
+           ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+                       "setuid: unable to change to uid: %ld",
+                       (long) ap_user_id);
+           exit(1);
+       }
+#endif
+       if (ap_setjmp(jmpbuffer)) {
+           exit(0);
+       }
+
+#ifdef TPF
+/* TPF only passes the incoming socket number from the internet daemon
+   in ebw000 */
+    sock_in = * (int*)(&(ecbptr()->ebw000));
+    sock_out = * (int*)(&(ecbptr()->ebw000));
+/* TPF also needs a signal set for alarm in inetd mode */
+    signal(SIGALRM, alrm_handler);
+#elif defined(MPE)
+/* HP MPE 5.5 inetd only passes the incoming socket as stdin (fd 0), whereas
+   HPUX inetd passes the incoming socket as stdin (fd 0) and stdout (fd 1).
+   Go figure.  SR 5003355016 has been submitted to request that the existing
+   functionality be documented, and then to enhance the functionality to be
+   like HPUX. */
+    sock_in = fileno(stdin);
+    sock_out = fileno(stdin);
+#else
+    sock_in = fileno(stdin);
+    sock_out = fileno(stdout);
+#endif
+
+       l = sizeof(sa_client);
+       if ((getpeername(sock_in, &sa_client, &l)) < 0) {
+/* get peername will fail if the input isn't a socket */
+           perror("getpeername");
+           memset(&sa_client, '\0', sizeof(sa_client));
+       }
+
+       l = sizeof(sa_server);
+       if (getsockname(sock_in, &sa_server, &l) < 0) {
+           perror("getsockname");
+           fprintf(stderr, "Error getting local address\n");
+           exit(1);
+       }
+       server_conf->port = ntohs(((struct sockaddr_in *) &sa_server)->sin_port);
+       cio = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
+        cio->fd = sock_out;
+        cio->fd_in = sock_in;
+       conn = new_connection(ptrans, server_conf, cio,
+                                 (struct sockaddr_in *) &sa_client,
+                                 (struct sockaddr_in *) &sa_server, -1);
+
+       while ((r = ap_read_request(conn)) != NULL) {
+
+           if (r->status == HTTP_OK)
+               ap_process_request(r);
+
+           if (!conn->keepalive || conn->aborted)
+               break;
+
+           ap_destroy_pool(r->pool);
+       }
+
+       ap_bclose(cio);
+    }
+    exit(0);
+}
+
+#else /* ndef MULTITHREAD */
+
+
+/**********************************************************************
+ * Multithreaded implementation
+ *
+ * This code is fairly specific to Win32.
+ *
+ * The model used to handle requests is a set of threads. One "main"
+ * thread listens for new requests. When something becomes
+ * available, it does a select and places the newly available socket
+ * onto a list of "jobs" (add_job()). Then any one of a fixed number
+ * of "worker" threads takes the top job off the job list with
+ * remove_job() and handles that connection to completion. After
+ * the connection has finished the thread is free to take another
+ * job from the job list.
+ *
+ * In the code, the "main" thread is running within the worker_main()
+ * function. The first thing this function does is create the
+ * worker threads, which operate in the child_sub_main() function. The
+ * main thread then goes into a loop within worker_main() where they
+ * do a select() on the listening sockets. The select times out once
+ * per second so that the thread can check for an "exit" signal
+ * from the parent process (see below). If this signal is set, the 
+ * thread can exit, but only after it has accepted all incoming
+ * connections already in the listen queue (since Win32 appears
+ * to through away listened but unaccepted connections when a 
+ * process dies).
+ *
+ * Because the main and worker threads exist within a single process
+ * they are vulnerable to crashes or memory leaks (crashes can also
+ * be caused within modules, of course). There also needs to be a 
+ * mechanism to perform restarts and shutdowns. This is done by
+ * creating the main & worker threads within a subprocess. A
+ * main process (the "parent process") creates one (or more) 
+ * processes to do the work, then the parent sits around waiting
+ * for the working process to die, in which case it starts a new
+ * one. The parent process also handles restarts (by creating
+ * a new working process then signalling the previous working process 
+ * exit ) and shutdowns (by signalling the working process to exit).
+ * The parent process operates within the master_main() function. This
+ * process also handles requests from the service manager (NT only).
+ *
+ * Signalling between the parent and working process uses a Win32
+ * event. Each child has a unique name for the event, which is
+ * passed to it with the -Z argument when the child is spawned. The
+ * parent sets (signals) this event to tell the child to die.
+ * At present all children do a graceful die - they finish all
+ * current jobs _and_ empty the listen queue before they exit.
+ * A non-graceful die would need a second event. The -Z argument in
+ * the child is also used to create the shutdown and restart events,
+ * since the prefix (apPID) contains the parent process PID.
+ *
+ * The code below starts with functions at the lowest level -
+ * worker threads, and works up to the top level - the main()
+ * function of the parent process.
+ *
+ * The scoreboard (in process memory) contains details of the worker
+ * threads (within the active working process). There is no shared
+ * "scoreboard" between processes, since only one is ever active
+ * at once (or at most, two, when one has been told to shutdown but
+ * is processes outstanding requests, and a new one has been started).
+ * This is controlled by a "start_mutex" which ensures only one working
+ * process is active at once.
+ **********************************************************************/
+
+/* The code protected by #ifdef UNGRACEFUL_RESTARTS/#endif sections
+ * could implement a sort-of ungraceful restart for Win32. instead of
+ * graceful restarts. 
+ *
+ * However it does not work too well because it does not intercept a
+ * connection already in progress (in child_sub_main()). We'd have to
+ * get that to poll on the exit event. 
+ */
+
+/*
+ * Definition of jobs, shared by main and worker threads.
+ */
+
+typedef struct joblist_s {
+    struct joblist_s *next;
+    int sock;
+} joblist;
+
+/*
+ * Globals common to main and worker threads. This structure is not
+ * used by the parent process.
+ */
+
+typedef struct globals_s {
+#ifdef UNGRACEFUL_RESTART
+    HANDLE thread_exit_event;
+#else
+    int exit_now;
+#endif
+    semaphore *jobsemaphore;
+    joblist *jobhead;
+    joblist *jobtail;
+    mutex *jobmutex;
+    int jobcount;
+} globals;
+
+globals allowed_globals =
+{0, NULL, NULL, NULL, NULL, 0};
+
+/*
+ * add_job()/remove_job() - add or remove an accepted socket from the
+ * list of sockets connected to clients. allowed_globals.jobmutex protects
+ * against multiple concurrent access to the linked list of jobs.
+ */
+
+void add_job(int sock)
+{
+    joblist *new_job;
+
+    ap_assert(allowed_globals.jobmutex);
+    /* TODO: If too many jobs in queue, sleep, check for problems */
+    ap_acquire_mutex(allowed_globals.jobmutex);
+    new_job = (joblist *) malloc(sizeof(joblist));
+    if (new_job == NULL) {
+       fprintf(stderr, "Ouch!  Out of memory in add_job()!\n");
+    }
+    new_job->next = NULL;
+    new_job->sock = sock;
+    if (allowed_globals.jobtail != NULL)
+       allowed_globals.jobtail->next = new_job;
+    allowed_globals.jobtail = new_job;
+    if (!allowed_globals.jobhead)
+       allowed_globals.jobhead = new_job;
+    allowed_globals.jobcount++;
+    release_semaphore(allowed_globals.jobsemaphore);
+    ap_release_mutex(allowed_globals.jobmutex);
+}
+
+int remove_job(void)
+{
+    joblist *job;
+    int sock;
+
+#ifdef UNGRACEFUL_RESTART
+    HANDLE hObjects[2];
+    int rv;
+
+    hObjects[0] = allowed_globals.jobsemaphore;
+    hObjects[1] = allowed_globals.thread_exit_event;
+
+    rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE);
+    ap_assert(rv != WAIT_FAILED);
+    if (rv == WAIT_OBJECT_0 + 1) {
+       /* thread_exit_now */
+       APD1("thread got exit now event");
+       return -1;
+    }
+    /* must be semaphore */
+#else
+    acquire_semaphore(allowed_globals.jobsemaphore);
+#endif
+    ap_assert(allowed_globals.jobmutex);
+
+#ifdef UNGRACEFUL_RESTART
+    if (!allowed_globals.jobhead) {
+#else
+    ap_acquire_mutex(allowed_globals.jobmutex);
+    if (allowed_globals.exit_now && !allowed_globals.jobhead) {
+#endif
+       ap_release_mutex(allowed_globals.jobmutex);
+       return (-1);
+    }
+    job = allowed_globals.jobhead;
+    ap_assert(job);
+    allowed_globals.jobhead = job->next;
+    if (allowed_globals.jobhead == NULL)
+       allowed_globals.jobtail = NULL;
+    ap_release_mutex(allowed_globals.jobmutex);
+    sock = job->sock;
+    free(job);
+    return (sock);
+}
+
+/*
+ * child_sub_main() - this is the main loop for the worker threads
+ *
+ * Each thread runs within this function. They wait within remove_job()
+ * for a job to become available, then handle all the requests on that
+ * connection until it is closed, then return to remove_job().
+ *
+ * The worker thread will exit when it removes a job which contains
+ * socket number -1. This provides a graceful thread exit, since
+ * it will never exit during a connection.
+ *
+ * This code in this function is basically equivalent to the child_main()
+ * from the multi-process (Unix) environment, except that we
+ *
+ *  - do not call child_init_modules (child init API phase)
+ *  - block in remove_job, and when unblocked we have an already
+ *    accepted socket, instead of blocking on a mutex or select().
+ */
+
+static void child_sub_main(int child_num)
+{
+    NET_SIZE_T clen;
+    struct sockaddr sa_server;
+    struct sockaddr sa_client;
+    pool *ptrans;
+    int requests_this_child = 0;
+    int csd = -1;
+    int dupped_csd = -1;
+    int srv = 0;
+
+    ptrans = ap_make_sub_pool(pconf);
+
+    (void) ap_update_child_status(child_num, SERVER_READY, (request_rec *) NULL);
+
+    /*
+     * Setup the jump buffers so that we can return here after a timeout.
+     */
+#if defined(USE_LONGJMP)
+    setjmp(jmpbuffer);
+#else
+    sigsetjmp(jmpbuffer, 1);
+#endif
+#ifdef SIGURG
+    signal(SIGURG, timeout);
+#endif
+
+    while (1) {
+       BUFF *conn_io;
+       request_rec *r;
+
+       /*
+        * (Re)initialize this child to a pre-connection state.
+        */
+
+       ap_set_callback_and_alarm(NULL, 0); /* Cancel any outstanding alarms */
+       timeout_req = NULL;                 /* No request in progress */
+       current_conn = NULL;
+
+       ap_clear_pool(ptrans);
+
+       (void) ap_update_child_status(child_num, SERVER_READY,
+                                     (request_rec *) NULL);
+
+       /* Get job from the job list. This will block until a job is ready.
+        * If -1 is returned then the main thread wants us to exit.
+        */
+       csd = remove_job();
+       if (csd == -1)
+           break;              /* time to exit */
+       requests_this_child++;
+
+       ap_note_cleanups_for_socket(ptrans, csd);
+
+       /*
+        * We now have a connection, so set it up with the appropriate
+        * socket options, file descriptors, and read/write buffers.
+        */
+
+       clen = sizeof(sa_server);
+       if (getsockname(csd, &sa_server, &clen) < 0) {
+           ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "getsockname");
+           continue;
+       }
+       clen = sizeof(sa_client);
+       if ((getpeername(csd, &sa_client, &clen)) < 0) {
+           /* get peername will fail if the input isn't a socket */
+           perror("getpeername");
+           memset(&sa_client, '\0', sizeof(sa_client));
+       }
+
+       sock_disable_nagle(csd);
+
+       (void) ap_update_child_status(child_num, SERVER_BUSY_READ,
+                                  (request_rec *) NULL);
+
+       conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
+       dupped_csd = csd;
+#if defined(NEED_DUPPED_CSD)
+       if ((dupped_csd = dup(csd)) < 0) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+                       "dup: couldn't duplicate csd");
+           dupped_csd = csd;   /* Oh well... */
+       }
+       ap_note_cleanups_for_socket(ptrans, dupped_csd);
+#endif
+       ap_bpushfd(conn_io, csd, dupped_csd);
+
+       current_conn = new_connection(ptrans, server_conf, conn_io,
+                                         (struct sockaddr_in *) &sa_client,
+                                         (struct sockaddr_in *) &sa_server,
+                                         child_num);
+
+       /*
+        * Read and process each request found on our connection
+        * until no requests are left or we decide to close.
+        */
+
+       while ((r = ap_read_request(current_conn)) != NULL) {
+           (void) ap_update_child_status(child_num, SERVER_BUSY_WRITE, r);
+
+           if (r->status == HTTP_OK)
+               ap_process_request(r);
+
+           if (ap_extended_status)
+               increment_counts(child_num, r);
+
+           if (!current_conn->keepalive || current_conn->aborted)
+               break;
+
+           ap_destroy_pool(r->pool);
+           (void) ap_update_child_status(child_num, SERVER_BUSY_KEEPALIVE,
+                                      (request_rec *) NULL);
+
+           ap_sync_scoreboard_image();
+       }
+
+       /*
+        * Close the connection, being careful to send out whatever is still
+        * in our buffers.  If possible, try to avoid a hard close until the
+        * client has ACKed our FIN and/or has stopped sending us data.
+        */
+       ap_kill_cleanups_for_socket(ptrans, csd);
+
+#ifdef NO_LINGCLOSE
+       ap_bclose(conn_io);     /* just close it */
+#else
+       if (r && r->connection
+           && !r->connection->aborted
+           && r->connection->client
+           && (r->connection->client->fd >= 0)) {
+
+           lingering_close(r);
+       }
+       else {
+           ap_bsetflag(conn_io, B_EOUT, 1);
+           ap_bclose(conn_io);
+       }
+#endif
+    }
+    ap_destroy_pool(ptrans);
+    (void) ap_update_child_status(child_num, SERVER_DEAD, NULL);
+}
+
+
+void child_main(int child_num_arg)
+{
+    /*
+     * Only reason for this function, is to pass in
+     * arguments to child_sub_main() on its stack so
+     * that longjump doesn't try to corrupt its local
+     * variables and I don't need to make those
+     * damn variables static/global
+     */
+    child_sub_main(child_num_arg);
+}
+
+
+
+void cleanup_thread(thread **handles, int *thread_cnt, int thread_to_clean)
+{
+    int i;
+
+    free_thread(handles[thread_to_clean]);
+    for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
+       handles[i] = handles[i + 1];
+    (*thread_cnt)--;
+}
+#ifdef WIN32
+/*
+ * The Win32 call WaitForMultipleObjects will only allow you to wait for 
+ * a maximum of MAXIMUM_WAIT_OBJECTS (current 64).  Since the threading 
+ * model in the multithreaded version of apache wants to use this call, 
+ * we are restricted to a maximum of 64 threads.  This is a simplistic 
+ * routine that will increase this size.
+ */
+static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, 
+                            DWORD dwSeconds)
+{
+    time_t tStopTime;
+    DWORD dwRet = WAIT_TIMEOUT;
+    DWORD dwIndex=0;
+    BOOL bFirst = TRUE;
+  
+    tStopTime = time(NULL) + dwSeconds;
+  
+    do {
+        if (!bFirst)
+            Sleep(1000);
+        else
+            bFirst = FALSE;
+          
+        for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) {
+            dwRet = WaitForMultipleObjects(
+                        min(MAXIMUM_WAIT_OBJECTS, 
+                            nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)),
+                        lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS), 
+                        0, 0);
+                                           
+            if (dwRet != WAIT_TIMEOUT) {                                          
+              break;
+            }
+        }
+    } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT));
+    
+    return dwRet;
+}
+#endif
+/*****************************************************************
+ * Executive routines.
+ */
+
+extern void main_control_server(void *); /* in hellop.c */
+
+event *exit_event;
+mutex *start_mutex;
+
+#define MAX_SIGNAL_NAME 30  /* Long enough for apPID_shutdown, where PID is an int */
+char signal_name_prefix[MAX_SIGNAL_NAME];
+char signal_restart_name[MAX_SIGNAL_NAME]; 
+char signal_shutdown_name[MAX_SIGNAL_NAME];
+
+#define MAX_SELECT_ERRORS 100
+
+/*
+ * Initialise the signal names, in the global variables signal_name_prefix, 
+ * signal_restart_name and signal_shutdown_name.
+ */
+
+void setup_signal_names(char *prefix)
+{
+    ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);    
+    ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name), 
+       "%s_shutdown", signal_name_prefix);    
+    ap_snprintf(signal_restart_name, sizeof(signal_restart_name), 
+       "%s_restart", signal_name_prefix);    
+
+    APD2("signal prefix %s", signal_name_prefix);
+}
+
+static void setup_inherited_listeners(pool *p)
+{
+    HANDLE pipe;
+    listen_rec *lr;
+    int fd;
+    WSAPROTOCOL_INFO WSAProtocolInfo;
+    DWORD BytesRead;
+
+    /* Open the pipe to the parent process to receive the inherited socket
+     * data. The sockets have been set to listening in the parent process.
+     */
+    pipe = GetStdHandle(STD_INPUT_HANDLE);
+
+    /* Setup the listeners */
+    listenmaxfd = -1;
+    FD_ZERO(&listenfds);
+    lr = ap_listeners;
+
+    FD_ZERO(&listenfds);
+
+    for (;;) {
+       fd = find_listener(lr);
+       if (fd < 0) {
+            if (!ReadFile(pipe, 
+                          &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO),
+                          &BytesRead,
+                          (LPOVERLAPPED) NULL)){
+                ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf,
+                             "setup_inherited_listeners: Unable to read socket data from parent");
+                exit(1);
+            }
+            fd = WSASocket(FROM_PROTOCOL_INFO,
+                           FROM_PROTOCOL_INFO,
+                           FROM_PROTOCOL_INFO,
+                           &WSAProtocolInfo,
+                           0,
+                           0);
+            if (fd == INVALID_SOCKET) {
+                ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf,
+                             "setup_inherited_listeners: WSASocket failed to get inherit the socket.");
+                exit(1);
+            }
+            APD2("setup_inherited_listeners: WSASocket() returned socket %d", fd);
+       }
+       else {
+           ap_note_cleanups_for_socket(p, fd);
+       }
+       if (fd >= 0) {
+           FD_SET(fd, &listenfds);
+           if (fd > listenmaxfd)
+               listenmaxfd = fd;
+       }
+       lr->fd = fd;
+       if (lr->next == NULL)
+           break;
+       lr = lr->next;
+    }
+    /* turn the list into a ring */
+    lr->next = ap_listeners;
+    head_listener = ap_listeners;
+    close_unused_listeners();
+    CloseHandle(pipe);
+    return;
+}
+
+/*
+ * worker_main() is main loop for the child process. The loop in
+ * this function becomes the controlling thread for the actually working
+ * threads (which run in a loop in child_sub_main()).
+ */
+
+void worker_main(void)
+{
+    int nthreads;
+    fd_set main_fds;
+    int srv;
+    int clen;
+    int csd;
+    struct sockaddr_in sa_client;
+    int total_jobs = 0;
+    thread **child_handles;
+    int rv;
+    time_t end_time;
+    int i;
+    struct timeval tv;
+    int wait_time = 1;
+    int max_jobs_per_exe;
+    int max_jobs_after_exit_request;
+    HANDLE hObjects[2];
+    int count_select_errors = 0;
+    pool *pchild;
+
+    pchild = ap_make_sub_pool(pconf);
+
+    ap_standalone = 1;
+    sd = -1;
+    nthreads = ap_threads_per_child;
+    max_jobs_after_exit_request = ap_excess_requests_per_child;
+    max_jobs_per_exe = ap_max_requests_per_child;
+    if (nthreads <= 0)
+       nthreads = 40;
+    if (max_jobs_per_exe <= 0)
+       max_jobs_per_exe = 0;
+    if (max_jobs_after_exit_request <= 0)
+       max_jobs_after_exit_request = max_jobs_per_exe / 10;
+
+    if (!one_process)
+       detach();
+
+    my_pid = getpid();
+
+    ++ap_my_generation;
+
+    copy_listeners(pconf);
+    ap_restart_time = time(NULL);
+
+    reinit_scoreboard(pconf);
+
+    /*
+     * Wait until we have permission to start accepting connections.
+     * start_mutex is used to ensure that only one child ever
+     * goes into the listen/accept loop at once. Also wait on exit_event,
+     * in case we (this child) is told to die before we get a chance to
+     * serve any requests.
+     */
+    hObjects[0] = (HANDLE)start_mutex;
+    hObjects[1] = (HANDLE)exit_event;
+    rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE);
+    if (rv == WAIT_FAILED) {
+       ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+                     "Waiting for start_mutex or exit_event -- process will exit");
+
+       ap_destroy_pool(pchild);
+       cleanup_scoreboard();
+       exit(0);
+    }
+    if (rv == WAIT_OBJECT_0 + 1) {
+       /* exit event signalled - exit now */
+       ap_destroy_pool(pchild);
+       cleanup_scoreboard();
+       exit(0);
+    }
+    /* start_mutex obtained, continue into the select() loop */
+    if (one_process) {
+        setup_listeners(pconf);
+    } else {
+        /* Get listeners from the parent process */
+        setup_inherited_listeners(pconf);
+    }
+
+    if (listenmaxfd == -1) {
+       /* Help, no sockets were made, better log something and exit */
+       ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, NULL,
+                   "No sockets were created for listening");
+
+       signal_parent(0);       /* tell parent to die */
+
+       ap_destroy_pool(pchild);
+       cleanup_scoreboard();
+       exit(0);
+    }
+    set_signals();
+
+    /*
+     * - Initialize allowed_globals
+     * - Create the thread table
+     * - Spawn off threads
+     * - Create listen socket set (done above)
+     * - loop {
+     *       wait for request
+     *       create new job
+     *   } while (!time to exit)
+     * - Close all listeners
+     * - Wait for all threads to complete
+     * - Exit
+     */
+
+    ap_child_init_modules(pconf, server_conf);
+
+    allowed_globals.jobsemaphore = create_semaphore(0);
+    allowed_globals.jobmutex = ap_create_mutex(NULL);
+
+    /* spawn off the threads */
+    child_handles = (thread *) alloca(nthreads * sizeof(int));
+    for (i = 0; i < nthreads; i++) {
+       child_handles[i] = create_thread((void (*)(void *)) child_main, (void *) i);
+    }
+    if (nthreads > max_daemons_limit) {
+       max_daemons_limit = nthreads;
+    }
+
+    while (1) {
+        if (max_jobs_per_exe && (total_jobs > max_jobs_per_exe)) {
+            /* MaxRequestsPerChild hit...
+             */
+            break;
+       }
+        /* Always check for the exit event being signaled.
+         */
+        rv = WaitForSingleObject(exit_event, 0);
+        ap_assert((rv == WAIT_TIMEOUT) || (rv == WAIT_OBJECT_0));
+        if (rv == WAIT_OBJECT_0) {
+            APD1("child: exit event signalled, exiting");
+            break;
+        }
+
+       tv.tv_sec = wait_time;
+       tv.tv_usec = 0;
+
+       memcpy(&main_fds, &listenfds, sizeof(fd_set));
+       srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
+#ifdef WIN32
+       if (srv == SOCKET_ERROR) {
+           /* Map the Win32 error into a standard Unix error condition */
+           errno = WSAGetLastError();
+           srv = -1;
+       }
+#endif /* WIN32 */
+
+       if (srv < 0) {
+           /* Error occurred - if EINTR, loop around with problem */
+           if (errno != EINTR) {
+               /* A "real" error occurred, log it and increment the count of
+                * select errors. This count is used to ensure we don't go into
+                * a busy loop of continuous errors.
+                */
+               ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)");
+               count_select_errors++;
+               if (count_select_errors > MAX_SELECT_ERRORS) {
+                   ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server_conf,
+                       "Too many errors in select loop. Child process exiting.");
+                   break;
+               }
+           }
+           continue;
+       }
+       count_select_errors = 0;    /* reset count of errors */
+       if (srv == 0) {
+            continue;
+       }
+
+       {
+           listen_rec *lr;
+
+           lr = find_ready_listener(&main_fds);
+           if (lr != NULL) {
+               sd = lr->fd;
+           }
+       }
+       do {
+           clen = sizeof(sa_client);
+           csd = accept(sd, (struct sockaddr *) &sa_client, &clen);
+#ifdef WIN32
+           if (csd == INVALID_SOCKET) {
+               csd = -1;
+               errno = WSAGetLastError();
+           }
+#endif /* WIN32 */
+       } while (csd < 0 && errno == EINTR);
+
+       if (csd < 0) {
+#if defined(EPROTO) && defined(ECONNABORTED)
+           if ((errno != EPROTO) && (errno != ECONNABORTED))
+#elif defined(EPROTO)
+           if (errno != EPROTO)
+#elif defined(ECONNABORTED)
+           if (errno != ECONNABORTED)
+#endif
+               ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+                           "accept: (client socket)");
+       }
+       else {
+           add_job(csd);
+           total_jobs++;
+       }
+    }
+      
+    APD2("process PID %d exiting", my_pid);
+
+    /* Get ready to shutdown and exit */
+    allowed_globals.exit_now = 1;
+    ap_release_mutex(start_mutex);
+
+#ifdef UNGRACEFUL_RESTART
+    SetEvent(allowed_globals.thread_exit_event);
+#else
+    for (i = 0; i < nthreads; i++) {
+       add_job(-1);
+    }
+#endif
+
+    APD2("process PID %d waiting for worker threads to exit", my_pid);
+    /* Wait for all your children */
+    end_time = time(NULL) + 180;
+    while (nthreads) {
+        rv = wait_for_many_objects(nthreads, child_handles, 
+                                   end_time - time(NULL));
+       if (rv != WAIT_TIMEOUT) {
+           rv = rv - WAIT_OBJECT_0;
+           ap_assert((rv >= 0) && (rv < nthreads));
+           cleanup_thread(child_handles, &nthreads, rv);
+           continue;
+       }
+       break;
+    }
+
+    APD2("process PID %d killing remaining worker threads", my_pid);
+    for (i = 0; i < nthreads; i++) {
+       kill_thread(child_handles[i]);
+       free_thread(child_handles[i]);
+    }
+#ifdef UNGRACEFUL_RESTART
+    ap_assert(CloseHandle(allowed_globals.thread_exit_event));
+#endif
+    destroy_semaphore(allowed_globals.jobsemaphore);
+    ap_destroy_mutex(allowed_globals.jobmutex);
+
+    ap_child_exit_modules(pconf, server_conf);
+    ap_destroy_pool(pchild);
+
+    cleanup_scoreboard();
+
+    APD2("process PID %d exited", my_pid);
+    clean_parent_exit(0);
+}                              /* standalone_main */
+
+/*
+ * Spawn a child Apache process. The child process has the command line arguments from
+ * argc and argv[], plus a -Z argument giving the name of an event. The child should
+ * open and poll or wait on this event. When it is signalled, the child should die.
+ * prefix is a prefix string for the event name.
+ * 
+ * The child_num argument on entry contains a serial number for this child (used to create
+ * a unique event name). On exit, this number will have been incremented by one, ready
+ * for the next call. 
+ *
+ * On exit, the value pointed to be *ev will contain the event created
+ * to signal the new child process.
+ *
+ * The return value is the handle to the child process if successful, else -1. If -1 is
+ * returned the error will already have been logged by ap_log_error().
+ */
+
+/**********************************************************************
+ * master_main - this is the parent (main) process. We create a
+ * child process to do the work, then sit around waiting for either
+ * the child to exit, or a restart or exit signal. If the child dies,
+ * we just respawn a new one. If we have a shutdown or graceful restart,
+ * tell the child to die when it is ready. If it is a non-graceful
+ * restart, force the child to die immediately.
+ **********************************************************************/
+
+#define MAX_PROCESSES 50 /* must be < MAX_WAIT_OBJECTS-1 */
+
+static void cleanup_process(HANDLE *handles, HANDLE *events, int position, int *processes)
+{
+    int i;
+    int handle = 0;
+
+    CloseHandle(handles[position]);
+    CloseHandle(events[position]);
+
+    handle = (int)handles[position];
+
+    for (i = position; i < (*processes)-1; i++) {
+       handles[i] = handles[i + 1];
+       events[i] = events[i + 1];
+    }
+    (*processes)--;
+
+    APD4("cleanup_processes: removed child in slot %d handle %d, max=%d", position, handle, *processes);
+}
+
+static int create_process(pool *p, HANDLE *handles, HANDLE *events, 
+                          int *processes, int *child_num, char *kill_event_name, int argc, char **argv)
+{
+
+    int rv, i;
+    HANDLE kill_event;
+    char buf[1024];
+    char exit_event_name[40]; /* apPID_C# */
+    char *pCommand;
+
+    STARTUPINFO si;           /* Filled in prior to call to CreateProcess */
+    PROCESS_INFORMATION pi;   /* filled in on call to CreateProces */
+    LPWSAPROTOCOL_INFO  lpWSAProtocolInfo;
+    listen_rec *lr;
+    DWORD BytesWritten;
+    HANDLE hPipeRead = NULL;
+    HANDLE hPipeWrite = NULL;
+    SECURITY_ATTRIBUTES sa = {0};  
+
+    sa.nLength = sizeof(sa);
+    sa.bInheritHandle = TRUE;
+    sa.lpSecurityDescriptor = NULL;
+
+    /* Build the command line. Should look something like this:
+     * C:/apache/bin/apache.exe -Z exit_event -f ap_server_confname 
+     * First, get the path to the executable...
+     */
+    rv = GetModuleFileName(NULL, buf, sizeof(buf));
+    if (rv == sizeof(buf)) {
+        ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+                     "Parent: Path to Apache process too long");
+        return -1;
+    } else if (rv == 0) {
+        ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+                     "Parent: GetModuleFileName() returned NULL for current process.");
+        return -1;
+    }
+    
+    /* Create the exit event (apPID_C#). Parent signals this event to tell the
+     * child to exit 
+     */
+    ap_snprintf(exit_event_name, sizeof(exit_event_name), "%s_C%d", kill_event_name, ++(*child_num));
+    kill_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name);
+    if (!kill_event) {
+        ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+                     "Parent: Could not create exit event for child process");
+        return -1;
+    }
+    
+    pCommand = ap_psprintf(p, "\"%s\" -Z %s -f \"%s\"", buf, exit_event_name, ap_server_confname);  
+
+    for (i = 1; i < argc; i++) {
+        pCommand = ap_pstrcat(p, pCommand, " ", argv[i], NULL);
+    }
+
+    /* Create a pipe to send socket info to the child */
+    if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) {
+        ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+                     "Parent: Unable to create pipe to child process.\n");
+        return -1;
+    }
+
+    /* Give the read in of teh pipe (hPipeRead) to the child as stdin. The 
+     * parent will write the socket data to the child on this pipe.
+     */
+    memset(&si, 0, sizeof(si));
+    memset(&pi, 0, sizeof(pi));
+    si.cb = sizeof(si);
+    si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+    si.wShowWindow = SW_HIDE;
+    si.hStdInput   = hPipeRead;
+
+    if (!CreateProcess(NULL, pCommand, NULL, NULL, 
+                       TRUE,      /* Inherit handles */
+                       0,         /* Creation flags */
+                       NULL, NULL,
+                       &si, &pi)) {
+        ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+                     "Parent: Not able to create the child process.");
+        /*
+         * We must close the handles to the new process and its main thread
+         * to prevent handle and memory leaks.
+         */ 
+        CloseHandle(pi.hProcess);
+        CloseHandle(pi.hThread);
+
+        return -1;
+    }
+    else {
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf,
+                     "Parent: Created child process %d", pi.dwProcessId);
+
+        /* Assume the child process lives. Update the process and event tables */
+        handles[*processes] = pi.hProcess;
+        events[*processes] = kill_event;
+        (*processes)++;
+
+        /* We never store the thread's handle, so close it now. */
+        CloseHandle(pi.hThread);
+
+        /* Run the chain of open sockets. For each socket, duplicate it 
+         * for the target process then send the WSAPROTOCOL_INFO 
+         * (returned by dup socket) to the child */
+        lr = ap_listeners;
+        while (lr != NULL) {
+            lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
+            ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf,
+                         "Parent: Duplicating socket %d and sending it to child process %d", lr->fd, pi.dwProcessId);
+            if (WSADuplicateSocket(lr->fd, 
+                                   pi.dwProcessId,
+                                   lpWSAProtocolInfo) == SOCKET_ERROR) {
+                ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+                             "Parent: WSADuplicateSocket failed for socket %d.", lr->fd );
+                return -1;
+            }
+
+            if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO),
+                           &BytesWritten,
+                           (LPOVERLAPPED) NULL)) {
+                ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+                             "Parent: Unable to write duplicated socket %d to the child.", lr->fd );
+                return -1;
+            }
+
+            lr = lr->next;
+            if (lr == ap_listeners)
+                break;
+        }
+    }
+    CloseHandle(hPipeRead);
+    CloseHandle(hPipeWrite);        
+
+    return 0;
+}
+
+/* To share the semaphores with other processes, we need a NULL ACL
+ * Code from MS KB Q106387
+ */
+
+static PSECURITY_ATTRIBUTES GetNullACL()
+{
+    PSECURITY_DESCRIPTOR pSD;
+    PSECURITY_ATTRIBUTES sa;
+
+    sa  = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
+    pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
+                                           SECURITY_DESCRIPTOR_MIN_LENGTH);
+    if (pSD == NULL || sa == NULL) {
+        return NULL;
+    }
+    /*
+     * We can safely use GetLastError() here without presetting it;
+     * {Initialize,Set}SecurityDescriptor() have been verified as clearing it
+     * on successful completion.
+     */
+    if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)
+       || GetLastError()) {
+        LocalFree( pSD );
+        LocalFree( sa );
+        return NULL;
+    }
+    if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)
+       || GetLastError()) {
+        LocalFree( pSD );
+        LocalFree( sa );
+        return NULL;
+    }
+    sa->nLength = sizeof(sa);
+    sa->lpSecurityDescriptor = pSD;
+    sa->bInheritHandle = TRUE;
+    return sa;
+}
+
+
+static void CleanNullACL( void *sa ) {
+    if( sa ) {
+        LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor);
+        LocalFree( sa );
+    }
+}
+
+int master_main(int argc, char **argv)
+{
+    /* returns NULL if invalid (Win95?) */
+    PSECURITY_ATTRIBUTES sa = GetNullACL();
+    int nchild = ap_daemons_to_start;
+    int child_num = 0;
+    int rv, cld;
+    char signal_prefix_string[100];
+    int i;
+    time_t tmstart;
+    HANDLE signal_shutdown_event;      /* used to signal shutdown to parent */
+    HANDLE signal_restart_event;       /* used to signal a restart to parent */
+    HANDLE process_handles[MAX_PROCESSES];
+    HANDLE process_kill_events[MAX_PROCESSES];
+    int current_live_processes = 0; /* number of child process we know about */
+    int processes_to_create = 0;    /* number of child processes to create */
+    pool *pparent = NULL;  /* pool for the parent process. Cleaned on each restart */
+
+    nchild = 1;            /* only allowed one child process for current generation */
+    processes_to_create = nchild;
+
+    is_graceful = 0;
+
+    ap_snprintf(signal_prefix_string, sizeof(signal_prefix_string),
+               "ap%d", getpid());
+    setup_signal_names(signal_prefix_string);
+
+    /* Create shutdown event, apPID_shutdown, where PID is the parent 
+     * Apache process ID. Shutdown is signaled by 'apache -k shutdown'.
+     */
+    signal_shutdown_event = CreateEvent(sa, TRUE, FALSE, signal_shutdown_name);
+    if (!signal_shutdown_event) {
+       ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+                   "master_main: Cannot create shutdown event %s", signal_shutdown_name);
+        CleanNullACL((void *)sa);
+       exit(1);
+    }
+
+    /* Create restart event, apPID_restart, where PID is the parent 
+     * Apache process ID. Restart is signaled by 'apache -k restart'.
+     */
+    signal_restart_event = CreateEvent(sa, TRUE, FALSE, signal_restart_name);
+    if (!signal_restart_event) {
+       CloseHandle(signal_shutdown_event);
+       ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+                   "master_main: Cannot create restart event %s", signal_restart_name);
+        CleanNullACL((void *)sa);
+       exit(1);
+    }
+    CleanNullACL((void *)sa);
+
+    /* Create the start mutex, apPID, where PID is the parent Apache process ID.
+     * Ths start mutex is used during a restart to prevent more than one 
+     * child process from entering the accept loop at once.
+     */
+    start_mutex = ap_create_mutex(signal_prefix_string);
+    restart_pending = shutdown_pending = 0;
+
+    do { /* restart-pending */
+       if (!is_graceful) {
+           ap_restart_time = time(NULL);
+       }
+        copy_listeners(pconf);
+       ap_clear_pool(pconf);
+       pparent = ap_make_sub_pool(pconf);
+
+       server_conf = ap_read_config(pconf, pparent, ap_server_confname);
+        setup_listeners(pconf);
+       ap_clear_pool(plog);
+       ap_open_logs(server_conf, plog);
+       ap_set_version();
+       ap_init_modules(pconf, server_conf);
+       version_locked++;
+        service_set_status(SERVICE_START_PENDING);
+        /* Create child processes */
+        while (processes_to_create--) {
+            if (create_process(pconf, process_handles, process_kill_events, 
+                               &current_live_processes, &child_num, signal_prefix_string, argc, argv) < 0) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+                             "master_main: create child process failed. Exiting.");
+                goto die_now;
+            }
+        }
+        service_set_status(SERVICE_RUNNING);
+       restart_pending = shutdown_pending = 0;
+
+        /* Wait for either the shutdown or restart events to be signaled */
+        process_handles[current_live_processes] = signal_shutdown_event;
+        process_handles[current_live_processes+1] = signal_restart_event;
+        rv = WaitForMultipleObjects(current_live_processes+2, (HANDLE *)process_handles, 
+                                    FALSE, INFINITE);
+        if (rv == WAIT_FAILED) {
+            /* Something serious is wrong */
+            ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, server_conf,
+                         "master_main: : WaitForMultipeObjects on process handles and apache-signal -- doing shutdown");
+            shutdown_pending = 1;
+            break;
+        }
+        if (rv == WAIT_TIMEOUT) {
+            /* Hey, this cannot happen */
+            ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+                         "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT");
+            shutdown_pending = 1;
+        }
+
+        cld = rv - WAIT_OBJECT_0;
+        APD4("main process: wait finished, cld=%d handle %d (max=%d)", cld, process_handles[cld], current_live_processes);
+        if (cld == current_live_processes) {
+            /* apPID_shutdown event signalled, we should exit now */
+            shutdown_pending = 1;
+            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, 
+                         "master_main: Shutdown event signaled. Shutting the server down.");
+            if (ResetEvent(signal_shutdown_event) == 0) {
+                ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+                             "ResetEvent(signal_shutdown_event)");
+            }
+           /* Signal each child processes to die */
+           for (i = 0; i < current_live_processes; i++) {
+               APD3("master_main: signalling child %d, handle %d to die", i, process_handles[i]);
+               if (SetEvent(process_kill_events[i]) == 0)
+                   ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+                                 "master_main: SetEvent for child process in slot #%d failed", i);
+           }
+            break;
+        } else if (cld == current_live_processes+1) {
+            /* apPID_restart event signalled, restart the child process */
+            int children_to_kill = current_live_processes;
+            restart_pending = 1;
+            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, 
+                         "master_main: Restart event signaled. Doing a graceful restart.");
+            if (ResetEvent(signal_restart_event) == 0) {
+                ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+                             "master_main: ResetEvent(signal_restart_event) failed.");
+            }
+            /* Signal each child process to die */
+           for (i = 0; i < children_to_kill; i++) {
+               APD3("master_main: signalling child #%d handle %d to die", i, process_handles[i]);
+               if (SetEvent(process_kill_events[i]) == 0)
+                   ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+                                 "master_main: SetEvent for child process in slot #%d failed", i);
+                /* Remove the process (and event) from the process table */
+                cleanup_process(process_handles, process_kill_events, i, &current_live_processes);
+           }
+           processes_to_create = nchild;
+            ++ap_my_generation;
+            continue;
+        } else {
+            /* A child process must have exited because of MaxRequestPerChild being hit
+             * or a fatal error condition (seg fault, etc.). Remove the dead process 
+             * from the process_handles and process_kill_events table and create a new
+             * child process.
+             */
+            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, 
+                         "master_main: Child processed exited (due to MaxRequestsPerChild?). Restarting the child process.");
+           ap_assert(cld < current_live_processes);
+           cleanup_process(process_handles, process_kill_events, cld, &current_live_processes);
+           APD2("main_process: child in slot %d died", rv);
+            processes_to_create = 1;
+            continue;
+       }
+
+    } while (1);
+
+    /* If we dropped out of the loop we definitly want to die completely. We need to
+     * make sure we wait for all the child process to exit first.
+     */
+
+    APD2("*** main process shutdown, processes=%d ***", current_live_processes);
+
+die_now:
+
+    tmstart = time(NULL);
+    while (current_live_processes && ((tmstart+60) > time(NULL))) {
+       service_set_status(SERVICE_STOP_PENDING);
+       rv = WaitForMultipleObjects(current_live_processes, (HANDLE *)process_handles, FALSE, 2000);
+       if (rv == WAIT_TIMEOUT)
+           continue;
+       ap_assert(rv != WAIT_FAILED);
+       cld = rv - WAIT_OBJECT_0;
+       ap_assert(rv < current_live_processes);
+       APD4("main_process: child in #%d handle %d died, left=%d", 
+           rv, process_handles[rv], current_live_processes);
+       cleanup_process(process_handles, process_kill_events, cld, &current_live_processes);
+    }
+    for (i = 0; i < current_live_processes; i++) {
+       ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO, server_conf,
+           "forcing termination of child #%d (handle %d)", i, process_handles[i]);
+       TerminateProcess((HANDLE) process_handles[i], 1);
+    }
+
+    CloseHandle(signal_restart_event);
+    CloseHandle(signal_shutdown_event);
+
+    /* cleanup pid file on normal shutdown */
+    {
+       const char *pidfile = NULL;
+       pidfile = ap_server_root_relative (pparent, ap_pid_fname);
+       if ( pidfile != NULL && unlink(pidfile) == 0)
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,
+                        server_conf,
+                        "removed PID file %s (pid=%ld)",
+                        pidfile, (long)getpid());
+    }
+
+    if (pparent) {
+       ap_destroy_pool(pparent);
+    }
+
+    ap_destroy_mutex(start_mutex);
+
+    service_set_status(SERVICE_STOPPED);
+    return (0);
+}
+
+/*
+ * Send signal to a running Apache. On entry signal should contain
+ * either "shutdown" or "restart"
+ */
+
+int send_signal(pool *p, char *signal)
+{
+    char prefix[20];
+    FILE *fp;
+    int nread;
+    char *fname;
+    int end;
+
+    fname = ap_server_root_relative (p, ap_pid_fname);
+
+    fp = fopen(fname, "r");
+    if (!fp) {
+       printf("Cannot read apache PID file %s\n", fname);
+        return FALSE;
+    }
+    prefix[0] = 'a';
+    prefix[1] = 'p';
+
+    nread = fread(prefix+2, 1, sizeof(prefix)-3, fp);
+    if (nread == 0) {
+       fclose(fp);
+       printf("PID file %s was empty\n", fname);
+        return FALSE;
+    }
+    fclose(fp);
+
+    /* Terminate the prefix string */
+    end = 2 + nread - 1;
+    while (end > 0 && (prefix[end] == '\r' || prefix[end] == '\n'))
+       end--;
+    prefix[end + 1] = '\0';
+
+    setup_signal_names(prefix);
+
+    if (!strcasecmp(signal, "shutdown"))
+       ap_start_shutdown();
+    else if (!strcasecmp(signal, "restart"))
+       ap_start_restart(1);
+    else {
+       printf("Unknown signal name \"%s\". Use either shutdown or restart.\n",
+           signal);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+void post_parse_init()
+{
+    ap_set_version();
+    ap_init_modules(pconf, server_conf);
+    ap_suexec_enabled = init_suexec();
+    version_locked++;
+    ap_open_logs(server_conf, plog);
+    set_group_privs();
+}
+
+int service_init()
+{
+    common_init();
+    ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root));
+    if (ap_registry_get_service_conf(pconf, ap_server_confname, sizeof(ap_server_confname),
+                                     ap_server_argv0))
+        return FALSE;
+
+    ap_setup_prelinked_modules();
+    server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+    ap_log_pid(pconf, ap_pid_fname);
+    post_parse_init();
+    return TRUE;
+}
+
+#ifdef WIN32
+__declspec(dllexport)
+     int apache_main(int argc, char *argv[])
+#else
+int REALMAIN(int argc, char *argv[]) 
+#endif
+{
+    int c;
+    int child = 0;
+    char *cp;
+    char *s;
+    char *service_name = NULL;
+    int install = 0;
+    int conf_specified = 0;
+    char *signal_to_send = NULL;
+    char cwd[MAX_STRING_LEN];
+
+    /* Service application
+     * Configuration file in registry at:
+     * HKLM\System\CurrentControlSet\Services\[Svc name]\Parameters\ConfPath
+     */
+    if (isProcessService()) {
+        service_main(master_main, argc, argv);
+        clean_parent_exit(0);
+    }
+
+    /* Console application or a child process. */
+
+    if ((s = strrchr(argv[0], PATHSEPARATOR)) != NULL) {
+        ap_server_argv0 = ++s;
+    }
+    else {
+        ap_server_argv0 = argv[0];
+    }
+
+    common_init();
+    ap_setup_prelinked_modules();
+
+    if(!GetCurrentDirectory(sizeof(cwd),cwd)) {
+       ap_log_error(APLOG_MARK,APLOG_WIN32ERROR, NULL,
+       "GetCurrentDirectory() failure");
+       return -1;
+    }
+
+    ap_cpystrn(cwd, ap_os_canonical_filename(pcommands, cwd), sizeof(cwd));
+    ap_cpystrn(ap_server_root, cwd, sizeof(ap_server_root));
+
+    while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVlLZ:iusStThk:n:")) != -1) {
+        char **new;
+       switch (c) {
+       case 'c':
+           new = (char **)ap_push_array(ap_server_post_read_config);
+           *new = ap_pstrdup(pcommands, optarg);
+           break;
+       case 'C':
+           new = (char **)ap_push_array(ap_server_pre_read_config);
+           *new = ap_pstrdup(pcommands, optarg);
+           break;
+       case 'D':
+           new = (char **)ap_push_array(ap_server_config_defines);
+           *new = ap_pstrdup(pcommands, optarg);
+           break;
+#ifdef WIN32
+       case 'Z':
+           exit_event = open_event(optarg);
+           APD2("child: opened process event %s", optarg);
+           cp = strchr(optarg, '_');
+           ap_assert(cp);
+           *cp = 0;
+           setup_signal_names(optarg);
+           start_mutex = ap_open_mutex(signal_name_prefix);
+           ap_assert(start_mutex);
+           child = 1;
+           break;
+        case 'n':
+            service_name = ap_pstrdup(pcommands, optarg);
+            if (isValidService(optarg)) {
+                ap_registry_get_service_conf(pconf, ap_server_confname, sizeof(ap_server_confname),
+                                             optarg);
+                conf_specified = 1;
+            }
+            break;
+       case 'i':
+           install = 1;
+           break;
+       case 'u':
+           install = -1;
+           break;
+       case 'S':
+           ap_dump_settings = 1;
+           break;
+       case 'k':
+           signal_to_send = optarg;
+           break;
+#endif /* WIN32 */
+       case 'd':
+            optarg = ap_os_canonical_filename(pcommands, optarg);
+            if (!ap_os_is_path_absolute(optarg)) {
+               optarg = ap_pstrcat(pcommands, cwd, optarg, NULL);
+                ap_getparents(optarg);
+            }
+            if (optarg[strlen(optarg)-1] != '/')
+                optarg = ap_pstrcat(pcommands, optarg, "/", NULL);
+            ap_cpystrn(ap_server_root,
+                       optarg,
+                       sizeof(ap_server_root));
+           break;
+       case 'f':
+            ap_cpystrn(ap_server_confname,
+                       ap_os_canonical_filename(pcommands, optarg),
+                       sizeof(ap_server_confname));
+            conf_specified = 1;
+           break;
+       case 'v':
+           ap_set_version();
+           printf("Server version: %s\n", ap_get_server_version());
+           printf("Server built:   %s\n", ap_get_server_built());
+           exit(0);
+       case 'V':
+           ap_set_version();
+           show_compile_settings();
+           exit(0);
+       case 'l':
+           ap_show_modules();
+           exit(0);
+       case 'L':
+           ap_show_directives();
+           exit(0);
+       case 'X':
+           ++one_process;      /* Weird debugging mode. */
+           break;
+       case 't':
+           ap_configtestonly = 1;
+           ap_docrootcheck = 1;
+           break;
+       case 'T':
+           ap_configtestonly = 1;
+           ap_docrootcheck = 0;
+           break;
+       case 'h':
+           usage(ap_server_argv0);
+       case '?':
+           usage(ap_server_argv0);
+        }   /* switch */
+    }       /* while  */
+
+    /* ServerConfFile is found in this order:
+     * (1) -f or -n
+     * (2) [-d]/SERVER_CONFIG_FILE
+     * (3) ./SERVER_CONFIG_FILE
+     * (4) [Registry: HKLM\Software\[product]\ServerRoot]/SERVER_CONFIG_FILE
+     * (5) /HTTPD_ROOT/SERVER_CONFIG_FILE
+     */
+
+    if (!conf_specified) {
+        ap_cpystrn(ap_server_confname, SERVER_CONFIG_FILE, sizeof(ap_server_confname));
+        if (access(ap_server_root_relative(pcommands, ap_server_confname), 0)) {
+            ap_registry_get_server_root(pconf, ap_server_root, sizeof(ap_server_root));
+            if (!*ap_server_root)
+                ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root));
+            ap_cpystrn(ap_server_root, ap_os_canonical_filename(pcommands, ap_server_root),
+                       sizeof(ap_server_root));
+        }
+    }
+
+    if (!ap_os_is_path_absolute(ap_server_confname)) {
+        char *full_conf_path;
+
+        full_conf_path = ap_pstrcat(pcommands, ap_server_root, "/", ap_server_confname, NULL);
+        full_conf_path = ap_os_canonical_filename(pcommands, full_conf_path);
+        ap_cpystrn(ap_server_confname, full_conf_path, sizeof(ap_server_confname));
+    }
+    ap_getparents(ap_server_confname);
+    ap_no2slash(ap_server_confname);
+
+#ifdef WIN32
+    if (install) {
+        if (!service_name)
+            service_name = ap_pstrdup(pconf, DEFAULTSERVICENAME);
+        if (install > 0) 
+            InstallService(service_name, ap_server_root_relative(pcommands, ap_server_confname));
+        else
+            RemoveService(service_name);
+        clean_parent_exit(0);
+    }
+    
+    if (service_name && signal_to_send) {
+        send_signal_to_service(service_name, signal_to_send);
+        clean_parent_exit(0);
+    }
+
+    if (service_name && !conf_specified) {
+        printf("Unknown service: %s\n", service_name);
+        clean_parent_exit(0);
+    }
+#endif
+    server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+
+    if (ap_configtestonly) {
+        fprintf(stderr, "%s: Syntax OK\n", ap_server_root_relative(pcommands, ap_server_confname));
+        clean_parent_exit(0);
+    }
+
+    if (ap_dump_settings) {
+        clean_parent_exit(0);
+    }
+
+    /* Treat -k start confpath as just -f confpath */
+    if (signal_to_send && strcasecmp(signal_to_send, "start")) {
+        send_signal(pconf, signal_to_send);
+        clean_parent_exit(0);
+    }
+
+    if (!child && !ap_dump_settings) { 
+        ap_log_pid(pconf, ap_pid_fname);
+    }
+
+    post_parse_init();
+
+#ifdef OS2
+    printf("%s running...\n", ap_get_server_version());
+#endif
+#ifdef WIN32
+    if (!child) {
+       printf("%s running...\n", ap_get_server_version());
+    }
+#endif
+    if (one_process && !exit_event)
+       exit_event = create_event(0, 0, NULL);
+    if (one_process && !start_mutex)
+       start_mutex = ap_create_mutex(NULL);
+    /*
+     * In the future, the main will spawn off a couple
+     * of children and monitor them. As soon as a child
+     * exits, it spawns off a new one
+     */
+    if (child || one_process) {
+       if (!exit_event || !start_mutex)
+           exit(-1);
+       worker_main();
+       ap_destroy_mutex(start_mutex);
+       destroy_event(exit_event);
+    }
+    else 
+        master_main(argc, argv);
+
+    clean_parent_exit(0);
+    return 0;  /* purely to avoid a warning */
+}
+
+#endif /* ndef MULTITHREAD */
+
+#else  /* ndef SHARED_CORE_TIESTATIC */
+
+/*
+**  Standalone Tie Program for Shared Core support
+**
+**  It's purpose is to tie the static libraries and 
+**  the shared core library under link-time and  
+**  passing execution control to the real main function
+**  in the shared core library under run-time.
+*/
+
+extern int ap_main(int argc, char *argv[]);
+
+int main(int argc, char *argv[]) 
+{
+    return ap_main(argc, argv);
+}
+
+#endif /* ndef SHARED_CORE_TIESTATIC */
+#else  /* ndef SHARED_CORE_BOOTSTRAP */
+
+#ifdef OS2
+/* Shared core loader for OS/2 */
+
+int ap_main(int argc, char *argv[]); /* Load time linked from libhttpd.dll */
+
+int main(int argc, char *argv[])
+{
+    return ap_main(argc, argv);
+}
+
+#else
+
+/*
+**  Standalone Bootstrap Program for Shared Core support
+**
+**  It's purpose is to initialise the LD_LIBRARY_PATH
+**  environment variable therewith the Unix loader is able
+**  to start the Standalone Tie Program (see above)
+**  and then replacing itself with this program by
+**  immediately passing execution to it.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ap_config.h"
+#include "httpd.h"
+
+#if defined(HPUX) || defined(HPUX10) || defined(HPUX11)
+#define VARNAME "SHLIB_PATH"
+#else
+#define VARNAME "LD_LIBRARY_PATH"
+#endif
+
+#ifndef SHARED_CORE_DIR 
+#define SHARED_CORE_DIR HTTPD_ROOT "/libexec"
+#endif
+
+#ifndef SHARED_CORE_EXECUTABLE_PROGRAM
+#define SHARED_CORE_EXECUTABLE_PROGRAM "lib" TARGET ".ep"
+#endif
+
+extern char *optarg;
+extern int   optind;
+
+int main(int argc, char *argv[], char *envp[]) 
+{
+    char prog[MAX_STRING_LEN];
+    char llp_buf[MAX_STRING_LEN];
+    char **llp_slot;
+    char *llp_existing;
+    char *llp_dir;
+    char **envpnew;
+    int c, i, l;
+
+    /* 
+     * parse argument line, 
+     * but only handle the -L option 
+     */
+    llp_dir = SHARED_CORE_DIR;
+    while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVlLR:SZ:tTh")) != -1) {
+       switch (c) {
+       case 'D':
+       case 'C':
+       case 'c':
+       case 'X':
+       case 'd':
+       case 'f':
+       case 'v':
+       case 'V':
+       case 'l':
+       case 'L':
+       case 'S':
+       case 'Z':
+       case 't':
+       case 'T':
+       case 'h':
+       case '?':
+           break;
+       case 'R':
+           llp_dir = strdup(optarg);
+           break;
+       }
+    }
+
+    /* 
+     * create path to SHARED_CORE_EXECUTABLE_PROGRAM
+     */
+    ap_snprintf(prog, sizeof(prog), "%s/%s", llp_dir, SHARED_CORE_EXECUTABLE_PROGRAM);
+
+    /* 
+     * adjust process environment therewith the Unix loader 
+     * is able to start the SHARED_CORE_EXECUTABLE_PROGRAM.
+     */
+    llp_slot = NULL;
+    llp_existing = NULL;
+    l = strlen(VARNAME);
+    for (i = 0; envp[i] != NULL; i++) {
+       if (strncmp(envp[i], VARNAME "=", l+1) == 0) {
+           llp_slot = &envp[i];
+           llp_existing = strchr(envp[i], '=') + 1;
+       }
+    }
+    if (llp_slot == NULL) {
+       envpnew = (char **)malloc(sizeof(char *)*(i + 2));
+       if (envpnew == NULL) {
+           fprintf(stderr, "Ouch!  Out of memory generating envpnew!\n");
+       }
+       memcpy(envpnew, envp, sizeof(char *)*i);
+       envp = envpnew;
+       llp_slot = &envp[i++];
+       envp[i] = NULL;
+    }
+    if (llp_existing != NULL)
+        ap_snprintf(llp_buf, sizeof(llp_buf), "%s=%s:%s", VARNAME, llp_dir, llp_existing);
+    else
+        ap_snprintf(llp_buf, sizeof(llp_buf), "%s=%s", VARNAME, llp_dir);
+    *llp_slot = strdup(llp_buf);
+
+    /* 
+     * finally replace our process with 
+     * the SHARED_CORE_EXECUTABLE_PROGRAM
+     */
+    if (execve(prog, argv, envp) == -1) {
+       fprintf(stderr, 
+               "%s: Unable to exec Shared Core Executable Program `%s'\n",
+               argv[0], prog);
+       return 1;
+    }
+    else
+       return 0;
+}
+
+#endif /* def OS2 */
+#endif /* ndef SHARED_CORE_BOOTSTRAP */
+
+#ifndef SHARED_CORE_BOOTSTRAP
+/*
+ * Force ap_validate_password() into the image so that modules like
+ * mod_auth can use it even if they're dynamically loaded.
+ */
+void suck_in_ap_validate_password(void);
+void suck_in_ap_validate_password(void)
+{
+    ap_validate_password("a", "b");
+}
+#endif
+
+/* force Expat to be linked into the server executable */
+#if defined(USE_EXPAT) && !defined(SHARED_CORE_BOOTSTRAP)
+#include "xmlparse.h"
+const XML_LChar *suck_in_expat(void);
+const XML_LChar *suck_in_expat(void)
+{
+    return XML_ErrorString(XML_ERROR_NONE);
+}
+#endif /* USE_EXPAT */
+
diff --git a/server/mpm/winnt/registry.c b/server/mpm/winnt/registry.c
new file mode 100644 (file)
index 0000000..23eeb5e
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Functions to handle interacting with the Win32 registry
+ */
+
+/*
+ * Apache registry key structure
+ *
+ * Apache's registry information is stored in the HKEY_LOCAL_MACHINE
+ * key, under
+ *
+ *  HKLM\SOFTWARE\Apache Group\Apache\version
+ *
+ * These keys are defined in this file. The definition of the "version" part
+ * will need updating each time Apache moves from beta to non-beta or from a
+ * release to a development or beta version.
+ */
+
+/* To allow for multiple services, store the configuration file's full path
+ * under each service entry:
+ *
+ * HKLM\System\CurrentControlSet\Services\[service name]\Parameters\ConfPath
+ *
+ * The default configuration path (for console apache) is still stored:
+ * 
+ * HKLM\Software\[Vendor]\[Software]\[Version]\ServerRoot
+ */
+
+#include <windows.h>
+#include <stdio.h>
+
+#include "httpd.h"
+#include "http_log.h"
+
+/* Define where the Apache values are stored in the registry. In general
+ * VERSION will be the same across all beta releases for a particular
+ * major release, but will change when the final release is made.
+ */
+
+#define VENDOR   "Apache Group"
+#define SOFTWARE "Apache"
+#define VERSION  "1.3.9"
+
+#define REGKEY "SOFTWARE\\" VENDOR "\\" SOFTWARE "\\" VERSION
+
+#define SERVICEKEYPRE  "System\\CurrentControlSet\\Services\\"
+#define SERVICEKEYPOST "\\Parameters"
+
+/*
+ * The Windows API registry key functions don't set the last error
+ * value (the windows equivalent of errno). So we need to set it
+ * with SetLastError() before calling the aplog_error() function.
+ * Because this is common, let's have a macro.
+ */
+#define do_error(rv,fmt,arg) do { \
+       SetLastError(rv); \
+       ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_ERR, NULL, fmt,arg); \
+    } while (0);
+
+/*
+ * Get the data for registry key value. This is a generic function that
+ * can either get a value into a caller-supplied buffer, or it can
+ * allocate space for the value from the pass-in pool. It will normally
+ * be used by other functions within this file to get specific key values
+ * (e.g. registry_get_server_root()). This function returns a number of
+ * different error statuses, allowing the caller to differentiate
+ * between a key or value not existing and other kinds of errors. Depending
+ * on the type of data being obtained the caller can then either ignore
+ * the key-not-existing error, or treat it as a real error.
+ *
+ * If ppValue is NULL, allocate space for the value and return it in
+ * *pValue. The return value is the number of bytes in the value.
+ * The first argument is the pool to use to allocate space for the value.
+ *
+ * If pValue is not NULL, assume it is a buffer of nSizeValue bytes,
+ * and write the value into the buffer. The return value is the number
+ * of bytes in the value (so if the return value is greater than
+ * the supplied nSizeValue, the caller knows that *pValue is truncated).
+ * The pool argument is ignored.
+ *
+ * The return value is the number of bytes in the successfully retreived
+ * key if everything worked, or:
+ *
+ *  -1 the key does not exists
+ *  -2 if out of memory during the function
+ *  -3 if the buffer specified by *pValue/nSizeValue was not large enough 
+ *     for the value.
+ *  -4 if an error occurred
+ *
+ * If the return value is negative a message will be logged to the error
+ * log (aplog_error) function. If the return value is -2, -3 or -4 the message
+ * will be logged at priority "error", while if the return value is -1 the
+ * message will be logged at priority "warning".
+ */
+
+static int ap_registry_get_key_int(pool *p, char *key, char *name, char *pBuffer, int nSizeBuffer, char **ppValue)
+{
+    long rv;
+    HKEY hKey;
+    char *pValue;
+    int nSize;
+    int retval;
+
+    rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                      key,
+                     0,
+                     KEY_READ,
+                     &hKey);
+
+    if (rv == ERROR_FILE_NOT_FOUND) {
+       ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,
+        "Registry does not contain key %s",key);
+       return -1;
+    }
+    if (rv != ERROR_SUCCESS) {
+        do_error(rv, "RegOpenKeyEx HKLM\\%s",key);
+       return -4;
+    }
+
+    if (pBuffer == NULL) {
+       /* Find the size required for the data by passing NULL as the buffer
+        * pointer. On return nSize will contain the size required for the
+        * buffer if the return value is ERROR_SUCCESS.
+        */
+       rv = RegQueryValueEx(hKey, 
+                            name,              /* key name */
+                            NULL,              /* reserved */
+                            NULL,              /* type */
+                            NULL,              /* for value */
+                            &nSize);           /* for size of "value" */
+
+       if (rv != ERROR_SUCCESS) {
+           do_error(rv, "RegQueryValueEx(key %s)", key);
+           return -1;
+       }
+
+       pValue = ap_palloc(p, nSize);
+       *ppValue = pValue;
+       if (!pValue) {
+           /* Eek, out of memory, probably not worth trying to carry on,
+            * but let's give it a go
+            */
+           ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,NULL,
+               "Error getting registry key: out of memory");
+           return -2;
+       }
+    }
+    else {
+       /* Get the value into the existing buffer of length nSizeBuffer */
+       pValue = pBuffer;
+       nSize = nSizeBuffer;
+    }
+
+    rv = RegQueryValueEx(hKey, 
+                        name,          /* key name */
+                        NULL,          /* reserved */
+                        NULL,          /* type */
+                        pValue,                /* for value */
+                        &nSize);               /* for size of "value" */
+
+    retval = 0;            /* Return value */
+
+    if (rv == ERROR_FILE_NOT_FOUND) {
+       ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,
+        "Registry does not contain value %s\\%s", key, name);
+       retval = -1;
+    }
+    else if (rv == ERROR_MORE_DATA) {
+       /* This should only happen if we got passed a pre-existing buffer
+        * (pBuffer, nSizeBuffer). But I suppose it could also happen if we
+        * allocate a buffer if another process changed the length of the
+        * value since we found out its length above. Umm.
+        */
+       ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,NULL,
+           "Error getting registry value %s: buffer not big enough", key);
+       retval = -3;
+    }
+    else if (rv != ERROR_SUCCESS) {
+       do_error(rv, "RegQueryValueEx(key %s)", key);
+       retval = -4;
+    }
+
+    rv = RegCloseKey(hKey);
+    if (rv != ERROR_SUCCESS) {
+    do_error(rv, "RegCloseKey HKLM\\%s", key);
+       if (retval == 0) {
+           /* Keep error status from RegQueryValueEx, if any */
+           retval = -4;  
+       }
+    }
+
+    return retval < 0 ? retval : nSize;
+}
+
+/*
+ * Get the server root from the registry into 'dir' which is
+ * size bytes long. Returns 0 if the server root was found
+ * or if the serverroot key does not exist (in which case
+ * dir will contain an empty string), or -1 if there was
+ * an error getting the key.
+ */
+
+int ap_registry_get_server_root(pool *p, char *dir, int size)
+{
+    int rv;
+
+    rv = ap_registry_get_key_int(p, REGKEY, "ServerRoot", dir, size, NULL);
+    if (rv < 0) {
+       dir[0] = '\0';
+    }
+
+    return (rv < -1) ? -1 : 0;
+}
+
+char *ap_get_service_key(char *service_name)
+{
+    char *key = malloc(strlen(SERVICEKEYPRE) +
+                       strlen(service_name) +
+                       strlen(SERVICEKEYPOST) + 1);
+
+    sprintf(key,"%s%s%s", SERVICEKEYPRE, service_name, SERVICEKEYPOST);
+
+    return(key);
+}
+
+int ap_registry_get_service_conf(pool *p, char *dir, int size, char *service_name)
+{
+    int rv;
+    char *key = ap_get_service_key(service_name);
+
+    rv = ap_registry_get_key_int(p, key, "ConfPath", dir, size, NULL);
+    if (rv < 0) {
+    dir[0] = '\0';
+    }
+
+    free(key);
+    return (rv < -1) ? -1 : 0;
+}
+
+/**********************************************************************
+ * The rest of this file deals with storing keys or values in the registry
+ */
+
+char *ap_registry_parse_key(int index, char *key)
+{
+    char *head = key, *skey;
+    int i;
+    
+    if(!key)
+        return(NULL);
+
+    for(i = 0; i <= index; i++)
+    {
+        if(key && key[0] == '\\')
+            key++;
+        if (!key)
+            return(NULL);
+        head = key;
+        key = strchr(head, '\\');
+    }
+
+    if(!key)
+        return(strdup(head));
+    *key = '\0';
+    skey = strdup(head);
+    *key = '\\';
+    return(skey);
+}
+
+/*
+ * ap_registry_create_apache_key() creates the Apache registry key
+ * (HLKM\SOFTWARE\Apache Group\Apache\version, as defined at the start
+ * of this file), if it does not already exist. It will be called by
+ * ap_registry_store_key_int() if it cannot open this key. This 
+ * function is intended to be called by ap_registry_store_key_int() if
+ * the Apache key does not exist when it comes to store a data item.
+ *
+ * Returns 0 on success or -1 on error. If -1 is returned, the error will
+ * already have been logged.
+ */
+
+static int ap_registry_create_key(char *longkey)
+{
+    int index;
+    HKEY hKey;
+    HKEY hKeyNext;
+    int retval;
+    int rv;
+    char *key;
+
+    hKey = HKEY_LOCAL_MACHINE;
+    index = 0;
+    retval = 0;
+
+    /* Walk the tree, creating at each stage if necessary */
+    while (key=ap_registry_parse_key(index,longkey)) {
+       int result;
+
+       rv = RegCreateKeyEx(hKey,
+                           key,         /* subkey */
+                           0,           /* reserved */
+                           NULL,        /* class */
+                           REG_OPTION_NON_VOLATILE,
+                           KEY_WRITE,
+                           NULL,
+                           &hKeyNext,
+                           &result);
+       if (rv != ERROR_SUCCESS) {
+           do_error(rv, "RegCreateKeyEx(%s)", longkey);
+           retval = -4;
+       }
+
+       /* Close the old key */
+       rv = RegCloseKey(hKey);
+       if (rv != ERROR_SUCCESS) {
+           do_error(rv, "RegCloseKey", NULL);
+           if (retval == 0) {
+               /* Keep error status from RegCreateKeyEx, if any */
+               retval = -4;  
+           }
+       }
+
+       if (retval) {
+           break;
+       }
+
+    free(key);
+       hKey = hKeyNext;
+       index++;
+    }
+
+    if (!key) {
+       /* Close the final key we opened, if we walked the entire
+        * tree
+        */
+       rv = RegCloseKey(hKey);
+       if (rv != ERROR_SUCCESS) {
+           do_error(rv, "RegCloseKey", NULL);
+           if (retval == 0) {
+               /* Keep error status from RegCreateKeyEx, if any */
+               retval = -4;  
+           }
+       }
+    }
+    else
+        free(key);
+
+    return retval;
+}
+
+/*
+ * ap_registry_store_key_int() stores a value name and value under the
+ * Apache registry key. If the Apache key does not exist it is created
+ * first. This function is intended to be called from a wrapper function
+ * in this file to set particular data values, such as 
+ * ap_registry_set_server_root() below.
+ *
+ * Returns 0 if the value name and data was stored successfully, or
+ * returns -1 if the Apache key does not exist (since we try to create 
+ * this key, this should never happen), or -4 if any other error occurred
+ * (these values are consistent with ap_registry_get_key_int()).
+ * If the return value is negative then the error will already have been
+ * logged via aplog_error().
+ */
+
+static int ap_registry_store_key_int(char *key, char *name, DWORD type, void *value, int value_size)
+{
+    long rv;
+    HKEY hKey;
+    int retval;
+
+    rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                     key,
+                     0,
+                     KEY_WRITE,
+                     &hKey);
+
+    if (rv == ERROR_FILE_NOT_FOUND) {
+       /* Key could not be opened -- try to create it 
+        */
+        if (ap_registry_create_key(key) < 0) {
+           /* Creation failed (error already reported) */
+           return -4;
+       }
+       
+       /* Now it has been created we should be able to open it
+        */
+       rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                 key,
+                 0,
+                 KEY_WRITE,
+                 &hKey);
+
+       if (rv == ERROR_FILE_NOT_FOUND) {
+            ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,
+                         "Registry does not contain key %s after creation",key);
+           return -1;
+       }
+    }
+
+    if (rv != ERROR_SUCCESS) {
+        do_error(rv, "RegOpenKeyEx HKLM\\%s", key);
+        return -4;
+    }
+
+    /* Now set the value and data */
+    rv = RegSetValueEx(hKey, 
+                       name,   /* value key name */
+                      0,       /* reserved */
+                      type,    /* type */
+                      value,   /* value data */
+                      (DWORD)value_size); /* for size of "value" */
+
+    retval = 0;            /* Return value */
+
+    if (rv != ERROR_SUCCESS) {
+       do_error(rv, "RegQueryValueEx(key %s)", key);
+       retval = -4;
+    }
+    else {
+       ap_log_error(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,NULL,
+           "Registry stored HKLM\\" REGKEY "\\%s value %s", key, 
+           type == REG_SZ ? value : "(not displayable)");
+    }
+
+    /* Make sure we close the key even if there was an error storing
+     * the data
+     */
+    rv = RegCloseKey(hKey);
+    if (rv != ERROR_SUCCESS) {
+        do_error(rv, "RegCloseKey HKLM\\%s", key);
+        if (retval == 0) {
+            /* Keep error status from RegQueryValueEx, if any */
+            retval = -4;  
+        }
+    }
+
+    return retval;
+}
+
+/*
+ * Sets the service confpath value within the registry. Returns 0 on success
+ * or -1 on error. If -1 is return the error will already have been
+ * logged via aplog_error().
+ */
+
+int ap_registry_set_service_conf(char *conf, char *service_name)
+{
+    int rv;
+    char *key = ap_get_service_key(service_name);
+    
+    rv = ap_registry_store_key_int(key, "ConfPath", REG_SZ, conf, strlen(conf)+1);
+    free(key);
+
+    return rv < 0 ? -1: 0;
+}
+
+/*
+ * Sets the serverroot value within the registry. Returns 0 on success
+ * or -1 on error. If -1 is return the error will already have been
+ * logged via aplog_error().
+ */
+
+int ap_registry_set_server_root(char *dir)
+{
+    int rv;
+
+    rv = ap_registry_store_key_int(REGKEY, "ServerRoot", REG_SZ, dir, strlen(dir)+1);
+
+    return rv < 0 ? -1 : 0;
+}
+
diff --git a/server/mpm/winnt/service.c b/server/mpm/winnt/service.c
new file mode 100644 (file)
index 0000000..8108ae1
--- /dev/null
@@ -0,0 +1,431 @@
+#ifdef WIN32
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <process.h>
+#include <direct.h>
+
+#include "httpd.h"
+#include "http_conf_globals.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "multithread.h"
+#include "service.h"
+#include "registry.h"
+
+static struct
+{
+    int (*main_fn)(int, char **);
+    event *stop_event;
+    int connected;
+    SERVICE_STATUS_HANDLE hServiceStatus;
+    char *name;
+    int exit_status;
+    SERVICE_STATUS ssStatus;
+    FILE *logFile;
+} globdat;
+
+static void WINAPI service_main_fn(DWORD, LPTSTR *);
+static void WINAPI service_ctrl(DWORD ctrlCode);
+static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint);
+static int ap_start_service(SC_HANDLE);
+static int ap_stop_service(SC_HANDLE);
+
+int service_main(int (*main_fn)(int, char **), int argc, char **argv )
+{
+    SERVICE_TABLE_ENTRY dispatchTable[] =
+    {
+        { "", service_main_fn },
+        { NULL, NULL }
+    };
+
+    globdat.main_fn = main_fn;
+    globdat.stop_event = create_event(0, 0, "apache-signal");
+    globdat.connected = 1;
+
+    if(!StartServiceCtrlDispatcher(dispatchTable))
+    {
+        /* This is a genuine failure of the SCM. */
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+        "Error starting service control dispatcher");
+        return(globdat.exit_status);
+    }
+    else
+    {
+        return(globdat.exit_status);
+    }
+}
+
+void service_cd()
+{
+    /* change to the drive with the executable */
+    char buf[300];
+    GetModuleFileName(NULL, buf, 300);
+    buf[2] = 0;
+    chdir(buf);
+}
+
+void __stdcall service_main_fn(DWORD argc, LPTSTR *argv)
+{
+    ap_server_argv0 = globdat.name = argv[0];
+
+    if(!(globdat.hServiceStatus = RegisterServiceCtrlHandler( globdat.name, service_ctrl)))
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+        "Failure registering service handler");
+        return;
+    }
+
+    ReportStatusToSCMgr(
+        SERVICE_START_PENDING, // service state
+        NO_ERROR,              // exit code
+        3000);                 // wait hint
+
+    service_cd();
+    if( service_init() ) 
+        /* Arguments are ok except for \! */
+        globdat.exit_status = (*globdat.main_fn)( argc, argv );
+    
+    ReportStatusToSCMgr(SERVICE_STOPPED, NO_ERROR, 0);
+
+    return;
+}
+
+void service_set_status(int status)
+{
+    ReportStatusToSCMgr(status, NO_ERROR, 3000);
+}
+
+
+
+//
+//  FUNCTION: service_ctrl
+//
+//  PURPOSE: This function is called by the SCM whenever
+//           ControlService() is called on this service.
+//
+//  PARAMETERS:
+//    dwCtrlCode - type of control requested
+//
+//  RETURN VALUE:
+//    none
+//
+//  COMMENTS:
+//
+VOID WINAPI service_ctrl(DWORD dwCtrlCode)
+{
+    int state;
+
+
+    state = globdat.ssStatus.dwCurrentState;
+    switch(dwCtrlCode)
+    {
+        // Stop the service.
+        //
+        case SERVICE_CONTROL_STOP:
+            state = SERVICE_STOP_PENDING;
+           ap_start_shutdown();
+            break;
+
+        // Update the service status.
+        //
+        case SERVICE_CONTROL_INTERROGATE:
+            break;
+
+        // invalid control code
+        //
+        default:
+            break;
+
+    }
+
+    ReportStatusToSCMgr(state, NO_ERROR, 0);
+}
+
+
+int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint)
+{
+    static int firstTime = 1;
+    static int checkPoint = 1;
+    int rv;
+    
+    if(firstTime)
+    {
+        firstTime = 0;
+        globdat.ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+        globdat.ssStatus.dwServiceSpecificExitCode = 0;
+        globdat.ssStatus.dwCheckPoint = 1;
+    }
+
+    if(globdat.connected)
+    {
+        if (currentState == SERVICE_START_PENDING)
+            globdat.ssStatus.dwControlsAccepted = 0;
+        else
+            globdat.ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+
+        globdat.ssStatus.dwCurrentState = currentState;
+        globdat.ssStatus.dwWin32ExitCode = exitCode;
+        if(waitHint)
+            globdat.ssStatus.dwWaitHint = waitHint;
+
+        if ( ( currentState == SERVICE_RUNNING ) ||
+             ( currentState == SERVICE_STOPPED ) )
+        {
+            globdat.ssStatus.dwWaitHint = 0;
+            globdat.ssStatus.dwCheckPoint = 0;
+        }
+        else
+            globdat.ssStatus.dwCheckPoint = ++checkPoint;
+
+        rv = SetServiceStatus(globdat.hServiceStatus, &globdat.ssStatus);
+
+    }
+    return(1);
+}
+
+void InstallService(char *service_name, char *conf)
+{
+    SC_HANDLE   schService;
+    SC_HANDLE   schSCManager;
+
+    TCHAR szPath[512];
+    TCHAR szQuotedPath[512];
+
+    printf("Installing the %s service to use %s\n", service_name, conf);
+
+    if (GetModuleFileName( NULL, szPath, 512 ) == 0)
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+        "GetModuleFileName failed");
+        return;
+    }
+
+    ap_snprintf(szQuotedPath, 512, "\"%s\"", szPath);
+
+    schSCManager = OpenSCManager(
+                        NULL,                   // machine (NULL == local)
+                        NULL,                   // database (NULL == default)
+                        SC_MANAGER_ALL_ACCESS   // access required
+                        );
+   if (!schSCManager) {
+       ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+          "OpenSCManager failed");
+    }
+    else {
+        schService = CreateService(
+            schSCManager,               // SCManager database
+            service_name,               // name of service
+            service_name,               // name to display
+            SERVICE_ALL_ACCESS,         // desired access
+            SERVICE_WIN32_OWN_PROCESS,  // service type
+            SERVICE_AUTO_START,       // start type
+            SERVICE_ERROR_NORMAL,       // error control type
+            szQuotedPath,               // service's binary
+            NULL,                       // no load ordering group
+            NULL,                       // no tag identifier
+            NULL,       // dependencies
+            NULL,                       // LocalSystem account
+            NULL);                      // no password
+
+        if (schService) {
+            CloseServiceHandle(schService);
+
+            /* Now store the server_root in the registry */
+            if(!ap_registry_set_service_conf(conf, service_name))
+                printf("The %s service has been installed successfully.\n", service_name );
+        }
+        else {
+            ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, 
+               "CreateService failed");
+        }
+
+        CloseServiceHandle(schSCManager);
+    }
+}
+
+
+void RemoveService(char *service_name)
+{
+    SC_HANDLE   schService;
+    SC_HANDLE   schSCManager;
+
+    printf("Removing the %s service\n", service_name);
+
+    schSCManager = OpenSCManager(
+                        NULL,                   // machine (NULL == local)
+                        NULL,                   // database (NULL == default)
+                        SC_MANAGER_ALL_ACCESS   // access required
+                        );
+    if (!schSCManager) {
+       ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+          "OpenSCManager failed");
+    }
+    else {
+        schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS);
+
+        if (schService == NULL) {
+            /* Could not open the service */
+           ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+                       "OpenService failed");
+        }
+        else {
+            /* try to stop the service */
+            ap_stop_service(schService);
+
+            // now remove the service
+            if (DeleteService(schService) == 0)
+               ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+                   "DeleteService failed");
+            else
+                printf("The %s service has been removed successfully.\n", service_name );
+            CloseServiceHandle(schService);
+        }
+        /* SCM removes registry parameters  */
+        CloseServiceHandle(schSCManager);
+    }
+
+}
+
+/* A hack to determine if we're running as a service without waiting for
+ * the SCM to fail; if AllocConsole succeeds, we're a service.
+ */
+
+BOOL isProcessService() {
+    if( !AllocConsole() ) 
+        return FALSE;
+    FreeConsole();
+    return TRUE;
+}
+
+/* Determine is service_name is a valid service
+ */
+
+BOOL isValidService(char *service_name) {
+    SC_HANDLE schSCM, schSVC;
+    int Err;
+
+    if (!(schSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+        "OpenSCManager failed");
+       return FALSE;
+    }
+
+    if ((schSVC = OpenService(schSCM, service_name, SERVICE_ALL_ACCESS))) {
+        CloseServiceHandle(schSVC);
+        CloseServiceHandle(schSCM);
+        return TRUE;
+    }
+
+    Err = GetLastError();
+    if (Err != ERROR_SERVICE_DOES_NOT_EXIST && Err != ERROR_INVALID_NAME)
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+        "OpenService failed");
+
+    return FALSE;
+}
+
+int send_signal_to_service(char *service_name, char *sig) {
+    SC_HANDLE   schService;
+    SC_HANDLE   schSCManager;
+    int success = FALSE;
+
+    enum                        { start,      restart,      stop, unknown } action;
+    static char *param[] =      { "start",    "restart",    "shutdown" };
+    static char *participle[] = { "starting", "restarting", "stopping" };
+    static char *past[]       = { "started",  "restarted",  "stopped"  };
+
+    for (action = start; action < unknown; action++)
+        if (!strcasecmp(sig, param[action]))
+            break;
+
+    if (action == unknown) {
+        printf("signal must be start, restart, or shutdown\n");
+        return FALSE;
+    }
+
+    schSCManager = OpenSCManager(
+                        NULL,                   // machine (NULL == local)
+                        NULL,                   // database (NULL == default)
+                        SC_MANAGER_ALL_ACCESS   // access required
+                        );
+    if (!schSCManager) {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+        "OpenSCManager failed");
+    }
+    else {
+        schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS);
+
+        if (schService == NULL) {
+            /* Could not open the service */
+           ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+            "OpenService failed");
+        }
+        else {
+            if (!QueryServiceStatus(schService, &globdat.ssStatus))
+                ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+                             "QueryService failed");
+            else {
+                if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED && action == stop)
+                    printf("The %s service is not started.\n", service_name);
+                else if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING && action == start)
+                    printf("The %s service has already been started.\n", service_name);
+                else {
+                    printf("The %s service is %s.\n", service_name, participle[action]);
+
+                    if (action == stop || action == restart)
+                        success = ap_stop_service(schService);
+                    if (action == start || action == restart)
+                        success = ap_start_service(schService);
+                
+                    if( success )
+                        printf("The %s service has %s.\n", service_name, past[action]);
+                    else
+                        printf("Failed to %s the %s service.\n", sig, service_name );
+                }
+
+                CloseServiceHandle(schService);
+            }
+        }
+        /* SCM removes registry parameters */
+        CloseServiceHandle(schSCManager);
+    }
+    return success;
+}
+
+int ap_stop_service(SC_HANDLE schService)
+{
+    if (ControlService(schService, SERVICE_CONTROL_STOP, &globdat.ssStatus)) {
+        Sleep(1000);
+        while (QueryServiceStatus(schService, &globdat.ssStatus)) {
+            if (globdat.ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
+                Sleep(1000);
+            else
+                break;
+        }
+    }
+    if (QueryServiceStatus(schService, &globdat.ssStatus))
+        if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED)
+            return TRUE;
+    return FALSE;
+}
+
+int ap_start_service(SC_HANDLE schService) {
+    if (StartService(schService, 0, NULL)) {
+        Sleep(1000);
+        while(QueryServiceStatus(schService, &globdat.ssStatus)) {
+            if(globdat.ssStatus.dwCurrentState == SERVICE_START_PENDING)
+                Sleep(1000);
+            else
+                break;
+        }
+    }
+    if (QueryServiceStatus(schService, &globdat.ssStatus))
+        if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING)
+            return TRUE;
+    return FALSE;
+}
+           
+#endif /* WIN32 */
+
diff --git a/server/rfc1413.c b/server/rfc1413.c
new file mode 100644 (file)
index 0000000..65bc6ef
--- /dev/null
@@ -0,0 +1,248 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * rfc1413() speaks a common subset of the RFC 1413, AUTH, TAP and IDENT
+ * protocols. The code queries an RFC 1413 etc. compatible daemon on a remote
+ * host to look up the owner of a connection. The information should not be
+ * used for authentication purposes. This routine intercepts alarm signals.
+ * 
+ * Diagnostics are reported through syslog(3).
+ * 
+ * Author: Wietse Venema, Eindhoven University of Technology,
+ * The Netherlands.
+ */
+
+/* Some small additions for Apache --- ditch the "sccsid" var if
+ * compiling with gcc (it *has* changed), include ap_config.h for the
+ * prototypes it defines on at least one system (SunlOSs) which has
+ * them missing from the standard header files, and one minor change
+ * below (extra parens around assign "if (foo = bar) ..." to shut up
+ * gcc -Wall).
+ */
+
+/* Rewritten by David Robinson */
+
+#include "httpd.h"             /* for server_rec, conn_rec, ap_longjmp, etc. */
+#include "http_log.h"          /* for aplog_error */
+#include "rfc1413.h"
+#include "http_main.h"         /* set_callback_and_alarm */
+
+/* Local stuff. */
+/* Semi-well-known port */
+#define        RFC1413_PORT    113
+/* maximum allowed length of userid */
+#define RFC1413_USERLEN 512
+/* rough limit on the amount of data we accept. */
+#define RFC1413_MAXDATA 1000
+
+#ifndef RFC1413_TIMEOUT
+#define RFC1413_TIMEOUT        30
+#endif
+#define        ANY_PORT        0       /* Any old port will do */
+#define FROM_UNKNOWN  "unknown"
+
+int ap_rfc1413_timeout = RFC1413_TIMEOUT;      /* Global so it can be changed */
+
+static JMP_BUF timebuf;
+
+/* bind_connect - bind both ends of a socket */
+/* Ambarish fix this. Very broken */
+static int get_rfc1413(int sock, const struct sockaddr_in *our_sin,
+                      const struct sockaddr_in *rmt_sin, 
+                      char user[RFC1413_USERLEN+1], server_rec *srv)
+{
+    struct sockaddr_in rmt_query_sin, our_query_sin;
+    unsigned int rmt_port, our_port;
+    int i;
+    char *cp;
+    char buffer[RFC1413_MAXDATA + 1];
+    int buflen;
+
+    /*
+     * Bind the local and remote ends of the query socket to the same
+     * IP addresses as the connection under investigation. We go
+     * through all this trouble because the local or remote system
+     * might have more than one network address. The RFC1413 etc.
+     * client sends only port numbers; the server takes the IP
+     * addresses from the query socket.
+     */
+
+    our_query_sin = *our_sin;
+    our_query_sin.sin_port = htons(ANY_PORT);
+    rmt_query_sin = *rmt_sin;
+    rmt_query_sin.sin_port = htons(RFC1413_PORT);
+
+    if (bind(sock, (struct sockaddr *) &our_query_sin,
+            sizeof(struct sockaddr_in)) < 0) {
+       ap_log_error(APLOG_MARK, APLOG_CRIT, srv,
+                   "bind: rfc1413: Error binding to local port");
+       return -1;
+    }
+
+/*
+ * errors from connect usually imply the remote machine doesn't support
+ * the service
+ */
+    if (connect(sock, (struct sockaddr *) &rmt_query_sin,
+               sizeof(struct sockaddr_in)) < 0)
+                   return -1;
+
+/* send the data */
+    buflen = ap_snprintf(buffer, sizeof(buffer), "%u,%u\r\n", ntohs(rmt_sin->sin_port),
+               ntohs(our_sin->sin_port));
+
+    /* send query to server. Handle short write. */
+#ifdef CHARSET_EBCDIC
+    ebcdic2ascii(&buffer, &buffer, buflen);
+#endif
+    i = 0;
+    while(i < strlen(buffer)) {
+        int j;
+       j = write(sock, buffer+i, (strlen(buffer+i)));
+       if (j < 0 && errno != EINTR) {
+         ap_log_error(APLOG_MARK, APLOG_CRIT, srv,
+                      "write: rfc1413: error sending request");
+         return -1;
+       }
+       else if (j > 0) {
+           i+=j; 
+       }
+    }
+
+    /*
+     * Read response from server. - the response should be newline 
+     * terminated according to rfc - make sure it doesn't stomp it's
+     * way out of the buffer.
+     */
+
+    i = 0;
+    memset(buffer, '\0', sizeof(buffer));
+    /*
+     * Note that the strchr function below checks for 10 instead of '\n'
+     * this allows it to work on both ASCII and EBCDIC machines.
+     */
+    while((cp = strchr(buffer, '\012')) == NULL && i < sizeof(buffer) - 1) {
+        int j;
+       j = read(sock, buffer+i, (sizeof(buffer) - 1) - i);
+       if (j < 0 && errno != EINTR) {
+          ap_log_error(APLOG_MARK, APLOG_CRIT, srv,
+                       "read: rfc1413: error reading response");
+          return -1;
+       }
+       else if (j > 0) {
+           i+=j; 
+       }
+    }
+
+/* RFC1413_USERLEN = 512 */
+#ifdef CHARSET_EBCDIC
+    ascii2ebcdic(&buffer, &buffer, (size_t)i);
+#endif
+    if (sscanf(buffer, "%u , %u : USERID :%*[^:]:%512s", &rmt_port, &our_port,
+              user) != 3 || ntohs(rmt_sin->sin_port) != rmt_port
+       || ntohs(our_sin->sin_port) != our_port)
+       return -1;
+
+    /*
+     * Strip trailing carriage return. It is part of the
+     * protocol, not part of the data.
+     */
+
+    if ((cp = strchr(user, '\r')))
+       *cp = '\0';
+
+    return 0;
+}
+
+/* ident_timeout - handle timeouts */
+static void ident_timeout(int sig)
+{
+    ap_longjmp(timebuf, sig);
+}
+
+/* rfc1413 - return remote user name, given socket structures */
+char *ap_rfc1413(conn_rec *conn, server_rec *srv)
+{
+    static char user[RFC1413_USERLEN + 1];     /* XXX */
+    static char *result;
+    static int sock;
+
+    result = FROM_UNKNOWN;
+
+    sock = ap_psocket(conn->pool, AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (sock < 0) {
+       ap_log_error(APLOG_MARK, APLOG_CRIT, srv,
+                   "socket: rfc1413: error creating socket");
+       conn->remote_logname = result;
+    }
+
+    /*
+     * Set up a timer so we won't get stuck while waiting for the server.
+     */
+    if (ap_setjmp(timebuf) == 0) {
+       ap_set_callback_and_alarm(ident_timeout, ap_rfc1413_timeout);
+
+       if (get_rfc1413(sock, &conn->local_addr, &conn->remote_addr, user, srv) >= 0)
+           result = user;
+    }
+    ap_set_callback_and_alarm(NULL, 0);
+    ap_pclosesocket(conn->pool, sock);
+    conn->remote_logname = result;
+
+    return conn->remote_logname;
+}
diff --git a/server/util.c b/server/util.c
new file mode 100644 (file)
index 0000000..b358501
--- /dev/null
@@ -0,0 +1,2145 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * util.c: string utility things
+ * 
+ * 3/21/93 Rob McCool
+ * 1995-96 Many changes by the Apache Group
+ * 
+ */
+
+/* Debugging aid:
+ * #define DEBUG            to trace all cfg_open*()/cfg_closefile() calls
+ * #define DEBUG_CFG_LINES  to trace every line read from the config files
+ */
+
+#include "httpd.h"
+#include "http_conf_globals.h" /* for user_id & group_id */
+#include "http_log.h"
+#if defined(SUNOS4)
+/* stdio.h has been read in ap_config.h already. Add missing prototypes here: */
+extern int fgetc(FILE *);
+extern char *fgets(char *s, int, FILE*);
+extern int fclose(FILE *);
+#endif
+
+/* A bunch of functions in util.c scan strings looking for certain characters.
+ * To make that more efficient we encode a lookup table.  The test_char_table
+ * is generated automatically by gen_test_char.c.
+ */
+#include "test_char.h"
+
+/* we assume the folks using this ensure 0 <= c < 256... which means
+ * you need a cast to (unsigned char) first, you can't just plug a
+ * char in here and get it to work, because if char is signed then it
+ * will first be sign extended.
+ */
+#define TEST_CHAR(c, f)        (test_char_table[(unsigned)(c)] & (f))
+
+void ap_util_init(void)
+{
+    /* nothing to do... previously there was run-time initialization of
+     * test_char_table here
+     */
+}
+
+
+API_VAR_EXPORT const char ap_month_snames[12][4] =
+{
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+API_VAR_EXPORT const char ap_day_snames[7][4] =
+{
+    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+API_EXPORT(char *) ap_get_time()
+{
+    time_t t;
+    char *time_string;
+
+    t = time(NULL);
+    time_string = ctime(&t);
+    time_string[strlen(time_string) - 1] = '\0';
+    return (time_string);
+}
+
+/*
+ * Examine a field value (such as a media-/content-type) string and return
+ * it sans any parameters; e.g., strip off any ';charset=foo' and the like.
+ */
+API_EXPORT(char *) ap_field_noparam(pool *p, const char *intype)
+{
+    const char *semi;
+
+    semi = strchr(intype, ';');
+    if (semi == NULL) {
+       return ap_pstrdup(p, intype);
+    } 
+    else {
+       while ((semi > intype) && ap_isspace(semi[-1])) {
+           semi--;
+       }
+       return ap_pstrndup(p, intype, semi - intype);
+    }
+}
+
+API_EXPORT(char *) ap_ht_time(pool *p, time_t t, const char *fmt, int gmt)
+{
+    char ts[MAX_STRING_LEN];
+    char tf[MAX_STRING_LEN];
+    struct tm *tms;
+
+    tms = (gmt ? gmtime(&t) : localtime(&t));
+    if(gmt) {
+       /* Convert %Z to "GMT" and %z to "+0000";
+        * on hosts that do not have a time zone string in struct tm,
+        * strftime must assume its argument is local time.
+        */
+       const char *f;
+       char *strp;
+       for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f)
+           ; f++, strp++) {
+           if (*f != '%') continue;
+           switch (f[1]) {
+           case '%':
+               *++strp = *++f;
+               break;
+           case 'Z':
+               *strp++ = 'G';
+               *strp++ = 'M';
+               *strp = 'T';
+               f++;
+               break;
+           case 'z': /* common extension */
+               *strp++ = '+';
+               *strp++ = '0';
+               *strp++ = '0';
+               *strp++ = '0';
+               *strp = '0';
+               f++;
+               break;
+           }
+       }
+       *strp = '\0';
+       fmt = tf;
+    }
+
+    /* check return code? */
+    strftime(ts, MAX_STRING_LEN, fmt, tms);
+    ts[MAX_STRING_LEN - 1] = '\0';
+    return ap_pstrdup(p, ts);
+}
+
+API_EXPORT(char *) ap_gm_timestr_822(pool *p, time_t sec)
+{
+    struct tm *tms;
+
+    tms = gmtime(&sec);
+
+    /* RFC date format; as strftime '%a, %d %b %Y %T GMT' */
+    return ap_psprintf(p,
+               "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[tms->tm_wday],
+               tms->tm_mday, ap_month_snames[tms->tm_mon], tms->tm_year + 1900,
+               tms->tm_hour, tms->tm_min, tms->tm_sec);
+}
+
+/* What a pain in the ass. */
+#if defined(HAVE_GMTOFF)
+API_EXPORT(struct tm *) ap_get_gmtoff(int *tz)
+{
+    time_t tt = time(NULL);
+    struct tm *t;
+
+    t = localtime(&tt);
+    *tz = (int) (t->tm_gmtoff / 60);
+    return t;
+}
+#else
+API_EXPORT(struct tm *) ap_get_gmtoff(int *tz)
+{
+    time_t tt = time(NULL);
+    struct tm gmt;
+    struct tm *t;
+    int days, hours, minutes;
+
+    /* Assume we are never more than 24 hours away. */
+    gmt = *gmtime(&tt);                /* remember gmtime/localtime return ptr to static */
+    t = localtime(&tt);                /* buffer... so be careful */
+    days = t->tm_yday - gmt.tm_yday;
+    hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24)
+            + t->tm_hour - gmt.tm_hour);
+    minutes = hours * 60 + t->tm_min - gmt.tm_min;
+    *tz = minutes;
+    return t;
+}
+#endif
+
+/* Roy owes Rob beer. */
+/* Rob owes Roy dinner. */
+
+/* These legacy comments would make a lot more sense if Roy hadn't
+ * replaced the old later_than() routine with util_date.c.
+ *
+ * Well, okay, they still wouldn't make any sense.
+ */
+
+/* Match = 0, NoMatch = 1, Abort = -1
+ * Based loosely on sections of wildmat.c by Rich Salz
+ * Hmmm... shouldn't this really go component by component?
+ */
+API_EXPORT(int) ap_strcmp_match(const char *str, const char *exp)
+{
+    int x, y;
+
+    for (x = 0, y = 0; exp[y]; ++y, ++x) {
+       if ((!str[x]) && (exp[y] != '*'))
+           return -1;
+       if (exp[y] == '*') {
+           while (exp[++y] == '*');
+           if (!exp[y])
+               return 0;
+           while (str[x]) {
+               int ret;
+               if ((ret = ap_strcmp_match(&str[x++], &exp[y])) != 1)
+                   return ret;
+           }
+           return -1;
+       }
+       else if ((exp[y] != '?') && (str[x] != exp[y]))
+           return 1;
+    }
+    return (str[x] != '\0');
+}
+
+API_EXPORT(int) ap_strcasecmp_match(const char *str, const char *exp)
+{
+    int x, y;
+
+    for (x = 0, y = 0; exp[y]; ++y, ++x) {
+       if ((!str[x]) && (exp[y] != '*'))
+           return -1;
+       if (exp[y] == '*') {
+           while (exp[++y] == '*');
+           if (!exp[y])
+               return 0;
+           while (str[x]) {
+               int ret;
+               if ((ret = ap_strcasecmp_match(&str[x++], &exp[y])) != 1)
+                   return ret;
+           }
+           return -1;
+       }
+       else if ((exp[y] != '?') && (ap_tolower(str[x]) != ap_tolower(exp[y])))
+           return 1;
+    }
+    return (str[x] != '\0');
+}
+
+API_EXPORT(int) ap_is_matchexp(const char *str)
+{
+    register int x;
+
+    for (x = 0; str[x]; x++)
+       if ((str[x] == '*') || (str[x] == '?'))
+           return 1;
+    return 0;
+}
+
+/* 
+ * Apache stub function for the regex libraries regexec() to make sure the
+ * whole regex(3) API is available through the Apache (exported) namespace.
+ * This is especially important for the DSO situations of modules.
+ * DO NOT MAKE A MACRO OUT OF THIS FUNCTION!
+ */
+API_EXPORT(int) ap_regexec(const regex_t *preg, const char *string,
+                           size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+    return regexec(preg, string, nmatch, pmatch, eflags);
+}
+
+API_EXPORT(size_t) ap_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
+{
+    return regerror(errcode, preg, errbuf, errbuf_size);
+}
+
+
+/* This function substitutes for $0-$9, filling in regular expression
+ * submatches. Pass it the same nmatch and pmatch arguments that you
+ * passed ap_regexec(). pmatch should not be greater than the maximum number
+ * of subexpressions - i.e. one more than the re_nsub member of regex_t.
+ *
+ * input should be the string with the $-expressions, source should be the
+ * string that was matched against.
+ *
+ * It returns the substituted string, or NULL on error.
+ *
+ * Parts of this code are based on Henry Spencer's regsub(), from his
+ * AT&T V8 regexp package.
+ */
+
+API_EXPORT(char *) ap_pregsub(pool *p, const char *input, const char *source,
+                          size_t nmatch, regmatch_t pmatch[])
+{
+    const char *src = input;
+    char *dest, *dst;
+    char c;
+    size_t no;
+    int len;
+
+    if (!source)
+       return NULL;
+    if (!nmatch)
+       return ap_pstrdup(p, src);
+
+    /* First pass, find the size */
+
+    len = 0;
+
+    while ((c = *src++) != '\0') {
+       if (c == '&')
+           no = 0;
+       else if (c == '$' && ap_isdigit(*src))
+           no = *src++ - '0';
+       else
+           no = 10;
+
+       if (no > 9) {           /* Ordinary character. */
+           if (c == '\\' && (*src == '$' || *src == '&'))
+               c = *src++;
+           len++;
+       }
+       else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+           len += pmatch[no].rm_eo - pmatch[no].rm_so;
+       }
+
+    }
+
+    dest = dst = ap_pcalloc(p, len + 1);
+
+    /* Now actually fill in the string */
+
+    src = input;
+
+    while ((c = *src++) != '\0') {
+       if (c == '&')
+           no = 0;
+       else if (c == '$' && ap_isdigit(*src))
+           no = *src++ - '0';
+       else
+           no = 10;
+
+       if (no > 9) {           /* Ordinary character. */
+           if (c == '\\' && (*src == '$' || *src == '&'))
+               c = *src++;
+           *dst++ = c;
+       }
+       else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+           len = pmatch[no].rm_eo - pmatch[no].rm_so;
+           memcpy(dst, source + pmatch[no].rm_so, len);
+           dst += len;
+       }
+
+    }
+    *dst = '\0';
+
+    return dest;
+}
+
+/*
+ * Parse .. so we don't compromise security
+ */
+API_EXPORT(void) ap_getparents(char *name)
+{
+    int l, w;
+
+    /* Four paseses, as per RFC 1808 */
+    /* a) remove ./ path segments */
+
+    for (l = 0, w = 0; name[l] != '\0';) {
+       if (name[l] == '.' && name[l + 1] == '/' && (l == 0 || name[l - 1] == '/'))
+           l += 2;
+       else
+           name[w++] = name[l++];
+    }
+
+    /* b) remove trailing . path, segment */
+    if (w == 1 && name[0] == '.')
+       w--;
+    else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
+       w--;
+    name[w] = '\0';
+
+    /* c) remove all xx/../ segments. (including leading ../ and /../) */
+    l = 0;
+
+    while (name[l] != '\0') {
+       if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
+           (l == 0 || name[l - 1] == '/')) {
+           register int m = l + 3, n;
+
+           l = l - 2;
+           if (l >= 0) {
+               while (l >= 0 && name[l] != '/')
+                   l--;
+               l++;
+           }
+           else
+               l = 0;
+           n = l;
+           while ((name[n] = name[m]))
+               (++n, ++m);
+       }
+       else
+           ++l;
+    }
+
+    /* d) remove trailing xx/.. segment. */
+    if (l == 2 && name[0] == '.' && name[1] == '.')
+       name[0] = '\0';
+    else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' && name[l - 3] == '/') {
+       l = l - 4;
+       if (l >= 0) {
+           while (l >= 0 && name[l] != '/')
+               l--;
+           l++;
+       }
+       else
+           l = 0;
+       name[l] = '\0';
+    }
+}
+
+API_EXPORT(void) ap_no2slash(char *name)
+{
+    char *d, *s;
+
+    s = d = name;
+
+#ifdef WIN32
+    /* Check for UNC names.  Leave leading two slashes. */
+    if (s[0] == '/' && s[1] == '/')
+        *d++ = *s++;
+#endif
+
+    while (*s) {
+       if ((*d++ = *s) == '/') {
+           do {
+               ++s;
+           } while (*s == '/');
+       }
+       else {
+           ++s;
+       }
+    }
+    *d = '\0';
+}
+
+
+/*
+ * copy at most n leading directories of s into d
+ * d should be at least as large as s plus 1 extra byte
+ * assumes n > 0
+ * the return value is the ever useful pointer to the trailing \0 of d
+ *
+ * examples:
+ *    /a/b, 1  ==> /
+ *    /a/b, 2  ==> /a/
+ *    /a/b, 3  ==> /a/b/
+ *    /a/b, 4  ==> /a/b/
+ */
+API_EXPORT(char *) ap_make_dirstr_prefix(char *d, const char *s, int n)
+{
+    for (;;) {
+       *d = *s;
+       if (*d == '\0') {
+           *d = '/';
+           break;
+       }
+       if (*d == '/' && (--n) == 0)
+           break;
+       ++d;
+       ++s;
+    }
+    *++d = 0;
+    return (d);
+}
+
+
+/*
+ * return the parent directory name including trailing / of the file s
+ */
+API_EXPORT(char *) ap_make_dirstr_parent(pool *p, const char *s)
+{
+    char *last_slash = strrchr(s, '/');
+    char *d;
+    int l;
+
+    if (last_slash == NULL) {
+       /* XXX: well this is really broken if this happens */
+       return (ap_pstrdup(p, "/"));
+    }
+    l = (last_slash - s) + 1;
+    d = ap_palloc(p, l + 1);
+    memcpy(d, s, l);
+    d[l] = 0;
+    return (d);
+}
+
+
+/*
+ * This function is deprecated.  Use one of the preceeding two functions
+ * which are faster.
+ */
+API_EXPORT(char *) ap_make_dirstr(pool *p, const char *s, int n)
+{
+    register int x, f;
+    char *res;
+
+    for (x = 0, f = 0; s[x]; x++) {
+       if (s[x] == '/')
+           if ((++f) == n) {
+               res = ap_palloc(p, x + 2);
+               memcpy(res, s, x);
+               res[x] = '/';
+               res[x + 1] = '\0';
+               return res;
+           }
+    }
+
+    if (s[strlen(s) - 1] == '/')
+       return ap_pstrdup(p, s);
+    else
+       return ap_pstrcat(p, s, "/", NULL);
+}
+
+API_EXPORT(int) ap_count_dirs(const char *path)
+{
+    register int x, n;
+
+    for (x = 0, n = 0; path[x]; x++)
+       if (path[x] == '/')
+           n++;
+    return n;
+}
+
+
+API_EXPORT(void) ap_chdir_file(const char *file)
+{
+    const char *x;
+    char buf[HUGE_STRING_LEN];
+
+    x = strrchr(file, '/');
+    if (x == NULL) {
+       chdir(file);
+    }
+    else if (x - file < sizeof(buf) - 1) {
+       memcpy(buf, file, x - file);
+       buf[x - file] = '\0';
+       chdir(buf);
+    }
+    /* XXX: well, this is a silly function, no method of reporting an
+     * error... ah well. */
+}
+
+API_EXPORT(char *) ap_getword_nc(pool *atrans, char **line, char stop)
+{
+    return ap_getword(atrans, (const char **) line, stop);
+}
+
+API_EXPORT(char *) ap_getword(pool *atrans, const char **line, char stop)
+{
+    char *pos = strchr(*line, stop);
+    char *res;
+
+    if (!pos) {
+       res = ap_pstrdup(atrans, *line);
+       *line += strlen(*line);
+       return res;
+    }
+
+    res = ap_pstrndup(atrans, *line, pos - *line);
+
+    while (*pos == stop) {
+       ++pos;
+    }
+
+    *line = pos;
+
+    return res;
+}
+
+API_EXPORT(char *) ap_getword_white_nc(pool *atrans, char **line)
+{
+    return ap_getword_white(atrans, (const char **) line);
+}
+
+API_EXPORT(char *) ap_getword_white(pool *atrans, const char **line)
+{
+    int pos = -1, x;
+    char *res;
+
+    for (x = 0; (*line)[x]; x++) {
+       if (ap_isspace((*line)[x])) {
+           pos = x;
+           break;
+       }
+    }
+
+    if (pos == -1) {
+       res = ap_pstrdup(atrans, *line);
+       *line += strlen(*line);
+       return res;
+    }
+
+    res = ap_palloc(atrans, pos + 1);
+    ap_cpystrn(res, *line, pos + 1);
+
+    while (ap_isspace((*line)[pos]))
+       ++pos;
+
+    *line += pos;
+
+    return res;
+}
+
+API_EXPORT(char *) ap_getword_nulls_nc(pool *atrans, char **line, char stop)
+{
+    return ap_getword_nulls(atrans, (const char **) line, stop);
+}
+
+API_EXPORT(char *) ap_getword_nulls(pool *atrans, const char **line, char stop)
+{
+    char *pos = strchr(*line, stop);
+    char *res;
+
+    if (!pos) {
+       res = ap_pstrdup(atrans, *line);
+       *line += strlen(*line);
+       return res;
+    }
+
+    res = ap_pstrndup(atrans, *line, pos - *line);
+
+    ++pos;
+
+    *line = pos;
+
+    return res;
+}
+
+/* Get a word, (new) config-file style --- quoted strings and backslashes
+ * all honored
+ */
+
+static char *substring_conf(pool *p, const char *start, int len, char quote)
+{
+    char *result = ap_palloc(p, len + 2);
+    char *resp = result;
+    int i;
+
+    for (i = 0; i < len; ++i) {
+       if (start[i] == '\\' && (start[i + 1] == '\\'
+                                || (quote && start[i + 1] == quote)))
+           *resp++ = start[++i];
+       else
+           *resp++ = start[i];
+    }
+
+    *resp++ = '\0';
+    return result;
+}
+
+API_EXPORT(char *) ap_getword_conf_nc(pool *p, char **line)
+{
+    return ap_getword_conf(p, (const char **) line);
+}
+
+API_EXPORT(char *) ap_getword_conf(pool *p, const char **line)
+{
+    const char *str = *line, *strend;
+    char *res;
+    char quote;
+
+    while (*str && ap_isspace(*str))
+       ++str;
+
+    if (!*str) {
+       *line = str;
+       return "";
+    }
+
+    if ((quote = *str) == '"' || quote == '\'') {
+       strend = str + 1;
+       while (*strend && *strend != quote) {
+           if (*strend == '\\' && strend[1] && strend[1] == quote)
+               strend += 2;
+           else
+               ++strend;
+       }
+       res = substring_conf(p, str + 1, strend - str - 1, quote);
+
+       if (*strend == quote)
+           ++strend;
+    }
+    else {
+       strend = str;
+       while (*strend && !ap_isspace(*strend))
+           ++strend;
+
+       res = substring_conf(p, str, strend - str, 0);
+    }
+
+    while (*strend && ap_isspace(*strend))
+       ++strend;
+    *line = strend;
+    return res;
+}
+
+API_EXPORT(int) ap_cfg_closefile(configfile_t *cfp)
+{
+#ifdef DEBUG
+    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, 
+        "Done with config file %s", cfp->name);
+#endif
+    return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
+}
+
+/* Common structure that holds the file and pool for ap_pcfg_openfile */
+typedef struct {
+    struct pool *pool;
+    FILE *file;
+} poolfile_t;
+
+static int cfg_close(void *param)
+{
+    poolfile_t *cfp = (poolfile_t *) param;
+    return (ap_pfclose(cfp->pool, cfp->file));
+}
+
+static int cfg_getch(void *param)
+{
+    poolfile_t *cfp = (poolfile_t *) param;
+    return (fgetc(cfp->file));
+}
+
+static void *cfg_getstr(void *buf, size_t bufsiz, void *param)
+{
+    poolfile_t *cfp = (poolfile_t *) param;
+    return (fgets(buf, bufsiz, cfp->file));
+}
+
+/* Open a configfile_t as FILE, return open configfile_t struct pointer */
+API_EXPORT(configfile_t *) ap_pcfg_openfile(pool *p, const char *name)
+{
+    configfile_t *new_cfg;
+    poolfile_t *new_pfile;
+    FILE *file;
+    struct stat stbuf;
+    int saved_errno;
+
+    if (name == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL,
+               "Internal error: pcfg_openfile() called with NULL filename");
+        return NULL;
+    }
+
+    if (!ap_os_is_filename_valid(name)) {
+        ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL,
+                    "Access to config file %s denied: not a valid filename",
+                    name);
+       errno = EACCES;
+        return NULL;
+    }
+
+    file = ap_pfopen(p, name, "r");
+#ifdef DEBUG
+    saved_errno = errno;
+    ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, NULL,
+                "Opening config file %s (%s)",
+                name, (file == NULL) ? strerror(errno) : "successful");
+    errno = saved_errno;
+#endif
+    if (file == NULL)
+        return NULL;
+
+    if (fstat(fileno(file), &stbuf) == 0 &&
+        !S_ISREG(stbuf.st_mode) &&
+#if defined(WIN32) || defined(OS2)
+        !(strcasecmp(name, "nul") == 0 ||
+          (strlen(name) >= 4 &&
+           strcasecmp(name + strlen(name) - 4, "/nul") == 0))) {
+#else
+        strcmp(name, "/dev/null") != 0) {
+#endif /* WIN32 || OS2 */
+       saved_errno = errno;
+        ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL,
+                    "Access to file %s denied by server: not a regular file",
+                    name);
+        ap_pfclose(p, file);
+       errno = saved_errno;
+        return NULL;
+    }
+
+    new_cfg = ap_palloc(p, sizeof(*new_cfg));
+    new_pfile = ap_palloc(p, sizeof(*new_pfile));
+    new_pfile->file = file;
+    new_pfile->pool = p;
+    new_cfg->param = new_pfile;
+    new_cfg->name = ap_pstrdup(p, name);
+    new_cfg->getch = (int (*)(void *)) cfg_getch;
+    new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr;
+    new_cfg->close = (int (*)(void *)) cfg_close;
+    new_cfg->line_number = 0;
+    return new_cfg;
+}
+
+
+/* Allocate a configfile_t handle with user defined functions and params */
+API_EXPORT(configfile_t *) ap_pcfg_open_custom(pool *p, const char *descr,
+    void *param,
+    int(*getch)(void *param),
+    void *(*getstr) (void *buf, size_t bufsiz, void *param),
+    int(*close_func)(void *param))
+{
+    configfile_t *new_cfg = ap_palloc(p, sizeof(*new_cfg));
+#ifdef DEBUG
+    ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, NULL, "Opening config handler %s", descr);
+#endif
+    new_cfg->param = param;
+    new_cfg->name = descr;
+    new_cfg->getch = getch;
+    new_cfg->getstr = getstr;
+    new_cfg->close = close_func;
+    new_cfg->line_number = 0;
+    return new_cfg;
+}
+
+
+/* Read one character from a configfile_t */
+API_EXPORT(int) ap_cfg_getc(configfile_t *cfp)
+{
+    register int ch = cfp->getch(cfp->param);
+    if (ch == LF) 
+       ++cfp->line_number;
+    return ch;
+}
+
+
+/* Read one line from open configfile_t, strip LF, increase line number */
+/* If custom handler does not define a getstr() function, read char by char */
+API_EXPORT(int) ap_cfg_getline(char *buf, size_t bufsize, configfile_t *cfp)
+{
+    /* If a "get string" function is defined, use it */
+    if (cfp->getstr != NULL) {
+       char *src, *dst;
+       char *cp;
+       char *cbuf = buf;
+       size_t cbufsize = bufsize;
+
+       while (1) {
+           ++cfp->line_number;
+           if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
+               return 1;
+
+           /*
+            *  check for line continuation,
+            *  i.e. match [^\\]\\[\r]\n only
+            */
+           cp = cbuf;
+           while (cp < cbuf+cbufsize && *cp != '\0')
+               cp++;
+           if (cp > cbuf && cp[-1] == LF) {
+               cp--;
+               if (cp > cbuf && cp[-1] == CR)
+                   cp--;
+               if (cp > cbuf && cp[-1] == '\\') {
+                   cp--;
+                   if (!(cp > cbuf && cp[-1] == '\\')) {
+                       /*
+                        * line continuation requested -
+                        * then remove backslash and continue
+                        */
+                       cbufsize -= (cp-cbuf);
+                       cbuf = cp;
+                       continue;
+                   }
+                   else {
+                       /* 
+                        * no real continuation because escaped -
+                        * then just remove escape character
+                        */
+                       for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
+                           cp[0] = cp[1];
+                   }   
+               }
+           }
+           break;
+       }
+
+       /*
+        * Leading and trailing white space is eliminated completely
+        */
+       src = buf;
+       while (ap_isspace(*src))
+           ++src;
+       /* blast trailing whitespace */
+       dst = &src[strlen(src)];
+       while (--dst >= src && ap_isspace(*dst))
+           *dst = '\0';
+        /* Zap leading whitespace by shifting */
+        if (src != buf)
+           for (dst = buf; (*dst++ = *src++) != '\0'; )
+               ;
+
+#ifdef DEBUG_CFG_LINES
+       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Read config: %s", buf);
+#endif
+       return 0;
+    } else {
+       /* No "get string" function defined; read character by character */
+       register int c;
+       register size_t i = 0;
+
+       buf[0] = '\0';
+       /* skip leading whitespace */
+       do {
+           c = cfp->getch(cfp->param);
+       } while (c == '\t' || c == ' ');
+
+       if (c == EOF)
+           return 1;
+       
+       if(bufsize < 2) {
+           /* too small, assume caller is crazy */
+           return 1;
+       }
+
+       while (1) {
+           if ((c == '\t') || (c == ' ')) {
+               buf[i++] = ' ';
+               while ((c == '\t') || (c == ' '))
+                   c = cfp->getch(cfp->param);
+           }
+           if (c == CR) {
+               /* silently ignore CR (_assume_ that a LF follows) */
+               c = cfp->getch(cfp->param);
+           }
+           if (c == LF) {
+               /* increase line number and return on LF */
+               ++cfp->line_number;
+           }
+           if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
+               /* 
+                *  check for line continuation
+                */
+               if (i > 0 && buf[i-1] == '\\') {
+                   i--;
+                   if (!(i > 0 && buf[i-1] == '\\')) {
+                       /* line is continued */
+                       c = cfp->getch(cfp->param);
+                       continue;
+                   }
+                   /* else nothing needs be done because
+                    * then the backslash is escaped and
+                    * we just strip to a single one
+                    */
+               }
+               /* blast trailing whitespace */
+               while (i > 0 && ap_isspace(buf[i - 1]))
+                   --i;
+               buf[i] = '\0';
+#ifdef DEBUG_CFG_LINES
+               ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Read config: %s", buf);
+#endif
+               return 0;
+           }
+           buf[i] = c;
+           ++i;
+           c = cfp->getch(cfp->param);
+       }
+    }
+}
+
+/* Size an HTTP header field list item, as separated by a comma.
+ * The return value is a pointer to the beginning of the non-empty list item
+ * within the original string (or NULL if there is none) and the address
+ * of field is shifted to the next non-comma, non-whitespace character.
+ * len is the length of the item excluding any beginning whitespace.
+ */
+API_EXPORT(const char *) ap_size_list_item(const char **field, int *len)
+{
+    const unsigned char *ptr = (const unsigned char *)*field;
+    const unsigned char *token;
+    int in_qpair, in_qstr, in_com;
+
+    /* Find first non-comma, non-whitespace byte */
+
+    while (*ptr == ',' || ap_isspace(*ptr))
+        ++ptr;
+
+    token = ptr;
+
+    /* Find the end of this item, skipping over dead bits */
+
+    for (in_qpair = in_qstr = in_com = 0;
+         *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
+         ++ptr) {
+
+        if (in_qpair) {
+            in_qpair = 0;
+        }
+        else {
+            switch (*ptr) {
+                case '\\': in_qpair = 1;      /* quoted-pair         */
+                           break;
+                case '"' : if (!in_com)       /* quoted string delim */
+                               in_qstr = !in_qstr;
+                           break;
+                case '(' : if (!in_qstr)      /* comment (may nest)  */
+                               ++in_com;
+                           break;
+                case ')' : if (in_com)        /* end comment         */
+                               --in_com;
+                           break;
+                default  : break;
+            }
+        }
+    }
+
+    if ((*len = (ptr - token)) == 0) {
+        *field = (const char *)ptr;
+        return NULL;
+    }
+
+    /* Advance field pointer to the next non-comma, non-white byte */
+
+    while (*ptr == ',' || ap_isspace(*ptr))
+       ++ptr;
+
+    *field = (const char *)ptr;
+    return (const char *)token;
+}
+
+/* Retrieve an HTTP header field list item, as separated by a comma,
+ * while stripping insignificant whitespace and lowercasing anything not in
+ * a quoted string or comment.  The return value is a new string containing
+ * the converted list item (or NULL if none) and the address pointed to by
+ * field is shifted to the next non-comma, non-whitespace.
+ */
+API_EXPORT(char *) ap_get_list_item(pool *p, const char **field)
+{
+    const char *tok_start;
+    const unsigned char *ptr;
+    unsigned char *pos;
+    char *token;
+    int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0;
+
+    /* Find the beginning and maximum length of the list item so that
+     * we can allocate a buffer for the new string and reset the field.
+     */
+    if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) {
+        return NULL;
+    }
+    token = ap_palloc(p, tok_len + 1);
+
+    /* Scan the token again, but this time copy only the good bytes.
+     * We skip extra whitespace and any whitespace around a '=', '/',
+     * or ';' and lowercase normal characters not within a comment,
+     * quoted-string or quoted-pair.
+     */
+    for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token;
+         *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
+         ++ptr) {
+
+        if (in_qpair) {
+            in_qpair = 0;
+            *pos++ = *ptr;
+        }
+        else {
+            switch (*ptr) {
+                case '\\': in_qpair = 1;
+                           if (addspace == 1)
+                               *pos++ = ' ';
+                           *pos++ = *ptr;
+                           addspace = 0;
+                           break;
+                case '"' : if (!in_com)
+                               in_qstr = !in_qstr;
+                           if (addspace == 1)
+                               *pos++ = ' ';
+                           *pos++ = *ptr;
+                           addspace = 0;
+                           break;
+                case '(' : if (!in_qstr)
+                               ++in_com;
+                           if (addspace == 1)
+                               *pos++ = ' ';
+                           *pos++ = *ptr;
+                           addspace = 0;
+                           break;
+                case ')' : if (in_com)
+                               --in_com;
+                           *pos++ = *ptr;
+                           addspace = 0;
+                           break;
+                case ' ' :
+                case '\t': if (addspace)
+                               break;
+                           if (in_com || in_qstr)
+                               *pos++ = *ptr;
+                           else
+                               addspace = 1;
+                           break;
+                case '=' :
+                case '/' :
+                case ';' : if (!(in_com || in_qstr))
+                               addspace = -1;
+                           *pos++ = *ptr;
+                           break;
+                default  : if (addspace == 1)
+                               *pos++ = ' ';
+                           *pos++ = (in_com || in_qstr) ? *ptr
+                                                        : ap_tolower(*ptr);
+                           addspace = 0;
+                           break;
+            }
+        }
+    }
+    *pos = '\0';
+
+    return token;
+}
+
+/* Find an item in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list.  Returns 1 if found, 0 if not found.
+ * This would be much more efficient if we stored header fields as
+ * an array of list items as they are received instead of a plain string.
+ */
+API_EXPORT(int) ap_find_list_item(pool *p, const char *line, const char *tok)
+{
+    const unsigned char *pos;
+    const unsigned char *ptr = (const unsigned char *)line;
+    int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
+
+    if (!line || !tok)
+        return 0;
+
+    do {  /* loop for each item in line's list */
+
+        /* Find first non-comma, non-whitespace byte */
+
+        while (*ptr == ',' || ap_isspace(*ptr))
+            ++ptr;
+
+        if (*ptr)
+            good = 1;  /* until proven otherwise for this item */
+        else
+            break;     /* no items left and nothing good found */
+
+        /* We skip extra whitespace and any whitespace around a '=', '/',
+         * or ';' and lowercase normal characters not within a comment,
+         * quoted-string or quoted-pair.
+         */
+        for (pos = (const unsigned char *)tok;
+             *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
+             ++ptr) {
+
+            if (in_qpair) {
+                in_qpair = 0;
+                if (good)
+                    good = (*pos++ == *ptr);
+            }
+            else {
+                switch (*ptr) {
+                    case '\\': in_qpair = 1;
+                               if (addspace == 1)
+                                   good = good && (*pos++ == ' ');
+                               good = good && (*pos++ == *ptr);
+                               addspace = 0;
+                               break;
+                    case '"' : if (!in_com)
+                                   in_qstr = !in_qstr;
+                               if (addspace == 1)
+                                   good = good && (*pos++ == ' ');
+                               good = good && (*pos++ == *ptr);
+                               addspace = 0;
+                               break;
+                    case '(' : if (!in_qstr)
+                                   ++in_com;
+                               if (addspace == 1)
+                                   good = good && (*pos++ == ' ');
+                               good = good && (*pos++ == *ptr);
+                               addspace = 0;
+                               break;
+                    case ')' : if (in_com)
+                                   --in_com;
+                               good = good && (*pos++ == *ptr);
+                               addspace = 0;
+                               break;
+                    case ' ' :
+                    case '\t': if (addspace || !good)
+                                   break;
+                               if (in_com || in_qstr)
+                                   good = (*pos++ == *ptr);
+                               else
+                                   addspace = 1;
+                               break;
+                    case '=' :
+                    case '/' :
+                    case ';' : if (!(in_com || in_qstr))
+                                   addspace = -1;
+                               good = good && (*pos++ == *ptr);
+                               break;
+                    default  : if (!good)
+                                   break;
+                               if (addspace == 1)
+                                   good = (*pos++ == ' ');
+                               if (in_com || in_qstr)
+                                   good = good && (*pos++ == *ptr);
+                               else
+                                   good = good && (*pos++ == ap_tolower(*ptr));
+                               addspace = 0;
+                               break;
+                }
+            }
+        }
+        if (good && *pos)
+            good = 0;          /* not good if only a prefix was matched */
+
+    } while (*ptr && !good);
+
+    return good;
+}
+
+
+/* Retrieve a token, spacing over it and returning a pointer to
+ * the first non-white byte afterwards.  Note that these tokens
+ * are delimited by semis and commas; and can also be delimited
+ * by whitespace at the caller's option.
+ */
+
+API_EXPORT(char *) ap_get_token(pool *p, const char **accept_line, int accept_white)
+{
+    const char *ptr = *accept_line;
+    const char *tok_start;
+    char *token;
+    int tok_len;
+
+    /* Find first non-white byte */
+
+    while (*ptr && ap_isspace(*ptr))
+       ++ptr;
+
+    tok_start = ptr;
+
+    /* find token end, skipping over quoted strings.
+     * (comments are already gone).
+     */
+
+    while (*ptr && (accept_white || !ap_isspace(*ptr))
+          && *ptr != ';' && *ptr != ',') {
+       if (*ptr++ == '"')
+           while (*ptr)
+               if (*ptr++ == '"')
+                   break;
+    }
+
+    tok_len = ptr - tok_start;
+    token = ap_pstrndup(p, tok_start, tok_len);
+
+    /* Advance accept_line pointer to the next non-white byte */
+
+    while (*ptr && ap_isspace(*ptr))
+       ++ptr;
+
+    *accept_line = ptr;
+    return token;
+}
+
+
+/* find http tokens, see the definition of token from RFC2068 */
+API_EXPORT(int) ap_find_token(pool *p, const char *line, const char *tok)
+{
+    const unsigned char *start_token;
+    const unsigned char *s;
+
+    if (!line)
+       return 0;
+
+    s = (const unsigned char *)line;
+    for (;;) {
+       /* find start of token, skip all stop characters, note NUL
+        * isn't a token stop, so we don't need to test for it
+        */
+       while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
+           ++s;
+       }
+       if (!*s) {
+           return 0;
+       }
+       start_token = s;
+       /* find end of the token */
+       while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
+           ++s;
+       }
+       if (!strncasecmp((const char *)start_token, (const char *)tok, s - start_token)) {
+           return 1;
+       }
+       if (!*s) {
+           return 0;
+       }
+    }
+}
+
+
+API_EXPORT(int) ap_find_last_token(pool *p, const char *line, const char *tok)
+{
+    int llen, tlen, lidx;
+
+    if (!line)
+       return 0;
+
+    llen = strlen(line);
+    tlen = strlen(tok);
+    lidx = llen - tlen;
+
+    if ((lidx < 0) ||
+       ((lidx > 0) && !(ap_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
+       return 0;
+
+    return (strncasecmp(&line[lidx], tok, tlen) == 0);
+}
+
+API_EXPORT(char *) ap_escape_shell_cmd(pool *p, const char *str)
+{
+    char *cmd;
+    unsigned char *d;
+    const unsigned char *s;
+
+    cmd = ap_palloc(p, 2 * strlen(str) + 1);   /* Be safe */
+    d = (unsigned char *)cmd;
+    s = (const unsigned char *)str;
+    for (; *s; ++s) {
+
+#if defined(OS2) || defined(WIN32)
+       /* Don't allow '&' in parameters under OS/2. */
+       /* This can be used to send commands to the shell. */
+       if (*s == '&') {
+           *d++ = ' ';
+           continue;
+       }
+#endif
+
+       if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
+           *d++ = '\\';
+       }
+       *d++ = *s;
+    }
+    *d = '\0';
+
+    return cmd;
+}
+
+static char x2c(const char *what)
+{
+    register char digit;
+
+#ifndef CHARSET_EBCDIC
+    digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
+    digit *= 16;
+    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
+#else /*CHARSET_EBCDIC*/
+    char xstr[5];
+    xstr[0]='0';
+    xstr[1]='x';
+    xstr[2]=what[0];
+    xstr[3]=what[1];
+    xstr[4]='\0';
+    digit = os_toebcdic[0xFF & strtol(xstr, NULL, 16)];
+#endif /*CHARSET_EBCDIC*/
+    return (digit);
+}
+
+/*
+ * Unescapes a URL.
+ * Returns 0 on success, non-zero on error
+ * Failure is due to
+ *   bad % escape       returns BAD_REQUEST
+ *
+ *   decoding %00 -> \0
+ *   decoding %2f -> /   (a special character)
+ *                      returns NOT_FOUND
+ */
+API_EXPORT(int) ap_unescape_url(char *url)
+{
+    register int x, y, badesc, badpath;
+
+    badesc = 0;
+    badpath = 0;
+    for (x = 0, y = 0; url[y]; ++x, ++y) {
+       if (url[y] != '%')
+           url[x] = url[y];
+       else {
+           if (!ap_isxdigit(url[y + 1]) || !ap_isxdigit(url[y + 2])) {
+               badesc = 1;
+               url[x] = '%';
+           }
+           else {
+               url[x] = x2c(&url[y + 1]);
+               y += 2;
+               if (url[x] == '/' || url[x] == '\0')
+                   badpath = 1;
+           }
+       }
+    }
+    url[x] = '\0';
+    if (badesc)
+       return BAD_REQUEST;
+    else if (badpath)
+       return NOT_FOUND;
+    else
+       return OK;
+}
+
+API_EXPORT(char *) ap_construct_server(pool *p, const char *hostname,
+                                   unsigned port, const request_rec *r)
+{
+    if (ap_is_default_port(port, r))
+       return ap_pstrdup(p, hostname);
+    else {
+       return ap_psprintf(p, "%s:%u", hostname, port);
+    }
+}
+
+/* c2x takes an unsigned, and expects the caller has guaranteed that
+ * 0 <= what < 256... which usually means that you have to cast to
+ * unsigned char first, because (unsigned)(char)(x) fist goes through
+ * signed extension to an int before the unsigned cast.
+ *
+ * The reason for this assumption is to assist gcc code generation --
+ * the unsigned char -> unsigned extension is already done earlier in
+ * both uses of this code, so there's no need to waste time doing it
+ * again.
+ */
+static const char c2x_table[] = "0123456789abcdef";
+
+static ap_inline unsigned char *c2x(unsigned what, unsigned char *where)
+{
+    *where++ = '%';
+    *where++ = c2x_table[what >> 4];
+    *where++ = c2x_table[what & 0xf];
+    return where;
+}
+
+/*
+ * escape_path_segment() escapes a path segment, as defined in RFC 1808. This
+ * routine is (should be) OS independent.
+ *
+ * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all
+ * cases if a ':' occurs before the first '/' in the URL, the URL should be
+ * prefixed with "./" (or the ':' escaped). In the case of Unix, this means
+ * leaving '/' alone, but otherwise doing what escape_path_segment() does. For
+ * efficiency reasons, we don't use escape_path_segment(), which is provided for
+ * reference. Again, RFC 1808 is where this stuff is defined.
+ *
+ * If partial is set, os_escape_path() assumes that the path will be appended to
+ * something with a '/' in it (and thus does not prefix "./").
+ */
+
+API_EXPORT(char *) ap_escape_path_segment(pool *p, const char *segment)
+{
+    char *copy = ap_palloc(p, 3 * strlen(segment) + 1);
+    const unsigned char *s = (const unsigned char *)segment;
+    unsigned char *d = (unsigned char *)copy;
+    unsigned c;
+
+    while ((c = *s)) {
+       if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
+           d = c2x(c, d);
+       }
+       else {
+           *d++ = c;
+       }
+       ++s;
+    }
+    *d = '\0';
+    return copy;
+}
+
+API_EXPORT(char *) ap_os_escape_path(pool *p, const char *path, int partial)
+{
+    char *copy = ap_palloc(p, 3 * strlen(path) + 3);
+    const unsigned char *s = (const unsigned char *)path;
+    unsigned char *d = (unsigned char *)copy;
+    unsigned c;
+
+    if (!partial) {
+       char *colon = strchr(path, ':');
+       char *slash = strchr(path, '/');
+
+       if (colon && (!slash || colon < slash)) {
+           *d++ = '.';
+           *d++ = '/';
+       }
+    }
+    while ((c = *s)) {
+       if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
+           d = c2x(c, d);
+       }
+       else {
+           *d++ = c;
+       }
+       ++s;
+    }
+    *d = '\0';
+    return copy;
+}
+
+/* ap_escape_uri is now a macro for os_escape_path */
+
+API_EXPORT(char *) ap_escape_html(pool *p, const char *s)
+{
+    int i, j;
+    char *x;
+
+    /* first, count the number of extra characters */
+    for (i = 0, j = 0; s[i] != '\0'; i++)
+       if (s[i] == '<' || s[i] == '>')
+           j += 3;
+       else if (s[i] == '&')
+           j += 4;
+
+    if (j == 0)
+       return ap_pstrndup(p, s, i);
+
+    x = ap_palloc(p, i + j + 1);
+    for (i = 0, j = 0; s[i] != '\0'; i++, j++)
+       if (s[i] == '<') {
+           memcpy(&x[j], "&lt;", 4);
+           j += 3;
+       }
+       else if (s[i] == '>') {
+           memcpy(&x[j], "&gt;", 4);
+           j += 3;
+       }
+       else if (s[i] == '&') {
+           memcpy(&x[j], "&amp;", 5);
+           j += 4;
+       }
+       else
+           x[j] = s[i];
+
+    x[j] = '\0';
+    return x;
+}
+
+API_EXPORT(int) ap_is_directory(const char *path)
+{
+    struct stat finfo;
+
+    if (stat(path, &finfo) == -1)
+       return 0;               /* in error condition, just return no */
+
+    return (S_ISDIR(finfo.st_mode));
+}
+
+API_EXPORT(char *) ap_make_full_path(pool *a, const char *src1,
+                                 const char *src2)
+{
+    register int x;
+
+    x = strlen(src1);
+    if (x == 0)
+       return ap_pstrcat(a, "/", src2, NULL);
+
+    if (src1[x - 1] != '/')
+       return ap_pstrcat(a, src1, "/", src2, NULL);
+    else
+       return ap_pstrcat(a, src1, src2, NULL);
+}
+
+/*
+ * Check for an absoluteURI syntax (see section 3.2 in RFC2068).
+ */
+API_EXPORT(int) ap_is_url(const char *u)
+{
+    register int x;
+
+    for (x = 0; u[x] != ':'; x++) {
+       if ((!u[x]) ||
+           ((!ap_isalpha(u[x])) && (!ap_isdigit(u[x])) &&
+            (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) {
+           return 0;
+       }
+    }
+
+    return (x ? 1 : 0);                /* If the first character is ':', it's broken, too */
+}
+
+API_EXPORT(int) ap_can_exec(const struct stat *finfo)
+{
+#ifdef MULTIPLE_GROUPS
+    int cnt;
+#endif
+#if defined(OS2) || defined(WIN32)
+    /* OS/2 dosen't have Users and Groups */
+    return 1;
+#else
+    if (ap_user_id == finfo->st_uid)
+       if (finfo->st_mode & S_IXUSR)
+           return 1;
+    if (ap_group_id == finfo->st_gid)
+       if (finfo->st_mode & S_IXGRP)
+           return 1;
+#ifdef MULTIPLE_GROUPS
+    for (cnt = 0; cnt < NGROUPS_MAX; cnt++) {
+       if (group_id_list[cnt] == finfo->st_gid)
+           if (finfo->st_mode & S_IXGRP)
+               return 1;
+    }
+#endif
+    return (finfo->st_mode & S_IXOTH);
+#endif
+}
+
+#ifdef NEED_STRDUP
+char *strdup(const char *str)
+{
+    char *sdup;
+
+    if (!(sdup = (char *) malloc(strlen(str) + 1))) {
+       fprintf(stderr, "Ouch!  Out of memory in our strdup()!\n");
+       return NULL;
+    }
+    sdup = strcpy(sdup, str);
+
+    return sdup;
+}
+#endif
+
+/* The following two routines were donated for SVR4 by Andreas Vogel */
+#ifdef NEED_STRCASECMP
+int strcasecmp(const char *a, const char *b)
+{
+    const char *p = a;
+    const char *q = b;
+    for (p = a, q = b; *p && *q; p++, q++) {
+       int diff = ap_tolower(*p) - ap_tolower(*q);
+       if (diff)
+           return diff;
+    }
+    if (*p)
+       return 1;               /* p was longer than q */
+    if (*q)
+       return -1;              /* p was shorter than q */
+    return 0;                  /* Exact match */
+}
+
+#endif
+
+#ifdef NEED_STRNCASECMP
+int strncasecmp(const char *a, const char *b, int n)
+{
+    const char *p = a;
+    const char *q = b;
+
+    for (p = a, q = b; /*NOTHING */ ; p++, q++) {
+       int diff;
+       if (p == a + n)
+           return 0;           /*   Match up to n characters */
+       if (!(*p && *q))
+           return *p - *q;
+       diff = ap_tolower(*p) - ap_tolower(*q);
+       if (diff)
+           return diff;
+    }
+    /*NOTREACHED */
+}
+#endif
+
+/* The following routine was donated for UTS21 by dwd@bell-labs.com */
+#ifdef NEED_STRSTR
+char *strstr(char *s1, char *s2)
+{
+    char *p1, *p2;
+    if (*s2 == '\0') {
+       /* an empty s2 */
+        return(s1);
+    }
+    while((s1 = strchr(s1, *s2)) != NULL) {
+       /* found first character of s2, see if the rest matches */
+        p1 = s1;
+        p2 = s2;
+        while (*++p1 == *++p2) {
+            if (*p1 == '\0') {
+                /* both strings ended together */
+                return(s1);
+            }
+        }
+        if (*p2 == '\0') {
+            /* second string ended, a match */
+            break;
+        }
+       /* didn't find a match here, try starting at next character in s1 */
+        s1++;
+    }
+    return(s1);
+}
+#endif
+
+#ifdef NEED_INITGROUPS
+int initgroups(const char *name, gid_t basegid)
+{
+#if defined(QNX) || defined(MPE) || defined(BEOS) || defined(_OSD_POSIX) || defined(TPF) || defined(__TANDEM)
+/* QNX, MPE and BeOS do not appear to support supplementary groups. */
+    return 0;
+#else /* ndef QNX */
+    gid_t groups[NGROUPS_MAX];
+    struct group *g;
+    int index = 0;
+
+    setgrent();
+
+    groups[index++] = basegid;
+
+    while (index < NGROUPS_MAX && ((g = getgrent()) != NULL))
+       if (g->gr_gid != basegid) {
+           char **names;
+
+           for (names = g->gr_mem; *names != NULL; ++names)
+               if (!strcmp(*names, name))
+                   groups[index++] = g->gr_gid;
+       }
+
+    endgrent();
+
+    return setgroups(index, groups);
+#endif /* def QNX */
+}
+#endif /* def NEED_INITGROUPS */
+
+#ifdef NEED_WAITPID
+/* From ikluft@amdahl.com
+ * this is not ideal but it works for SVR3 variants
+ * Modified by dwd@bell-labs.com to call wait3 instead of wait because
+ *   apache started to use the WNOHANG option.
+ */
+int waitpid(pid_t pid, int *statusp, int options)
+{
+    int tmp_pid;
+    if (kill(pid, 0) == -1) {
+       errno = ECHILD;
+       return -1;
+    }
+    while (((tmp_pid = wait3(statusp, options, 0)) != pid) &&
+               (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1))
+       ;
+    return tmp_pid;
+}
+#endif
+
+API_EXPORT(int) ap_ind(const char *s, char c)
+{
+    register int x;
+
+    for (x = 0; s[x]; x++)
+       if (s[x] == c)
+           return x;
+
+    return -1;
+}
+
+API_EXPORT(int) ap_rind(const char *s, char c)
+{
+    register int x;
+
+    for (x = strlen(s) - 1; x != -1; x--)
+       if (s[x] == c)
+           return x;
+
+    return -1;
+}
+
+API_EXPORT(void) ap_str_tolower(char *str)
+{
+    while (*str) {
+       *str = ap_tolower(*str);
+       ++str;
+    }
+}
+
+API_EXPORT(uid_t) ap_uname2id(const char *name)
+{
+#ifdef WIN32
+    return (1);
+#else
+    struct passwd *ent;
+
+    if (name[0] == '#')
+       return (atoi(&name[1]));
+
+    if (!(ent = getpwnam(name))) {
+       fprintf(stderr, "%s: bad user name %s\n", ap_server_argv0, name);
+       exit(1);
+    }
+    return (ent->pw_uid);
+#endif
+}
+
+API_EXPORT(gid_t) ap_gname2id(const char *name)
+{
+#ifdef WIN32
+    return (1);
+#else
+    struct group *ent;
+
+    if (name[0] == '#')
+       return (atoi(&name[1]));
+
+    if (!(ent = getgrnam(name))) {
+       fprintf(stderr, "%s: bad group name %s\n", ap_server_argv0, name);
+       exit(1);
+    }
+    return (ent->gr_gid);
+#endif
+}
+
+
+/*
+ * Parses a host of the form <address>[:port]
+ * :port is permitted if 'port' is not NULL
+ */
+unsigned long ap_get_virthost_addr(char *w, unsigned short *ports)
+{
+    struct hostent *hep;
+    unsigned long my_addr;
+    char *p;
+
+    p = strchr(w, ':');
+    if (ports != NULL) {
+       *ports = 0;
+       if (p != NULL && strcmp(p + 1, "*") != 0)
+           *ports = atoi(p + 1);
+    }
+
+    if (p != NULL)
+       *p = '\0';
+    if (strcmp(w, "*") == 0) {
+       if (p != NULL)
+           *p = ':';
+       return htonl(INADDR_ANY);
+    }
+
+    my_addr = ap_inet_addr((char *)w);
+    if (my_addr != INADDR_NONE) {
+       if (p != NULL)
+           *p = ':';
+       return my_addr;
+    }
+
+    hep = gethostbyname(w);
+
+    if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
+       fprintf(stderr, "Cannot resolve host name %s --- exiting!\n", w);
+       exit(1);
+    }
+
+    if (hep->h_addr_list[1]) {
+       fprintf(stderr, "Host %s has multiple addresses ---\n", w);
+       fprintf(stderr, "you must choose one explicitly for use as\n");
+       fprintf(stderr, "a virtual host.  Exiting!!!\n");
+       exit(1);
+    }
+
+    if (p != NULL)
+       *p = ':';
+
+    return ((struct in_addr *) (hep->h_addr))->s_addr;
+}
+
+
+static char *find_fqdn(pool *a, struct hostent *p)
+{
+    int x;
+
+    if (!strchr(p->h_name, '.')) {
+       for (x = 0; p->h_aliases[x]; ++x) {
+           if (strchr(p->h_aliases[x], '.') &&
+               (!strncasecmp(p->h_aliases[x], p->h_name, strlen(p->h_name))))
+               return ap_pstrdup(a, p->h_aliases[x]);
+       }
+       return NULL;
+    }
+    return ap_pstrdup(a, (void *) p->h_name);
+}
+
+char *ap_get_local_host(pool *a)
+{
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+    char str[MAXHOSTNAMELEN + 1];
+    char *server_hostname;
+    struct hostent *p;
+
+#ifdef BEOS /* BeOS returns zero as an error for gethostname */
+    if (gethostname(str, sizeof(str) - 1) == 0) {
+#else    
+    if (gethostname(str, sizeof(str) - 1) != 0) {
+#endif /* BeOS */
+       perror("Unable to gethostname");
+       exit(1);
+    }
+    str[MAXHOSTNAMELEN] = '\0';
+    if ((!(p = gethostbyname(str))) || (!(server_hostname = find_fqdn(a, p)))) {
+       fprintf(stderr, "%s: cannot determine local host name.\n",
+               ap_server_argv0);
+       fprintf(stderr, "Use the ServerName directive to set it manually.\n");
+       exit(1);
+    }
+
+    return server_hostname;
+}
+
+/* simple 'pool' alloc()ing glue to ap_base64.c
+ */
+API_EXPORT(char *) ap_pbase64decode(pool *p, const char *bufcoded)
+{
+    char *decoded;
+    int l;
+
+    decoded = (char *) ap_palloc(p, 1 + ap_base64decode_len(bufcoded));
+    l = ap_base64decode(decoded, bufcoded);
+    decoded[l] = '\0'; /* make binary sequence into string */
+
+    return decoded;
+}
+
+API_EXPORT(char *) ap_pbase64encode(pool *p, char *string) 
+{ 
+    char *encoded;
+    int l = strlen(string);
+
+    encoded = (char *) ap_palloc(p, 1 + ap_base64encode_len(l));
+    l = ap_base64encode(encoded, string, l);
+    encoded[l] = '\0'; /* make binary sequence into string */
+
+    return encoded;
+}
+
+/* deprecated names for the above two functions, here for compatibility
+ */
+API_EXPORT(char *) ap_uudecode(pool *p, const char *bufcoded)
+{
+    return ap_pbase64decode(p, bufcoded);
+}
+
+API_EXPORT(char *) ap_uuencode(pool *p, char *string) 
+{ 
+    return ap_pbase64encode(p, string);
+}
+
+#ifdef OS2
+void os2pathname(char *path)
+{
+    char newpath[MAX_STRING_LEN];
+    int loop;
+    int offset;
+
+    offset = 0;
+    for (loop = 0; loop < (strlen(path) + 1) && loop < sizeof(newpath) - 1; loop++) {
+       if (path[loop] == '/') {
+           newpath[offset] = '\\';
+           /*
+              offset = offset + 1;
+              newpath[offset] = '\\';
+            */
+       }
+       else
+           newpath[offset] = path[loop];
+       offset = offset + 1;
+    };
+    /* Debugging code */
+    /* fprintf(stderr, "%s \n", newpath); */
+
+    strcpy(path, newpath);
+};
+
+/* quotes in the string are doubled up.
+ * Used to escape quotes in args passed to OS/2's cmd.exe
+ */
+char *ap_double_quotes(pool *p, char *str)
+{
+    int num_quotes = 0;
+    int len = 0;
+    char *quote_doubled_str, *dest;
+    
+    while (str[len]) {
+        num_quotes += str[len++] == '\"';
+    }
+    
+    quote_doubled_str = ap_palloc(p, len + num_quotes + 1);
+    dest = quote_doubled_str;
+    
+    while (*str) {
+        if (*str == '\"')
+            *(dest++) = '\"';
+        *(dest++) = *(str++);
+    }
+    
+    *dest = 0;
+    return quote_doubled_str;
+}
+#endif
+
+
+#ifdef NEED_STRERROR
+char *
+     strerror(int err)
+{
+
+    char *p;
+    extern char *const sys_errlist[];
+
+    p = sys_errlist[err];
+    return (p);
+}
+#endif
+
+#if defined(NEED_DIFFTIME)
+double difftime(time_t time1, time_t time0)
+{
+    return (time1 - time0);
+}
+#endif
+
+/* we want to downcase the type/subtype for comparison purposes
+ * but nothing else because ;parameter=foo values are case sensitive.
+ * XXX: in truth we want to downcase parameter names... but really,
+ * apache has never handled parameters and such correctly.  You
+ * also need to compress spaces and such to be able to compare
+ * properly. -djg
+ */
+API_EXPORT(void) ap_content_type_tolower(char *str)
+{
+    char *semi;
+
+    semi = strchr(str, ';');
+    if (semi) {
+       *semi = '\0';
+    }
+    while (*str) {
+       *str = ap_tolower(*str);
+       ++str;
+    }
+    if (semi) {
+       *semi = ';';
+    }
+}
+
+/*
+ * Given a string, replace any bare " with \" .
+ */
+API_EXPORT(char *) ap_escape_quotes (pool *p, const char *instring)
+{
+    int newlen = 0;
+    const char *inchr = instring;
+    char *outchr, *outstring;
+
+    /*
+     * Look through the input string, jogging the length of the output
+     * string up by an extra byte each time we find an unescaped ".
+     */
+    while (*inchr != '\0') {
+       newlen++;
+        if (*inchr == '"') {
+           newlen++;
+       }
+       /*
+        * If we find a slosh, and it's not the last byte in the string,
+        * it's escaping something - advance past both bytes.
+        */
+       if ((*inchr == '\\') && (inchr[1] != '\0')) {
+           inchr++;
+           newlen++;
+       }
+       inchr++;
+    }
+    outstring = ap_palloc(p, newlen + 1);
+    inchr = instring;
+    outchr = outstring;
+    /*
+     * Now copy the input string to the output string, inserting a slosh
+     * in front of every " that doesn't already have one.
+     */
+    while (*inchr != '\0') {
+       if ((*inchr == '\\') && (inchr[1] != '\0')) {
+           *outchr++ = *inchr++;
+           *outchr++ = *inchr++;
+       }
+       if (*inchr == '"') {
+           *outchr++ = '\\';
+       }
+       if (*inchr != '\0') {
+           *outchr++ = *inchr++;
+       }
+    }
+    *outchr = '\0';
+    return outstring;
+}
diff --git a/server/util_date.c b/server/util_date.c
new file mode 100644 (file)
index 0000000..5111a60
--- /dev/null
@@ -0,0 +1,321 @@
+/* ====================================================================
+ * Copyright (c) 1996-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * util_date.c: date parsing utility routines
+ *     These routines are (hopefully) platform-independent.
+ * 
+ * 27 Oct 1996  Roy Fielding
+ *     Extracted (with many modifications) from mod_proxy.c and
+ *     tested with over 50,000 randomly chosen valid date strings
+ *     and several hundred variations of invalid date strings.
+ * 
+ */
+
+#include "ap_config.h"
+#include "util_date.h"
+#include <ctype.h>
+#include <string.h>
+
+/*
+ * Compare a string to a mask
+ * Mask characters (arbitrary maximum is 256 characters, just in case):
+ *   @ - uppercase letter
+ *   $ - lowercase letter
+ *   & - hex digit
+ *   # - digit
+ *   ~ - digit or space
+ *   * - swallow remaining characters 
+ *  <x> - exact match for any other character
+ */
+API_EXPORT(int) ap_checkmask(const char *data, const char *mask)
+{
+    int i;
+    char d;
+
+    for (i = 0; i < 256; i++) {
+       d = data[i];
+       switch (mask[i]) {
+       case '\0':
+           return (d == '\0');
+
+       case '*':
+           return 1;
+
+       case '@':
+           if (!ap_isupper(d))
+               return 0;
+           break;
+       case '$':
+           if (!ap_islower(d))
+               return 0;
+           break;
+       case '#':
+           if (!ap_isdigit(d))
+               return 0;
+           break;
+       case '&':
+           if (!ap_isxdigit(d))
+               return 0;
+           break;
+       case '~':
+           if ((d != ' ') && !ap_isdigit(d))
+               return 0;
+           break;
+       default:
+           if (mask[i] != d)
+               return 0;
+           break;
+       }
+    }
+    return 0;                  /* We only get here if mask is corrupted (exceeds 256) */
+}
+
+/*
+ * tm2sec converts a GMT tm structure into the number of seconds since
+ * 1st January 1970 UT.  Note that we ignore tm_wday, tm_yday, and tm_dst.
+ * 
+ * The return value is always a valid time_t value -- (time_t)0 is returned
+ * if the input date is outside that capable of being represented by time(),
+ * i.e., before Thu, 01 Jan 1970 00:00:00 for all systems and 
+ * beyond 2038 for 32bit systems.
+ *
+ * This routine is intended to be very fast, much faster than mktime().
+ */
+API_EXPORT(time_t) ap_tm2sec(const struct tm * t)
+{
+    int year;
+    time_t days;
+    static const int dayoffset[12] =
+    {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275};
+
+    year = t->tm_year;
+
+    if (year < 70 || ((sizeof(time_t) <= 4) && (year >= 138)))
+       return BAD_DATE;
+
+    /* shift new year to 1st March in order to make leap year calc easy */
+
+    if (t->tm_mon < 2)
+       year--;
+
+    /* Find number of days since 1st March 1900 (in the Gregorian calendar). */
+
+    days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4;
+    days += dayoffset[t->tm_mon] + t->tm_mday - 1;
+    days -= 25508;             /* 1 jan 1970 is 25508 days since 1 mar 1900 */
+
+    days = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec;
+
+    if (days < 0)
+       return BAD_DATE;        /* must have overflowed */
+    else
+       return days;            /* must be a valid time */
+}
+
+/*
+ * Parses an HTTP date in one of three standard forms:
+ *
+ *     Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
+ *     Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ *     Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
+ *
+ * and returns the time_t number of seconds since 1 Jan 1970 GMT, or
+ * 0 if this would be out of range or if the date is invalid.
+ *
+ * The restricted HTTP syntax is
+ * 
+ *     HTTP-date    = rfc1123-date | rfc850-date | asctime-date
+ *
+ *     rfc1123-date = wkday "," SP date1 SP time SP "GMT"
+ *     rfc850-date  = weekday "," SP date2 SP time SP "GMT"
+ *     asctime-date = wkday SP date3 SP time SP 4DIGIT
+ *
+ *     date1        = 2DIGIT SP month SP 4DIGIT
+ *                    ; day month year (e.g., 02 Jun 1982)
+ *     date2        = 2DIGIT "-" month "-" 2DIGIT
+ *                    ; day-month-year (e.g., 02-Jun-82)
+ *     date3        = month SP ( 2DIGIT | ( SP 1DIGIT ))
+ *                    ; month day (e.g., Jun  2)
+ *
+ *     time         = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ *                    ; 00:00:00 - 23:59:59
+ *
+ *     wkday        = "Mon" | "Tue" | "Wed"
+ *                  | "Thu" | "Fri" | "Sat" | "Sun"
+ *
+ *     weekday      = "Monday" | "Tuesday" | "Wednesday"
+ *                  | "Thursday" | "Friday" | "Saturday" | "Sunday"
+ *
+ *     month        = "Jan" | "Feb" | "Mar" | "Apr"
+ *                  | "May" | "Jun" | "Jul" | "Aug"
+ *                  | "Sep" | "Oct" | "Nov" | "Dec"
+ *
+ * However, for the sake of robustness (and Netscapeness), we ignore the
+ * weekday and anything after the time field (including the timezone).
+ *
+ * This routine is intended to be very fast; 10x faster than using sscanf.
+ *
+ * Originally from Andrew Daviel <andrew@vancouver-webpages.com>, 29 Jul 96
+ * but many changes since then.
+ *
+ */
+API_EXPORT(time_t) ap_parseHTTPdate(const char *date)
+{
+    struct tm ds;
+    int mint, mon;
+    const char *monstr, *timstr;
+    static const int months[12] =
+    {
+       ('J' << 16) | ('a' << 8) | 'n', ('F' << 16) | ('e' << 8) | 'b',
+       ('M' << 16) | ('a' << 8) | 'r', ('A' << 16) | ('p' << 8) | 'r',
+       ('M' << 16) | ('a' << 8) | 'y', ('J' << 16) | ('u' << 8) | 'n',
+       ('J' << 16) | ('u' << 8) | 'l', ('A' << 16) | ('u' << 8) | 'g',
+       ('S' << 16) | ('e' << 8) | 'p', ('O' << 16) | ('c' << 8) | 't',
+       ('N' << 16) | ('o' << 8) | 'v', ('D' << 16) | ('e' << 8) | 'c'};
+
+    if (!date)
+       return BAD_DATE;
+
+    while (*date && ap_isspace(*date)) /* Find first non-whitespace char */
+       ++date;
+
+    if (*date == '\0')
+       return BAD_DATE;
+
+    if ((date = strchr(date, ' ')) == NULL)    /* Find space after weekday */
+       return BAD_DATE;
+
+    ++date;                    /* Now pointing to first char after space, which should be */
+    /* start of the actual date information for all 3 formats. */
+
+    if (ap_checkmask(date, "## @$$ #### ##:##:## *")) {        /* RFC 1123 format */
+       ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100;
+       if (ds.tm_year < 0)
+           return BAD_DATE;
+
+       ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0');
+
+       ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
+
+       monstr = date + 3;
+       timstr = date + 12;
+    }
+    else if (ap_checkmask(date, "##-@$$-## ##:##:## *")) {             /* RFC 850 format  */
+       ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
+       if (ds.tm_year < 70)
+           ds.tm_year += 100;
+
+       ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
+
+       monstr = date + 3;
+       timstr = date + 10;
+    }
+    else if (ap_checkmask(date, "@$$ ~# ##:##:## ####*")) {    /* asctime format  */
+       ds.tm_year = ((date[16] - '0') * 10 + (date[17] - '0') - 19) * 100;
+       if (ds.tm_year < 0)
+           return BAD_DATE;
+
+       ds.tm_year += ((date[18] - '0') * 10) + (date[19] - '0');
+
+       if (date[4] == ' ')
+           ds.tm_mday = 0;
+       else
+           ds.tm_mday = (date[4] - '0') * 10;
+
+       ds.tm_mday += (date[5] - '0');
+
+       monstr = date;
+       timstr = date + 7;
+    }
+    else
+       return BAD_DATE;
+
+    if (ds.tm_mday <= 0 || ds.tm_mday > 31)
+       return BAD_DATE;
+
+    ds.tm_hour = ((timstr[0] - '0') * 10) + (timstr[1] - '0');
+    ds.tm_min = ((timstr[3] - '0') * 10) + (timstr[4] - '0');
+    ds.tm_sec = ((timstr[6] - '0') * 10) + (timstr[7] - '0');
+
+    if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61))
+       return BAD_DATE;
+
+    mint = (monstr[0] << 16) | (monstr[1] << 8) | monstr[2];
+    for (mon = 0; mon < 12; mon++)
+       if (mint == months[mon])
+           break;
+    if (mon == 12)
+       return BAD_DATE;
+
+    if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10))
+       return BAD_DATE;
+
+    /* February gets special check for leapyear */
+
+    if ((mon == 1) &&
+       ((ds.tm_mday > 29)
+        || ((ds.tm_mday == 29)
+            && ((ds.tm_year & 3)
+                || (((ds.tm_year % 100) == 0)
+                    && (((ds.tm_year % 400) != 100)))))))
+       return BAD_DATE;
+
+    ds.tm_mon = mon;
+
+    return ap_tm2sec(&ds);
+}
diff --git a/server/util_md5.c b/server/util_md5.c
new file mode 100644 (file)
index 0000000..368ddf6
--- /dev/null
@@ -0,0 +1,229 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/************************************************************************
+ * NCSA HTTPd Server
+ * Software Development Group
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * 605 E. Springfield, Champaign, IL 61820
+ * httpd@ncsa.uiuc.edu
+ *
+ * Copyright  (C)  1995, Board of Trustees of the University of Illinois
+ *
+ ************************************************************************
+ *
+ * md5.c: NCSA HTTPd code which uses the md5c.c RSA Code
+ *
+ *  Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc.
+ *  Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon
+ *     University (see Copyright below).
+ *  Portions of Content-MD5 code Copyright (C) 1991 Bell Communications 
+ *     Research, Inc. (Bellcore) (see Copyright below).
+ *  Portions extracted from mpack, John G. Myers - jgm+@cmu.edu
+ *  Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk)
+ *
+ */
+
+
+
+/* md5.c --Module Interface to MD5. */
+/* Jeff Hostetler, Spyglass, Inc., 1994. */
+
+#include "httpd.h"
+#include "util_md5.h"
+
+API_EXPORT(char *) ap_md5_binary(pool *p, const unsigned char *buf, int length)
+{
+    const char *hex = "0123456789abcdef";
+    AP_MD5_CTX my_md5;
+    unsigned char hash[16];
+    char *r, result[33];
+    int i;
+
+    /*
+     * Take the MD5 hash of the string argument.
+     */
+
+    ap_MD5Init(&my_md5);
+    ap_MD5Update(&my_md5, buf, (unsigned int)length);
+    ap_MD5Final(hash, &my_md5);
+
+    for (i = 0, r = result; i < 16; i++) {
+       *r++ = hex[hash[i] >> 4];
+       *r++ = hex[hash[i] & 0xF];
+    }
+    *r = '\0';
+
+    return ap_pstrdup(p, result);
+}
+
+API_EXPORT(char *) ap_md5(pool *p, const unsigned char *string)
+{
+    return ap_md5_binary(p, string, (int) strlen((char *)string));
+}
+
+/* these portions extracted from mpack, John G. Myers - jgm+@cmu.edu */
+
+/* (C) Copyright 1993,1994 by Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Carnegie
+ * Mellon University not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  Carnegie Mellon University makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
+ *
+ * Permission to use, copy, modify, and distribute this material
+ * for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice
+ * appear in all copies, and that the name of Bellcore not be
+ * used in advertising or publicity pertaining to this
+ * material without the specific, prior written permission
+ * of an authorized representative of Bellcore.  BELLCORE
+ * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
+ * OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.  
+ */
+
+static char basis_64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+API_EXPORT(char *) ap_md5contextTo64(pool *a, AP_MD5_CTX * context)
+{
+    unsigned char digest[18];
+    char *encodedDigest;
+    int i;
+    char *p;
+
+    encodedDigest = (char *) ap_pcalloc(a, 25 * sizeof(char));
+
+    ap_MD5Final(digest, context);
+    digest[sizeof(digest) - 1] = digest[sizeof(digest) - 2] = 0;
+
+    p = encodedDigest;
+    for (i = 0; i < sizeof(digest); i += 3) {
+       *p++ = basis_64[digest[i] >> 2];
+       *p++ = basis_64[((digest[i] & 0x3) << 4) | ((int) (digest[i + 1] & 0xF0) >> 4)];
+       *p++ = basis_64[((digest[i + 1] & 0xF) << 2) | ((int) (digest[i + 2] & 0xC0) >> 6)];
+       *p++ = basis_64[digest[i + 2] & 0x3F];
+    }
+    *p-- = '\0';
+    *p-- = '=';
+    *p-- = '=';
+    return encodedDigest;
+}
+
+#ifdef CHARSET_EBCDIC
+
+API_EXPORT(char *) ap_md5digest(pool *p, FILE *infile, int convert)
+{
+    AP_MD5_CTX context;
+    unsigned char buf[1000];
+    long length = 0;
+    int nbytes;
+
+    ap_MD5Init(&context);
+    while ((nbytes = fread(buf, 1, sizeof(buf), infile))) {
+      length += nbytes;
+        if (!convert) {
+            ascii2ebcdic(buf, buf, nbytes);
+        }
+      ap_MD5Update(&context, buf, nbytes);
+    }
+    rewind(infile);
+    return ap_md5contextTo64(p, &context);
+}
+
+#else
+
+API_EXPORT(char *) ap_md5digest(pool *p, FILE *infile)
+{
+    AP_MD5_CTX context;
+    unsigned char buf[1000];
+    long length = 0;
+    unsigned int nbytes;
+
+    ap_MD5Init(&context);
+    while ((nbytes = fread(buf, 1, sizeof(buf), infile))) {
+       length += nbytes;
+       ap_MD5Update(&context, buf, nbytes);
+    }
+    rewind(infile);
+    return ap_md5contextTo64(p, &context);
+}
+
+#endif /* CHARSET_EBCDIC */
diff --git a/server/util_script.c b/server/util_script.c
new file mode 100644 (file)
index 0000000..467dd29
--- /dev/null
@@ -0,0 +1,1138 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_conf_globals.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_core.h"         /* For document_root.  Sigh... */
+#include "http_request.h"      /* for sub_req_lookup_uri() */
+#include "util_script.h"
+#include "util_date.h"         /* For parseHTTPdate() */
+
+#ifdef OS2
+#define INCL_DOS
+#include <os2.h>
+#endif
+
+/*
+ * Various utility functions which are common to a whole lot of
+ * script-type extensions mechanisms, and might as well be gathered
+ * in one place (if only to avoid creating inter-module dependancies
+ * where there don't have to be).
+ */
+
+#define MALFORMED_MESSAGE "malformed header from script. Bad header="
+#define MALFORMED_HEADER_LENGTH_TO_SHOW 30
+
+/* If a request includes query info in the URL (stuff after "?"), and
+ * the query info does not contain "=" (indicative of a FORM submission),
+ * then this routine is called to create the argument list to be passed
+ * to the CGI script.  When suexec is enabled, the suexec path, user, and
+ * group are the first three arguments to be passed; if not, all three
+ * must be NULL.  The query info is split into separate arguments, where
+ * "+" is the separator between keyword arguments.
+ *
+ * XXXX: note that the WIN32 code uses one of the suexec strings 
+ * to pass an interpreter name.  Remember this if changing the way they
+ * are handled in create_argv.
+ *
+ */
+static char **create_argv(pool *p, char *path, char *user, char *group,
+                         char *av0, const char *args)
+{
+    int x, numwords;
+    char **av;
+    char *w;
+    int idx = 0;
+
+    /* count the number of keywords */
+
+    for (x = 0, numwords = 1; args[x]; x++) {
+        if (args[x] == '+') {
+           ++numwords;
+       }
+    }
+
+    if (numwords > APACHE_ARG_MAX - 5) {
+       numwords = APACHE_ARG_MAX - 5;  /* Truncate args to prevent overrun */
+    }
+    av = (char **) ap_palloc(p, (numwords + 5) * sizeof(char *));
+
+    if (path) {
+       av[idx++] = path;
+    }
+    if (user) {
+       av[idx++] = user;
+    }
+    if (group) {
+       av[idx++] = group;
+    }
+
+    av[idx++] = av0;
+
+    for (x = 1; x <= numwords; x++) {
+       w = ap_getword_nulls(p, &args, '+');
+       ap_unescape_url(w);
+       av[idx++] = ap_escape_shell_cmd(p, w);
+    }
+    av[idx] = NULL;
+    return av;
+}
+
+
+static char *http2env(pool *a, char *w)
+{
+    char *res = ap_pstrcat(a, "HTTP_", w, NULL);
+    char *cp = res;
+
+    while (*++cp) {
+       if (!ap_isalnum(*cp) && *cp != '_') {
+           *cp = '_';
+       }
+       else {
+           *cp = ap_toupper(*cp);
+       }
+    }
+
+    return res;
+}
+
+API_EXPORT(char **) ap_create_environment(pool *p, table *t)
+{
+    array_header *env_arr = ap_table_elts(t);
+    table_entry *elts = (table_entry *) env_arr->elts;
+    char **env = (char **) ap_palloc(p, (env_arr->nelts + 2) * sizeof(char *));
+    int i, j;
+    char *tz;
+    char *whack;
+
+    j = 0;
+    if (!ap_table_get(t, "TZ")) {
+       tz = getenv("TZ");
+       if (tz != NULL) {
+           env[j++] = ap_pstrcat(p, "TZ=", tz, NULL);
+       }
+    }
+    for (i = 0; i < env_arr->nelts; ++i) {
+        if (!elts[i].key) {
+           continue;
+       }
+       env[j] = ap_pstrcat(p, elts[i].key, "=", elts[i].val, NULL);
+       whack = env[j];
+       if (ap_isdigit(*whack)) {
+           *whack++ = '_';
+       }
+       while (*whack != '=') {
+           if (!ap_isalnum(*whack) && *whack != '_') {
+               *whack = '_';
+           }
+           ++whack;
+       }
+       ++j;
+    }
+
+    env[j] = NULL;
+    return env;
+}
+
+API_EXPORT(void) ap_add_common_vars(request_rec *r)
+{
+    table *e;
+    server_rec *s = r->server;
+    conn_rec *c = r->connection;
+    const char *rem_logname;
+    char *env_path;
+#ifdef WIN32
+    char *env_temp;
+#endif
+    const char *host;
+    array_header *hdrs_arr = ap_table_elts(r->headers_in);
+    table_entry *hdrs = (table_entry *) hdrs_arr->elts;
+    int i;
+
+    /* use a temporary table which we'll overlap onto
+     * r->subprocess_env later
+     */
+    e = ap_make_table(r->pool, 25 + hdrs_arr->nelts);
+
+    /* First, add environment vars from headers... this is as per
+     * CGI specs, though other sorts of scripting interfaces see
+     * the same vars...
+     */
+
+    for (i = 0; i < hdrs_arr->nelts; ++i) {
+        if (!hdrs[i].key) {
+           continue;
+       }
+
+       /* A few headers are special cased --- Authorization to prevent
+        * rogue scripts from capturing passwords; content-type and -length
+        * for no particular reason.
+        */
+
+       if (!strcasecmp(hdrs[i].key, "Content-type")) {
+           ap_table_addn(e, "CONTENT_TYPE", hdrs[i].val);
+       }
+       else if (!strcasecmp(hdrs[i].key, "Content-length")) {
+           ap_table_addn(e, "CONTENT_LENGTH", hdrs[i].val);
+       }
+       /*
+        * You really don't want to disable this check, since it leaves you
+        * wide open to CGIs stealing passwords and people viewing them
+        * in the environment with "ps -e".  But, if you must...
+        */
+#ifndef SECURITY_HOLE_PASS_AUTHORIZATION
+       else if (!strcasecmp(hdrs[i].key, "Authorization") 
+                || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) {
+           continue;
+       }
+#endif
+       else {
+           ap_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val);
+       }
+    }
+
+    if (!(env_path = getenv("PATH"))) {
+       env_path = DEFAULT_PATH;
+    }
+
+#ifdef WIN32
+    if (env_temp = getenv("SystemRoot")) {
+        ap_table_addn(e, "SystemRoot", env_temp);         
+    }
+    if (env_temp = getenv("COMSPEC")) {
+        ap_table_addn(e, "COMSPEC", env_temp);            
+    }
+    if (env_temp = getenv("WINDIR")) {
+        ap_table_addn(e, "WINDIR", env_temp);
+    }
+#endif
+
+    ap_table_addn(e, "PATH", env_path);
+    ap_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r));
+    ap_table_addn(e, "SERVER_SOFTWARE", ap_get_server_version());
+    ap_table_addn(e, "SERVER_NAME", ap_get_server_name(r));
+    ap_table_addn(e, "SERVER_ADDR", r->connection->local_ip);  /* Apache */
+    ap_table_addn(e, "SERVER_PORT",
+                 ap_psprintf(r->pool, "%u", ap_get_server_port(r)));
+    host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST);
+    if (host) {
+       ap_table_addn(e, "REMOTE_HOST", host);
+    }
+    ap_table_addn(e, "REMOTE_ADDR", c->remote_ip);
+    ap_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r));    /* Apache */
+    ap_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */
+    ap_table_addn(e, "SCRIPT_FILENAME", r->filename);  /* Apache */
+
+    ap_table_addn(e, "REMOTE_PORT",
+                 ap_psprintf(r->pool, "%d", ntohs(c->remote_addr.sin_port)));
+
+    if (c->user) {
+       ap_table_addn(e, "REMOTE_USER", c->user);
+    }
+    if (c->ap_auth_type) {
+       ap_table_addn(e, "AUTH_TYPE", c->ap_auth_type);
+    }
+    rem_logname = ap_get_remote_logname(r);
+    if (rem_logname) {
+       ap_table_addn(e, "REMOTE_IDENT", ap_pstrdup(r->pool, rem_logname));
+    }
+
+    /* Apache custom error responses. If we have redirected set two new vars */
+
+    if (r->prev) {
+        if (r->prev->args) {
+           ap_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args);
+       }
+       if (r->prev->uri) {
+           ap_table_addn(e, "REDIRECT_URL", r->prev->uri);
+       }
+    }
+
+    ap_overlap_tables(r->subprocess_env, e, AP_OVERLAP_TABLES_SET);
+}
+
+/* This "cute" little function comes about because the path info on
+ * filenames and URLs aren't always the same. So we take the two,
+ * and find as much of the two that match as possible.
+ */
+
+API_EXPORT(int) ap_find_path_info(const char *uri, const char *path_info)
+{
+    int lu = strlen(uri);
+    int lp = strlen(path_info);
+
+    while (lu-- && lp-- && uri[lu] == path_info[lp]);
+
+    if (lu == -1) {
+       lu = 0;
+    }
+
+    while (uri[lu] != '\0' && uri[lu] != '/') {
+        lu++;
+    }
+    return lu;
+}
+
+/* Obtain the Request-URI from the original request-line, returning
+ * a new string from the request pool containing the URI or "".
+ */
+static char *original_uri(request_rec *r)
+{
+    char *first, *last;
+
+    if (r->the_request == NULL) {
+       return (char *) ap_pcalloc(r->pool, 1);
+    }
+
+    first = r->the_request;    /* use the request-line */
+
+    while (*first && !ap_isspace(*first)) {
+       ++first;                /* skip over the method */
+    }
+    while (ap_isspace(*first)) {
+       ++first;                /*   and the space(s)   */
+    }
+
+    last = first;
+    while (*last && !ap_isspace(*last)) {
+       ++last;                 /* end at next whitespace */
+    }
+
+    return ap_pstrndup(r->pool, first, last - first);
+}
+
+API_EXPORT(void) ap_add_cgi_vars(request_rec *r)
+{
+    table *e = r->subprocess_env;
+
+    ap_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1");
+    ap_table_setn(e, "SERVER_PROTOCOL", r->protocol);
+    ap_table_setn(e, "REQUEST_METHOD", r->method);
+    ap_table_setn(e, "QUERY_STRING", r->args ? r->args : "");
+    ap_table_setn(e, "REQUEST_URI", original_uri(r));
+
+    /* Note that the code below special-cases scripts run from includes,
+     * because it "knows" that the sub_request has been hacked to have the
+     * args and path_info of the original request, and not any that may have
+     * come with the script URI in the include command.  Ugh.
+     */
+
+    if (!strcmp(r->protocol, "INCLUDED")) {
+       ap_table_setn(e, "SCRIPT_NAME", r->uri);
+       if (r->path_info && *r->path_info) {
+           ap_table_setn(e, "PATH_INFO", r->path_info);
+       }
+    }
+    else if (!r->path_info || !*r->path_info) {
+       ap_table_setn(e, "SCRIPT_NAME", r->uri);
+    }
+    else {
+       int path_info_start = ap_find_path_info(r->uri, r->path_info);
+
+       ap_table_setn(e, "SCRIPT_NAME",
+                     ap_pstrndup(r->pool, r->uri, path_info_start));
+
+       ap_table_setn(e, "PATH_INFO", r->path_info);
+    }
+
+    if (r->path_info && r->path_info[0]) {
+       /*
+        * To get PATH_TRANSLATED, treat PATH_INFO as a URI path.
+        * Need to re-escape it for this, since the entire URI was
+        * un-escaped before we determined where the PATH_INFO began.
+        */
+       request_rec *pa_req;
+
+       pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r);
+
+       if (pa_req->filename) {
+#ifdef WIN32
+           char buffer[HUGE_STRING_LEN];
+#endif
+           char *pt = ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info,
+                                 NULL);
+#ifdef WIN32
+           /* We need to make this a real Windows path name */
+           GetFullPathName(pt, HUGE_STRING_LEN, buffer, NULL);
+           ap_table_setn(e, "PATH_TRANSLATED", ap_pstrdup(r->pool, buffer));
+#else
+           ap_table_setn(e, "PATH_TRANSLATED", pt);
+#endif
+       }
+       ap_destroy_sub_req(pa_req);
+    }
+}
+
+
+static int set_cookie_doo_doo(void *v, const char *key, const char *val)
+{
+    ap_table_addn(v, key, val);
+    return 1;
+}
+
+API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
+                                      int (*getsfunc) (char *, int, void *),
+                                      void *getsfunc_data)
+{
+    char x[MAX_STRING_LEN];
+    char *w, *l;
+    int p;
+    int cgi_status = HTTP_OK;
+    table *merge;
+    table *cookie_table;
+
+    if (buffer) {
+       *buffer = '\0';
+    }
+    w = buffer ? buffer : x;
+
+    ap_hard_timeout("read script header", r);
+
+    /* temporary place to hold headers to merge in later */
+    merge = ap_make_table(r->pool, 10);
+
+    /* The HTTP specification says that it is legal to merge duplicate
+     * headers into one.  Some browsers that support Cookies don't like
+     * merged headers and prefer that each Set-Cookie header is sent
+     * separately.  Lets humour those browsers by not merging.
+     * Oh what a pain it is.
+     */
+    cookie_table = ap_make_table(r->pool, 2);
+    ap_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL);
+
+    while (1) {
+
+       if ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data) == 0) {
+           ap_kill_timeout(r);
+           ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                         "Premature end of script headers: %s", r->filename);
+           return HTTP_INTERNAL_SERVER_ERROR;
+       }
+
+       /* Delete terminal (CR?)LF */
+
+       p = strlen(w);
+       if (p > 0 && w[p - 1] == '\n') {
+           if (p > 1 && w[p - 2] == '\015') {
+               w[p - 2] = '\0';
+           }
+           else {
+               w[p - 1] = '\0';
+           }
+       }
+
+       /*
+        * If we've finished reading the headers, check to make sure any
+        * HTTP/1.1 conditions are met.  If so, we're done; normal processing
+        * will handle the script's output.  If not, just return the error.
+        * The appropriate thing to do would be to send the script process a
+        * SIGPIPE to let it know we're ignoring it, close the channel to the
+        * script process, and *then* return the failed-to-meet-condition
+        * error.  Otherwise we'd be waiting for the script to finish
+        * blithering before telling the client the output was no good.
+        * However, we don't have the information to do that, so we have to
+        * leave it to an upper layer.
+        */
+       if (w[0] == '\0') {
+           int cond_status = OK;
+
+           ap_kill_timeout(r);
+           if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) {
+               cond_status = ap_meets_conditions(r);
+           }
+           ap_overlap_tables(r->err_headers_out, merge,
+               AP_OVERLAP_TABLES_MERGE);
+           if (!ap_is_empty_table(cookie_table)) {
+               /* the cookies have already been copied to the cookie_table */
+               ap_table_unset(r->err_headers_out, "Set-Cookie");
+               r->err_headers_out = ap_overlay_tables(r->pool,
+                   r->err_headers_out, cookie_table);
+           }
+           return cond_status;
+       }
+
+       /* if we see a bogus header don't ignore it. Shout and scream */
+
+#ifdef CHARSET_EBCDIC
+           /* Chances are that we received an ASCII header text instead of
+            * the expected EBCDIC header lines. Try to auto-detect:
+            */
+       if (!(l = strchr(w, ':'))) {
+           int maybeASCII = 0, maybeEBCDIC = 0;
+           char *cp;
+
+           for (cp = w; *cp != '\0'; ++cp) {
+               if (isprint(*cp) && !isprint(os_toebcdic[*cp]))
+                   ++maybeEBCDIC;
+               if (!isprint(*cp) && isprint(os_toebcdic[*cp]))
+                   ++maybeASCII;
+               }
+           if (maybeASCII > maybeEBCDIC) {
+               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
+                        "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)", r->filename);
+               ascii2ebcdic(w, w, cp - w);
+           }
+       }
+#endif
+       if (!(l = strchr(w, ':'))) {
+           char malformed[(sizeof MALFORMED_MESSAGE) + 1
+                          + MALFORMED_HEADER_LENGTH_TO_SHOW];
+
+           strcpy(malformed, MALFORMED_MESSAGE);
+           strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW);
+
+           if (!buffer) {
+               /* Soak up all the script output - may save an outright kill */
+               while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) {
+                   continue;
+               }
+           }
+
+           ap_kill_timeout(r);
+           ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+                         "%s: %s", malformed, r->filename);
+           return HTTP_INTERNAL_SERVER_ERROR;
+       }
+
+       *l++ = '\0';
+       while (*l && ap_isspace(*l)) {
+           ++l;
+       }
+
+       if (!strcasecmp(w, "Content-type")) {
+           char *tmp;
+
+           /* Nuke trailing whitespace */
+
+           char *endp = l + strlen(l) - 1;
+           while (endp > l && ap_isspace(*endp)) {
+               *endp-- = '\0';
+           }
+
+           tmp = ap_pstrdup(r->pool, l);
+           ap_content_type_tolower(tmp);
+           r->content_type = tmp;
+       }
+       /*
+        * If the script returned a specific status, that's what
+        * we'll use - otherwise we assume 200 OK.
+        */
+       else if (!strcasecmp(w, "Status")) {
+           r->status = cgi_status = atoi(l);
+           r->status_line = ap_pstrdup(r->pool, l);
+       }
+       else if (!strcasecmp(w, "Location")) {
+           ap_table_set(r->headers_out, w, l);
+       }
+       else if (!strcasecmp(w, "Content-Length")) {
+           ap_table_set(r->headers_out, w, l);
+       }
+       else if (!strcasecmp(w, "Transfer-Encoding")) {
+           ap_table_set(r->headers_out, w, l);
+       }
+       /*
+        * If the script gave us a Last-Modified header, we can't just
+        * pass it on blindly because of restrictions on future values.
+        */
+       else if (!strcasecmp(w, "Last-Modified")) {
+           time_t mtime = ap_parseHTTPdate(l);
+
+           ap_update_mtime(r, mtime);
+           ap_set_last_modified(r);
+       }
+       else if (!strcasecmp(w, "Set-Cookie")) {
+           ap_table_add(cookie_table, w, l);
+       }
+       else {
+           ap_table_add(merge, w, l);
+       }
+    }
+}
+
+static int getsfunc_FILE(char *buf, int len, void *f)
+{
+    return fgets(buf, len, (FILE *) f) != NULL;
+}
+
+API_EXPORT(int) ap_scan_script_header_err(request_rec *r, FILE *f,
+                                         char *buffer)
+{
+    return ap_scan_script_header_err_core(r, buffer, getsfunc_FILE, f);
+}
+
+static int getsfunc_BUFF(char *w, int len, void *fb)
+{
+    return ap_bgets(w, len, (BUFF *) fb) > 0;
+}
+
+API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *fb,
+                                              char *buffer)
+{
+    return ap_scan_script_header_err_core(r, buffer, getsfunc_BUFF, fb);
+}
+
+
+API_EXPORT(void) ap_send_size(size_t size, request_rec *r)
+{
+    /* XXX: this -1 thing is a gross hack */
+    if (size == (size_t)-1) {
+       ap_rputs("    -", r);
+    }
+    else if (!size) {
+       ap_rputs("   0k", r);
+    }
+    else if (size < 1024) {
+       ap_rputs("   1k", r);
+    }
+    else if (size < 1048576) {
+       ap_rprintf(r, "%4dk", (size + 512) / 1024);
+    }
+    else if (size < 103809024) {
+       ap_rprintf(r, "%4.1fM", size / 1048576.0);
+    }
+    else {
+       ap_rprintf(r, "%4dM", (size + 524288) / 1048576);
+    }
+}
+
+#if defined(OS2) || defined(WIN32)
+static char **create_argv_cmd(pool *p, char *av0, const char *args, char *path)
+{
+    register int x, n;
+    char **av;
+    char *w;
+
+    for (x = 0, n = 2; args[x]; x++) {
+        if (args[x] == '+') {
+           ++n;
+       }
+    }
+
+    /* Add extra strings to array. */
+    n = n + 2;
+
+    av = (char **) ap_palloc(p, (n + 1) * sizeof(char *));
+    av[0] = av0;
+
+    /* Now insert the extra strings we made room for above. */
+    av[1] = strdup("/C");
+    av[2] = strdup(path);
+
+    for (x = (1 + 2); x < n; x++) {
+       w = ap_getword(p, &args, '+');
+       ap_unescape_url(w);
+       av[x] = ap_escape_shell_cmd(p, w);
+    }
+    av[n] = NULL;
+    return av;
+}
+#endif
+
+
+API_EXPORT(int) ap_call_exec(request_rec *r, child_info *pinfo, char *argv0,
+                            char **env, int shellcmd)
+{
+    int pid = 0;
+#if defined(RLIMIT_CPU)  || defined(RLIMIT_NPROC) || \
+    defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
+
+    core_dir_config *conf;
+    conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,
+                                                   &core_module);
+
+#endif
+
+#if !defined(WIN32) && !defined(OS2)
+    /* the fd on r->server->error_log is closed, but we need somewhere to
+     * put the error messages from the log_* functions. So, we use stderr,
+     * since that is better than allowing errors to go unnoticed.  Don't do
+     * this on Win32, though, since we haven't fork()'d.
+     */
+    r->server->error_log = stderr;
+#endif
+
+#ifdef RLIMIT_CPU
+    if (conf->limit_cpu != NULL) {
+        if ((setrlimit(RLIMIT_CPU, conf->limit_cpu)) != 0) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                        "setrlimit: failed to set CPU usage limit");
+       }
+    }
+#endif
+#ifdef RLIMIT_NPROC
+    if (conf->limit_nproc != NULL) {
+        if ((setrlimit(RLIMIT_NPROC, conf->limit_nproc)) != 0) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                        "setrlimit: failed to set process limit");
+       }
+    }
+#endif
+#if defined(RLIMIT_AS)
+    if (conf->limit_mem != NULL) {
+        if ((setrlimit(RLIMIT_AS, conf->limit_mem)) != 0) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                        "setrlimit(RLIMIT_AS): failed to set memory "
+                        "usage limit");
+       }
+    }
+#elif defined(RLIMIT_DATA)
+    if (conf->limit_mem != NULL) {
+        if ((setrlimit(RLIMIT_DATA, conf->limit_mem)) != 0) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                        "setrlimit(RLIMIT_DATA): failed to set memory "
+                        "usage limit");
+       }
+    }
+#elif defined(RLIMIT_VMEM)
+    if (conf->limit_mem != NULL) {
+        if ((setrlimit(RLIMIT_VMEM, conf->limit_mem)) != 0) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+                        "setrlimit(RLIMIT_VMEM): failed to set memory "
+                        "usage limit");
+       }
+    }
+#endif
+
+#ifdef OS2
+    {
+       /* Additions by Alec Kloss, to allow exec'ing of scripts under OS/2 */
+       int is_script = 0;
+       char interpreter[2048]; /* hope it's enough for the interpreter path */
+       char error_object[260];
+       FILE *program;
+        char *cmdline = r->filename, *cmdline_pos;
+        int cmdlen;
+       char *args = "", *args_end;
+       ULONG rc;
+        RESULTCODES rescodes;
+        int env_len, e;
+        char *env_block, *env_block_pos;
+
+       if (r->args && r->args[0] && !strchr(r->args, '='))
+           args = r->args;
+           
+       program = fopen(r->filename, "rt");
+       
+       if (!program) {
+           ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "fopen(%s) failed",
+                        r->filename);
+           return (pid);
+       }
+       
+       fgets(interpreter, sizeof(interpreter), program);
+       fclose(program);
+       
+       if (!strncmp(interpreter, "#!", 2)) {
+           is_script = 1;
+            interpreter[strlen(interpreter) - 1] = '\0';
+            if (interpreter[2] != '/' && interpreter[2] != '\\' && interpreter[3] != ':') {
+                char buffer[300];
+                if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) {
+                    strcpy(interpreter+2, buffer);
+                } else {
+                    strcat(interpreter, ".exe");
+                    if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) {
+                        strcpy(interpreter+2, buffer);
+                    }
+                }
+            }
+       }
+
+        if (is_script) {
+            cmdline = ap_pstrcat(r->pool, interpreter+2, " ", r->filename, NULL);
+        }
+        else if (strstr(strupr(r->filename), ".CMD") > 0) {
+            /* Special case to allow use of REXX commands as scripts. */
+            os2pathname(r->filename);
+            cmdline = ap_pstrcat(r->pool, SHELL_PATH, " /C ", r->filename, NULL);
+        }
+        else {
+            cmdline = r->filename;
+       }
+       
+        args = ap_pstrdup(r->pool, args);
+        ap_unescape_url(args);
+        args = ap_double_quotes(r->pool, args);
+        args_end = args + strlen(args);
+
+        if (args_end - args > 4000) { /* cmd.exe won't handle lines longer than 4k */
+            args_end = args + 4000;
+            *args_end = 0;
+        }
+
+        /* +4 = 1 space between progname and args, 2 for double null at end, 2 for possible quote on first arg */
+        cmdlen = strlen(cmdline) + strlen(args) + 4; 
+        cmdline_pos = cmdline;
+
+        while (*cmdline_pos) {
+            cmdlen += 2 * (*cmdline_pos == '+');  /* Allow space for each arg to be quoted */
+            cmdline_pos++;
+        }
+
+        cmdline = ap_pstrndup(r->pool, cmdline, cmdlen);
+        cmdline_pos = cmdline + strlen(cmdline);
+
+       while (args < args_end) {
+            char *arg;
+           
+            arg = ap_getword_nc(r->pool, &args, '+');
+
+            if (strpbrk(arg, "&|<> "))
+                arg = ap_pstrcat(r->pool, "\"", arg, "\"", NULL);
+
+            *(cmdline_pos++) = ' ';
+            strcpy(cmdline_pos, arg);
+            cmdline_pos += strlen(cmdline_pos);
+        }
+
+        *(++cmdline_pos) = 0; /* Add required second terminator */
+       args = strchr(cmdline, ' ');
+       
+       if (args) {
+           *args = 0;
+           args++;
+       }
+
+        /* Create environment block from list of envariables */
+        for (env_len=1, e=0; env[e]; e++)
+            env_len += strlen(env[e]) + 1;
+
+        env_block = ap_palloc(r->pool, env_len);
+        env_block_pos = env_block;
+
+        for (e=0; env[e]; e++) {
+            strcpy(env_block_pos, env[e]);
+            env_block_pos += strlen(env_block_pos) + 1;
+        }
+
+        *env_block_pos = 0; /* environment block is terminated by a double null */
+
+       rc = DosExecPgm(error_object, sizeof(error_object), EXEC_ASYNC, cmdline, env_block, &rescodes, cmdline);
+       
+       if (rc) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "DosExecPgm(%s %s) failed, %s - %s",
+                          cmdline, args ? args : "", ap_os_error_message(rc), error_object );
+           return -1;
+       }
+       
+       return rescodes.codeTerminate;
+    }
+#elif defined(WIN32)
+    {
+        /* Adapted from Alec Kloss' work for OS/2 */
+        char *interpreter = NULL;
+        char *arguments = NULL;
+        char *ext = NULL;
+        char *exename = NULL;
+        char *s = NULL;
+        char *quoted_filename;
+        char *pCommand;
+        char *pEnvBlock, *pNext;
+
+        int i;
+        int iEnvBlockLen;
+
+        file_type_e fileType;
+
+        STARTUPINFO si;
+        PROCESS_INFORMATION pi;
+
+        memset(&si, 0, sizeof(si));
+        memset(&pi, 0, sizeof(pi));
+
+        pid = -1;
+
+        if (!shellcmd) {
+
+            fileType = ap_get_win32_interpreter(r, &interpreter);
+
+            if (fileType == eFileTypeUNKNOWN) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
+                              "%s is not executable; ensure interpreted scripts have "
+                              "\"#!\" first line", 
+                              r->filename);
+                return (pid);
+            }
+
+            /*
+             * Look at the arguments...
+             */
+            arguments = "";
+            if ((r->args) && (r->args[0]) && !strchr(r->args, '=')) { 
+                /* If we are in this leg, there are some other arguments
+                 * that we must include in the execution of the CGI.
+                 * Because CreateProcess is the way it is, we have to
+                 * create a command line like format for the execution
+                 * of the CGI.  This means we need to create on long
+                 * string with the executable and arguments.
+                 *
+                 * The arguments string comes in the request structure,
+                 * and each argument is separated by a '+'.  We'll replace
+                 * these pluses with spaces.
+                 */
+
+                int iStringSize = 0;
+                int x;
+           
+                /*
+                 *  Duplicate the request structure string so we don't change it.
+                 */                                   
+                arguments = ap_pstrdup(r->pool, r->args);
+                
+                /*
+                 *  Change the '+' to ' '
+                 */
+                for (x=0; arguments[x]; x++) {
+                    if ('+' == arguments[x]) {
+                        arguments[x] = ' ';
+                    }
+                }
+       
+                /*
+                 * We need to unescape any characters that are 
+                 * in the arguments list.
+                 */
+                ap_unescape_url(arguments);
+                arguments = ap_escape_shell_cmd(r->pool, arguments);
+            }
+
+            /*
+             * We have the interpreter (if there is one) and we have 
+             * the arguments (if there are any).
+             * Build the command string to pass to CreateProcess. 
+             */
+            quoted_filename = ap_pstrcat(r->pool, "\"", r->filename, "\"", NULL);
+            if (interpreter && *interpreter) {
+                pCommand = ap_pstrcat(r->pool, interpreter, " ", 
+                                      quoted_filename, " ", arguments, NULL);
+            }
+            else {
+                pCommand = ap_pstrcat(r->pool, quoted_filename, " ", arguments, NULL);
+            }
+
+         } else {
+
+            char *shell_cmd = "CMD.EXE /C ";
+            OSVERSIONINFO osver;
+            osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+         
+            /*
+             * Use CMD.EXE for NT, COMMAND.COM for WIN95
+             */
+            if (GetVersionEx(&osver)) {
+                if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT) {
+                    shell_cmd = "COMMAND.COM /C ";
+                }
+            }       
+            pCommand = ap_pstrcat(r->pool, shell_cmd, argv0, NULL);
+        }
+
+        /*
+         * Make child process use hPipeOutputWrite as standard out,
+         * and make sure it does not show on screen.
+         */
+        si.cb = sizeof(si);
+        si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+        si.wShowWindow = SW_HIDE;
+        si.hStdInput   = pinfo->hPipeInputRead;
+        si.hStdOutput  = pinfo->hPipeOutputWrite;
+        si.hStdError   = pinfo->hPipeErrorWrite;
+  
+        /*
+         * Win32's CreateProcess call requires that the environment
+         * be passed in an environment block, a null terminated block of
+         * null terminated strings.
+         */  
+        i = 0;
+        iEnvBlockLen = 1;
+        while (env[i]) {
+            iEnvBlockLen += strlen(env[i]) + 1;
+            i++;
+        }
+  
+        pEnvBlock = (char *)ap_pcalloc(r->pool,iEnvBlockLen);
+    
+        i = 0;
+        pNext = pEnvBlock;
+        while (env[i]) {
+            strcpy(pNext, env[i]);
+            pNext = pNext + strlen(pNext) + 1;
+            i++;
+        }
+
+        if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, DETACHED_PROCESS, pEnvBlock,
+                          ap_make_dirstr_parent(r->pool, r->filename),
+                          &si, &pi)) {
+            if (fileType == eFileTypeEXE16) {
+                /* Hack to get 16-bit CGI's working. It works for all the 
+                 * standard modules shipped with Apache. pi.dwProcessId is 0 
+                 * for 16-bit CGIs and all the Unix specific code that calls 
+                 * ap_call_exec interprets this as a failure case. And we can't 
+                 * use -1 either because it is mapped to 0 by the caller.
+                 */
+                pid = -2;
+            }
+            else {
+                pid = pi.dwProcessId;
+                /*
+                 * We must close the handles to the new process and its main thread
+                 * to prevent handle and memory leaks.
+                 */ 
+                CloseHandle(pi.hProcess);
+                CloseHandle(pi.hThread);
+            }
+        }
+        return (pid);
+    }
+
+#else
+    if (ap_suexec_enabled
+       && ((r->server->server_uid != ap_user_id)
+           || (r->server->server_gid != ap_group_id)
+           || (!strncmp("/~", r->uri, 2)))) {
+
+       char *execuser, *grpname;
+       struct passwd *pw;
+       struct group *gr;
+
+       if (!strncmp("/~", r->uri, 2)) {
+           gid_t user_gid;
+           char *username = ap_pstrdup(r->pool, r->uri + 2);
+           char *pos = strchr(username, '/');
+
+           if (pos) {
+               *pos = '\0';
+           }
+
+           if ((pw = getpwnam(username)) == NULL) {
+               ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                            "getpwnam: invalid username %s", username);
+               return (pid);
+           }
+           execuser = ap_pstrcat(r->pool, "~", pw->pw_name, NULL);
+           user_gid = pw->pw_gid;
+
+           if ((gr = getgrgid(user_gid)) == NULL) {
+               if ((grpname = ap_palloc(r->pool, 16)) == NULL) {
+                   return (pid);
+               }
+               else {
+                   ap_snprintf(grpname, 16, "%ld", (long) user_gid);
+               }
+           }
+           else {
+               grpname = gr->gr_name;
+           }
+       }
+       else {
+           if ((pw = getpwuid(r->server->server_uid)) == NULL) {
+               ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                            "getpwuid: invalid userid %ld",
+                            (long) r->server->server_uid);
+               return (pid);
+           }
+           execuser = ap_pstrdup(r->pool, pw->pw_name);
+
+           if ((gr = getgrgid(r->server->server_gid)) == NULL) {
+               ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                            "getgrgid: invalid groupid %ld",
+                            (long) r->server->server_gid);
+               return (pid);
+           }
+           grpname = gr->gr_name;
+       }
+
+       if (shellcmd) {
+           execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
+                  NULL, env);
+       }
+
+       else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
+           execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
+                  NULL, env);
+       }
+
+       else {
+           execve(SUEXEC_BIN,
+                  create_argv(r->pool, SUEXEC_BIN, execuser, grpname,
+                              argv0, r->args),
+                  env);
+       }
+    }
+    else {
+        if (shellcmd) {
+           execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
+       }
+
+       else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
+           execle(r->filename, argv0, NULL, env);
+       }
+
+       else {
+           execve(r->filename,
+                  create_argv(r->pool, NULL, NULL, NULL, argv0, r->args),
+                  env);
+       }
+    }
+    return (pid);
+#endif
+}
diff --git a/server/util_uri.c b/server/util_uri.c
new file mode 100644 (file)
index 0000000..10eb5dc
--- /dev/null
@@ -0,0 +1,599 @@
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * util_uri.c: URI related utility things
+ * 
+ */
+
+#include "httpd.h"
+#include "http_log.h"
+#include "http_conf_globals.h" /* for user_id & group_id */
+#include "util_uri.h"
+
+/* Some WWW schemes and their default ports; this is basically /etc/services */
+/* This will become global when the protocol abstraction comes */
+/* As the schemes are searched by a linear search, */
+/* they are sorted by their expected frequency */
+static schemes_t schemes[] =
+{
+    {"http",   DEFAULT_HTTP_PORT},
+    {"ftp",    DEFAULT_FTP_PORT},
+    {"https",  DEFAULT_HTTPS_PORT},
+    {"gopher", DEFAULT_GOPHER_PORT},
+    {"wais",   DEFAULT_WAIS_PORT},
+    {"nntp",   DEFAULT_NNTP_PORT},
+    {"snews",  DEFAULT_SNEWS_PORT},
+    {"prospero", DEFAULT_PROSPERO_PORT},
+    { NULL, 0xFFFF }                   /* unknown port */
+};
+
+
+API_EXPORT(unsigned short) ap_default_port_for_scheme(const char *scheme_str)
+{
+    schemes_t *scheme;
+
+    for (scheme = schemes; scheme->name != NULL; ++scheme)
+       if (strcasecmp(scheme_str, scheme->name) == 0)
+           return scheme->default_port;
+
+    return 0;
+}
+
+API_EXPORT(unsigned short) ap_default_port_for_request(const request_rec *r)
+{
+    return (r->parsed_uri.scheme)
+       ? ap_default_port_for_scheme(r->parsed_uri.scheme)
+       : 0;
+}
+
+/* Create a copy of a "struct hostent" record; it was presumably returned
+ * from a call to gethostbyname() and lives in static storage.
+ * By creating a copy we can tuck it away for later use.
+ */
+API_EXPORT(struct hostent *) ap_pduphostent(pool *p, const struct hostent *hp)
+{
+    struct hostent *newent;
+    char         **ptrs;
+    char         **aliases;
+    struct in_addr *addrs;
+    int                   i = 0, j = 0;
+
+    if (hp == NULL)
+       return NULL;
+
+    /* Count number of alias entries */
+    if (hp->h_aliases != NULL)
+       for (; hp->h_aliases[j] != NULL; ++j)
+           continue;
+
+    /* Count number of in_addr entries */
+    if (hp->h_addr_list != NULL)
+       for (; hp->h_addr_list[i] != NULL; ++i)
+           continue;
+
+    /* Allocate hostent structure, alias ptrs, addr ptrs, addrs */
+    newent = (struct hostent *) ap_palloc(p, sizeof(*hp));
+    aliases = (char **) ap_palloc(p, (j+1) * sizeof(char*));
+    ptrs = (char **) ap_palloc(p, (i+1) * sizeof(char*));
+    addrs  = (struct in_addr *) ap_palloc(p, (i+1) * sizeof(struct in_addr));
+
+    *newent = *hp;
+    newent->h_name = ap_pstrdup(p, hp->h_name);
+    newent->h_aliases = aliases;
+    newent->h_addr_list = (char**) ptrs;
+
+    /* Copy Alias Names: */
+    for (j = 0; hp->h_aliases[j] != NULL; ++j) {
+       aliases[j] = ap_pstrdup(p, hp->h_aliases[j]);
+    }
+    aliases[j] = NULL;
+
+    /* Copy address entries */
+    for (i = 0; hp->h_addr_list[i] != NULL; ++i) {
+       ptrs[i] = (char*) &addrs[i];
+       addrs[i] = *(struct in_addr *) hp->h_addr_list[i];
+    }
+    ptrs[i] = NULL;
+
+    return newent;
+}
+
+
+/* pgethostbyname(): resolve hostname, if successful return an ALLOCATED
+ * COPY OF the hostent structure, intended to be stored and used later.
+ * (gethostbyname() uses static storage that would be overwritten on each call)
+ */
+API_EXPORT(struct hostent *) ap_pgethostbyname(pool *p, const char *hostname)
+{
+    struct hostent *hp = gethostbyname(hostname);
+    return (hp == NULL) ? NULL : ap_pduphostent(p, hp);
+}
+
+
+/* Unparse a uri_components structure to an URI string.
+ * Optionally suppress the password for security reasons.
+ */
+API_EXPORT(char *) ap_unparse_uri_components(pool *p, const uri_components *uptr, unsigned flags)
+{
+    char *ret = "";
+
+    /* If suppressing the site part, omit both user name & scheme://hostname */
+    if (!(flags & UNP_OMITSITEPART)) {
+
+       /* Construct a "user:password@" string, honoring the passed UNP_ flags: */
+       if (uptr->user||uptr->password)
+           ret = ap_pstrcat (p,
+                       (uptr->user     && !(flags & UNP_OMITUSER)) ? uptr->user : "",
+                       (uptr->password && !(flags & UNP_OMITPASSWORD)) ? ":" : "",
+                       (uptr->password && !(flags & UNP_OMITPASSWORD))
+                          ? ((flags & UNP_REVEALPASSWORD) ? uptr->password : "XXXXXXXX")
+                          : "",
+                       "@", NULL);
+
+       /* Construct scheme://site string */
+       if (uptr->hostname) {
+           int is_default_port;
+
+           is_default_port =
+               (uptr->port_str == NULL ||
+                uptr->port == 0 ||
+                uptr->port == ap_default_port_for_scheme(uptr->scheme));
+
+           ret = ap_pstrcat (p,
+                       uptr->scheme, "://", ret, 
+                       uptr->hostname ? uptr->hostname : "",
+                       is_default_port ? "" : ":",
+                       is_default_port ? "" : uptr->port_str,
+                       NULL);
+       }
+    }
+
+    /* Should we suppress all path info? */
+    if (!(flags & UNP_OMITPATHINFO)) {
+       /* Append path, query and fragment strings: */
+       ret = ap_pstrcat (p,
+               ret,
+               uptr->path ? uptr->path : "",
+               (uptr->query    && !(flags & UNP_OMITQUERY)) ? "?" : "",
+               (uptr->query    && !(flags & UNP_OMITQUERY)) ? uptr->query : "",
+               (uptr->fragment && !(flags & UNP_OMITQUERY)) ? "#" : NULL,
+               (uptr->fragment && !(flags & UNP_OMITQUERY)) ? uptr->fragment : NULL,
+               NULL);
+    }
+    return ret;
+}
+
+/* The regex version of parse_uri_components has the advantage that it is
+ * relatively easy to understand and extend.  But it has the disadvantage
+ * that the regexes are complex enough that regex libraries really
+ * don't do a great job with them performancewise.
+ *
+ * The default is a hand coded scanner that is two orders of magnitude
+ * faster.
+ */
+#ifdef UTIL_URI_REGEX
+
+static regex_t re_uri;
+static regex_t re_hostpart;
+
+void ap_util_uri_init(void)
+{
+    int ret;
+    const char *re_str;
+
+    /* This is a modified version of the regex that appeared in
+     * draft-fielding-uri-syntax-01.  It doesnt allow the uri to contain a
+     * scheme but no hostinfo or vice versa. 
+     *
+     * draft-fielding-uri-syntax-01.txt, section 4.4 tells us:
+     *
+     *     Although the BNF defines what is allowed in each component, it is
+     *     ambiguous in terms of differentiating between a site component and
+     *     a path component that begins with two slash characters.
+     *  
+     * RFC2068 disambiguates this for the Request-URI, which may only ever be
+     * the "abs_path" portion of the URI.  So a request "GET //foo/bar
+     * HTTP/1.1" is really referring to the path //foo/bar, not the host foo,
+     * path /bar.  Nowhere in RFC2068 is it possible to have a scheme but no
+     * hostinfo or a hostinfo but no scheme.  (Unless you're proxying a
+     * protocol other than HTTP, but this parsing engine probably won't work
+     * for other protocols.)
+     *
+     *         12            3          4       5   6        7 8 */
+    re_str = "^(([^:/?#]+)://([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
+    /*          ^scheme--^   ^site---^  ^path--^   ^query^    ^frag */
+    if ((ret = regcomp(&re_uri, re_str, REG_EXTENDED)) != 0) {
+       char line[1024];
+
+       /* Make a readable error message */
+       ret = regerror(ret, &re_uri, line, sizeof line);
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+               "Internal error: regcomp(\"%s\") returned non-zero (%s) - "
+               "possibly due to broken regex lib! "
+               "Did you define WANTHSREGEX=yes?",
+               re_str, line);
+
+       exit(1);
+    }
+
+    /* This is a sub-RE which will break down the hostinfo part,
+     * i.e., user, password, hostname and port.
+     * $          12      3 4        5       6 7    */
+    re_str    = "^(([^:]*)(:(.*))?@)?([^@:]*)(:([0-9]*))?$";
+    /*             ^^user^ :pw        ^host^ ^:[port]^ */
+    if ((ret = regcomp(&re_hostpart, re_str, REG_EXTENDED)) != 0) {
+       char line[1024];
+
+       /* Make a readable error message */
+       ret = regerror(ret, &re_hostpart, line, sizeof line);
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+               "Internal error: regcomp(\"%s\") returned non-zero (%s) - "
+               "possibly due to broken regex lib! "
+               "Did you define WANTHSREGEX=yes?",
+               re_str, line);
+
+       exit(1);
+    }
+}
+
+
+/* parse_uri_components():
+ * Parse a given URI, fill in all supplied fields of a uri_components
+ * structure. This eliminates the necessity of extracting host, port,
+ * path, query info repeatedly in the modules.
+ * Side effects:
+ *  - fills in fields of uri_components *uptr
+ *  - none on any of the r->* fields
+ */
+API_EXPORT(int) ap_parse_uri_components(pool *p, const char *uri, uri_components *uptr)
+{
+    int ret;
+    regmatch_t match[10];      /* This must have at least as much elements
+                               * as there are braces in the re_strings */
+
+    ap_assert (uptr != NULL);
+
+    /* Initialize the structure. parse_uri() and parse_uri_components()
+     * can be called more than once per request.
+     */
+    memset (uptr, '\0', sizeof(*uptr));
+    uptr->is_initialized = 1;
+
+    ret = ap_regexec(&re_uri, uri, re_uri.re_nsub + 1, match, 0);
+
+    if (ret != 0) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+                    "ap_regexec() could not parse uri (\"%s\")",
+                   uri);
+
+       return HTTP_BAD_REQUEST;
+    }
+
+    if (match[2].rm_so != match[2].rm_eo)
+       uptr->scheme = ap_pstrndup (p, uri+match[2].rm_so, match[2].rm_eo - match[2].rm_so);
+
+    /* empty hostinfo is valid, that's why we test $1 but use $3 */
+    if (match[1].rm_so != match[1].rm_eo)
+       uptr->hostinfo = ap_pstrndup (p, uri+match[3].rm_so, match[3].rm_eo - match[3].rm_so);
+
+    if (match[4].rm_so != match[4].rm_eo)
+       uptr->path = ap_pstrndup (p, uri+match[4].rm_so, match[4].rm_eo - match[4].rm_so);
+
+    /* empty query string is valid, that's why we test $5 but use $6 */
+    if (match[5].rm_so != match[5].rm_eo)
+       uptr->query = ap_pstrndup (p, uri+match[6].rm_so, match[6].rm_eo - match[6].rm_so);
+
+    /* empty fragment is valid, test $7 use $8 */
+    if (match[7].rm_so != match[7].rm_eo)
+       uptr->fragment = ap_pstrndup (p, uri+match[8].rm_so, match[8].rm_eo - match[8].rm_so);
+
+    if (uptr->hostinfo) {
+       /* Parse the hostinfo part to extract user, password, host, and port */
+       ret = ap_regexec(&re_hostpart, uptr->hostinfo, re_hostpart.re_nsub + 1, match, 0);
+       if (ret != 0) {
+           ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+                    "ap_regexec() could not parse (\"%s\") as host part",
+                   uptr->hostinfo);
+
+           return HTTP_BAD_REQUEST;
+       }
+
+       /* $      12      3 4        5       6 7            */
+       /*      "^(([^:]*)(:(.*))?@)?([^@:]*)(:([0-9]*))?$" */
+       /*         ^^user^ :pw        ^host^ ^:[port]^      */
+
+       /* empty user is valid, that's why we test $1 but use $2 */
+       if (match[1].rm_so != match[1].rm_eo)
+           uptr->user = ap_pstrndup (p, uptr->hostinfo+match[2].rm_so, match[2].rm_eo - match[2].rm_so);
+
+       /* empty password is valid, test $3 but use $4 */
+       if (match[3].rm_so != match[3].rm_eo)
+           uptr->password = ap_pstrndup (p, uptr->hostinfo+match[4].rm_so, match[4].rm_eo - match[4].rm_so);
+
+       /* empty hostname is valid, and implied by the existence of hostinfo */
+       uptr->hostname = ap_pstrndup (p, uptr->hostinfo+match[5].rm_so, match[5].rm_eo - match[5].rm_so);
+
+       if (match[6].rm_so != match[6].rm_eo) {
+           /* Note that the port string can be empty.
+            * If it is, we use the default port associated with the scheme
+            */
+           uptr->port_str = ap_pstrndup (p, uptr->hostinfo+match[7].rm_so, match[7].rm_eo - match[7].rm_so);
+           if (uptr->port_str[0] != '\0') {
+               char *endstr;
+               int port;
+
+               port = strtol(uptr->port_str, &endstr, 10);
+               uptr->port = port;
+               if (*endstr != '\0') {
+                   /* Invalid characters after ':' found */
+                   return HTTP_BAD_REQUEST;
+               }
+           }
+           else {
+               uptr->port = uptr->scheme ? ap_default_port_for_scheme(uptr->scheme) : DEFAULT_HTTP_PORT;
+           }
+       }
+    }
+
+    if (ret == 0)
+       ret = HTTP_OK;
+    return ret;
+}
+#else
+
+/* Here is the hand-optimized parse_uri_components().  There are some wild
+ * tricks we could pull in assembly language that we don't pull here... like we
+ * can do word-at-time scans for delimiter characters using the same technique
+ * that fast memchr()s use.  But that would be way non-portable. -djg
+ */
+
+/* We have a table that we can index by character and it tells us if the
+ * character is one of the interesting delimiters.  Note that we even get
+ * compares for NUL for free -- it's just another delimiter.
+ */
+
+#define T_COLON                0x01    /* ':' */
+#define T_SLASH                0x02    /* '/' */
+#define T_QUESTION     0x04    /* '?' */
+#define T_HASH         0x08    /* '#' */
+#define T_NUL          0x80    /* '\0' */
+
+/* the uri_delims.h file is autogenerated by gen_uri_delims.c */
+#include "uri_delims.h"
+
+/* it works like this:
+    if (uri_delims[ch] & NOTEND_foobar) {
+       then we're not at a delimiter for foobar
+    }
+*/
+
+/* Note that we optimize the scheme scanning here, we cheat and let the
+ * compiler know that it doesn't have to do the & masking.
+ */
+#define NOTEND_SCHEME  (0xff)
+#define NOTEND_HOSTINFO        (T_SLASH | T_QUESTION | T_HASH | T_NUL)
+#define NOTEND_PATH    (T_QUESTION | T_HASH | T_NUL)
+
+void ap_util_uri_init(void)
+{
+    /* nothing to do */
+}
+
+/* parse_uri_components():
+ * Parse a given URI, fill in all supplied fields of a uri_components
+ * structure. This eliminates the necessity of extracting host, port,
+ * path, query info repeatedly in the modules.
+ * Side effects:
+ *  - fills in fields of uri_components *uptr
+ *  - none on any of the r->* fields
+ */
+API_EXPORT(int) ap_parse_uri_components(pool *p, const char *uri, uri_components *uptr)
+{
+    const char *s;
+    const char *s1;
+    const char *hostinfo;
+    char *endstr;
+    int port;
+
+    /* Initialize the structure. parse_uri() and parse_uri_components()
+     * can be called more than once per request.
+     */
+    memset (uptr, '\0', sizeof(*uptr));
+    uptr->is_initialized = 1;
+
+    /* We assume the processor has a branch predictor like most --
+     * it assumes forward branches are untaken and backwards are taken.  That's
+     * the reason for the gotos.  -djg
+     */
+    if (uri[0] == '/') {
+deal_with_path:
+       /* we expect uri to point to first character of path ... remember
+        * that the path could be empty -- http://foobar?query for example
+        */
+       s = uri;
+       while ((uri_delims[*(unsigned char *)s] & NOTEND_PATH) == 0) {
+           ++s;
+       }
+       if (s != uri) {
+           uptr->path = ap_pstrndup(p, uri, s - uri);
+       }
+       if (*s == 0) {
+           return HTTP_OK;
+       }
+       if (*s == '?') {
+           ++s;
+           s1 = strchr(s, '#');
+           if (s1) {
+               uptr->fragment = ap_pstrdup(p, s1 + 1);
+               uptr->query = ap_pstrndup(p, s, s1 - s);
+           }
+           else {
+               uptr->query = ap_pstrdup(p, s);
+           }
+           return HTTP_OK;
+       }
+       /* otherwise it's a fragment */
+       uptr->fragment = ap_pstrdup(p, s + 1);
+       return HTTP_OK;
+    }
+
+    /* find the scheme: */
+    s = uri;
+    while ((uri_delims[*(unsigned char *)s] & NOTEND_SCHEME) == 0) {
+       ++s;
+    }
+    /* scheme must be non-empty and followed by :// */
+    if (s == uri || s[0] != ':' || s[1] != '/' || s[2] != '/') {
+       goto deal_with_path;    /* backwards predicted taken! */
+    }
+
+    uptr->scheme = ap_pstrndup(p, uri, s - uri);
+    s += 3;
+    hostinfo = s;
+    while ((uri_delims[*(unsigned char *)s] & NOTEND_HOSTINFO) == 0) {
+       ++s;
+    }
+    uri = s;   /* whatever follows hostinfo is start of uri */
+    uptr->hostinfo = ap_pstrndup(p, hostinfo, uri - hostinfo);
+
+    /* If there's a username:password@host:port, the @ we want is the last @...
+     * too bad there's no memrchr()... For the C purists, note that hostinfo
+     * is definately not the first character of the original uri so therefore
+     * &hostinfo[-1] < &hostinfo[0] ... and this loop is valid C.
+     */
+    do {
+       --s;
+    } while (s >= hostinfo && *s != '@');
+    if (s < hostinfo) {
+       /* again we want the common case to be fall through */
+deal_with_host:
+       /* We expect hostinfo to point to the first character of
+        * the hostname.  If there's a port it is the first colon.
+        */
+       s = memchr(hostinfo, ':', uri - hostinfo);
+       if (s == NULL) {
+           /* we expect the common case to have no port */
+           uptr->hostname = ap_pstrndup(p, hostinfo, uri - hostinfo);
+           goto deal_with_path;
+       }
+       uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo);
+       ++s;
+       uptr->port_str = ap_pstrndup(p, s, uri - s);
+       if (uri != s) {
+           port = strtol(uptr->port_str, &endstr, 10);
+           uptr->port = port;
+           if (*endstr == '\0') {
+               goto deal_with_path;
+           }
+           /* Invalid characters after ':' found */
+           return HTTP_BAD_REQUEST;
+       }
+       uptr->port = ap_default_port_for_scheme(uptr->scheme);
+       goto deal_with_path;
+    }
+
+    /* first colon delimits username:password */
+    s1 = memchr(hostinfo, ':', s - hostinfo);
+    if (s1) {
+       uptr->user = ap_pstrndup(p, hostinfo, s1 - hostinfo);
+       ++s1;
+       uptr->password = ap_pstrndup(p, s1, s - s1);
+    }
+    else {
+       uptr->user = ap_pstrndup(p, hostinfo, s - hostinfo);
+    }
+    hostinfo = s + 1;
+    goto deal_with_host;
+}
+
+/* Special case for CONNECT parsing: it comes with the hostinfo part only */
+/* See the INTERNET-DRAFT document "Tunneling SSL Through a WWW Proxy"
+ * currently at http://www.mcom.com/newsref/std/tunneling_ssl.html
+ * for the format of the "CONNECT host:port HTTP/1.0" request
+ */
+API_EXPORT(int) ap_parse_hostinfo_components(pool *p, const char *hostinfo, uri_components *uptr)
+{
+    const char *s;
+    char *endstr;
+
+    /* Initialize the structure. parse_uri() and parse_uri_components()
+     * can be called more than once per request.
+     */
+    memset (uptr, '\0', sizeof(*uptr));
+    uptr->is_initialized = 1;
+    uptr->hostinfo = ap_pstrdup(p, hostinfo);
+
+    /* We expect hostinfo to point to the first character of
+     * the hostname.  There must be a port, separated by a colon
+     */
+    s = strchr(hostinfo, ':');
+    if (s == NULL) {
+       return HTTP_BAD_REQUEST;
+    }
+    uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo);
+    ++s;
+    uptr->port_str = ap_pstrdup(p, s);
+    if (*s != '\0') {
+       uptr->port = strtol(uptr->port_str, &endstr, 10);
+       if (*endstr == '\0') {
+           return HTTP_OK;
+       }
+       /* Invalid characters after ':' found */
+    }
+    return HTTP_BAD_REQUEST;
+}
+#endif
diff --git a/server/vhost.c b/server/vhost.c
new file mode 100644 (file)
index 0000000..793048f
--- /dev/null
@@ -0,0 +1,917 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_vhost.c: functions pertaining to virtual host addresses
+ *     (configuration and run-time)
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_conf_globals.h"
+#include "http_log.h"
+#include "http_vhost.h"
+#include "http_protocol.h"
+
+/*
+ * After all the definitions there's an explanation of how it's all put
+ * together.
+ */
+
+/* meta-list of name-vhosts.  Each server_rec can be in possibly multiple
+ * lists of name-vhosts.
+ */
+typedef struct name_chain name_chain;
+struct name_chain {
+    name_chain *next;
+    server_addr_rec *sar;      /* the record causing it to be in
+                                * this chain (needed for port comparisons) */
+    server_rec *server;                /* the server to use on a match */
+};
+
+/* meta-list of ip addresses.  Each server_rec can be in possibly multiple
+ * hash chains since it can have multiple ips.
+ */
+typedef struct ipaddr_chain ipaddr_chain;
+struct ipaddr_chain {
+    ipaddr_chain *next;
+    server_addr_rec *sar;      /* the record causing it to be in
+                                * this chain (need for both ip addr and port
+                                * comparisons) */
+    server_rec *server;                /* the server to use if this matches */
+    name_chain *names;         /* if non-NULL then a list of name-vhosts
+                                * sharing this address */
+};
+
+/* This defines the size of the hash table used for hashing ip addresses
+ * of virtual hosts.  It must be a power of two.
+ */
+#ifndef IPHASH_TABLE_SIZE
+#define IPHASH_TABLE_SIZE 256
+#endif
+
+/* A (n) bucket hash table, each entry has a pointer to a server rec and
+ * a pointer to the other entries in that bucket.  Each individual address,
+ * even for virtualhosts with multiple addresses, has an entry in this hash
+ * table.  There are extra buckets for _default_, and name-vhost entries.
+ *
+ * Note that after config time this is constant, so it is thread-safe.
+ */
+static ipaddr_chain *iphash_table[IPHASH_TABLE_SIZE];
+
+/* dump out statistics about the hash function */
+/* #define IPHASH_STATISTICS */
+
+/* list of the _default_ servers */
+static ipaddr_chain *default_list;
+
+/* list of the NameVirtualHost addresses */
+static server_addr_rec *name_vhost_list;
+static server_addr_rec **name_vhost_list_tail;
+
+/*
+ * How it's used:
+ *
+ * The ip address determines which chain in iphash_table is interesting, then
+ * a comparison is done down that chain to find the first ipaddr_chain whose
+ * sar matches the address:port pair.
+ *
+ * If that ipaddr_chain has names == NULL then you're done, it's an ip-vhost.
+ *
+ * Otherwise it's a name-vhost list, and the default is the server in the
+ * ipaddr_chain record.  We tuck away the ipaddr_chain record in the
+ * conn_rec field vhost_lookup_data.  Later on after the headers we get a
+ * second chance, and we use the name_chain to figure out what name-vhost
+ * matches the headers.
+ *
+ * If there was no ip address match in the iphash_table then do a lookup
+ * in the default_list.
+ *
+ * How it's put together ... well you should be able to figure that out
+ * from how it's used.  Or something like that.
+ */
+
+
+/* called at the beginning of the config */
+void ap_init_vhost_config(pool *p)
+{
+    memset(iphash_table, 0, sizeof(iphash_table));
+    default_list = NULL;
+    name_vhost_list = NULL;
+    name_vhost_list_tail = &name_vhost_list;
+}
+
+
+/*
+ * Parses a host of the form <address>[:port]
+ * paddr is used to create a list in the order of input
+ * **paddr is the ->next pointer of the last entry (or s->addrs)
+ * *paddr is the variable used to keep track of **paddr between calls
+ * port is the default port to assume
+ */
+static const char *get_addresses(pool *p, char *w, server_addr_rec ***paddr,
+                           unsigned port)
+{
+    struct hostent *hep;
+    unsigned long my_addr;
+    server_addr_rec *sar;
+    char *t;
+    int i, is_an_ip_addr;
+
+    if (*w == 0)
+       return NULL;
+
+    t = strchr(w, ':');
+    if (t) {
+       if (strcmp(t + 1, "*") == 0) {
+           port = 0;
+       }
+       else if ((i = atoi(t + 1))) {
+           port = i;
+       }
+       else {
+           return ":port must be numeric";
+       }
+       *t = 0;
+    }
+
+    is_an_ip_addr = 0;
+    if (strcmp(w, "*") == 0) {
+       my_addr = htonl(INADDR_ANY);
+       is_an_ip_addr = 1;
+    }
+    else if (strcasecmp(w, "_default_") == 0
+            || strcmp(w, "255.255.255.255") == 0) {
+       my_addr = DEFAULT_VHOST_ADDR;
+       is_an_ip_addr = 1;
+    }
+    else if ((my_addr = ap_inet_addr(w)) != INADDR_NONE) {
+       is_an_ip_addr = 1;
+    }
+    if (is_an_ip_addr) {
+       sar = ap_pcalloc(p, sizeof(server_addr_rec));
+       **paddr = sar;
+       *paddr = &sar->next;
+       sar->host_addr.s_addr = my_addr;
+       sar->host_port = port;
+       sar->virthost = ap_pstrdup(p, w);
+       if (t != NULL)
+           *t = ':';
+       return NULL;
+    }
+
+    hep = gethostbyname(w);
+
+    if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
+       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+           "Cannot resolve host name %s --- ignoring!", w);
+       if (t != NULL)
+           *t = ':';
+       return NULL;
+    }
+
+    for (i = 0; hep->h_addr_list[i]; ++i) {
+       sar = ap_pcalloc(p, sizeof(server_addr_rec));
+       **paddr = sar;
+       *paddr = &sar->next;
+       sar->host_addr = *(struct in_addr *) hep->h_addr_list[i];
+       sar->host_port = port;
+       sar->virthost = ap_pstrdup(p, w);
+    }
+
+    if (t != NULL)
+       *t = ':';
+    return NULL;
+}
+
+
+/* parse the <VirtualHost> addresses */
+const char *ap_parse_vhost_addrs(pool *p, const char *hostname, server_rec *s)
+{
+    server_addr_rec **addrs;
+    const char *err;
+
+    /* start the list of addreses */
+    addrs = &s->addrs;
+    while (hostname[0]) {
+       err = get_addresses(p, ap_getword_conf(p, &hostname), &addrs, s->port);
+       if (err) {
+           *addrs = NULL;
+           return err;
+       }
+    }
+    /* terminate the list */
+    *addrs = NULL;
+    if (s->addrs) {
+       if (s->addrs->host_port) {
+           /* override the default port which is inherited from main_server */
+           s->port = s->addrs->host_port;
+       }
+    }
+    return NULL;
+}
+
+
+const char *ap_set_name_virtual_host (cmd_parms *cmd, void *dummy, char *arg)
+{
+    /* use whatever port the main server has at this point */
+    return get_addresses(cmd->pool, arg, &name_vhost_list_tail,
+                           cmd->server->port);
+}
+
+
+/* hash table statistics, keep this in here for the beta period so
+ * we can find out if the hash function is ok
+ */
+#ifdef IPHASH_STATISTICS
+static int iphash_compare(const void *a, const void *b)
+{
+    return (*(const int *) b - *(const int *) a);
+}
+
+
+static void dump_iphash_statistics(server_rec *main_s)
+{
+    unsigned count[IPHASH_TABLE_SIZE];
+    int i;
+    ipaddr_chain *src;
+    unsigned total;
+    char buf[HUGE_STRING_LEN];
+    char *p;
+
+    total = 0;
+    for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
+       count[i] = 0;
+       for (src = iphash_table[i]; src; src = src->next) {
+           ++count[i];
+           if (i < IPHASH_TABLE_SIZE) {
+               /* don't count the slop buckets in the total */
+               ++total;
+           }
+       }
+    }
+    qsort(count, IPHASH_TABLE_SIZE, sizeof(count[0]), iphash_compare);
+    p = buf + ap_snprintf(buf, sizeof(buf),
+                   "iphash: total hashed = %u, avg chain = %u, "
+                   "chain lengths (count x len):",
+                   total, total / IPHASH_TABLE_SIZE);
+    total = 1;
+    for (i = 1; i < IPHASH_TABLE_SIZE; ++i) {
+       if (count[i - 1] != count[i]) {
+           p += ap_snprintf(p, sizeof(buf) - (p - buf), " %ux%u",
+                            total, count[i - 1]);
+           total = 1;
+       }
+       else {
+           ++total;
+       }
+    }
+    p += ap_snprintf(p, sizeof(buf) - (p - buf), " %ux%u",
+                    total, count[IPHASH_TABLE_SIZE - 1]);
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, main_s, buf);
+}
+#endif
+
+
+/* This hashing function is designed to get good distribution in the cases
+ * where the server is handling entire "networks" of servers.  i.e. a
+ * whack of /24s.  This is probably the most common configuration for
+ * ISPs with large virtual servers.
+ *
+ * NOTE: This function is symmetric (i.e. collapses all 4 octets
+ * into one), so machine byte order (big/little endianness) does not matter.
+ *
+ * Hash function provided by David Hankins.
+ */
+static ap_inline unsigned hash_inaddr(unsigned key)
+{
+    key ^= (key >> 16);
+    return ((key >> 8) ^ key) % IPHASH_TABLE_SIZE;
+}
+
+
+
+static ipaddr_chain *new_ipaddr_chain(pool *p,
+                                   server_rec *s, server_addr_rec *sar)
+{
+    ipaddr_chain *new;
+
+    new = ap_palloc(p, sizeof(*new));
+    new->names = NULL;
+    new->server = s;
+    new->sar = sar;
+    new->next = NULL;
+    return new;
+}
+
+
+static name_chain *new_name_chain(pool *p, server_rec *s, server_addr_rec *sar)
+{
+    name_chain *new;
+
+    new = ap_palloc(p, sizeof(*new));
+    new->server = s;
+    new->sar = sar;
+    new->next = NULL;
+    return new;
+}
+
+
+static ap_inline ipaddr_chain *find_ipaddr(struct in_addr *server_ip,
+    unsigned port)
+{
+    unsigned bucket;
+    ipaddr_chain *trav;
+    unsigned addr;
+
+    /* scan the hash table for an exact match first */
+    addr = server_ip->s_addr;
+    bucket = hash_inaddr(addr);
+    for (trav = iphash_table[bucket]; trav; trav = trav->next) {
+       server_addr_rec *sar = trav->sar;
+       if ((sar->host_addr.s_addr == addr)
+           && (sar->host_port == 0 || sar->host_port == port
+               || port == 0)) {
+           return trav;
+       }
+    }
+    return NULL;
+}
+
+
+static ipaddr_chain *find_default_server(unsigned port)
+{
+    server_addr_rec *sar;
+    ipaddr_chain *trav;
+
+    for (trav = default_list; trav; trav = trav->next) {
+        sar = trav->sar;
+        if (sar->host_port == 0 || sar->host_port == port) {
+            /* match! */
+           return trav;
+        }
+    }
+    return NULL;
+}
+
+
+static void dump_vhost_config(FILE *f)
+{
+    int i;
+    ipaddr_chain *ic;
+    name_chain *nc;
+    char buf[MAX_STRING_LEN];
+
+    fprintf(f, "VirtualHost configuration:\n");
+    for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
+       for (ic = iphash_table[i]; ic; ic = ic->next) {
+           if (ic->sar->host_port == 0) {
+               ap_snprintf(buf, sizeof(buf), "%pA:*", &ic->sar->host_addr);
+           }
+           else {
+               ap_snprintf(buf, sizeof(buf), "%pA:%u", &ic->sar->host_addr,
+                   ic->sar->host_port);
+           }
+           if (ic->names == NULL) {
+               fprintf(f, "%-22s %s (%s:%u)\n", buf,
+                   ic->server->server_hostname, ic->server->defn_name,
+                   ic->server->defn_line_number);
+               continue;
+           }
+           fprintf(f, "%-22s is a NameVirtualHost\n"
+                      "%22s default server %s (%s:%u)\n",
+                      buf, "", ic->server->server_hostname,
+                      ic->server->defn_name, ic->server->defn_line_number);
+           for (nc = ic->names; nc; nc = nc->next) {
+               if (nc->sar->host_port) {
+                   fprintf(f, "%22s port %u ", "", nc->sar->host_port);
+               }
+               else {
+                   fprintf(f, "%22s port * ", "");
+               }
+               fprintf(f, "namevhost %s (%s:%u)\n",
+                       nc->server->server_hostname,
+                       nc->server->defn_name,
+                       nc->server->defn_line_number);
+           }
+       }
+    }
+    if (default_list) {
+       fprintf(f, "_default_ servers:\n");
+       for (ic = default_list; ic; ic = ic->next) {
+           if (ic->sar->host_port == 0) {
+               fprintf(f, "port * ");
+           }
+           else {
+               fprintf(f, "port %u ", ic->sar->host_port);
+           }
+           fprintf(f, "server %s (%s:%u)\n",
+               ic->server->server_hostname, ic->server->defn_name,
+               ic->server->defn_line_number);
+       }
+    }
+}
+
+/* compile the tables and such we need to do the run-time vhost lookups */
+void ap_fini_vhost_config(pool *p, server_rec *main_s)
+{
+    server_addr_rec *sar;
+    int has_default_vhost_addr;
+    server_rec *s;
+    int i;
+    ipaddr_chain **iphash_table_tail[IPHASH_TABLE_SIZE];
+
+    /* terminate the name_vhost list */
+    *name_vhost_list_tail = NULL;
+
+    /* Main host first */
+    s = main_s;
+
+    if (!s->server_hostname) {
+       s->server_hostname = ap_get_local_host(p);
+    }
+
+    /* initialize the tails */
+    for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
+       iphash_table_tail[i] = &iphash_table[i];
+    }
+
+    /* The first things to go into the hash table are the NameVirtualHosts
+     * Since name_vhost_list is in the same order that the directives
+     * occured in the config file, we'll copy it in that order.
+     */
+    for (sar = name_vhost_list; sar; sar = sar->next) {
+       unsigned bucket = hash_inaddr(sar->host_addr.s_addr);
+       ipaddr_chain *new = new_ipaddr_chain(p, NULL, sar);
+
+       *iphash_table_tail[bucket] = new;
+       iphash_table_tail[bucket] = &new->next;
+
+       /* Notice that what we've done is insert an ipaddr_chain with
+        * both server and names NULL.  Remember that.
+        */
+    }
+
+    /* The next things to go into the hash table are the virtual hosts
+     * themselves.  They're listed off of main_s->next in the reverse
+     * order they occured in the config file, so we insert them at
+     * the iphash_table_tail but don't advance the tail.
+     */
+
+    for (s = main_s->next; s; s = s->next) {
+       has_default_vhost_addr = 0;
+       for (sar = s->addrs; sar; sar = sar->next) {
+           ipaddr_chain *ic;
+
+           if (sar->host_addr.s_addr == DEFAULT_VHOST_ADDR
+               || sar->host_addr.s_addr == INADDR_ANY) {
+               /* add it to default bucket for each appropriate sar
+                * since we need to do a port test
+                */
+               ipaddr_chain *other;
+
+               other = find_default_server(sar->host_port);
+               if (other && other->sar->host_port != 0) {
+                   ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s,
+                           "_default_ VirtualHost overlap on port %u,"
+                           " the first has precedence", sar->host_port);
+               }
+               has_default_vhost_addr = 1;
+               ic = new_ipaddr_chain(p, s, sar);
+               ic->next = default_list;
+               default_list = ic;
+           }
+           else {
+               /* see if it matches something we've already got */
+               ic = find_ipaddr(&sar->host_addr, sar->host_port);
+
+               /* the first time we encounter a NameVirtualHost address
+                * ic->server will be NULL, on subsequent encounters
+                * ic->names will be non-NULL.
+                */
+               if (ic && (ic->names || ic->server == NULL)) {
+                   name_chain *nc = new_name_chain(p, s, sar);
+                   nc->next = ic->names;
+                   ic->names = nc;
+                   ic->server = s;
+                   if (sar->host_port != ic->sar->host_port) {
+                       /* one of the two is a * port, the other isn't */
+                       ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, main_s,
+                               "VirtualHost %s:%u -- mixing * "
+                               "ports and non-* ports with "
+                               "a NameVirtualHost address is not supported,"
+                               " proceeding with undefined results",
+                               sar->virthost, sar->host_port);
+                   }
+               }
+               else if (ic) {
+                   ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s,
+                           "VirtualHost %s:%u overlaps with "
+                           "VirtualHost %s:%u, the first has precedence, "
+                           "perhaps you need a NameVirtualHost directive",
+                           sar->virthost, sar->host_port,
+                           ic->sar->virthost, ic->sar->host_port);
+                   ic->sar = sar;
+                   ic->server = s;
+               }
+               else {
+                   unsigned bucket = hash_inaddr(sar->host_addr.s_addr);
+
+                   ic = new_ipaddr_chain(p, s, sar);
+                   ic->next = *iphash_table_tail[bucket];
+                   *iphash_table_tail[bucket] = ic;
+               }
+           }
+       }
+
+       /* Ok now we want to set up a server_hostname if the user was
+        * silly enough to forget one.
+        * XXX: This is silly we should just crash and burn.
+        */
+       if (!s->server_hostname) {
+           if (has_default_vhost_addr) {
+               s->server_hostname = main_s->server_hostname;
+           }
+           else if (!s->addrs) {
+               /* what else can we do?  at this point this vhost has
+                   no configured name, probably because they used
+                   DNS in the VirtualHost statement.  It's disabled
+                   anyhow by the host matching code.  -djg */
+               s->server_hostname =
+                   ap_pstrdup(p, "bogus_host_without_forward_dns");
+           }
+           else {
+               struct hostent *h;
+
+               if ((h = gethostbyaddr((char *) &(s->addrs->host_addr),
+                                       sizeof(struct in_addr), AF_INET))) {
+                   s->server_hostname = ap_pstrdup(p, (char *) h->h_name);
+               }
+               else {
+                   /* again, what can we do?  They didn't specify a
+                      ServerName, and their DNS isn't working. -djg */
+                   ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, main_s,
+                           "Failed to resolve server name "
+                           "for %s (check DNS) -- or specify an explicit "
+                           "ServerName",
+                           inet_ntoa(s->addrs->host_addr));
+                   s->server_hostname =
+                       ap_pstrdup(p, "bogus_host_without_reverse_dns");
+               }
+           }
+       }
+    }
+
+    /* now go through and delete any NameVirtualHosts that didn't have any
+     * hosts associated with them.  Lamers.
+     */
+    for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
+       ipaddr_chain **pic = &iphash_table[i];
+
+       while (*pic) {
+           ipaddr_chain *ic = *pic;
+
+           if (ic->server == NULL) {
+               ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s,
+                       "NameVirtualHost %s:%u has no VirtualHosts",
+                       ic->sar->virthost, ic->sar->host_port);
+               *pic = ic->next;
+           }
+           else if (ic->names == NULL) {
+               /* if server != NULL and names == NULL then we're done
+                * looking at NameVirtualHosts
+                */
+               break;
+           }
+           else {
+               pic = &ic->next;
+           }
+       }
+    }
+
+#ifdef IPHASH_STATISTICS
+    dump_iphash_statistics(main_s);
+#endif
+    if (ap_dump_settings) {
+       dump_vhost_config(stderr);
+    }
+}
+
+
+/*****************************************************************************
+ * run-time vhost matching functions
+ */
+
+/* Remove :port and optionally a single trailing . from the hostname, this
+ * canonicalizes it somewhat.
+ */
+static void fix_hostname(request_rec *r)
+{
+    const char *hostname = r->hostname;
+    char *host = ap_getword(r->pool, &hostname, ':');  /* get rid of port */
+    size_t l;
+
+    /* trim a trailing . */
+    l = strlen(host);
+    if (l > 0 && host[l-1] == '.') {
+        host[l-1] = '\0';
+    }
+
+    r->hostname = host;
+}
+
+
+/* return 1 if host matches ServerName or ServerAliases */
+static int matches_aliases(server_rec *s, const char *host)
+{
+    int i;
+    array_header *names;
+
+    /* match ServerName */
+    if (!strcasecmp(host, s->server_hostname)) {
+       return 1;
+    }
+
+    /* search all the aliases from ServerAlias directive */
+    names = s->names;
+    if (names) {
+       char **name = (char **) names->elts;
+       for (i = 0; i < names->nelts; ++i) {
+           if(!name[i]) continue;
+           if (!strcasecmp(host, name[i]))
+               return 1;
+       }
+    }
+    names = s->wild_names;
+    if (names) {
+       char **name = (char **) names->elts;
+       for (i = 0; i < names->nelts; ++i) {
+           if(!name[i]) continue;
+           if (!ap_strcasecmp_match(host, name[i]))
+               return 1;
+       }
+    }
+    return 0;
+}
+
+
+/* Suppose a request came in on the same socket as this r, and included
+ * a header "Host: host:port", would it map to r->server?  It's more
+ * than just that though.  When we do the normal matches for each request
+ * we don't even bother considering Host: etc on non-namevirtualhosts,
+ * we just call it a match.  But here we require the host:port to match
+ * the ServerName and/or ServerAliases.
+ */
+API_EXPORT(int) ap_matches_request_vhost(request_rec *r, const char *host,
+    unsigned port)
+{
+    server_rec *s;
+    server_addr_rec *sar;
+
+    s = r->server;
+
+    /* search all the <VirtualHost> values */
+    /* XXX: If this is a NameVirtualHost then we may not be doing the Right Thing
+     * consider: 
+     *
+     *     NameVirtualHost 10.1.1.1
+     *     <VirtualHost 10.1.1.1>
+     *     ServerName v1
+     *     </VirtualHost>
+     *     <VirtualHost 10.1.1.1>
+     *     ServerName v2
+     *     </VirtualHost>
+     *
+     * Suppose r->server is v2, and we're asked to match "10.1.1.1".  We'll say
+     * "yup it's v2", when really it isn't... if a request came in for 10.1.1.1
+     * it would really go to v1.
+     */
+    for (sar = s->addrs; sar; sar = sar->next) {
+       if ((sar->host_port == 0 || port == sar->host_port)
+           && !strcasecmp(host, sar->virthost)) {
+           return 1;
+       }
+    }
+
+    /* the Port has to match now, because the rest don't have ports associated
+     * with them. */
+    if (port != s->port) {
+       return 0;
+    }
+
+    return matches_aliases(s, host);
+}
+
+
+static void check_hostalias(request_rec *r)
+{
+    /*
+     * Even if the request has a Host: header containing a port we ignore
+     * that port.  We always use the physical port of the socket.  There
+     * are a few reasons for this:
+     *
+     * - the default of 80 or 443 for SSL is easier to handle this way
+     * - there is less of a possibility of a security problem
+     * - it simplifies the data structure
+     * - the client may have no idea that a proxy somewhere along the way
+     *   translated the request to another ip:port
+     * - except for the addresses from the VirtualHost line, none of the other
+     *   names we'll match have ports associated with them
+     */
+    const char *host = r->hostname;
+    unsigned port = ntohs(r->connection->local_addr.sin_port);
+    server_rec *s;
+    server_rec *last_s;
+    name_chain *src;
+
+    last_s = NULL;
+
+    /* Recall that the name_chain is a list of server_addr_recs, some of
+     * whose ports may not match.  Also each server may appear more than
+     * once in the chain -- specifically, it will appear once for each
+     * address from its VirtualHost line which matched.  We only want to
+     * do the full ServerName/ServerAlias comparisons once for each
+     * server, fortunately we know that all the VirtualHost addresses for
+     * a single server are adjacent to each other.
+     */
+
+    for (src = r->connection->vhost_lookup_data; src; src = src->next) {
+        server_addr_rec *sar;
+
+       /* We only consider addresses on the name_chain which have a matching
+        * port
+        */
+       sar = src->sar;
+       if (sar->host_port != 0 && port != sar->host_port) {
+           continue;
+       }
+
+        s = src->server;
+
+       /* does it match the virthost from the sar? */
+       if (!strcasecmp(host, sar->virthost)) {
+           goto found;
+       }
+
+       if (s == last_s) {
+           /* we've already done ServerName and ServerAlias checks for this
+            * vhost
+            */
+           continue;
+       }
+       last_s = s;
+
+       if (matches_aliases(s, host)) {
+           goto found;
+       }
+    }
+    return;
+
+found:
+    /* s is the first matching server, we're done */
+    r->server = r->connection->server = s;
+}
+
+
+static void check_serverpath(request_rec *r)
+{
+    server_rec *s;
+    server_rec *last_s;
+    name_chain *src;
+    unsigned port = ntohs(r->connection->local_addr.sin_port);
+
+    /*
+     * This is in conjunction with the ServerPath code in http_core, so we
+     * get the right host attached to a non- Host-sending request.
+     *
+     * See the comment in check_hostalias about how each vhost can be
+     * listed multiple times.
+     */
+
+    last_s = NULL;
+    for (src = r->connection->vhost_lookup_data; src; src = src->next) {
+       /* We only consider addresses on the name_chain which have a matching
+        * port
+        */
+       if (src->sar->host_port != 0 && port != src->sar->host_port) {
+           continue;
+       }
+
+        s = src->server;
+       if (s == last_s) {
+           continue;
+       }
+       last_s = s;
+
+        if (s->path && !strncmp(r->uri, s->path, s->pathlen) &&
+            (s->path[s->pathlen - 1] == '/' ||
+             r->uri[s->pathlen] == '/' ||
+             r->uri[s->pathlen] == '\0')) {
+            r->server = r->connection->server = s;
+           return;
+       }
+    }
+}
+
+
+void ap_update_vhost_from_headers(request_rec *r)
+{
+    /* must set this for HTTP/1.1 support */
+    if (r->hostname || (r->hostname = ap_table_get(r->headers_in, "Host"))) {
+       fix_hostname(r);
+    }
+    /* check if we tucked away a name_chain */
+    if (r->connection->vhost_lookup_data) {
+        if (r->hostname)
+            check_hostalias(r);
+        else
+            check_serverpath(r);
+    }
+}
+
+
+/* Called for a new connection which has a known local_addr.  Note that the
+ * new connection is assumed to have conn->server == main server.
+ */
+void ap_update_vhost_given_ip(conn_rec *conn)
+{
+    ipaddr_chain *trav;
+    unsigned port = ntohs(conn->local_addr.sin_port);
+
+    /* scan the hash table for an exact match first */
+    trav = find_ipaddr(&conn->local_addr.sin_addr, port);
+    if (trav) {
+       /* save the name_chain for later in case this is a name-vhost */
+       conn->vhost_lookup_data = trav->names;
+       conn->server = trav->server;
+       return;
+    }
+
+    /* There's certainly no name-vhosts with this address, they would have
+     * been matched above.
+     */
+    conn->vhost_lookup_data = NULL;
+
+    /* maybe there's a default server matching this port */
+    trav = find_default_server(port);
+    if (trav) {
+       conn->server = trav->server;
+    }
+
+    /* otherwise we're stuck with just the main server */
+}
diff --git a/srclib/.cvsignore b/srclib/.cvsignore
new file mode 100644 (file)
index 0000000..f3c7a7c
--- /dev/null
@@ -0,0 +1 @@
+Makefile
diff --git a/support/.cvsignore b/support/.cvsignore
new file mode 100644 (file)
index 0000000..399f114
--- /dev/null
@@ -0,0 +1,11 @@
+Makefile
+rotatelogs
+htpasswd
+htdigest
+unescape
+inc2shtml
+httpd_monitor
+suexec
+logresolve
+ab
+apxs
diff --git a/support/.indent.pro b/support/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/support/README b/support/README
new file mode 100644 (file)
index 0000000..80e9caf
--- /dev/null
@@ -0,0 +1,62 @@
+Support files:
+
+ab
+       ABuse your server with this benchmarker. Rudimentary
+       command line testing tool.
+
+apachectl
+       Apache run-time Control script. To facilitate the
+       administrator and/or your rc.d scripts to control the 
+       functioning of the Apache httpd daemon.
+
+apxs
+       APache eXtenSion tool. Eases building and installing
+       DSO style modules.
+
+dbmmanage
+       Create and update user authentication files in the faster
+       DBM format used by mod_auth_db.
+
+htdigest
+       Create and update user authentication files used in
+       DIGEST authentification. See mod_auth_digest.
+
+htpasswd 
+       Create and update user authentication files used in
+       BASIC authentification. I.e. the htpasswd files.
+       See mod_auth.
+
+httpd.8
+       General apache man page.
+
+log_server_status
+       This script is designed to be run at a frequent interval by something
+       like cron.  It connects to the server and downloads the status
+       information.  It reformats the information to a single line and logs
+       it to a file. 
+
+logresolve
+       resolve hostnames for IP-adresses in Apache logfiles
+
+phf_abuse_log.cgi
+       This script can be used to detect people trying to abuse an ancient
+       and long plugged security hole which existed in a CGI script distributed
+       with Apache 1.0.3 and earlier versions.
+
+rotatelogs
+       rotate Apache logs without having to kill the server.
+
+split-logfile
+       This script will take a combined virtual hosts access
+       log file and break its contents into separate files.
+
+suexec
+       Switch User For Exec. Used internally by apache, 
+        see  the  document  `Apache  suEXEC  Support'
+       under http://www.apache.org/docs/suexec.html .
+
+SHA1
+       This directory includes some utilities to allow Apache 1.3.6 to 
+       recognize passwords in SHA1 format, as used by Netscape web 
+       servers. It is not installed by default.
+
diff --git a/support/SHA1/README.sha1 b/support/SHA1/README.sha1
new file mode 100644 (file)
index 0000000..3998e1f
--- /dev/null
@@ -0,0 +1,34 @@
+This directory includes some utilities to allow Apache 1.3.6 to 
+recognize passwords in SHA1 format, as used by Netscape web servers.  
+
+From Netscape's admin interface, export the password database to an 
+ldif file and then use convert.pl in this distribution to generate 
+apache style password files.  
+
+Note: SHA1 support is useful for migration purposes, but is less
+      secure than Apache's password format, since Apache's (MD5)
+      password format uses a random eight character salt to generate
+      one of many possible hashes for the same password.  Netscape
+      uses plain SHA1 without a salt, so the same password
+      will always generate the same hash, making it easier
+      to break since the search space is smaller.
+
+This code was contributed by Clinton Wong <clintdw@netcom.com>.
+
+README.sha1 
+       this file
+
+convert-sha1.pl 
+       takes an ldif dump from Netscape's web server on
+        standard in, outputs apache htpasswd format on standard out.
+
+        Usage: convert.pl < ldif > passwords
+
+htpasswd-sha1.pl
+       perl script to generate entries in apache htpasswd format.
+
+               Usage: htpasswd-sha1.pl some_user some_password
+
+ldif-sha1.example
+       sample ldif dump with one sha1 password and one crypt password.
+
diff --git a/support/SHA1/convert-sha1.pl b/support/SHA1/convert-sha1.pl
new file mode 100644 (file)
index 0000000..3522802
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/perl -w
+use strict;
+
+# This is public domain code.  Do whatever you want with it.
+# It was originally included in Clinton Wong's Apache 1.3.6 SHA1/ldif
+# patch distribution as sample code for converting accounts from
+# ldif format (as used by Netscape web servers) to Apache password format.
+
+my $uid='';
+my $passwd='';
+
+while (my $line = <>) {
+  chomp $line;
+  if ( $line =~ /uid:\s*(.+)/) { $uid = $1 }
+  if ( $line =~ /userpassword:\s*(\{\w+\}.+)/) {
+    $passwd = $1;
+    $passwd =~ s/^\{crypt\}//i;  # Apache stores crypt without a magic string
+  }
+
+  if (length($line)==0) {
+
+    if (length $uid and length $passwd) {
+      print $uid, ':', $passwd, "\n";
+    } # output if we have something to print
+
+    $uid = '';
+    $passwd = '';
+
+  } # if newline
+} # while something to read
+
+# handle last entry if there isn't a newline before EOF
+    if (length $uid and length $passwd) {
+  print $uid, ':', $passwd, "\n";
+}
+
diff --git a/support/SHA1/htpasswd-sha1.pl b/support/SHA1/htpasswd-sha1.pl
new file mode 100644 (file)
index 0000000..ad624d1
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/perl -w
+use strict;
+#
+# Utility which takes a username and password
+# on the command line and generates a username
+# sha1-encrytped password on the stdout.
+# 
+# Typical useage:
+#      ./htpasswd-sha1.pl dirkx MySecret >> sha1-passwd
+#
+# This is public domain code.  Do whatever you want with it.
+# It was originally included in Clinton Wong's Apache 1.3.6 SHA1/ldif
+# patch distribution as sample code for generating entries for
+# Apache password files using SHA1.
+
+use MIME::Base64;  # http://www.cpan.org/modules/by-module/MIME/
+use Digest::SHA1;  # http://www.cpan.org/modules/by-module/MD5/
+
+if ($#ARGV!=1) { die "Usage $0: user password\n" }
+
+print $ARGV[0], ':{SHA}', encode_base64( Digest::SHA1::sha1($ARGV[1]) );
+
diff --git a/support/SHA1/ldif-sha1.example b/support/SHA1/ldif-sha1.example
new file mode 100644 (file)
index 0000000..b8fe917
--- /dev/null
@@ -0,0 +1,19 @@
+dn: cn=someuser
+cn: someuser
+sn: someuser
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+uid: someuser
+userpassword: {SHA}GvF+c3IdvgxAARuC7Uuxp9vjzik=
+
+dn: cn=anotheruser
+cn: anotheruser
+sn: anotheruser
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+uid: anotheruser
+userpassword: {crypt}eFnp.4sz5XnH6
diff --git a/support/ab.c b/support/ab.c
new file mode 100644 (file)
index 0000000..0d968b2
--- /dev/null
@@ -0,0 +1,1100 @@
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+   ** This program is based on ZeusBench V1.0 written by Adam Twiss
+   ** which is Copyright (c) 1996 by Zeus Technology Ltd. http://www.zeustech.net/
+   **
+   ** This software is provided "as is" and any express or implied waranties,
+   ** including but not limited to, the implied warranties of merchantability and
+   ** fitness for a particular purpose are disclaimed.  In no event shall
+   ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
+   ** exemplary, or consequential damaged (including, but not limited to,
+   ** procurement of substitute good or services; loss of use, data, or profits;
+   ** or business interruption) however caused and on theory of liability.  Whether
+   ** in contract, strict liability or tort (including negligence or otherwise)
+   ** arising in any way out of the use of this software, even if advised of the
+   ** possibility of such damage.
+   **
+ */
+
+/*
+   ** HISTORY:
+   **    - Originally written by Adam Twiss <adam@zeus.co.uk>, March 1996
+   **      with input from Mike Belshe <mbelshe@netscape.com> and
+   **      Michael Campanella <campanella@stevms.enet.dec.com>
+   **    - Enhanced by Dean Gaudet <dgaudet@apache.org>, November 1997
+   **    - Cleaned up by Ralf S. Engelschall <rse@apache.org>, March 1998
+   **    - POST and verbosity by Kurt Sussman <kls@merlot.com>, August 1998
+   **    - HTML table output added by David N. Welton <davidw@prosa.it>, January 1999
+   **    - Added Cookie, Arbitrary header and auth support. <dirkx@webweaving.org>, April 199
+   **
+ */
+
+/*
+ * BUGS:
+ *
+ * - uses strcpy/etc.
+ * - has various other poor buffer attacks related to the lazy parsing of
+ *   response headers from the server
+ * - doesn't implement much of HTTP/1.x, only accepts certain forms of
+ *   responses
+ * - (performance problem) heavy use of strstr shows up top in profile
+ *   only an issue for loopback usage
+ */
+
+#define VERSION "1.3a"
+
+/*  -------------------------------------------------------------------- */
+
+/* affects include files on Solaris */
+#define BSD_COMP
+
+/* allow compilation outside an Apache build tree */
+#ifdef NO_APACHE_INCLUDES
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+#define ap_select       select
+#else                          /* (!)NO_APACHE_INCLUDES */
+#include "ap_config.h"
+#include "ap.h"
+#ifdef CHARSET_EBCDIC
+#include "ebcdic.h"
+#endif
+#include <fcntl.h>
+#include <sys/time.h>
+
+#ifndef NO_WRITEV
+#include <sys/types.h>
+#include <sys/uio.h>
+#endif
+
+#endif                         /* NO_APACHE_INCLUDES */
+/* ------------------- DEFINITIONS -------------------------- */
+
+/* maximum number of requests on a time limited test */
+#define MAX_REQUESTS 50000
+
+/* good old state hostname */
+#define STATE_UNCONNECTED 0
+#define STATE_CONNECTING  1
+#define STATE_READ        2
+
+#define CBUFFSIZE       512
+
+struct connection {
+    int fd;
+    int state;
+    int read;                  /* amount of bytes read */
+    int bread;                 /* amount of body read */
+    int length;                        /* Content-Length value used for keep-alive */
+    char cbuff[CBUFFSIZE];     /* a buffer to store server response header */
+    int cbx;                   /* offset in cbuffer */
+    int keepalive;             /* non-zero if a keep-alive request */
+    int gotheader;             /* non-zero if we have the entire header in
+                                * cbuff */
+    struct timeval start, connect, done;
+};
+
+struct data {
+    int read;                  /* number of bytes read */
+    int ctime;                 /* time in ms to connect */
+    int time;                  /* time in ms for connection */
+};
+
+#define ap_min(a,b) ((a)<(b))?(a):(b)
+#define ap_max(a,b) ((a)>(b))?(a):(b)
+
+/* --------------------- GLOBALS ---------------------------- */
+
+int verbosity = 0;             /* no verbosity by default */
+int posting = 0;               /* GET by default */
+int requests = 1;              /* Number of requests to make */
+int concurrency = 1;           /* Number of multiple requests to make */
+int tlimit = 0;                        /* time limit in cs */
+int keepalive = 0;             /* try and do keepalive connections */
+char servername[1024];         /* name that server reports */
+char hostname[1024];           /* host name */
+char path[1024];               /* path name */
+char postfile[1024];           /* name of file containing post data */
+char *postdata;                        /* *buffer containing data from postfile */
+int postlen = 0;               /* length of data to be POSTed */
+char content_type[1024];       /* content type to put in POST header */
+char cookie[1024],             /* optional cookie line */
+     auth[1024],               /* optional (basic/uuencoded)
+                                * authentification */
+     hdrs[4096];               /* optional arbitrary headers */
+int port = 80;                 /* port number */
+
+int use_html = 0;              /* use html in the report */
+char *tablestring;
+char *trstring;
+char *tdstring;
+
+int doclen = 0;                        /* the length the document should be */
+int totalread = 0;             /* total number of bytes read */
+int totalbread = 0;            /* totoal amount of entity body read */
+int totalposted = 0;           /* total number of bytes posted, inc. headers */
+int done = 0;                  /* number of requests we have done */
+int doneka = 0;                        /* number of keep alive connections done */
+int good = 0, bad = 0;         /* number of good and bad requests */
+
+/* store error cases */
+int err_length = 0, err_conn = 0, err_except = 0;
+int err_response = 0;
+
+struct timeval start, endtime;
+
+/* global request (and its length) */
+char request[512];
+int reqlen;
+
+/* one global throw-away buffer to read stuff into */
+char buffer[8192];
+
+struct connection *con;                /* connection array */
+struct data *stats;            /* date for each request */
+
+fd_set readbits, writebits;    /* bits for select */
+struct sockaddr_in server;     /* server addr structure */
+
+/* --------------------------------------------------------- */
+
+/* simple little function to perror and exit */
+
+static void err(char *s)
+{
+    if (errno) {
+       perror(s);
+    }
+    else {
+       printf("%s", s);
+    }
+    exit(errno);
+}
+
+/* --------------------------------------------------------- */
+
+/* write out request to a connection - assumes we can write
+   (small) request out in one go into our new socket buffer  */
+
+static void write_request(struct connection * c)
+{
+#ifndef NO_WRITEV
+    struct iovec out[2]; int outcnt = 1;
+#endif
+    gettimeofday(&c->connect, 0);
+#ifndef NO_WRITEV
+    out[0].iov_base = request;
+    out[0].iov_len = reqlen;
+
+    if (posting) {
+       out[1].iov_base = postdata;
+       out[1].iov_len = postlen;
+       outcnt = 2;
+       totalposted += (reqlen + postlen);
+    }
+    writev(c->fd,out, outcnt);
+#else
+    write(c->fd,request,reqlen);
+    if (posting) {
+        write(c->fd,postdata,postlen);
+        totalposted += (reqlen + postlen);
+    }
+#endif
+
+    c->state = STATE_READ;
+    FD_SET(c->fd, &readbits);
+    FD_CLR(c->fd, &writebits);
+}
+
+/* --------------------------------------------------------- */
+
+/* make an fd non blocking */
+
+static void nonblock(int fd)
+{
+    int i = 1;
+    ioctl(fd, FIONBIO, &i);
+}
+
+/* --------------------------------------------------------- */
+
+/* returns the time in ms between two timevals */
+
+static int timedif(struct timeval a, struct timeval b)
+{
+    register int us, s;
+
+    us = a.tv_usec - b.tv_usec;
+    us /= 1000;
+    s = a.tv_sec - b.tv_sec;
+    s *= 1000;
+    return s + us;
+}
+
+/* --------------------------------------------------------- */
+
+/* calculate and output results */
+
+static void output_results(void)
+{
+    int timetaken;
+
+    gettimeofday(&endtime, 0);
+    timetaken = timedif(endtime, start);
+
+    printf("\r                                                                           \r");
+    printf("Server Software:        %s\n", servername);
+    printf("Server Hostname:        %s\n", hostname);
+    printf("Server Port:            %d\n", port);
+    printf("\n");
+    printf("Document Path:          %s\n", path);
+    printf("Document Length:        %d bytes\n", doclen);
+    printf("\n");
+    printf("Concurrency Level:      %d\n", concurrency);
+    printf("Time taken for tests:   %d.%03d seconds\n",
+          timetaken / 1000, timetaken % 1000);
+    printf("Complete requests:      %d\n", done);
+    printf("Failed requests:        %d\n", bad);
+    if (bad)
+       printf("   (Connect: %d, Length: %d, Exceptions: %d)\n",
+              err_conn, err_length, err_except);
+    if (err_response)
+       printf("Non-2xx responses:      %d\n", err_response);
+    if (keepalive)
+       printf("Keep-Alive requests:    %d\n", doneka);
+    printf("Total transferred:      %d bytes\n", totalread);
+    if (posting)
+       printf("Total POSTed:           %d\n", totalposted);
+    printf("HTML transferred:       %d bytes\n", totalbread);
+
+    /* avoid divide by zero */
+    if (timetaken) {
+       printf("Requests per second:    %.2f\n", 1000 * (float) (done) / timetaken);
+       printf("Transfer rate:          %.2f kb/s received\n",
+              (float) (totalread) / timetaken);
+       if (posting) {
+           printf("                        %.2f kb/s sent\n",
+                  (float) (totalposted) / timetaken);
+           printf("                        %.2f kb/s total\n",
+                  (float) (totalread + totalposted) / timetaken);
+       }
+    }
+
+    {
+       /* work out connection times */
+       int i;
+       int totalcon = 0, total = 0;
+       int mincon = 9999999, mintot = 999999;
+       int maxcon = 0, maxtot = 0;
+
+       for (i = 0; i < requests; i++) {
+           struct data s = stats[i];
+           mincon = ap_min(mincon, s.ctime);
+           mintot = ap_min(mintot, s.time);
+           maxcon = ap_max(maxcon, s.ctime);
+           maxtot = ap_max(maxtot, s.time);
+           totalcon += s.ctime;
+           total += s.time;
+       }
+       if (requests > 0) { /* avoid division by zero (if 0 requests) */
+           printf("\nConnnection Times (ms)\n");
+           printf("              min   avg   max\n");
+           printf("Connect:    %5d %5d %5d\n", mincon, totalcon / requests, maxcon);
+           printf("Processing: %5d %5d %5d\n",
+                  mintot - mincon, (total / requests) - (totalcon / requests),
+                  maxtot - maxcon);
+           printf("Total:      %5d %5d %5d\n", mintot, total / requests, maxtot);
+       }
+    }
+}
+
+/* --------------------------------------------------------- */
+
+/* calculate and output results in HTML  */
+
+static void output_html_results(void)
+{
+    int timetaken;
+
+    gettimeofday(&endtime, 0);
+    timetaken = timedif(endtime, start);
+
+    printf("\n\n<table %s>\n", tablestring);
+    printf("<tr %s><th colspan=2 %s>Server Software:</th>"
+          "<td colspan=2 %s>%s</td></tr>\n",
+          trstring, tdstring, tdstring, servername);
+    printf("<tr %s><th colspan=2 %s>Server Hostname:</th>"
+          "<td colspan=2 %s>%s</td></tr>\n",
+          trstring, tdstring, tdstring, hostname);
+    printf("<tr %s><th colspan=2 %s>Server Port:</th>"
+          "<td colspan=2 %s>%d</td></tr>\n",
+          trstring, tdstring, tdstring, port);
+    printf("<tr %s><th colspan=2 %s>Document Path:</th>"
+          "<td colspan=2 %s>%s</td></tr>\n",
+          trstring, tdstring, tdstring, path);
+    printf("<tr %s><th colspan=2 %s>Document Length:</th>"
+          "<td colspan=2 %s>%d bytes</td></tr>\n",
+          trstring, tdstring, tdstring, doclen);
+    printf("<tr %s><th colspan=2 %s>Concurrency Level:</th>"
+          "<td colspan=2 %s>%d</td></tr>\n",
+          trstring, tdstring, tdstring, concurrency);
+    printf("<tr %s><th colspan=2 %s>Time taken for tests:</th>"
+          "<td colspan=2 %s>%d.%03d seconds</td></tr>\n",
+          trstring, tdstring, tdstring, timetaken / 1000, timetaken % 1000);
+    printf("<tr %s><th colspan=2 %s>Complete requests:</th>"
+          "<td colspan=2 %s>%d</td></tr>\n",
+          trstring, tdstring, tdstring, done);
+    printf("<tr %s><th colspan=2 %s>Failed requests:</th>"
+          "<td colspan=2 %s>%d</td></tr>\n",
+          trstring, tdstring, tdstring, bad);
+    if (bad)
+       printf("<tr %s><td colspan=4 %s >   (Connect: %d, Length: %d, Exceptions: %d)</td></tr>\n",
+              trstring, tdstring, err_conn, err_length, err_except);
+    if (err_response)
+       printf("<tr %s><th colspan=2 %s>Non-2xx responses:</th>"
+              "<td colspan=2 %s>%d</td></tr>\n",
+              trstring, tdstring, tdstring, err_response);
+    if (keepalive)
+       printf("<tr %s><th colspan=2 %s>Keep-Alive requests:</th>"
+              "<td colspan=2 %s>%d</td></tr>\n",
+              trstring, tdstring, tdstring, doneka);
+    printf("<tr %s><th colspan=2 %s>Total transferred:</th>"
+          "<td colspan=2 %s>%d bytes</td></tr>\n",
+          trstring, tdstring, tdstring, totalread);
+    if (posting)
+       printf("<tr %s><th colspan=2 %s>Total POSTed:</th>"
+              "<td colspan=2 %s>%d</td></tr>\n",
+              trstring, tdstring, tdstring, totalposted);
+    printf("<tr %s><th colspan=2 %s>HTML transferred:</th>"
+          "<td colspan=2 %s>%d bytes</td></tr>\n",
+          trstring, tdstring, tdstring, totalbread);
+
+    /* avoid divide by zero */
+    if (timetaken) {
+       printf("<tr %s><th colspan=2 %s>Requests per second:</th>"
+              "<td colspan=2 %s>%.2f</td></tr>\n",
+          trstring, tdstring, tdstring, 1000 * (float) (done) / timetaken);
+       printf("<tr %s><th colspan=2 %s>Transfer rate:</th>"
+              "<td colspan=2 %s>%.2f kb/s received</td></tr>\n",
+            trstring, tdstring, tdstring, (float) (totalread) / timetaken);
+       if (posting) {
+           printf("<tr %s><td colspan=2 %s>&nbsp;</td>"
+                  "<td colspan=2 %s>%.2f kb/s sent</td></tr>\n",
+                  trstring, tdstring, tdstring,
+                  (float) (totalposted) / timetaken);
+           printf("<tr %s><td colspan=2 %s>&nbsp;</td>"
+                  "<td colspan=2 %s>%.2f kb/s total</td></tr>\n",
+                  trstring, tdstring, tdstring,
+                  (float) (totalread + totalposted) / timetaken);
+       }
+    }
+
+    {
+       /* work out connection times */
+       int i;
+       int totalcon = 0, total = 0;
+       int mincon = 9999999, mintot = 999999;
+       int maxcon = 0, maxtot = 0;
+
+       for (i = 0; i < requests; i++) {
+           struct data s = stats[i];
+           mincon = ap_min(mincon, s.ctime);
+           mintot = ap_min(mintot, s.time);
+           maxcon = ap_max(maxcon, s.ctime);
+           maxtot = ap_max(maxtot, s.time);
+           totalcon += s.ctime;
+           total += s.time;
+       }
+
+       if (requests > 0) { /* avoid division by zero (if 0 requests) */
+           printf("<tr %s><th %s colspan=4>Connnection Times (ms)</th></tr>\n",
+                  trstring, tdstring);
+           printf("<tr %s><th %s>&nbsp;</th> <th %s>min</th>   <th %s>avg</th>   <th %s>max</th></tr>\n",
+                  trstring, tdstring, tdstring, tdstring, tdstring);
+           printf("<tr %s><th %s>Connect:</th>"
+                  "<td %s>%5d</td>"
+                  "<td %s>%5d</td>"
+                  "<td %s>%5d</td></tr>\n",
+                  trstring, tdstring, tdstring, mincon, tdstring, totalcon / requests, tdstring, maxcon);
+           printf("<tr %s><th %s>Processing:</th>"
+                  "<td %s>%5d</td>"
+                  "<td %s>%5d</td>"
+                  "<td %s>%5d</td></tr>\n",
+                  trstring, tdstring, tdstring, mintot - mincon, tdstring,
+                  (total / requests) - (totalcon / requests), tdstring, maxtot - maxcon);
+           printf("<tr %s><th %s>Total:</th>"
+                  "<td %s>%5d</td>"
+                  "<td %s>%5d</td>"
+                  "<td %s>%5d</td></tr>\n",
+                  trstring, tdstring, tdstring, mintot, tdstring, total / requests, tdstring, maxtot);
+       }
+       printf("</table>\n");
+    }
+}
+
+/* --------------------------------------------------------- */
+
+/* start asnchronous non-blocking connection */
+
+static void start_connect(struct connection * c)
+{
+    c->read = 0;
+    c->bread = 0;
+    c->keepalive = 0;
+    c->cbx = 0;
+    c->gotheader = 0;
+
+    c->fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (c->fd < 0)
+       err("socket");
+
+    nonblock(c->fd);
+    gettimeofday(&c->start, 0);
+
+    if (connect(c->fd, (struct sockaddr *) & server, sizeof(server)) < 0) {
+       if (errno == EINPROGRESS) {
+           c->state = STATE_CONNECTING;
+           FD_SET(c->fd, &writebits);
+           return;
+       }
+       else {
+           close(c->fd);
+           err_conn++;
+           if (bad++ > 10) {
+               err("\nTest aborted after 10 failures\n\n");
+           }
+           start_connect(c);
+       }
+    }
+
+    /* connected first time */
+    write_request(c);
+}
+
+/* --------------------------------------------------------- */
+
+/* close down connection and save stats */
+
+static void close_connection(struct connection * c)
+{
+    if (c->read == 0 && c->keepalive) {
+       /* server has legitimately shut down an idle keep alive request */
+       good--;                 /* connection never happend */
+    }
+    else {
+       if (good == 1) {
+           /* first time here */
+           doclen = c->bread;
+       }
+       else if (c->bread != doclen) {
+           bad++;
+           err_length++;
+       }
+
+       /* save out time */
+       if (done < requests) {
+           struct data s;
+           gettimeofday(&c->done, 0);
+           s.read = c->read;
+           s.ctime = timedif(c->connect, c->start);
+           s.time = timedif(c->done, c->start);
+           stats[done++] = s;
+       }
+    }
+
+    close(c->fd);
+    FD_CLR(c->fd, &readbits);
+    FD_CLR(c->fd, &writebits);
+
+    /* connect again */
+    start_connect(c);
+    return;
+}
+
+/* --------------------------------------------------------- */
+
+/* read data from connection */
+
+static void read_connection(struct connection * c)
+{
+    int r;
+    char *part;
+    char respcode[4];          /* 3 digits and null */
+
+    r = read(c->fd, buffer, sizeof(buffer));
+    if (r == 0 || (r < 0 && errno != EAGAIN)) {
+       good++;
+       close_connection(c);
+       return;
+    }
+
+    if (r < 0 && errno == EAGAIN)
+       return;
+
+    c->read += r;
+    totalread += r;
+
+    if (!c->gotheader) {
+       char *s;
+       int l = 4;
+       int space = CBUFFSIZE - c->cbx - 1;     /* -1 to allow for 0
+                                                * terminator */
+       int tocopy = (space < r) ? space : r;
+#ifndef CHARSET_EBCDIC
+       memcpy(c->cbuff + c->cbx, buffer, tocopy);
+#else                          /* CHARSET_EBCDIC */
+       ascii2ebcdic(c->cbuff + c->cbx, buffer, tocopy);
+#endif                         /* CHARSET_EBCDIC */
+       c->cbx += tocopy;
+       space -= tocopy;
+       c->cbuff[c->cbx] = 0;   /* terminate for benefit of strstr */
+       if (verbosity >= 4) {
+           printf("LOG: header received:\n%s\n", c->cbuff);
+       }
+       s = strstr(c->cbuff, "\r\n\r\n");
+       /*
+        * this next line is so that we talk to NCSA 1.5 which blatantly
+        * breaks the http specification
+        */
+       if (!s) {
+           s = strstr(c->cbuff, "\n\n");
+           l = 2;
+       }
+
+       if (!s) {
+           /* read rest next time */
+           if (space)
+               return;
+           else {
+               /* header is in invalid or too big - close connection */
+               close(c->fd);
+               if (bad++ > 10) {
+                   err("\nTest aborted after 10 failures\n\n");
+               }
+               FD_CLR(c->fd, &writebits);
+               start_connect(c);
+           }
+       }
+       else {
+           /* have full header */
+           if (!good) {
+               /* this is first time, extract some interesting info */
+               char *p, *q;
+               p = strstr(c->cbuff, "Server:");
+               q = servername;
+               if (p) {
+                   p += 8;
+                   while (*p > 32)
+                       *q++ = *p++;
+               }
+               *q = 0;
+           }
+
+           /*
+            * XXX: this parsing isn't even remotely HTTP compliant... but in
+            * the interest of speed it doesn't totally have to be, it just
+            * needs to be extended to handle whatever servers folks want to
+            * test against. -djg
+            */
+
+           /* check response code */
+           part = strstr(c->cbuff, "HTTP");    /* really HTTP/1.x_ */
+           strncpy(respcode, (part + strlen("HTTP/1.x_")), 3);
+           respcode[3] = '\0';
+           if (respcode[0] != '2') {
+               err_response++;
+               if (verbosity >= 2)
+                   printf("WARNING: Response code not 2xx (%s)\n", respcode);
+           }
+           else if (verbosity >= 3) {
+               printf("LOG: Response code = %s\n", respcode);
+           }
+
+           c->gotheader = 1;
+           *s = 0;             /* terminate at end of header */
+           if (keepalive &&
+               (strstr(c->cbuff, "Keep-Alive")
+                || strstr(c->cbuff, "keep-alive"))) {  /* for benefit of MSIIS */
+               char *cl;
+               cl = strstr(c->cbuff, "Content-Length:");
+               /* handle NCSA, which sends Content-length: */
+               if (!cl)
+                   cl = strstr(c->cbuff, "Content-length:");
+               if (cl) {
+                   c->keepalive = 1;
+                   c->length = atoi(cl + 16);
+               }
+           }
+           c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy;
+           totalbread += c->bread;
+       }
+    }
+    else {
+       /* outside header, everything we have read is entity body */
+       c->bread += r;
+       totalbread += r;
+    }
+
+    if (c->keepalive && (c->bread >= c->length)) {
+       /* finished a keep-alive connection */
+       good++;
+       doneka++;
+       /* save out time */
+       if (good == 1) {
+           /* first time here */
+           doclen = c->bread;
+       }
+       else if (c->bread != doclen) {
+           bad++;
+           err_length++;
+       }
+       if (done < requests) {
+           struct data s;
+           gettimeofday(&c->done, 0);
+           s.read = c->read;
+           s.ctime = timedif(c->connect, c->start);
+           s.time = timedif(c->done, c->start);
+           stats[done++] = s;
+       }
+       c->keepalive = 0;
+       c->length = 0;
+       c->gotheader = 0;
+       c->cbx = 0;
+       c->read = c->bread = 0;
+       write_request(c);
+       c->start = c->connect;  /* zero connect time with keep-alive */
+    }
+}
+
+/* --------------------------------------------------------- */
+
+/* run the tests */
+
+static void test(void)
+{
+    struct timeval timeout, now;
+    fd_set sel_read, sel_except, sel_write;
+    int i;
+
+    if (!use_html) {
+       printf("Benchmarking %s (be patient)...", hostname);
+       fflush(stdout);
+    }
+
+    {
+       /* get server information */
+       struct hostent *he;
+       he = gethostbyname(hostname);
+       if (!he)
+           err("bad hostname");
+       server.sin_family = he->h_addrtype;
+       server.sin_port = htons(port);
+       server.sin_addr.s_addr = ((unsigned long *) (he->h_addr_list[0]))[0];
+    }
+
+    con = malloc(concurrency * sizeof(struct connection));
+    memset(con, 0, concurrency * sizeof(struct connection));
+
+    stats = malloc(requests * sizeof(struct data));
+
+    FD_ZERO(&readbits);
+    FD_ZERO(&writebits);
+
+    /* setup request */
+    if (!posting) {
+       sprintf(request, "GET %s HTTP/1.0\r\n"
+               "User-Agent: ApacheBench/%s\r\n"
+               "%s" "%s" "%s"
+               "Host: %s\r\n"
+               "Accept: */*\r\n"
+               "\r\n" "%s",
+               path,
+               VERSION,
+               keepalive ? "Connection: Keep-Alive\r\n" : "",
+               cookie, auth, hostname, hdrs);
+    }
+    else {
+       sprintf(request, "POST %s HTTP/1.0\r\n"
+               "User-Agent: ApacheBench/%s\r\n"
+               "%s" "%s" "%s"
+               "Host: %s\r\n"
+               "Accept: */*\r\n"
+               "Content-length: %d\r\n"
+               "Content-type: %s\r\n"
+               "%s"
+               "\r\n",
+               path,
+               VERSION,
+               keepalive ? "Connection: Keep-Alive\r\n" : "",
+               cookie, auth,
+               hostname, postlen,
+               (content_type[0]) ? content_type : "text/plain", hdrs);
+    }
+
+    if (verbosity >= 2)
+       printf("INFO: POST header == \n---\n%s\n---\n", request);
+
+    reqlen = strlen(request);
+
+#ifdef CHARSET_EBCDIC
+    ebcdic2ascii(request, request, reqlen);
+#endif                         /* CHARSET_EBCDIC */
+
+    /* ok - lets start */
+    gettimeofday(&start, 0);
+
+    /* initialise lots of requests */
+    for (i = 0; i < concurrency; i++)
+       start_connect(&con[i]);
+
+    while (done < requests) {
+       int n;
+       /* setup bit arrays */
+       memcpy(&sel_except, &readbits, sizeof(readbits));
+       memcpy(&sel_read, &readbits, sizeof(readbits));
+       memcpy(&sel_write, &writebits, sizeof(readbits));
+
+       /* check for time limit expiry */
+       gettimeofday(&now, 0);
+       if (tlimit && timedif(now, start) > (tlimit * 1000)) {
+           requests = done;    /* so stats are correct */
+       }
+
+       /* Timeout of 30 seconds. */
+       timeout.tv_sec = 30;
+       timeout.tv_usec = 0;
+       n = ap_select(FD_SETSIZE, &sel_read, &sel_write, &sel_except, &timeout);
+       if (!n) {
+           err("\nServer timed out\n\n");
+       }
+       if (n < 1)
+           err("select");
+
+       for (i = 0; i < concurrency; i++) {
+           int s = con[i].fd;
+           if (FD_ISSET(s, &sel_except)) {
+               bad++;
+               err_except++;
+               start_connect(&con[i]);
+               continue;
+           }
+           if (FD_ISSET(s, &sel_read))
+               read_connection(&con[i]);
+           if (FD_ISSET(s, &sel_write))
+               write_request(&con[i]);
+       }
+    }
+    if (use_html)
+       output_html_results();
+    else
+       output_results();
+}
+
+/* ------------------------------------------------------- */
+
+/* display copyright information */
+static void copyright(void)
+{
+    if (!use_html) {
+       printf("This is ApacheBench, Version %s\n", VERSION);
+       printf("Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n");
+       printf("Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/\n");
+       printf("\n");
+    }
+    else {
+       printf("<p>\n");
+       printf(" This is ApacheBench, Version %s<br>\n", VERSION);
+       printf(" Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/<br>\n");
+       printf(" Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/<br>\n");
+       printf("</p>\n<p>\n");
+    }
+}
+
+/* display usage information */
+static void usage(char *progname)
+{
+    fprintf(stderr, "Usage: %s [options] [http://]hostname[:port]/path\n", progname);
+    fprintf(stderr, "Options are:\n");
+    fprintf(stderr, "    -n requests     Number of requests to perform\n");
+    fprintf(stderr, "    -c concurrency  Number of multiple requests to make\n");
+    fprintf(stderr, "    -t timelimit    Seconds to max. wait for responses\n");
+    fprintf(stderr, "    -p postfile     File containg data to POST\n");
+    fprintf(stderr, "    -T content-type Content-type header for POSTing\n");
+    fprintf(stderr, "    -v verbosity    How much troubleshooting info to print\n");
+    fprintf(stderr, "    -w              Print out results in HTML tables\n");
+    fprintf(stderr, "    -x attributes   String to insert as table attributes\n");
+    fprintf(stderr, "    -y attributes   String to insert as tr attributes\n");
+    fprintf(stderr, "    -z attributes   String to insert as td or th attributes\n");
+    fprintf(stderr, "    -C attribute    Add cookie, eg. 'Apache=1234. (repeatable)\n");
+    fprintf(stderr, "    -H attribute    Add Arbitrary header line, eg. 'Accept-Encoding: zop'\n");
+    fprintf(stderr, "                    Inserted after all normal header lines. (repeatable)\n");
+    fprintf(stderr, "    -A attribute    Add Basic WWW Authentication, the attributes\n");
+    fprintf(stderr, "                    are a colon separated username and password.\n");
+    fprintf(stderr, "    -p attribute    Add Basic Proxy Authentication, the attributes\n");
+    fprintf(stderr, "                    are a colon separated username and password.\n");
+    fprintf(stderr, "    -V              Print version number and exit\n");
+    fprintf(stderr, "    -k              Use HTTP KeepAlive feature\n");
+    fprintf(stderr, "    -h              Display usage information (this message)\n");
+    exit(EINVAL);
+}
+
+/* ------------------------------------------------------- */
+
+/* split URL into parts */
+
+static int parse_url(char *url)
+{
+    char *cp;
+    char *h;
+    char *p = NULL;
+
+    if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0)
+       url += 7;
+    h = url;
+    if ((cp = strchr(url, ':')) != NULL) {
+       *cp++ = '\0';
+       p = cp;
+       url = cp;
+    }
+    if ((cp = strchr(url, '/')) == NULL)
+       return 1;
+    strcpy(path, cp);
+    *cp = '\0';
+    strcpy(hostname, h);
+    if (p != NULL)
+       port = atoi(p);
+    return 0;
+}
+
+/* ------------------------------------------------------- */
+
+/* read data to POST from file, save contents and length */
+
+static int open_postfile(char *pfile)
+{
+    int postfd, status;
+    struct stat postfilestat;
+
+    if ((postfd = open(pfile, O_RDONLY)) == -1) {
+       printf("Invalid postfile name (%s)\n", pfile);
+       return errno;
+    }
+    if ((status = fstat(postfd, &postfilestat)) == -1) {
+       perror("Can\'t stat postfile\n");
+       return status;
+    }
+    postdata = malloc(postfilestat.st_size);
+    if (!postdata) {
+       printf("Can\'t alloc postfile buffer\n");
+       return ENOMEM;
+    }
+    if (read(postfd, postdata, postfilestat.st_size) != postfilestat.st_size) {
+       printf("error reading postfilen");
+       return EIO;
+    }
+    postlen = postfilestat.st_size;
+    return 0;
+}
+
+/* ------------------------------------------------------- */
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+/* sort out command-line args and call test */
+int main(int argc, char **argv)
+{
+    int c, r,l;
+    char tmp[1024];
+
+    /* table defaults  */
+    tablestring = "";
+    trstring = "";
+    tdstring = "bgcolor=white";
+    cookie[0] = '\0';
+    auth[0] = '\0';
+    hdrs[0] = '\0';
+    optind = 1;
+    while ((c = getopt(argc, argv, "n:c:t:T:p:v:kVhwx:y:z:C:H:P:A:")) > 0) {
+       switch (c) {
+       case 'n':
+           requests = atoi(optarg);
+           if (!requests) {
+               err("Invalid number of requests\n");
+           }
+           break;
+       case 'k':
+           keepalive = 1;
+           break;
+       case 'c':
+           concurrency = atoi(optarg);
+           break;
+       case 'p':
+           if (0 == (r = open_postfile(optarg))) {
+               posting = 1;
+           }
+           else if (postdata) {
+               exit(r);
+           }
+           break;
+       case 'v':
+           verbosity = atoi(optarg);
+           break;
+       case 't':
+           tlimit = atoi(optarg);
+           requests = MAX_REQUESTS;    /* need to size data array on
+                                        * something */
+           break;
+       case 'T':
+           strcpy(content_type, optarg);
+           break;
+       case 'C': 
+           strncat(cookie, "Cookie: ", sizeof(cookie));
+           strncat(cookie, optarg, sizeof(cookie));
+           strncat(cookie, "\r\n", sizeof(cookie));
+           break;
+       case 'A': 
+           /* assume username passwd already to be in colon separated form. Ready
+            * to be uu-encoded.
+            */
+           while(isspace(*optarg))
+               optarg++;
+           l=ap_base64encode(tmp,optarg,strlen(optarg));
+           tmp[l]='\0';
+
+           strncat(auth, "Authorization: basic ", sizeof(auth));
+           strncat(auth, tmp, sizeof(auth));
+           strncat(auth, "\r\n", sizeof(auth));
+           break;
+       case 'P':
+           /*
+            * assume username passwd already to be in colon separated form.
+            */
+           while(isspace(*optarg))
+               optarg++;
+           l=ap_base64encode(tmp,optarg,strlen(optarg));
+           tmp[l]='\0';
+
+           strncat(auth, "Proxy-Authorization: basic ", sizeof(auth));
+           strncat(auth, tmp, sizeof(auth));
+           strncat(auth, "\r\n", sizeof(auth));
+           break;
+       case 'H':
+           strncat(hdrs, optarg, sizeof(hdrs));
+           strncat(hdrs, "\r\n", sizeof(hdrs));
+           break;
+       case 'V':
+           copyright();
+           exit(0);
+           break;
+       case 'w':
+           use_html = 1;
+           break;
+           /*
+            * if any of the following three are used, turn on html output
+            * automatically
+            */
+       case 'x':
+           use_html = 1;
+           tablestring = optarg;
+           break;
+       case 'y':
+           use_html = 1;
+           trstring = optarg;
+           break;
+       case 'z':
+           use_html = 1;
+           tdstring = optarg;
+           break;
+       case 'h':
+           usage(argv[0]);
+           break;
+       default:
+           fprintf(stderr, "%s: invalid option `%c'\n", argv[0], c);
+           usage(argv[0]);
+           break;
+       }
+    }
+    if (optind != argc - 1) {
+       fprintf(stderr, "%s: wrong number of arguments\n", argv[0]);
+       usage(argv[0]);
+    }
+
+    if (parse_url(argv[optind++])) {
+       fprintf(stderr, "%s: invalid URL\n", argv[0]);
+       usage(argv[0]);
+    }
+
+    copyright();
+    test();
+
+    exit(0);
+}
diff --git a/support/apxs.in b/support/apxs.in
new file mode 100644 (file)
index 0000000..3bdd35d
--- /dev/null
@@ -0,0 +1,655 @@
+#!/usr/local/bin/perl
+## ====================================================================
+## Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions
+## are met:
+##
+## 1. Redistributions of source code must retain the above copyright
+##    notice, this list of conditions and the following disclaimer. 
+##
+## 2. Redistributions in binary form must reproduce the above copyright
+##    notice, this list of conditions and the following disclaimer in
+##    the documentation and/or other materials provided with the
+##    distribution.
+##
+## 3. All advertising materials mentioning features or use of this
+##    software must display the following acknowledgment:
+##    "This product includes software developed by the Apache Group
+##    for use in the Apache HTTP server project (http://www.apache.org/)."
+##
+## 4. The names "Apache Server" and "Apache Group" must not be used to
+##    endorse or promote products derived from this software without
+##    prior written permission. For written permission, please contact
+##    apache@apache.org.
+##
+## 5. Products derived from this software may not be called "Apache"
+##    nor may "Apache" appear in their names without prior written
+##    permission of the Apache Group.
+##
+## 6. Redistributions of any form whatsoever must retain the following
+##    acknowledgment:
+##    "This product includes software developed by the Apache Group
+##    for use in the Apache HTTP server project (http://www.apache.org/)."
+##
+## THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+## EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+## PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+## ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+## STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+## OF THE POSSIBILITY OF SUCH DAMAGE.
+## ====================================================================
+##
+## This software consists of voluntary contributions made by many
+## individuals on behalf of the Apache Group and was originally based
+## on public domain software written at the National Center for
+## Supercomputing Applications, University of Illinois, Urbana-Champaign.
+## For more information on the Apache Group and the Apache HTTP server
+## project, please see <http://www.apache.org/>.
+##
+
+##
+##  apxs -- APache eXtenSion tool
+##  Written by Ralf S. Engelschall <rse@apache.org>
+##
+
+require 5.003;
+use strict;
+package apxs;
+
+##
+##  Configuration
+##
+
+my $CFG_TARGET        = '@TARGET@';            # substituted via Makefile.tmpl 
+my $CFG_CC            = '@CC@';                # substituted via Makefile.tmpl
+my $CFG_CFLAGS        = '@CFLAGS@';            # substituted via Makefile.tmpl
+my $CFG_CFLAGS_SHLIB  = '@CFLAGS_SHLIB@';      # substituted via Makefile.tmpl
+my $CFG_LD_SHLIB      = '@LD_SHLIB@';          # substituted via Makefile.tmpl
+my $CFG_LDFLAGS_SHLIB = '@LDFLAGS_MOD_SHLIB@'; # substituted via Makefile.tmpl 
+my $CFG_LIBS_SHLIB    = '@LIBS_SHLIB@';        # substituted via Makefile.tmpl 
+my $CFG_PREFIX        = '@prefix@';            # substituted via APACI install
+my $CFG_SBINDIR       = '@sbindir@';           # substituted via APACI install
+my $CFG_INCLUDEDIR    = '@includedir@';        # substituted via APACI install
+my $CFG_LIBEXECDIR    = '@libexecdir@';        # substituted via APACI install
+my $CFG_SYSCONFDIR    = '@sysconfdir@';        # substituted via APACI install
+
+##
+##  Cleanup the above stuff
+##
+$CFG_CFLAGS =~ s|^\s+||;
+$CFG_CFLAGS =~ s|\s+$||;
+$CFG_CFLAGS =~ s|\s+`.+apaci`||;
+
+##
+##  Initial shared object support check
+##
+if (not -x "$CFG_SBINDIR/$CFG_TARGET") {
+       print STDERR "apxs:Error: $CFG_SBINDIR/$CFG_TARGET not found or not executable\n";
+       exit(1);
+}
+if (not grep(/mod_so/, `$CFG_SBINDIR/$CFG_TARGET -l`)) {
+    print STDERR "apxs:Error: Sorry, no shared object support for Apache\n";
+    print STDERR "apxs:Error: available under your platform. Make sure\n";
+    print STDERR "apxs:Error: the Apache module mod_so is compiled into\n";
+    print STDERR "apxs:Error: your server binary `$CFG_SBINDIR/$CFG_TARGET'.\n";
+    exit(1);
+}
+
+##
+##  parse argument line
+##
+
+#   defaults for parameters
+my $opt_n = '';
+my $opt_g = '';
+my $opt_c = 0;
+my $opt_o = '';
+my @opt_D = ();
+my @opt_I = ();
+my @opt_L = ();
+my @opt_l = ();
+my @opt_W = ();
+my @opt_S = ();
+my $opt_e = 0;
+my $opt_i = 0;
+my $opt_a = 0;
+my $opt_A = 0;
+my $opt_q = 0;
+
+#   this subroutine is derived from Perl's getopts.pl with the enhancement of
+#   the "+" metacharater at the format string to allow a list to be build by
+#   subsequent occurance of the same option.
+sub Getopts {
+    my ($argumentative, @ARGV) = @_;
+    my (@args, $first, $rest, $pos);
+    my ($errs) = 0;
+    local ($_);
+    local ($[) = 0;
+
+    @args = split( / */, $argumentative);
+    while(@ARGV && ($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
+        ($first, $rest) = ($1,$2);
+        if ($_ =~ m|^--$|) {
+            shift(@ARGV);
+            last;
+        }
+        $pos = index($argumentative,$first);
+        if($pos >= $[) {
+            if($args[$pos+1] eq ':') {
+                shift(@ARGV);
+                if($rest eq '') {
+                    unless (@ARGV) {
+                        print STDERR "apxs:Error: Incomplete option: $first (needs an argument)\n";
+                        ++$errs;
+                    }
+                    $rest = shift(@ARGV);
+                }
+                eval "\$opt_$first = \$rest;";
+            }
+            elsif ($args[$pos+1] eq '+') {
+                shift(@ARGV);
+                if($rest eq '') {
+                    unless (@ARGV) {
+                        print STDERR "apxs:Error: Incomplete option: $first (needs an argument)\n";
+                        ++$errs;
+                    }
+                    $rest = shift(@ARGV);
+                }
+                eval "push(\@opt_$first, \$rest);";
+            }
+            else {
+                eval "\$opt_$first = 1";
+                if($rest eq '') {
+                    shift(@ARGV);
+                }
+                else {
+                    $ARGV[0] = "-$rest";
+                }
+            }
+        }
+        else {
+            print STDERR "apxs:Error: Unknown option: $first\n";
+            ++$errs;
+            if($rest ne '') {
+                $ARGV[0] = "-$rest";
+            }
+            else {
+                shift(@ARGV);
+            }
+        }
+    }
+    return ($errs == 0, @ARGV);
+}
+
+sub usage {
+    print STDERR "Usage: apxs -g [-S <var>=<val>] -n <modname>\n";
+    print STDERR "       apxs -q [-S <var>=<val>] <query> ...\n";
+    print STDERR "       apxs -c [-S <var>=<val>] [-o <dsofile>] [-D <name>[=<value>]]\n";
+    print STDERR "               [-I <incdir>] [-L <libdir>] [-l <libname>] [-Wc,<flags>]\n";
+    print STDERR "               [-Wl,<flags>] <files> ...\n";
+    print STDERR "       apxs -i [-S <var>=<val>] [-a] [-A] [-n <modname>] <dsofile> ...\n";
+    print STDERR "       apxs -e [-S <var>=<val>] [-a] [-A] [-n <modname>] <dsofile> ...\n";
+    exit(1);
+}
+
+#   option handling
+my $rc;
+($rc, @ARGV) = &Getopts("qn:gco:I+D+L+l+W+S+eiaA", @ARGV);
+&usage if ($rc == 0);
+&usage if ($#ARGV == -1 and not $opt_g);
+&usage if (not $opt_q and not ($opt_g and $opt_n) and not $opt_i and not $opt_c and not $opt_e);
+
+#   argument handling
+my @args = @ARGV;
+my $name = 'unknown';
+$name = $opt_n if ($opt_n ne '');
+
+if (@opt_S) {
+    my ($opt_S);
+    foreach $opt_S (@opt_S) {
+       if ($opt_S =~ m/^([^=]+)=(.*)$/) {
+           my ($var) = $1;
+           my ($val) = $2;
+           my $oldval = eval "\$CFG_$var";
+
+           unless ($var and $oldval) {
+               print STDERR "apxs:Error: no config variable $var\n";
+               &usage;
+           }
+
+           eval "\$CFG_${var}=\"${val}\"";
+       } else {
+           print STDERR "apxs:Error: malformatted -S option\n";
+           &usage;
+       }       
+    }
+}
+
+##
+##  Operation
+##
+
+#   helper function for executing a list of
+#   system command with return code checks
+sub execute_cmds {
+    my (@cmds) = @_;
+    my ($cmd, $rc);
+
+    foreach $cmd (@cmds) {
+        print STDERR "$cmd\n";
+        $rc = system("$cmd");
+        if ($rc != 0) {
+            printf(STDERR "apxs:Break: Command failed with rc=%d\n", $rc << 8);
+            exit(1);
+        }
+    }
+}
+
+if ($opt_g) {
+    ##
+    ##  SAMPLE MODULE SOURCE GENERATION
+    ##
+
+    if (-d $name) {
+        print STDERR "apxs:Error: Directory `$name' already exists. Remove first\n";
+        exit(1);
+    }
+
+    my $data = join('', <DATA>);
+    $data =~ s|%NAME%|$name|sg;
+    $data =~ s|%TARGET%|$CFG_TARGET|sg;
+
+    my ($mkf, $src) = ($data =~ m|^(.+)-=#=-\n(.+)|s);
+
+    print STDERR "Creating [DIR]  $name\n";
+    system("mkdir $name");
+    print STDERR "Creating [FILE] $name/Makefile\n";
+    open(FP, ">${name}/Makefile") || die;
+    print FP $mkf;
+    close(FP);
+    print STDERR "Creating [FILE] $name/mod_$name.c\n";
+    open(FP, ">${name}/mod_${name}.c") || die;
+    print FP $src;
+    close(FP);
+
+    exit(0);
+}
+
+
+if ($opt_q) {
+    ##
+    ##  QUERY INFORMATION 
+    ##
+
+    my $result = '';
+    my $arg;
+    foreach $arg (@args) {
+        my $ok = 0;
+        my $name;
+        foreach $name (qw(
+            TARGET CC CFLAGS CFLAGS_SHLIB LD_SHLIB LDFLAGS_SHLIB LIBS_SHLIB
+            PREFIX SBINDIR INCLUDEDIR LIBEXECDIR SYSCONFDIR
+        )) {
+            if ($arg eq $name or $arg eq lc($name)) {
+                my $val = eval "\$CFG_$name";
+                $result .= "${val}::";
+                $ok = 1;
+            }
+        }
+        if (not $ok) {
+            printf(STDERR "apxs:Error: Invalid query string `%s'\n", $arg);
+            exit(1);
+        }
+    }
+    $result =~ s|::$||;
+    $result =~ s|::| |;
+    print $result;
+}
+
+if ($opt_c) {
+    ##
+    ##  SHARED OBJECT COMPILATION
+    ##
+
+    #   split files into sources and objects
+    my @srcs = ();
+    my @objs = ();
+    my $f;
+    foreach $f (@args) {
+        if ($f =~ m|\.c$|) {
+            push(@srcs, $f);
+        }
+        else {
+            push(@objs, $f);
+        }
+    }
+
+    #   determine output file
+    my $dso_file;
+    if ($opt_o eq '') {
+        if ($#srcs > -1) {
+            $dso_file = $srcs[0];
+            $dso_file =~ s|\.[^.]+$|.so|;
+        }
+        elsif ($#objs > -1) {
+            $dso_file = $objs[0];
+            $dso_file =~ s|\.[^.]+$|.so|;
+        }
+        else {
+            $dso_file = "mod_unknown.so";
+        }
+    }
+    else {
+        $dso_file = $opt_o;
+    }
+
+    #   create compilation commands
+    my @cmds = ();
+    my $opt = '';
+    my ($opt_Wc, $opt_I, $opt_D);
+    foreach $opt_Wc (@opt_W) {
+        $opt .= "$1 " if ($opt_Wc =~ m|^\s*c,(.*)$|);
+    }
+    foreach $opt_I (@opt_I) {
+        $opt .= "-I$opt_I ";
+    }
+    foreach $opt_D (@opt_D) {
+        $opt .= "-D$opt_D ";
+    }
+    my $cflags = "$CFG_CFLAGS $CFG_CFLAGS_SHLIB";
+    my $s;
+    foreach $s (@srcs) {
+        my $o = $s;
+        $o =~ s|\.c$|.o|;
+        push(@cmds, "$CFG_CC $cflags -I$CFG_INCLUDEDIR $opt -c $s");
+        unshift(@objs, $o);
+    }
+
+    #   create link command
+    my $cmd = "$CFG_LD_SHLIB $CFG_LDFLAGS_SHLIB -o $dso_file";
+    my $o;
+    foreach $o (@objs) {
+        $cmd .= " $o";
+    }
+    $opt = '';
+    my ($opt_Wl, $opt_L, $opt_l);
+    foreach $opt_Wl (@opt_W) {
+               if($CFG_LD_SHLIB ne "gcc") {
+               $opt .= " $1" if ($opt_Wl =~ m|^\s*l,(.*)$|);
+               } else {
+               $opt .= " -W$opt_Wl";
+               }
+    }
+    foreach $opt_L (@opt_L) {
+        $opt .= " -L$opt_L";
+    }
+    foreach $opt_l (@opt_l) {
+        $opt .= " -l$opt_l";
+    }
+    $cmd .= $opt;
+    $cmd .= " $CFG_LIBS_SHLIB";
+    push(@cmds, $cmd);
+
+    #   execute the commands
+    &execute_cmds(@cmds);
+
+    #   allow one-step compilation and installation
+    if ($opt_i or $opt_e) {
+        @args = ( $dso_file );
+    }
+}
+
+if ($opt_i or $opt_e) {
+    ##
+    ##  SHARED OBJECT INSTALLATION
+    ##
+
+    #   determine installation commands
+    #   and corresponding LoadModule/AddModule directives
+    my @lmd = ();
+    my @amd = ();
+    my @cmds = ();
+    my $f;
+    foreach $f (@args) {
+        if ($f !~ m|\.so$|) {
+            print STDERR "apxs:Error: file $f is not a shared object\n";
+            exit(1);
+        }
+        my $t = $f;
+        $t =~ s|^.+/([^/]+)$|$1|;
+        if ($opt_i) {
+           push(@cmds, "cp $f $CFG_LIBEXECDIR/$t");
+           push(@cmds, "chmod 755 $CFG_LIBEXECDIR/$t");
+        }
+
+        #   determine module symbolname and filename
+        my $filename = '';
+        if ($name eq 'unknown') {
+            $name = '';
+            my $base = $f;
+            $base =~ s|\.[^.]+$||;
+            if (-f "$base.c") {
+                open(FP, "<$base.c");
+                my $content = join('', <FP>);
+                close(FP);
+                if ($content =~ m|.*module\s+(?:MODULE_VAR_EXPORT\s+)?([a-zA-Z0-9_]+)_module\s*=\s*.*|s) {
+                    $name = "$1";
+                    $filename = "$base.c";
+                    $filename =~ s|^[^/]+/||;
+                }
+            }
+            if ($name eq '') {
+                if ($base =~ m|.*mod_([a-zA-Z0-9_]+)\..+|) {
+                    $name = "$1";
+                    $filename = $base;
+                    $filename =~ s|^[^/]+/||;
+                }
+            }
+            if ($name eq '') {
+                print "apxs:Error: Sorry, cannot determine bootstrap symbol name\n";
+                print "apxs:Error: Please specify one with option `-n'\n";
+                exit(1);
+            }
+        }
+        if ($filename eq '') {
+            $filename = "mod_${name}.c";
+        }
+        my $dir = $CFG_LIBEXECDIR;
+        $dir =~ s|^$CFG_PREFIX/?||;
+        $dir =~ s|(.)$|$1/|;
+        push(@lmd, sprintf("LoadModule %-18s %s", "${name}_module", "$dir$t"));
+        push(@amd, sprintf("AddModule %s", $filename));
+    }
+
+    #   execute the commands
+    &execute_cmds(@cmds);
+
+    #   activate module via LoadModule/AddModule directive
+    if ($opt_a or $opt_A) {
+        if (not -f "$CFG_SYSCONFDIR/$CFG_TARGET.conf") {
+            print "apxs:Error: Config file $CFG_SYSCONFDIR/$CFG_TARGET.conf not found\n";
+            exit(1);
+        }
+
+        open(FP, "<$CFG_SYSCONFDIR/$CFG_TARGET.conf") || die;
+        my $content = join('', <FP>);
+        close(FP);
+
+        if ($content !~ m|\n#?\s*LoadModule\s+|) {
+            print STDERR "apxs:Error: Activation failed for custom $CFG_SYSCONFDIR/$CFG_TARGET.conf file.\n";
+            print STDERR "apxs:Error: At least one `LoadModule' directive already has to exist.\n";
+            exit(1);
+        }
+
+        my $lmd;
+        my $c = '';
+        $c = '#' if ($opt_A);
+        foreach $lmd (@lmd) {
+            my $what = $opt_A ? "preparing" : "activating";
+            if ($content !~ m|\n#?\s*$lmd|) {
+                 $content =~ s|^(.*\n#?\s*LoadModule\s+[^\n]+\n)|$1$c$lmd\n|sg;
+            } else {
+                 $content =~ s|^(.*\n)#?\s*$lmd[^\n]*\n|$1$c$lmd\n|sg;
+            }
+            $lmd =~ m|LoadModule\s+(.+?)_module.*|;
+            print STDERR "[$what module `$1' in $CFG_SYSCONFDIR/$CFG_TARGET.conf]\n";
+        }
+        my $amd;
+        foreach $amd (@amd) {
+            if ($content !~ m|\n#?\s*$amd|) {
+                 $content =~ s|^(.*\n#?\s*AddModule\s+[^\n]+\n)|$1$c$amd\n|sg;
+            } else {
+                 $content =~ s|^(.*\n)#?\s*$amd[^\n]*\n|$1$c$amd\n|sg;
+            }
+        }
+        if (@lmd or @amd) {
+            open(FP, ">$CFG_SYSCONFDIR/$CFG_TARGET.conf.new") || die;
+            print FP $content;
+            close(FP);
+            system("cp $CFG_SYSCONFDIR/$CFG_TARGET.conf $CFG_SYSCONFDIR/$CFG_TARGET.conf.bak && " .
+                   "cp $CFG_SYSCONFDIR/$CFG_TARGET.conf.new $CFG_SYSCONFDIR/$CFG_TARGET.conf && " .
+                   "rm $CFG_SYSCONFDIR/$CFG_TARGET.conf.new");
+        }
+    }
+}
+
+##EOF##
+__DATA__
+##
+##  Makefile -- Build procedure for sample %NAME% Apache module
+##  Autogenerated via ``apxs -n %NAME% -g''.
+##
+
+#   the used tools
+APXS=apxs
+APACHECTL=apachectl
+
+#   additional defines, includes and libraries
+#DEF=-Dmy_define=my_value
+#INC=-Imy/include/dir
+#LIB=-Lmy/lib/dir -lmylib
+
+#   the default target
+all: mod_%NAME%.so
+
+#   compile the shared object file
+mod_%NAME%.so: mod_%NAME%.c
+       $(APXS) -c $(DEF) $(INC) $(LIB) mod_%NAME%.c
+
+#   install the shared object file into Apache 
+install: all
+       $(APXS) -i -a -n '%NAME%' mod_%NAME%.so
+
+#   cleanup
+clean:
+       -rm -f mod_%NAME%.o mod_%NAME%.so
+
+#   simple test
+test: reload
+       lynx -mime_header http://localhost/%NAME%
+
+#   install and activate shared object by reloading Apache to
+#   force a reload of the shared object file
+reload: install restart
+
+#   the general Apache start/restart/stop
+#   procedures
+start:
+       $(APACHECTL) start
+restart:
+       $(APACHECTL) restart
+stop:
+       $(APACHECTL) stop
+
+-=#=-
+/* 
+**  mod_%NAME%.c -- Apache sample %NAME% module
+**  [Autogenerated via ``apxs -n %NAME% -g'']
+**
+**  To play with this sample module first compile it into a
+**  DSO file and install it into Apache's libexec directory 
+**  by running:
+**
+**    $ apxs -c -i mod_%NAME%.c
+**
+**  Then activate it in Apache's %TARGET%.conf file for instance
+**  for the URL /%NAME% in as follows:
+**
+**    #   %TARGET%.conf
+**    LoadModule %NAME%_module libexec/mod_%NAME%.so
+**    <Location /%NAME%>
+**    SetHandler %NAME%
+**    </Location>
+**
+**  Then after restarting Apache via
+**
+**    $ apachectl restart
+**
+**  you immediately can request the URL /%NAME and watch for the
+**  output of this module. This can be achieved for instance via:
+**
+**    $ lynx -mime_header http://localhost/%NAME% 
+**
+**  The output should be similar to the following one:
+**
+**    HTTP/1.1 200 OK
+**    Date: Tue, 31 Mar 1998 14:42:22 GMT
+**    Server: Apache/1.3.4 (Unix)
+**    Connection: close
+**    Content-Type: text/html
+**  
+**    The sample page from mod_%NAME%.c
+*/ 
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_protocol.h"
+#include "ap_config.h"
+
+/* The sample content handler */
+static int %NAME%_handler(request_rec *r)
+{
+    r->content_type = "text/html";      
+    ap_send_http_header(r);
+    if (!r->header_only)
+        ap_rputs("The sample page from mod_%NAME%.c\n", r);
+    return OK;
+}
+
+/* Dispatch list of content handlers */
+static const handler_rec %NAME%_handlers[] = { 
+    { "%NAME%", %NAME%_handler }, 
+    { NULL, NULL }
+};
+
+/* Dispatch list for API hooks */
+module MODULE_VAR_EXPORT %NAME%_module = {
+    STANDARD_MODULE_STUFF, 
+    NULL,                  /* module initializer                  */
+    NULL,                  /* create per-dir    config structures */
+    NULL,                  /* merge  per-dir    config structures */
+    NULL,                  /* create per-server config structures */
+    NULL,                  /* merge  per-server config structures */
+    NULL,                  /* table of config file commands       */
+    %NAME%_handlers,       /* [#8] MIME-typed-dispatched handlers */
+    NULL,                  /* [#1] URI to filename translation    */
+    NULL,                  /* [#4] validate user id from request  */
+    NULL,                  /* [#5] check if the user is ok _here_ */
+    NULL,                  /* [#3] check access by host address   */
+    NULL,                  /* [#6] determine MIME type            */
+    NULL,                  /* [#7] pre-run fixups                 */
+    NULL,                  /* [#9] log a transaction              */
+    NULL,                  /* [#2] header parser                  */
+    NULL,                  /* child_init                          */
+    NULL,                  /* child_exit                          */
+    NULL                   /* [#0] post read-request              */
+};
+
diff --git a/support/dbmmanage b/support/dbmmanage
new file mode 100644 (file)
index 0000000..721bdd1
--- /dev/null
@@ -0,0 +1,189 @@
+#!/usr/local/bin/perl
+
+# ====================================================================
+# Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer. 
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in
+#    the documentation and/or other materials provided with the
+#    distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+#    software must display the following acknowledgment:
+#    "This product includes software developed by the Apache Group
+#    for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+#    endorse or promote products derived from this software without
+#    prior written permission. For written permission, please contact
+#    apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache"
+#    nor may "Apache" appear in their names without prior written
+#    permission of the Apache Group.
+#
+# 6. Redistributions of any form whatsoever must retain the following
+#    acknowledgment:
+#    "This product includes software developed by the Apache Group
+#    for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see <http://www.apache.org/>.
+
+#for more functionality see the HTTPD::UserAdmin module:
+# http://www.perl.com/CPAN/modules/by-module/HTTPD/HTTPD-Tools-x.xx.tar.gz
+#
+# usage: dbmmanage <DBMfile> <command> <key> <value>
+
+package dbmmanage;
+#                               -ldb    -lndbm    -lgdbm
+BEGIN { @AnyDBM_File::ISA = qw(DB_File NDBM_File GDBM_File) }
+use strict;
+use Fcntl;
+use AnyDBM_File ();
+
+my($file,$command,$key,$crypted_pwd) = @ARGV;
+
+usage() unless $file and $command and defined &{$dbmc::{$command}};
+
+# if your osname is in $newstyle_salt, then use new style salt (starts with '_' and contains
+# four bytes of iteration count and four bytes of salt).  Otherwise, just use
+# the traditional two-byte salt.
+# see the man page on your system to decide if you have a newer crypt() lib.
+# I believe that 4.4BSD derived systems do (at least BSD/OS 2.0 does).
+# The new style crypt() allows up to 20 characters of the password to be
+# significant rather than only 8.
+my $newstyle_salt = join '|', qw{bsdos}; #others?
+
+# remove extension if any
+my $chop = join '|', qw{db.? pag dir};
+$file =~ s/\.($chop)$//;
+
+my $is_update = $command eq "update";
+my $Is_Win32  = $^O eq "MSWin32"; 
+my %DB = ();
+my @range = ();
+my($mode, $flags) = $command =~ 
+    /^(?:view|check)$/ ? (0644, O_RDONLY) : (0644, O_RDWR|O_CREAT);
+
+tie %DB, "AnyDBM_File", $file, $flags, $mode || die "Can't tie $file: $!";
+dbmc->$command();
+untie %DB;
+
+sub usage {
+    my $cmds = join "|", sort keys %dbmc::;
+    die "usage: $0 filename [$cmds] [username]\n";
+}
+
+my $x;
+sub genseed {
+    my $psf;
+    for (qw(-xlwwa -le)) { 
+       `ps $_ 2>/dev/null`;
+       $psf = $_, last unless $?;
+    }
+    srand (time ^ $$ ^ unpack("%L*", `ps $psf | gzip -f`));
+    @range = (qw(. /), '0'..'9','a'..'z','A'..'Z');
+    $x = int scalar @range;
+}
+
+sub randchar { 
+    join '', map $range[rand $x], 1..shift||1;
+}
+
+sub salt {
+    my $newstyle = $^O =~ /(?:$newstyle_salt)/;
+    genseed() unless @range; 
+    return $newstyle ? 
+       join '', "_", randchar, "a..", randchar(4) :
+        randchar(2);
+}
+
+sub getpass {
+    my $prompt = shift || "Enter password:";
+
+    unless($Is_Win32) { 
+       open STDIN, "/dev/tty" or warn "couldn't open /dev/tty $!\n";
+       system "stty -echo;";
+    }
+
+    my($c,$pwd);
+    print STDERR $prompt;
+    while (($c = getc(STDIN)) ne '' and $c ne "\n" and $c ne "\r") {
+       $pwd .= $c;
+    }
+
+    system "stty echo" unless $Is_Win32;
+    print STDERR "\n";
+    die "Can't use empty password!\n" unless length $pwd;
+    return $pwd;
+}
+
+sub dbmc::update {
+    die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key};
+    dbmc->adduser;
+}
+
+sub dbmc::add {
+    die "Can't use empty password!\n" unless $crypted_pwd;
+    unless($is_update) {
+       die "Sorry, user `$key' already exists!\n" if $DB{$key};
+    }
+    $DB{$key} = $crypted_pwd;
+    my $action = $is_update ? "updated" : "added";
+    print "User $key $action with password encrypted to $DB{$key}\n";
+}
+
+sub dbmc::adduser {
+    my $value = getpass "New password:";
+    die "They don't match, sorry.\n" unless getpass("Re-type new password:") eq $value;
+    $crypted_pwd = crypt $value, caller->salt;
+    dbmc->add;
+}
+
+sub dbmc::delete {
+    die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key};
+    delete $DB{$key}, print "`$key' deleted\n";
+}
+
+sub dbmc::view {
+    print $key ? "$key:$DB{$key}\n" : map { "$_:$DB{$_}\n" if $DB{$_} } keys %DB;
+}
+
+sub dbmc::check {
+    die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key};
+    print crypt(getpass(), $DB{$key}) eq $DB{$key} ? "password ok\n" : "password mismatch\n";
+}
+
+sub dbmc::import {
+    while(defined($_ = <STDIN>) and chomp) {
+       ($key,$crypted_pwd) = split /:/, $_, 2;
+       dbmc->add;
+    }
+}
+
diff --git a/support/htdigest.c b/support/htdigest.c
new file mode 100644 (file)
index 0000000..d1fc418
--- /dev/null
@@ -0,0 +1,271 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+/******************************************************************************
+ ******************************************************************************
+ * NOTE! This program is not safe as a setuid executable!  Do not make it
+ * setuid!
+ ******************************************************************************
+ *****************************************************************************/
+/*
+ * htdigest.c: simple program for manipulating digest passwd file for Apache
+ *
+ * by Alexei Kosut, based on htpasswd.c, by Rob McCool
+ */
+
+#include "ap_config.h"
+#include <sys/types.h>
+#include "ap.h"
+#include "ap_md5.h"
+#if defined(MPE) || defined(QNX) || defined(WIN32) || defined(__TANDEM)
+#include <signal.h>
+#else
+#include <sys/signal.h>
+#endif
+
+#ifdef WIN32
+#include <conio.h>
+#define unlink _unlink
+#endif
+
+#ifdef CHARSET_EBCDIC
+#define LF '\n'
+#define CR '\r'
+#else
+#define LF 10
+#define CR 13
+#endif /* CHARSET_EBCDIC */
+
+#define MAX_STRING_LEN 256
+
+char *tn;
+
+static void getword(char *word, char *line, char stop)
+{
+    int x = 0, y;
+
+    for (x = 0; ((line[x]) && (line[x] != stop)); x++)
+       word[x] = line[x];
+
+    word[x] = '\0';
+    if (line[x])
+       ++x;
+    y = 0;
+
+    while ((line[y++] = line[x++]));
+}
+
+static int getline(char *s, int n, FILE *f)
+{
+    register int i = 0;
+
+    while (1) {
+       s[i] = (char) fgetc(f);
+
+       if (s[i] == CR)
+           s[i] = fgetc(f);
+
+       if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
+           s[i] = '\0';
+           return (feof(f) ? 1 : 0);
+       }
+       ++i;
+    }
+}
+
+static void putline(FILE *f, char *l)
+{
+    int x;
+
+    for (x = 0; l[x]; x++)
+       fputc(l[x], f);
+    fputc('\n', f);
+}
+
+
+static void add_password(char *user, char *realm, FILE *f)
+{
+    char *pw;
+    AP_MD5_CTX context;
+    unsigned char digest[16];
+    char string[MAX_STRING_LEN];
+    char pwin[MAX_STRING_LEN];
+    char pwv[MAX_STRING_LEN];
+    unsigned int i;
+
+    if (ap_getpass("New password: ", pwin, sizeof(pwin)) != 0) {
+       fprintf(stderr, "password too long");
+       exit(5);
+    }
+    ap_getpass("Re-type new password: ", pwv, sizeof(pwv));
+    if (strcmp(pwin, pwv) != 0) {
+       fprintf(stderr, "They don't match, sorry.\n");
+       if (tn) {
+           unlink(tn);
+       }
+       exit(1);
+    }
+    pw = pwin;
+    fprintf(f, "%s:%s:", user, realm);
+
+    /* Do MD5 stuff */
+    sprintf(string, "%s:%s:%s", user, realm, pw);
+
+    ap_MD5Init(&context);
+    ap_MD5Update(&context, (unsigned char *) string, strlen(string));
+    ap_MD5Final(digest, &context);
+
+    for (i = 0; i < 16; i++)
+       fprintf(f, "%02x", digest[i]);
+
+    fprintf(f, "\n");
+}
+
+static void usage(void)
+{
+    fprintf(stderr, "Usage: htdigest [-c] passwordfile realm username\n");
+    fprintf(stderr, "The -c flag creates a new file.\n");
+    exit(1);
+}
+
+static void interrupted(void)
+{
+    fprintf(stderr, "Interrupted.\n");
+    if (tn)
+       unlink(tn);
+    exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+    FILE *tfp, *f;
+    char user[MAX_STRING_LEN];
+    char realm[MAX_STRING_LEN];
+    char line[MAX_STRING_LEN];
+    char l[MAX_STRING_LEN];
+    char w[MAX_STRING_LEN];
+    char x[MAX_STRING_LEN];
+    char command[MAX_STRING_LEN];
+    int found;
+
+    tn = NULL;
+    signal(SIGINT, (void (*)(int)) interrupted);
+    if (argc == 5) {
+       if (strcmp(argv[1], "-c"))
+           usage();
+       if (!(tfp = fopen(argv[2], "w"))) {
+           fprintf(stderr, "Could not open passwd file %s for writing.\n",
+                   argv[2]);
+           perror("fopen");
+           exit(1);
+       }
+       printf("Adding password for %s in realm %s.\n", argv[4], argv[3]);
+       add_password(argv[4], argv[3], tfp);
+       fclose(tfp);
+       exit(0);
+    }
+    else if (argc != 4)
+       usage();
+
+    tn = tmpnam(NULL);
+    if (!(tfp = fopen(tn, "w"))) {
+       fprintf(stderr, "Could not open temp file.\n");
+       exit(1);
+    }
+
+    if (!(f = fopen(argv[1], "r"))) {
+       fprintf(stderr,
+               "Could not open passwd file %s for reading.\n", argv[1]);
+       fprintf(stderr, "Use -c option to create new one.\n");
+       exit(1);
+    }
+    strcpy(user, argv[3]);
+    strcpy(realm, argv[2]);
+
+    found = 0;
+    while (!(getline(line, MAX_STRING_LEN, f))) {
+       if (found || (line[0] == '#') || (!line[0])) {
+           putline(tfp, line);
+           continue;
+       }
+       strcpy(l, line);
+       getword(w, l, ':');
+       getword(x, l, ':');
+       if (strcmp(user, w) || strcmp(realm, x)) {
+           putline(tfp, line);
+           continue;
+       }
+       else {
+           printf("Changing password for user %s in realm %s\n", user, realm);
+           add_password(user, realm, tfp);
+           found = 1;
+       }
+    }
+    if (!found) {
+       printf("Adding user %s in realm %s\n", user, realm);
+       add_password(user, realm, tfp);
+    }
+    fclose(f);
+    fclose(tfp);
+#if defined(OS2) || defined(WIN32)
+    sprintf(command, "copy \"%s\" \"%s\"", tn, argv[1]);
+#else
+    sprintf(command, "cp %s %s", tn, argv[1]);
+#endif
+    system(command);
+    unlink(tn);
+    return 0;
+}
diff --git a/support/htpasswd.c b/support/htpasswd.c
new file mode 100644 (file)
index 0000000..21a24e0
--- /dev/null
@@ -0,0 +1,577 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/******************************************************************************
+ ******************************************************************************
+ * NOTE! This program is not safe as a setuid executable!  Do not make it
+ * setuid!
+ ******************************************************************************
+ *****************************************************************************/
+/*
+ * htpasswd.c: simple program for manipulating password file for
+ * the Apache HTTP server
+ * 
+ * Originally by Rob McCool
+ *
+ * Exit values:
+ *  0: Success
+ *  1: Failure; file access/permission problem
+ *  2: Failure; command line syntax problem (usage message issued)
+ *  3: Failure; password verification failure
+ *  4: Failure; operation interrupted (such as with CTRL/C)
+ *  5: Failure; buffer would overflow (username, filename, or computed
+ *     record too long)
+ *  6: Failure; username contains illegal or reserved characters
+ */
+
+#include "ap_config.h"
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include "ap.h"
+#include "ap_md5.h"
+#include "ap_sha1.h"
+
+#ifdef WIN32
+#include <conio.h>
+#include "../os/win32/getopt.h"
+#define unlink _unlink
+#endif
+
+#ifndef CHARSET_EBCDIC
+#define LF 10
+#define CR 13
+#else /*CHARSET_EBCDIC*/
+#define LF '\n'
+#define CR '\r'
+#endif /*CHARSET_EBCDIC*/
+
+#define MAX_STRING_LEN 256
+#define ALG_PLAIN 0
+#define ALG_CRYPT 1
+#define ALG_APMD5 2
+#define ALG_APSHA 3 
+
+#define ERR_FILEPERM 1
+#define ERR_SYNTAX 2
+#define ERR_PWMISMATCH 3
+#define ERR_INTERRUPTED 4
+#define ERR_OVERFLOW 5
+#define ERR_BADUSER 6
+
+/*
+ * This needs to be declared statically so the signal handler can
+ * access it.
+ */
+static char *tempfilename;
+
+/*
+ * Get a line of input from the user, not including any terminating
+ * newline.
+ */
+static int getline(char *s, int n, FILE *f)
+{
+    register int i = 0;
+
+    while (1) {
+       s[i] = (char) fgetc(f);
+
+       if (s[i] == CR) {
+           s[i] = fgetc(f);
+       }
+
+       if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
+           s[i] = '\0';
+           return (feof(f) ? 1 : 0);
+       }
+       ++i;
+    }
+}
+
+static void putline(FILE *f, char *l)
+{
+    int x;
+
+    for (x = 0; l[x]; x++) {
+       fputc(l[x], f);
+    }
+    fputc('\n', f);
+}
+
+/*
+ * Make a password record from the given information.  A zero return
+ * indicates success; failure means that the output buffer contains an
+ * error message instead.
+ */
+static int mkrecord(char *user, char *record, size_t rlen, char *passwd,
+                   int alg)
+{
+    char *pw;
+    char cpw[120];
+    char pwin[MAX_STRING_LEN];
+    char pwv[MAX_STRING_LEN];
+    char salt[9];
+
+    if (passwd != NULL) {
+       pw = passwd;
+    }
+    else {
+       if (ap_getpass("New password: ", pwin, sizeof(pwin)) != 0) {
+           ap_snprintf(record, (rlen - 1), "password too long (>%d)",
+                       sizeof(pwin) - 1);
+           return ERR_OVERFLOW;
+       }
+       ap_getpass("Re-type new password: ", pwv, sizeof(pwv));
+       if (strcmp(pwin, pwv) != 0) {
+           ap_cpystrn(record, "password verification error", (rlen - 1));
+           return ERR_PWMISMATCH;
+       }
+       pw = pwin;
+        memset(pwv, '\0', sizeof(pwin));
+    }
+    switch (alg) {
+
+    case ALG_APSHA:
+       /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */
+       ap_sha1_base64(pw,strlen(pw),cpw);
+       break;
+
+    case ALG_APMD5: 
+        (void) srand((int) time((time_t *) NULL));
+        ap_to64(&salt[0], rand(), 8);
+        salt[8] = '\0';
+
+       ap_MD5Encode((const unsigned char *)pw, (const unsigned char *)salt,
+                    cpw, sizeof(cpw));
+       break;
+
+    case ALG_PLAIN:
+       /* XXX this len limitation is not in sync with any HTTPd len. */
+       ap_cpystrn(cpw,pw,sizeof(cpw));
+       break;
+
+    case ALG_CRYPT:
+    default:
+        (void) srand((int) time((time_t *) NULL));
+        ap_to64(&salt[0], rand(), 8);
+        salt[8] = '\0';
+
+       ap_cpystrn(cpw, (char *)crypt(pw, salt), sizeof(cpw) - 1);
+       break;
+    }
+    memset(pw, '\0', strlen(pw));
+
+    /*
+     * Check to see if the buffer is large enough to hold the username,
+     * hash, and delimiters.
+     */
+    if ((strlen(user) + 1 + strlen(cpw)) > (rlen - 1)) {
+       ap_cpystrn(record, "resultant record too long", (rlen - 1));
+       return ERR_OVERFLOW;
+    }
+    strcpy(record, user);
+    strcat(record, ":");
+    strcat(record, cpw);
+    return 0;
+}
+
+static int usage(void)
+{
+    fprintf(stderr, "Usage:\n");
+    fprintf(stderr, "\thtpasswd [-cmdps] passwordfile username\n");
+    fprintf(stderr, "\thtpasswd -b[cmdps] passwordfile username password\n\n");
+    fprintf(stderr, " -c  Create a new file.\n");
+    fprintf(stderr, " -m  Force MD5 encryption of the password"
+#if defined(WIN32) || defined(TPF)
+       " (default)"
+#endif
+       ".\n");
+    fprintf(stderr, " -d  Force CRYPT encryption of the password"
+#if (!(defined(WIN32) || defined(TPF)))
+           " (default)"
+#endif
+           ".\n");
+    fprintf(stderr, " -p  Do not encrypt the password (plaintext).\n");
+    fprintf(stderr, " -s  Force SHA encryption of the password.\n");
+    fprintf(stderr, " -b  Use the password from the command line rather "
+           "than prompting for it.\n");
+    fprintf(stderr,
+           "On Windows and TPF systems the '-m' flag is used by default.\n");
+    fprintf(stderr,
+           "On all other systems, the '-p' flag will probably not work.\n");
+    return ERR_SYNTAX;
+}
+
+static void interrupted(void)
+{
+    fprintf(stderr, "Interrupted.\n");
+    if (tempfilename != NULL) {
+       unlink(tempfilename);
+    }
+    exit(ERR_INTERRUPTED);
+}
+
+/*
+ * Check to see if the specified file can be opened for the given
+ * access.
+ */
+static int accessible(char *fname, char *mode)
+{
+    FILE *s;
+
+    s = fopen(fname, mode);
+    if (s == NULL) {
+       return 0;
+    }
+    fclose(s);
+    return 1;
+}
+
+/*
+ * Return true if a file is readable.
+ */
+static int readable(char *fname)
+{
+    return accessible(fname, "r");
+}
+
+/*
+ * Return true if the specified file can be opened for write access.
+ */
+static int writable(char *fname)
+{
+    return accessible(fname, "a");
+}
+
+/*
+ * Return true if the named file exists, regardless of permissions.
+ */
+static int exists(char *fname)
+{
+#ifdef WIN32
+    struct _stat sbuf;
+#else
+    struct stat sbuf;
+#endif
+    int check;
+
+#ifdef WIN32
+    check = _stat(fname, &sbuf);
+#else
+    check = stat(fname, &sbuf);
+#endif
+    return ((check == -1) && (errno == ENOENT)) ? 0 : 1;
+}
+
+/*
+ * Copy from the current position of one file to the current position
+ * of another.
+ */
+static void copy_file(FILE *target, FILE *source)
+{
+    static char line[MAX_STRING_LEN];
+
+    while (fgets(line, sizeof(line), source) != NULL) {
+       fputs(line, target);
+    }
+}
+
+/*
+ * Let's do it.  We end up doing a lot of file opening and closing,
+ * but what do we care?  This application isn't run constantly.
+ */
+int main(int argc, char *argv[])
+{
+    FILE *ftemp = NULL;
+    FILE *fpw = NULL;
+    char user[MAX_STRING_LEN];
+    char password[MAX_STRING_LEN];
+    char record[MAX_STRING_LEN];
+    char line[MAX_STRING_LEN];
+    char pwfilename[MAX_STRING_LEN];
+    char *arg;
+    int found = 0;
+    int alg = ALG_CRYPT;
+    int newfile = 0;
+    int noninteractive = 0;
+    int i;
+    int args_left = 2;
+
+    tempfilename = NULL;
+    signal(SIGINT, (void (*)(int)) interrupted);
+
+    /*
+     * Preliminary check to make sure they provided at least
+     * three arguments, we'll do better argument checking as 
+     * we parse the command line.
+     */
+    if (argc < 3) {
+       return usage();
+    }
+
+    /*
+     * Go through the argument list and pick out any options.  They
+     * have to precede any other arguments.
+     */
+    for (i = 1; i < argc; i++) {
+       arg = argv[i];
+       if (*arg != '-') {
+           break;
+       }
+       while (*++arg != '\0') {
+           if (*arg == 'c') {
+               newfile++;
+           }
+           else if (*arg == 'm') {
+               alg = ALG_APMD5;
+           }
+           else if (*arg == 's') {
+               alg = ALG_APSHA;
+           }
+           else if (*arg == 'p') {
+               alg = ALG_PLAIN;
+           }
+           else if (*arg == 'd') {
+               alg = ALG_CRYPT;
+           }
+           else if (*arg == 'b') {
+               noninteractive++;
+               args_left++;
+           }
+           else {
+               return usage();
+           }
+       }
+    }
+
+    /*
+     * Make sure we still have exactly the right number of arguments left
+     * (the filename, the username, and possibly the password if -b was
+     * specified).
+     */
+    if ((argc - i) != args_left) {
+       return usage();
+    }
+    if (strlen(argv[i]) > (sizeof(pwfilename) - 1)) {
+       fprintf(stderr, "%s: filename too long\n", argv[0]);
+       return ERR_OVERFLOW;
+    }
+    strcpy(pwfilename, argv[i]);
+    if (strlen(argv[i + 1]) > (sizeof(user) - 1)) {
+       fprintf(stderr, "%s: username too long (>%d)\n", argv[0],
+               sizeof(user) - 1);
+       return ERR_OVERFLOW;
+    }
+    strcpy(user, argv[i + 1]);
+    if ((arg = strchr(user, ':')) != NULL) {
+       fprintf(stderr, "%s: username contains illegal character '%c'\n",
+               argv[0], *arg);
+       return ERR_BADUSER;
+    }
+    if (noninteractive) {
+       if (strlen(argv[i + 2]) > (sizeof(password) - 1)) {
+           fprintf(stderr, "%s: password too long (>%d)\n", argv[0],
+                   sizeof(password) - 1);
+           return ERR_OVERFLOW;
+       }
+       strcpy(password, argv[i + 2]);
+    }
+
+#ifdef WIN32
+    if (alg == ALG_CRYPT) {
+       alg = ALG_APMD5;
+       fprintf(stderr, "Automatically using MD5 format on Windows.\n");
+    }
+#endif
+
+#if (!(defined(WIN32) || defined(TPF)))
+    if (alg == ALG_PLAIN) {
+       fprintf(stderr,"Warning: storing passwords as plain text might "
+               "just not work on this platform.\n");
+    }
+#endif
+    /*
+     * Verify that the file exists if -c was omitted.  We give a special
+     * message if it doesn't.
+     */
+    if ((! newfile) && (! exists(pwfilename))) {
+       fprintf(stderr, "%s: cannot modify file %s; use '-c' to create it\n",
+               argv[0], pwfilename);
+       perror("fopen");
+       exit(ERR_FILEPERM);
+    }
+    /*
+     * Verify that we can read the existing file in the case of an update
+     * to it (rather than creation of a new one).
+     */
+    if ((! newfile) && (! readable(pwfilename))) {
+       fprintf(stderr, "%s: cannot open file %s for read access\n",
+               argv[0], pwfilename);
+       perror("fopen");
+       exit(ERR_FILEPERM);
+    }
+    /*
+     * Now check to see if we can preserve an existing file in case
+     * of password verification errors on a -c operation.
+     */
+    if (newfile && exists(pwfilename) && (! readable(pwfilename))) {
+       fprintf(stderr, "%s: cannot open file %s for read access\n"
+               "%s: existing auth data would be lost on password mismatch",
+               argv[0], pwfilename, argv[0]);
+       perror("fopen");
+       exit(ERR_FILEPERM);
+    }
+    /*
+     * Now verify that the file is writable!
+     */
+    if (! writable(pwfilename)) {
+       fprintf(stderr, "%s: cannot open file %s for write access\n",
+               argv[0], pwfilename);
+       perror("fopen");
+       exit(ERR_FILEPERM);
+    }
+
+    /*
+     * All the file access checks have been made.  Time to go to work;
+     * try to create the record for the username in question.  If that
+     * fails, there's no need to waste any time on file manipulations.
+     * Any error message text is returned in the record buffer, since
+     * the mkrecord() routine doesn't have access to argv[].
+     */
+    i = mkrecord(user, record, sizeof(record) - 1,
+                noninteractive ? password : NULL,
+                alg);
+    if (i != 0) {
+       fprintf(stderr, "%s: %s\n", argv[0], record);
+       exit(i);
+    }
+
+    /*
+     * We can access the files the right way, and we have a record
+     * to add or update.  Let's do it..
+     */
+    tempfilename = tmpnam(NULL);
+    ftemp = fopen(tempfilename, "w+");
+    if (ftemp == NULL) {
+       fprintf(stderr, "%s: unable to create temporary file\n", argv[0]);
+       perror("fopen");
+       exit(ERR_FILEPERM);
+    }
+    /*
+     * If we're not creating a new file, copy records from the existing
+     * one to the temporary file until we find the specified user.
+     */
+    if (! newfile) {
+       char scratch[MAX_STRING_LEN];
+
+       fpw = fopen(pwfilename, "r");
+       while (! (getline(line, sizeof(line), fpw))) {
+           char *colon;
+
+           if ((line[0] == '#') || (line[0] == '\0')) {
+               putline(ftemp, line);
+               continue;
+           }
+           strcpy(scratch, line);
+           /*
+            * See if this is our user.
+            */
+           colon = strchr(scratch, ':');
+           if (colon != NULL) {
+               *colon = '\0';
+           }
+           if (strcmp(user, scratch) != 0) {
+               putline(ftemp, line);
+               continue;
+           }
+           found++;
+           break;
+       }
+    }
+    if (found) {
+       fprintf(stderr, "Updating ");
+    }
+    else {
+       fprintf(stderr, "Adding ");
+    }
+    fprintf(stderr, "password for user %s\n", user);
+    /*
+     * Now add the user record we created.
+     */
+    putline(ftemp, record);
+    /*
+     * If we're updating an existing file, there may be additional
+     * records beyond the one we're updating, so copy them.
+     */
+    if (! newfile) {
+       copy_file(ftemp, fpw);
+       fclose(fpw);
+    }
+    /*
+     * The temporary file now contains the information that should be
+     * in the actual password file.  Close the open files, re-open them
+     * in the appropriate mode, and copy them file to the real one.
+     */
+    fclose(ftemp);
+    fpw = fopen(pwfilename, "w+");
+    ftemp = fopen(tempfilename, "r");
+    copy_file(fpw, ftemp);
+    fclose(fpw);
+    fclose(ftemp);
+    unlink(tempfilename);
+    return 0;
+}
diff --git a/support/httpd.exp b/support/httpd.exp
new file mode 100644 (file)
index 0000000..31164b5
--- /dev/null
@@ -0,0 +1,412 @@
+#!
+ap_MD5Encode
+ap_MD5Final
+ap_MD5Init
+ap_MD5Update
+ap_SHA1Final
+ap_SHA1Init
+ap_SHA1Update_binary
+ap_SHA1Update
+ap_add_cgi_vars
+ap_add_common_vars
+ap_add_module
+ap_add_named_module
+ap_add_per_dir_conf
+ap_add_per_url_conf
+ap_add_version_component
+ap_allow_options
+ap_allow_overrides
+ap_append_arrays
+ap_array_cat
+ap_array_pstrcat
+ap_auth_name
+ap_auth_type
+ap_base64encode
+ap_base64encode_binary
+ap_base64encode_len
+ap_base64decode
+ap_base64decode_binary
+ap_base64decode_len
+ap_basic_http_header
+ap_bclose
+ap_bcreate
+ap_bfilbuf
+ap_bfileno
+ap_bflsbuf
+ap_bflush
+ap_bgetopt
+ap_bgets
+ap_bhalfduplex
+ap_bind_address
+ap_block_alarms
+ap_blookc
+ap_bnonblock
+ap_bonerror
+ap_bprintf
+ap_bpushfd
+ap_bputs
+ap_bread
+ap_bsetflag
+ap_bsetopt
+ap_bskiplf
+ap_bspawn_child
+ap_bvputs
+ap_bwrite
+ap_bytes_in_free_blocks
+ap_bytes_in_pool
+ap_call_exec
+ap_can_exec
+ap_cfg_closefile
+ap_cfg_getc
+ap_cfg_getline
+ap_chdir_file
+ap_check_access
+ap_check_auth
+ap_check_cmd_context
+ap_check_user_id
+ap_checkmask
+ap_child_exit_modules
+ap_child_init_modules
+ap_child_terminate
+ap_cleanup_for_exec
+ap_clear_module_list
+ap_clear_pool
+ap_clear_table
+ap_close_piped_log
+ap_construct_server
+ap_construct_url
+ap_content_type_tolower
+ap_copy_array
+ap_copy_array_hdr
+ap_copy_table
+ap_core_reorder_directories
+ap_coredump_dir
+ap_count_dirs
+ap_cpystrn
+ap_create_environment
+ap_create_per_dir_config
+ap_create_request_config
+ap_daemons_limit
+ap_daemons_max_free
+ap_daemons_min_free
+ap_daemons_to_start
+ap_day_snames
+ap_default_port_for_request
+ap_default_port_for_scheme
+ap_default_type
+ap_destroy_pool
+ap_destroy_sub_req
+ap_die
+ap_discard_request_body
+ap_document_root
+ap_dummy_mutex
+ap_each_byterange
+ap_error_log2stderr
+ap_escape_html
+ap_escape_path_segment
+ap_escape_quotes
+ap_escape_shell_cmd
+ap_excess_requests_per_child
+ap_exists_config_define
+ap_exists_scoreboard_image
+ap_extended_status
+ap_field_noparam
+ap_finalize_request_protocol
+ap_finalize_sub_req_protocol
+ap_find_command
+ap_find_command_in_modules
+ap_find_last_token
+ap_find_linked_module
+ap_find_list_item
+ap_find_module_name
+ap_find_path_info
+ap_find_token
+ap_find_types
+ap_fini_vhost_config
+ap_fnmatch
+ap_force_library_loading
+ap_get_basic_auth_pw
+ap_get_client_block
+ap_get_gmtoff
+ap_get_list_item
+ap_get_local_host
+ap_get_remote_host
+ap_get_remote_logname
+ap_get_server_built
+ap_get_server_name
+ap_get_server_port
+ap_get_server_version
+ap_get_time
+ap_get_token
+ap_get_virthost_addr
+ap_getparents
+ap_getword
+ap_getword_conf
+ap_getword_conf_nc
+ap_getword_nc
+ap_getword_nulls
+ap_getword_nulls_nc
+ap_getword_white
+ap_getword_white_nc
+ap_gm_timestr_822
+ap_gname2id
+ap_group_id
+ap_handle_command
+ap_hard_timeout
+ap_header_parse
+ap_ht_time
+ap_ind
+ap_index_of_response
+ap_init_alloc
+ap_init_modules
+ap_init_vhost_config
+ap_init_virtual_host
+ap_internal_redirect
+ap_internal_redirect_handler
+ap_invoke_handler
+ap_is_directory
+ap_is_fnmatch
+ap_is_initial_req
+ap_is_matchexp
+ap_is_url
+ap_keepalive_timeout
+ap_kill_cleanup
+ap_kill_cleanups_for_fd
+ap_kill_cleanups_for_socket
+ap_kill_timeout
+ap_limit_section
+ap_listenbacklog
+ap_listeners
+ap_lock_fname
+ap_log_assert
+ap_log_error
+ap_log_error_old
+ap_log_pid
+ap_log_printf
+ap_log_reason
+ap_log_rerror
+ap_log_transaction
+ap_log_unixerr
+ap_make_array
+ap_make_dirstr
+ap_make_dirstr_parent
+ap_make_dirstr_prefix
+ap_make_etag
+ap_make_full_path
+ap_make_sub_pool
+ap_make_table
+ap_matches_request_vhost
+ap_max_requests_per_child
+ap_md5
+ap_md5contextTo64
+ap_md5digest
+ap_meets_conditions
+ap_merge_per_dir_configs
+ap_method_number_of
+ap_month_snames
+ap_my_generation
+ap_no2slash
+ap_note_auth_failure
+ap_note_basic_auth_failure
+ap_note_cleanups_for_fd
+ap_note_cleanups_for_file
+ap_note_cleanups_for_socket
+ap_note_digest_auth_failure
+ap_note_subprocess
+ap_null_cleanup
+ap_open_logs
+ap_open_piped_log
+ap_os_escape_path
+ap_os_is_path_absolute
+ap_overlay_tables
+ap_overlap_tables
+ap_palloc
+ap_parseHTTPdate
+ap_parse_hostinfo_components
+ap_parse_htaccess
+ap_parse_uri
+ap_parse_uri_components
+ap_parse_vhost_addrs
+ap_pbase64decode
+ap_pbase64encode
+ap_pcalloc
+ap_pcfg_open_custom
+ap_pcfg_openfile
+ap_pclosedir
+ap_pclosef
+ap_pclosesocket
+ap_pduphostent
+ap_pfclose
+ap_pfdopen
+ap_pfopen
+ap_pgethostbyname
+ap_pid_fname
+ap_popendir
+ap_popenf
+ap_pregcomp
+ap_pregfree
+ap_pregsub
+ap_prelinked_modules
+ap_preloaded_modules
+ap_process_request
+ap_process_resource_config
+ap_psignature
+ap_psocket
+ap_psprintf
+ap_pstrcat
+ap_pstrdup
+ap_pstrndup
+ap_push_array
+ap_pvsprintf
+ap_rationalize_mtime
+ap_read_config
+ap_read_request
+ap_regerror
+ap_regexec
+ap_register_cleanup
+ap_register_other_child
+ap_remove_module
+ap_requires
+ap_reset_timeout
+ap_response_code_string
+ap_restart_time
+ap_rfc1413
+ap_rfc1413_timeout
+ap_rflush
+ap_rind
+ap_rprintf
+ap_rputc
+ap_rputs
+ap_run_cleanup
+ap_run_fixups
+ap_run_post_read_request
+ap_run_sub_req
+ap_rvputs
+ap_rwrite
+ap_satisfies
+ap_scan_script_header_err
+ap_scan_script_header_err_buff
+ap_scoreboard_fname
+ap_scoreboard_image
+ap_send_error_response
+ap_send_fb
+ap_send_fb_length
+ap_send_fd
+ap_send_fd_length
+ap_send_header_field
+ap_send_http_header
+ap_send_http_options
+ap_send_http_trace
+ap_send_mmap
+ap_send_size
+ap_server_argv0
+ap_server_config_defines
+ap_server_confname
+ap_server_post_read_config
+ap_server_pre_read_config
+ap_server_root
+ap_server_root_relative
+ap_set_byterange
+ap_set_callback_and_alarm
+ap_set_content_length
+ap_set_etag
+ap_set_file_slot
+ap_set_flag_slot
+ap_set_keepalive
+ap_set_last_modified
+ap_set_name_virtual_host
+ap_set_string_slot
+ap_set_string_slot_lower
+ap_set_sub_req_protocol
+ap_setup_client_block
+ap_setup_prelinked_modules
+ap_sha1_base64
+ap_should_client_block
+ap_show_directives
+ap_show_modules
+ap_signal
+ap_single_module_configure
+ap_size_list_item
+ap_slack
+ap_snprintf
+ap_soft_timeout
+ap_some_auth_required
+ap_spawn_child
+ap_srm_command_loop
+ap_standalone
+ap_start_restart
+ap_start_shutdown
+ap_str_tolower
+ap_strcasecmp_match
+ap_strcmp_match
+ap_sub_req_lookup_file
+ap_sub_req_lookup_uri
+ap_sub_req_method_uri
+ap_suexec_enabled
+ap_sync_scoreboard_image
+ap_table_add
+ap_table_addn
+ap_table_do
+ap_table_get
+ap_table_merge
+ap_table_mergen
+ap_table_set
+ap_table_setn
+ap_table_unset
+ap_threads_per_child
+ap_tm2sec
+ap_translate_name
+ap_uname2id
+ap_unblock_alarms
+ap_unescape_url
+ap_unparse_uri_components
+ap_unregister_other_child
+ap_update_child_status
+ap_update_mtime
+ap_update_vhost_from_headers
+ap_update_vhost_given_ip
+ap_user_id
+ap_user_name
+ap_util_init
+ap_util_uri_init
+ap_uudecode
+ap_validate_password
+ap_vbprintf
+ap_vformatter
+ap_vsnprintf
+core_module
+top_module
+XML_DefaultCurrent
+XML_ErrorString
+XML_ExternalEntityParserCreate
+XML_GetBase
+XML_GetBuffer
+XML_GetCurrentByteCount
+XML_GetCurrentByteIndex
+XML_GetCurrentColumnNumber
+XML_GetCurrentLineNumber
+XML_GetErrorCode
+XML_GetSpecifiedAttributeCount
+XML_Parse
+XML_ParseBuffer
+XML_ParserCreate
+XML_ParserCreateNS
+XML_ParserFree
+XML_SetBase
+XML_SetCdataSectionHandler
+XML_SetCharacterDataHandler
+XML_SetCommentHandler
+XML_SetDefaultHandler
+XML_SetDefaultHandlerExpand
+XML_SetElementHandler
+XML_SetEncoding
+XML_SetExternalEntityRefHandler
+XML_SetExternalEntityRefHandlerArg
+XML_SetNamespaceDeclHandler
+XML_SetNotStandaloneHandler
+XML_SetNotationDeclHandler
+XML_SetProcessingInstructionHandler
+XML_SetUnknownEncodingHandler
+XML_SetUnparsedEntityDeclHandler
+XML_SetUserData
+XML_UseParserAsHandlerArg
diff --git a/support/log_server_status b/support/log_server_status
new file mode 100644 (file)
index 0000000..1eb2893
--- /dev/null
@@ -0,0 +1,118 @@
+#!/usr/local/bin/perl
+
+# ====================================================================
+# Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer. 
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in
+#    the documentation and/or other materials provided with the
+#    distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+#    software must display the following acknowledgment:
+#    "This product includes software developed by the Apache Group
+#    for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+#    endorse or promote products derived from this software without
+#    prior written permission. For written permission, please contact
+#    apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache"
+#    nor may "Apache" appear in their names without prior written
+#    permission of the Apache Group.
+#
+# 6. Redistributions of any form whatsoever must retain the following
+#    acknowledgment:
+#    "This product includes software developed by the Apache Group
+#    for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see <http://www.apache.org/>.
+
+
+# Log Server Status
+# Mark J Cox, UK Web Ltd 1996, mark@ukweb.com
+#
+# This script is designed to be run at a frequent interval by something
+# like cron.  It connects to the server and downloads the status
+# information.  It reformats the information to a single line and logs
+# it to a file.  Make sure the directory $wherelog is writable by the
+# user who runs this script.
+#
+require 'sys/socket.ph';
+
+$wherelog = "/var/log/graph/";  # Logs will be like "/var/log/graph/19960312"
+$server = "localhost";          # Name of server, could be "www.foo.com"
+$port = "80";                   # Port on server
+$request = "/status/?auto";     # Request to send
+
+sub tcp_connect
+{
+       local($host,$port) =@_;
+        $sockaddr='S n a4 x8';
+        chop($hostname=`hostname`);
+        $port=(getservbyname($port, 'tcp'))[2]  unless $port =~ /^\d+$/;
+        $me=pack($sockaddr,&AF_INET,0,(gethostbyname($hostname))[4]);
+        $them=pack($sockaddr,&AF_INET,$port,(gethostbyname($host))[4]);
+        socket(S,&PF_INET,&SOCK_STREAM,(getprotobyname('tcp'))[2]) || 
+               die "socket: $!";
+        bind(S,$me) || return "bind: $!";
+        connect(S,$them) || return "connect: $!";
+        select(S); 
+       $| = 1; 
+       select(stdout);
+       return "";
+}
+
+### Main
+
+{
+        $year=`date +%y`;
+       chomp($year);
+       $year += ($year < 70) ? 2000 : 1900;
+       $date = $year . `date +%m%d:%H%M%S`;
+       chomp($date);
+       ($day,$time)=split(/:/,$date);
+       $res=&tcp_connect($server,$port);
+       open(OUT,">>$wherelog$day");
+       if ($res) {
+               print OUT "$time:-1:-1:-1:-1:$res\n";
+               exit 1;
+       }
+       print S "GET $request\n";
+       while (<S>) {
+               $requests=$1 if ( m|^BusyServers:\ (\S+)|);
+               $idle=$1 if ( m|^IdleServers:\ (\S+)|);
+               $number=$1 if ( m|sses:\ (\S+)|);
+               $cpu=$1 if (m|^CPULoad:\ (\S+)|);
+       }
+       print OUT "$time:$requests:$idle:$number:$cpu\n";
+}
+
+
diff --git a/support/logresolve.c b/support/logresolve.c
new file mode 100644 (file)
index 0000000..bcdc6e5
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * logresolve 1.1
+ *
+ * Tom Rathborne - tomr@uunet.ca - http://www.uunet.ca/~tomr/
+ * UUNET Canada, April 16, 1995
+ *
+ * Rewritten by David Robinson. (drtr@ast.cam.ac.uk)
+ *
+ * Usage: logresolve [-s filename] [-c] < access_log > new_log
+ *
+ * Arguments:
+ *    -s filename     name of a file to record statistics
+ *    -c              check the DNS for a matching A record for the host.
+ *
+ * Notes:
+ *
+ * To generate meaningful statistics from an HTTPD log file, it's good
+ * to have the domain name of each machine that accessed your site, but
+ * doing this on the fly can slow HTTPD down.
+ *
+ * Compiling NCSA HTTPD with the -DMINIMAL_DNS flag turns IP#->hostname
+ * resolution off. Before running your stats program, just run your log
+ * file through this program (logresolve) and all of your IP numbers will
+ * be resolved into hostnames (where possible).
+ *
+ * logresolve takes an HTTPD access log (in the COMMON log file format,
+ * or any other format that has the IP number/domain name as the first
+ * field for that matter), and outputs the same file with all of the
+ * domain names looked up. Where no domain name can be found, the IP
+ * number is left in.
+ *
+ * To minimize impact on your nameserver, logresolve has its very own
+ * internal hash-table cache. This means that each IP number will only
+ * be looked up the first time it is found in the log file.
+ *
+ * The -c option causes logresolve to apply the same check as httpd
+ * compiled with -DMAXIMUM_DNS; after finding the hostname from the IP
+ * address, it looks up the IP addresses for the hostname and checks
+ * that one of these matches the original address.
+ */
+
+#include "ap_config.h"
+#include <sys/types.h>
+
+#include <ctype.h>
+
+#ifndef MPE
+#include <arpa/inet.h>
+#endif
+
+static void cgethost(struct in_addr ipnum, char *string, int check);
+static int getline(char *s, int n);
+static void stats(FILE *output);
+
+
+/* maximum line length */
+#define MAXLINE 1024
+
+/* maximum length of a domain name */
+#ifndef MAXDNAME
+#define MAXDNAME 256
+#endif
+
+/* number of buckets in cache hash table */
+#define BUCKETS 256
+
+#if defined(NEED_STRDUP)
+char *strdup (const char *str)
+{
+    char *dup;
+
+    if (!(dup = (char *) malloc(strlen(str) + 1)))
+       return NULL;
+    dup = strcpy(dup, str);
+
+    return dup;
+}
+#endif
+
+/*
+ * struct nsrec - record of nameservice for cache linked list
+ * 
+ * ipnum - IP number hostname - hostname noname - nonzero if IP number has no
+ * hostname, i.e. hostname=IP number
+ */
+
+struct nsrec {
+    struct in_addr ipnum;
+    char *hostname;
+    int noname;
+    struct nsrec *next;
+}    *nscache[BUCKETS];
+
+/*
+ * statistics - obvious
+ */
+
+#ifndef h_errno
+extern int h_errno; /* some machines don't have this in their headers */
+#endif
+
+/* largeste value for h_errno */
+#define MAX_ERR (NO_ADDRESS)
+#define UNKNOWN_ERR (MAX_ERR+1)
+#define NO_REVERSE  (MAX_ERR+2)
+
+static int cachehits = 0;
+static int cachesize = 0;
+static int entries = 0;
+static int resolves = 0;
+static int withname = 0;
+static int errors[MAX_ERR + 3];
+
+/*
+ * cgethost - gets hostname by IP address, caching, and adding unresolvable
+ * IP numbers with their IP number as hostname, setting noname flag
+ */
+
+static void cgethost (struct in_addr ipnum, char *string, int check)
+{
+    struct nsrec **current, *new;
+    struct hostent *hostdata;
+    char *name;
+
+    current = &nscache[((ipnum.s_addr + (ipnum.s_addr >> 8) +
+                        (ipnum.s_addr >> 16) + (ipnum.s_addr >> 24)) % BUCKETS)];
+
+    while (*current != NULL && ipnum.s_addr != (*current)->ipnum.s_addr)
+       current = &(*current)->next;
+
+    if (*current == NULL) {
+       cachesize++;
+       new = (struct nsrec *) malloc(sizeof(struct nsrec));
+       if (new == NULL) {
+           perror("malloc");
+           fprintf(stderr, "Insufficient memory\n");
+           exit(1);
+       }
+       *current = new;
+       new->next = NULL;
+
+       new->ipnum = ipnum;
+
+       hostdata = gethostbyaddr((const char *) &ipnum, sizeof(struct in_addr),
+                                AF_INET);
+       if (hostdata == NULL) {
+           if (h_errno > MAX_ERR)
+               errors[UNKNOWN_ERR]++;
+           else
+               errors[h_errno]++;
+           new->noname = h_errno;
+           name = strdup(inet_ntoa(ipnum));
+       }
+       else {
+           new->noname = 0;
+           name = strdup(hostdata->h_name);
+           if (check) {
+               if (name == NULL) {
+                   perror("strdup");
+                   fprintf(stderr, "Insufficient memory\n");
+                   exit(1);
+               }
+               hostdata = gethostbyname(name);
+               if (hostdata != NULL) {
+                   char **hptr;
+
+                   for (hptr = hostdata->h_addr_list; *hptr != NULL; hptr++)
+                       if (((struct in_addr *) (*hptr))->s_addr == ipnum.s_addr)
+                           break;
+                   if (*hptr == NULL)
+                       hostdata = NULL;
+               }
+               if (hostdata == NULL) {
+                   fprintf(stderr, "Bad host: %s != %s\n", name,
+                           inet_ntoa(ipnum));
+                   new->noname = NO_REVERSE;
+                   free(name);
+                   name = strdup(inet_ntoa(ipnum));
+                   errors[NO_REVERSE]++;
+               }
+           }
+       }
+       new->hostname = name;
+       if (new->hostname == NULL) {
+           perror("strdup");
+           fprintf(stderr, "Insufficient memory\n");
+           exit(1);
+       }
+    }
+    else
+       cachehits++;
+
+    /* size of string == MAXDNAME +1 */
+    strncpy(string, (*current)->hostname, MAXDNAME);
+    string[MAXDNAME] = '\0';
+}
+
+/*
+ * prints various statistics to output
+ */
+
+static void stats (FILE *output)
+{
+    int i;
+    char *ipstring;
+    struct nsrec *current;
+    char *errstring[MAX_ERR + 3];
+
+    for (i = 0; i < MAX_ERR + 3; i++)
+       errstring[i] = "Unknown error";
+    errstring[HOST_NOT_FOUND] = "Host not found";
+    errstring[TRY_AGAIN] = "Try again";
+    errstring[NO_RECOVERY] = "Non recoverable error";
+    errstring[NO_DATA] = "No data record";
+    errstring[NO_ADDRESS] = "No address";
+    errstring[NO_REVERSE] = "No reverse entry";
+
+    fprintf(output, "logresolve Statistics:\n");
+
+    fprintf(output, "Entries: %d\n", entries);
+    fprintf(output, "    With name   : %d\n", withname);
+    fprintf(output, "    Resolves    : %d\n", resolves);
+    if (errors[HOST_NOT_FOUND])
+       fprintf(output, "    - Not found : %d\n", errors[HOST_NOT_FOUND]);
+    if (errors[TRY_AGAIN])
+       fprintf(output, "    - Try again : %d\n", errors[TRY_AGAIN]);
+    if (errors[NO_DATA])
+       fprintf(output, "    - No data   : %d\n", errors[NO_DATA]);
+    if (errors[NO_ADDRESS])
+       fprintf(output, "    - No address: %d\n", errors[NO_ADDRESS]);
+    if (errors[NO_REVERSE])
+       fprintf(output, "    - No reverse: %d\n", errors[NO_REVERSE]);
+    fprintf(output, "Cache hits      : %d\n", cachehits);
+    fprintf(output, "Cache size      : %d\n", cachesize);
+    fprintf(output, "Cache buckets   :     IP number * hostname\n");
+
+    for (i = 0; i < BUCKETS; i++)
+       for (current = nscache[i]; current != NULL; current = current->next) {
+           ipstring = inet_ntoa(current->ipnum);
+           if (current->noname == 0)
+               fprintf(output, "  %3d  %15s - %s\n", i, ipstring,
+                       current->hostname);
+           else {
+               if (current->noname > MAX_ERR + 2)
+                   fprintf(output, "  %3d  %15s : Unknown error\n", i,
+                           ipstring);
+               else
+                   fprintf(output, "  %3d  %15s : %s\n", i, ipstring,
+                           errstring[current->noname]);
+           }
+       }
+}
+
+
+/*
+ * gets a line from stdin
+ */
+
+static int getline (char *s, int n)
+{
+    char *cp;
+
+    if (!fgets(s, n, stdin))
+       return (0);
+    cp = strchr(s, '\n');
+    if (cp)
+       *cp = '\0';
+    return (1);
+}
+
+int main (int argc, char *argv[])
+{
+    struct in_addr ipnum;
+    char *bar, hoststring[MAXDNAME + 1], line[MAXLINE], *statfile;
+    int i, check;
+
+    check = 0;
+    statfile = NULL;
+    for (i = 1; i < argc; i++) {
+       if (strcmp(argv[i], "-c") == 0)
+           check = 1;
+       else if (strcmp(argv[i], "-s") == 0) {
+           if (i == argc - 1) {
+               fprintf(stderr, "logresolve: missing filename to -s\n");
+               exit(1);
+           }
+           i++;
+           statfile = argv[i];
+       }
+       else {
+           fprintf(stderr, "Usage: logresolve [-s statfile] [-c] < input > output\n");
+           exit(0);
+       }
+    }
+
+
+    for (i = 0; i < BUCKETS; i++)
+       nscache[i] = NULL;
+    for (i = 0; i < MAX_ERR + 2; i++)
+       errors[i] = 0;
+
+    while (getline(line, MAXLINE)) {
+       if (line[0] == '\0')
+           continue;
+       entries++;
+       if (!isdigit(line[0])) {        /* short cut */
+           puts(line);
+           withname++;
+           continue;
+       }
+       bar = strchr(line, ' ');
+       if (bar != NULL)
+           *bar = '\0';
+       ipnum.s_addr = inet_addr(line);
+       if (ipnum.s_addr == 0xffffffffu) {
+           if (bar != NULL)
+               *bar = ' ';
+           puts(line);
+           withname++;
+           continue;
+       }
+
+       resolves++;
+
+       cgethost(ipnum, hoststring, check);
+       if (bar != NULL)
+           printf("%s %s\n", hoststring, bar + 1);
+       else
+           puts(hoststring);
+    }
+
+    if (statfile != NULL) {
+       FILE *fp;
+       fp = fopen(statfile, "w");
+       if (fp == NULL) {
+           fprintf(stderr, "logresolve: could not open statistics file '%s'\n"
+                   ,statfile);
+           exit(1);
+       }
+       stats(fp);
+       fclose(fp);
+    }
+
+    return (0);
+}
diff --git a/support/logresolve.pl b/support/logresolve.pl
new file mode 100644 (file)
index 0000000..59ab9eb
--- /dev/null
@@ -0,0 +1,264 @@
+#!/usr/local/bin/perl
+
+# ====================================================================
+# Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer. 
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in
+#    the documentation and/or other materials provided with the
+#    distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+#    software must display the following acknowledgment:
+#    "This product includes software developed by the Apache Group
+#    for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+#    endorse or promote products derived from this software without
+#    prior written permission. For written permission, please contact
+#    apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache"
+#    nor may "Apache" appear in their names without prior written
+#    permission of the Apache Group.
+#
+# 6. Redistributions of any form whatsoever must retain the following
+#    acknowledgment:
+#    "This product includes software developed by the Apache Group
+#    for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see <http://www.apache.org/>.
+
+# logresolve.pl
+#
+# v 1.2 by robh @ imdb.com
+# 
+# usage: logresolve.pl <infile >outfile
+#
+# input = Apache/NCSA/.. logfile with IP numbers at start of lines
+# output = same logfile with IP addresses resolved to hostnames where
+#  name lookups succeeded.
+#
+# this differs from the C based 'logresolve' in that this script
+# spawns a number ($CHILDREN) of subprocesses to resolve addresses
+# concurrently and sets a short timeout ($TIMEOUT) for each lookup in
+# order to keep things moving quickly.
+#
+# the parent process handles caching of IP->hostnames using a Perl hash
+# it also avoids sending the same IP to multiple child processes to be
+# resolved multiple times concurrently.
+#
+# Depending on the settings of $CHILDREN and $TIMEOUT you should see
+# significant reductions in the overall time taken to resolve your
+# logfiles. With $CHILDREN=40 and $TIMEOUT=5 I've seen 200,000 - 300,000
+# logfile lines processed per hour compared to ~45,000 per hour
+# with 'logresolve'.
+#
+# I haven't yet seen any noticable reduction in the percentage of IPs
+# that fail to get resolved. Your mileage will no doubt vary. 5s is long
+# enough to wait IMO.
+#
+# Known to work with FreeBSD 2.2
+# Known to have problems with Solaris
+#
+# 980417 - use 'sockaddr_un' for bind/connect to make the script work
+#  with linux. Fix from Luuk de Boer <luuk_de_boer@pi.net>
+
+require 5.004;
+
+$|=1;
+
+use FileHandle;
+use Socket;
+
+use strict;
+no strict 'refs';
+
+use vars qw($PROTOCOL);
+$PROTOCOL = 0;
+
+my $CHILDREN = 40;
+my $TIMEOUT  = 5;
+
+my $filename;
+my %hash = ();
+my $parent = $$;
+
+my @children = ();
+for (my $child = 1; $child <=$CHILDREN; $child++) {
+       my $f = fork(); 
+       if (!$f) {
+               $filename = "./.socket.$parent.$child";
+               if (-e $filename) { unlink($filename) || warn "$filename .. $!\n";}
+               &child($child);
+               exit(0);
+       }
+       push(@children, $f);
+}
+
+&parent;
+&cleanup;
+
+## remove all temporary files before shutting down
+sub cleanup {
+        # die kiddies, die
+       kill(15, @children);
+       for (my $child = 1; $child <=$CHILDREN; $child++) {
+               if (-e "./.socket.$parent.$child") {
+                       unlink("./.socket.$parent.$child")
+                               || warn ".socket.$parent.$child $!";
+               }
+       }
+}
+       
+sub parent {
+       # Trap some possible signals to trigger temp file cleanup
+       $SIG{'KILL'} = $SIG{'INT'} = $SIG{'PIPE'} = \&cleanup;
+
+       my %CHILDSOCK;
+       my $filename;
+        ## fork child processes. Each child will create a socket connection
+        ## to this parent and use an unique temp filename to do so.
+       for (my $child = 1; $child <=$CHILDREN; $child++) {
+               $CHILDSOCK{$child}= FileHandle->new;
+
+               if (!socket($CHILDSOCK{$child}, AF_UNIX, SOCK_STREAM, $PROTOCOL)) {
+                       warn "parent socket to child failed $!";
+               }
+               $filename = "./.socket.$parent.$child";
+               my $response;
+               do {
+                       $response = connect($CHILDSOCK{$child}, sockaddr_un($filename));
+                       if ($response != 1) {
+                               sleep(1);
+                       }                       
+               } while ($response != 1);
+               $CHILDSOCK{$child}->autoflush;
+       }
+       ## All child processes should now be ready or at worst warming up 
+
+       my (@buffer, $child, $ip, $rest, $hostname, $response);
+        ## read the logfile lines from STDIN
+       while(<STDIN>) {
+               @buffer = ();   # empty the logfile line buffer array.
+               $child = 1;             # children are numbered 1..N, start with #1
+
+               # while we have a child to talk to and data to give it..
+               do {
+                       push(@buffer, $_);                                      # buffer the line
+                       ($ip, $rest) = split(/ /, $_, 2);       # separate IP form rest
+
+                       unless ($hash{$ip}) {                           # resolve if unseen IP
+                               $CHILDSOCK{$child}->print("$ip\n"); # pass IP to next child
+                               $hash{$ip} = $ip;                               # don't look it up again.
+                               $child++;
+                       }
+               } while (($child < ($CHILDREN-1)) and ($_ = <STDIN>));
+
+                ## now poll each child for a response
+               while (--$child > 0) { 
+                       $response = $CHILDSOCK{$child}->getline;
+                       chomp($response);
+                        # child sends us back both the IP and HOSTNAME, no need for us
+                        # to remember what child received any given IP, and no worries
+                        # what order we talk to the children
+                       ($ip, $hostname) = split(/\|/, $response, 2);
+                       $hash{$ip} = $hostname;
+               }
+
+                # resolve all the logfiles lines held in the log buffer array..
+               for (my $line = 0; $line <=$#buffer; $line++) {
+                        # get next buffered line
+                       ($ip, $rest) = split(/ /, $buffer[$line], 2);
+                        # separate IP from rest and replace with cached hostname
+                       printf STDOUT ("%s %s", $hash{$ip}, $rest);
+               }
+       }
+}
+
+########################################
+
+sub child {
+        # arg = numeric ID - how the parent refers to me
+       my $me = shift;
+
+        # add trap for alarm signals.
+       $SIG{'ALRM'} = sub { die "alarmed"; };
+
+        # create a socket to communicate with parent
+       socket(INBOUND, AF_UNIX, SOCK_STREAM, $PROTOCOL)
+               || die "Error with Socket: !$\n";
+       $filename = "./.socket.$parent.$me";
+       bind(INBOUND, sockaddr_un($filename))
+               || die "Error Binding $filename: $!\n";
+       listen(INBOUND, 5) || die "Error Listening: $!\n";
+
+       my ($ip, $send_back);
+       my $talk = FileHandle->new;
+
+        # accept a connection from the parent process. We only ever have
+        # have one connection where we exchange 1 line of info with the
+        # parent.. 1 line in (IP address), 1 line out (IP + hostname).
+       accept($talk, INBOUND) || die "Error Accepting: $!\n";
+        # disable I/O buffering just in case
+       $talk->autoflush;
+        # while the parent keeps sending data, we keep responding..
+       while(($ip = $talk->getline)) {
+               chomp($ip);
+                # resolve the IP if time permits and send back what we found..
+               $send_back = sprintf("%s|%s", $ip, &nslookup($ip));
+               $talk->print($send_back."\n");
+       }
+}
+
+# perform a time restricted hostname lookup.
+sub nslookup {
+        # get the IP as an arg
+       my $ip = shift;
+       my $hostname = undef;
+
+        # do the hostname lookup inside an eval. The eval will use the
+        # already configured SIGnal handler and drop out of the {} block
+        # regardless of whether the alarm occured or not.
+       eval {
+               alarm($TIMEOUT);
+               $hostname = gethostbyaddr(gethostbyname($ip), AF_INET);
+               alarm(0);
+       };
+       if ($@ =~ /alarm/) {
+                # useful for debugging perhaps..
+               # print "alarming, isn't it? ($ip)";
+       }
+
+        # return the hostname or the IP address itself if there is no hostname
+       $hostname ne "" ? $hostname : $ip;
+}
+
+
diff --git a/support/phf_abuse_log.cgi b/support/phf_abuse_log.cgi
new file mode 100644 (file)
index 0000000..9ce2749
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/local/bin/perl
+
+# This script can be used to detect people trying to abuse the security hole which
+# existed in A CGI script direstributed with Apache 1.0.3 and earlier versions.
+# You can redirect them to here using the "<Location /cgi-bin/phf*>" suggestion in
+# httpd.conf.  
+#
+# The format logged to is "[date] remote_addr remote_host [date] referrer user_agent".
+
+$LOG = "/var/log/phf_log";
+
+require "ctime.pl";
+$when = &ctime(time);
+$when =~ s/\n//go;
+$ENV{HTTP_USER_AGENT} .= " via $ENV{HTTP_VIA}" if($ENV{HTTP_VIA});
+
+open(LOG, ">>$LOG") || die "boo hoo, phf_log $!";
+print LOG "[$when] $ENV{REMOTE_ADDR} $ENV{REMOTE_HOST} $ENV{$HTTP_REFERER} $ENV{HTTP_USER_AGENT}\n";
+close(LOG);
+
+print "Content-type: text/html\r\n\r\n<BLINK>Smile, you're on Candid Camera.</BLINK>\n";
diff --git a/support/rotatelogs.c b/support/rotatelogs.c
new file mode 100644 (file)
index 0000000..dc6bd97
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Simple program to rotate Apache logs without having to kill the server.
+ *
+ * Contributed by Ben Laurie <ben@algroup.co.uk>
+ *
+ * 12 Mar 1996
+ */
+
+
+#define BUFSIZE                65536
+#define MAX_PATH       1024
+
+#include "ap_config.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+
+int main (int argc, char **argv)
+{
+    char buf[BUFSIZE], buf2[MAX_PATH];
+    time_t tLogEnd = 0;
+    time_t tRotation;
+    int nLogFD = -1;
+    int nRead;
+    char *szLogRoot;
+
+    if (argc != 3) {
+       fprintf(stderr,
+               "%s <logfile> <rotation time in seconds>\n\n",
+               argv[0]);
+#ifdef OS2
+       fprintf(stderr,
+               "Add this:\n\nTransferLog \"|%s.exe /some/where 86400\"\n\n",
+               argv[0]);
+#else
+       fprintf(stderr,
+               "Add this:\n\nTransferLog \"|%s /some/where 86400\"\n\n",
+               argv[0]);
+#endif
+       fprintf(stderr,
+               "to httpd.conf. The generated name will be /some/where.nnnn "
+               "where nnnn is the\nsystem time at which the log nominally "
+               "starts (N.B. this time will always be a\nmultiple of the "
+               "rotation time, so you can synchronize cron scripts with it).\n"
+               "At the end of each rotation time a new log is started.\n");
+       exit(1);
+    }
+
+    szLogRoot = argv[1];
+    tRotation = atoi(argv[2]);
+    if (tRotation <= 0) {
+       fprintf(stderr, "Rotation time must be > 0\n");
+       exit(6);
+    }
+
+    for (;;) {
+       nRead = read(0, buf, sizeof buf);
+       if (nRead == 0)
+           exit(3);
+       if (nRead < 0)
+           if (errno != EINTR)
+               exit(4);
+       if (nLogFD >= 0 && (time(NULL) >= tLogEnd || nRead < 0)) {
+           close(nLogFD);
+           nLogFD = -1;
+       }
+       if (nLogFD < 0) {
+           time_t tLogStart = (time(NULL) / tRotation) * tRotation;
+           sprintf(buf2, "%s.%010d", szLogRoot, (int) tLogStart);
+           tLogEnd = tLogStart + tRotation;
+           nLogFD = open(buf2, O_WRONLY | O_CREAT | O_APPEND, 0666);
+           if (nLogFD < 0) {
+               perror(buf2);
+               exit(2);
+           }
+       }
+       if (write(nLogFD, buf, nRead) != nRead) {
+           perror(buf2);
+           exit(5);
+       }
+    }
+}
diff --git a/support/split-logfile b/support/split-logfile
new file mode 100644 (file)
index 0000000..7cc4652
--- /dev/null
@@ -0,0 +1,103 @@
+#!/usr/local/bin/perl
+#
+# ====================================================================
+# Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer. 
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in
+#    the documentation and/or other materials provided with the
+#    distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+#    software must display the following acknowledgment:
+#    "This product includes software developed by the Apache Group
+#    for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+#    endorse or promote products derived from this software without
+#    prior written permission.  For permission please contact
+#    Apache@Apache.Org.
+#
+# 5. Products derived from this software may not be called "Apache"
+#    nor may "Apache" appear in their names without prior written
+#    permission of the Apache Group.
+#
+# 6. Redistributions of any form whatsoever must retain the following
+#    acknowledgment:
+#    "This product includes software developed by the Apache Group
+#    for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see <http://www.apache.org/>.
+#
+
+#
+# This script will take a combined Web server access
+# log file and break its contents into separate files.
+# It assumes that the first field of each line is the
+# virtual host identity (put there by "%v"), and that
+# the logfiles should be named that+".log" in the current
+# directory.
+#
+# The combined log file is read from stdin. Records read
+# will be appended to any existing log files.
+#
+%is_open = ();
+
+while ($log_line = <STDIN>) {
+    #
+    # Get the first token from the log record; it's the
+    # identity of the virtual host to which the record
+    # applies.
+    #
+    ($vhost) = split (/\s/, $log_line);
+    #
+    # Normalize the virtual host name to all lowercase.
+    # If it's blank, the request was handled by the default
+    # server, so supply a default name.  This shouldn't
+    # happen, but caution rocks.
+    #
+    $vhost = lc ($vhost) or "access";
+    #
+    # If the log file for this virtual host isn't opened
+    # yet, do it now.
+    #
+    if (! $is_open{$vhost}) {
+        open $vhost, ">>${vhost}.log"
+            or die ("Can't open ${vhost}.log");
+        $is_open{$vhost} = 1;
+    }
+    #
+    # Strip off the first token (which may be null in the
+    # case of the default server), and write the edited
+    # record to the current log file.
+    #
+    $log_line =~ s/^\S*\s+//;
+    printf $vhost "%s", $log_line;
+}
+exit 0;
diff --git a/support/suexec.c b/support/suexec.c
new file mode 100644 (file)
index 0000000..13f3fcc
--- /dev/null
@@ -0,0 +1,566 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * suexec.c -- "Wrapper" support program for suEXEC behaviour for Apache
+ *
+ ***********************************************************************
+ *
+ * NOTE! : DO NOT edit this code!!!  Unless you know what you are doing,
+ *         editing this code might open up your system in unexpected 
+ *         ways to would-be crackers.  Every precaution has been taken 
+ *         to make this code as safe as possible; alter it at your own
+ *         risk.
+ *
+ ***********************************************************************
+ *
+ *
+ */
+
+#include "ap_config.h"
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <stdarg.h>
+
+#include "suexec.h"
+
+/*
+ ***********************************************************************
+ * There is no initgroups() in QNX, so I believe this is safe :-)
+ * Use cc -osuexec -3 -O -mf -DQNX suexec.c to compile.
+ *
+ * May 17, 1997.
+ * Igor N. Kovalenko -- infoh@mail.wplus.net
+ ***********************************************************************
+ */
+
+#if defined(NEED_INITGROUPS)
+int initgroups(const char *name, gid_t basegid)
+{
+/* QNX and MPE do not appear to support supplementary groups. */
+    return 0;
+}
+#endif
+
+#if defined(PATH_MAX)
+#define AP_MAXPATH PATH_MAX
+#elif defined(MAXPATHLEN)
+#define AP_MAXPATH MAXPATHLEN
+#else
+#define AP_MAXPATH 8192
+#endif
+
+#define AP_ENVBUF 256
+
+extern char **environ;
+static FILE *log = NULL;
+
+char *safe_env_lst[] =
+{
+    "AUTH_TYPE",
+    "CONTENT_LENGTH",
+    "CONTENT_TYPE",
+    "DATE_GMT",
+    "DATE_LOCAL",
+    "DOCUMENT_NAME",
+    "DOCUMENT_PATH_INFO",
+    "DOCUMENT_ROOT",
+    "DOCUMENT_URI",
+    "FILEPATH_INFO",
+    "GATEWAY_INTERFACE",
+    "LAST_MODIFIED",
+    "PATH_INFO",
+    "PATH_TRANSLATED",
+    "QUERY_STRING",
+    "QUERY_STRING_UNESCAPED",
+    "REMOTE_ADDR",
+    "REMOTE_HOST",
+    "REMOTE_IDENT",
+    "REMOTE_PORT",
+    "REMOTE_USER",
+    "REDIRECT_QUERY_STRING",
+    "REDIRECT_STATUS",
+    "REDIRECT_URL",
+    "REQUEST_METHOD",
+    "REQUEST_URI",
+    "SCRIPT_FILENAME",
+    "SCRIPT_NAME",
+    "SCRIPT_URI",
+    "SCRIPT_URL",
+    "SERVER_ADMIN",
+    "SERVER_NAME",
+    "SERVER_ADDR",
+    "SERVER_PORT",
+    "SERVER_PROTOCOL",
+    "SERVER_SOFTWARE",
+    "UNIQUE_ID",
+    "USER_NAME",
+    "TZ",
+    NULL
+};
+
+
+static void err_output(const char *fmt, va_list ap)
+{
+#ifdef LOG_EXEC
+    time_t timevar;
+    struct tm *lt;
+
+    if (!log) {
+       if ((log = fopen(LOG_EXEC, "a")) == NULL) {
+           fprintf(stderr, "failed to open log file\n");
+           perror("fopen");
+           exit(1);
+       }
+    }
+
+    time(&timevar);
+    lt = localtime(&timevar);
+
+    fprintf(log, "[%d-%.2d-%.2d %.2d:%.2d:%.2d]: ",
+           lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday,
+           lt->tm_hour, lt->tm_min, lt->tm_sec);
+
+    vfprintf(log, fmt, ap);
+
+    fflush(log);
+#endif /* LOG_EXEC */
+    return;
+}
+
+static void log_err(const char *fmt,...)
+{
+#ifdef LOG_EXEC
+    va_list ap;
+
+    va_start(ap, fmt);
+    err_output(fmt, ap);
+    va_end(ap);
+#endif /* LOG_EXEC */
+    return;
+}
+
+static void clean_env(void)
+{
+    char pathbuf[512];
+    char **cleanenv;
+    char **ep;
+    int cidx = 0;
+    int idx;
+
+
+    if ((cleanenv = (char **) calloc(AP_ENVBUF, sizeof(char *))) == NULL) {
+        log_err("failed to malloc memory for environment\n");
+       exit(120);
+    }
+
+    sprintf(pathbuf, "PATH=%s", SAFE_PATH);
+    cleanenv[cidx] = strdup(pathbuf);
+    cidx++;
+
+    for (ep = environ; *ep && cidx < AP_ENVBUF-1; ep++) {
+       if (!strncmp(*ep, "HTTP_", 5)) {
+           cleanenv[cidx] = *ep;
+           cidx++;
+       }
+       else {
+           for (idx = 0; safe_env_lst[idx]; idx++) {
+               if (!strncmp(*ep, safe_env_lst[idx],
+                            strlen(safe_env_lst[idx]))) {
+                   cleanenv[cidx] = *ep;
+                   cidx++;
+                   break;
+               }
+           }
+       }
+    }
+
+    cleanenv[cidx] = NULL;
+
+    environ = cleanenv;
+}
+
+int main(int argc, char *argv[])
+{
+    int userdir = 0;           /* ~userdir flag             */
+    uid_t uid;                 /* user information          */
+    gid_t gid;                 /* target group placeholder  */
+    char *target_uname;                /* target user name          */
+    char *target_gname;                /* target group name         */
+    char *target_homedir;      /* target home directory     */
+    char *actual_uname;                /* actual user name          */
+    char *actual_gname;                /* actual group name         */
+    char *prog;                        /* name of this program      */
+    char *cmd;                 /* command to be executed    */
+    char cwd[AP_MAXPATH];      /* current working directory */
+    char dwd[AP_MAXPATH];      /* docroot working directory */
+    struct passwd *pw;         /* password entry holder     */
+    struct group *gr;          /* group entry holder        */
+    struct stat dir_info;      /* directory info holder     */
+    struct stat prg_info;      /* program info holder       */
+
+    /*
+     * If there are a proper number of arguments, set
+     * all of them to variables.  Otherwise, error out.
+     */
+    prog = argv[0];
+    if (argc < 4) {
+       log_err("too few arguments\n");
+       exit(101);
+    }
+    target_uname = argv[1];
+    target_gname = argv[2];
+    cmd = argv[3];
+
+    /*
+     * Check existence/validity of the UID of the user
+     * running this program.  Error out if invalid.
+     */
+    uid = getuid();
+    if ((pw = getpwuid(uid)) == NULL) {
+       log_err("invalid uid: (%ld)\n", uid);
+       exit(102);
+    }
+
+    /*
+     * Check to see if the user running this program
+     * is the user allowed to do so as defined in
+     * suexec.h.  If not the allowed user, error out.
+     */
+#ifdef _OSD_POSIX
+    /* User name comparisons are case insensitive on BS2000/OSD */
+    if (strcasecmp(HTTPD_USER, pw->pw_name)) {
+        log_err("user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER);
+       exit(103);
+    }
+#else  /*_OSD_POSIX*/
+    if (strcmp(HTTPD_USER, pw->pw_name)) {
+        log_err("user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER);
+       exit(103);
+    }
+#endif /*_OSD_POSIX*/
+
+    /*
+     * Check for a leading '/' (absolute path) in the command to be executed,
+     * or attempts to back up out of the current directory,
+     * to protect against attacks.  If any are
+     * found, error out.  Naughty naughty crackers.
+     */
+    if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3))
+       || (strstr(cmd, "/../") != NULL)) {
+        log_err("invalid command (%s)\n", cmd);
+       exit(104);
+    }
+
+    /*
+     * Check to see if this is a ~userdir request.  If
+     * so, set the flag, and remove the '~' from the
+     * target username.
+     */
+    if (!strncmp("~", target_uname, 1)) {
+       target_uname++;
+       userdir = 1;
+    }
+
+    /*
+     * Error out if the target username is invalid.
+     */
+    if ((pw = getpwnam(target_uname)) == NULL) {
+       log_err("invalid target user name: (%s)\n", target_uname);
+       exit(105);
+    }
+
+    /*
+     * Error out if the target group name is invalid.
+     */
+    if (strspn(target_gname, "1234567890") != strlen(target_gname)) {
+       if ((gr = getgrnam(target_gname)) == NULL) {
+           log_err("invalid target group name: (%s)\n", target_gname);
+           exit(106);
+       }
+       gid = gr->gr_gid;
+       actual_gname = strdup(gr->gr_name);
+    }
+    else {
+       gid = atoi(target_gname);
+       actual_gname = strdup(target_gname);
+    }
+
+#ifdef _OSD_POSIX
+    /*
+     * Initialize BS2000 user environment
+     */
+    {
+       pid_t pid;
+       int status;
+
+       switch (pid = ufork(target_uname))
+       {
+       case -1:        /* Error */
+           log_err("failed to setup bs2000 environment for user %s: %s\n",
+                   target_uname, strerror(errno));
+           exit(150);
+       case 0: /* Child */
+           break;
+       default:        /* Father */
+           while (pid != waitpid(pid, &status, 0))
+               ;
+           /* @@@ FIXME: should we deal with STOP signals as well? */
+           if (WIFSIGNALED(status))
+               kill (getpid(), WTERMSIG(status));
+           exit(WEXITSTATUS(status));
+       }
+    }
+#endif /*_OSD_POSIX*/
+
+    /*
+     * Save these for later since initgroups will hose the struct
+     */
+    uid = pw->pw_uid;
+    actual_uname = strdup(pw->pw_name);
+    target_homedir = strdup(pw->pw_dir);
+
+    /*
+     * Log the transaction here to be sure we have an open log 
+     * before we setuid().
+     */
+    log_err("uid: (%s/%s) gid: (%s/%s) cmd: %s\n",
+           target_uname, actual_uname,
+           target_gname, actual_gname,
+           cmd);
+
+    /*
+     * Error out if attempt is made to execute as root or as
+     * a UID less than UID_MIN.  Tsk tsk.
+     */
+    if ((uid == 0) || (uid < UID_MIN)) {
+       log_err("cannot run as forbidden uid (%d/%s)\n", uid, cmd);
+       exit(107);
+    }
+
+    /*
+     * Error out if attempt is made to execute as root group
+     * or as a GID less than GID_MIN.  Tsk tsk.
+     */
+    if ((gid == 0) || (gid < GID_MIN)) {
+       log_err("cannot run as forbidden gid (%d/%s)\n", gid, cmd);
+       exit(108);
+    }
+
+    /*
+     * Change UID/GID here so that the following tests work over NFS.
+     *
+     * Initialize the group access list for the target user,
+     * and setgid() to the target group. If unsuccessful, error out.
+     */
+    if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) {
+       log_err("failed to setgid (%ld: %s)\n", gid, cmd);
+       exit(109);
+    }
+
+    /*
+     * setuid() to the target user.  Error out on fail.
+     */
+    if ((setuid(uid)) != 0) {
+       log_err("failed to setuid (%ld: %s)\n", uid, cmd);
+       exit(110);
+    }
+
+    /*
+     * Get the current working directory, as well as the proper
+     * document root (dependant upon whether or not it is a
+     * ~userdir request).  Error out if we cannot get either one,
+     * or if the current working directory is not in the docroot.
+     * Use chdir()s and getcwd()s to avoid problems with symlinked
+     * directories.  Yuck.
+     */
+    if (getcwd(cwd, AP_MAXPATH) == NULL) {
+       log_err("cannot get current working directory\n");
+       exit(111);
+    }
+
+    if (userdir) {
+       if (((chdir(target_homedir)) != 0) ||
+           ((chdir(USERDIR_SUFFIX)) != 0) ||
+           ((getcwd(dwd, AP_MAXPATH)) == NULL) ||
+           ((chdir(cwd)) != 0)) {
+           log_err("cannot get docroot information (%s)\n", target_homedir);
+           exit(112);
+       }
+    }
+    else {
+       if (((chdir(DOC_ROOT)) != 0) ||
+           ((getcwd(dwd, AP_MAXPATH)) == NULL) ||
+           ((chdir(cwd)) != 0)) {
+           log_err("cannot get docroot information (%s)\n", DOC_ROOT);
+           exit(113);
+       }
+    }
+
+    if ((strncmp(cwd, dwd, strlen(dwd))) != 0) {
+       log_err("command not in docroot (%s/%s)\n", cwd, cmd);
+       exit(114);
+    }
+
+    /*
+     * Stat the cwd and verify it is a directory, or error out.
+     */
+    if (((lstat(cwd, &dir_info)) != 0) || !(S_ISDIR(dir_info.st_mode))) {
+       log_err("cannot stat directory: (%s)\n", cwd);
+       exit(115);
+    }
+
+    /*
+     * Error out if cwd is writable by others.
+     */
+    if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) {
+       log_err("directory is writable by others: (%s)\n", cwd);
+       exit(116);
+    }
+
+    /*
+     * Error out if we cannot stat the program.
+     */
+    if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) {
+       log_err("cannot stat program: (%s)\n", cmd);
+       exit(117);
+    }
+
+    /*
+     * Error out if the program is writable by others.
+     */
+    if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) {
+       log_err("file is writable by others: (%s/%s)\n", cwd, cmd);
+       exit(118);
+    }
+
+    /*
+     * Error out if the file is setuid or setgid.
+     */
+    if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID)) {
+       log_err("file is either setuid or setgid: (%s/%s)\n", cwd, cmd);
+       exit(119);
+    }
+
+    /*
+     * Error out if the target name/group is different from
+     * the name/group of the cwd or the program.
+     */
+    if ((uid != dir_info.st_uid) ||
+       (gid != dir_info.st_gid) ||
+       (uid != prg_info.st_uid) ||
+       (gid != prg_info.st_gid)) {
+       log_err("target uid/gid (%ld/%ld) mismatch "
+               "with directory (%ld/%ld) or program (%ld/%ld)\n",
+               uid, gid,
+               dir_info.st_uid, dir_info.st_gid,
+               prg_info.st_uid, prg_info.st_gid);
+       exit(120);
+    }
+    /*
+     * Error out if the program is not executable for the user.
+     * Otherwise, she won't find any error in the logs except for
+     * "[error] Premature end of script headers: ..."
+     */
+    if (!(prg_info.st_mode & S_IXUSR)) {
+       log_err("file has no execute permission: (%s/%s)\n", cwd, cmd);
+       exit(121);
+    }
+
+    clean_env();
+
+    /* 
+     * Be sure to close the log file so the CGI can't
+     * mess with it.  If the exec fails, it will be reopened 
+     * automatically when log_err is called.  Note that the log
+     * might not actually be open if LOG_EXEC isn't defined.
+     * However, the "log" cell isn't ifdef'd so let's be defensive
+     * and assume someone might have done something with it
+     * outside an ifdef'd LOG_EXEC block.
+     */
+    if (log != NULL) {
+       fclose(log);
+       log = NULL;
+    }
+
+    /*
+     * Execute the command, replacing our image with its own.
+     */
+#ifdef NEED_HASHBANG_EMUL
+    /* We need the #! emulation when we want to execute scripts */
+    {
+       extern char **environ;
+
+       ap_execve(cmd, &argv[3], environ);
+    }
+#else /*NEED_HASHBANG_EMUL*/
+    execv(cmd, &argv[3]);
+#endif /*NEED_HASHBANG_EMUL*/
+
+    /*
+     * (I can't help myself...sorry.)
+     *
+     * Uh oh.  Still here.  Where's the kaboom?  There was supposed to be an
+     * EARTH-shattering kaboom!
+     *
+     * Oh well, log the failure and error out.
+     */
+    log_err("(%d)%s: exec failed (%s)\n", errno, strerror(errno), cmd);
+    exit(255);
+}
diff --git a/support/suexec.h b/support/suexec.h
new file mode 100644 (file)
index 0000000..892badd
--- /dev/null
@@ -0,0 +1,143 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * suexec.h -- user-definable variables for the suexec wrapper code.
+ *             (See README.configure on how to customize these variables.)
+ */
+
+
+#ifndef _SUEXEC_H
+#define _SUEXEC_H
+
+/*
+ * HTTPD_USER -- Define as the username under which Apache normally
+ *               runs.  This is the only user allowed to execute
+ *               this program.
+ */
+#ifndef HTTPD_USER
+#define HTTPD_USER "www"
+#endif
+
+/*
+ * UID_MIN -- Define this as the lowest UID allowed to be a target user
+ *            for suEXEC.  For most systems, 500 or 100 is common.
+ */
+#ifndef UID_MIN
+#define UID_MIN 100
+#endif
+
+/*
+ * GID_MIN -- Define this as the lowest GID allowed to be a target group
+ *            for suEXEC.  For most systems, 100 is common.
+ */
+#ifndef GID_MIN
+#define GID_MIN 100
+#endif
+
+/*
+ * USERDIR_SUFFIX -- Define to be the subdirectory under users' 
+ *                   home directories where suEXEC access should
+ *                   be allowed.  All executables under this directory
+ *                   will be executable by suEXEC as the user so 
+ *                   they should be "safe" programs.  If you are 
+ *                   using a "simple" UserDir directive (ie. one 
+ *                   without a "*" in it) this should be set to 
+ *                   the same value.  suEXEC will not work properly
+ *                   in cases where the UserDir directive points to 
+ *                   a location that is not the same as the user's
+ *                   home directory as referenced in the passwd file.
+ *
+ *                   If you have VirtualHosts with a different
+ *                   UserDir for each, you will need to define them to
+ *                   all reside in one parent directory; then name that
+ *                   parent directory here.  IF THIS IS NOT DEFINED
+ *                   PROPERLY, ~USERDIR CGI REQUESTS WILL NOT WORK!
+ *                   See the suEXEC documentation for more detailed
+ *                   information.
+ */
+#ifndef USERDIR_SUFFIX
+#define USERDIR_SUFFIX "public_html"
+#endif
+
+/*
+ * LOG_EXEC -- Define this as a filename if you want all suEXEC
+ *             transactions and errors logged for auditing and
+ *             debugging purposes.
+ */
+#ifndef LOG_EXEC
+#define LOG_EXEC "/usr/local/apache/logs/cgi.log"      /* Need me? */
+#endif
+
+/*
+ * DOC_ROOT -- Define as the DocumentRoot set for Apache.  This
+ *             will be the only hierarchy (aside from UserDirs)
+ *             that can be used for suEXEC behavior.
+ */
+#ifndef DOC_ROOT
+#define DOC_ROOT "/usr/local/apache/htdocs"
+#endif
+
+/*
+ * SAFE_PATH -- Define a safe PATH environment to pass to CGI executables.
+ *
+ */
+#ifndef SAFE_PATH
+#define SAFE_PATH "/usr/local/bin:/usr/bin:/bin"
+#endif
+
+#endif /* _SUEXEC_H */
diff --git a/test/.cvsignore b/test/.cvsignore
new file mode 100644 (file)
index 0000000..e2244a4
--- /dev/null
@@ -0,0 +1,11 @@
+a.out
+time-FCNTL
+time-FLOCK
+time-SYSVSEM
+time-SYSVSEM2
+time-PTHREAD
+time-USLOCK
+zb
+test-writev
+test_date
+test_select
diff --git a/test/.indent.pro b/test/.indent.pro
new file mode 100644 (file)
index 0000000..a9fbe9f
--- /dev/null
@@ -0,0 +1,54 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
diff --git a/test/README b/test/README
new file mode 100644 (file)
index 0000000..9f8be50
--- /dev/null
@@ -0,0 +1,3 @@
+This directory contains useful test code for testing various bits
+of Apache functionality.  This stuff is for the developers only,
+so we might remove it on public releases.
diff --git a/test/check_chunked b/test/check_chunked
new file mode 100644 (file)
index 0000000..6a12167
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/perl -w
+
+# This is meant to be used on the raw output of an HTTP/1.1 connection
+# to check that the chunks are all correctly laid out.  It's easiest
+# to use a tool like netcat to generate the output.  This script
+# *insists* that \r exist in the output.
+#
+# You can find netcat at avian.org:/src/hacks/nc110.tgz.
+
+use strict;
+
+my $is_chunked = 0;
+
+# must toss headers
+while(<>) {
+    if (/^Transfer-Encoding:\s+chunked/i) {
+       $is_chunked = 1;
+    }
+    last if ($_ eq "\r\n");
+}
+
+$is_chunked || die "wasn't chunked\n";
+
+for(;;) {
+    $_ = <> || die "unexpected end of file!\n";
+
+    m#^([0-9a-f]+) *\r$#i || die "bogus chunklen: $_";
+
+    my $chunklen = hex($1);
+
+    exit 0 if ($chunklen == 0);
+
+    chop; chop;
+    print "$_ ";
+
+    my $data = '';
+    read(ARGV, $data, $chunklen) == $chunklen || die "short read!\n";
+
+    $_ = <> || die "unexpected end of file!\n";
+
+    $_ eq "\r\n" || die "missing chunk trailer!\n";
+}
diff --git a/test/cls.c b/test/cls.c
new file mode 100644 (file)
index 0000000..91114a7
--- /dev/null
@@ -0,0 +1,166 @@
+#include <ctype.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+/*
+ * Compare a string to a mask
+ * Mask characters:
+ *   @ - uppercase letter
+ *   # - lowercase letter
+ *   & - hex digit
+ *   # - digit
+ *   * - swallow remaining characters 
+ *  <x> - exact match for any other character
+ */
+static int checkmask(const char *data, const char *mask)
+{
+    int i, ch, d;
+
+    for (i = 0; mask[i] != '\0' && mask[i] != '*'; i++) {
+       ch = mask[i];
+       d = data[i];
+       if (ch == '@') {
+           if (!isupper(d))
+               return 0;
+       }
+       else if (ch == '$') {
+           if (!islower(d))
+               return 0;
+       }
+       else if (ch == '#') {
+           if (!isdigit(d))
+               return 0;
+       }
+       else if (ch == '&') {
+           if (!isxdigit(d))
+               return 0;
+       }
+       else if (ch != d)
+           return 0;
+    }
+
+    if (mask[i] == '*')
+       return 1;
+    else
+       return (data[i] == '\0');
+}
+
+/*
+ * Converts 8 hex digits to a time integer
+ */
+static int hex2sec(const char *x)
+{
+    int i, ch;
+    unsigned int j;
+
+    for (i = 0, j = 0; i < 8; i++) {
+       ch = x[i];
+       j <<= 4;
+       if (isdigit(ch))
+           j |= ch - '0';
+       else if (isupper(ch))
+           j |= ch - ('A' - 10);
+       else
+           j |= ch - ('a' - 10);
+    }
+    if (j == 0xffffffff)
+       return -1;              /* so that it works with 8-byte ints */
+    else
+       return j;
+}
+
+int main(int argc, char **argv)
+{
+    int i, ver;
+    DIR *d;
+    struct dirent *e;
+    const char *s;
+    FILE *fp;
+    char path[FILENAME_MAX + 1];
+    char line[1035];
+    time_t date, lmod, expire;
+    unsigned int len;
+    struct tm ts;
+    char sdate[30], slmod[30], sexpire[30];
+    const char time_format[] = "%e %b %Y %R";
+
+    if (argc != 2) {
+       printf("Usage: cls directory\n");
+       exit(0);
+    }
+
+    d = opendir(argv[1]);
+    if (d == NULL) {
+       perror("opendir");
+       exit(1);
+    }
+
+    for (;;) {
+       e = readdir(d);
+       if (e == NULL)
+           break;
+       s = e->d_name;
+       if (s[0] == '.' || s[0] == '#')
+           continue;
+       sprintf(path, "%s/%s", argv[1], s);
+       fp = fopen(path, "r");
+       if (fp == NULL) {
+           perror("fopen");
+           continue;
+       }
+       if (fgets(line, 1034, fp) == NULL) {
+           perror("fgets");
+           fclose(fp);
+           continue;
+       }
+       if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&\n")) {
+           fprintf(stderr, "Bad cache file\n");
+           fclose(fp);
+           continue;
+       }
+       date = hex2sec(line);
+       lmod = hex2sec(line + 9);
+       expire = hex2sec(line + 18);
+       ver = hex2sec(line + 27);
+       len = hex2sec(line + 35);
+       if (fgets(line, 1034, fp) == NULL) {
+           perror("fgets");
+           fclose(fp);
+           continue;
+       }
+       fclose(fp);
+       i = strlen(line);
+       if (strncmp(line, "X-URL: ", 7) != 0 || line[i - 1] != '\n') {
+           fprintf(stderr, "Bad cache file\n");
+           continue;
+       }
+       line[i - 1] = '\0';
+       if (date != -1) {
+           ts = *gmtime(&date);
+           strftime(sdate, 30, time_format, &ts);
+       }
+       else
+           strcpy(sdate, "-");
+
+       if (lmod != -1) {
+           ts = *gmtime(&lmod);
+           strftime(slmod, 30, time_format, &ts);
+       }
+       else
+           strcpy(slmod, "-");
+
+       if (expire != -1) {
+           ts = *gmtime(&expire);
+           strftime(sexpire, 30, time_format, &ts);
+       }
+       else
+           strcpy(sexpire, "-");
+
+       printf("%s: %d; %s  %s  %s\n", line + 7, ver, sdate, slmod, sexpire);
+    }
+
+    closedir(d);
+    return 0;
+}
diff --git a/test/tcpdumpscii.txt b/test/tcpdumpscii.txt
new file mode 100644 (file)
index 0000000..9c1060e
--- /dev/null
@@ -0,0 +1,50 @@
+
+From marcs@znep.com Fri Apr 17 15:16:16 1998
+Date: Sat, 22 Nov 1997 20:44:10 -0700 (MST)
+From: Marc Slemko <marcs@znep.com>
+To: TLOSAP <new-httpd@apache.org>
+Subject: Re: Getting ethernet packets content under FreeBSD?  (fwd)
+Reply-To: new-httpd@apache.org
+
+Anyone too lazy to hack tcpdump (eg. my tcpdump has a -X option to display
+the data in ASCII) can use something like the below to grab HTTP headers
+when debugging broken clients.
+
+Nothing complicated, but handy.
+
+---------- Forwarded message ----------
+Date: Sat, 22 Nov 1997 14:35:23 PST
+From: Bill Fenner <fenner@parc.xerox.com>
+To: Nate Williams <nate@mt.sri.com>
+Cc: bmah@ca.sandia.gov, hackers@FreeBSD.ORG
+Subject: Re: Getting ethernet packets content under FreeBSD? 
+
+I usually just use this perl script, which I call "tcpdumpscii".
+Then run "tcpdumpscii -s 1500 -x [other tcpdump args]".
+
+  Bill
+
+#!/import/misc/bin/perl
+#
+#
+open(TCPDUMP,"tcpdump -l @ARGV|");
+while (<TCPDUMP>) {
+       if (/^\s+(\S\S)+/) {
+               $sav = $_;
+               $asc = "";
+               while (s/\s*(\S\S)\s*//) {
+                       $i = hex($1);
+                       if ($i < 32 || $i > 126) {
+                               $asc .= ".";
+                       } else {
+                               $asc .= pack(C,hex($1));
+                       }
+               }
+               $foo = "." x length($asc);
+               $_ = $sav;
+               s/\t/        /g;
+               s/^$foo/$asc/;
+       }
+       print;
+}
+
diff --git a/test/test-writev.c b/test/test-writev.c
new file mode 100644 (file)
index 0000000..852c701
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+    test-writev: use this to figure out if your writev() does intelligent
+    things on the network.  Some writev()s when given multiple buffers
+    will break them up into multiple packets, which is a waste.
+
+    Linux prior to 2.0.31 has this problem.
+
+    Solaris 2.5, 2.5.1 doesn't appear to, 2.6 hasn't been tested.
+
+    IRIX 5.3 doesn't have this problem.
+
+    To use this you want to snoop the wire with tcpdump, and then run
+    "test-writev a.b.c.d port#" ... against some TCP service on another
+    box.  For example you can run it against port 80 on another server.
+    You want to look to see how many data packets are sent, you're hoping
+    only one of size 300 is sent.
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE (-1ul)
+#endif
+
+void main( int argc, char **argv )
+{
+    struct sockaddr_in server_addr;
+    int s;
+    struct iovec vector[3];
+    char buf[100];
+    int i;
+    const int just_say_no = 1;
+
+    if( argc != 3 ) {
+usage:
+       fprintf( stderr, "usage: test-writev a.b.c.d port#\n" );
+       exit( 1 );
+    }
+    server_addr.sin_family = AF_INET;
+    server_addr.sin_addr.s_addr = inet_addr( argv[1] );
+    if( server_addr.sin_addr.s_addr == INADDR_NONE ) {
+       fprintf( stderr, "bogus address\n" );
+       goto usage;
+    }
+    server_addr.sin_port = htons( atoi( argv[2] ) );
+
+    s = socket( AF_INET, SOCK_STREAM, 0 );
+    if( s < 0 ) {
+       perror("socket");
+       exit(1);
+    }
+    if( connect( s, (struct sockaddr *)&server_addr, sizeof( server_addr ) )
+       != 0 ) {
+       perror("connect");
+       exit(1);
+    }
+
+    if( setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&just_say_no,
+       sizeof(just_say_no)) != 0 ) {
+       perror( "TCP_NODELAY" );
+       exit(1);
+    }
+    /* now build up a two part writev and write it out */
+    for( i = 0; i < sizeof( buf ); ++i ) {
+       buf[i] = 'x';
+    }
+    vector[0].iov_base = buf;
+    vector[0].iov_len = sizeof(buf);
+    vector[1].iov_base = buf;
+    vector[1].iov_len = sizeof(buf);
+    vector[2].iov_base = buf;
+    vector[2].iov_len = sizeof(buf);
+
+    i = writev( s, &vector[0], 3 );
+    fprintf( stdout, "i=%d, errno=%d\n", i, errno );
+    exit(0);
+}
diff --git a/test/test_date.c b/test/test_date.c
new file mode 100644 (file)
index 0000000..4beaf79
--- /dev/null
@@ -0,0 +1,180 @@
+/* This program tests the parseHTTPdate routine in ../main/util_date.c.
+ *
+ * It is only semiautomated in that I would run it, modify the code to
+ * use a different algorithm or seed, recompile and run again, etc.
+ * Obviously it should use an argument for that, but I never got around
+ * to changing the implementation.
+ * 
+ *     gcc -g -O2 -I../main -o test_date ../main/util_date.o test_date.c
+ *     test_date | egrep '^No '
+ * 
+ * Roy Fielding, 1996
+ */
+#define API_EXPORT(x) x
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "util_date.h"
+
+static const long year2secs[] = {
+             0L,    /* 1970 */
+      31536000L,    /* 1971 */
+      63072000L,    /* 1972 */
+      94694400L,    /* 1973 */
+     126230400L,    /* 1974 */
+     157766400L,    /* 1975 */
+     189302400L,    /* 1976 */
+     220924800L,    /* 1977 */
+     252460800L,    /* 1978 */
+     283996800L,    /* 1979 */
+     315532800L,    /* 1980 */
+     347155200L,    /* 1981 */
+     378691200L,    /* 1982 */
+     410227200L,    /* 1983 */
+     441763200L,    /* 1984 */
+     473385600L,    /* 1985 */
+     504921600L,    /* 1986 */
+     536457600L,    /* 1987 */
+     567993600L,    /* 1988 */
+     599616000L,    /* 1989 */
+     631152000L,    /* 1990 */
+     662688000L,    /* 1991 */
+     694224000L,    /* 1992 */
+     725846400L,    /* 1993 */
+     757382400L,    /* 1994 */
+     788918400L,    /* 1995 */
+     820454400L,    /* 1996 */
+     852076800L,    /* 1997 */
+     883612800L,    /* 1998 */
+     915148800L,    /* 1999 */
+     946684800L,    /* 2000 */
+     978307200L,    /* 2001 */
+    1009843200L,    /* 2002 */
+    1041379200L,    /* 2003 */
+    1072915200L,    /* 2004 */
+    1104537600L,    /* 2005 */
+    1136073600L,    /* 2006 */
+    1167609600L,    /* 2007 */
+    1199145600L,    /* 2008 */
+    1230768000L,    /* 2009 */
+    1262304000L,    /* 2010 */
+    1293840000L,    /* 2011 */
+    1325376000L,    /* 2012 */
+    1356998400L,    /* 2013 */
+    1388534400L,    /* 2014 */
+    1420070400L,    /* 2015 */
+    1451606400L,    /* 2016 */
+    1483228800L,    /* 2017 */
+    1514764800L,    /* 2018 */
+    1546300800L,    /* 2019 */
+    1577836800L,    /* 2020 */
+    1609459200L,    /* 2021 */
+    1640995200L,    /* 2022 */
+    1672531200L,    /* 2023 */
+    1704067200L,    /* 2024 */
+    1735689600L,    /* 2025 */
+    1767225600L,    /* 2026 */
+    1798761600L,    /* 2027 */
+    1830297600L,    /* 2028 */
+    1861920000L,    /* 2029 */
+    1893456000L,    /* 2030 */
+    1924992000L,    /* 2031 */
+    1956528000L,    /* 2032 */
+    1988150400L,    /* 2033 */
+    2019686400L,    /* 2034 */
+    2051222400L,    /* 2035 */
+    2082758400L,    /* 2036 */
+    2114380800L,    /* 2037 */
+    2145916800L     /* 2038 */
+};
+
+const char month_snames[12][4] = {
+    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+void gm_timestr_822(char *ts, time_t sec)
+{
+    static const char *const days[7]=
+       {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+    struct tm *tms;
+    tms = gmtime(&sec);
+    sprintf(ts, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", days[tms->tm_wday],
+            tms->tm_mday, month_snames[tms->tm_mon], tms->tm_year + 1900,
+            tms->tm_hour, tms->tm_min, tms->tm_sec);
+}
+
+void gm_timestr_850(char *ts, time_t sec)
+{
+    static const char *const days[7]=
+ {"Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
+    struct tm *tms;
+    int year;
+    tms = gmtime(&sec);
+
+    year = tms->tm_year;
+    if (year >= 100) year -= 100;
+    sprintf(ts, "%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT", days[tms->tm_wday],
+            tms->tm_mday, month_snames[tms->tm_mon], year,
+            tms->tm_hour, tms->tm_min, tms->tm_sec);
+}
+
+void gm_timestr_ccc(char *ts, time_t sec)
+{
+    static const char *const days[7]=
+       {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+    struct tm *tms;
+    tms = gmtime(&sec);
+    sprintf(ts, "%s %s %2d %.2d:%.2d:%.2d %d", days[tms->tm_wday],
+            month_snames[tms->tm_mon], tms->tm_mday, 
+            tms->tm_hour, tms->tm_min, tms->tm_sec, tms->tm_year + 1900);
+}
+
+int main (void)
+{
+    int year, i;
+    time_t guess;
+    time_t offset = 0;
+ /* time_t offset = 0; */
+ /* time_t offset = ((31 + 28) * 24 * 3600) - 1; */
+    time_t secstodate, newsecs;
+    char datestr[50];
+
+    for (year = 1970; year < 2038; ++year) {
+        secstodate = (time_t)year2secs[year - 1970] + offset;
+        gm_timestr_822(datestr, secstodate);
+        newsecs = parseHTTPdate(datestr);
+        if (secstodate == newsecs)
+            printf("Yes %4d %11ld  %s\n", year, (long)secstodate, datestr);
+        else if (newsecs == BAD_DATE)
+            printf("No  %4d %11ld %11ld %s\n", year, (long)secstodate, 
+                   (long)newsecs, datestr);
+        else
+            printf("No* %4d %11ld %11ld %s\n", year, (long)secstodate, 
+                   (long)newsecs, datestr);
+    }
+    
+    srand48(978245L);
+
+    for (i = 0; i < 10000; ++i) {
+        guess = (time_t)mrand48();
+        if (guess < 0) guess *= -1;
+        secstodate = guess + offset;
+        gm_timestr_822(datestr, secstodate);
+        newsecs = parseHTTPdate(datestr);
+        if (secstodate == newsecs)
+            printf("Yes %11ld  %s\n", (long)secstodate, datestr);
+        else if (newsecs == BAD_DATE)
+            printf("No  %11ld %11ld %s\n", (long)secstodate, 
+                   (long)newsecs, datestr);
+        else
+            printf("No* %11ld %11ld %s\n", (long)secstodate, 
+                   (long)newsecs, datestr);
+    }
+    exit(0);
+}
diff --git a/test/test_find.c b/test/test_find.c
new file mode 100644 (file)
index 0000000..6f887f5
--- /dev/null
@@ -0,0 +1,62 @@
+/* This program tests the ap_find_list_item routine in ../main/util.c.
+ *
+ * The defines in this sample compile line are specific to Roy's system.
+ * They should match whatever was used to compile Apache first.
+ *
+     gcc -g -O2 -I../os/unix -I../include -o test_find \
+            -DSOLARIS2=250 -Wall -DALLOC_DEBUG -DPOOL_DEBUG \
+            ../main/alloc.o ../main/buff.o ../main/util.o \
+            ../ap/libap.a -lsocket -lnsl test_find.c
+ * 
+ * Roy Fielding, 1999
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include "httpd.h"
+#include "alloc.h"
+
+/*
+ * Dummy a bunch of stuff just to get a compile
+ */
+uid_t ap_user_id;
+gid_t ap_group_id;
+void *ap_dummy_mutex = &ap_dummy_mutex;
+char *ap_server_argv0;
+
+API_EXPORT(void) ap_block_alarms(void)
+{
+    ;
+}
+
+API_EXPORT(void) ap_unblock_alarms(void)
+{
+    ;
+}
+
+API_EXPORT(void) ap_log_error(const char *file, int line, int level,
+                              const request_rec *r, const char *fmt, ...)
+{
+    ;
+}
+
+int main (void)
+{
+    ap_pool *p;
+    char line[512];
+    char tok[512];
+
+    p = ap_init_alloc();
+
+    printf("Enter field value to find items within:\n");
+    if (!gets(line))
+        exit(0);
+
+    printf("Enter search item:\n");
+    while (gets(tok)) {
+        printf("  [%s] == %s\n", tok, ap_find_list_item(p, line, tok)
+                                  ? "Yes" : "No");
+        printf("Enter search item:\n");
+    }
+    
+    exit(0);
+}
diff --git a/test/test_limits.c b/test/test_limits.c
new file mode 100644 (file)
index 0000000..8d76ce2
--- /dev/null
@@ -0,0 +1,200 @@
+/**************************************************************
+ * test_limits.c
+ *
+ * A simple program for sending abusive requests to a server, based
+ * on the sioux.c exploit code that this nimrod posted (see below).
+ * Roy added options for testing long header fieldsize (-t h), long
+ * request-lines (-t r), and a long request body (-t b).
+ *
+ * FreeBSD 2.2.x, FreeBSD 3.0, IRIX 5.3, IRIX 6.2:
+ *   gcc -o test_limits test_limits.c
+ * 
+ * Solaris 2.5.1:
+ *   gcc -o test_limits test_limits.c -lsocket -lnsl
+ * 
+ *
+ * Message-ID: <861zqspvtw.fsf@niobe.ewox.org>
+ * Date:       Fri, 7 Aug 1998 19:04:27 +0200
+ * Sender: Bugtraq List <BUGTRAQ@netspace.org>
+ * From: Dag-Erling Coidan =?ISO-8859-1?Q?Sm=F8rgrav?= <finrod@EWOX.ORG>
+ * Subject:      YA Apache DoS attack
+ * 
+ * Copyright (c) 1998 Dag-Erling Codan Smrgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Kudos to Mark Huizer who originally suggested this on freebsd-current
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define TEST_LONG_REQUEST_LINE      1
+#define TEST_LONG_REQUEST_FIELDS    2
+#define TEST_LONG_REQUEST_FIELDSIZE 3
+#define TEST_LONG_REQUEST_BODY      4
+
+void
+usage(void)
+{
+    fprintf(stderr,
+      "usage: test_limits [-t (r|n|h|b)] [-a address] [-p port] [-n num]\n");
+    exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+    struct sockaddr_in sin;
+    struct hostent *he;
+    FILE *f;
+    int o, sd;
+
+    /* default parameters */
+    char *addr = "localhost";
+    int port = 80;
+    int num = 1000;
+    int testtype = TEST_LONG_REQUEST_FIELDS;
+
+    /* get options */
+    while ((o = getopt(argc, argv, "t:a:p:n:")) != EOF)
+        switch (o) {
+        case 't':
+            if (*optarg == 'r')
+                testtype = TEST_LONG_REQUEST_LINE;
+            else if (*optarg == 'n')
+                testtype = TEST_LONG_REQUEST_FIELDS;
+            else if (*optarg == 'h')
+                testtype = TEST_LONG_REQUEST_FIELDSIZE;
+            else if (*optarg == 'b')
+                testtype = TEST_LONG_REQUEST_BODY;
+            break;
+        case 'a':
+            addr = optarg;
+            break;
+        case 'p':
+            port = atoi(optarg);
+            break;
+        case 'n':
+            num = atoi(optarg);
+            break;
+        default:
+            usage();
+        }
+
+    if (argc != optind)
+        usage();
+
+    /* connect */
+    if ((he = gethostbyname(addr)) == NULL) {
+        perror("gethostbyname");
+        exit(1);
+    }
+    bzero(&sin, sizeof(sin));
+    bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length);
+    sin.sin_family = he->h_addrtype;
+    sin.sin_port = htons(port);
+
+    if ((sd = socket(sin.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+        perror("socket");
+        exit(1);
+    }
+
+    if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+        perror("connect");
+        exit(1);
+    }
+
+    if ((f = fdopen(sd, "r+")) == NULL) {
+        perror("fdopen");
+        exit(1);
+    }
+
+    /* attack! */
+    fprintf(stderr, "Testing like a plague of locusts on %s\n", addr);
+
+    if (testtype == TEST_LONG_REQUEST_LINE) {
+        fprintf(f, "GET ");
+        while (num-- && !ferror(f)) {
+            fprintf(f, "/123456789");
+            fflush(f);
+        }
+        fprintf(f, " HTTP/1.0\r\n\r\n");
+    }
+    else {
+        fprintf(f, "GET /fred/foo HTTP/1.0\r\n");
+
+        if (testtype == TEST_LONG_REQUEST_FIELDSIZE) {
+            while (num-- && !ferror(f)) {
+                fprintf(f, "User-Agent: sioux");
+                fflush(f);
+            }
+            fprintf(f, "\r\n");
+        }
+        else if (testtype == TEST_LONG_REQUEST_FIELDS) {
+            while (num-- && !ferror(f))
+                fprintf(f, "User-Agent: sioux\r\n");
+            fprintf(f, "\r\n");
+        }
+        else if (testtype == TEST_LONG_REQUEST_BODY) {
+            fprintf(f, "User-Agent: sioux\r\n");
+            fprintf(f, "Content-Length: 33554433\r\n");
+            fprintf(f, "\r\n");
+            while (num-- && !ferror(f))
+                fprintf(f, "User-Agent: sioux\r\n");
+        }
+        else {
+            fprintf(f, "\r\n");
+        }
+    }
+    fflush(f);
+
+    {
+        ssize_t len;
+        char buff[512];
+
+        while ((len = read(sd, buff, 512)) > 0)
+            len = write(1, buff, len);
+    }
+    if (ferror(f)) {
+        perror("fprintf");
+        exit(1);
+    }
+
+    fclose(f);
+    exit(0);
+}
diff --git a/test/test_parser.c b/test/test_parser.c
new file mode 100644 (file)
index 0000000..23e2c21
--- /dev/null
@@ -0,0 +1,59 @@
+/* This program tests the ap_get_list_item routine in ../main/util.c.
+ *
+ * The defines in this sample compile line are specific to Roy's system.
+ * They should match whatever was used to compile Apache first.
+ *
+     gcc -g -O2 -I../os/unix -I../include -o test_parser \
+            -DSOLARIS2=250 -Wall -DALLOC_DEBUG -DPOOL_DEBUG \
+            ../main/alloc.o ../main/buff.o ../main/util.o \
+            ../ap/libap.a -lsocket -lnsl test_parser.c
+ * 
+ * Roy Fielding, 1999
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include "httpd.h"
+#include "alloc.h"
+
+/*
+ * Dummy a bunch of stuff just to get a compile
+ */
+uid_t ap_user_id;
+gid_t ap_group_id;
+void *ap_dummy_mutex = &ap_dummy_mutex;
+char *ap_server_argv0;
+
+API_EXPORT(void) ap_block_alarms(void)
+{
+    ;
+}
+
+API_EXPORT(void) ap_unblock_alarms(void)
+{
+    ;
+}
+
+API_EXPORT(void) ap_log_error(const char *file, int line, int level,
+                               const request_rec *r, const char *fmt, ...)
+{
+    ;
+}
+
+int main (void)
+{
+    ap_pool *p;
+    const char *field;
+    char *newstr;
+    char instr[512];
+
+    p = ap_init_alloc();
+
+    while (gets(instr)) {
+        printf("  [%s] ==\n", instr);
+        field = instr;
+        while ((newstr = ap_get_list_item(p, &field)) != NULL)
+            printf("  <%s> ..\n", newstr);
+    }
+    
+    exit(0);
+}
diff --git a/test/test_select.c b/test/test_select.c
new file mode 100644 (file)
index 0000000..8c0a6a4
--- /dev/null
@@ -0,0 +1,30 @@
+/* This is just a quick test program to see how long a wait is
+ * produced by a select loop with an exponential backoff.
+ *
+ *   gcc -g -O2 -o test_select test_select.c
+ *   test_select
+ *
+ * Roy Fielding, 1996
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+int main (void)
+{
+    int srv;
+    long waittime = 4096;
+    struct timeval tv;
+
+    printf("Start\n");
+    while ((waittime > 0) && (waittime < 3000000)) {
+        printf("%d\n", waittime);
+        tv.tv_sec  = waittime/1000000;
+        tv.tv_usec = waittime%1000000;
+        waittime <<= 1;
+        srv = select(0, NULL, NULL, NULL, &tv);
+    }
+    printf("End\n");
+    exit(0);
+}
diff --git a/test/time-sem.c b/test/time-sem.c
new file mode 100644 (file)
index 0000000..8bf67c7
--- /dev/null
@@ -0,0 +1,572 @@
+/*
+time-sem.c has the basics of the semaphores we use in http_main.c.  It's
+intended for timing differences between various methods on an
+architecture.  In practice we've found many things affect which semaphore
+to be used:
+
+    - NFS filesystems absolutely suck for fcntl() and flock()
+
+    - uslock absolutely sucks on single-processor IRIX boxes, but
+       absolutely rocks on multi-processor boxes.  The converse
+       is true for fcntl.  sysvsem seems a moderate balance.
+
+    - Under Solaris you can't have too many processes use SEM_UNDO, there
+       might be a tuneable somewhere that increases the limit from 29.
+       We're not sure what the tunable is, so there's a define
+       NO_SEM_UNDO which can be used to simulate us trapping/blocking
+       signals to be able to properly release the semaphore on a clean
+       child death.  You'll also need to define NEED_UNION_SEMUN
+       under solaris.
+
+You'll need to define USE_SHMGET_SCOREBOARD if anonymous shared mmap()
+doesn't work on your system (i.e. linux).
+
+argv[1] is the #children, argv[2] is the #iterations per child
+
+You should run each over many different #children inputs, and choose
+#iter such that the program runs for at least a second or so... or even
+longer depending on your patience.
+
+compile with:
+
+gcc -o time-FCNTL -Wall -O time-sem.c -DUSE_FCNTL_SERIALIZED_ACCEPT
+gcc -o time-FLOCK -Wall -O time-sem.c -DUSE_FLOCK_SERIALIZED_ACCEPT
+gcc -o time-SYSVSEM -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT
+gcc -o time-SYSVSEM2 -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT -DNO_SEM_UNDO
+gcc -o time-PTHREAD -Wall -O time-sem.c -DUSE_PTHREAD_SERIALIZED_ACCEPT -lpthread
+gcc -o time-USLOCK -Wall -O time-sem.c -DUSE_USLOCK_SERIALIZED_ACCEPT
+
+not all versions work on all systems.
+*/
+
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <signal.h>
+
+#if defined(USE_FCNTL_SERIALIZED_ACCEPT)
+
+static struct flock lock_it;
+static struct flock unlock_it;
+
+static int fcntl_fd=-1;
+
+#define accept_mutex_child_init()
+#define accept_mutex_cleanup()
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+void
+accept_mutex_init(void)
+{
+
+    lock_it.l_whence = SEEK_SET;   /* from current point */
+    lock_it.l_start  = 0;          /* -"- */
+    lock_it.l_len    = 0;          /* until end of file */
+    lock_it.l_type   = F_WRLCK;    /* set exclusive/write lock */
+    lock_it.l_pid    = 0;          /* pid not actually interesting */
+    unlock_it.l_whence = SEEK_SET; /* from current point */
+    unlock_it.l_start  = 0;        /* -"- */
+    unlock_it.l_len    = 0;        /* until end of file */
+    unlock_it.l_type   = F_UNLCK;  /* set exclusive/write lock */
+    unlock_it.l_pid    = 0;        /* pid not actually interesting */
+
+    printf("opening test-lock-thing in current directory\n");
+    fcntl_fd = open("test-lock-thing", O_CREAT | O_WRONLY | O_EXCL, 0644);
+    if (fcntl_fd == -1)
+    {
+       perror ("open");
+       fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing");
+       exit (1);
+    }
+    unlink("test-lock-thing");
+}
+
+void accept_mutex_on(void)
+{
+    int ret;
+    
+    while ((ret = fcntl(fcntl_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR)
+       continue;
+
+    if (ret < 0) {
+       perror ("fcntl lock_it");
+       exit(1);
+    }
+}
+
+void accept_mutex_off(void)
+{
+    if (fcntl (fcntl_fd, F_SETLKW, &unlock_it) < 0)
+    {
+       perror ("fcntl unlock_it");
+       exit(1);
+    }
+}
+
+#elif defined(USE_FLOCK_SERIALIZED_ACCEPT)
+
+#include <sys/file.h>
+
+static int flock_fd=-1;
+
+#define FNAME "test-lock-thing"
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+void accept_mutex_init(void)
+{
+
+    printf("opening " FNAME " in current directory\n");
+    flock_fd = open(FNAME, O_CREAT | O_WRONLY | O_EXCL, 0644);
+    if (flock_fd == -1)
+    {
+       perror ("open");
+       fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing");
+       exit (1);
+    }
+}
+
+void accept_mutex_child_init(void)
+{
+    flock_fd = open(FNAME, O_WRONLY, 0600);
+    if (flock_fd == -1) {
+       perror("open");
+       exit(1);
+    }
+}
+
+void accept_mutex_cleanup(void)
+{
+    unlink(FNAME);
+}
+
+void accept_mutex_on(void)
+{
+    int ret;
+    
+    while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR)
+       continue;
+
+    if (ret < 0) {
+       perror ("flock(LOCK_EX)");
+       exit(1);
+    }
+}
+
+void accept_mutex_off(void)
+{
+    if (flock (flock_fd, LOCK_UN) < 0)
+    {
+       perror ("flock(LOCK_UN)");
+       exit(1);
+    }
+}
+
+#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+
+static   int sem_id = -1;
+#ifdef NO_SEM_UNDO
+static sigset_t accept_block_mask;
+static sigset_t accept_previous_mask;
+#endif
+
+#define accept_mutex_child_init()
+#define accept_mutex_cleanup()
+
+void accept_mutex_init(void)
+{
+#ifdef NEED_UNION_SEMUN
+    /* believe it or not, you need to define this under solaris */
+    union semun {
+       int val;
+       struct semid_ds *buf;
+       ushort *array;
+    };
+#endif
+
+    union semun ick;
+
+    sem_id = semget(999, 1, IPC_CREAT | 0666);
+    if (sem_id < 0) {
+       perror ("semget");
+       exit (1);
+    }
+    ick.val = 1;
+    if (semctl(sem_id, 0, SETVAL, ick) < 0) {
+       perror ("semctl");
+        exit(1);
+    }
+#ifdef NO_SEM_UNDO
+    sigfillset(&accept_block_mask);
+    sigdelset(&accept_block_mask, SIGHUP);
+    sigdelset(&accept_block_mask, SIGTERM);
+    sigdelset(&accept_block_mask, SIGUSR1);
+#endif
+}
+
+void accept_mutex_on()
+{
+    struct sembuf op;
+
+#ifdef NO_SEM_UNDO
+    if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
+       perror("sigprocmask(SIG_BLOCK)");
+       exit (1);
+    }
+    op.sem_flg = 0;
+#else
+    op.sem_flg = SEM_UNDO;
+#endif
+    op.sem_num = 0;
+    op.sem_op  = -1;
+    if (semop(sem_id, &op, 1) < 0) {
+       perror ("accept_mutex_on");
+       exit (1);
+    }
+}
+
+void accept_mutex_off()
+{
+    struct sembuf op;
+
+    op.sem_num = 0;
+    op.sem_op  = 1;
+#ifdef NO_SEM_UNDO
+    op.sem_flg = 0;
+#else
+    op.sem_flg = SEM_UNDO;
+#endif
+    if (semop(sem_id, &op, 1) < 0) {
+       perror ("accept_mutex_off");
+        exit (1);
+    }
+#ifdef NO_SEM_UNDO
+    if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
+       perror("sigprocmask(SIG_SETMASK)");
+       exit (1);
+    }
+#endif
+}
+
+#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT)
+
+/* note: pthread mutexes aren't released on child death, hence the
+ * signal goop ... in a real implementation we'd do special things
+ * during hup, term, usr1.
+ */
+
+#include <pthread.h>
+
+static pthread_mutex_t *mutex;
+static sigset_t accept_block_mask;
+static sigset_t accept_previous_mask;
+
+#define accept_mutex_child_init()
+#define accept_mutex_cleanup()
+
+void accept_mutex_init(void)
+{
+    pthread_mutexattr_t mattr;
+    int fd;
+
+    fd = open ("/dev/zero", O_RDWR);
+    if (fd == -1) {
+       perror ("open(/dev/zero)");
+       exit (1);
+    }
+    mutex = (pthread_mutex_t *)mmap ((caddr_t)0, sizeof (*mutex),
+                   PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+    if (mutex == (void *)(caddr_t)-1) {
+       perror ("mmap");
+       exit (1);
+    }
+    close (fd);
+    if (pthread_mutexattr_init(&mattr)) {
+       perror ("pthread_mutexattr_init");
+       exit (1);
+    }
+    if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED)) {
+       perror ("pthread_mutexattr_setpshared");
+       exit (1);
+    }
+    if (pthread_mutex_init(mutex, &mattr)) {
+       perror ("pthread_mutex_init");
+       exit (1);
+    }
+    sigfillset(&accept_block_mask);
+    sigdelset(&accept_block_mask, SIGHUP);
+    sigdelset(&accept_block_mask, SIGTERM);
+    sigdelset(&accept_block_mask, SIGUSR1);
+}
+
+void accept_mutex_on()
+{
+    if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
+       perror("sigprocmask(SIG_BLOCK)");
+       exit (1);
+    }
+    if (pthread_mutex_lock (mutex)) {
+       perror ("pthread_mutex_lock");
+       exit (1);
+    }
+}
+
+void accept_mutex_off()
+{
+    if (pthread_mutex_unlock (mutex)) {
+       perror ("pthread_mutex_unlock");
+       exit (1);
+    }
+    if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
+       perror("sigprocmask(SIG_SETMASK)");
+       exit (1);
+    }
+}
+
+#elif defined (USE_USLOCK_SERIALIZED_ACCEPT)
+
+#include <ulocks.h>
+
+static usptr_t *us = NULL;
+static ulock_t uslock = NULL;
+
+#define accept_mutex_child_init()
+#define accept_mutex_cleanup()
+
+void accept_mutex_init(void)
+{
+    ptrdiff_t old;
+    /* default is 8 */
+#define CONF_INITUSERS_MAX 15
+    if ((old = usconfig(CONF_INITUSERS, CONF_INITUSERS_MAX)) == -1) {
+        perror("usconfig");
+        exit(-1);
+    }
+    if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) {
+        perror("usconfig");
+        exit(-1);
+    }
+    if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) {
+        perror("usconfig");
+        exit(-1);
+    }
+    if ((us = usinit("/dev/zero")) == NULL) {
+        perror("usinit");
+        exit(-1);
+    }
+    if ((uslock = usnewlock(us)) == NULL) {
+        perror("usnewlock");
+        exit(-1);
+    }
+}
+void accept_mutex_on()
+{
+    switch(ussetlock(uslock)) {
+        case 1:
+            /* got lock */
+            break;
+        case 0:
+            fprintf(stderr, "didn't get lock\n");
+            exit(-1);
+        case -1:
+            perror("ussetlock");
+            exit(-1);
+    }
+}
+void accept_mutex_off()
+{
+    if (usunsetlock(uslock) == -1) {
+        perror("usunsetlock");
+        exit(-1);
+    }
+}
+#endif
+
+
+#ifndef USE_SHMGET_SCOREBOARD
+static void *get_shared_mem(size_t size)
+{
+    void *result;
+
+    /* allocate shared memory for the shared_counter */
+    result = (unsigned long *)mmap ((caddr_t)0, size,
+                   PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
+    if (result == (void *)(caddr_t)-1) {
+       perror ("mmap");
+       exit (1);
+    }
+    return result;
+}
+#else
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+static void *get_shared_mem(size_t size)
+{
+    key_t shmkey = IPC_PRIVATE;
+    int shmid = -1;
+    void *result;
+#ifdef MOVEBREAK
+    char *obrk;
+#endif
+
+    if ((shmid = shmget(shmkey, size, IPC_CREAT | SHM_R | SHM_W)) == -1) {
+       perror("shmget");
+       exit(1);
+    }
+
+#ifdef MOVEBREAK
+    /*
+     * Some SysV systems place the shared segment WAY too close
+     * to the dynamic memory break point (sbrk(0)). This severely
+     * limits the use of malloc/sbrk in the program since sbrk will
+     * refuse to move past that point.
+     *
+     * To get around this, we move the break point "way up there",
+     * attach the segment and then move break back down. Ugly
+     */
+    if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) {
+       perror("sbrk");
+    }
+#endif
+
+#define BADSHMAT       ((void *)(-1))
+    if ((result = shmat(shmid, 0, 0)) == BADSHMAT) {
+       perror("shmat");
+    }
+    /*
+     * We must avoid leaving segments in the kernel's
+     * (small) tables.
+     */
+    if (shmctl(shmid, IPC_RMID, NULL) != 0) {
+       perror("shmctl(IPC_RMID)");
+    }
+    if (result == BADSHMAT)    /* now bailout */
+       exit(1);
+
+#ifdef MOVEBREAK
+    if (obrk == (char *) -1)
+       return;                 /* nothing else to do */
+    if (sbrk(-(MOVEBREAK)) == (char *) -1) {
+       perror("sbrk 2");
+    }
+#endif
+    return result;
+}
+#endif
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+/* don't ask */
+#define _P __P
+#include <sched.h>
+#define YIELD  sched_yield()
+#else
+#define YIELD  do { struct timeval zero; zero.tv_sec = zero.tv_usec = 0; select(0,0,0,0,&zero); } while(0)
+#endif
+
+void main (int argc, char **argv)
+{
+    int num_iter;
+    int num_child;
+    int i;
+    struct timeval first;
+    struct timeval last;
+    long ms;
+    int pid;
+    unsigned long *shared_counter;
+
+    if (argc != 3) {
+       fprintf (stderr, "Usage: time-sem num-child num-iter\n");
+       exit (1);
+    }
+
+    num_child = atoi (argv[1]);
+    num_iter = atoi (argv[2]);
+
+    /* allocate shared memory for the shared_counter */
+    shared_counter = get_shared_mem(sizeof(*shared_counter));
+
+    /* initialize counter to 0 */
+    *shared_counter = 0;
+
+    accept_mutex_init ();
+
+    /* parent grabs mutex until done spawning children */
+    accept_mutex_on ();
+
+    for (i = 0; i < num_child; ++i) {
+       pid = fork();
+       if (pid == 0) {
+           /* child, do our thing */
+           accept_mutex_child_init();
+           for (i = 0; i < num_iter; ++i) {
+               unsigned long tmp;
+
+               accept_mutex_on ();
+               tmp = *shared_counter;
+               YIELD;
+               *shared_counter = tmp + 1;
+               accept_mutex_off ();
+           }
+           exit (0);
+       } else if (pid == -1) {
+           perror ("fork");
+           exit (1);
+       }
+    }
+
+    /* a quick test to see that nothing is screwed up */
+    if (*shared_counter != 0) {
+       puts ("WTF! shared_counter != 0 before the children have been started!");
+       exit (1);
+    }
+
+    gettimeofday (&first, NULL);
+    /* launch children into action */
+    accept_mutex_off ();
+    for (i = 0; i < num_child; ++i) {
+       if (wait(NULL) == -1) {
+           perror ("wait");
+       }
+    }
+    gettimeofday (&last, NULL);
+
+    if (*shared_counter != num_child * num_iter) {
+       printf ("WTF! shared_counter != num_child * num_iter!\n"
+               "shared_counter = %lu\nnum_child = %d\nnum_iter=%d\n",
+               *shared_counter,
+               num_child, num_iter);
+    }
+
+    last.tv_sec -= first.tv_sec;
+    ms = last.tv_usec - first.tv_usec;
+    if (ms < 0) {
+       --last.tv_sec;
+       ms += 1000000;
+    }
+    last.tv_usec = ms;
+    printf ("%8lu.%06lu\n", last.tv_sec, last.tv_usec);
+
+    accept_mutex_cleanup();
+
+    exit(0);
+}
+
diff --git a/test/zb.c b/test/zb.c
new file mode 100644 (file)
index 0000000..0a6666e
--- /dev/null
+++ b/test/zb.c
@@ -0,0 +1,567 @@
+
+/*                          ZeusBench V1.01
+                           ===============
+
+This program is Copyright (C) Zeus Technology Limited 1996.
+
+This program may be used and copied freely providing this copyright notice
+is not removed.
+
+This software is provided "as is" and any express or implied waranties, 
+including but not limited to, the implied warranties of merchantability and
+fitness for a particular purpose are disclaimed.  In no event shall 
+Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, 
+exemplary, or consequential damaged (including, but not limited to, 
+procurement of substitute good or services; loss of use, data, or profits;
+or business interruption) however caused and on theory of liability.  Whether
+in contract, strict liability or tort (including negligence or otherwise) 
+arising in any way out of the use of this software, even if advised of the
+possibility of such damage.
+
+     Written by Adam Twiss (adam@zeus.co.uk).  March 1996
+
+Thanks to the following people for their input:
+  Mike Belshe (mbelshe@netscape.com) 
+  Michael Campanella (campanella@stevms.enet.dec.com)
+
+*/
+
+/* -------------------- Notes on compiling ------------------------------
+
+This should compile unmodified using gcc on HP-UX, FreeBSD, Linux,
+IRIX, Solaris, AIX and Digital Unix (OSF).  On Solaris 2.x you will
+need to compile with "-lnsl -lsocket" options.  If you have any
+difficulties compiling then let me know.
+
+On SunOS 4.x.x you may need to compile with -DSUNOS4 to add the following 
+two lines of code which appear not to exist in my SunOS headers */
+
+#ifdef SUNOS4
+extern char *optarg; 
+extern int optind, opterr, optopt;   
+#endif
+
+/*  -------------------------------------------------------------------- */
+
+/* affects include files on Solaris */
+#define BSD_COMP
+
+#include <sys/time.h> 
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h> 
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+/* ------------------- DEFINITIONS -------------------------- */
+
+/* maximum number of requests on a time limited test */
+#define MAX_REQUESTS 50000 
+
+/* good old state machine */
+#define STATE_UNCONNECTED 0
+#define STATE_CONNECTING  1
+#define STATE_READ        2
+
+#define CBUFFSIZE       512
+
+struct connection 
+{
+  int fd;
+  int state;
+  int read;       /* amount of bytes read */
+  int bread;      /* amount of body read */
+  int length;     /* Content-Length value used for keep-alive */
+  char cbuff[CBUFFSIZE];  /* a buffer to store server response header */
+  int cbx;                /* offset in cbuffer */
+  int keepalive;          /* non-zero if a keep-alive request */
+  int gotheader;          /* non-zero if we have the entire header in cbuff */
+  struct timeval start, connect, done; 
+};
+
+struct data 
+{
+  int read;      /* number of bytes read */
+  int ctime;     /* time in ms to connect */
+  int time;      /* time in ms for connection */
+};
+
+#define min(a,b) ((a)<(b))?(a):(b)
+#define max(a,b) ((a)>(b))?(a):(b)
+
+/* --------------------- GLOBALS ---------------------------- */
+
+int requests = 1;      /* Number of requests to make */
+int concurrency = 1;   /* Number of multiple requests to make */
+int tlimit = 0;        /* time limit in cs */
+int keepalive = 0;     /* try and do keepalive connections */
+char *machine;         /* Machine name */
+char *file;            /* file name to use */
+char server_name[80];  /* name that server reports */
+int port = 80;         /* port to use */
+
+int doclen = 0;        /* the length the document should be */
+int totalread = 0;     /* total number of bytes read */
+int totalbread = 0;    /* totoal amount of entity body read */
+int done=0;            /* number of requests we have done */
+int doneka=0;          /* number of keep alive connections done */
+int good=0, bad=0;     /* number of good and bad requests */
+
+/* store error cases */
+int err_length = 0, err_conn = 0, err_except = 0;
+
+struct timeval start, endtime;
+
+/* global request (and its length) */
+char request[512];
+int reqlen;
+
+/* one global throw-away buffer to read stuff into */
+char buffer[4096];
+
+struct connection *con;      /* connection array */
+struct data *stats;          /* date for each request */
+
+fd_set readbits, writebits;  /* bits for select */
+struct sockaddr_in server;   /* server addr structure */
+
+/* --------------------------------------------------------- */
+
+/* simple little function to perror and exit */
+
+static void err(char *s) 
+{
+  perror(s);
+  exit(errno);
+}
+
+/* --------------------------------------------------------- */
+
+/* write out request to a connection - assumes we can write 
+   (small) request out in one go into our new socket buffer  */
+
+void write_request(struct connection *c)
+{
+  gettimeofday(&c->connect,0);
+  write(c->fd,request, reqlen);  
+  c->state = STATE_READ;
+  FD_SET(c->fd, &readbits);
+  FD_CLR(c->fd, &writebits);
+}
+
+/* --------------------------------------------------------- */
+
+/* make an fd non blocking */
+
+void nonblock(int fd) 
+{
+  int i=1;
+  ioctl(fd, FIONBIO, &i);
+}
+
+/* --------------------------------------------------------- */
+
+/* returns the time in ms between two timevals */
+
+int timedif(struct timeval a, struct timeval b)
+{
+  register int us,s;
+
+  us = a.tv_usec - b.tv_usec;
+  us /= 1000;
+  s = a.tv_sec - b.tv_sec;
+  s *= 1000;
+  return s+us;
+}
+
+/* --------------------------------------------------------- */
+
+/* calculate and output results and exit */
+
+void output_results()
+{
+  int timetaken;
+  
+  gettimeofday(&endtime,0);
+  timetaken = timedif(endtime, start);
+  
+  printf("\n---\n");
+  printf("Server:                 %s\n", server_name);
+  printf("Document Length:        %d\n", doclen);  
+  printf("Concurency Level:       %d\n", concurrency);
+  printf("Time taken for tests:   %d.%03d seconds\n", 
+        timetaken/1000, timetaken%1000);
+  printf("Complete requests:      %d\n", done);
+  printf("Failed requests:        %d\n", bad);
+  if(bad) printf("   (Connect: %d, Length: %d, Exceptions: %d)\n",
+                err_conn, err_length, err_except);
+  if(keepalive) printf("Keep-Alive requests:    %d\n", doneka);
+  printf("Bytes transfered:       %d\n", totalread);
+  printf("HTML transfered:        %d\n", totalbread);
+  
+  /* avoid divide by zero */
+  if(timetaken) {
+    printf("Requests per seconds:   %.2f\n", 1000*(float)(done)/timetaken);
+    printf("Transfer rate:          %.2f kb/s\n", 
+          (float)(totalread)/timetaken);
+  }
+
+  {
+    /* work out connection times */
+    int i;
+    int totalcon=0, total=0;
+    int mincon=9999999, mintot=999999;
+    int maxcon=0, maxtot=0;
+
+    for(i=0; i<requests; i++) {
+      struct data s = stats[i];
+      mincon = min(mincon, s.ctime);
+      mintot = min(mintot, s.time);
+      maxcon = max(maxcon, s.ctime);
+      maxtot = max(maxtot, s.time);
+      totalcon += s.ctime;
+      total += s.time;
+    }
+    printf("\nConnnection Times (ms)\n");
+    printf("           min   avg   max\n");
+    printf("Connect: %5d %5d %5d\n",mincon, totalcon/requests, maxcon );
+    printf("Total:   %5d %5d %5d\n", mintot, total/requests, maxtot);
+    printf("---\n\n");
+  }
+
+  exit(0);
+}
+
+/* --------------------------------------------------------- */
+
+/* start asnchronous non-blocking connection */
+
+void start_connect(struct connection *c)
+{
+  c->read = 0;
+  c->bread = 0;
+  c->keepalive = 0;
+  c->cbx = 0; 
+  c->gotheader = 0;
+
+  c->fd = socket(AF_INET, SOCK_STREAM, 0);
+  if(c->fd<0) err("socket");
+
+  nonblock(c->fd);
+  gettimeofday(&c->start,0);
+
+  if(connect(c->fd, (struct sockaddr *) &server, sizeof(server))<0) {
+    if(errno==EINPROGRESS) {
+      c->state = STATE_CONNECTING;
+      FD_SET(c->fd, &writebits);
+      return;
+    }
+    else {
+      close(c->fd);
+      err_conn++;
+      if(bad++>10) {
+       printf("\nTest aborted after 10 failures\n\n");
+       exit(1);
+      } 
+      start_connect(c);
+    }      
+  }
+  
+  /* connected first time */
+  write_request(c);
+}
+
+/* --------------------------------------------------------- */
+
+/* close down connection and save stats */
+
+void close_connection(struct connection *c)
+{
+  if(c->read == 0 && c->keepalive) {
+    /* server has legitiamately shut down an idle keep alive request */
+    good--;  /* connection never happend */
+  } 
+  else {
+    if(good==1) {
+      /* first time here */
+      doclen = c->bread;
+    } else if (c->bread!=doclen) { 
+      bad++; 
+      err_length++; 
+    }
+    
+    /* save out time */
+    if(done < requests) {
+      struct data s;
+      gettimeofday(&c->done,0);
+      s.read = c->read;
+      s.ctime = timedif(c->connect, c->start);
+      s.time = timedif(c->done, c->start);
+      stats[done++] = s;
+    }
+  }
+
+  close(c->fd);
+  FD_CLR(c->fd, &readbits);
+  FD_CLR(c->fd, &writebits);
+
+  /* connect again */
+  start_connect(c); 
+  return;
+}
+
+/* --------------------------------------------------------- */
+
+/* read data from connection */
+
+void read_connection(struct connection *c)
+{
+  int r;
+  
+  r=read(c->fd,buffer,sizeof(buffer));
+  if(r==0 || (r<0 && errno!=EAGAIN)) {
+    good++;
+    close_connection(c);
+    return;
+  } 
+  
+  if(r<0 && errno==EAGAIN) return;
+
+  c->read += r;
+  totalread += r;
+  
+  if(!c->gotheader) {
+    char *s;
+    int l=4;
+    int space = CBUFFSIZE - c->cbx - 1; /* -1 to allow for 0 terminator */
+    int tocopy = (space<r)?space:r;
+    memcpy(c->cbuff+c->cbx, buffer, space);
+    c->cbx += tocopy; space -= tocopy;
+    c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */
+    s = strstr(c->cbuff, "\r\n\r\n");
+    /* this next line is so that we talk to NCSA 1.5 which blatantly breaks 
+       the http specifaction */
+    if(!s) { s = strstr(c->cbuff,"\n\n"); l=2; }
+
+    if(!s) {
+       /* read rest next time */
+      if(space) 
+       return;
+      else {
+       /* header is in invalid or too big - close connection */
+       close(c->fd);
+       if(bad++>10) {
+         printf("\nTest aborted after 10 failures\n\n");
+         exit(1);
+       } 
+       FD_CLR(c->fd, &writebits);
+       start_connect(c);
+      }        
+    }
+    else {
+      /* have full header */
+      if(!good) {
+       /* this is first time, extract some interesting info */
+       char *p, *q;
+       p = strstr(c->cbuff, "Server:");
+       q = server_name;
+       if(p) { p+=8; while(*p>32) *q++ = *p++; }
+       *q = 0;
+      }
+       
+      c->gotheader = 1;
+      *s = 0; /* terminate at end of header */
+      if(keepalive && 
+        (strstr(c->cbuff, "Keep-Alive") 
+         || strstr(c->cbuff, "keep-alive")))  /* for benefit of MSIIS */
+       {
+       char *cl;
+       cl = strstr(c->cbuff, "Content-Length:");
+       /* for cacky servers like NCSA which break the spec and send a 
+          lower case 'l' */
+       if(!cl) cl = strstr(c->cbuff, "Content-length:");
+       if(cl) {
+         c->keepalive=1;
+         c->length = atoi(cl+16);
+       }
+      }
+      c->bread += c->cbx - (s+l-c->cbuff) + r-tocopy;
+      totalbread += c->bread;
+    }
+  }  
+  else {
+    /* outside header, everything we have read is entity body */
+    c->bread += r;
+    totalbread += r;
+  }
+
+  if(c->keepalive && (c->bread >= c->length)) {
+    /* finished a keep-alive connection */
+    good++; doneka++;
+    /* save out time */
+    if(good==1) {
+      /* first time here */
+      doclen = c->bread;
+    } else if(c->bread!=doclen) { bad++; err_length++; }
+    if(done < requests) {
+      struct data s;
+      gettimeofday(&c->done,0);
+      s.read = c->read;
+      s.ctime = timedif(c->connect, c->start);
+      s.time = timedif(c->done, c->start);
+      stats[done++] = s;
+    }
+    c->keepalive = 0; c->length = 0; c->gotheader=0; c->cbx = 0;
+    c->read = c->bread = 0;
+    write_request(c);
+    c->start = c->connect; /* zero connect time with keep-alive */
+  }
+}
+
+/* --------------------------------------------------------- */
+
+/* run the tests */
+
+int test() 
+{
+  struct timeval timeout, now;
+  fd_set sel_read, sel_except, sel_write;
+  int i;
+  
+  {
+    /* get server information */
+    struct hostent *he;
+    he = gethostbyname(machine);
+    if (!he) err("gethostbyname");
+    server.sin_family      = he->h_addrtype;
+    server.sin_port        = htons(port);
+    server.sin_addr.s_addr = ((unsigned long *)(he->h_addr_list[0]))[0];
+  }
+
+  con = malloc(concurrency*sizeof(struct connection));
+  memset(con,0,concurrency*sizeof(struct connection));
+  
+  stats = malloc(requests * sizeof(struct data));
+
+  FD_ZERO(&readbits);
+  FD_ZERO(&writebits);
+
+  /* setup request */
+  sprintf(request,"GET %s HTTP/1.0\r\nUser-Agent: ZeusBench/1.0\r\n"
+         "%sHost: %s\r\nAccept: */*\r\n\r\n", file, 
+         keepalive?"Connection: Keep-Alive\r\n":"", machine );
+    
+  reqlen = strlen(request);
+
+  /* ok - lets start */
+  gettimeofday(&start,0);
+
+  /* initialise lots of requests */
+  for(i=0; i<concurrency; i++) start_connect(&con[i]);
+
+  while(done<requests) {
+    int n;
+    /* setup bit arrays */
+    memcpy(&sel_except, &readbits, sizeof(readbits));
+    memcpy(&sel_read, &readbits, sizeof(readbits));
+    memcpy(&sel_write, &writebits, sizeof(readbits));
+
+    /* check for time limit expiry */
+    gettimeofday(&now,0);
+    if(tlimit && timedif(now,start) > (tlimit*1000)) {
+      requests=done;   /* so stats are correct */
+      output_results();
+    }
+
+    /* Timeout of 30 seconds. */
+    timeout.tv_sec=30; timeout.tv_usec=0;
+    n=select(256, &sel_read, &sel_write, &sel_except, &timeout);
+    if(!n) {
+      printf("\nServer timed out\n\n");
+      exit(1);
+    }
+    if(n<1) err("select");
+
+    for(i=0; i<concurrency; i++) {
+      int s = con[i].fd;
+      if(FD_ISSET(s, &sel_except)) {
+       bad++; 
+       err_except++;
+       start_connect(&con[i]);
+       continue;
+      }
+      if(FD_ISSET(s, &sel_read)) read_connection(&con[i]);
+      if(FD_ISSET(s, &sel_write)) write_request(&con[i]);
+    }
+    if(done>=requests) output_results();
+  }
+  return 0;
+}
+
+/* ------------------------------------------------------- */
+
+/* display usage information */
+
+void usage(char *progname) {
+  printf("\nZeusBench v1.0\n\n");
+  printf("Usage: %s <machine> <file> [-k] [-n requests | -t timelimit (sec)]"
+        "\n\t\t[-c concurrency] [-p port] \n",progname);
+  printf("Filename should start with a '/' e.g. /index.html\n\n");
+  exit(EINVAL);
+}
+
+/* ------------------------------------------------------- */
+
+/* sort out command-line args and call test */
+
+int main(int argc, char **argv) {
+  int c;
+  if (argc < 3) usage(argv[0]);
+  
+  machine = argv[1]; 
+  file = argv[2];
+  optind = 3;
+  while ((c = getopt(argc,argv,"p:n:c:d:t:d:k"))>0) {
+    switch(c) {
+    case 'd':
+      break;
+    case 'n': 
+      requests = atoi(optarg);
+      if(!requests) {
+       printf("Invalid number of requests\n");
+       exit(1);
+      }
+      break;
+    case 'k':
+      keepalive=1;
+      break;
+    case 'c':
+      concurrency = atoi(optarg);
+      break;
+    case 'p':
+      port = atoi(optarg);
+      break;
+    case 't':
+      tlimit = atoi(optarg);
+      requests = MAX_REQUESTS;  /* need to size data array on something */
+      break;
+    default:
+      usage(argv[0]);
+      break;
+    }
+  }   
+  test();
+  return 0;
+}
+
+
+
+
+
+