From: (no author) <(no author)@unknown> Date: Wed, 3 Jul 1996 17:19:50 +0000 (+0000) Subject: This commit was manufactured by cvs2svn to create branch X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=422ca60cc06433771f6bfd0b1c410151809ac691;p=thirdparty%2Fapache%2Fhttpd.git This commit was manufactured by cvs2svn to create branch 'RELEASE_1_1_X'. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/1.3@76629 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/RELEASE_1_1_X/CHANGES b/RELEASE_1_1_X/CHANGES new file mode 100644 index 00000000000..a9d5649dac4 --- /dev/null +++ b/RELEASE_1_1_X/CHANGES @@ -0,0 +1,85 @@ +New features with this release, as extensions of the Apache functionality +(see also more detailed CHANGES file) in the source directory. For more +information, see http://www.apache.org/docs/1.1/ + +In addition to a number of bug fixes and internal performance +enhancements, Apache 1.1 has the following specific new user +features: + + *) Caching Proxy Server + Apache can now act as an HTTP proxy server, allowing clients + behind firewalls to use the server to access the outside world. In + addition, it can cache documents it proxies, speeding up access to + frequently requested documents. + + *) Filetype-based Script "Actions" + You can now run CGI scripts whenever a file of a certion type is + requested. Makes it much easier to execute scripts that process + files. + + *) Support for Keep-Alive Persistent Connections + Apache now has (optional) support for persistent connections, as + defined by the HTTP/1.1 draft. This protocol, supported by a + number of current HTTP servers and browsers (including Netscape + Navigator 2.0) has been shown to increase speed of document + transfer by up to 50% in certain cases. + + *) Customizable CGI Environment Variables (not in this release) + New PassEnv and SetEnv directives allow you to modify the + environment variables passed to CGI scripts + + *) CERN Metafile Support + Now emulates the CERN httpd's support for metafiles containing + additional HTTP headers to be supplied with a document. + + *) Redirect Now Usable in .htaccess Files + The Redirect directive can now be used in .htaccess files when the + FileInfo directive has been set on. This allows users to redirect + parts of their directories without requiring CGI scripts + + *) Improved UserDir Directive (not in this release) + Now supports the ability to point user's files (as specificed by + URLs beginning with the "~" character) at directories other than + those specified by the Unix password file. + + *) Minimal DNS Now Runtime Option + New HostnameLookups server configuration directive can be used to + turn On or Off DNS lookups. This supercedes the -DMINIMAL_DNS + compile-time configuration option. + + *) Listen to Multiple Addresses and Ports + Using the new Listen directive, Apache can listen to more than one + port and IP address, using the same configuration set. + + *) Anonymous HTTP Logins + New options allow you to allow, using Basic HTTP Authentication, + anonymous logins, like those of FTP. This allows you to collect + email addresses of people accessing your site. + + *) File Owner Avialable to Included CGI Scripts + Server-side includes that call CGI scripts will now set a + USER_NAME environment variable that contains the owner of the file + which included it. + + *) Improved Icons + Thanks to Kevin Hughes, Apache's nifty color GIF icons for + directory listings have been updated. In addition, the Powered by + Apache (apache_pb.gif) logo has been included. + + *) Log Rotation + New support utility to allow log rotation without shutting down the + server. See support/rotatelogs.c. + +NEW AUTHENTICATION MODULES: + +Note: These modules are not compiled into the server by default, as +they require special support on the host system. They must be enabled +specifically in the Configuration file. + + *) Support for Unix DB Authentication - mod_db.c + In addition to DBM support, Apache now contains optional support + for Berkeley DB databases. + + *) mSQL Database Authentication - mod_auth_msql.html + Support for the use of mSQL databases for user authentication via + HTTP is now supported. diff --git a/RELEASE_1_1_X/LICENSE b/RELEASE_1_1_X/LICENSE new file mode 100644 index 00000000000..825e5c10b2b --- /dev/null +++ b/RELEASE_1_1_X/LICENSE @@ -0,0 +1,56 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + + + diff --git a/RELEASE_1_1_X/README b/RELEASE_1_1_X/README new file mode 100644 index 00000000000..2eb4025df3c --- /dev/null +++ b/RELEASE_1_1_X/README @@ -0,0 +1,76 @@ + Apache + Version 1.1 (and up) + +What is it? +----------- + +Apache is an HTTP server designed as a plug-in replacement for the NCSA +server version 1.3 (or 1.4). It fixes numerous bugs in the NCSA server and +includes many frequently requested new features, and has an API which +allows it to be extended to meet users' needs more easily. + +Documentation +------------- + +All the documentation is on-line on the WWW, via . + +Installation +------------ + +Unless you grabbed a binary distribution of Apache, you must compile +it for your specific platform. In order to compile it, you must set +compile-time options (in particular, system type) for your system by +editing a Configuration file, run a script which generates a Makefile +and a small piece of C code, and then compile it. + +For instructions on compilation, see the file 'INSTALL' in the src/ directory. + +After compilation, you will have a binary called "httpd" in the src/ +directory. If you received a binary distribution of apache, you +should have this file already. + +The next step is to edit the configuration files for the server. In +the subdirectory called "conf" you should find distribution versions +of the three configuration files: srm.conf-dist, access.conf-dist, and +httpd.conf-dist. Copy them to srm.conf, access.conf, httpd.conf +respectively. + +First edit httpd.conf. This sets up general attributes about the +server - the port number, the user it runs as, etc. Next edit the +srm.conf file - this sets up the root of the document tree, special +functions like server-parsed HTML or internal imagemap parsing, etc. +Finally, edit the access.conf file to at least set the base cases of +access. Documentation for all of these is located at +. + +Finally, make a call to httpd, with a -f to the full path to the +httpd.conf file. I.e., the common case: + + /usr/local/etc/apache/src/httpd -f /usr/local/etc/apache/conf/httpd.conf + +And voila! The server should be running. + +By default the srm.conf and access.conf files are located by name - to +specifically call them by other names, use the AccessConfig and +ResourceConfig directives in httpd.conf. + +The Latest Version +------------------ + +Details of the latest version are in the apache project page (above). + +NOTE: At the time of release of Apache 1.1, two modules had updated +versions which were not sufficiently tested for inclusion into the 1.1 +distribution: mod_auth_msql and mod_cern_meta. The newer versions are +available from http://www.apache.org/dist/contrib/modules/ - if you +plan on using this functionality we encourage you to give the newer +modules a test drive, and let us know of any problems. In particular, +mod_cern_meta is now configurable per-directory, and the configuration +directives have changed. We can not promise that the existing +configuration directives provided by the mod_cern_meta included in the +distribution will be supported in the future. + +Licencing +--------- + +Please see the file called LICENSE. diff --git a/RELEASE_1_1_X/RULES.CVS b/RELEASE_1_1_X/RULES.CVS new file mode 100644 index 00000000000..7ecbbffd4c6 --- /dev/null +++ b/RELEASE_1_1_X/RULES.CVS @@ -0,0 +1,13 @@ +1. Don't commit several unrelated changes in one go. Each change should be +committed separately (that is, it can affect multiple files, but it should all +be part of a single change). + +2. When committing, make sure that the CVS log message explains what the +patches do and why. + +3. Don't edit CHANGES files unless there is a change which is visible to a user +of Apache. CVS logs provide a detailed history of all changes (if people follow +rule 2), so there is no need to duplicate it in CHANGES. + +4. Don't change src/Configuration.tmpl to your local configuration. Copy it to +Configuration (which is _not_ version controlled) and change that. diff --git a/RELEASE_1_1_X/cgi-bin/printenv b/RELEASE_1_1_X/cgi-bin/printenv new file mode 100644 index 00000000000..7d389e0ac56 --- /dev/null +++ b/RELEASE_1_1_X/cgi-bin/printenv @@ -0,0 +1,7 @@ +#!/usr/local/bin/perl + +print "Content-type: text/html\n\n"; +while (($key, $val) = each %ENV) { + print "$key = $val
\n"; +} + diff --git a/RELEASE_1_1_X/cgi-bin/test-cgi b/RELEASE_1_1_X/cgi-bin/test-cgi new file mode 100644 index 00000000000..97489710d9b --- /dev/null +++ b/RELEASE_1_1_X/cgi-bin/test-cgi @@ -0,0 +1,28 @@ +#!/bin/sh + +echo Content-type: text/plain +echo + +echo CGI/1.0 test script report: +echo + +echo argc is $#. argv is "$*". +echo + +echo SERVER_SOFTWARE = $SERVER_SOFTWARE +echo SERVER_NAME = $SERVER_NAME +echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE +echo SERVER_PROTOCOL = $SERVER_PROTOCOL +echo SERVER_PORT = $SERVER_PORT +echo REQUEST_METHOD = $REQUEST_METHOD +echo HTTP_ACCEPT = "$HTTP_ACCEPT" +echo PATH_INFO = "$PATH_INFO" +echo PATH_TRANSLATED = "$PATH_TRANSLATED" +echo SCRIPT_NAME = "$SCRIPT_NAME" +echo QUERY_STRING = "$QUERY_STRING" +echo REMOTE_HOST = $REMOTE_HOST +echo REMOTE_ADDR = $REMOTE_ADDR +echo REMOTE_USER = $REMOTE_USER +echo AUTH_TYPE = $AUTH_TYPE +echo CONTENT_TYPE = $CONTENT_TYPE +echo CONTENT_LENGTH = $CONTENT_LENGTH diff --git a/RELEASE_1_1_X/conf/access.conf-dist b/RELEASE_1_1_X/conf/access.conf-dist new file mode 100644 index 00000000000..3947bc1488b --- /dev/null +++ b/RELEASE_1_1_X/conf/access.conf-dist @@ -0,0 +1,59 @@ +# access.conf: Global access configuration +# Online docs at http://www.apache.org/ + +# This file defines server settings which affect which types of services +# are allowed, and in what circumstances. + +# Each directory to which Apache has access, can be configured with respect +# to which services and features are allowed and/or disabled in that +# directory (and its subdirectories). + +# Originally by Rob McCool + +# This should be changed to whatever you set DocumentRoot to. + + + +# This may also be "None", "All", or any combination of "Indexes", +# "Includes", "FollowSymLinks", "ExecCGI", or "MultiViews". + +# Note that "MultiViews" must be named *explicitly* --- "Options All" +# doesn't give it to you (or at least, not yet). + +Options Indexes FollowSymLinks + +# This controls which options the .htaccess files in directories can +# override. Can also be "All", or any combination of "Options", "FileInfo", +# "AuthConfig", and "Limit" + +AllowOverride None + +# Controls who can get stuff from this server. + +order allow,deny +allow from all + + + +# /usr/local/etc/httpd/cgi-bin should be changed to whatever your ScriptAliased +# CGI directory exists, if you have that configured. + + +AllowOverride None +Options None + + +# Allow server status reports, with the URL of http://servername/status +# Change the ".nowhere.com" to match your domain to enable. + +# +#SetHandler server-status + +#order deny,allow +#deny from all +#allow from .nowhere.com +# + +# You may place any other directories or locations you wish to have +# access information for after this one. + diff --git a/RELEASE_1_1_X/conf/httpd.conf-dist b/RELEASE_1_1_X/conf/httpd.conf-dist new file mode 100644 index 00000000000..7fd7de94696 --- /dev/null +++ b/RELEASE_1_1_X/conf/httpd.conf-dist @@ -0,0 +1,168 @@ +# This is the main server configuration file. See URL http://www.apache.org/ +# for instructions. + +# Do NOT simply read the instructions in here without understanding +# what they do, if you are unsure consult the online docs. You have been +# warned. + +# Originally by Rob McCool + +# ServerType is either inetd, or standalone. + +ServerType standalone + +# If you are running from inetd, go to "ServerAdmin". + +# Port: The port the standalone listens to. For ports < 1023, you will +# need httpd to be run as root initially. + +Port 80 + +# HostnameLookups: Log the names of clients or just their IP numbers +# e.g. www.apache.org (on) or 204.62.129.132 (off) +HostnameLookups on + +# If you wish httpd to run as a different user or group, you must run +# httpd as root initially and it will switch. + +# User/Group: The name (or #number) of the user/group to run httpd as. +# On SCO (ODT 3) use User nouser and Group nogroup +User nobody +Group #-1 + +# ServerAdmin: Your address, where problems with the server should be +# e-mailed. + +ServerAdmin you@your.address + +# ServerRoot: The directory the server's config, error, and log files +# are kept in + +ServerRoot /usr/local/etc/httpd + +# BindAddress: You can support virtual hosts with this option. This option +# is used to tell the server which IP address to listen to. It can either +# contain "*", an IP address, or a fully qualified Internet domain name. +# See also the VirtualHost directive. + +#BindAddress * + +# ErrorLog: The location of the error log file. If this does not start +# with /, ServerRoot is prepended to it. + +ErrorLog logs/error_log + +# TransferLog: The location of the transfer log file. If this does not +# start with /, ServerRoot is prepended to it. + +TransferLog logs/access_log + +# PidFile: The file the server should log its pid to +PidFile logs/httpd.pid + +# ScoreBoardFile: File used to store internal server process information +ScoreBoardFile logs/apache_status + +# ServerName allows you to set a host name which is sent back to clients for +# your server if it's different than the one the program would get (i.e. use +# "www" instead of the host's real name). +# +# Note: You cannot just invent host names and hope they work. The name you +# define here must be a valid DNS name for your host. If you don't understand +# this, ask your network administrator. + +#ServerName new.host.name + +# CacheNegotiatedDocs: By default, Apache sends Pragma: no-cache with each +# document that was negotiated on the basis of content. This asks proxy +# servers not to cache the document. Uncommenting the following line disables +# this behavior, and proxies will be allowed to cache the documents. + +#CacheNegotiatedDocs + +# Timeout: The number of seconds before receives and sends time out +# n.b. the compiled default is 1200 (20 minutes !) + +Timeout 400 + +# KeepAlive: The number of Keep-Alive persistent requests to accept +# per connection. Set to 0 to deactivate Keep-Alive support + +KeepAlive 5 + +# KeepAliveTimeout: Number of seconds to wait for the next request + +KeepAliveTimeout 15 + +# Server-pool size regulation. Rather than making you guess how many +# server processes you need, Apache dynamically adapts to the load it +# sees --- that is, it tries to maintain enough server processes to +# handle the current load, plus a few spare servers to handle transient +# load spikes (e.g., multiple simultaneous requests from a single +# Netscape browser). + +# It does this by periodically checking how many servers are waiting +# for a request. If there are fewer than MinSpareServers, it creates +# a new spare. If there are more than MaxSpareServers, some of the +# spares die off. These values are probably OK for most sites --- + +MinSpareServers 5 +MaxSpareServers 10 + +# Number of servers to start --- should be a reasonable ballpark figure. + +StartServers 5 + +# Limit on total number of servers running, i.e., limit on the number +# of clients who can simultaneously connect --- if this limit is ever +# reached, clients will be LOCKED OUT, so it should NOT BE SET TOO LOW. +# It is intended mainly as a brake to keep a runaway server from taking +# Unix with it as it spirals down... + +MaxClients 150 + +# MaxRequestsPerChild: the number of requests each child process is +# allowed to process before the child dies. +# The child will exit so as to avoid problems after prolonged use when +# Apache (and maybe the libraries it uses) leak. On most systems, this +# isn't really needed, but a few (such as Solaris) do have notable leaks +# in the libraries. + +MaxRequestsPerChild 30 + +# Proxy Server directives. Uncomment the following line to +# enable the proxy server: + +#ProxyRequests On + +# To enable the cache as well, edit and uncomment the following lines: + +#CacheRoot /usr/local/etc/httpd/proxy +#CacheSize 5 +#CacheGcInterval 4 +#CacheMaxExpire 24 +#CacheLastModifiedFactor 0.1 +#CacheDefaultExpire 1 +#NoCache adomain.com anotherdomain.edu joes.garage.com + +# Listen: Allows you to bind Apache to specific IP addresses and/or +# ports, in addition to the default. See also the VirtualHost command + +#Listen 3000 +#Listen 12.34.56.78:80 + +# VirtualHost: Allows the daemon to respond to requests for more than one +# server address, if your server machine is configured to accept IP packets +# for multiple addresses. This can be accomplished with the ifconfig +# alias flag, or through kernel patches like VIF. + +# Any httpd.conf or srm.conf directive may go into a VirtualHost command. +# See alto the BindAddress entry. + +# +#ServerAdmin webmaster@host.foo.com +#DocumentRoot /www/docs/host.foo.com +#ServerName host.foo.com +#ErrorLog logs/host.foo.com-error_log +#TransferLog logs/host.foo.com-access_log +# diff --git a/RELEASE_1_1_X/conf/mime.types b/RELEASE_1_1_X/conf/mime.types new file mode 100644 index 00000000000..8853c1f965a --- /dev/null +++ b/RELEASE_1_1_X/conf/mime.types @@ -0,0 +1,99 @@ +# This is a comment. I love comments. + + +application/activemessage +application/andrew-inset +application/applefile +application/atomicmail +application/dca-rft +application/dec-dx +application/mac-binhex40 hqx +application/mac-compactpro cpt +application/macwriteii +application/msword doc +application/news-message-id +application/news-transmission +application/octet-stream bin dms lha lzh exe class +application/oda oda +application/pdf pdf +application/postscript ai eps ps +application/powerpoint ppt +application/remote-printing +application/rtf rtf +application/slate +application/wita +application/wordperfect5.1 +application/x-bcpio bcpio +application/x-cdlink vcd +application/x-compress Z +application/x-cpio cpio +application/x-csh csh +application/x-director dcr dir dxr +application/x-dvi dvi +application/x-gtar gtar +application/x-gzip gz +application/x-hdf hdf +application/x-httpd-cgi cgi +application/x-koan skp skd skt skm +application/x-latex latex +application/x-mif mif +application/x-netcdf nc cdf +application/x-sh sh +application/x-shar shar +application/x-stuffit sit +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-tar tar +application/x-tcl tcl +application/x-tex tex +application/x-texinfo texinfo texi +application/x-troff t tr roff +application/x-troff-man man +application/x-troff-me me +application/x-troff-ms ms +application/x-ustar ustar +application/x-wais-source src +application/zip zip +audio/basic au snd +audio/mpeg mpga mp2 +audio/x-aiff aif aiff aifc +audio/x-pn-realaudio ram +audio/x-pn-realaudio-plugin rpm +audio/x-realaudio ra +audio/x-wav wav +chemical/x-pdb pdb xyz +image/gif gif +image/ief ief +image/jpeg jpeg jpg jpe +image/png png +image/tiff tiff tif +image/x-cmu-raster ras +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +message/external-body +message/news +message/partial +message/rfc822 +multipart/alternative +multipart/appledouble +multipart/digest +multipart/mixed +multipart/parallel +text/html html htm +text/plain txt +text/richtext rtx +text/tab-separated-values tsv +text/x-setext etx +text/x-sgml sgml sgm +video/mpeg mpeg mpg mpe +video/quicktime qt mov +video/x-msvideo avi +video/x-sgi-movie movie +x-conference/x-cooltalk ice +x-world/x-vrml wrl vrml diff --git a/RELEASE_1_1_X/conf/srm.conf-dist b/RELEASE_1_1_X/conf/srm.conf-dist new file mode 100644 index 00000000000..668e513e707 --- /dev/null +++ b/RELEASE_1_1_X/conf/srm.conf-dist @@ -0,0 +1,210 @@ +# With this document, you define the name space that users see of your http +# server. This file also defines server settings which affect how requests are +# serviced, and how results should be formatted. + +# See the tutorials at http://www.apache.org/ for +# more information. + +# Originally by Rob McCool; Adapted for Apache + + +# DocumentRoot: The directory out of which you will serve your +# documents. By default, all requests are taken from this directory, but +# symbolic links and aliases may be used to point to other locations. + +DocumentRoot /usr/local/etc/httpd/htdocs + +# UserDir: The name of the directory which is appended onto a user's home +# directory if a ~user request is recieved. + +UserDir public_html + +# DirectoryIndex: Name of the file or files to use as a pre-written HTML +# directory index. Separate multiple entries with spaces. + +DirectoryIndex index.html + +# FancyIndexing is whether you want fancy directory indexing or standard + +FancyIndexing on + +# AddIcon tells the server which icon to show for different files or filename +# extensions + +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +AddIcon /icons/bomb.gif core + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +# DefaultIcon is which icon to show for files which do not have an icon +# explicitly set. + +DefaultIcon /icons/unknown.gif + +# AddDescription allows you to place a short description after a file in +# server-generated indexes. +# Format: AddDescription "description" filename + +# ReadmeName is the name of the README file the server will look for by +# default. Format: ReadmeName name +# +# The server will first look for name.html, include it if found, and it will +# then look for name and include it as plaintext if found. +# +# HeaderName is the name of a file which should be prepended to +# directory indexes. + +ReadmeName README +HeaderName HEADER + +# IndexIgnore is a set of filenames which directory indexing should ignore +# Format: IndexIgnore name1 name2... + +IndexIgnore */.??* *~ *# */HEADER* */README* */RCS + +# AccessFileName: The name of the file to look for in each directory +# for access control information. + +AccessFileName .htaccess + +# DefaultType is the default MIME type for documents which the server +# cannot find the type of from filename extensions. + +DefaultType text/plain + +# AddEncoding allows you to have certain browsers (Mosaic/X 2.1+) uncompress +# information on the fly. Note: Not all browsers support this. + +AddEncoding x-compress Z +AddEncoding x-gzip gz + +# AddLanguage allows you to specify the language of a document. You can +# then use content negotiation to give a browser a file in a language +# it can understand. Note that the suffix does not have to be the same +# as the language keyword --- those with documents in Polish (whose +# net-standard language code is pl) may wish to use "AddLanguage pl .po" +# to avoid the ambiguity with the common suffix for perl scripts. + +AddLanguage en .en +AddLanguage fr .fr +AddLanguage de .de +AddLanguage da .da +AddLanguage el .el +AddLanguage it .it + +# LanguagePriority allows you to give precedence to some languages +# in case of a tie during content negotiation. +# Just list the languages in decreasing order of preference. + +LanguagePriority en fr de + +# Redirect allows you to tell clients about documents which used to exist in +# your server's namespace, but do not anymore. This allows you to tell the +# clients where to look for the relocated document. +# Format: Redirect fakename url + + +# Aliases: Add here as many aliases as you need (with no limit). The format is +# Alias fakename realname + +#Alias /icons/ /usr/local/etc/httpd/icons/ + +# ScriptAlias: This controls which directories contain server scripts. +# Format: ScriptAlias fakename realname + +#ScriptAlias /cgi-bin/ /usr/local/etc/httpd/cgi-bin/ + +# If you want to use server side includes, or CGI outside +# ScriptAliased directories, uncomment the following lines. + +# AddType allows you to tweak mime.types without actually editing it, or to +# make certain files to be certain types. +# Format: AddType type/subtype ext1 + +# AddHandler allows you to map certain file extensions to "handlers", +# actions unrelated to filetype. These can be either built into the server +# or added with the Action command (see below) +# Format: AddHandler action-name ext1 + +# To use CGI scripts: +#AddHandler cgi-script .cgi + +# To use server-parsed HTML files +#AddType text/html .shtml +#AddHandler server-parsed .shtml + +# Uncomment the following line to enable Apache's send-asis HTTP file +# feature +#AddHandler send-as-is asis + +# If you wish to use server-parsed imagemap files, use +#AddHandler imap-file map + +# To enable type maps, you might want to use +#AddHandler type-map var + +# Action lets you define media types that will execute a script whenever +# a matching file is called. This eliminates the need for repeated URL +# pathnames for oft-used CGI file processors. +# Format: Action media/type /cgi-script/location +# Format: Action handler-name /cgi-script/location + +# For example to add a footer (footer.html in your document root) to +# files with extension .foot (e.g. foo.html.foot), you could use: +#AddHandler foot-action foot +#Action foot-action /cgi-bin/footer + +# Or to do this for all HTML files, for example, use: +#Action text/html /cgi-bin/footer + +# MetaDir: specifies the name of the directory in which Apache can find +# meta information files. These files contain additional HTTP headers +# to include when sending the document + +#MetaDir .web + +# MetaSuffix: specifies the file name suffix for the file containing the +# meta information. + +#MetaSuffix .meta + +# Customizable error response (Apache style) +# these come in three flavors +# +# 1) plain text +#ErrorDocument 500 "The server made a boo boo. +# n.b. the (") marks it as text, it does not get output +# +# 2) local redirects +#ErrorDocument 404 /missing.html +# to redirect to local url /missing.html +#ErrorDocument 404 /cgi-bin/missing_handler.pl +# n.b. can redirect to a script or a document using server-side-includes. +# +# 3) external redirects +#ErrorDocument 402 http://other.server.com/subscription_info.html +# diff --git a/RELEASE_1_1_X/htdocs/apache_pb.gif b/RELEASE_1_1_X/htdocs/apache_pb.gif new file mode 100644 index 00000000000..3a1c139fc42 Binary files /dev/null and b/RELEASE_1_1_X/htdocs/apache_pb.gif differ diff --git a/RELEASE_1_1_X/htdocs/index.html b/RELEASE_1_1_X/htdocs/index.html new file mode 100644 index 00000000000..576fcf31be6 --- /dev/null +++ b/RELEASE_1_1_X/htdocs/index.html @@ -0,0 +1,30 @@ + + + + +Test Page for Apache + + + + + +

It Worked!

+ +If you can see this, then your +Apache installation was +successful. You may now add content to this directory and +replace this page. + +

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

+ + + + + + + diff --git a/RELEASE_1_1_X/icons/README b/RELEASE_1_1_X/icons/README new file mode 100644 index 00000000000..a1fc5a5a9c7 --- /dev/null +++ b/RELEASE_1_1_X/icons/README @@ -0,0 +1,161 @@ +Public Domain Icons + + These icons were originally made for Mosaic for X and have been + included in the NCSA httpd and Apache server distributions in the + past. They are in the public domain and may be freely included in any + application. The originals were done by Kevin Hughes (kevinh@eit.com). + + Many thanks to Andy Polyakov for tuning the icon colors and adding a + few new images. If you'd like to contribute additions or ideas to + this set, please let me know. + + The distribution site for these icons is at: + + http://www.eit.com/goodies/www.icons/ + + Kevin Hughes + September 11, 1995 + + +Suggested Uses + +The following are a few suggestions, to serve as a starting point for ideas. +Please feel free to tweak and rename the icons as you like. + + a.gif + This might be used to represent PostScript or text layout + languages. + + alert.black.gif, alert.red.gif + These can be used to highlight any important items, such as a + README file in a directory. + + back.gif, forward.gif + These can be used as links to go to previous and next areas. + + ball.gray.gif, ball.red.gif + These might be used as bullets. + + binary.gif + This can be used to represent binary files. + + binhex.gif + This can represent BinHex-encoded data. + + blank.gif + This can be used as a placeholder or a spacing element. + + bomb.gif + This can be used to repreesnt core files. + + box1.gif, box2.gif + These icons can be used to represent generic 3D applications and + related files. + + broken.gif + This can represent corrupted data. + + burst.gif + This can call attention to new and important items. + + c.gif + This might represent C source code. + + comp.blue.gif, comp.red.gif + These little computer icons can stand for telnet or FTP + sessions. + + compressed.gif + This may represent compressed data. + + continued.gif + This can be a link to a continued listing of a directory. + + down.gif, up.gif, left.gif, right.gif + These can be used to scroll up, down, left and right in a + listing or may be used to denote items in an outline. + + dvi.gif + This can represent DVI files. + + f.gif + This might represent FORTRAN or Forth source code. + + folder.gif, folder.open.gif, folder.sec.gif + The folder can represent directories. There is also a version + that can represent secure directories or directories that cannot + be viewed. + + generic.gif, generic.sec.gif, generic.red.gif + These can represent generic files, secure files, and important + files, respectively. + + hand.right.gif, hand.up.gif + These can point out important items (pun intended). + + image1.gif, image2.gif, image3.gif + These can represent image formats of various types. + + index.gif + This might represent a WAIS index or search facility. + + layout.gif + This might represent files and formats that contain graphics as + well as text layout, such as HTML and PDF files. + + link.gif + This might represent files that are symbolic links. + + movie.gif + This can represent various movie formats. + + p.gif + This may stand for Perl or Python source code. + + pie0.gif ... pie8.gif + These icons can be used in applications where a list of + documents is returned from a search. The little pie chart images + can denote how relevant the documents may be to your search + query. + + patch.gif + This may stand for patches and diff files. + + portal.gif + This might be a link to an online service or a 3D world. + + ps.gif, quill.gif + These may represent PostScript files. + + screw1.gif, screw2.gif + These may represent CAD or engineering data and formats. + + script.gif + This can represent any of various interpreted languages, such as + Perl, python, TCL, and shell scripts, as well as server + configuration files. + + sound1.gif, sound2.gif + These can represent sound files. + + sphere1.gif, sphere2.gif + These can represent 3D worlds or rendering applications and + formats. + + tex.gif + This can represent TeX files. + + text.gif + This can represent generic (plain) text files. + + transfer.gif + This can represent FTP transfers or uploads/downloads. + + unknown.gif + This may represent a file of an unknown type. + + uuencoded.gif + This can stand for uuencoded data. + + world1.gif, world2.gif + These can represent 3D worlds or other 3D formats. diff --git a/RELEASE_1_1_X/icons/a.gif b/RELEASE_1_1_X/icons/a.gif new file mode 100644 index 00000000000..bb23d971f4c Binary files /dev/null and b/RELEASE_1_1_X/icons/a.gif differ diff --git a/RELEASE_1_1_X/icons/alert.black.gif b/RELEASE_1_1_X/icons/alert.black.gif new file mode 100644 index 00000000000..eaecd2172a0 Binary files /dev/null and b/RELEASE_1_1_X/icons/alert.black.gif differ diff --git a/RELEASE_1_1_X/icons/alert.red.gif b/RELEASE_1_1_X/icons/alert.red.gif new file mode 100644 index 00000000000..a4238940433 Binary files /dev/null and b/RELEASE_1_1_X/icons/alert.red.gif differ diff --git a/RELEASE_1_1_X/icons/apache_pb.gif b/RELEASE_1_1_X/icons/apache_pb.gif new file mode 100644 index 00000000000..3a1c139fc42 Binary files /dev/null and b/RELEASE_1_1_X/icons/apache_pb.gif differ diff --git a/RELEASE_1_1_X/icons/back.gif b/RELEASE_1_1_X/icons/back.gif new file mode 100644 index 00000000000..a694ae1ec3f Binary files /dev/null and b/RELEASE_1_1_X/icons/back.gif differ diff --git a/RELEASE_1_1_X/icons/ball.gray.gif b/RELEASE_1_1_X/icons/ball.gray.gif new file mode 100644 index 00000000000..eb84268c4cc Binary files /dev/null and b/RELEASE_1_1_X/icons/ball.gray.gif differ diff --git a/RELEASE_1_1_X/icons/ball.red.gif b/RELEASE_1_1_X/icons/ball.red.gif new file mode 100644 index 00000000000..a8425cb574b Binary files /dev/null and b/RELEASE_1_1_X/icons/ball.red.gif differ diff --git a/RELEASE_1_1_X/icons/binary.gif b/RELEASE_1_1_X/icons/binary.gif new file mode 100644 index 00000000000..9a15cbae04c Binary files /dev/null and b/RELEASE_1_1_X/icons/binary.gif differ diff --git a/RELEASE_1_1_X/icons/binhex.gif b/RELEASE_1_1_X/icons/binhex.gif new file mode 100644 index 00000000000..62d0363108d Binary files /dev/null and b/RELEASE_1_1_X/icons/binhex.gif differ diff --git a/RELEASE_1_1_X/icons/blank.gif b/RELEASE_1_1_X/icons/blank.gif new file mode 100644 index 00000000000..0ccf01e1983 Binary files /dev/null and b/RELEASE_1_1_X/icons/blank.gif differ diff --git a/RELEASE_1_1_X/icons/bomb.gif b/RELEASE_1_1_X/icons/bomb.gif new file mode 100644 index 00000000000..270fdb1c064 Binary files /dev/null and b/RELEASE_1_1_X/icons/bomb.gif differ diff --git a/RELEASE_1_1_X/icons/box1.gif b/RELEASE_1_1_X/icons/box1.gif new file mode 100644 index 00000000000..65dcd002eaf Binary files /dev/null and b/RELEASE_1_1_X/icons/box1.gif differ diff --git a/RELEASE_1_1_X/icons/box2.gif b/RELEASE_1_1_X/icons/box2.gif new file mode 100644 index 00000000000..c43bc4faecf Binary files /dev/null and b/RELEASE_1_1_X/icons/box2.gif differ diff --git a/RELEASE_1_1_X/icons/broken.gif b/RELEASE_1_1_X/icons/broken.gif new file mode 100644 index 00000000000..9f8cbe9f760 Binary files /dev/null and b/RELEASE_1_1_X/icons/broken.gif differ diff --git a/RELEASE_1_1_X/icons/burst.gif b/RELEASE_1_1_X/icons/burst.gif new file mode 100644 index 00000000000..fbdcf575f78 Binary files /dev/null and b/RELEASE_1_1_X/icons/burst.gif differ diff --git a/RELEASE_1_1_X/icons/c.gif b/RELEASE_1_1_X/icons/c.gif new file mode 100644 index 00000000000..7555b6c164f Binary files /dev/null and b/RELEASE_1_1_X/icons/c.gif differ diff --git a/RELEASE_1_1_X/icons/comp.blue.gif b/RELEASE_1_1_X/icons/comp.blue.gif new file mode 100644 index 00000000000..f8d76a8c23f Binary files /dev/null and b/RELEASE_1_1_X/icons/comp.blue.gif differ diff --git a/RELEASE_1_1_X/icons/comp.gray.gif b/RELEASE_1_1_X/icons/comp.gray.gif new file mode 100644 index 00000000000..7664cd03649 Binary files /dev/null and b/RELEASE_1_1_X/icons/comp.gray.gif differ diff --git a/RELEASE_1_1_X/icons/compressed.gif b/RELEASE_1_1_X/icons/compressed.gif new file mode 100644 index 00000000000..39e732739f5 Binary files /dev/null and b/RELEASE_1_1_X/icons/compressed.gif differ diff --git a/RELEASE_1_1_X/icons/continued.gif b/RELEASE_1_1_X/icons/continued.gif new file mode 100644 index 00000000000..b0ffb7e0cc0 Binary files /dev/null and b/RELEASE_1_1_X/icons/continued.gif differ diff --git a/RELEASE_1_1_X/icons/dir.gif b/RELEASE_1_1_X/icons/dir.gif new file mode 100644 index 00000000000..48264601ae0 Binary files /dev/null and b/RELEASE_1_1_X/icons/dir.gif differ diff --git a/RELEASE_1_1_X/icons/down.gif b/RELEASE_1_1_X/icons/down.gif new file mode 100644 index 00000000000..a354c871cd0 Binary files /dev/null and b/RELEASE_1_1_X/icons/down.gif differ diff --git a/RELEASE_1_1_X/icons/dvi.gif b/RELEASE_1_1_X/icons/dvi.gif new file mode 100644 index 00000000000..791be33105d Binary files /dev/null and b/RELEASE_1_1_X/icons/dvi.gif differ diff --git a/RELEASE_1_1_X/icons/f.gif b/RELEASE_1_1_X/icons/f.gif new file mode 100644 index 00000000000..fbe353c2822 Binary files /dev/null and b/RELEASE_1_1_X/icons/f.gif differ diff --git a/RELEASE_1_1_X/icons/folder.gif b/RELEASE_1_1_X/icons/folder.gif new file mode 100644 index 00000000000..48264601ae0 Binary files /dev/null and b/RELEASE_1_1_X/icons/folder.gif differ diff --git a/RELEASE_1_1_X/icons/folder.open.gif b/RELEASE_1_1_X/icons/folder.open.gif new file mode 100644 index 00000000000..30979cb5285 Binary files /dev/null and b/RELEASE_1_1_X/icons/folder.open.gif differ diff --git a/RELEASE_1_1_X/icons/folder.sec.gif b/RELEASE_1_1_X/icons/folder.sec.gif new file mode 100644 index 00000000000..75332d9e59b Binary files /dev/null and b/RELEASE_1_1_X/icons/folder.sec.gif differ diff --git a/RELEASE_1_1_X/icons/forward.gif b/RELEASE_1_1_X/icons/forward.gif new file mode 100644 index 00000000000..b2959b4c85c Binary files /dev/null and b/RELEASE_1_1_X/icons/forward.gif differ diff --git a/RELEASE_1_1_X/icons/generic.gif b/RELEASE_1_1_X/icons/generic.gif new file mode 100644 index 00000000000..de60b2940f9 Binary files /dev/null and b/RELEASE_1_1_X/icons/generic.gif differ diff --git a/RELEASE_1_1_X/icons/generic.red.gif b/RELEASE_1_1_X/icons/generic.red.gif new file mode 100644 index 00000000000..94743981d93 Binary files /dev/null and b/RELEASE_1_1_X/icons/generic.red.gif differ diff --git a/RELEASE_1_1_X/icons/generic.sec.gif b/RELEASE_1_1_X/icons/generic.sec.gif new file mode 100644 index 00000000000..88d5240c3c3 Binary files /dev/null and b/RELEASE_1_1_X/icons/generic.sec.gif differ diff --git a/RELEASE_1_1_X/icons/hand.right.gif b/RELEASE_1_1_X/icons/hand.right.gif new file mode 100644 index 00000000000..5cdbc7206da Binary files /dev/null and b/RELEASE_1_1_X/icons/hand.right.gif differ diff --git a/RELEASE_1_1_X/icons/hand.up.gif b/RELEASE_1_1_X/icons/hand.up.gif new file mode 100644 index 00000000000..85a5d683177 Binary files /dev/null and b/RELEASE_1_1_X/icons/hand.up.gif differ diff --git a/RELEASE_1_1_X/icons/icon.sheet.gif b/RELEASE_1_1_X/icons/icon.sheet.gif new file mode 100644 index 00000000000..ad1686e4480 Binary files /dev/null and b/RELEASE_1_1_X/icons/icon.sheet.gif differ diff --git a/RELEASE_1_1_X/icons/image1.gif b/RELEASE_1_1_X/icons/image1.gif new file mode 100644 index 00000000000..01e442bfa92 Binary files /dev/null and b/RELEASE_1_1_X/icons/image1.gif differ diff --git a/RELEASE_1_1_X/icons/image2.gif b/RELEASE_1_1_X/icons/image2.gif new file mode 100644 index 00000000000..751faeea364 Binary files /dev/null and b/RELEASE_1_1_X/icons/image2.gif differ diff --git a/RELEASE_1_1_X/icons/image3.gif b/RELEASE_1_1_X/icons/image3.gif new file mode 100644 index 00000000000..4f30484ff64 Binary files /dev/null and b/RELEASE_1_1_X/icons/image3.gif differ diff --git a/RELEASE_1_1_X/icons/index.gif b/RELEASE_1_1_X/icons/index.gif new file mode 100644 index 00000000000..162478fb3a7 Binary files /dev/null and b/RELEASE_1_1_X/icons/index.gif differ diff --git a/RELEASE_1_1_X/icons/layout.gif b/RELEASE_1_1_X/icons/layout.gif new file mode 100644 index 00000000000..c96338a1522 Binary files /dev/null and b/RELEASE_1_1_X/icons/layout.gif differ diff --git a/RELEASE_1_1_X/icons/left.gif b/RELEASE_1_1_X/icons/left.gif new file mode 100644 index 00000000000..279e6710d49 Binary files /dev/null and b/RELEASE_1_1_X/icons/left.gif differ diff --git a/RELEASE_1_1_X/icons/link.gif b/RELEASE_1_1_X/icons/link.gif new file mode 100644 index 00000000000..c5b6889a76d Binary files /dev/null and b/RELEASE_1_1_X/icons/link.gif differ diff --git a/RELEASE_1_1_X/icons/movie.gif b/RELEASE_1_1_X/icons/movie.gif new file mode 100644 index 00000000000..00351837741 Binary files /dev/null and b/RELEASE_1_1_X/icons/movie.gif differ diff --git a/RELEASE_1_1_X/icons/p.gif b/RELEASE_1_1_X/icons/p.gif new file mode 100644 index 00000000000..7b917b4e91e Binary files /dev/null and b/RELEASE_1_1_X/icons/p.gif differ diff --git a/RELEASE_1_1_X/icons/patch.gif b/RELEASE_1_1_X/icons/patch.gif new file mode 100644 index 00000000000..39bc90e7953 Binary files /dev/null and b/RELEASE_1_1_X/icons/patch.gif differ diff --git a/RELEASE_1_1_X/icons/pdf.gif b/RELEASE_1_1_X/icons/pdf.gif new file mode 100644 index 00000000000..c88fd777c4b Binary files /dev/null and b/RELEASE_1_1_X/icons/pdf.gif differ diff --git a/RELEASE_1_1_X/icons/pie0.gif b/RELEASE_1_1_X/icons/pie0.gif new file mode 100644 index 00000000000..6f7a0ae7a70 Binary files /dev/null and b/RELEASE_1_1_X/icons/pie0.gif differ diff --git a/RELEASE_1_1_X/icons/pie1.gif b/RELEASE_1_1_X/icons/pie1.gif new file mode 100644 index 00000000000..03aa6be71eb Binary files /dev/null and b/RELEASE_1_1_X/icons/pie1.gif differ diff --git a/RELEASE_1_1_X/icons/pie2.gif b/RELEASE_1_1_X/icons/pie2.gif new file mode 100644 index 00000000000..b04c5e09086 Binary files /dev/null and b/RELEASE_1_1_X/icons/pie2.gif differ diff --git a/RELEASE_1_1_X/icons/pie3.gif b/RELEASE_1_1_X/icons/pie3.gif new file mode 100644 index 00000000000..4db9d023eda Binary files /dev/null and b/RELEASE_1_1_X/icons/pie3.gif differ diff --git a/RELEASE_1_1_X/icons/pie4.gif b/RELEASE_1_1_X/icons/pie4.gif new file mode 100644 index 00000000000..93471fdd885 Binary files /dev/null and b/RELEASE_1_1_X/icons/pie4.gif differ diff --git a/RELEASE_1_1_X/icons/pie5.gif b/RELEASE_1_1_X/icons/pie5.gif new file mode 100644 index 00000000000..57aee93f070 Binary files /dev/null and b/RELEASE_1_1_X/icons/pie5.gif differ diff --git a/RELEASE_1_1_X/icons/pie6.gif b/RELEASE_1_1_X/icons/pie6.gif new file mode 100644 index 00000000000..0dc327b5697 Binary files /dev/null and b/RELEASE_1_1_X/icons/pie6.gif differ diff --git a/RELEASE_1_1_X/icons/pie7.gif b/RELEASE_1_1_X/icons/pie7.gif new file mode 100644 index 00000000000..8661337f067 Binary files /dev/null and b/RELEASE_1_1_X/icons/pie7.gif differ diff --git a/RELEASE_1_1_X/icons/pie8.gif b/RELEASE_1_1_X/icons/pie8.gif new file mode 100644 index 00000000000..59ddb34ce0f Binary files /dev/null and b/RELEASE_1_1_X/icons/pie8.gif differ diff --git a/RELEASE_1_1_X/icons/portal.gif b/RELEASE_1_1_X/icons/portal.gif new file mode 100644 index 00000000000..0e6e506e004 Binary files /dev/null and b/RELEASE_1_1_X/icons/portal.gif differ diff --git a/RELEASE_1_1_X/icons/ps.gif b/RELEASE_1_1_X/icons/ps.gif new file mode 100644 index 00000000000..0f565bc1db7 Binary files /dev/null and b/RELEASE_1_1_X/icons/ps.gif differ diff --git a/RELEASE_1_1_X/icons/quill.gif b/RELEASE_1_1_X/icons/quill.gif new file mode 100644 index 00000000000..818a5cdc7e0 Binary files /dev/null and b/RELEASE_1_1_X/icons/quill.gif differ diff --git a/RELEASE_1_1_X/icons/right.gif b/RELEASE_1_1_X/icons/right.gif new file mode 100644 index 00000000000..b256e5f75fb Binary files /dev/null and b/RELEASE_1_1_X/icons/right.gif differ diff --git a/RELEASE_1_1_X/icons/screw1.gif b/RELEASE_1_1_X/icons/screw1.gif new file mode 100644 index 00000000000..af6ba2b097b Binary files /dev/null and b/RELEASE_1_1_X/icons/screw1.gif differ diff --git a/RELEASE_1_1_X/icons/screw2.gif b/RELEASE_1_1_X/icons/screw2.gif new file mode 100644 index 00000000000..06dccb3e44c Binary files /dev/null and b/RELEASE_1_1_X/icons/screw2.gif differ diff --git a/RELEASE_1_1_X/icons/script.gif b/RELEASE_1_1_X/icons/script.gif new file mode 100644 index 00000000000..d8a853bc582 Binary files /dev/null and b/RELEASE_1_1_X/icons/script.gif differ diff --git a/RELEASE_1_1_X/icons/sound1.gif b/RELEASE_1_1_X/icons/sound1.gif new file mode 100644 index 00000000000..8efb49f55d6 Binary files /dev/null and b/RELEASE_1_1_X/icons/sound1.gif differ diff --git a/RELEASE_1_1_X/icons/sound2.gif b/RELEASE_1_1_X/icons/sound2.gif new file mode 100644 index 00000000000..48e6a7fb2fa Binary files /dev/null and b/RELEASE_1_1_X/icons/sound2.gif differ diff --git a/RELEASE_1_1_X/icons/sphere1.gif b/RELEASE_1_1_X/icons/sphere1.gif new file mode 100644 index 00000000000..7067070da27 Binary files /dev/null and b/RELEASE_1_1_X/icons/sphere1.gif differ diff --git a/RELEASE_1_1_X/icons/sphere2.gif b/RELEASE_1_1_X/icons/sphere2.gif new file mode 100644 index 00000000000..a9e462a377c Binary files /dev/null and b/RELEASE_1_1_X/icons/sphere2.gif differ diff --git a/RELEASE_1_1_X/icons/tar.gif b/RELEASE_1_1_X/icons/tar.gif new file mode 100644 index 00000000000..617e779efa5 Binary files /dev/null and b/RELEASE_1_1_X/icons/tar.gif differ diff --git a/RELEASE_1_1_X/icons/tex.gif b/RELEASE_1_1_X/icons/tex.gif new file mode 100644 index 00000000000..45e43233b84 Binary files /dev/null and b/RELEASE_1_1_X/icons/tex.gif differ diff --git a/RELEASE_1_1_X/icons/text.gif b/RELEASE_1_1_X/icons/text.gif new file mode 100644 index 00000000000..4c623909fbf Binary files /dev/null and b/RELEASE_1_1_X/icons/text.gif differ diff --git a/RELEASE_1_1_X/icons/transfer.gif b/RELEASE_1_1_X/icons/transfer.gif new file mode 100644 index 00000000000..33697dbb667 Binary files /dev/null and b/RELEASE_1_1_X/icons/transfer.gif differ diff --git a/RELEASE_1_1_X/icons/unknown.gif b/RELEASE_1_1_X/icons/unknown.gif new file mode 100644 index 00000000000..32b1ea23fb6 Binary files /dev/null and b/RELEASE_1_1_X/icons/unknown.gif differ diff --git a/RELEASE_1_1_X/icons/up.gif b/RELEASE_1_1_X/icons/up.gif new file mode 100644 index 00000000000..6d6d6d1ebf8 Binary files /dev/null and b/RELEASE_1_1_X/icons/up.gif differ diff --git a/RELEASE_1_1_X/icons/uu.gif b/RELEASE_1_1_X/icons/uu.gif new file mode 100644 index 00000000000..4387d529f69 Binary files /dev/null and b/RELEASE_1_1_X/icons/uu.gif differ diff --git a/RELEASE_1_1_X/icons/uuencoded.gif b/RELEASE_1_1_X/icons/uuencoded.gif new file mode 100644 index 00000000000..4387d529f69 Binary files /dev/null and b/RELEASE_1_1_X/icons/uuencoded.gif differ diff --git a/RELEASE_1_1_X/icons/world1.gif b/RELEASE_1_1_X/icons/world1.gif new file mode 100644 index 00000000000..05b4ec20588 Binary files /dev/null and b/RELEASE_1_1_X/icons/world1.gif differ diff --git a/RELEASE_1_1_X/icons/world2.gif b/RELEASE_1_1_X/icons/world2.gif new file mode 100644 index 00000000000..e3203f7a881 Binary files /dev/null and b/RELEASE_1_1_X/icons/world2.gif differ diff --git a/RELEASE_1_1_X/src/.cvsignore b/RELEASE_1_1_X/src/.cvsignore new file mode 100644 index 00000000000..c383b46c7cf --- /dev/null +++ b/RELEASE_1_1_X/src/.cvsignore @@ -0,0 +1,4 @@ +Configuration +Makefile +modules.c +httpd diff --git a/RELEASE_1_1_X/src/API.html b/RELEASE_1_1_X/src/API.html new file mode 100644 index 00000000000..87fc4a0d925 --- /dev/null +++ b/RELEASE_1_1_X/src/API.html @@ -0,0 +1,980 @@ +Apache API notes +

Apache API notes

+ +

Robert S. Thau

+ +These are some notes on the Apache API and the data structures you +have to deal with, etc. They are not yet nearly complete, but +hopefully, they will help you get your bearings. Keep in mind that +the API is still subject to change as we gain experience with it. +(See the TODO file for what might be coming). However, +it will be easy to adapt modules to any changes that are made. +(We have more modules to adapt than you do). +

+ +A few notes on general pedagogical style here. In the interest of +conciseness, all structure declarations here are incomplete --- the +real ones have more slots, that I'm not telling you about. For the +most part, these are reserved to one component of the server core or +another, and should be altered by modules with caution. However, in +some cases, they really are things I just haven't gotten around to +yet. Welcome to the bleeding edge.

+ +Finally, here's an outline, to give you some bare idea of what's +coming up, and in what order: + +

+ +

Basic concepts.

+ +We begin with an overview of the basic concepts behind the +API, and how they are manifested in the code. + +

Handlers, Modules, and Requests

+ +Apache breaks down request handling into a series of steps, more or +less the same way the Netscape Server API does (although this API has +a few more stages than NetSite does, as hooks for stuff I thought +might be useful in the future). These are: + +
    +
  • URI -> Filename translation +
  • Auth ID checking [is the user who they say they are?] +
  • Auth access checking [is the user authorized here?] +
  • Access checking other than auth +
  • Determining MIME type of the object requested +
  • "Fixups" --- there aren't any of these yet, but the phase is + intended as a hook for possible extensions like + SetEnv, which don't really fit well elsewhere. +
  • Actually sending a response back to the client. +
  • Logging the request +
+ +These phases are handled by looking at each of a succession of +modules, looking to see if each of them has a handler for the +phase, and attempting invoking it if so. The handler can typically do +one of three things: + +
    +
  • Handle the request, and indicate that it has done so + by returning the magic constant OK. +
  • Decline to handle the request, by returning the magic + integer constant DECLINED. In this case, the + server behaves in all respects as if the handler simply hadn't + been there. +
  • Signal an error, by returning one of the HTTP error codes. + This terminates normal handling of the request, although an + ErrorDocument may be invoked to try to mop up, and it will be + logged in any case. +
+ +Most phases are terminated by the first module that handles them; +however, for logging, "fixups", and non-access authentication +checking, all handlers always run (barring an error). Also, the +response phase is unique in that modules may declare multiple handlers +for it, via a dispatch table keyed on the MIME type of the requested +object. Modules may declare a response-phase handler which can handle +any request, by giving it the key */* (i.e., a +wildcard MIME type specification). However, wildcard handlers are +only invoked if the server has already tried and failed to find a more +specific response handler for the MIME type of the requested object +(either none existed, or they all declined).

+ +The handlers themselves are functions of one argument (a +request_rec structure. vide infra), which returns an +integer, as above.

+ +

A brief tour of a module

+ +At this point, we need to explain the structure of a module. Our +candidate will be one of the messier ones, the CGI module --- this +handles both CGI scripts and the ScriptAlias config file +command. It's actually a great deal more complicated than most +modules, but if we're going to have only one example, it might as well +be the one with its fingers in everyplace.

+ +Let's begin with handlers. In order to handle the CGI scripts, the +module declares a response handler for them. Because of +ScriptAlias, it also has handlers for the name +translation phase (to recognise ScriptAliased URI's), the +type-checking phase (any ScriptAliased request is typed +as a CGI script).

+ +The module needs to maintain some per (virtual) +server information, namely, the ScriptAliases in effect; +the module structure therefore contains pointers to a functions which +builds these structures, and to another which combines two of them (in +case the main server and a virtual server both have +ScriptAliases declared).

+ +Finally, this module contains code to handle the +ScriptAlias command itself. This particular module only +declares one command, but there could be more, so modules have +command tables which declare their commands, and describe +where they are permitted, and how they are to be invoked.

+ +A final note on the declared types of the arguments of some of these +commands: a pool is a pointer to a resource pool +structure; these are used by the server to keep track of the memory +which has been allocated, files opened, etc., either to service a +particular request, or to handle the process of configuring itself. +That way, when the request is over (or, for the configuration pool, +when the server is restarting), the memory can be freed, and the files +closed, en masse, without anyone having to write explicit code to +track them all down and dispose of them. Also, a +cmd_parms structure contains various information about +the config file being read, and other status information, which is +sometimes of use to the function which processes a config-file command +(such as ScriptAlias). + +With no further ado, the module itself: + +

+/* Declarations of handlers. */
+
+int translate_scriptalias (request_rec *);
+int type_scriptalias (request_rec *);
+int cgi_handler (request_rec *);
+
+/* Subsdiary dispatch table for response-phase handlers, by MIME type */
+
+handler_rec cgi_handlers[] = {
+{ "application/x-httpd-cgi", cgi_handler },
+{ NULL }
+};
+
+/* Declarations of routines to manipulate the module's configuration
+ * info.  Note that these are returned, and passed in, as void *'s;
+ * the server core keeps track of them, but it doesn't, and can't,
+ * know their internal structure.
+ */
+
+void *make_cgi_server_config (pool *);
+void *merge_cgi_server_config (pool *, void *, void *);
+
+/* Declarations of routines to handle config-file commands */
+
+char *script_alias (cmd_parms *, void *per_dir_config, char *fake, char *real);
+
+command_rec cgi_cmds[] = {
+{ "ScriptAlias", script_alias, NULL, RSRC_CONF, TAKE2,
+    "a fakename and a realname"},
+{ NULL }
+};
+
+module cgi_module = {
+   STANDARD_MODULE_STUFF,
+   NULL,			/* initializer */
+   NULL,			/* dir config creater */
+   NULL,			/* dir merger --- default is to override */
+   make_cgi_server_config,      /* server config */
+   merge_cgi_server_config,	/* merge server config */
+   cgi_cmds,			/* command table */
+   cgi_handlers,		/* handlers */
+   translate_scriptalias,	/* filename translation */
+   NULL,			/* check_user_id */
+   NULL,			/* check auth */
+   NULL,			/* check access */
+   type_scriptalias,		/* type_checker */
+   NULL,			/* fixups */
+   NULL				/* logger */
+};
+
+ +

How handlers work

+ +The sole argument to handlers is a request_rec structure. +This structure describes a particular request which has been made to +the server, on behalf of a client. In most cases, each connection to +the client generates only one request_rec structure.

+ +

A brief tour of the request_rec

+ +The request_rec contains pointers to a resource pool +which will be cleared when the server is finished handling the +request; to structures containing per-server and per-connection +information, and most importantly, information on the request itself.

+ +The most important such information is a small set of character +strings describing attributes of the object being requested, including +its URI, filename, content-type and content-encoding (these being filled +in by the translation and type-check handlers which handle the +request, respectively).

+ +Other commonly used data items are tables giving the MIME headers on +the client's original request, MIME headers to be sent back with the +ppppresponse (which modules can add to at will), and environment variables +for any subprocesses which are spawned off in the course of servicing +the request. These tables are manipulated using the +table_get and table_set routines.

+ +Finally, there are pointers to two data structures which, in turn, +point to per-module configuration structures. Specifically, these +hold pointers to the data structures which the module has built to +describe the way it has been configured to operate in a given +directory (via .htaccess files or +<Directory> sections), for private data it has +built in the course of servicing the request (so modules' handlers for +one phase can pass "notes" to their handlers for other phases). There +is another such configuration vector in the server_rec +data structure pointed to by the request_rec, which +contains per (virtual) server configuration data.

+ +Here is an abridged declaration, giving the fields most commonly used:

+ +

+struct request_rec {
+
+  pool *pool;
+  conn_rec *connection;
+  server_rec *server;
+
+  /* What object is being requested */
+  
+  char *uri;
+  char *filename;
+  char *path_info;
+  char *args;			/* QUERY_ARGS, if any */
+  struct stat finfo;		/* Set by server core;
+                                 * st_mode set to zero if no such file */
+  
+  char *content_type;
+  char *content_encoding;
+  
+  /* MIME header environments, in and out.  Also, an array containing
+   * environment variables to be passed to subprocesses, so people can
+   * write modules to add to that environment.
+   *
+   * The difference between headers_out and err_headers_out is that the
+   * latter are printed even on error, and persist across internal redirects
+   * (so the headers printed for ErrorDocument handlers will have them).
+   */
+  
+  table *headers_in;
+  table *headers_out;
+  table *err_headers_out;
+  table *subprocess_env;
+
+  /* Info about the request itself... */
+  
+  int header_only;		/* HEAD request, as opposed to GET */
+  char *protocol;		/* Protocol, as given to us, or HTTP/0.9 */
+  char *method;			/* GET, HEAD, POST, etc. */
+  int method_number;		/* M_GET, M_POST, etc. */
+
+  /* Info for logging */
+
+  char *the_request;
+  int bytes_sent;
+
+  /* A flag which modules can set, to indicate that the data being
+   * returned is volatile, and clients should be told not to cache it.
+   */
+
+  int no_cache;
+
+  /* Various other config info which may change with .htaccess files
+   * These are config vectors, with one void* pointer for each module
+   * (the thing pointed to being the module's business).
+   */
+  
+  void *per_dir_config;		/* Options set in config files, etc. */
+  void *request_config;		/* Notes on *this* request */
+  
+};
+
+
+ +

Where request_rec structures come from

+ +Most request_rec structures are built by reading an HTTP +request from a client, and filling in the fields. However, there are +a few exceptions: + +
    +
  • If the request is to an imagemap, a type map (i.e., a + *.var file), or a CGI script which returned a + local "Location:", then the resource which the user requested + is going to be ultimately located by some URI other than what + the client originally supplied. In this case, the server does + an internal redirect, constructing a new + request_rec for the new URI, and processing it + almost exactly as if the client had requested the new URI + directly.

    + +

  • If some handler signaled an error, and an + ErrorDocument is in scope, the same internal + redirect machinery comes into play.

    + +

  • Finally, a handler occasionally needs to investigate "what + would happen if" some other request were run. For instance, + the directory indexing module needs to know what MIME type + would be assigned to a request for each directory entry, in + order to figure out what icon to use.

    + + Such handlers can construct a sub-request, using the + functions sub_req_lookup_file and + sub_req_lookup_uri; this constructs a new + request_rec structure and processes it as you + would expect, up to but not including the point of actually + sending a response. (These functions skip over the access + checks if the sub-request is for a file in the same directory + as the original request).

    + + (Server-side includes work by building sub-requests and then + actually invoking the response handler for them, via the + function run_sub_request). +

+ +

Handling requests, declining, and returning error codes

+ +As discussed above, each handler, when invoked to handle a particular +request_rec, has to return an int to +indicate what happened. That can either be + +
    +
  • OK --- the request was handled successfully. This may or may + not terminate the phase. +
  • DECLINED --- no erroneous condition exists, but the module + declines to handle the phase; the server tries to find another. +
  • an HTTP error code, which aborts handling of the request. +
+ +Note that if the error code returned is REDIRECT, then +the module should put a Location in the request's +headers_out, to indicate where the client should be +redirected to.

+ +

Special considerations for response handlers

+ +Handlers for most phases do their work by simply setting a few fields +in the request_rec structure (or, in the case of access +checkers, simply by returning the correct error code). However, +response handlers have to actually send a request back to the client.

+ +They should begin by sending an HTTP response header, using the +function send_http_header. (You don't have to do +anything special to skip sending the header for HTTP/0.9 requests; the +function figures out on its own that it shouldn't do anything). If +the request is marked header_only, that's all they should +do; they should return after that, without attempting any further +output.

+ +Otherwise, they should produce a request body which responds to the +client as appropriate. The primitives for this are rputc +and rprintf, for internally generated output, and +send_fd, to copy the contents of some FILE * +straight to the client.

+ +At this point, you should more or less understand the following piece +of code, which is the handler which handles GET requests +which have no more specific handler; it also shows how conditional +GETs can be handled, if it's desirable to do so in a +particular response handler --- set_last_modified checks +agaisnt the If-modified-since value supplied by the +client, if annny, and returns an appropriate code (which will, if +nonzero, be USE_LOCAL_COPY). No similar considerations apply for +set_content_length, but it returns an error code for +symmetry.

+ +

+int default_handler (request_rec *r)
+{
+    int errstatus;
+    FILE *f;
+    
+    if (r->method_number != M_GET) return DECLINED;
+    if (r->finfo.st_mode == 0) return NOT_FOUND;
+	
+    if ((errstatus = set_content_length (r, r->finfo.st_size))
+	|| (errstatus = set_last_modified (r, r->finfo.st_mtime)))
+        return errstatus;
+    
+    f = fopen (r->filename, "r");
+
+    if (f == NULL) {
+        log_reason("file permissions deny server access", r->filename, r);
+        return FORBIDDEN;
+    }
+      
+    register_timeout ("send", r);
+    send_http_header (r);
+
+    if (!r->header_only) send_fd (f, r);
+    pfclose (r->pool, f);
+    return OK;
+}
+
+ +Finally, if all of this is too much of a challenge, there are a few +ways out of it. First off, as shown above, a response handler which +has not yet produced any output can simply return an error code, in +which case the server will automatically produce an error response. +Secondly, it can punt to some other handler by invoking +internal_redirect, which is how the internal redirection +machinery discussed above is invoked. A response handler which has +internally redirected should always return OK.

+ +(Invoking internal_redirect from handlers which are +not response handlers will lead to serious confusion). + +

Special considerations for authentication handlers

+ +Stuff that should be discussed here in detail: + +
    +
  • Authentication-phase handlers not invoked unless auth is + configured for the directory. +
  • Common auth configuration stored in the core per-dir + configuration; it has accessors auth_type, + auth_name, and requires. +
  • Common routines, to handle the protocol end of things, at least + for HTTP basic authentication (get_basic_auth_pw, + which sets the connection->user structure field + automatically, and note_basic_auth_failure, which + arranges for the proper WWW-Authenticate: header + to be sent back). +
+ +

Special considerations for logging handlers

+ +When a request has internally redirected, there is the question of +what to log. Apache handles this by bundling the entire chain of +redirects into a list of request_rec structures which are +threaded through the r->prev and r->next +pointers. The request_rec which is passed to the logging +handlers in such cases is the one which was originally built for the +intial request from the client; note that the bytes_sent field will +only be correct in the last request in the chain (the one for which a +response was actually sent). + +

Resource allocation and resource pools

+ +One of the problems of writing and designing a server-pool server is +that of preventing leakage, that is, allocating resources (memory, +open files, etc.), without subsequently releasing them. The resource +pool machinery is designed to make it easy to prevent this from +happening, by allowing resource to be allocated in such a way that +they are automatically released when the server is done with +them.

+ +The way this works is as follows: the memory which is allocated, file +opened, etc., to deal with a particular request are tied to a +resource pool which is allocated for the request. The pool +is a data structure which itself tracks the resources in question.

+ +When the request has been processed, the pool is cleared. At +that point, all the memory associated with it is released for reuse, +all files associated with it are closed, and any other clean-up +functions which are associated with the pool are run. When this is +over, we can be confident that all the resource tied to the pool have +been released, and that none of them have leaked.

+ +Server restarts, and allocation of memory and resources for per-server +configuration, are handled in a similar way. There is a +configuration pool, which keeps track of resources which were +allocated while reading the server configuration files, and handling +the commands therein (for instance, the memory that was allocated for +per-server module configuration, log files and other files that were +opened, and so forth). When the server restarts, and has to reread +the configuration files, the configuration pool is cleared, and so the +memory and file descriptors which were taken up by reading them the +last time are made available for reuse.

+ +It should be noted that use of the pool machinery isn't generally +obligatory, except for situations like logging handlers, where you +really need to register cleanups to make sure that the log file gets +closed when the server restarts (this is most easily done by using the +function pfopen, which also +arranges for the underlying file descriptor to be closed before any +child processes, such as for CGI scripts, are execed), or +in case you are using the timeout machinery (which isn't yet even +documented here). However, there are two benefits to using it: +resources allocated to a pool never leak (even if you allocate a +scratch string, and just forget about it); also, for memory +allocation, palloc is generally faster than +malloc.

+ +We begin here by describing how memory is allocated to pools, and then +discuss how other resources are tracked by the resource pool +machinery. + +

Allocation of memory in pools

+ +Memory is allocated to pools by calling the function +palloc, which takes two arguments, one being a pointer to +a resource pool structure, and the other being the amount of memory to +allocate (in chars). Within handlers for handling +requests, the most common way of getting a resource pool structure is +by looking at the pool slot of the relevant +request_rec; hence the repeated appearance of the +following idiom in module code: + +
+int my_handler(request_rec *r)
+{
+    struct my_structure *foo;
+    ...
+
+    foo = (foo *)palloc (r->pool, sizeof(my_structure));
+}
+
+ +Note that there is no pfree --- +palloced memory is freed only when the associated +resource pool is cleared. This means that palloc doesn't +have to do as much accounting as malloc(); all it does in +the typical case is to round up the size, bump a pointer, and do a +range check.

+ +(It also raises the possibility that heavy use of palloc +could cause a server process to grow excessively large. There are +two ways to deal with this, which are dealt with below; briefly, you +can use malloc, and try to be sure that all of the memory +gets explicitly freed, or you can allocate a sub-pool of +the main pool, allocate your memory in the sub-pool, and clear it out +periodically. The latter technique is discussed in the section on +sub-pools below, and is used in the directory-indexing code, in order +to avoid excessive storage allocation when listing directories with +thousands of files). + +

Allocating initialized memory

+ +There are functions which allocate initialized memory, and are +frequently useful. The function pcalloc has the same +interface as palloc, but clears out the memory it +allocates before it returns it. The function pstrdup +takes a resource pool and a char * as arguments, and +allocates memory for a copy of the string the pointer points to, +returning a pointer to the copy. Finally pstrcat is a +varargs-style function, which takes a pointer to a resource pool, and +at least two char * arguments, the last of which must be +NULL. It allocates enough memory to fit copies of each +of the strings, as a unit; for instance: + +
+     pstrcat (r->pool, "foo", "/", "bar", NULL);
+
+ +returns a pointer to 8 bytes worth of memory, initialized to +"foo/bar". + +

Tracking open files, etc.

+ +As indicated above, resource pools are also used to track other sorts +of resources besides memory. The most common are open files. The +routine which is typically used for this is pfopen, which +takes a resource pool and two strings as arguments; the strings are +the same as the typical arguments to fopen, e.g., + +
+     ...
+     FILE *f = pfopen (r->pool, r->filename, "r");
+
+     if (f == NULL) { ... } else { ... }
+
+ +There is also a popenf routine, which parallels the +lower-level open system call. Both of these routines +arrange for the file to be closed when the resource pool in question +is cleared.

+ +Unlike the case for memory, there are functions to close +files allocated with pfopen, and popenf, +namely pfclose and pclosef. (This is +because, on many systems, the number of files which a single process +can have open is quite limited). It is important to use these +functions to close files allocated with pfopen and +popenf, since to do otherwise could cause fatal errors on +systems such as Linux, which react badly if the same +FILE* is closed more than once.

+ +(Using the close functions is not mandatory, since the +file will eventually be closed regardless, but you should consider it +in cases where your module is opening, or could open, a lot of files). + +

Other sorts of resources --- cleanup functions

+ +More text goes here. Describe the the cleanup primitives in terms of +which the file stuff is implemented; also, spawn_process. + +

Fine control --- creating and dealing with sub-pools, with a note +on sub-requests

+ +On rare occasions, too-free use of palloc() and the +associated primitives may result in undesirably profligate resource +allocation. You can deal with such a case by creating a +sub-pool, allocating within the sub-pool rather than the main +pool, and clearing or destroying the sub-pool, which releases the +resources which were associated with it. (This really is a +rare situation; the only case in which it comes up in the standard +module set is in case of listing directories, and then only with +very large directories. Unnecessary use of the primitives +dicussed here can hair up your code quite a bit, with very little +gain).

+ +The primitive for creating a sub-pool is make_sub_pool, +which takes another pool (the parent pool) as an argument. When the +main pool is cleared, the sub-pool will be destroyed. The sub-pool +may also be cleared or destroyed at any time, by calling the functions +clear_pool and destroy_pool, respectively. +(The difference is that clear_pool frees resources +associated with the pool, while destroy_pool also +deallocates the pool itself. In the former case, you can allocate new +resources within the pool, and clear it again, and so forth; in the +latter case, it is simply gone).

+ +One final note --- sub-requests have their own resource pools, which +are sub-pools of the resource pool for the main request. The polite +way to reclaim the resources associated with a sub request which you +have allocated (using the sub_req_lookup_... functions) +is destroy_sub_request, which frees the resource pool. +Before calling this function, be sure to copy anything that you care +about which might be allocated in the sub-request's resource pool into +someplace a little less volatile (for instance, the filename in its +request_rec structure).

+ +(Again, under most circumstances, you shouldn't feel obliged to call +this function; only 2K of memory or so are allocated for a typical sub +request, and it will be freed anyway when the main request pool is +cleared. It is only when you are allocating many, many sub-requests +for a single main request that you should seriously consider the +destroy... functions). + +

Configuration, commands and the like

+ +One of the design goals for this server was to maintain external +compatibility with the NCSA 1.3 server --- that is, to read the same +configuration files, to process all the directives therein correctly, +and in general to be a drop-in replacement for NCSA. On the other +hand, another design goal was to move as much of the server's +functionality into modules which have as little as possible to do with +the monolithic server core. The only way to reconcile these goals is +to move the handling of most commands from the central server into the +modules.

+ +However, just giving the modules command tables is not enough to +divorce them completely from the server core. The server has to +remember the commands in order to act on them later. That involves +maintaining data which is private to the modules, and which can be +either per-server, or per-directory. Most things are per-directory, +including in particular access control and authorization information, +but also information on how to determine file types from suffixes, +which can be modified by AddType and +DefaultType directives, and so forth. In general, the +governing philosophy is that anything which can be made +configurable by directory should be; per-server information is +generally used in the standard set of modules for information like +Aliases and Redirects which come into play +before the request is tied to a particular place in the underlying +file system.

+ +Another requirement for emulating the NCSA server is being able to +handle the per-directory configuration files, generally called +.htaccess files, though even in the NCSA server they can +contain directives which have nothing at all to do with access +control. Accordingly, after URI -> filename translation, but before +performing any other phase, the server walks down the directory +hierarchy of the underlying filesystem, following the translated +pathname, to read any .htaccess files which might be +present. The information which is read in then has to be +merged with the applicable information from the server's own +config files (either from the <Directory> sections +in access.conf, or from defaults in +srm.conf, which actually behaves for most purposes almost +exactly like <Directory />).

+ +Finally, after having served a request which involved reading +.htaccess files, we need to discard the storage allocated +for handling them. That is solved the same way it is solved wherever +else similar problems come up, by tying those structures to the +per-transaction resource pool.

+ +

Per-directory configuration structures

+ +Let's look out how all of this plays out in mod_mime.c, +which defines the file typing handler which emulates the NCSA server's +behavior of determining file types from suffixes. What we'll be +looking at, here, is the code which implements the +AddType and AddEncoding commands. These +commands can appear in .htaccess files, so they must be +handled in the module's private per-directory data, which in fact, +consists of two separate tables for MIME types and +encoding information, and is declared as follows: + +
+typedef struct {
+    table *forced_types;	/* Additional AddTyped stuff */
+    table *encoding_types;	/* Added with AddEncoding... */
+} mime_dir_config;
+
+ +When the server is reading a configuration file, or +<Directory> section, which includes one of the MIME +module's commands, it needs to create a mime_dir_config +structure, so those commands have something to act on. It does this +by invoking the function it finds in the module's "create per-dir +config slot", with two arguments: the name of the directory to which +this configuration information applies (or NULL for +srm.conf), and a pointer to a resource pool in which the +allocation should happen.

+ +(If we are reading a .htaccess file, that resource pool +is the per-request resource pool for the request; otherwise it is a +resource pool which is used for configuration data, and cleared on +restarts. Either way, it is important for the structure being created +to vanish when the pool is cleared, by registering a cleanup on the +pool if necessary).

+ +For the MIME module, the per-dir config creation function just +pallocs the structure above, and a creates a couple of +tables to fill it. That looks like this: + +

+void *create_mime_dir_config (pool *p, char *dummy)
+{
+    mime_dir_config *new =
+      (mime_dir_config *) palloc (p, sizeof(mime_dir_config));
+
+    new->forced_types = make_table (p, 4);
+    new->encoding_types = make_table (p, 4);
+    
+    return new;
+}
+
+ +Now, suppose we've just read in a .htaccess file. We +already have the per-directory configuration structure for the next +directory up in the hierarchy. If the .htaccess file we +just read in didn't have any AddType or +AddEncoding commands, its per-directory config structure +for the MIME module is still valid, and we can just use it. +Otherwise, we need to merge the two structures somehow.

+ +To do that, the server invokes the module's per-directory config merge +function, if one is present. That function takes three arguments: +the two structures being merged, and a resource pool in which to +allocate the result. For the MIME module, all that needs to be done +is overlay the tables from the new per-directory config structure with +those from the parent: + +

+void *merge_mime_dir_configs (pool *p, void *parent_dirv, void *subdirv)
+{
+    mime_dir_config *parent_dir = (mime_dir_config *)parent_dirv;
+    mime_dir_config *subdir = (mime_dir_config *)subdirv;
+    mime_dir_config *new =
+      (mime_dir_config *)palloc (p, sizeof(mime_dir_config));
+
+    new->forced_types = overlay_tables (p, subdir->forced_types,
+					parent_dir->forced_types);
+    new->encoding_types = overlay_tables (p, subdir->encoding_types,
+					  parent_dir->encoding_types);
+
+    return new;
+}
+
+ +As a note --- if there is no per-directory merge function present, the +server will just use the subdirectory's configuration info, and ignore +the parent's. For some modules, that works just fine (e.g., for the +includes module, whose per-directory configuration information +consists solely of the state of the XBITHACK), and for +those modules, you can just not declare one, and leave the +corresponding structure slot in the module itself NULL.

+ +

Command handling

+ +Now that we have these structures, we need to be able to figure out +how to fill them. That involves processing the actual +AddType and AddEncoding commands. To find +commands, the server looks in the module's command table. +That table contains information on how many arguments the commands +take, and in what formats, where it is permitted, and so forth. That +information is sufficient to allow the server to invoke most +command-handling functions with preparsed arguments. Without further +ado, let's look at the AddType command handler, which +looks like this (the AddEncoding command looks basically +the same, and won't be shown here): + +
+char *add_type(cmd_parms *cmd, mime_dir_config *m, char *ct, char *ext)
+{
+    if (*ext == '.') ++ext;
+    table_set (m->forced_types, ext, ct);
+    return NULL;
+}
+
+ +This command handler is unusually simple. As you can see, it takes +four arguments, two of which are preparsed arguments, the third being +the per-directory configuration structure for the module in question, +and the fourth being a pointer to a cmd_parms structure. +That structure contains a bunch of arguments which are frequently of +use to some, but not all, commands, including a resource pool (from +which memory can be allocated, and to which cleanups should be tied), +and the (virtual) server being configured, from which the module's +per-server configuration data can be obtained if required.

+ +Another way in which this particular command handler is unusually +simple is that there are no error conditions which it can encounter. +If there were, it could return an error message instead of +NULL; this causes an error to be printed out on the +server's stderr, followed by a quick exit, if it is in +the main config files; for a .htaccess file, the syntax +error is logged in the server error log (along with an indication of +where it came from), and the request is bounced with a server error +response (HTTP error status, code 500).

+ +The MIME module's command table has entries for these commands, which +look like this: + +

+command_rec mime_cmds[] = {
+{ "AddType", add_type, NULL, OR_FILEINFO, TAKE2, 
+    "a mime type followed by a file extension" },
+{ "AddEncoding", add_encoding, NULL, OR_FILEINFO, TAKE2, 
+    "an encoding (e.g., gzip), followed by a file extension" },
+{ NULL }
+};
+
+ +The entries in these tables are: + +
    +
  • The name of the command +
  • The function which handles it +
  • a (void *) pointer, which is passed in the + cmd_parms structure to the command handler --- + this is useful in case many similar commands are handled by the + same function. +
  • A bit mask indicating where the command may appear. There are + mask bits corresponding to each AllowOverride + option, and an additional mask bit, RSRC_CONF, + indicating that the command may appear in the server's own + config files, but not in any .htaccess + file. +
  • A flag indicating how many arguments the command handler wants + preparsed, and how they should be passed in. + TAKE2 indicates two preparsed arguments. Other + options are TAKE1, which indicates one preparsed + argument, FLAG, which indicates that the argument + should be On or Off, and is passed in + as a boolean flag, RAW_ARGS, which causes the + server to give the command the raw, unparsed arguments + (everything but the command name itself). There is also + ITERATE, which means that the handler looks the + same as TAKE1, but that if multiple arguments are + present, it should be called multiple times, and finally + ITERATE2, which indicates that the command handler + looks like a TAKE2, but if more arguments are + present, then it should be called multiple times, holding the + first argument constant. +
  • Finally, we have a string which describes the arguments that + should be present. If the arguments in the actual config file + are not as required, this string will be used to help give a + more specific error message. (You can safely leave this + NULL). +
+ +Finally, having set this all up, we have to use it. This is +ultimately done in the module's handlers, specifically for its +file-typing handler, which looks more or less like this; note that the +per-directory configuration structure is extracted from the +request_rec's per-directory configuration vector by using +the get_module_config function. + +
+int find_ct(request_rec *r)
+{
+    int i;
+    char *fn = pstrdup (r->pool, r->filename);
+    mime_dir_config *conf =
+      (mime_dir_config *)get_module_config(r->per_dir_config, &mime_module);
+    char *type;
+
+    if (S_ISDIR(r->finfo.st_mode)) {
+        r->content_type = DIR_MAGIC_TYPE;
+	return OK;
+    }
+    
+    if((i=rind(fn,'.')) < 0) return DECLINED;
+    ++i;
+
+    if ((type = table_get (conf->encoding_types, &fn[i])))
+    {
+        r->content_encoding = type;
+
+	/* go back to previous extension to try to use it as a type */
+	
+        fn[i-1] = '\0';
+	if((i=rind(fn,'.')) < 0) return OK;
+	++i;
+    }
+
+    if ((type = table_get (conf->forced_types, &fn[i])))
+    {
+        r->content_type = type;
+    }
+    
+    return OK;
+}
+
+
+ +

Side notes --- per-server configuration, virtual servers, etc.

+ +The basic ideas behind per-server module configuration are basically +the same as those for per-directory configuration; there is a creation +function and a merge function, the latter being invoked where a +virtual server has partially overriden the base server configuration, +and a combined structure must be computed. (As with per-directory +configuration, the default if no merge function is specified, and a +module is configured in some virtual server, is that the base +configuration is simply ignored).

+ +The only substantial difference is that when a command needs to +configure the per-server private module data, it needs to go to the +cmd_parms data to get at it. Here's an example, from the +alias module, which also indicates how a syntax error can be returned +(note that the per-directory configuration argument to the command +handler is declared as a dummy, since the module doesn't actually have +per-directory config data): + +

+char *add_redirect(cmd_parms *cmd, void *dummy, char *f, char *url)
+{
+    server_rec *s = cmd->server;
+    alias_server_conf *conf =
+        (alias_server_conf *)get_module_config(s->module_config,&alias_module);
+    alias_entry *new = push_array (conf->redirects);
+
+    if (!is_url (url)) return "Redirect to non-URL";
+    
+    new->fake = f; new->real = url;
+    return NULL;
+}
+
+ diff --git a/RELEASE_1_1_X/src/CHANGES b/RELEASE_1_1_X/src/CHANGES new file mode 100644 index 00000000000..ced9c519a40 --- /dev/null +++ b/RELEASE_1_1_X/src/CHANGES @@ -0,0 +1,1093 @@ + *) Bring NeXT support up to date. [Takaaki Matsumoto] + + *) Bring QNX support up to date. [Ben Laurie] + + *) Make virtual hosts default to main server keepalive parameters. + [Alexei Kosut, Ben Laurie] + + *) Allow ScanHTMLTitles to work with lowercase tags. [Alexei Kosut] + + *) Fix missing address family for connect, also remove unreachable statement + in mod_proxy. [Ben Laurie] + + *) mod_env now turned on by default in Configuration.tmpl. + + *) Bugs which were fixed: + a) yet more mod_proxy bugs [Ben Laurie] + b) CGI works again with inetd [Alexei Kosut] + c) Leading colons were stripped from passwords [osm@interguide.com] + d) Another fix to multi-method Limit problem [jk@tools.de] + +Changes with Apache 1.1b4: + + *) r->bytes_sent variable restored. [Robert Thau] + + *) Previously broken multi-method <Limit> parsing fixed. [Robert Thau] + + *) More possibly unsecure programs removed from the support directory. + + *) More mod_auth_msql authentication improvements. + + *) VirtualHosts based on Host: headers no longer conflict with the + Listen directive. + + *) OS/2 compatibility enhancements. [Gary Smiley] + + *) POST now allowed to directory index CGI scripts. + + *) Actions now work with files of the default type. + + *) Bugs which were fixed: + a) more mod_proxy bugs + b) early termination of inetd requests + c) compile warnings on several systems + d) problems when scripts stop reading output early + +Changes with Apache 1.1b3: + + *) Much of cgi-bin and all of cgi-src has been removed, due to + various security holes found and that we could no longer support + them. + + *) The "Set-Cookie" header is now special-special-cased to not + merge multiple instances, since certain popular browsers can not + handle multiple Set-Cookie instructions in a single header. + + *) rprintf() added to buffer code, occurrences of sprintf removed. + [Ben Laurie] + + *) CONNECT method for proxy module, which means tunneling SSL should work. + (No crypto needed) Also a NoCache config directive. + + *) Several API additions: pstrndup(), table_unset() and get_token() + functions now available to modules. + + *) mod_imap fixups, in particular Location: headers are now complete + URL's. + + *) New "info" module which reports on installed module set through a + special URL, a la mod_status. + + *) "ServerPath" directive added - allows for graceful transition + for Host:-header-based virtual hosts. + + *) Anonymous authentication module improvements. + + *) MSQL authentication module improvements. + + *) Status module design improved - output now table-based. [Ben Laurie] + + *) htdigest utility included for use with digest authentication + module. + + *) mod_negotiation: Accept values with wildcards to be treated with + less priority than those without wildcards at the same quality + value. [Alexei Kosut] + + *) Bugs which were fixed: + a) numerous mod_proxy bugs + b) CGI early-termination bug [Ben Laurie] + c) Keepalives not working with virtual hosts + d) RefererIgnore problems + e) closing fd's twice in mod_include (causing core dumps on + Linux and elsewhere). + +Changes with Apache 1.1b2. + + *) Bugfixes: + a) core dumps in mod_digest + b) truncated hostnames/ip address in the logs + c) relative URL's in mod_imap map files + +Changes with Apache 1.1b1. Also see http://www.apache.org/docs/1.1/ + + *) Add new <Location> directive, similar to <Directory>, but protects + URLs instead of directories. [Alexei Kosut] + + *) Allow ErrorDocument directive to also be used from .htaccess files. + This is enabled/disabled by using the FileInfo flag with + AllowOverrides [Mark Cox] + + *) Add AddHandler command, which allows content-type-independent + "handlers" to be defined for file extensions. These can be either + built into Apache (such as CGI scripts or server-side includes, or + added with the Action command). [Alexei Kosut] + + *) Added new Explain() function, intended to help trace execution. Currently + only used in mod_proxy.c (and there not yet complete). Switch it on with + -DEXPLAIN. See explain.h. [Ben Laurie] + + *) Added two new directives to proxy module, CacheDirLevels and + CacheDirLength. Values of 3 and 2 respectively will result in files like + aa/bb/cc/dddddd in the cache. [Ben Laurie] + + *) Modify <VirtualHost> directive to work without the system supporting + multiple IP addresses. It can now take advantage of the Host: header + sent by some (but not all) new web browsers (e.g. Netscape 2.0). Those + that do not support it will get the default documents. More names or + name patters can be added using the ScriptAlias directive, e.g. + "ScriptAlias foo.bar.net *.foo.com foo.com" [Alexei Kosut] + + *) Added Status module with preliminary documentation available + http://www.apache.org/docs/1.1/mod_status.html [Mark Cox] + + *) Added simple and complex instrumentation to Apache to allow a + instantaneous status screen to be displayed. Since the complex + instrumentation makes the scoreboard file bigger it has been + made a compile-time define (add "-DSTATUS" to + CFLAGS in the Configuration file) [Mark Cox] + + *) Made it so XBITHACK behavior only occurs for files of type text/html, as + any other file type doesn't really make sense. [Alexei Kosut] + + *) Slightly modified the API to allow handlers by things other than + content type. Setting r->handler will override r->content_type in + invoke_handler(). [Alexei Kosut] + + *) Added FLOCK_SERIALIZED_ACCEPT for those systems, like A/UX, + that need mutex-capability but using fcntl-locking is + expensive. [Jim Jagielski] + + *) Added in Cliff Skolnick's read_client_block patch (check for + no reads and break) [Jim Jagielski] + + *) If there was no default MIME type defined, Apache sent Content-type: + without a newline, thus swallowing the next header. Changed to send no + Content-type: at all. [Ben Laurie] + + *) Long overdue revamp of the Cookies module to stop Apache cookies + colliding with other cookies a site may be using. Cookies are now + always generated for every browser, not just Netscape (and clones) + and the cookie log can be a pipe just like the other logs [Mark Cox] + + *) Add HAVE_MMAP to SCO 5. [Ben Laurie] + + *) SCO 5 reportedly (by Jay Thorne <jay@result.com>) requires + FCNTL_SERIALIZED_ACCEPT. Added to conf.h. [Ben Laurie] + + *) Clear up some nasty (int)NULLs. [Ben Laurie] + + *) Clean code to warning level 3 under SCO 5. [Ben Laurie] + + *) Fix potential buffer overrun in mod_proxy.c. [Ben Laurie] + + *) Internal redirects now preserve HTTP headers [Rob Hartil] + + *) Adds SysV-based Shared Memory support for Apache [Jim Jagielski] + + *) Use bread() etc instead of fread() for reading/writing to client. + Move the count of bytes sent into the buffering routines. + Rewrite the buffering routines to provide bputc(), bgetc() macros. + [David Robinson] + + *) Make hostname_lookups a per-directory core configuration variable. + Replace all accesses to the connection.remote_host and remote_name + variables to calls to get_remote_host(), which is now in http_core.c. + [David Robinson] + + *) Make do_rfc1413 a per-directory core configuration variable. + Replace all accesses to the connection.remote_logname + variables to calls to get_remote_logname(). Rewrite rfc931.c as + rfc1413.c to conform to new standard, avoid stdio and avoid syslog. + [David Robinson] + + *) CGI variables are now available to server side include directives + as well as the QUERY_STRING_UNESCAPED variable, as required by + the NCSA server side include documentation. [Nathan Schrenk] + + *) Better diagnostic info to enhance "malformed header from script" + [Rob Hartill] + + *) Improvements and new features added to the UserDir directive: + Additional forms of specifiying user directories, and the ability to + specify multiple user directories, much like the DirectoryIndex + command. [Alexei Kosut] + + *) Internal redirects no longer force the GET method, except when desired + (CGI scripts and error documents). [Adam Sussman and Rasmus Lerdorf] + + *) Rewrite/reorganization of mod_imap.c. New handling of default, base + and relative URLs. New Configuration directives. Support for creating + non-graphical menu added. (backwards compatible). [Nathan Kurz] + + *) New module which allows Apache's CGI and SSI environment to inherit + environment variables from the shell which invoked the httpd + process. CERN webservers are able to do this, so this module is + especially useful to webadmins who wish to migrate from CERN to Apache + without rewriting all their scripts [Andrew Wilson] + + *) Updated /cgi-src to the latest NCSA version, including the IBM-ERS patch + to close a security hole, as well as other changes to the distribution + since it was last updated (Apache 0.8.5) [Alexei Kosut] + + *) Move Configuration to Configuration.tmpl for CVS development. Make CVS + ignore Configuration, Makefile and modules.c. [Ben Laurie] + +Changes with Apache 1.1b0 + + *) Use shmget() (and family) for shared memory for the scoreboard + for SysV-based systems; basically A/UX and IRIX. Allow for + memory break-point (brk(0)) to be moved if need be before + attaching the mem segment. [Jim Jagielski] + + *) Use mmap() to allocated shared memory for the scoreboard on + Solaris, SunOS, Digital UNIX, BSDI and HP/UX. It should work on + most of the other architectures. [David Robinson] + + *) Cache the contents of .htaccess file for the duration of a request. + [David Robinson] + + *) Avoid use of strftime() whilst processing request. [David Robinson] + + *) Add the ability to have media type-based CGI script actions, + as well as an example and relevent srm.conf entries. [Alexei Kosut] + + *) Update to the icon set, and apache_pb (Powered by Apache!) logo + added [Alexei Kosut] + + *) Added an extra variable for use in mod_include: FILE_OWNER + which contains the owner of the file. [Nick Williams] + + *) Adds support for Keep-Alive persistent connections, as defined + in the HTTP/1.1 draft and implemented by several existing browsers + and servers. [Alexei Kosut and Rob Hartill] + + *) "HostnameLookups" is a new httpd.conf setting (on/off) to make obsolete + -DMINIMAL_DNS and enable name lookups to be switched + on/off without recompiling Apache. [Rob Hartill] + + *) Allows Redirect entries in .htaccess files, allowing users to + add their own redirects without using CGI scripts, asis files, or + access to the main server conf file. [Alexei Kosut] + + *) Scoreboard monitor program (C version) in support/ directory + [Jim Jagielski] + + *) A module to implement a caching HTTP and FTP proxy server + [David Robinson] + + *) Adds OS/2 support [Garey Smiley] + + *) Add Listen directive, causing Apache to bind to the address and + port specified. Allow <VirtualHost *>, <VirtualHost *:port>, + VirtualHost addr:*> syntaxes [David Robinson] + + *) Allow anonymous HTTP logins [Dirk van Gulik] + + *) Authentication using Berkeley DB [Andrew Cohen] + + *) Authentication using mSQL [Dirk van Gulik] + + *) Emulate the CERN HTTPD metafile semantics, allowing the ability + to manipulate HTTP headers for individual files. [Andrew Wilson[ + +Changes with Apache 1.0.3 + + *) Internal redirects which occur in mod_dir.c now preserve the + query portion of a request (the bit after the question mark). + [Adam Sussman] + + *) Escape active characters '<', '>' and '&' in html output in + directory listings, error messages and redirection links. + [David Robinson] + + *) Apache will now work with LynxOS 2.3 and later [Steven Watt] + + *) Fix for POSIX compliance in waiting for processes in alloc.c. + [Nick Williams] + + *) setsockopt no longer takes a const declared argument [Martijn Koster] + + *) Reset timeout timer after each successful fwrite() to the network. + This patch adds a reset_timeout() procedure that is called by + send_fd() to reset the timeout ever time data is written to the net. + [Nathan Schrenk] + + *) timeout() signal handler now checks for SIGPIPE and reports + lost connections in a more user friendly way. [Rob Hartill] + + *) Location of the "scoreboard" file which used to live in /tmp is + now configurable (for OSes that can't use mmap) via ScoreBoardFile + which works similar to PidFile (in httpd.conf) [Rob Hartill] + + *) Include sys/resource.h in the correct place for SunOS4 [Samerr Parekh] + + *) the pstrcat call in mod_cookies.c didn't have an ending NULL, + which caused a SEGV with cookies enabled + + *) Output warning when MinSpareServers is set to <= 0 and change it to 1 + [Rob Hartill] + + *) Log the UNIX textual error returned by some system calls, in + particular errors from accept() [David Robinson] + + *) Add strerror function to util.c for SunOS4 [Randy Terbush] + +Changes with Apache 1.0.2 + + *) patch to get Apache compiled on UnixWare 2.x, recommended as + a temporary measure, pending rewrite of rfc931.c. [Chuck Murcko] + + *) Fix get_basic_auth_pw() to set the auth_type of the request. + [David Robinson] + + *) past changes to http_config.c to only use the + setrlimit function on systems defining RLIMIT_NOFILE + broke the feature on SUNOS4. Now defines HAVE_RESOURCE + for SUNOS and prototypes the needed functions. + + *) Remove uses of MAX_STRING_LEN/HUGE_STRING_LEN from several routines. + [David Robinson] + + *) Fix use of pointer to scratch memory. [Cliff Skolnick] + + *) Merge multiple headers from CGI scripts instead of taking last + one. [David Robinson] + + *) Add support for SCO 5. [Ben Laurie] + +Changes with Apache 1.0.1 + + *) Silence mod_log_referer and mod_log_agent if not configured + [Randy Terbush] + + *) Recursive includes can occur if the client supplies PATH_INFO data + and the server provider uses relative links; as file.html + relative to /doc.shtml/pathinfo is /doc.shtml/file.html. [David Robinson] + + *) The replacement for initgroups() did not call {set,end}grent(). This + had two implications: if anything else used getgrent(), then + initgroups() would fail, and it was consuming a file descriptor. + [Ben Laurie] + + *) On heavily loaded servers it was possible for the scoreboard to get + out of sync with reality, as a result of a race condition. + The observed symptoms are far more Apaches running than should + be, and heavy system loads, generally followed by catastrophic + system failure. [Ben Laurie] + + *) Fix typo in licence. [David Robinson] + +Changes with Apache 1.0.0 + + *) 'dbmmanage' script now takes fourth command-line argument, which is + group(s) of the user, if given. [Rob Hartill] + + *) httpd_monitor Perl utility script added to "support" directory; + lets you know what your active server processes are up to. [Known + to work on SunOS and HP-UX at least]. [Rob Hartill] + + *) Passes TZ environment variable to child processes if set on startup + [ David Robinson ] + + *) Attempting to retrieve the URI /foo/bar/zot.map (an imagemap request + with *no* QUERY_STRING) returns the default URL, rather than a core + dump. [Mark Cox] + + *) Attempting to configure auth without any "requires" lines should now + be equivalent to "require valid-user", rather than a core dump. + [Patch supplied by J. Katzman] + + *) Getparents() routine fixed to handle some awkward corner cases better; + in particular, '/./' is eliminated from incoming URIs. Also fixed the + no2slash() utility routine to reduce any number of consecutive slashes + to one. These changes keep "trick" URIs from avoiding <Directory> + checks. [David Robinson] + + *) Allowed any number of '/' characters to match a single '/' when + checking for Aliases; this keeps certain ScriptAlias directives + from getting bypassed. [Robert Thau] + + *) DBM group code now checks all groups, not just the first [Rob Hartill] + + *) "allow from good.com" no longer allows clients from nogood.com to + connect; formerly this was allowed because the terminal substrings + do match. [Robert Thau] + + *) Transactions interrupted in the middle of processing some include + directive are now logged correctly [Robert Thau] + + *) Cleaned up Configure script so it doesn't abuse EXTRA_LIBS to specify + host-specific default libraries. [Ben Laurie] + + *) Cleaned up http_main code a bit; IdentityCheck may now work better on + systems with virtual hosts. [David Robinson] + + *) Improved installation instructions in top-level README [Brian Behlendorf] + +Changes with Apache 0.8.16 + + *) New man page for 'httpd' added to support directory [David Robinson] + + *) .htgroup files can have more than one line giving members for a + given group (each must have the group name in front), for NCSA + back-compatibility [Robert Thau] + + *) Mutual exclusion around accept() is on by default for SVR4 systems + generally, since they generally can't handle multiple processes in + accept() on the same socket. This should cure flaky behavior on + a lot of those systems. [David Robinson] + + *) AddType, AddEncoding, and AddLanguage directives take multiple + extensions on a single command line [David Robinson] + + *) UserDir can be disabled for a given virtual host by saying + "UserDir disabled" in the <VirtualHost> section --- it was a bug + that this didn't work. [David Robinson] + + *) Compiles on QNX [Ben Laurie] + + *) Corrected parsing of ctime time format [David Robinson] + + *) httpd does a perror() before exiting if it can't log its pid + to the PidFile, to make diagnosing the error a bit easier. + [David Robinson] + + *) <!--#include file="..."--> can no longer include files in the + parent directory, for NCSA back-compatibility. [David Robinson] + + *) '~' is *not* escaped in URIs generated for directory listings + [Roy Fielding] + + *) Eliminated compiler warning in the imagemap module [Randy Terbush] + + *) Fixed bug involving handling URIs with escaped %-characters + in redirects [David Robinson] + +Changes with Apache 0.8.15 + + *) Switched to new, simpler license + + *) Eliminated core dumps with improperly formatted DBM group files [Mark Cox] + + *) Don't allow requests for ordinary files to have PATH_INFO [Ben Laurie] + + *) Reject paths containing %-escaped '%' or null characters [David Robinson] + + *) Correctly handles internal redirects to files with names containing '%' + [David Robinson] + + *) Repunctuated some error messages [Aram Mirzadeh, Andrew Wilson] + + *) Use geteuid() rather than getuid() to see if we have root privilege, + so that server correctly resets privilege if run setuid root. [Andrew + Wilson] + + *) Handle ftp: and telnet: URLs correctly in imagemaps (built-in module) + [Randy Terbush] + + *) Fix relative URLs in imagemap files [Randy Terbush] + + *) Somewhat better fix for the old "Alias /foo/ /bar/" business + [David Robinson] + + *) Don't repeatedly open the ErrorLog if a bunch of <VirtualHost> + entries all name the same one. [David Robinson] + + *) Fix directory listings with filenames containing unusual characters + [David Robinson] + + *) Better URI-escaping for generated URIs in directories with filenames + containing unusual characters [Ben Laurie] + + *) Fixed potential FILE* leak in http_main.c [Ben Laurie] + + *) Unblock alarms on error return from spawn_child() [David Robinson] + + *) Sample Config files have extra note for SCO users [Ben Laurie] + + *) Configuration has note for HP-UX users [Rob Hartill] + + *) Eliminated some bogus Linux-only #defines in conf.h [Aram Mirzadeh] + + *) Nuked bogus #define in httpd.h [David Robinson] + + *) Better test for whether a system has setrlimit() [David Robinson] + + *) Calls update_child_status() after reopen_scoreboard() [David Robinson] + + *) Doesn't send itself SIGHUP on startup when run in the -X debug-only mode + [Ben Laurie] + +Changes with Apache 0.8.14 + + *) Compiles on SCO ODT 3.0 [Ben Laurie] + + *) AddDescription works (better) [Ben Laurie] + + *) Leaves an intelligible error diagnostic when it can't set group + privileges on standalone startup [Andrew Wilson] + + *) Compiles on NeXT again --- the 0.8.13 RLIMIT patch was failing on + that machine, which claims to be BSD but does not support RLIMIT. + [Randy Terbush] + + *) gcc -Wall no longer complains about an unused variable when util.c + is compiled with -DMINIMAL_DNS [Andrew Wilson] + + *) Nuked another compiler warning for -Wall on Linux [Aram Mirzadeh] + +Changes with Apache 0.8.13 + + *) Make IndexIgnore *work* (ooops) [Jarkko Torppa] + + *) Have built-in imagemap code recognize & honor Point directive [James + Cloos] + + *) Generate cleaner directory listings in directories with a mix of + long and short filenames [Rob Hartill] + + *) Properly initialize dynamically loaded modules [Royston Shufflebotham] + + *) Properly default ServerName for virtual servers [Robert Thau] + + *) Rationalize handling of BSD in conf.h and elsewhere [Randy Terbush, + Paul Richards and a cast of thousands...] + + *) On self-identified BSD systems (we don't try to guess any more), + allocate a few extra file descriptors per virtual host with setrlimit, + if we can, to avoid running out. [Randy Terbush] + + *) Write 22-character lock file name into buffer with enough space + on startup [Konstantin Olchanski] + + *) Use archaic setpgrp() interface on NeXT, which requires it [Brian + Pinkerton] + + *) Suppress -Wall warning by casting const away in util.c [Aram Mirzadeh] + + *) Suppress -Wall warning by initializing variable in negotation code + [Tobias Weingartner] + +Changes with Apache 0.8.12 + + *) Doesn't pause three seconds after including a CGI script which is + too slow to die off (this is done by not even trying to kill off + subprocesses, including the SIGTERM/pause/SIGKILL routine, until + after the entire document has been processed). [Robert Thau] + + *) Doesn't do SSI if Options Includes is off. (Ooops). [David Robinson] + + *) Options IncludesNoExec allows inclusion of at least text/* [Roy Fielding] + + *) Allows .htaccess files to override <Directory> sections naming the + same directory [David Robinson] + + *) Removed an efficiency hack in sub_req_lookup_uri which was + causing certain extremely marginal cases (e.g., ScriptAlias of a + *particular* index.html file) to fail. [David Robinson] + + *) Doesn't log an error when the requested URI requires + authentication, but no auth header line was supplied by the + client; this is a normal condition (the client doesn't no auth is + needed here yet). [Robert Thau] + + *) Behaves more sanely when the name server loses its mind [Sean Welch] + + *) RFC931 code compiles cleanly on old BSDI releases [Randy Terbush] + + *) RFC931 code no longer passes out name of prior clients on current + requests if the current request came from a server that doesn't + do RFC931. [David Robinson] + + *) Configuration script accepts "Module" lines with trailing whitespace. + [Robert Thau] + + *) Cleaned up compiler warning from mod_access.c [Robert Thau] + + *) Cleaned up comments in mod_cgi.c [Robert Thau] + +Changes with Apache 0.8.11 + + *) Wildcard <Directory> specifications work. [Robert Thau] + + *) Doesn't loop for buggy CGI on Solaris [Cliff Skolnick] + + *) Symlink checks (FollowSymLinks off, or SymLinkIfOwnerMatch) always check + the file being requested itself, in addition to the directories leading + up to it. [Robert Thau] + + *) Logs access failures due to symlink checks or invalid client address + in the error log [Roy Fielding, Robert Thau] + + *) Symlink checks deal correctly with systems where lstat of + "/path/to/some/link/" follows the link. [Thau, Fielding] + + *) Doesn't reset DirectoryIndex to 'index.html' when + other directory options are set in a .htaccess file. [Robert Thau] + + *) Clarifed init code and nuked bogus warning in mod_access.c + [Florent Guillaume] + + *) Corrected several directives in sample srm.conf + --- includes corrections to directory indexing icon-related directives + (using unknown.gif rather than unknown.xbm as the DefaultIcon, doing + icons for encodings right, and turning on AddEncoding by default). + [Roy Fielding] + + *) Corrected descriptions of args to AddIcon and AddAlt in command table + [James Cloos] + + *) INSTALL & README mention "contributed modules" directory [Brian + Behlendorf] + + *) Fixed English in the license language... "for for" --> "for". + [Roy Fielding] + + *) Fixed ScriptAlias/Alias interaction by moving ScriptAlias handling to + mod_alias.c, merging it almost completely with handling of Alias, and + adding a 'notes' field to the request_rec which allows the CGI module + to discover whether the Alias module has put this request through + ScriptAlias (which it needs to know for back-combatibility, as the old + NCSA code did not check Options ExecCGI in ScriptAliased directories). + [Robert Thau] + + +Changes with Apache 0.8.10 + + *) AllowOverride applies to the named directory, and not just + subdirectories. [David Robinson] + + *) Do locking for accept() exclusion (on systems that need it) + using a special file created for the purpose in /usr/tmp, and + not the error log; using the error log causes real problems + if it's NFS-mounted; this is known to be the cause of a whole + lot of "server hang" problems with Solaris. [David Robinson; + thanks to Merten Schumann for help diagnosing the problem]. + +Changes with Apache 0.8.9 + + *) Compiles with -DMAXIMUM_DNS ---- ooops! [Henrik Mortensen] + + *) Nested includes see environment variables of the including document, + for NCSA bug-compatibility (some sites have standard footer includes + which try to print out the last-modified date). [Eric Hagberg/Robert + Thau] + + *) <!--exec cgi="/some/uri/here"--> always treats the item named by the + URI as a CGI script, even if it would have been treated as something + else if requested directly, for NCSA back-combatibility. (Note that + this means that people who know the name of the script can see the + code just by asking for it). [Robert Thau] + + *) New version of dbmmanage script included in support directory as + dbmmanage.new. + + *) Check if scoreboard file couldn't be opened, and say so, rather + then going insane [David Robinson] + + *) POST to CGI works on A/UX [Jim Jaglieski] + + *) AddIcon and AddAlt commands work properly [Rob Hartill] + + *) NCSA server push works properly --- the Arena bug compatibility + workaround, which broke it, is gone (use -DARENA_BUG_WORKAROUND + if you still want the workaround). [Rob Hartill] + + *) If client didn't submit any Accept-encodings, ignore encodings in + content negotiation. (NB this will all have to be reworked anyway + for the new HTTP draft). [Florent Guillaume] + + *) Don't dump core when trying to log timed-out requests [Jim Jaglieski] + + *) Really honor CacheNegotiatedDocs [Florent Guillaume] + + *) Give Redirect priority over Alias, for NCSA bug compatibility + [David Robinson] + + *) Correctly set PATH_TRANSLATED in all cases from <!--#exec cmd=""-->, + paralleling earlier bug fix for CGI [David Robinson] + + *) If DBM auth is improperly configured, report a server error and don't + dump core. + + *) Deleted FCNTL_SERIALIZED_ACCEPTS from conf.h entry for A/UX; + it seems to work well enough without it (even in a 10 hits/sec + workout), and the overhead for the locking under A/UX is + alarmingly high (though it is very low on other systems). + [Eric Hagberg] + + *) Fixed portability problems with mod_cookies.c [Cliff Skolnick] + + *) Further de-Berklize mod_cookies.c; change the bogus #include. [Brian + Behlendorf/Eric Hagberg] + + *) More improvements to default Configuration for A/UX [Jim Jaglieski] + + *) Compiles clean on NEXT [Rob Hartill] + + *) Compiles clean on SGI [Robert Thau] + +Changes with Apache 0.8.8 + + *) SunOS library prototypes now never included unless explicitly + requested in the configuration (via -DSUNOS_LIB_PROTOTYPES); + people using GNU libc on SunOS are screwed by prototypes for the + standard library. + + (Those who wish to compile clean with gcc -Wall on a standard + SunOS setup need the prototypes, and may obtain them using + -DSUNOS_LIB_PROTOTYPES. Those wishing to use -Wall on a system + with nonstandard libraries are presumably competent to make their + own arrangements). + + *) Strips trailing '/' characters off both args to the Alias command, + to make 'Alias /foo/ /bar/' work. + +Changes with Apache 0.8.7 + + *) Don't hang when restarting with a child from 'TransferLog "|..."' running + [reported by David Robinson] + + *) Compiles clean on OSF/1 [David Robinson] + + *) Added some of the more recent significant changes (AddLanguage stuff, + experimental LogFormat support) to CHANGES file in distribution root + directory + +Changes with Apache 0.8.6 + + *) Deleted Netscape reload workaround --- it's in violation of HTTP specs. + (If you actually wanted a conditional GET which bypassed the cache, you + couldn't get it). [Reported by Roy Fielding] + + *) Properly terminate headers on '304 Not Modified' replies to conditional + GETs --- no browser we can find cares much, but the CERN proxy chokes. + [Reported by Cliff Skolnick; fix discovered independently by Rob Hartill] + + *) httpd -v doesn't call itself "Shambhala". [Reported by Chuck Murcko] + + *) SunOS lib-function prototypes in conf.h conditionalized on __GNUC__, + not __SUNPRO_C (they're needed to quiet gcc -Wall, but acc chokes on 'em, + and older versions don't set the __SUNPRO_C preprocessor variable). On + all other systems, these are never used anyway. [Reported by Mark Cox]. + + *) Scoreboard file (/tmp/htstatus.*) no longer publically writable. + +Changes with Apache 0.8.5 + + *) Added last-minute configurable log experiment, as optional module + + *) Correctly set r->bytes_sent for HTTP/0.9 requests, so they get logged + properly. (One-line fix to http_protocol.c). + + *) Work around bogus behavior when reloading from Netscape. + It's Netscape's bug --- for some reason they expect a request with + If-modified-since: to not function as a conditional GET if it also + comes with Pragma: no-cache, which is way out of line with the HTTP + spec (according to Roy Fielding, the redactor). + + *) Added parameter to set maximum number of server processes. + + *) Added patches to make it work on A/UX. A/UX is *weird*. [Eric Hagberg] + + *) IdentityCheck bugfix [Chuck Murcko]. + + *) Corrected cgi-src/Makefile entry for new imagemap script. [Alexei Kosut] + + *) More sample config file corrections; add extension to AddType for + *.asis, move AddType generic description to its proper place, and + fix miscellaneous typos. [ Alexei Kosut ] + + *) Deleted the *other* reference to the regents from the Berkeley + legal disclaimer (everyplace). + + *) Nuked Shambhala name from src/README; had already cleaned it out + of everywhere else. + +Changes with Apache 0.8.4 + + *) Changes to server-pool management parms --- renamed current + StartServers to MinSpareServers, created separate StartServers + parameter which means what it says, and renamed MaxServers to + MaxSpareServers (though the old name still works, for NCSA 1.4 + back-combatibility). The old names were generally regarded as + too confusing. Also altered "docs" in sample config files. + + *) More improvements to default config files --- + sample directives (commented out) for XBitHack, BindAddress, + CacheNegotiatedDocs, VirtualHost; decent set of AddLanguage + defaults, AddTypes for send-as-is and imagemap magic types, and + improvements to samples for DirectoryIndex [Alexei Kosut] + + *) Yet more improvements to default config files --- changes to + Alexei's sample AddLanguage directives, and sample LanguagePriority + [ Florent Guillaume ] + + *) Set config file locations properly if not set in httpd.conf + [ David Robinson ] + + *) Don't escape URIs in internal redirects multiple times; don't + do that when translating PATH_INFO to PATH_TRANSLATED either. + [ David Robinson ] + + *) Corrected spelling of "Required" in 401 error reports [Andrew Wilson] + +Changes with Apache 0.8.3 + + *) Edited distribution README to *briefly* summarize installation + procedures, and give a pointer to the INSTALL file in the src/ + directory. + + *) Upgraded imagemap script in cgi-bin to 1.8 version from more + recent NCSA distributions. + + *) Bug fix to previous bug fix --- if .htaccess file and <Directory> + exist for the same directory, use both and don't segfault. [Reported + by David Robinson] + + *) Proper makefile dependencies [David Robinson] + + *) Note (re)starts in error log --- reported by Rob Hartill. + + *) Only call no2slash() after get_path_info() has been done, to + preserve multiple slashes in the PATH_INFO [NCSA compatibility, + reported by Andrew Wilson, though this one is probably a real bug] + + *) Fixed mod_imap.c --- relative paths with base_uri referer don't + dump core when Referer is not supplied. [Randy Terbush] + + *) Lightly edited sample config files to refer people to our documentation + instead of NCSA's, and to list Rob McCool as *original* author (also + deleted his old, and no doubt non-functional email address). Would be + nice to have examples of new features... + +Changes with Apache 0.8.2 + + *) Added AddLanuage code [Florent Guillaume] + + *) Don't say "access forbidden" when a CGI script is not found. [Mark Cox] + + *) All sorts of problems when MultiViews finds a directory. It would + be nice if mod_dir.c was robust enough to handle that, but for now, + just punt. [reported by Brian Behlendorf] + + *) Wait for all children on restart, to make sure that the old socket + is gone and we can reopen it. [reported by Randy Terbush] + + *) Imagemap module is enabled in default Configuration + + *) RefererLog and UserAgentLog modules properly default the logfile + [Randy Terbush] + + *) Mark Cox's mod_cookies added to the distribution as an optional + module (commented out in the default Configuration, and noted as + an experiment, along with mod_dld). [Mark Cox] + + *) Compiles on Ultrix (a continuing battle...). [Robert Thau] + + *) Fixed nasty bug in SIGTERM handling [reported by Randy Terbush] + + *) Changed "Shambhala" to "Apache" in API docs. [Robert Thau] + + *) Added new, toothier legal disclaimer. [Robert Thau; copied from BSD + license] + +Changes with Apache 0.8.1 + + *) New imagemap module [Randy Terbush] + + *) Replacement referer log module with NCSA-compatible RefererIgnore + [Matthew Gray again] + + *) Don't mung directory listings with very long filenames. + [Florent Guillaume] + +Changes with Apache 0.8.0 (nee Shambhala 0.6.2): + + *) New config script. See INSTALL for info. [Robert Thau] + + *) Scoreboard mechanism for regulating the number of extant server + processes. MaxServers and StartServers defaults are the same as + for NCSA, but the meanings are slightly different. (Actually, + I should probably lower the MaxServers default to 10). + + Before asking for a new connection, each server process checks + the number of other servers which are also waiting for a + connection. If there are more than MaxServers, it quietly dies + off. Conversely, every second, the root, or caretaker, process + looks to see how many servers are waiting for a new connection; + if there are fewer than StartServers, it starts a new one. This + does not depend on the number of server processes already extant. + The accounting is arranged through a "scoreboard" file, named + /tmp/htstatus.*, on which each process has an independant file + descriptor (they need to seek without interference). + + The end effect is that MaxServers is the maximum number of + servers on an *inactive* server machine, but more will be forked + off to handle unusually heavy loads (or unusually slow clients); + these will die off when they are no longer needed --- without + reverting to the overhead of full forking operation. There is a + hard maximum of 150 server processes compiled in, largely to + avoid forking out of control and dragging the machine down. + (This is arguably too high). + + In my server endurance tests, this mechanism did not appear to + impose any significant overhead, even after I forced it to put the + scoreboard file on a normal filesystem (which might have more + overhead than tmpfs). [Robert Thau] + + *) Set HTTP_FOO variables for SSI <!--#exec cmd-->s, not just CGI scripts. + [Cliff Skolnick] + + *) Read .htaccess files even in directory with <Directory> section. + (Former incompatibility noted on mailing list, now fixed). [Robert + Thau] + + *) "HEAD /" gives the client a "Bad Request" error message, rather + than trying to send no body *and* no headers. [Cliff Skolnick]. + + *) Don't produce double error reports for some very obscure cases + mainly involving auth configuration (the "all modules decline to + handle" case which is a sure sign of a server bug in most cases, + but also happens when authentication is badly misconfigured). + [Robert Thau] + + *) Moved FCNTL_SERIALIZED_ACCEPT defines into conf.h (that's what + it's *for*, and this sort of thing really shouldn't be cluttering + up the Makefile). [Robert Thau] + + *) Incidental code cleanups in http_main.c --- stop dragging + sa_client around; just declare it where used. [Robert Thau] + + *) Another acc-related fix. (It doesn't like const char + in some places...). [Mark Cox] + +Changes with 0.6.1 + + *) Fixed auth_name-related typos in http_core.c [Brian Behlendorf] + Also, fixed auth typo in http_protocol.c unmasked by this fix. + + *) Compiles clean with acc on SunOS [Paul Sutton] + + *) Reordered modules in modules.c so that Redirect takes priority + over ScriptAlias, for NCSA bug-compatibility [Rob Hartill] --- + believe it or not, he has an actual site with a ScriptAlias and + a Redirect declared for the *exact same directory*. Even *my* + compatibility fetish wouldn't motivate me to fix this if the fix + required any effort, but it doesn't, so what the hey. + + *) Fixed to properly default several server_rec fields for virtual + servers from the corresponding fields in the main server_rec. + [Cliff Skolnick --- 'port' was a particular irritant]. + + *) No longer kills off nph- child processes before they are + finished sending output. [Matthew Gray] + +Changes with 0.6.0 + + *) Two styles of timeout --- hard and soft. soft_timeout()s just put + the connection to the client in an "aborted" state, but otherwise + allow whatever handlers are running to clean up. hard_timeout()s + abort the request in progress completely; anything not tied to some + resource pool cleanup will leak. They're still around because I + haven't yet come up with a more elegant way of handling + timeouts when talking to something that isn't the client. The + default_handler and the dir_handler now use soft timeouts, largely + so I can test the feature. [Robert Thau] + + *) TransferLog "| my_postprocessor ..." seems to be there. Note that + the case of log handlers dying prematurely is probably handled VERY + gracelessly at this point, and if the logger stops reading input, + the server will hang. (It is known to correctly restart the + logging process on server restart; this is (should be!) going through + the same SIGTERM/pause/SIGKILL routine used to ding an errant CGI + script). [Robert Thau] + + *) asis files supported (new module). [Robert Thau] + + *) IdentityCheck code is compiled in, but has not been tested. (I + don't know anyone who runs identd). [Robert Thau] + + *) PATH_INFO and PATH_TRANSLATED are not set unless some real PATH_INFO + came in with the request, for NCSA bug-compatibility. [Robert Thau] + + *) Don't leak the DIR * on HEAD request for a directory. [Robert Thau] + + *) Deleted the block_alarms() stuff from dbm_auth; no longer necessary, + as timeouts are not in scope. [Robert Thau] + + *) quoted-string args in config files now handled correctly (doesn't drop + the last character). [Robert Thau; reported by Randy Terbush] + + *) Fixed silly typo in http_main.c which was suddenly fatal in HP-UX. + How the hell did it ever work? [Robert Thau; reported by Rob Hartill] + + *) mod_core.c --- default_type returns DEFAULT_TYPE (the compile-time + default default type); the former default default behavior when all + type-checkers defaulted had been a core dump. [Paul Sutton] + + *) Copy filenames out of the struct dirent when indexing + directories. (On Linux, readdir() returns a pointer to the same + memory area every time). Fix is in mod_dir.c. [Paul Sutton] + +Changes with 0.5.3 [not released] + + *) Default response handler notes "file not found" in the error log, + if the file was not found. [Cliff Skolnick]. + + *) Another Cliff bug --- "GET /~user" now properly redirects (the userdir + code no longer sets up bogus PATH_INFO which fakes out the directory + handler). [Cliff Skolnick] + +Changes with 0.5.2 + + *) Changes to http_main.c --- root server no longer plays silly + games with SIGCHLD, and so now detects and replaces dying + children. Child processes just die on SIGTERM, without taking + the whole process group with them. Potential problem --- if any + child process refuses to die, we hang in restart. + MaxRequestsPerChild may still not work, but it certainly works + better than it did before this! [Robert Thau] + + *) mod_dir.c bug fixes: ReadmeName and HeaderName + work (or work better, at least); over-long description lines + properly terminated. [Mark Cox] + + *) http_request.c now calls unescape_url() more places where it + should [Paul Sutton]. + + *) More directory handling bugs (reported by Cox) + Parent Directory link is now set correctly. [Robert Thau] + +Changes with 0.5.1: [Hopefully complete] + + *) Generalized cleanup interface in alloc.c --- any function can be + registered with alloc.c as a cleanup for a resource pool; + tracking of files and file descriptors has been reimplemented in + terms of this interface, so I can give it some sort of a test. + [Robert Thau] + + *) More changes in alloc.c --- new cleanup_for_exec() function, + which tracks down and closes all file descriptors which have been + registered with the alloc.c machinery before the server exec()s a + child process for CGI or <!--#exec-->. CGI children now get + started with exactly three file descriptors open. Hopefully, + this cures the problem Rob H. was having with overly persistent + CGI connections. [Robert Thau] + + *) Mutual exclusion around the accept() in child_main() --- this is + required on at least SGI, Solaris and Linux, and is #ifdef'ed in + by default on those systems only (-DFCNTL_SERIALIZED_ACCEPT). + This uses fcntl(F_SETLK,...) on the error log descriptor because + flock() on that descriptor won't work on systems which have BSD + flock() semantics, including (I think) Linux 1.3 and Solaris. + + This does work on SunOS (when the server is idle, only one + process in the pool is waiting on accept()); it *ought* to work + on the other systems. [Robert Thau] + + *) FreeBSD and BSDI portability tweaks [Chuck Murcko] + + *) sizeof(*sa_client) bugfix from [Rob Hartill] + + *) pstrdup(..., NULL) returns NULL, [Randy Terbush] + + *) block_alarms() to avoid leaking the DBM* in dbm auth (this should + be unnecessary if I go to the revised timeout-handling scheme). + [Robert Thau] + + *) For NCSA bug-compatibility, set QUERY_STRING env var (to a null + string) even if none came in with the request. [Robert Thau] + + *) CHANGES file added to distribution ;-). diff --git a/RELEASE_1_1_X/src/Configuration.tmpl b/RELEASE_1_1_X/src/Configuration.tmpl new file mode 100644 index 00000000000..bd29647b3da --- /dev/null +++ b/RELEASE_1_1_X/src/Configuration.tmpl @@ -0,0 +1,272 @@ +# Config file for the Apache httpd. + +# Configuration.tmpl is the template for Configuration. Configuration should +# be edited to select system type. Configuration.tmpl should only be changed +# when a new system or module is added, or an existing one modified. + +# There are three types of lines here: + +# '#' comments, distinguished by having a '#' as the first non-blank character +# +# Lines which set a Make option --- these are simply copied into the Makefile +# +# Module selection lines, distinguished by having 'Module' at the front. +# These list the configured modules, in priority order (highest priority +# first). They're down at the bottom. + +# First, ordinary compile-time configuration. + +# What to call the compiler: For normal machines with ANSI compilers +# CC= cc +# For Suns or other non-ANSI platforms. Please make sure your gcc is +# 2.0 or later, as 1.40 seems to create bad code for the Sun 4. +CC= gcc + +# CFLAGS, compile flags. + +# -DMINIMAL_DNS is now obsolete. Use httpd.conf settings of +# HostnameLookups on +# or +# HostnameLookups off +# +# If you want to have more secure hostname resolution at the cost of some +# performance, use -DMAXIMUM_DNS. +# If you want setting the xbit of a file to cause it to be treated as +# server-included HTML (unless it is a CGI script), say -DXBITHACK. Note +# that this is a run-time option, per-directory, either way (via the XBITHACK +# command); this option only sets the default. + +# If you find that your OS can't cope with mmap (compiles OKAY but refuses +# to run and moans "httpd: Could not mmap memory" .. or similar) try +# disabling use of shared memory for process management (scoreboard with +# -DNO_MMAP + +# Status Instrumentation +# In order for the status module to obtain full statistics Apache must +# be modified to keep track of various information. This is not +# turned on by default. In order to enable full status details add -DSTATUS +# to the end of the CFLAGS line below. + +# Using SOCKS +# Apache can be compiled to work over a SOCKS firewall by +# adding the following string to your CFLAGS define: +# +# -Dconnect=Rconnect -Dselect=Rselect -Dgethostbyname=Rgethostbyname +# +# and by adding the following to the EXTRA_LIBS define: +# +# -L/usr/local/lib -lsocks +# +# making sure that -L points to wherever you've put libsocks.a. + +# [Some other former Apache compile-time options are now treated differently; +# the virtual host code is always present; DBM auth is an optional module, and +# may be configured out by changing the module config below, though it still +# defaults in. Note that this config file does not include DBM auth by +# default --- configure it in below if you need it]. + +CFLAGS= -O2 + +# Place here any flags you may need upon linking, such as a flag to +# prevent dynamic linking (if desired) +LFLAGS= + +# Place here any extra libraries you may need to link to. +# -lndbm is commonly required for DBM auth, if that is configured in. +EXTRA_LIBS= + +# AUX_CFLAGS are system-specific control flags. +# NOTE: IF YOU DO NOT CHOOSE ONE OF THESE, EDIT httpd.h AND CHOOSE +# SETTINGS FOR THE SYSTEM FLAGS. IF YOU DON'T, BAD THINGS WILL HAPPEN. + +# For SunOS 4 +#AUX_CFLAGS= -DSUNOS4 +# For Solaris 2. +#AUX_CFLAGS= -DSOLARIS2 +#AUX_LIBS= -lsocket -lnsl +# For SGI IRIX. Use the AUX_LIBS line if you're using NIS and want +# user-supported directories +#AUX_CFLAGS= -DIRIX +#AUX_LIBS= -lsun +# For HP-UX n.b. if you use the paid-for HP CC compiler, use flag -Ae +#AUX_CFLAGS= -DHPUX +# For AIX +#AUX_CFLAGS= -DAIX -U__STR__ +# For Ultrix +#AUX_CFLAGS= -DULTRIX +# For DEC OSF/1 +#AUX_CFLAGS= -DOSF1 +# For NeXT +#AUX_CFLAGS= -DNEXT +# For Sequent +#AUX_CFLAGS= -DSEQUENT +# For Linux -m486 ONLY IF YOU HAVE 486 BINARY SUPPORT IN KERNEL +#AUX_CFLAGS= -DLINUX +# For A/UX +#AUX_CFLAGS= -DAUX -D_POSIX_SOURCE +#AUX_LIBS= -lposix -lbsd -s +# For SCO ODT 3 +# libcrypt_i available from sosco.sco.com, files /SLS/lng225b.Z and +# /SLS/lng225b.ltr.Z +# the -Oe option causes cc to die compiling mod_imap (using 3.0.0a of the dev sys) +#CFLAGS= -Oacgiltz +#AUX_CFLAGS= -DSCO +#AUX_LIBS= -lPW -lsocket -lmalloc -lcrypt_i +# For SCO OpenServer Release 5 +# -K noinline is needed to work around an optimiser bug which appears in +# http_bprintf.c +#AUX_CFLAGS= -DSCO5 +#AUX_LIBS=-lsocket -lmalloc -lprot +#BROKEN_BPRINTF_FLAGS=-K noinline +# For SVR4 +# Some SVR4 implementations will require SO_LINGER option to be set in order +# to guarantee buffer flushes. Dell, Esix, and UnixWare are a few of these. +# Use -DNEED_LINGER in addition to other AUX_CFLAGS for these. +#AUX_CFLAGS= -DSVR4 +#AUX_LIBS= -lsocket -lnsl -lc +# For UnixWare 2.x, no longer just SVR4 (sigh) - use cc, not gcc +# AUX_LIBS= -lsocket -lnsl -lcrypt +# For Amdahl UTS 2.1 +# -Xa enables ANSI mode, -eft is expanded types +#AUX_CFLAGS= -Xa -eft -DUTS21 +#AUX_LIBS= -lsocket -lbsd -la +# For HP/Apollo Domain/OS +#AUX_CFLAGS= -DAPOLLO +# For NetBSD/FreeBSD/BSDI 2.x +# -m486 only if you are running on Intel 486/586 +#AUX_CFLAGS= -m486 +# BSDI doesn't need -lcrypt +#AUX_LIBS= -lcrypt +# For QNX +#AUX_CFLAGS= -DQNX +#AUX_LFLAGS= -N 0x20000 +# For LynxOS +#AUX_CFLAGS= -DLYNXOS +#EXTRA_LIBS=-lbsd -ldes -lc_p +# For DG/UX 5.4 +#AUX_CFLAGS= -DDGUX +#AUX_LIBS= + +# For EMX OS/2 port +#AUX_CFLAGS= -Zbsd-signals -Zbin-files +#-DNO_KILLPG -DNEED_STRCASECMP -DNO_SETSID +#-g +#AUX_LIBS= -lsocket -llibufc -lgdbm -lbsd + +################################################################ +# Module configuration +# +# Modules are listed in reverse priority order --- the ones that come +# later can override the behavior of those that come earlier. This +# can have visible effects; for instance, if UserDir followed Alias, +# you couldn't alias out a particular user's home directory. + +# The configuration below is what we consider a decent default +# configuration. If you want the functionality provided by a particular +# module, remove the "#" sign at the beginning of the line. But remember, +# the more modules you compile into the server, the larger the executable +# is and the more memory it will take, so if you are unlikely to use the +# functionality of a particular module you might wish to leave it out. + +## Basic modules (i.e., generally useful stuff that works everyplace): +## You should probably not comment out any of these unless you know what it +## does and you know you won't need it. + +Module mime_module mod_mime.o +Module access_module mod_access.o +Module auth_module mod_auth.o +Module negotiation_module mod_negotiation.o +Module includes_module mod_include.o +Module dir_module mod_dir.o +Module cgi_module mod_cgi.o +Module userdir_module mod_userdir.o +Module alias_module mod_alias.o +Module env_module mod_env.o +Module common_log_module mod_log_common.o + +## The asis module implemented ".asis" file types, which allow the embedding +## of HTTP headers at the beginning of the document. mod_imap handles internal +## imagemaps (no more cgi-bin/imagemap/!). mod_actions is used to specify +## CGI scripts which act as "handlers" for particular files, for example to +## automatically convert every GIF to another file type. + +Module asis_module mod_asis.o +Module imap_module mod_imap.o +Module action_module mod_actions.o + +## Optional modules for NCSA user-agent/referer logging compatibility +## We recommend, however, that you migrate to the configurable logging +## module, below. + +# Module agent_log_module mod_log_agent.o +# Module referer_log_module mod_log_referer.o + +## This is a *replacement* for mod_log_common which supports a +## LogFormat directive which allows you to specify what goes into +## the TransferLog (if you want Referer, etc.) source code for docs. +## +## If you play with this, remember to drop the standard +## mod_log_common --- a server with both will work, but you'll get +## very confused trying to figure out what's going on... + +# Module config_log_module mod_log_config.o + +## cern_meta mimicks the behavior of the CERN web server with regards to +## metainformation files. + +# Module cern_meta_module mod_cern_meta.o + +## The status module allows the server to display current details about +## how well it is performing and what it is doing. Consider also enabling +## -DSTATUS (see the CFLAGS section near the start of the file) to allow +## full status information. Check conf/access.conf on how to enable this. + +# Module status_module mod_status.o + +## The Info module displays configuration information for the server and +## all included modules. It's very useful for debugging. + +# Module info_module mod_info.o + +## Optional authentication modules. +## +## The anon_auth module allows for anonymous-FTP-style username/ +## password authentication. + +# Module anon_auth_module mod_auth_anon.o + +## db_auth and dbm_auth work with Berkeley DB files - make sure there +## is support for DBM files on your system. You may need to grab the GNU +## "gdbm" package if not. + +# Module db_auth_module mod_auth_db.o +# Module dbm_auth_module mod_auth_dbm.o + +## msql_auth checks against an MSQL database. You must have MSQL installed +## and an "msql.h" available for this to even compile. Additionally, +## you may need to add a couple entries to the CFLAGS line, like +## +## -lmsql -L/usr/local/lib -L/usr/local/Minerva/lib +## +## This depends on your installation of MSQL. + +# Module msql_auth_module mod_auth_msql.o + +## "digest" implements HTTP Digest Authentication rather than the less +## secure Basic Auth used by the other modules. + +# Module digest_module mod_digest.o + +## Outright experiments --- mod_dld defines commands which +## allows other modules to be loaded in at runtime, and mod_cookies +## uses Netscape cookies to automatically construct and log accurate +## click-trails from Netscape cookies, for Netscape-using clients who +## aren't coming in via proxy. + +# Module dld_module mod_dld.o +# Module cookies_module mod_cookies.o + +## Finally, the proxy module. It's not as complete as it could +## be yet, so use at your own risk. + +# Module proxy_module mod_proxy.o diff --git a/RELEASE_1_1_X/src/Configure b/RELEASE_1_1_X/src/Configure new file mode 100755 index 00000000000..d27cf4ba7a6 --- /dev/null +++ b/RELEASE_1_1_X/src/Configure @@ -0,0 +1,85 @@ +#! /bin/sh + +# Apache configuration script, first cut --- rst. +# Dont like it? Inspired to do something better? Go for it. + +file=Configuration +tmpfile=htconf.$$ + +if [ "x$1" = "x-file" ] ; then + echo "Using alternate config file $2" + file=$2 +else + echo "Using '$file' as config file" +fi + +# First, strip comments and blank lines... + +sed 's/#.*//' $file | sed '/^[ ]*$/d' | sed 's/[ ]*$//' > $tmpfile + +# Check for syntax errors... + +if grep -v = $tmpfile | \ + egrep -v '^Module[ ]+[A-Za-z0-9_]+[ ]+[^ ]+$' > /dev/null +then + echo "Syntax error --- each config file command must either" + echo "set a Makefile option, or configure a module (giving a" + echo "module name and a filename). This doesn't appear to be" + echo "doing either:" + grep -v = $tmpfile | \ + egrep -v '^Module[ ]+[A-Za-z0-9_]+[ ]+[^ ]+$' + rm $tmpfile + exit 1 +fi + +# File is OK --- make backup copies of things and then get the new ones: + +if [ -f Makefile ] ; then mv Makefile Makefile.bak; fi +if [ -f modules.c ] ; then mv modules.c modules.c.bak; fi + +awk >modules.c <$tmpfile '\ + BEGIN { modules[n++] = "core_module" } \ + /^Module/ { modules[n++] = $2 } \ + END { print "/* modules.c --- automatically generated by Apache"; \ + print " * configuration script. DO NOT HAND EDIT!!!!!"; \ + print " */"; \ + print ""; \ + print "#include \"httpd.h\""; \ + print "#include \"http_config.h\""; \ + print ""; \ + for (i = 0; i < n; ++i) { \ + printf ("extern module %s;\n", modules[i]); \ + } \ + print ""; \ + print "module *prelinked_modules[] = {"; \ + for (i = 0; i < n; ++i) { \ + printf " &%s,\n", modules[i]; \ + } \ + print " NULL"; \ + print "};"; \ + print "char *module_names[] = {"; \ + for (i = n-1; i > -1; --i) { \ + printf " \"%s\",\n", modules[i]; \ + } \ + print " NULL"; \ + print "};"; \ + }' + +awk >Makefile <$tmpfile '\ + BEGIN { print "# Makefile automatically generated from Makefile.tmpl"; \ + print "# and configuration file by Apache config script. "; \ + print "# Hand-edited changes will be lost if the config script"; \ + print "# is re-run."; \ + } \ + /^Module/ { modules[n++] = $3 } \ + /\=/ { print } \ + END { print "MODULES=\\"; \ + for (i = 0; i < n; ++i) { \ + if (i < n-1) printf (" %s \\\n", modules[i]); \ + else printf (" %s\n", modules[i]); \ + } \ + print "" \ + }' + +cat Makefile.tmpl >> Makefile +rm $tmpfile diff --git a/RELEASE_1_1_X/src/INSTALL b/RELEASE_1_1_X/src/INSTALL new file mode 100644 index 00000000000..ced55b87aed --- /dev/null +++ b/RELEASE_1_1_X/src/INSTALL @@ -0,0 +1,51 @@ +This release of Apache supports the notion of "optional modules". +However, the server has to know which modules are compiled into it, in +order for those modules to be effective; this requires generation of a +short bit of code ("modules.c") which simply has a list of them. + +It is also necessary to choose the correct options for your platform. + +To do this: + +1) Copy the file "Configuration.tmpl" to "Configuration" and then edit + "Configuration". This contains the per-machine config settings of + the Makefile, and also an additional section at the bottom which + lists the modules which have been compiled in, and also names the + files containing them. You will need to: + + a) Select a compiler, and compilation options as appropriate to + your machine. + + b) Uncomment lines corresponding to those optional modules you wish + to include (among the Module lines at the bottom of the file), + or add new lines corresponding to custom modules you have written. + (See API.html for preliminary docs on how to do that). + + Note that DBM auth has to be explicitly configured in, if you want + it --- just uncomment the corresponding line. + +2) Run the "Configure" script: + + % Configure + Using 'Configuration' as config file + % + + This generates new versions of the Makefile and of modules.c. (If + you want to maintain multiple configurations, you can say, e.g., + + % Configure -file Configuration.ai + Using alternate config file Configuration.ai + % + +3) Type "make". + +The modules we place in the Apache distribution are the ones we have +tested and are used regularly by various members of the Apache +development group. Additional modules contributed by members or third +parties with specific needs or functions are available at +<URL:http://www.apache.org/dist/contrib/modules/>. There are +instructions on that page for linking these modules into the +core Apache code. + + + diff --git a/RELEASE_1_1_X/src/Makefile.tmpl b/RELEASE_1_1_X/src/Makefile.tmpl new file mode 100644 index 00000000000..3b57a2b4498 --- /dev/null +++ b/RELEASE_1_1_X/src/Makefile.tmpl @@ -0,0 +1,80 @@ +# Apache makefile template (well, suffix). + +# This is combined with the information in the "Configuration" file +# by the configure script to make the actual Makefile. + +OBJS= alloc.o http_main.o http_core.o http_config.o http_request.o \ + http_log.o http_protocol.o rfc1413.o util.o util_script.o modules.o buff.o\ + md5c.o util_md5.o explain.o http_bprintf.o $(MODULES) + +.c.o: + $(CC) -c $(CFLAGS) $(AUX_CFLAGS) $< + +all: Configuration httpd + +Configuration: Configuration.tmpl + @echo "Configuration older than Configuration.tmpl, or doesn't exist." + @echo "Consider copying Configuration.tmpl to Configuration, editing and rerunning" + @echo "Configure." + @echo "If not, you will at least have to touch Configuration." + @false + +httpd: $(OBJS) + $(CC) $(LFLAGS) $(AUX_LFLAGS) -o httpd $(OBJS) $(EXTRA_LIBS) $(AUX_LIBS) + +clean: + rm -f httpd $(OBJS) *pure* + +dist.tar: + # Assure a semi-sensible configuration going out... + cp Makefile.orig Makefile + cp modules.c.orig modules.c + tar cvf dist.tar README INSTALL CHANGES TODO API.html \ + Configuration Configure Makefile.tmpl Makefile *.h *.c + +# Work around broken compilers +http_bprintf.o: http_bprintf.c + $(CC) -c $(CFLAGS) $(AUX_CFLAGS) $(BROKEN_BPRINTF_FLAGS) http_bprintf.c + +#Dependencies +#Core code +$(OBJS): Makefile httpd.h alloc.h buff.h conf.h + +http_config.o http_core.o http_main.o util.o: http_conf_globals.h +http_config.o http_core.o http_main.o: http_config.h http_log.h +http_log.o http_protocol.o http_request.o modules.o: http_config.h +http_config.o http_core.o http_log.o http_main.o http_protocol.o: http_core.h +http_request.o: http_core.h +http_protocol.o http_request.o util_script.o rfc1413.o: http_log.h +http_core.o http_main.o http_protocol.o http_request.o: http_main.h +http_core.o http_main.o http_protocol.o http_request.o: http_protocol.h +http_config.o http_main.o http_request.o: http_request.h +http_main.o: scoreboard.h +http_core.o rfc1413.o: rfc1413.h + +#Modules +$(MODULES): http_config.h httpd.h +mod_dld.o: http_conf_globals.h +mod_ai_backcompat.o mod_auth.o mod_auth_dbm.o mod_cgi.o mod_dir.o: http_core.h +mod_access.o mod_imap.o mod_include.o mod_negotiation.o: http_core.h +mod_asis.o mod_auth.o mod_auth_dbm.o mod_cgi.o mod_dir.o: http_core.h +mod_status.o mod_cookies.o mod_log_common.o: http_core.h +mod_access.o mod_proxy.o mod_imap.o mod_include.o mod_negotiation.o: http_log.h +mod_proxy.o mod_asis.o mod_cgi.o mod_dir.o mod_imap.o: http_main.h +mod_status.o mod_include.o: http_main.h +mod_proxy.o mod_asis.o mod_auth.o mod_auth_dbm.o mod_cgi.o: http_protocol.h +mod_status.o mod_dir.o: http_protocol.h +mod_imap.o mod_include.o: http_protocol.h +mod_cgi.o mod_dir.o mod_imap.o mod_include.o mod_negotiation.o: http_request.h +mod_proxy.o: md5.h +mod_status.o: util_script.h +mod_asis.o mod_cgi.o mod_dir.o mod_imap.o mod_include.o: util_script.h +mod_digest.o: util_md5.h +mod_status.o: scoreboard.h + +#Utils +md5c.o: md5.h +util.o: http_conf_globals.h +util_script.o: http_core.h http_main.h http_protocol.h util_script.h +util_md5.o: md5.h + diff --git a/RELEASE_1_1_X/src/README b/RELEASE_1_1_X/src/README new file mode 100644 index 00000000000..d1e6c1ae187 --- /dev/null +++ b/RELEASE_1_1_X/src/README @@ -0,0 +1,219 @@ +The basic idea of the new Apache release is to make a modular +"tinkertoy" server, to which people can easily add code which is +valuable to them (even if it isn't universally useful) without hairing +up a monolithic server. Applications for this idea include database +integration, support for experimental search and scripting extensions, +new authentication modes (digest authentication, for instance, could +be done entirely as a module), and so forth. All modules have the +same interface to the server core, and through it, to each other. + +In particular, the following are modules in the current code base: +common log format (other loggers can easily coexist with it), auth and +dbm auth (although both use common code in http_protocol.c to parse +the Authorization: line), directory handling (which can be added or +replaced), handling of aliases and access control, content +negotiation, CGI, includes, aliases, and so forth. (What's left in +the basic server? Not a whole lot). The configuration file commands +which configure these things are defined, for the most part, by the +modules themselves, and not by the server core (each module has, or +can have, a command dispatch table). + +Besides carving up the base code into modules, this release makes a +few other fairly pervasive changes. Most of the global variables are +gone; most of the MAX_STRING_LENGTH char arrays are gone (the few that +are left being sprintf() targets, or I/O buffers of various sorts), +and unmunge_name has vanished. The most drastic change is the use of +a "compool" strategy to manage resources allocated for a request --- +the code in alloc.c keeps track of it all and allows it to be freed en +bloc at the end of the request. This strategy seems to be effective +in stanching memory and descriptor leaks. + +Additional third-party modules can be found at +<URL:http://www.apache.org/dist/contrib/modules/>. + + +A brief code review: + +The code here can be divided into the server core (the http_* files, +along with alloc.c and the various utility files), and several modules +(the mod_* files). + +The core interfaces to modules through the "module" structure which +describes each one. There's a linked list of these things rooted at +top_module, through which http_config.c dispatches when necessary. The +module structures themselves are defined at the bottom of the mod_foo +files. (Loading new modules dynamically at runtime should be simple; +just push them onto the linked list. The only complication is what to +do with AddModule commands when the config files are reread, +particularly if you find a module has been taken out). + +In addition to the core itself (which does have a module structure to +hold its command tables, and the handlers for various phases of +request handling which make it *barely* a web server on its own), +the modules included here are the following: + +mod_mime.c --- deduction of MIME types and content-encodings from + filename extensions. This module defines the AddType, AddEncoding, + and TypesConfig config-file directives. This code is off in a + module by itself so that people who want to experiment with other + meta-information schemes can replace it, and still have content + negotiation work. + +mod_common_log.c --- logging in common log format. + +mod_auth.c --- HTTP authentication. Defines the AuthUserFile and + AuthGroupFile directives (other auth-related commands are handled by + the core itself, so it knows which requests require it to poll the + modules for authentication handlers). + +mod_auth_dbm.c --- DBM auth. Untested, and left out of the modules + list in modules.c because of that, but it does at least compile. + Grump. + +mod_access.c --- access checking by DNS name or IP address; defines + the "order", "allow" and "deny" config-file commands. (If this + module is compiled out, the server fails safe --- any attempt to + configure access control will die on a config file syntax error when + the relevant commands go unrecognized). + +mod_negotiation.c --- Content negotiation. Defines the + CacheNegotiatedDocs config-file command. Making this a module is + perhaps going overboard, but I wanted to see how far I could push + it. + +mod_alias.c --- Alias command and file translation. + +mod_userdir.c --- ditto for Userdir. + +mod_cgi.c --- Common Gateway Interface. Also defines ScriptAlias, + because scripts are treated slightly differently depending on + whether they are ScriptAliased or not (in particular, ExecCGI is not + required in the former case). + +mod_includes.c --- server-side includes. + +mod_dir.c --- defines a whole *raft* of commands; handles directories. + +mod_asis.c --- ASIS file handling. + +mod_dld.c --- the experimental runtime-code-loader described above. + You'll have to alter the makefile and modules.c to make this active + if you want it. + + + +As to the core, here's a brief review of what's where: + +http_protocol.c --- functions for dealing directly with the client. + Reading requests, writing replies of various sorts. I've tried to + route all data transfer between server and client through here, so + there's a single piece of code to change if we want to add, say, + HTTP-NG packetization. The major glaring exception is NPH- CGI + scripts; what *will* we do with those for HTTP-NG? + +http_request.c --- functions which direct the processing of requests, + including error handling. Generally responsible for making sure + that the right module handlers get invoked, in the right order. + (This includes the "sub-request" mechanism, which is used by + includes and other stuff to ask about the status of particular + subfiles). + +http_core.c --- + Contains the core module structure, its command table, and the + command handlers, also the filename translation routine, and the + like for the core. (Basically, this is all of the core module stuff + which looks more or less like the boilerplate from the other modules). + +http_config.c --- Functions to read config files and dispatch to the + command handlers; also, routines to manage configuration vectors, + and to dispatch to modules' handlers for the various phases of + handling a request. + +http_log.c --- just the error log. Error handling is split between + http_protocol.c (for generating the default error responses) and + http_request.c (for executive handling, including ErrorDocument + invocation); transaction logging is in the modules. + +http_main.c --- System startup, restart, and accepting connections; + also timeout handling (which is pretty grotesque right now; ideas?) + +alloc.c --- allocation of all resources which might have to be reclaimed + eventually, including memory, files, and child processes. + + ================================================= +List of various #defines used in the code: +The Apache code, specifically in conf.h, uses a variety of #defines to +control how the code is compiled and what options are available for each +support OS. The following list provides a short list and description +of these #defines. + +-- + + NEED_*: + If the particular OS doesn't supply the specified function, we use the + Apache-supplied version (in util.c). + + NEED_STRERROR: + NEED_STRDUP: + NEED_STRCASECMP: + NEED_STRNCASECMP: + NEED_INITGROUPS: + NEED_WAITPID: + NEED_STRERROR: + NEED_SELECT_H: + Define if the OS needs the <sys/select.h> header file. + +-- + + HAVE_*: + Does this OS have/support this capablity? + + HAS_GMTOFF: + Define if the OS's tm struct has the tm_gmtoff element + + HAVE_RESOURCE: + Define as 1 if the OS supports the getrlimit()/setrlimit() functions + + HAVE_MMAP: + Define if the OS supports the BSD mmap() call. This is used by various + OSs to allow the scoreboard file to be held in shared mmapped-memory + instead of a real file. + + HAVE_SHMGET: + Define if the OS has the SysV-based shmget() family of shared-memory + functions. Used to allow the scoreboard to live in a shared-memory + slot instead of a real file. + + HAVE_CRYPT_H: + Define if the OS has the <crypt.h> header file. + +-- + + NO_*: + These are defined if the OS does NOT have the specified function or if + we should not use it. + + NO_KILLPG: + NO_SETSID: + NO_USE_SIGACTION: + Do not use the sigaction() call, even if we have it. +-- + + MISC #DEFINES: + Various other #defines used in the code. + + FCNTL_SERIALIZED_ACCEPT: + Define if the OS requires a mutex "lock" around the socket accept() + call. Use fcntl() locking. + + FLOCK_SERIALIZED_ACCEPT: + Define if the OS requires a mutex "lock" around the socket accept() + call. Use flock() locking (fcntl() is expensive on some OSs, esp. + when using NFS). + + JMP_BUF: + The variable-type for siglongjmp() or longjmp() call. + + MOVEBREAK: + Amount to move sbrk() breakpoint, if required, before attaching + shared-memory segment. diff --git a/RELEASE_1_1_X/src/TODO b/RELEASE_1_1_X/src/TODO new file mode 100644 index 00000000000..af09059f1ac --- /dev/null +++ b/RELEASE_1_1_X/src/TODO @@ -0,0 +1,45 @@ +*) Random stray failures to get_local_addr --- it's returning EINVAL, of all + things. Are these clients aborting really, really early or what? + +*) Clean up inclusion stuff --- PATH_INFO in <!--#exec cgi-->, also + make sure that if the response handler sets an error code, then an + error report winds up on the output... + +*) More complete scoreboard for child processes. For each process, at least: + number of requests handled, start time, current request (first line and + client and server addresses). Also an httpstat program to print this all + out readably (thereby giving you your first real shot at figuring out what + that CGI script which has been chewing up all your CPU for the past few + minutes thinks it's actually doing...). + +*) Assess efficiency cost of rputc() in includes. + +*) Varargs printf-like log_reason (would allow me to ditch most of the + remaining instances of MAX_STRING_LENGTH). + +*) Byte ranges, as per recent Netscape/Franks I-D. + +*) Allow modules to request their own AllowOverrides and Options bits. + (If nothing else, will get rid of the last bit of directory-handling + lint in the core --- likewise for CGI and includes lint). + +*) -c command line option; processed as commands, *after* config files + (so -c "Port ..." can override). + +*) multiple util_files: util_time, util_string, util_sys, util_misc (?). + +*) Look at multithreading... known problem areas: + need to throw a mutex around free-block management in alloc.c; need to + adjust timeout and SIGPIPE handling (who got the SIGPIPE?); need to + create new per-connection pools in main.c (as subpools of pconf, + presumably), rather than just using one global ptrans pool, and need + to triple-check that no thread will want to allocate in the same pool + as another that is simultaneously running. Then we get to worry about + all the obscure C library functions that return pointers to static + data (the time stuff is particularly bad about this). + + Oh yeah, the rfc931 code is nowhere near thread-safe (is it leak-safe?); + it has a single static jmp_buf, and returns a pointer to static data. + +*) Possible cleanup --- feed module functions their per-directory configuration + as a second argument, rather than making them fish it out of r->per_dir_.... diff --git a/RELEASE_1_1_X/src/ap/ap_md5c.c b/RELEASE_1_1_X/src/ap/ap_md5c.c new file mode 100644 index 00000000000..4ec60cb1a38 --- /dev/null +++ b/RELEASE_1_1_X/src/ap/ap_md5c.c @@ -0,0 +1,354 @@ +/* + * This is work is derived from material Copyright RSA Data Security, Inc. + * + * The RSA copyright statement and Licence for that original material is + * included below. This is followed by the Apache copyright statement and + * licence for the modifications made to that material. + */ + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* ==================================================================== + * Copyright (c) 1996 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +#include <string.h> + +#include "md5.h" + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform(UINT4 state[4], const unsigned char block[64]); +static void Encode(unsigned char *output, const UINT4 *input, + unsigned int len); +static void Decode(UINT4 *output, const unsigned char *input, + unsigned int len); + +static unsigned char PADDING[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void +MD5Init(MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void +MD5Update(MD5_CTX *context, const unsigned char *input, unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += (UINT4)inputLen >> 29; + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) + { + memcpy(&context->buffer[index], input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy(&context->buffer[index], &input[i], inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void +MD5Final(unsigned char digest[16], MD5_CTX *context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information. */ + memset(context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ +static void +MD5Transform(UINT4 state[4], const unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + memset(x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void +Encode(unsigned char *output, const UINT4 *input, unsigned int len) +{ + unsigned int i, j; + UINT4 k; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + k = input[i]; + output[j] = (unsigned char)(k & 0xff); + output[j+1] = (unsigned char)((k >> 8) & 0xff); + output[j+2] = (unsigned char)((k >> 16) & 0xff); + output[j+3] = (unsigned char)((k >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void +Decode(UINT4 *output, const unsigned char *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} diff --git a/RELEASE_1_1_X/src/include/alloc.h b/RELEASE_1_1_X/src/include/alloc.h new file mode 100644 index 00000000000..c65b3af0485 --- /dev/null +++ b/RELEASE_1_1_X/src/include/alloc.h @@ -0,0 +1,242 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +/* + * Resource allocation routines... + * + * designed so that we don't have to keep track of EVERYTHING so that + * it can be explicitly freed later (a fundamentally unsound strategy --- + * particularly in the presence of die()). + * + * Instead, we maintain pools, and allocate items (both memory and I/O + * handlers) from the pools --- currently there are two, one for per + * transaction info, and one for config info. When a transaction is over, + * we can delete everything in the per-transaction pool without fear, and + * without thinking too hard about it either. + * + * rst + */ + +/* Arenas for configuration info and transaction info + * --- actual layout of the pool structure is private to + * alloc.c. + */ + +typedef struct pool pool; + +extern pool *permanent_pool; +void init_alloc(); /* Set up everything */ +pool *make_sub_pool (pool *); /* All pools are subpools of permanent_pool */ +void destroy_pool (pool *); + +/* Clearing out EVERYTHING in an pool... destroys any sub-pools */ + +void clear_pool (struct pool *); + +/* Preparing for exec() --- close files, etc., but *don't* flush I/O + * buffers, *don't* wait for subprocesses, and *don't* free any memory. + */ + +void cleanup_for_exec (); + +/* routines to allocate memory from an pool... */ + +void *palloc(struct pool *, int nbytes); +void *pcalloc(struct pool *, int nbytes); +extern char *pstrdup(struct pool *, const char *s); +extern char *pstrndup(struct pool *, const char *s, int n); +char *pstrcat(struct pool *, ...); /* all '...' must be char* */ + +/* array and alist management... keeping lists of things. + * Common enough to want common support code ... + */ + +typedef struct { + pool *pool; + int elt_size; + int nelts; + int nalloc; + char *elts; +} array_header; + +array_header *make_array (pool *p, int nelts, int elt_size); +void *push_array (array_header *); +void array_cat (array_header *dst, array_header *src); +array_header *append_arrays (pool *, array_header *, array_header *); + +/* copy_array copies the *entire* array. copy_array_hdr just copies + * the header, and arranges for the elements to be copied if (and only + * if) the code subsequently does a push or arraycat. + */ + +array_header *copy_array (pool *p, array_header *src); +array_header *copy_array_hdr (pool *p, array_header *src); + + +/* Tables. Implemented alist style, for now, though we try to keep + * it so that imposing a hash table structure on top in the future + * wouldn't be *too* hard... + * + * Note that key comparisons for these are case-insensitive, largely + * because that's what's appropriate and convenient everywhere they're + * currently being used... + */ + +typedef array_header table; + +typedef struct { + char *key; /* maybe NULL in future; + * check when iterating thru table_elts + */ + char *val; +} table_entry; + +table *make_table (pool *p, int nelts); +table *copy_table (pool *p, table *); +char *table_get (table *, char *); +void table_set (table *, const char *name, const char *val); +void table_merge (table *, char *name, char *more_val); +void table_unset (table *, char *key); +void table_add (table *, char *name, char *val); + +table *overlay_tables (pool *p, table *overlay, table *base); + +array_header *table_elts (table *); + +/* routines to remember allocation of other sorts of things... + * generic interface first. Note that we want to have two separate + * cleanup functions in the general case, one for exec() preparation, + * to keep CGI scripts and the like from inheriting access to things + * they shouldn't be able to touch, and one for actually cleaning up, + * when the actual server process wants to get rid of the thing, + * whatever it is. + * + * kill_cleanup disarms a cleanup, presumably because the resource in + * question has been closed, freed, or whatever, and it's scarce + * enough to want to reclaim (e.g., descriptors). It arranges for the + * resource not to be cleaned up a second time (it might have been + * reallocated). run_cleanup does the same, but runs it first. + * + * Cleanups are identified for purposes of finding & running them off by the + * plain_cleanup and data, which should presumably be unique. + * + * NB any code which invokes register_cleanup or kill_cleanup directly + * is a critical section which should be guarded by block_alarms() and + * unblock_alarms() below... + */ + +void register_cleanup (pool *p, void *data, + void (*plain_cleanup)(void *), + void (*child_cleanup)(void *)); + +void kill_cleanup (pool *p, void *data, void (*plain_cleanup)(void *)); + +/* The time between when a resource is actually allocated, and when it + * its cleanup is registered is a critical section, during which the + * resource could leak if we got interrupted or timed out. So, anything + * which registers cleanups should bracket resource allocation and the + * cleanup registry with these. (This is done internally by run_cleanup). + * + * NB they are actually implemented in http_main.c, since they are bound + * up with timeout handling in general... + */ + +extern void block_alarms(); +extern void unblock_alarms(); + +/* Common cases which want utility support.. + * the note_cleanups_for_foo routines are for + */ + +FILE *pfopen(struct pool *, char *name, char *fmode); +FILE *pfdopen(struct pool *, int fd, char *fmode); +int popenf(struct pool *, char *name, int flg, int mode); + +void note_cleanups_for_file (pool *, FILE *); +void note_cleanups_for_fd (pool *, int); + +/* routines to note closes... file descriptors are constrained enough + * on some systems that we want to support this. + */ + +int pfclose(struct pool *, FILE *); +int pclosef(struct pool *, int fd); + +/* ... even child processes (which we may want to wait for, + * or to kill outright, on unexpected termination). + * + * spawn_child is a utility routine which handles an awful lot of + * the rigamarole associated with spawning a child --- it arranges + * for pipes to the child's stdin and stdout, if desired (if not, + * set the associated args to NULL). It takes as args a function + * to call in the child, and an argument to be passed to the function. + */ + +enum kill_conditions { kill_never, kill_always, kill_after_timeout, just_wait}; + +int spawn_child (pool *, void (*)(void *), void *, + enum kill_conditions, FILE **pipe_in, FILE **pipe_out); +#ifdef __EMX__ +int spawn_child_os2 (pool *, void (*)(void *), void *, + enum kill_conditions, FILE **pipe_in, FILE **pipe_out, char *buffer, int lenp); +#endif + +/* magic numbers --- only one so far, min free bytes in a new pool block */ + +#define BLOCK_MINFREE 8192 + +/* Finally, some accounting */ + +long bytes_in_pool(pool *p); +long bytes_in_free_blocks(); diff --git a/RELEASE_1_1_X/src/include/ap_config.h b/RELEASE_1_1_X/src/include/ap_config.h new file mode 100644 index 00000000000..fef4055ae71 --- /dev/null +++ b/RELEASE_1_1_X/src/include/ap_config.h @@ -0,0 +1,511 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +/* + * conf.h: system-dependant #defines and includes... + * See README for a listing of what they mean + */ + +#ifndef QNX +#include <sys/param.h> +#endif + +/* Define one of these according to your system. */ +#if defined(SUNOS4) +#define HAS_GMTOFF +#define HAVE_RESOURCE 1 +#undef NO_KILLPG +#undef NO_SETSID +char *crypt(char *pw, char *salt); +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP +#include <sys/time.h> +#define NEED_STRERROR + +#elif defined(SOLARIS2) +#undef HAS_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#define HAVE_RESOURCE 1 +#define bzero(a,b) memset(a,0,b) +#define getwd(d) getcwd(d,MAX_STRING_LEN) +#define JMP_BUF sigjmp_buf +#define FCNTL_SERIALIZED_ACCEPT +#define HAVE_MMAP +#define HAVE_CRYPT_H + +#elif defined(IRIX) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define FCNTL_SERIALIZED_ACCEPT +#define HAVE_SHMGET +#define HAVE_CRYPT_H + +#elif defined(HPUX) +#define HAVE_RESOURCE 1 +#undef HAS_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#ifndef _HPUX_SOURCE +#define _HPUX_SOURCE +#endif +#define getwd(d) getcwd(d,MAX_STRING_LEN) +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP + +#elif defined(AIX) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_SELECT_H +#define JMP_BUF sigjmp_buf + +#elif defined(ULTRIX) +#define HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define ULTRIX_BRAIN_DEATH +#define NEED_STRDUP +/* If you have Ultrix 4.3, and are using cc, const is broken */ +#ifndef __ultrix__ /* Hack to check for pre-Ultrix 4.4 cc */ +#define const /* Not implemented */ +#endif +#define JMP_BUF sigjmp_buf + +#elif defined(OSF1) +#define HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP +#define HAVE_CRYPT_H + +#elif defined(SEQUENT) +#define HAS_GMTOFF +#undef NO_KILLPG +#define NO_SETSID +#define NEED_STRDUP +#define tolower(c) (isupper(c) ? tolower(c) : c) + +#elif defined(NEXT) +#include <libc.h> +typedef unsigned short mode_t; +#define HAS_GMTOFF +#undef NO_KILLPG +#define NO_SETSID +#define NEED_STRDUP +#undef _POSIX_SOURCE +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&(S_IFMT)) == (S_IFDIR)) +#endif +#ifndef S_ISREG +#define S_ISREG(m) (((m)&(S_IFMT)) == (S_IFREG)) +#endif +#ifndef S_IXUSR +#define S_IXUSR 00100 +#endif +#ifndef S_IRGRP +#define S_IRGRP 00040 +#endif +#ifndef S_IXGRP +#define S_IXGRP 00010 +#endif +#ifndef S_IROTH +#define S_IROTH 00004 +#endif +#ifndef S_IXOTH +#define S_IXOTH 00001 +#endif +#ifndef S_IRUSR +#define S_IRUSR S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR S_IWRITE +#endif +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define waitpid(a,b,c) wait4(a,(union wait *)b,c,NULL) +typedef int pid_t; +#define JMP_BUF jmp_buf +#define NO_USE_SIGACTION + +#elif defined(LINUX) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define JMP_BUF sigjmp_buf +#define FCNTL_SERIALIZED_ACCEPT +#include <sys/time.h> + +#elif defined(SCO) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_INITGROUPS +#define JMP_BUF sigjmp_buf +#define SIGURG SIGUSR1 /* but note, this signal will be sent to a process group if enabled (for OOB data). It is not currently enabled. */ +#define getwd(d) getcwd(d,MAX_STRING_LEN) + +#elif defined(SCO5) + +#define JMP_BUF sigjmp_buf +#define SIGURG SIGUSR1 +#define NEED_SELECT_H +#define FCNTL_SERIALIZED_ACCEPT +#define HAVE_MMAP +#define SecureWare + +/* Although SCO 5 defines these in <strings.h> (note the "s") they don't have +consts. Sigh. */ +extern int strcasecmp(const char *,const char *); +extern int strncasecmp(const char *,const char *,unsigned); + +#elif defined(CONVEXOS) +#define HAS_GMTOFF +#define NEED_STRDUP +#define getwd(d) getcwd(d,MAX_STRING_LEN) + +#elif defined(AUX) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_STRDUP +#define JMP_BUF sigjmp_buf +/* fcntl() locking is expensive with NFS */ +#undef FLOCK_SERIALIZED_ACCEPT +#define HAVE_SHMGET +#define MOVEBREAK 0x4000000 +/* These are to let -Wall compile more cleanly */ +extern int strcasecmp(const char *, const char *); +extern int strncasecmp(const char *,const char *,unsigned); + +#elif defined(SVR4) +#define NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define bzero(a,b) memset(a,0,b) +#define JMP_BUF sigjmp_buf +#define getwd(d) getcwd(d,MAX_STRING_LEN) +/* A lot of SVR4 systems need this */ +#define FCNTL_SERIALIZED_ACCEPT + +#elif defined(DGUX) +#define NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define bzero(a,b) memset(a,0,b) +#define JMP_BUF sigjmp_buf +#define getwd(d) getcwd(d,MAX_STRING_LEN) +/* A lot of SVR4 systems need this */ +#define FCNTL_SERIALIZED_ACCEPT + +#elif defined(__NetBSD__) +#define HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf + +#elif defined(UTS21) +#undef HAS_GMTOFF +#undef NO_KILLPG +#define NO_SETSID +#define NEED_WAITPID +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define strftime(buf,bufsize,fmt,tm) ascftime(buf,fmt,tm) +#include <sys/types.h> + +#elif defined(APOLLO) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define timezone _bky_timezone + +#elif defined(__FreeBSD__) || defined(__bsdi__) +#define HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP + +#elif defined(QNX) +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_INITGROUPS +#define NEED_SELECT_H +#define JMP_BUF sigjmp_buf + +#elif defined(LYNXOS) +#undef NO_KILLPG +#undef NO_SETSID +#define NO_TIMEZONE +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define NEED_INITGROUPS +#define JMP_BUF jmp_buf + +#elif defined(__EMX__) +/* Defines required for EMX OS/2 port. */ +#define JMP_BUF sigjmp_buf +#define NO_KILLPG +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define NO_SETSID +/* Add some drive name support */ +#define chdir _chdir2 + +/* Unknown system - Edit these to match */ +#else +#ifdef BSD +#define HAS_GMTOFF +#else +#undef HAS_GMTOFF +#endif +/* NO_KILLPG is set on systems that don't have killpg */ +#undef NO_KILLPG +/* NO_SETSID is set on systems that don't have setsid */ +#undef NO_SETSID +/* NEED_STRDUP is set on stupid systems that don't have strdup. */ +#undef NEED_STRDUP +#endif + +/* Do we have sys/resource.h; assume that BSD does. */ +#ifndef HAVE_RESOURCE +#ifdef BSD +#define HAVE_RESOURCE 1 +#else +#define HAVE_RESOURCE 0 +#endif +#endif /* HAVE_RESOURCE */ + +/* + * The particular directory style your system supports. If you have dirent.h + * in /usr/include (POSIX) or /usr/include/sys (SYSV), #include + * that file and define DIR_TYPE to be dirent. Otherwise, if you have + * /usr/include/sys/dir.h, define DIR_TYPE to be direct and include that + * file. If you have neither, I'm confused. + */ + +#include <sys/types.h> + +#if !defined(NEXT) && !defined(CONVEXOS) +#include <dirent.h> +#define DIR_TYPE dirent +#else +#include <sys/dir.h> +#define DIR_TYPE direct +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/socket.h> +#ifdef NEED_SELECT_H +#include <sys/select.h> +#endif +#include <ctype.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> /* for inet_ntoa */ +#include <time.h> /* for ctime */ +#include <signal.h> +#include <errno.h> +#include <sys/wait.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <limits.h> +#ifndef QNX +#include <memory.h> +#endif + +#if HAVE_RESOURCE +#include <sys/resource.h> +#ifdef SUNOS4 +int getrlimit( int, struct rlimit *); +int setrlimit( int, struct rlimit *); +#endif +#endif +#ifdef HAVE_MMAP +#include <sys/mman.h> +#endif +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +#define MAP_ANON MAP_ANONYMOUS +#endif + +#if defined(HAVE_MMAP) && defined(NO_MMAP) +#undef HAVE_MMAP +#endif + +#ifndef LOGNAME_MAX +#define LOGNAME_MAX 25 +#endif + +#ifndef NEXT +#include <unistd.h> +#endif + +#ifdef ultrix +#define ULTRIX_BRAIN_DEATH +#endif + +#ifndef S_ISLNK +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif + +/* Finding offsets of elements within structures. + * Taken from the X code... they've sweated portability of this stuff + * so we don't have to. Sigh... + */ + +#if defined(CRAY) || defined(__arm) +#if __STDC__ +#define XtOffset(p_type,field) _Offsetof(p_type,field) +#else +#ifdef CRAY2 +#define XtOffset(p_type,field) \ + (sizeof(int)*((unsigned int)&(((p_type)NULL)->field))) + +#else /* !CRAY2 */ + +#define XtOffset(p_type,field) ((unsigned int)&(((p_type)NULL)->field)) + +#endif /* !CRAY2 */ +#endif /* __STDC__ */ +#else /* ! (CRAY || __arm) */ + +#define XtOffset(p_type,field) \ + ((long) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL))) + +#endif /* !CRAY */ + +#ifdef offsetof +#define XtOffsetOf(s_type,field) offsetof(s_type,field) +#else +#define XtOffsetOf(s_type,field) XtOffset(s_type*,field) +#endif + +#ifdef SUNOS_LIB_PROTOTYPES +/* Prototypes needed to get a clean compile with gcc -Wall. + * Believe it or not, these do have to be declared, at least on SunOS, + * because they aren't mentioned in the relevant system headers. + * Sun Quality Software. Gotta love it. + */ + +int getopt (int, char **, char *); + +int strcasecmp (char *, char *); +int strncasecmp (char *, char *, int); +int toupper(int); +int tolower(int); + +int printf (char *, ...); +int fprintf (FILE *, char *, ...); +int fputs (char *, FILE *); +int fread (char *, int, int, FILE *); +int fwrite (char *, int, int, FILE *); +int fflush (FILE *); +int fclose (FILE *); +int ungetc (int, FILE *); +int _filbuf (FILE *); /* !!! */ +int _flsbuf (unsigned char, FILE *); /* !!! */ +int sscanf (char *, char *, ...); +void setbuf (FILE *, char *); +void perror (char *); + +time_t time (time_t *); +int strftime (char *, int, char *, struct tm *); + +int initgroups (char *, int); +int wait3 (int *, int, void*); /* Close enough for us... */ +int lstat (const char *, struct stat *); +int stat (const char *, struct stat *); +int flock (int, int); +int getwd (char *); +#ifndef NO_KILLPG +int killpg(int, int); +#endif +int socket (int, int, int); +int setsockopt (int, int, int, const char*, int); +int listen (int, int); +int bind (int, struct sockaddr *, int); +int connect (int, struct sockaddr *, int); +int accept (int, struct sockaddr *, int *); +int shutdown (int, int); + +int getsockname (int s, struct sockaddr *name, int *namelen); +int getpeername (int s, struct sockaddr *name, int *namelen); +int gethostname (char *name, int namelen); +void syslog (int, char *, ...); +char *mktemp (char *); + +#include <stdarg.h> +long vfprintf (FILE *, char *, va_list); + +#endif diff --git a/RELEASE_1_1_X/src/include/buff.h b/RELEASE_1_1_X/src/include/buff.h new file mode 100644 index 00000000000..bb432eeb232 --- /dev/null +++ b/RELEASE_1_1_X/src/include/buff.h @@ -0,0 +1,127 @@ +/* ==================================================================== + * Copyright (c) 1996 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +#include <stdarg.h> + +/* Reading is buffered */ +#define B_RD (1) +/* Writing is buffered */ +#define B_WR (2) +#define B_RDWR (3) +/* At end of file, or closed stream; no further input allowed */ +#define B_EOF (4) +/* No further output possible */ +#define B_EOUT (8) +/* A read error has occurred */ +#define B_RDERR (16) +/* A write error has occurred */ +#define B_WRERR (32) +#define B_ERROR (48) + +typedef struct buff_struct BUFF; + +struct buff_struct +{ + int flags; /* flags */ + unsigned char *inptr; /* pointer to next location to read */ + int incnt; /* number of bytes left to read from input buffer; + * always 0 if had a read error */ + int outcnt; /* number of byte put in output buffer */ + unsigned char *inbase; + unsigned char *outbase; + int bufsiz; + void (*error)(BUFF *fb, int op, void *data); + void *error_data; + long int bytes_sent; /* number of bytes actually written */ + + pool *pool; + +/* could also put pointers to the basic I/O routines here */ + int fd; /* the file descriptor */ + int fd_in; /* input file descriptor, if different */ +}; + +/* Options to bset/getopt */ +#define BO_BYTECT (1) + +/* Stream creation and modification */ +extern BUFF *bcreate(pool *p, int flags); +extern void bpushfd(BUFF *fb, int fd_in, int fd_out); +extern int bsetopt(BUFF *fb, int optname, const void *optval); +extern int bgetopt(BUFF *fb, int optname, void *optval); +extern int bclose(BUFF *fb); + +/* Error handling */ +extern void bonerror(BUFF *fb, void (*error)(BUFF *, int, void *), + void *data); + +/* I/O */ +extern int bread(BUFF *fb, void *buf, int nbyte); +extern int bgets(char *s, int n, BUFF *fb); +extern int bskiplf(BUFF *fb); +extern int bwrite(BUFF *fb, const void *buf, int nbyte); +extern int bflush(BUFF *fb); +extern int bputs(const char *x, BUFF *fb); +extern int bvputs(BUFF *fb, ...); +extern int bprintf(BUFF *fb,const char *fmt,...); +extern int vbprintf(BUFF *fb,const char *fmt,va_list vlist); + +/* Internal routines */ +extern int bflsbuf(int c, BUFF *fb); +extern int bfilbuf(BUFF *fb); + +#define bgetc(fb) ( ((fb)->incnt == 0) ? bfilbuf(fb) : \ + ((fb)->incnt--, *((fb)->inptr++)) ) + +#define bputc(c, fb) ((((fb)->flags & (B_EOUT|B_WRERR|B_WR)) != B_WR || \ + (fb)->outcnt == (fb)->bufsiz) ? bflsbuf(c, (fb)) : \ + ((fb)->outbase[(fb)->outcnt++] = (c), 0)) diff --git a/RELEASE_1_1_X/src/include/conf.h b/RELEASE_1_1_X/src/include/conf.h new file mode 100644 index 00000000000..fef4055ae71 --- /dev/null +++ b/RELEASE_1_1_X/src/include/conf.h @@ -0,0 +1,511 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +/* + * conf.h: system-dependant #defines and includes... + * See README for a listing of what they mean + */ + +#ifndef QNX +#include <sys/param.h> +#endif + +/* Define one of these according to your system. */ +#if defined(SUNOS4) +#define HAS_GMTOFF +#define HAVE_RESOURCE 1 +#undef NO_KILLPG +#undef NO_SETSID +char *crypt(char *pw, char *salt); +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP +#include <sys/time.h> +#define NEED_STRERROR + +#elif defined(SOLARIS2) +#undef HAS_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#define HAVE_RESOURCE 1 +#define bzero(a,b) memset(a,0,b) +#define getwd(d) getcwd(d,MAX_STRING_LEN) +#define JMP_BUF sigjmp_buf +#define FCNTL_SERIALIZED_ACCEPT +#define HAVE_MMAP +#define HAVE_CRYPT_H + +#elif defined(IRIX) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define FCNTL_SERIALIZED_ACCEPT +#define HAVE_SHMGET +#define HAVE_CRYPT_H + +#elif defined(HPUX) +#define HAVE_RESOURCE 1 +#undef HAS_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#ifndef _HPUX_SOURCE +#define _HPUX_SOURCE +#endif +#define getwd(d) getcwd(d,MAX_STRING_LEN) +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP + +#elif defined(AIX) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_SELECT_H +#define JMP_BUF sigjmp_buf + +#elif defined(ULTRIX) +#define HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define ULTRIX_BRAIN_DEATH +#define NEED_STRDUP +/* If you have Ultrix 4.3, and are using cc, const is broken */ +#ifndef __ultrix__ /* Hack to check for pre-Ultrix 4.4 cc */ +#define const /* Not implemented */ +#endif +#define JMP_BUF sigjmp_buf + +#elif defined(OSF1) +#define HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP +#define HAVE_CRYPT_H + +#elif defined(SEQUENT) +#define HAS_GMTOFF +#undef NO_KILLPG +#define NO_SETSID +#define NEED_STRDUP +#define tolower(c) (isupper(c) ? tolower(c) : c) + +#elif defined(NEXT) +#include <libc.h> +typedef unsigned short mode_t; +#define HAS_GMTOFF +#undef NO_KILLPG +#define NO_SETSID +#define NEED_STRDUP +#undef _POSIX_SOURCE +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&(S_IFMT)) == (S_IFDIR)) +#endif +#ifndef S_ISREG +#define S_ISREG(m) (((m)&(S_IFMT)) == (S_IFREG)) +#endif +#ifndef S_IXUSR +#define S_IXUSR 00100 +#endif +#ifndef S_IRGRP +#define S_IRGRP 00040 +#endif +#ifndef S_IXGRP +#define S_IXGRP 00010 +#endif +#ifndef S_IROTH +#define S_IROTH 00004 +#endif +#ifndef S_IXOTH +#define S_IXOTH 00001 +#endif +#ifndef S_IRUSR +#define S_IRUSR S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR S_IWRITE +#endif +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define waitpid(a,b,c) wait4(a,(union wait *)b,c,NULL) +typedef int pid_t; +#define JMP_BUF jmp_buf +#define NO_USE_SIGACTION + +#elif defined(LINUX) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define JMP_BUF sigjmp_buf +#define FCNTL_SERIALIZED_ACCEPT +#include <sys/time.h> + +#elif defined(SCO) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_INITGROUPS +#define JMP_BUF sigjmp_buf +#define SIGURG SIGUSR1 /* but note, this signal will be sent to a process group if enabled (for OOB data). It is not currently enabled. */ +#define getwd(d) getcwd(d,MAX_STRING_LEN) + +#elif defined(SCO5) + +#define JMP_BUF sigjmp_buf +#define SIGURG SIGUSR1 +#define NEED_SELECT_H +#define FCNTL_SERIALIZED_ACCEPT +#define HAVE_MMAP +#define SecureWare + +/* Although SCO 5 defines these in <strings.h> (note the "s") they don't have +consts. Sigh. */ +extern int strcasecmp(const char *,const char *); +extern int strncasecmp(const char *,const char *,unsigned); + +#elif defined(CONVEXOS) +#define HAS_GMTOFF +#define NEED_STRDUP +#define getwd(d) getcwd(d,MAX_STRING_LEN) + +#elif defined(AUX) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_STRDUP +#define JMP_BUF sigjmp_buf +/* fcntl() locking is expensive with NFS */ +#undef FLOCK_SERIALIZED_ACCEPT +#define HAVE_SHMGET +#define MOVEBREAK 0x4000000 +/* These are to let -Wall compile more cleanly */ +extern int strcasecmp(const char *, const char *); +extern int strncasecmp(const char *,const char *,unsigned); + +#elif defined(SVR4) +#define NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define bzero(a,b) memset(a,0,b) +#define JMP_BUF sigjmp_buf +#define getwd(d) getcwd(d,MAX_STRING_LEN) +/* A lot of SVR4 systems need this */ +#define FCNTL_SERIALIZED_ACCEPT + +#elif defined(DGUX) +#define NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define bzero(a,b) memset(a,0,b) +#define JMP_BUF sigjmp_buf +#define getwd(d) getcwd(d,MAX_STRING_LEN) +/* A lot of SVR4 systems need this */ +#define FCNTL_SERIALIZED_ACCEPT + +#elif defined(__NetBSD__) +#define HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf + +#elif defined(UTS21) +#undef HAS_GMTOFF +#undef NO_KILLPG +#define NO_SETSID +#define NEED_WAITPID +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define strftime(buf,bufsize,fmt,tm) ascftime(buf,fmt,tm) +#include <sys/types.h> + +#elif defined(APOLLO) +#undef HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define timezone _bky_timezone + +#elif defined(__FreeBSD__) || defined(__bsdi__) +#define HAS_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP + +#elif defined(QNX) +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_INITGROUPS +#define NEED_SELECT_H +#define JMP_BUF sigjmp_buf + +#elif defined(LYNXOS) +#undef NO_KILLPG +#undef NO_SETSID +#define NO_TIMEZONE +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define NEED_INITGROUPS +#define JMP_BUF jmp_buf + +#elif defined(__EMX__) +/* Defines required for EMX OS/2 port. */ +#define JMP_BUF sigjmp_buf +#define NO_KILLPG +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define NO_SETSID +/* Add some drive name support */ +#define chdir _chdir2 + +/* Unknown system - Edit these to match */ +#else +#ifdef BSD +#define HAS_GMTOFF +#else +#undef HAS_GMTOFF +#endif +/* NO_KILLPG is set on systems that don't have killpg */ +#undef NO_KILLPG +/* NO_SETSID is set on systems that don't have setsid */ +#undef NO_SETSID +/* NEED_STRDUP is set on stupid systems that don't have strdup. */ +#undef NEED_STRDUP +#endif + +/* Do we have sys/resource.h; assume that BSD does. */ +#ifndef HAVE_RESOURCE +#ifdef BSD +#define HAVE_RESOURCE 1 +#else +#define HAVE_RESOURCE 0 +#endif +#endif /* HAVE_RESOURCE */ + +/* + * The particular directory style your system supports. If you have dirent.h + * in /usr/include (POSIX) or /usr/include/sys (SYSV), #include + * that file and define DIR_TYPE to be dirent. Otherwise, if you have + * /usr/include/sys/dir.h, define DIR_TYPE to be direct and include that + * file. If you have neither, I'm confused. + */ + +#include <sys/types.h> + +#if !defined(NEXT) && !defined(CONVEXOS) +#include <dirent.h> +#define DIR_TYPE dirent +#else +#include <sys/dir.h> +#define DIR_TYPE direct +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/socket.h> +#ifdef NEED_SELECT_H +#include <sys/select.h> +#endif +#include <ctype.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> /* for inet_ntoa */ +#include <time.h> /* for ctime */ +#include <signal.h> +#include <errno.h> +#include <sys/wait.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <limits.h> +#ifndef QNX +#include <memory.h> +#endif + +#if HAVE_RESOURCE +#include <sys/resource.h> +#ifdef SUNOS4 +int getrlimit( int, struct rlimit *); +int setrlimit( int, struct rlimit *); +#endif +#endif +#ifdef HAVE_MMAP +#include <sys/mman.h> +#endif +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +#define MAP_ANON MAP_ANONYMOUS +#endif + +#if defined(HAVE_MMAP) && defined(NO_MMAP) +#undef HAVE_MMAP +#endif + +#ifndef LOGNAME_MAX +#define LOGNAME_MAX 25 +#endif + +#ifndef NEXT +#include <unistd.h> +#endif + +#ifdef ultrix +#define ULTRIX_BRAIN_DEATH +#endif + +#ifndef S_ISLNK +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif + +/* Finding offsets of elements within structures. + * Taken from the X code... they've sweated portability of this stuff + * so we don't have to. Sigh... + */ + +#if defined(CRAY) || defined(__arm) +#if __STDC__ +#define XtOffset(p_type,field) _Offsetof(p_type,field) +#else +#ifdef CRAY2 +#define XtOffset(p_type,field) \ + (sizeof(int)*((unsigned int)&(((p_type)NULL)->field))) + +#else /* !CRAY2 */ + +#define XtOffset(p_type,field) ((unsigned int)&(((p_type)NULL)->field)) + +#endif /* !CRAY2 */ +#endif /* __STDC__ */ +#else /* ! (CRAY || __arm) */ + +#define XtOffset(p_type,field) \ + ((long) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL))) + +#endif /* !CRAY */ + +#ifdef offsetof +#define XtOffsetOf(s_type,field) offsetof(s_type,field) +#else +#define XtOffsetOf(s_type,field) XtOffset(s_type*,field) +#endif + +#ifdef SUNOS_LIB_PROTOTYPES +/* Prototypes needed to get a clean compile with gcc -Wall. + * Believe it or not, these do have to be declared, at least on SunOS, + * because they aren't mentioned in the relevant system headers. + * Sun Quality Software. Gotta love it. + */ + +int getopt (int, char **, char *); + +int strcasecmp (char *, char *); +int strncasecmp (char *, char *, int); +int toupper(int); +int tolower(int); + +int printf (char *, ...); +int fprintf (FILE *, char *, ...); +int fputs (char *, FILE *); +int fread (char *, int, int, FILE *); +int fwrite (char *, int, int, FILE *); +int fflush (FILE *); +int fclose (FILE *); +int ungetc (int, FILE *); +int _filbuf (FILE *); /* !!! */ +int _flsbuf (unsigned char, FILE *); /* !!! */ +int sscanf (char *, char *, ...); +void setbuf (FILE *, char *); +void perror (char *); + +time_t time (time_t *); +int strftime (char *, int, char *, struct tm *); + +int initgroups (char *, int); +int wait3 (int *, int, void*); /* Close enough for us... */ +int lstat (const char *, struct stat *); +int stat (const char *, struct stat *); +int flock (int, int); +int getwd (char *); +#ifndef NO_KILLPG +int killpg(int, int); +#endif +int socket (int, int, int); +int setsockopt (int, int, int, const char*, int); +int listen (int, int); +int bind (int, struct sockaddr *, int); +int connect (int, struct sockaddr *, int); +int accept (int, struct sockaddr *, int *); +int shutdown (int, int); + +int getsockname (int s, struct sockaddr *name, int *namelen); +int getpeername (int s, struct sockaddr *name, int *namelen); +int gethostname (char *name, int namelen); +void syslog (int, char *, ...); +char *mktemp (char *); + +#include <stdarg.h> +long vfprintf (FILE *, char *, va_list); + +#endif diff --git a/RELEASE_1_1_X/src/include/explain.h b/RELEASE_1_1_X/src/include/explain.h new file mode 100644 index 00000000000..5912502585e --- /dev/null +++ b/RELEASE_1_1_X/src/include/explain.h @@ -0,0 +1,23 @@ +#ifndef EXPLAIN +#define DEF_Explain +#define Explain0(f) +#define Explain1(f,a1) +#define Explain2(f,a1,a2) +#define Explain3(f,a1,a2,a3) +#define Explain4(f,a1,a2,a3,a4) +#define Explain5(f,a1,a2,a3,a4,a5) +#define Explain6(f,a1,a2,a3,a4,a5,a6) +#else +#define DEF_Explain static const char *__ExplainFile=__FILE__; +void _Explain(const char *szFile,int nLine,const char *szFmt,...); +#define Explain0(f) _Explain(__ExplainFile,__LINE__,f) +#define Explain1(f,a1) _Explain(__ExplainFile,__LINE__,f,a1) +#define Explain2(f,a1,a2) _Explain(__ExplainFile,__LINE__,f,a1,a2) +#define Explain3(f,a1,a2,a3) _Explain(__ExplainFile,__LINE__,f,a1,a2,a3) +#define Explain4(f,a1,a2,a3,a4) _Explain(__ExplainFile,__LINE__,f,a1,a2,a3,a4) +#define Explain5(f,a1,a2,a3,a4,a5) \ + _Explain(__ExplainFile,__LINE__,f,a1,a2,a3,a4,a5) +#define Explain6(f,a1,a2,a3,a4,a5,a6) \ + _Explain(__ExplainFile,__LINE__,f,a1,a2,a3,a4,a5,a6) + +#endif diff --git a/RELEASE_1_1_X/src/include/http_conf_globals.h b/RELEASE_1_1_X/src/include/http_conf_globals.h new file mode 100644 index 00000000000..6bdcac5e63c --- /dev/null +++ b/RELEASE_1_1_X/src/include/http_conf_globals.h @@ -0,0 +1,83 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +/* + * Process config --- what the process ITSELF is doing + */ + +extern int standalone; +extern uid_t user_id; +extern char *user_name; +extern gid_t group_id; +extern int max_requests_per_child; +extern struct in_addr bind_address; +extern listen_rec *listeners; +extern int daemons_to_start; +extern int daemons_min_free; +extern int daemons_max_free; +extern int daemons_limit; + +extern char *pid_fname; +extern char *scoreboard_fname; +extern char *server_argv0; + +/* Trying to allocate these in the config pool gets us into some *nasty* + * chicken-and-egg problems in http_main.c --- where do you stick them + * when pconf gets cleared? Better to just allocate a little space + * statically... + */ + +extern char server_root[MAX_STRING_LEN]; +extern char server_confname[MAX_STRING_LEN]; + diff --git a/RELEASE_1_1_X/src/include/http_config.h b/RELEASE_1_1_X/src/include/http_config.h new file mode 100644 index 00000000000..7228e4b7eb8 --- /dev/null +++ b/RELEASE_1_1_X/src/include/http_config.h @@ -0,0 +1,260 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +/* + * The central data structures around here... + */ + +/* Command dispatch structures... */ + +enum cmd_how { + RAW_ARGS, /* cmd_func parses command line itself */ + TAKE1, /* one argument only */ + TAKE2, /* two arguments only */ + ITERATE, /* one argument, occuring multiple times + * (e.g., IndexIgnore) + */ + ITERATE2, /* two arguments, 2nd occurs multiple times + * (e.g., AddIcon) + */ + FLAG, /* One of 'On' or 'Off' */ + NO_ARGS /* No args at all, e.g. </Directory> */ +}; + +typedef struct command_struct { + char *name; /* Name of this command */ + char *(*func)(); /* Function invoked */ + void *cmd_data; /* Extra data, for functions which + * implement multiple commands... + */ + int req_override; /* What overrides need to be allowed to + * enable this command. + */ + enum cmd_how args_how; /* What the command expects as arguments */ + + char *errmsg; /* 'usage' message, in case of syntax errors */ +} command_rec; + +/* Values for the req_override slot */ + +#define OR_NONE 0 +#define OR_LIMIT 1 +#define OR_OPTIONS 2 +#define OR_FILEINFO 4 +#define OR_AUTHCFG 8 +#define OR_INDEXES 16 +#define OR_UNSET 32 +#define ACCESS_CONF 64 /* command valid in access.conf */ +#define RSRC_CONF 128 /* command valid in srm.conf */ +#define OR_ALL (OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG|OR_INDEXES) + +/* + * This structure is passed to a command which is being invoked, + * to carry a large variety of miscellaneous data which is all of + * use to *somebody*... + */ + +typedef struct { + void *info; /* Argument to command from cmd_table */ + int override; /* Which allow-override bits are set */ + int limited; /* Which methods are <Limit>ed */ + + char *config_file; /* Filename cmd read from */ + int config_line; /* Line cmd read from */ + FILE *infile; /* fd for more lines (not currently used) */ + + pool *pool; /* Pool to allocate new storage in */ + pool *temp_pool; /* Pool for scratch memory; persists during + * configuration, but wiped before the first + * request is served... + */ + server_rec *server; /* Server_rec being configured for */ + char *path; /* If configuring for a directory, + * pathname of that directory. + */ +} cmd_parms; + +/* This structure records the existence of handlers in a module... */ + +typedef struct { + char *content_type; + int (*handler) (); +} handler_rec; + +/* + * Module structures. Just about everything is dispatched through + * these, directly or indirectly (through the command and handler + * tables). + */ + +typedef struct module_struct { + int version; /* API version, *not* module version; + * check that module is compatible with this + * version of the server. + */ + int module_index; /* Index to this modules structures in + * config vectors. + */ + struct module_struct *next; + +#ifdef ULTRIX_BRAIN_DEATH + void (*init)(); + void *(*create_dir_config)(); + void *(*merge_dir_config)(); + void *(*create_server_config)(); + void *(*merge_server_config)(); +#else + void (*init)(server_rec *, pool *); + void *(*create_dir_config)(pool *p, char *dir); + void *(*merge_dir_config)(pool *p, void *base_conf, void *new_conf); + void *(*create_server_config)(pool *p, server_rec *s); + void *(*merge_server_config)(pool *p, void *base_conf, void *new_conf); +#endif + + command_rec *cmds; + handler_rec *handlers; + + /* Hooks for getting into the middle of server ops... + * + * translate_handler --- translate URI to filename + * access_checker --- check access by host address, etc. All of these + * run; if all decline, that's still OK. + * check_user_id --- get and validate user id from the HTTP request + * auth_checker --- see if the user (from check_user_id) is OK *here*. + * If all of *these* decline, the request is rejected + * (as a SERVER_ERROR, since the module which was + * supposed to handle this was configured wrong). + * type_checker --- Determine MIME type of the requested entity; + * sets content_type, _encoding and _language fields. + * logger --- log a transaction. Not supported yet out of sheer + * laziness on my part. + */ + + int (*translate_handler)(request_rec *); + int (*check_user_id)(request_rec *); + int (*auth_checker)(request_rec *); + int (*access_checker)(request_rec *); + int (*type_checker)(request_rec *); + int (*fixer_upper)(request_rec *); + int (*logger)(request_rec *); +} module; + +/* Initializer for the first few module slots, which are only + * really set up once we start running. Note that the first word + * is a version check; this should allow us to deal with changes to + * the API (the server can detect an old-format module, and either + * handle it back-compatibly, or at least signal an error). + */ + +#define MODULE_MAGIC_NUMBER 19960526 +#define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER, 0, NULL + +/* Generic accessors for other modules to get at their own module-specific + * data + */ + +void *get_module_config (void *conf_vector, module *m); +void set_module_config (void *conf_vector, module *m, void *val); + +/* Generic command handling function... */ + +char *set_string_slot (cmd_parms *, char *, char *); + +/* For modules which need to read config files, open logs, etc. ... + * this returns the fname argument if it begins with '/'; otherwise + * it relativizes it wrt server_root. + */ + +char *server_root_relative (pool *p, char *fname); + +/* Finally, the hook for dynamically loading modules in... */ + +void add_module (module *m); + +#ifdef CORE_PRIVATE + +/* For http_main.c... */ + +server_rec *read_config (pool *conf_pool, pool *temp_pool, char *config_name); +void setup_prelinked_modules(); + +/* For http_request.c... */ + +void *create_request_config (pool *p); +void *create_per_dir_config (pool *p); +void *merge_per_dir_configs (pool *p, void *base, void *new); + +/* For http_core.c... (<Directory> command and virtual hosts) */ + +int parse_htaccess(void **result, request_rec *r, int override, + char *path, char *file); +char *srm_command_loop (cmd_parms *parms, void *config); + +server_rec *init_virtual_host (pool *p, char *hostname); +int is_virtual_server (server_rec *); +void process_resource_config(server_rec *s, char *fname, pool *p, pool *ptemp); + +/* Module-method dispatchers, also for http_request.c */ + +int translate_name (request_rec *); +int directory_walk (request_rec *); /* check symlinks, get per-dir config */ +int check_access (request_rec *); /* check access on non-auth basis */ +int check_user_id (request_rec *); /* obtain valid username from client auth */ +int check_auth (request_rec *); /* check (validated) user is authorized here */ +int find_types (request_rec *); /* identify MIME type */ +int run_fixups (request_rec *); /* poke around for other metainfo, etc.... */ +int invoke_handler (request_rec *); +int log_transaction (request_rec *r); + +#endif diff --git a/RELEASE_1_1_X/src/include/http_core.h b/RELEASE_1_1_X/src/include/http_core.h new file mode 100644 index 00000000000..2af40dbeece --- /dev/null +++ b/RELEASE_1_1_X/src/include/http_core.h @@ -0,0 +1,174 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +/***************************************************************** + * + * The most basic server code is encapsulated in a single module + * known as the core, which is just *barely* functional enough to + * serve documents, though not terribly well. + * + * Largely for NCSA back-compatibility reasons, the core needs to + * make pieces of its config structures available to other modules. + * The accessors are declared here, along with the interpretation + * of one of them (allow_options). + */ + +#define OPT_NONE 0 +#define OPT_INDEXES 1 +#define OPT_INCLUDES 2 +#define OPT_SYM_LINKS 4 +#define OPT_EXECCGI 8 +#define OPT_UNSET 16 +#define OPT_INCNOEXEC 32 +#define OPT_SYM_OWNER 64 +#define OPT_MULTI 128 +#define OPT_ALL (OPT_INDEXES|OPT_INCLUDES|OPT_SYM_LINKS|OPT_EXECCGI) + +/* options for get_remote_host() */ +#define REMOTE_HOST (0) +#define REMOTE_NAME (1) + +int allow_options (request_rec *); +int allow_overrides (request_rec *); +char *default_type (request_rec *); +char *document_root (request_rec *); /* Don't use this! If your request went + * through a Userdir, or something like + * that, it'll screw you. But it's + * back-compatible... + */ +extern const char *get_remote_host(conn_rec *conn, void *dir_config, int type); +extern const char *get_remote_logname(request_rec *r); + +/* Authentication stuff. This is one of the places where compatibility + * with the old config files *really* hurts; they don't discriminate at + * all between different authentication schemes, meaning that we need + * to maintain common state for all of them in the core, and make it + * available to the other modules through interfaces. + */ + +typedef struct { + int method_mask; + char *requirement; +} require_line; + +char *auth_type (request_rec *); +char *auth_name (request_rec *); +array_header *requires (request_rec *); + +#ifdef CORE_PRIVATE + +/* + * Core is also unlike other modules in being implemented in more than + * one file... so, data structures are declared here, even though most of + * the code that cares really is in http_core.c. Also, anothre accessor. + */ + +char *response_code_string (request_rec *r, int error_index); + +extern module core_module; + +/* Per-directory configuration */ + +typedef char allow_options_t; +typedef char overrides_t; + +typedef struct { + char *d; + allow_options_t opts; + overrides_t override; + + /* MIME typing --- the core doesn't do anything at all with this, + * but it does know what to slap on a request for a document which + * goes untyped by other mechanisms before it slips out the door... + */ + + char *default_type; + + /* Authentication stuff. Groan... */ + + char *auth_type; + char *auth_name; + array_header *requires; + + int content_md5; + + /* Custom response config. These can contain text or a URL to redirect to. + */ + + char *response_code_strings[RESPONSE_CODES+1]; + + /* Hostname resolution etc */ + int hostname_lookups; + int do_rfc1413; /* See if client is advertising a username? */ + +} core_dir_config; + +/* Per-server core configuration */ + +typedef struct { + + /* Name translations --- we want the core to be able to do *something* + * so it's at least a minimally functional web server on its own (and + * can be tested that way). But let's keep it to the bare minimum: + */ + char *document_root; + + /* Access control */ + + char *access_name; + array_header *sec; + array_header *sec_url; +} core_server_config; + +#endif diff --git a/RELEASE_1_1_X/src/include/http_log.h b/RELEASE_1_1_X/src/include/http_log.h new file mode 100644 index 00000000000..32fd4c4d4d7 --- /dev/null +++ b/RELEASE_1_1_X/src/include/http_log.h @@ -0,0 +1,64 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +void open_logs (server_rec *, pool *p); +void error_log2stderr (server_rec *); + +void log_pid (pool *p, char *pid_fname); +void log_error(char *err, server_rec *s); +extern void log_unixerr(const char *routine, const char *file, + const char *msg, server_rec *s); +void log_printf(const server_rec *s, const char *fmt, ...); +void log_reason(char *reason, char *fname, request_rec *r); + diff --git a/RELEASE_1_1_X/src/include/http_main.h b/RELEASE_1_1_X/src/include/http_main.h new file mode 100644 index 00000000000..30aea737e07 --- /dev/null +++ b/RELEASE_1_1_X/src/include/http_main.h @@ -0,0 +1,99 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +/* + * Routines in http_main.c which other code --- in particular modules --- + * may want to call. Right now, that's limited to timeout handling. + * There are two functions which modules can call to trigger a timeout + * (with the per-virtual-server timeout duration); these are hard_timeout + * and soft_timeout. + * + * The difference between the two is what happens when the timeout + * expires (or earlier than that, if the client connection aborts) --- + * a soft_timeout just puts the connection to the client in an + * "aborted" state, which will cause http_protocol.c to stop trying to + * talk to the client, but otherwise allows the code to continue normally. + * hard_timeout(), by contrast, logs the request, and then aborts it + * completely --- longjmp()ing out to the accept() loop in http_main. + * Any resources tied into the request's resource pool will be cleaned up; + * everything that isn't will leak. + * + * soft_timeout() is recommended as a general rule, because it gives your + * code a chance to clean up. However, hard_timeout() may be the most + * convenient way of dealing with timeouts waiting for some external + * resource other than the client, if you can live with the restrictions. + * + * (When a hard timeout is in scope, critical sections can be guarded + * with block_alarms() and unblock_alarms() --- these are declared in + * alloc.c because they are most often used in conjunction with + * routines to allocate something or other, to make sure that the + * cleanup does get registered before any alarm is allowed to happen + * which might require it to be cleaned up; they * are, however, + * implemented in http_main.c). + * + * kill_timeout() will disarm either variety of timeout. + * + * reset_timeout() resets the timeout in progress. + */ + +void hard_timeout (char *, request_rec *); +void soft_timeout (char *, request_rec *); +void kill_timeout (request_rec *); +void reset_timeout (request_rec *); + +void sync_scoreboard_image (); +int update_child_status (int child_num, int status, request_rec *r); +int get_child_status (int child_num); +int count_busy_servers (); +int count_idle_servers (); diff --git a/RELEASE_1_1_X/src/include/http_protocol.h b/RELEASE_1_1_X/src/include/http_protocol.h new file mode 100644 index 00000000000..c4687393223 --- /dev/null +++ b/RELEASE_1_1_X/src/include/http_protocol.h @@ -0,0 +1,173 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +/* + * Prototypes for routines which either talk directly back to the user, + * or control the ones that eventually do. + */ + +/* Read a request and fill in the fields. */ + +request_rec *read_request (conn_rec *c); + +/* Send header for http response */ + +void send_http_header (request_rec *l); + +/* Send error back to client... last arg indicates error status in case + * we get an error in the process of trying to deal with an ErrorDocument + * to handle some other error. In that case, we print the default report + * for the first thing that went wrong, and more briefly report on the + * problem with the ErrorDocument. + */ + +void send_error_response (request_rec *r, int recursive_error); + +/* Set last modified header line from the lastmod date of the associated file. + * Also, set content length. + * + * May return an error status, typically USE_LOCAL_COPY (that when the + * permit_cache argument is set to one). + */ + +int set_content_length (request_rec *r, long length); +int set_keepalive (request_rec *r); +int set_last_modified (request_rec *r, time_t mtime); + +void add_env_var (array_header *env, char *header_name, char *val); + +/* Other ways to send stuff at the client. All of these keep track + * of bytes_sent automatically. This indirection is intended to make + * it a little more painless to slide things like HTTP-NG packetization + * underneath the main body of the code later. In the meantime, it lets + * us centralize a bit of accounting (bytes_sent). + * + * These also return the number of bytes written by the call. + * They should only be called with a timeout registered, for obvious reaasons. + * (Ditto the send_header stuff). + */ + +long send_fd(FILE *f, request_rec *r); + +/* Hmmm... could macrofy these for now, and maybe forever, though the + * definitions of the macros would get a whole lot hairier. + */ + +#if 0 +long rprintf (request_rec *r, char *s, ...); +#endif +int rputc (int c, request_rec *r); +int rputs(const char *str, request_rec *r); +int rvputs(request_rec *r, ...); +int rprintf(request_rec *r,const char *fmt,...); + +/* + * Index used in custom_responses array for a specific error code + * (only use outside protocol.c is in getting them configured). + */ + +int index_of_response (int status); + +/* Reading a block of data from the client connection (e.g., POST arg) */ + +long read_client_block (request_rec *r, char *buffer, int bufsiz); + +/* Finally, this charming little number is here to encapsulate the + * degree to which nph- scripts completely escape from any discipline + * the protocol code might care to impose (this as opposed to other + * scripts, which *partially* escape to the extent that they may try + * to explicitly set the status line). + */ + +void client_to_stdout (conn_rec *c); + + +/* Support for the Basic authentication protocol. Note that there's + * nothing that prevents these from being in mod_auth.c, except that other + * modules which wanted to provide their own variants on finding users and + * passwords for Basic auth (a fairly common request) would then require + * mod_auth to be loaded or they wouldn't work. + * + * get_basic_auth_pw returns 0 (OK) if it set the 'pw' argument (and assured + * a correct value in r->connection->user); otherwise it returns an error + * code, either SERVER_ERROR if things are really confused, AUTH_REQUIRED + * if no authentication at all seemed to be in use, or DECLINED if there + * was authentication but it wasn't Basic (in which case, the caller should + * presumably decline as well). + * + * note_basic_auth_failure arranges for the right stuff to be scribbled on + * the HTTP return so that the client knows how to authenticate itself the + * next time. As does note_digest_auth_failure for Digest auth. + * + * note_auth_failure does the same thing, but will call the correct one + * based on the authentication type in use. + * + */ + +void note_auth_failure(request_rec *r); +void note_basic_auth_failure(request_rec *r); +void note_digest_auth_failure(request_rec *r); +int get_basic_auth_pw (request_rec *r, char **pw); + +/* + * Setting up the protocol fields for subsidiary requests... + * Also, a wrapup function to keep the internal accounting straight. + */ + +void set_sub_req_protocol (request_rec *rnew, request_rec *r); +void finalize_sub_req_protocol (request_rec *sub_r); + +/* This is also useful for putting sub_reqs and internal_redirects together */ + +void parse_uri (request_rec *r, char *uri); diff --git a/RELEASE_1_1_X/src/include/http_request.h b/RELEASE_1_1_X/src/include/http_request.h new file mode 100644 index 00000000000..b433457ff85 --- /dev/null +++ b/RELEASE_1_1_X/src/include/http_request.h @@ -0,0 +1,93 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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 is the code which handles the main line of request + * processing, once a request has been read in (finding the right per- + * directory configuration, building it if necessary, and calling all + * the module dispatch functions in the right order). + * + * The pieces here which are public to the modules, allow them to learn + * how the server would handle some other file or URI, or perhaps even + * direct the server to serve that other file instead of the one the + * client requested directly. + * + * There are two ways to do that. The first is the sub_request mechanism, + * which handles looking up files and URIs as adjuncts to some other + * request (e.g., directory entries for multiviews and directory listings); + * the lookup functions stop short of actually running the request, but + * (e.g., for includes), a module may call for the request to be run + * by calling run_sub_req. The space allocated to create sub_reqs can be + * reclaimed by calling destroy_sub_req --- be sure to copy anything you care + * about which was allocated in its pool elsewhere before doing this. + */ + +request_rec *sub_req_lookup_uri (char *new_file, request_rec *r); +request_rec *sub_req_lookup_file (char *new_file, request_rec *r); +int run_sub_req (request_rec *r); +void destroy_sub_req (request_rec *r); + +/* + * Then there's the case that you want some other request to be served + * as the top-level request INSTEAD of what the client requested directly. + * If so, call this from a handler, and then immediately return OK. + */ + +void internal_redirect (char *new_uri, request_rec *); +void internal_redirect_handler (char *new_uri, request_rec *); + +#ifdef CORE_PRIVATE +/* Function called by main.c to handle first-level request */ +void process_request (request_rec *); +int default_handler (request_rec *); +#endif diff --git a/RELEASE_1_1_X/src/include/httpd.h b/RELEASE_1_1_X/src/include/httpd.h new file mode 100644 index 00000000000..4e4ab533b6d --- /dev/null +++ b/RELEASE_1_1_X/src/include/httpd.h @@ -0,0 +1,560 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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.h: header for simple (ha! not anymore) http daemon + */ + +/* Headers in which EVERYONE has an interest... */ + +#include "conf.h" +#include "alloc.h" +#include "buff.h" + +/* ----------------------------- config dir ------------------------------ */ + +/* Define this to be the default server home dir. Anything later in this + * file with a relative pathname will have this added. + */ +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define HTTPD_ROOT "/os2httpd" +#else +#define HTTPD_ROOT "/usr/local/etc/httpd" +#endif + +/* Root of server */ +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define DOCUMENT_LOCATION "/os2httpd/docs" +#else +#define DOCUMENT_LOCATION "/usr/local/etc/httpd/htdocs" +#endif + +/* Max. number of dynamically loaded modules */ +#define DYNAMIC_MODULE_LIMIT 64 + +/* Default administrator's address */ +#define DEFAULT_ADMIN "[no address given]" + +/* + * --------- You shouldn't have to edit anything below this line ---------- + * + * Any modifications to any defaults not defined above should be done in the + * respective config. file. + * + */ + + +/* -------------- Port number for server running standalone --------------- */ + +#define DEFAULT_PORT 80 + +/* --------- Default user name and group name running standalone ---------- */ +/* --- These may be specified as numbers by placing a # before a number --- */ + +#define DEFAULT_USER "#-1" +#define DEFAULT_GROUP "#-1" + +/* The name of the log files */ +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define DEFAULT_XFERLOG "logs/access.log" +#else +#define DEFAULT_XFERLOG "logs/access_log" +#endif +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define DEFAULT_ERRORLOG "logs/error.log" +#else +#define DEFAULT_ERRORLOG "logs/error_log" +#endif +#define DEFAULT_PIDLOG "logs/httpd.pid" +#define DEFAULT_SCOREBOARD "logs/apache_runtime_status" + +/* Define this to be what your HTML directory content files are called */ +#define DEFAULT_INDEX "index.html" + +/* Define this to 1 if you want fancy indexing, 0 otherwise */ +#define DEFAULT_INDEXING 0 + +/* Define this to be what type you'd like returned for files with unknown */ +/* suffixes */ +#define DEFAULT_TYPE "text/html" + +/* Define this to be what your per-directory security files are called */ +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define DEFAULT_ACCESS_FNAME "htaccess" +#else +#define DEFAULT_ACCESS_FNAME ".htaccess" +#endif + +/* The name of the server config file */ +#define SERVER_CONFIG_FILE "conf/httpd.conf" + +/* The name of the document config file */ +#define RESOURCE_CONFIG_FILE "conf/srm.conf" + +/* The name of the MIME types file */ +#define TYPES_CONFIG_FILE "conf/mime.types" + +/* The name of the access file */ +#define ACCESS_CONFIG_FILE "conf/access.conf" + +/* Whether we should enable rfc1413 identity checking */ +#define DEFAULT_RFC1413 0 +/* The default directory in user's home dir */ +#define DEFAULT_USER_DIR "public_html" + +/* The default path for CGI scripts if none is currently set */ +#define DEFAULT_PATH "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin" + +/* The path to the Bourne shell, for parsed docs */ +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define SHELL_PATH "CMD.EXE" +#else +#define SHELL_PATH "/bin/sh" +#endif + +/* The default string lengths */ +#define MAX_STRING_LEN HUGE_STRING_LEN +#define HUGE_STRING_LEN 8192 + +/* The timeout for waiting for messages */ +#define DEFAULT_TIMEOUT 1200 + +/* The timeout for waiting for keepalive timeout until next request */ +#define DEFAULT_KEEPALIVE_TIMEOUT 15 + +/* The number of requests to entertain per connection */ +#define DEFAULT_KEEPALIVE 5 + +/* The size of the server's internal read-write buffers */ +#define IOBUFSIZE 8192 + +/* The number of header lines we will accept from a client */ +#define MAX_HEADERS 200 + +/* Number of servers to spawn off by default --- also, if fewer than + * this free when the caretaker checks, it will spawn more. + */ +#define DEFAULT_START_DAEMON 5 + +/* Maximum number of *free* server processes --- more than this, and + * they will die off. + */ + +#define DEFAULT_MAX_FREE_DAEMON 10 + +/* Minimum --- fewer than this, and more will be created */ + +#define DEFAULT_MIN_FREE_DAEMON 5 + +/* Limit on the total --- clients will be locked out if more servers than + * this are needed. It is intended solely to keep the server from crashing + * when things get out of hand. + * + * We keep a hard maximum number of servers, for two reasons --- first off, + * in case something goes seriously wrong, we want to stop the fork bomb + * short of actually crashing the machine we're running on by filling some + * kernel table (I was originally going to make this 256, but it turns out + * that that would actually fill the process table on reasonably configured + * machines). Secondly, it keeps the size of the scoreboard file small + * enough that we can read the whole thing without worrying too much about + * the overhead. + */ +#define HARD_SERVER_LIMIT 150 + +/* Number of requests to try to handle in a single process. If <= 0, + * the children don't die off. That's the default here, since I'm still + * interested in finding and stanching leaks. + */ + +#define DEFAULT_MAX_REQUESTS_PER_CHILD 0 + +/* If you have altered Apache and wish to change the SERVER_VERSION define + * below, please keep to the HTTP/1.0 specification. This states that + * the identification string should consist of product tokens with an optional + * slash and version designator. Sub-products which form a significant part + * of the application can be listed, separated by whitespace. The tokens + * are listed in order of their significance for identifying the application. + * + * "Product tokens should be short and to the point -- use of them for + * advertizing or other non-essential information is explicitly forbidden." + * + * Example: "Apache/1.1b3 MrWidget/0.1-alpha" + */ + +#define SERVER_VERSION "Apache/1.1.0" /* SEE COMMENTS ABOVE */ + +#define SERVER_PROTOCOL "HTTP/1.0" +#define SERVER_SUPPORT "http://www.apache.org/" + +#define DECLINED -1 /* Module declines to handle */ +#define OK 0 /* Module has handled this stage. */ + +/* ------------------------------ error types ------------------------------ */ + +#define DOCUMENT_FOLLOWS 200 +#define REDIRECT 302 +#define USE_LOCAL_COPY 304 +#define BAD_REQUEST 400 +#define AUTH_REQUIRED 401 +#define FORBIDDEN 403 +#define NOT_FOUND 404 +#define SERVER_ERROR 500 +#define NOT_IMPLEMENTED 501 +#define BAD_GATEWAY 502 +#define SERVICE_UNAVAILABLE 503 +#define RESPONSE_CODES 10 + +#define METHODS 6 +#define M_GET 0 +#define M_PUT 1 +#define M_POST 2 +#define M_DELETE 3 +#define M_CONNECT 4 +#define M_INVALID 5 + +#define CGI_MAGIC_TYPE "application/x-httpd-cgi" +#define INCLUDES_MAGIC_TYPE "text/x-server-parsed-html" +#define INCLUDES_MAGIC_TYPE3 "text/x-server-parsed-html3" +#define MAP_FILE_MAGIC_TYPE "application/x-type-map" +#define ASIS_MAGIC_TYPE "httpd/send-as-is" +#define DIR_MAGIC_TYPE "httpd/unix-directory" +#define STATUS_MAGIC_TYPE "application/x-httpd-status" + +/* Just in case your linefeed isn't the one the other end is expecting. */ +#define LF 10 +#define CR 13 + +/* Things which may vary per file-lookup WITHIN a request --- + * e.g., state of MIME config. Basically, the name of an object, info + * about the object, and any other info we may ahve which may need to + * change as we go poking around looking for it (e.g., overridden by + * .htaccess files). + * + * Note how the default state of almost all these things is properly + * zero, so that allocating it with pcalloc does the right thing without + * a whole lot of hairy initialization... so long as we are willing to + * make the (fairly) portable assumption that the bit pattern of a NULL + * pointer is, in fact, zero. + */ + +/* This represents the result of calling htaccess; these are cached for + * each request. + */ +struct htaccess_result +{ + char *dir; /* the directory to which this applies */ + int override; /* the overrides allowed for the .htaccess file */ + void *htaccess; /* the configuration directives */ +/* the next one, or NULL if no more; N.B. never change this */ + const struct htaccess_result *next; +}; + + +typedef struct conn_rec conn_rec; +typedef struct server_rec server_rec; +typedef struct request_rec request_rec; +typedef struct listen_rec listen_rec; + +struct request_rec { + + pool *pool; + conn_rec *connection; + server_rec *server; + + request_rec *next; /* If we wind up getting redirected, + * pointer to the request we redirected to. + */ + request_rec *prev; /* If this is an internal redirect, + * pointer to where we redirected *from*. + */ + + request_rec *main; /* If this is a sub_request (see request.h) + * pointer back to the main request. + */ + + /* Info about the request itself... we begin with stuff that only + * protocol.c should ever touch... + */ + + char *the_request; /* First line of request, so we can log it */ + int assbackwards; /* HTTP/0.9, "simple" request */ + int proxyreq; /* A proxy request */ + int header_only; /* HEAD request, as opposed to GET */ + char *protocol; /* Protocol, as given to us, or HTTP/0.9 */ + int proto_num; /* Number version of protocol; 1.1 = 1001 */ + char *hostname; /* Host, as set by full URI or Host: */ + int hostlen; /* Length of http://host:port in full URI */ + + char *status_line; /* Status line, if set by script */ + int status; /* In any case */ + + /* Request method, two ways; also, protocol, etc.. Outside of protocol.c, + * look, but don't touch. + */ + + char *method; /* GET, HEAD, POST, etc. */ + int method_number; /* M_GET, M_POST, etc. */ + + int sent_bodyct; /* byte count in stream is for body */ + long bytes_sent; /* body byte count, for easy access */ + + /* MIME header environments, in and out. Also, an array containing + * environment variables to be passed to subprocesses, so people can + * write modules to add to that environment. + * + * The difference between headers_out and err_headers_out is that the + * latter are printed even on error, and persist across internal redirects + * (so the headers printed for ErrorDocument handlers will have them). + * + * The 'notes' table is for notes from one module to another, with no + * other set purpose in mind... + */ + + table *headers_in; + table *headers_out; + table *err_headers_out; + table *subprocess_env; + table *notes; + + char *content_type; /* Break these out --- we dispatch on 'em */ + char *handler; /* What we *really* dispatch on */ + + char *content_encoding; + char *content_language; + + int no_cache; + + /* What object is being requested (either directly, or via include + * or content-negotiation mapping). + */ + + char *uri; /* complete URI for a proxy req, or + URL path for a non-proxy req */ + char *filename; + char *path_info; + char *args; /* QUERY_ARGS, if any */ + struct stat finfo; /* ST_MODE set to zero if no such file */ + + /* Various other config info which may change with .htaccess files + * These are config vectors, with one void* pointer for each module + * (the thing pointed to being the module's business). + */ + + void *per_dir_config; /* Options set in config files, etc. */ + void *request_config; /* Notes on *this* request */ + +/* + * a linked list of the configuration directives in the .htaccess files + * accessed by this request. + * N.B. always add to the head of the list, _never_ to the end. + * that way, a sub request's list can (temporarily) point to a parent's list + */ + const struct htaccess_result *htaccess; +}; + + +/* Things which are per connection + */ + +struct conn_rec { + + pool *pool; + server_rec *server; + + /* Information about the connection itself */ + + int child_num; /* The number of the child handling conn_rec */ + BUFF *client; /* Connetion to the guy */ + int aborted; /* Are we still talking? */ + + /* Who is the client? */ + + struct sockaddr_in local_addr; /* local address */ + struct sockaddr_in remote_addr;/* remote address */ + char *remote_ip; /* Client's IP address */ + char *remote_host; /* Client's DNS name, if known. + * NULL if DNS hasn't been checked, + * "" if it has and no address was found. + * N.B. Only access this though + * get_remote_host() */ + char *remote_logname; /* Only ever set if doing_rfc931 + * N.B. Only access this through + * get_remote_logname() */ + char *user; /* If an authentication check was made, + * this gets set to the user name. We assume + * that there's only one user per connection(!) + */ + char *auth_type; /* Ditto. */ + + int keepalive; /* Are we using HTTP Keep-Alive? */ + int keptalive; /* Did we use HTTP Keep-Alive? */ + int keepalives; /* How many times have we used it? */ +}; + +/* Per-vhost config... */ + +struct server_rec { + + server_rec *next; + + /* Full locations of server config info */ + + char *srm_confname; + char *access_confname; + + /* Contact information */ + + char *server_admin; + char *server_hostname; + short port; /* for redirects, etc. */ + + /* Log files --- note that transfer log is now in the modules... */ + + char *error_fname; + FILE *error_log; + + /* Module-specific configuration for server, and defaults... */ + + int is_virtual; /* true if this is the virtual server */ + void *module_config; /* Config vector containing pointers to + * modules' per-server config structures. + */ + void *lookup_defaults; /* MIME type info, etc., before we start + * checking per-directory info. + */ + /* Transaction handling */ + + struct in_addr host_addr; /* The bound address, for this server */ + short host_port; /* The bound port, for this server */ + int timeout; /* Timeout, in seconds, before we give up */ + int keep_alive_timeout; /* Seconds we'll wait for another request */ + int keep_alive; /* Maximum requests per connection */ + + char *path; /* Pathname for ServerPath */ + int pathlen; /* Length of path */ + + char *names; /* Wildcarded names for HostAlias servers */ + char *virthost; /* The name given in <VirtualHost> */ +}; + +/* These are more like real hosts than virtual hosts */ +struct listen_rec { + listen_rec *next; + struct sockaddr_in local_addr; /* local IP address and port */ +/* more stuff here, like which protocol is bound to the port */ +}; + +/* Prototypes for utilities... util.c. + */ + +/* Time */ +extern const char month_snames[12][4]; + +struct tm *get_gmtoff(long *tz); +char *get_time(); +char *ht_time (pool *p, time_t t, char *fmt, int gmt); +char *gm_timestr_822(pool *p, time_t t); + +/* String handling */ + +char *getword(pool *p, char **line, char stop); +char *getword_nulls (pool *p, char **line, char stop); +char *getword_conf (pool *p, char **line); + +char *get_token (pool *p, char **accept_line, int accept_white); + +int is_url(char *u); +extern int unescape_url(char *url); +void no2slash(char *name); +void getparents(char *name); +char *escape_path_segment(pool *p, const char *s); +char *os_escape_path(pool *p,const char *path,int partial); +char *escape_uri (pool *p, char *s); +extern char *escape_html(pool *p, const char *s); +char *construct_url (pool *p, char *path, server_rec *s); +char *escape_shell_cmd (pool *p, char *s); + +int count_dirs(char *path); +char *make_dirstr(pool *a, char *s, int n); +char *make_full_path(pool *a, char *dir, char *f); + +int is_matchexp(char *str); +int strcmp_match(char *str, char *exp); +int strcasecmp_match(char *str, char *exp); +char *uudecode (pool *, char *); + +void str_tolower (char *); +int ind (const char *, char); /* Sigh... */ +int rind (const char *, char); + +int cfg_getline(char *s, int n, FILE *f); + +/* Misc system hackery */ + +uid_t uname2id(char *name); +gid_t gname2id(char *name); +int is_directory(char *name); +int can_exec(struct stat *); +void chdir_file(char *file); + +char *get_local_host(pool *); +unsigned long get_virthost_addr (char *hostname, short int *port); + +extern time_t restart_time; diff --git a/RELEASE_1_1_X/src/include/md5.h b/RELEASE_1_1_X/src/include/md5.h new file mode 100644 index 00000000000..786b010ace0 --- /dev/null +++ b/RELEASE_1_1_X/src/include/md5.h @@ -0,0 +1,99 @@ +/* + * This is work is derived from material Copyright RSA Data Security, Inc. + * + * The RSA copyright statement and Licence for that original material is + * included below. This is followed by the Apache copyright statement and + * licence for the modifications made to that material. + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* ==================================================================== + * Copyright (c) 1996 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. 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/>. + * + */ + + +/* MD5.H - header file for MD5C.C */ + +/* UINT4 defines a four byte word */ +typedef unsigned int UINT4; + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +extern void MD5Init(MD5_CTX *context); +extern void MD5Update(MD5_CTX *context, const unsigned char *input, + unsigned int inputLen); +extern void MD5Final(unsigned char digest[16], MD5_CTX *context); diff --git a/RELEASE_1_1_X/src/include/rfc1413.h b/RELEASE_1_1_X/src/include/rfc1413.h new file mode 100644 index 00000000000..a15df86cd7d --- /dev/null +++ b/RELEASE_1_1_X/src/include/rfc1413.h @@ -0,0 +1,53 @@ +/* ==================================================================== + * Copyright (c) 1996 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. 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/>. + * + */ + +extern char *rfc1413(conn_rec *conn, server_rec *srv); diff --git a/RELEASE_1_1_X/src/include/scoreboard.h b/RELEASE_1_1_X/src/include/scoreboard.h new file mode 100644 index 00000000000..fe482b84c5e --- /dev/null +++ b/RELEASE_1_1_X/src/include/scoreboard.h @@ -0,0 +1,94 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +#include <sys/times.h> + +/* Scoreboard info on a process is, for now, kept very brief --- + * just status value and pid (the latter so that the caretaker process + * can properly update the scoreboard when a process dies). We may want + * to eventually add a separate set of long_score structures which would + * give, for each process, the number of requests serviced, and info on + * the current, or most recent, request. + * + * Status values: + */ + +#define SERVER_UNKNOWN (-1) /* should never be in this state */ +#define SERVER_DEAD 0 +#define SERVER_READY 1 /* Waiting for connection (or accept() lock) */ +#define SERVER_STARTING 3 /* Server Starting up */ +#define SERVER_BUSY_READ 2 /* Reading a client request */ +#define SERVER_BUSY_WRITE 4 /* Processing a client request */ +#define SERVER_BUSY_KEEPALIVE 5 /* Waiting for more requests via keepalive */ +#define SERVER_BUSY_LOG 6 /* Logging the request */ +#define SERVER_BUSY_DNS 7 /* Looking up a hostname */ + +typedef struct { + pid_t pid; + char status; +#if defined(STATUS) + unsigned long access_count; + unsigned long bytes_served; + unsigned long my_access_count; + unsigned long my_bytes_served; + unsigned long conn_bytes; + unsigned short conn_count; + struct tms times; + time_t last_used; + char client[32]; /* Keep 'em small... */ + char request[64]; /* We just want an idea... */ +#endif +} short_score; + +extern void sync_scoreboard_image(void); +short_score get_scoreboard_info(int x); diff --git a/RELEASE_1_1_X/src/include/util_md5.h b/RELEASE_1_1_X/src/include/util_md5.h new file mode 100644 index 00000000000..121dfec1e8e --- /dev/null +++ b/RELEASE_1_1_X/src/include/util_md5.h @@ -0,0 +1,59 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +#include "md5.h" + +char *md5(pool *a, unsigned char *string); +char *md5contextTo64(pool *p, MD5_CTX *context); +char *md5digest(pool *p, FILE *infile); + diff --git a/RELEASE_1_1_X/src/include/util_script.h b/RELEASE_1_1_X/src/include/util_script.h new file mode 100644 index 00000000000..056f614f134 --- /dev/null +++ b/RELEASE_1_1_X/src/include/util_script.h @@ -0,0 +1,64 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +char **create_argv(pool *p, char *av0, char *args); +#ifdef __EMX__ +char **create_argv_cmd(pool *p, char *av0, char *args, char *path); +#endif +char **create_environment(pool *p, table *t); +void add_cgi_vars(request_rec *r); +void add_common_vars(request_rec *r); +int scan_script_header(request_rec *r, FILE *f); +void send_size(size_t size, request_rec *r); + diff --git a/RELEASE_1_1_X/src/main/alloc.c b/RELEASE_1_1_X/src/main/alloc.c new file mode 100644 index 00000000000..9afaa9abf8c --- /dev/null +++ b/RELEASE_1_1_X/src/main/alloc.c @@ -0,0 +1,1070 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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/>. + * + */ + + +/* + * Resource allocation code... the code here is responsible for making + * sure that nothing leaks. + * + * rst --- 4/95 --- 6/95 + */ + +#include "conf.h" +#include "alloc.h" + +#include <stdarg.h> + +/***************************************************************** + * + * Managing free storage blocks... + */ + +union align +{ + /* Types which are likely to have the longest RELEVANT alignment + * restrictions... + */ + + char *cp; + void (*f)(); + long l; + FILE *fp; + double d; +}; + +#define CLICK_SZ (sizeof(union align)) + +union block_hdr +{ + union align a; + + /* Actual header... */ + + struct { + char *endp; + union block_hdr *next; + char *first_avail; + } h; +}; + +union block_hdr *block_freelist = NULL; + + + +/* Get a completely new block from the system pool */ + +union block_hdr *malloc_block (int size) +{ + union block_hdr *blok = + (union block_hdr *)malloc(size + sizeof(union block_hdr)); + + if (blok == NULL) return NULL; + + blok->h.next = NULL; + blok->h.first_avail = (char *)(blok + 1); + blok->h.endp = size + blok->h.first_avail; + + return blok; +} + + + +void chk_on_blk_list (union block_hdr *blok, union block_hdr *free_blk) +{ + /* Debugging code. Left in for the moment. */ + + while (free_blk) { + if (free_blk == blok) { + fprintf (stderr, "Ouch! Freeing free block\n"); + exit (1); + } + free_blk = free_blk->h.next; + } +} + +/* Free a chain of blocks --- must be called with alarms blocked. */ + +void free_blocks (union block_hdr *blok) +{ + /* First, put new blocks at the head of the free list --- + * we'll eventually bash the 'next' pointer of the last block + * in the chain to point to the free blocks we already had. + */ + + union block_hdr *old_free_list = block_freelist; + + if (blok == NULL) return; /* Sanity check --- freeing empty pool? */ + + block_freelist = blok; + + /* + * Next, adjust first_avail pointers of each block --- have to do it + * sooner or later, and it simplifies the search in new_block to do it + * now. + */ + + while (blok->h.next != NULL) { + chk_on_blk_list (blok, old_free_list); + blok->h.first_avail = (char *)(blok + 1); + blok = blok->h.next; + } + + chk_on_blk_list (blok, old_free_list); + blok->h.first_avail = (char *)(blok + 1); + + /* Finally, reset next pointer to get the old free blocks back */ + + blok->h.next = old_free_list; +} + + + + +/* Get a new block, from our own free list if possible, from the system + * if necessary. Must be called with alarms blocked. + */ + +union block_hdr *new_block (int min_size) +{ + union block_hdr **lastptr = &block_freelist; + union block_hdr *blok = block_freelist; + + /* First, see if we have anything of the required size + * on the free list... + */ + + min_size += BLOCK_MINFREE; + + while (blok != NULL) { + if (min_size <= blok->h.endp - blok->h.first_avail) { + + *lastptr = blok->h.next; + blok->h.next = NULL; + return blok; + } + else { + lastptr = &blok->h.next; + blok = blok->h.next; + } + } + + /* Nope. */ + + return malloc_block (min_size); +} + + + +/* Accounting */ + +long bytes_in_block_list (union block_hdr *blok) +{ + long size = 0; + + while (blok) { + size += blok->h.endp - (char *)(blok + 1); + blok = blok->h.next; + } + + return size; +} + + +/***************************************************************** + * + * Pool internals and management... + * NB that subprocesses are not handled by the generic cleanup code, + * basically because we don't want cleanups for multiple subprocesses + * to result in multiple three-second pauses. + */ + +struct process_chain; +struct cleanup; + +static void run_cleanups (struct cleanup *); +static void free_proc_chain (struct process_chain *); + +struct pool { + union block_hdr *first; + union block_hdr *last; + struct cleanup *cleanups; + struct process_chain *subprocesses; + struct pool *sub_pools; + struct pool *sub_next; + struct pool *sub_prev; + struct pool *parent; + char *free_first_avail; +}; + +pool *permanent_pool; + +/* Each pool structure is allocated in the start of its own first block, + * so we need to know how many bytes that is (once properly aligned...). + * This also means that when a pool's sub-pool is destroyed, the storage + * associated with it is *completely* gone, so we have to make sure it + * gets taken off the parent's sub-pool list... + */ + +#define POOL_HDR_CLICKS (1 + ((sizeof(struct pool) - 1) / CLICK_SZ)) +#define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ) + +struct pool *make_sub_pool (struct pool *p) +{ + union block_hdr *blok; + pool *new_pool; + + block_alarms(); + + blok = new_block (0); + new_pool = (pool *)blok->h.first_avail; + blok->h.first_avail += POOL_HDR_BYTES; + + memset ((char *)new_pool, '\0', sizeof (struct pool)); + new_pool->free_first_avail = blok->h.first_avail; + new_pool->first = new_pool->last = blok; + + if (p) { + new_pool->parent = p; + new_pool->sub_next = p->sub_pools; + if (new_pool->sub_next) new_pool->sub_next->sub_prev = new_pool; + p->sub_pools = new_pool; + } + + unblock_alarms(); + + return new_pool; +} + +void init_alloc() { permanent_pool = make_sub_pool (NULL); } + +void clear_pool (struct pool *a) +{ + block_alarms(); + + while (a->sub_pools) + destroy_pool (a->sub_pools); + + a->sub_pools = NULL; + + run_cleanups (a->cleanups); a->cleanups = NULL; + free_proc_chain (a->subprocesses); a->subprocesses = NULL; + free_blocks (a->first->h.next); a->first->h.next = NULL; + + a->last = a->first; + a->first->h.first_avail = a->free_first_avail; + + unblock_alarms(); +} + +void destroy_pool (pool *a) +{ + block_alarms(); + clear_pool (a); + + if (a->parent) { + if (a->parent->sub_pools == a) a->parent->sub_pools = a->sub_next; + if (a->sub_prev) a->sub_prev->sub_next = a->sub_next; + if (a->sub_next) a->sub_next->sub_prev = a->sub_prev; + } + + free_blocks (a->first); + unblock_alarms(); +} + +long bytes_in_pool (pool *p) { return bytes_in_block_list (p->first); } +long bytes_in_free_blocks () { return bytes_in_block_list (block_freelist); } + +/***************************************************************** + * + * Allocating stuff... + */ + + +void *palloc (struct pool *a, int reqsize) +{ + /* Round up requested size to an even number of alignment units (core clicks) + */ + + int nclicks = 1 + ((reqsize - 1) / CLICK_SZ); + int size = nclicks * CLICK_SZ; + + /* First, see if we have space in the block most recently + * allocated to this pool + */ + + union block_hdr *blok = a->last; + char *first_avail = blok->h.first_avail; + char *new_first_avail; + + if (size <= 0) size = 1; + new_first_avail = first_avail + size; + + if (new_first_avail <= blok->h.endp) { + blok->h.first_avail = new_first_avail; + return (void *)first_avail; + } + + /* Nope --- get a new one that's guaranteed to be big enough */ + + block_alarms(); + blok = new_block (size); + a->last->h.next = blok; + a->last = blok; + unblock_alarms(); + + first_avail = blok->h.first_avail; + blok->h.first_avail += size; + + return (void *)first_avail; +} + +void *pcalloc(struct pool *a, int size) +{ + void *res = palloc (a, size); + memset (res, '\0', size); + return res; +} + +char *pstrdup(struct pool *a, const char *s) +{ + char *res; + if (s == NULL) return NULL; + res = palloc (a, strlen(s) + 1); + strcpy (res, s); + return res; +} + +char *pstrndup(struct pool *a, const char *s, int n) +{ + char *res; + if (s == NULL) return NULL; + res = palloc (a, n + 1); + strncpy (res, s, n); + res[n] = '\0'; + return res; +} + +char *pstrcat(pool *a, ...) +{ + char *cp, *argp, *res; + + /* Pass one --- find length of required string */ + + int len = 0; + va_list adummy; + + va_start (adummy, a); + + while ((cp = va_arg (adummy, char *)) != NULL) + len += strlen(cp); + + va_end (adummy); + + /* Allocate the required string */ + + res = (char *)palloc(a, len + 1); + cp = res; + + /* Pass two --- copy the argument strings into the result space */ + + va_start (adummy, a); + + while ((argp = va_arg (adummy, char *)) != NULL) { + strcpy (cp, argp); + cp += strlen(argp); + } + + va_end (adummy); + + /* Return the result string */ + + return res; +} + + +/***************************************************************** + * + * The 'array' functions... + */ + +array_header *make_array (pool *p, int nelts, int elt_size) +{ + array_header *res = (array_header *)palloc(p, sizeof(array_header)); + + if (nelts < 1) nelts = 1; /* Assure sanity if someone asks for + * array of zero elts. + */ + + res->elts = pcalloc (p, nelts * elt_size); + + res->pool = p; + res->elt_size = elt_size; + res->nelts = 0; /* No active elements yet... */ + res->nalloc = nelts; /* ...but this many allocated */ + + return res; +} + +void *push_array (array_header *arr) +{ + if (arr->nelts == arr->nalloc) { + char *new_data = pcalloc (arr->pool, arr->nalloc * arr->elt_size * 2); + + memcpy (new_data, arr->elts, arr->nalloc * arr->elt_size); + arr->elts = new_data; + arr->nalloc *= 2; + } + + ++arr->nelts; + return arr->elts + (arr->elt_size * (arr->nelts - 1)); +} + +void array_cat (array_header *dst, array_header *src) +{ + int elt_size = dst->elt_size; + + if (dst->nelts + src->nelts > dst->nalloc) { + int new_size = dst->nalloc * 2; + char *new_data; + + if (new_size == 0) ++new_size; + + while (dst->nelts + src->nelts > new_size) + new_size *= 2; + + new_data = pcalloc (dst->pool, elt_size * new_size); + memcpy (new_data, dst->elts, dst->nalloc * elt_size); + + dst->elts = new_data; + dst->nalloc = new_size; + } + + memcpy (dst->elts + dst->nelts * elt_size, src->elts, elt_size * src->nelts); + dst->nelts += src->nelts; +} + +array_header *copy_array (pool *p, array_header *arr) +{ + array_header *res = make_array (p, arr->nalloc, arr->elt_size); + + memcpy (res->elts, arr->elts, arr->elt_size * arr->nelts); + res->nelts = arr->nelts; + return res; +} + +/* This cute function copies the array header *only*, but arranges + * for the data section to be copied on the first push or arraycat. + * It's useful when the elements of the array being copied are + * read only, but new stuff *might* get added on the end; we have the + * overhead of the full copy only where it is really needed. + */ + +array_header *copy_array_hdr (pool *p, array_header *arr) +{ + array_header *res = (array_header *)palloc(p, sizeof(array_header)); + + res->elts = arr->elts; + + res->pool = p; + res->elt_size = arr->elt_size; + res->nelts = arr->nelts; + res->nalloc = arr->nelts; /* Force overflow on push */ + + return res; +} + +/* The above is used here to avoid consing multiple new array bodies... */ + +array_header *append_arrays (pool *p, + array_header *first, array_header *second) +{ + array_header *res = copy_array_hdr (p, first); + + array_cat (res, second); + return res; +} + + +/***************************************************************** + * + * The "table" functions. + */ + +table *make_table (pool *p, int nelts) { + return make_array (p, nelts, sizeof (table_entry)); +} + +table *copy_table (pool *p, table *t) { + return copy_array (p, t); +} + +array_header *table_elts (table *t) { return t; } + +char *table_get (table *t, char *key) +{ + table_entry *elts = (table_entry *)t->elts; + int i; + + if (key == NULL) return NULL; + + for (i = 0; i < t->nelts; ++i) + if (!strcasecmp (elts[i].key, key)) + return elts[i].val; + + return NULL; +} + +void table_set (table *t, const char *key, const char *val) +{ + table_entry *elts = (table_entry *)t->elts; + int i; + + for (i = 0; i < t->nelts; ++i) + if (!strcasecmp (elts[i].key, key)) { + elts[i].val = pstrdup (t->pool, val); + return; + } + + elts = (table_entry *)push_array(t); + elts->key = pstrdup (t->pool, key); + elts->val = pstrdup (t->pool, val); +} + +void table_unset( table *t, char *key ) +{ + table_entry *elts = (table_entry *)t->elts; + int i; + int j; + + for (i = 0; i < t->nelts; ++i) + if (!strcasecmp (elts[i].key, key)) { + + /* found the element to skip over + * there are any number of ways to remove an element from + * a contiguous block of memory. I've chosen one that + * doesn't do a memcpy/bcopy/array_delete, *shrug*... + */ + j = i; + ++i; + for ( ; i < t->nelts; ) { + elts[j].key = elts[i].key; + elts[j].val = elts[i].val; + ++i; + ++j; + }; + --t->nelts; + + return; + } +} + +void table_merge (table *t, char *key, char *val) +{ + table_entry *elts = (table_entry *)t->elts; + int i; + + for (i = 0; i < t->nelts; ++i) + if (!strcasecmp (elts[i].key, key)) { + elts[i].val = pstrcat (t->pool, elts[i].val, ", ", val, NULL); + return; + } + + elts = (table_entry *)push_array(t); + elts->key = pstrdup (t->pool, key); + elts->val = pstrdup (t->pool, val); +} + +void table_add (table *t, char *key, char *val) +{ + table_entry *elts = (table_entry *)t->elts; + + elts = (table_entry *)push_array(t); + elts->key = pstrdup (t->pool, key); + elts->val = pstrdup (t->pool, val); +} + +table* overlay_tables (pool *p, table *overlay, table *base) +{ + return append_arrays (p, overlay, base); +} + +/***************************************************************** + * + * Managing generic cleanups. + */ + +struct cleanup { + void *data; + void (*plain_cleanup)(void *); + void (*child_cleanup)(void *); + struct cleanup *next; +}; + +void register_cleanup (pool *p, void *data, void (*plain_cleanup)(void *), + void (*child_cleanup)(void *)) +{ + struct cleanup *c = (struct cleanup *)palloc(p, sizeof (struct cleanup)); + c->data = data; + c->plain_cleanup = plain_cleanup; + c->child_cleanup = child_cleanup; + c->next = p->cleanups; + p->cleanups = c; +} + +void kill_cleanup (pool *p, void *data, void (*cleanup)(void *)) +{ + struct cleanup *c = p->cleanups; + struct cleanup **lastp = &p->cleanups; + + while (c) { + if (c->data == data && c->plain_cleanup == cleanup) { + *lastp = c->next; + break; + } + + lastp = &c->next; + c = c->next; + } +} + +void run_cleanup (pool *p, void *data, void (*cleanup)(void *)) +{ + block_alarms(); /* Run cleanup only once! */ + (*cleanup)(data); + kill_cleanup (p, data, cleanup); + unblock_alarms(); +} + +static void run_cleanups (struct cleanup *c) +{ + while (c) { + (*c->plain_cleanup)(c->data); + c = c->next; + } +} + +static void run_child_cleanups (struct cleanup *c) +{ + while (c) { + (*c->child_cleanup)(c->data); + c = c->next; + } +} + +static void cleanup_pool_for_exec (pool *p) +{ + run_child_cleanups (p->cleanups); + p->cleanups = NULL; + + for (p = p->sub_pools; p; p = p->sub_next) + cleanup_pool_for_exec (p); +} + +void cleanup_for_exec() +{ + block_alarms(); + cleanup_pool_for_exec (permanent_pool); + unblock_alarms(); +} + +/***************************************************************** + * + * Files and file descriptors; these are just an application of the + * generic cleanup interface. + */ + +static void fd_cleanup (void *fdv) { close ((int)fdv); } + +void note_cleanups_for_fd (pool *p, int fd) { + register_cleanup (p, (void *)fd, fd_cleanup, fd_cleanup); +} + +int popenf(struct pool *a, char *name, int flg, int mode) +{ + int fd; + + block_alarms(); + fd = open(name, flg, mode); + if (fd >= 0) note_cleanups_for_fd (a, fd); + unblock_alarms(); + return fd; +} + +int pclosef(struct pool *a, int fd) +{ + int res; + + block_alarms(); + res = close(fd); + kill_cleanup(a, (void *)fd, fd_cleanup); + unblock_alarms(); + return res; +} + +/* Note that we have separate plain_ and child_ cleanups for FILE *s, + * since fclose() would flush I/O buffers, which is extremely undesirable; + * we just close the descriptor. + */ + +static void file_cleanup (void *fpv) { fclose ((FILE *)fpv); } +static void file_child_cleanup (void *fpv) { close (fileno ((FILE *)fpv)); } + +void note_cleanups_for_file (struct pool *p, FILE *fp) { + register_cleanup (p, (void *)fp, file_cleanup, file_child_cleanup); +} + +FILE *pfopen(struct pool *a, char *name, char *mode) +{ + FILE *fd; + + block_alarms(); + fd = fopen(name, mode); + if (fd != NULL) note_cleanups_for_file (a, fd); + unblock_alarms(); + return fd; +} + +FILE *pfdopen(struct pool *a,int fd,char *mode) +{ + FILE *f; + + block_alarms(); + f=fdopen(fd,mode); + if(f != NULL) + note_cleanups_for_file(a,f); + unblock_alarms(); + return f; +} + + +int pfclose(struct pool *a, FILE *fd) +{ + int res; + + block_alarms(); + res = fclose(fd); + kill_cleanup(a, (void *)fd, file_cleanup); + unblock_alarms(); + return res; +} + +/***************************************************************** + * + * More grotty system stuff... subprocesses. Frump. These don't use + * the generic cleanup interface because I don't want multiple + * subprocesses to result in multiple three-second pauses; the + * subprocesses have to be "freed" all at once. If someone comes + * along with another resource they want to allocate which has the + * same property, we might want to fold support for that into the + * generic interface, but for now, it's a special case + */ + +struct process_chain { + pid_t pid; + enum kill_conditions kill_how; + struct process_chain *next; +}; + +void note_subprocess (pool *a, int pid, enum kill_conditions how) +{ + struct process_chain *new = + (struct process_chain *)palloc(a, sizeof(struct process_chain)); + + new->pid = pid; + new->kill_how = how; + new->next = a->subprocesses; + a->subprocesses = new; +} + +int spawn_child (pool *p, void (*func)(void *), void *data, + enum kill_conditions kill_how, + FILE **pipe_in, FILE **pipe_out) +{ + int pid; + int in_fds[2]; + int out_fds[2]; + + block_alarms(); + + if (pipe_in && pipe (in_fds) < 0) + { + unblock_alarms(); + return 0; + } + + if (pipe_out && pipe (out_fds) < 0) { + if (pipe_in) { + close (in_fds[0]); close (in_fds[1]); + } + unblock_alarms(); + return 0; + } + + if ((pid = fork()) < 0) { + if (pipe_in) { + close (in_fds[0]); close (in_fds[1]); + } + if (pipe_out) { + close (out_fds[0]); close (out_fds[1]); + } + unblock_alarms(); + return 0; + } + + if (!pid) { + /* Child process */ + + if (pipe_out) { + close (out_fds[0]); + dup2 (out_fds[1], STDOUT_FILENO); + close (out_fds[1]); + } + + if (pipe_in) { + close (in_fds[1]); + dup2 (in_fds[0], STDIN_FILENO); + close (in_fds[0]); + } + + /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */ + signal (SIGCHLD, SIG_DFL); /* Was that it? */ + + func (data); + exit (0); /* Should never get here... */ + } + + /* Parent process */ + + note_subprocess (p, pid, kill_how); + + if (pipe_out) { + close (out_fds[1]); +#ifdef __EMX__ + /* Need binary mode set for OS/2. */ + *pipe_out = fdopen (out_fds[0], "rb"); +#else + *pipe_out = fdopen (out_fds[0], "r"); +#endif + + if (*pipe_out) note_cleanups_for_file (p, *pipe_out); + } + + if (pipe_in) { + close (in_fds[0]); +#ifdef __EMX__ + /* Need binary mode set for OS/2 */ + *pipe_in = fdopen (in_fds[1], "wb"); +#else + *pipe_in = fdopen (in_fds[1], "w"); +#endif + + if (*pipe_in) note_cleanups_for_file (p, *pipe_in); + } + + unblock_alarms(); + return pid; +} + +static void free_proc_chain (struct process_chain *procs) +{ + /* Dispose of the subprocesses we've spawned off in the course of + * whatever it was we're cleaning up now. This may involve killing + * some of them off... + */ + + struct process_chain *p; + int need_timeout = 0; + int status; + + if (procs == NULL) return; /* No work. Whew! */ + + /* First, check to see if we need to do the SIGTERM, sleep, SIGKILL + * dance with any of the processes we're cleaning up. If we've got + * any kill-on-sight subprocesses, ditch them now as well, so they + * don't waste any more cycles doing whatever it is that they shouldn't + * be doing anymore. + */ + +#ifndef NEED_WAITPID + /* Pick up all defunct processes */ + for (p = procs; p; p = p->next) { + if (waitpid (p->pid, (int *) 0, WNOHANG) > 0) { + p->kill_how = kill_never; + } + } +#endif + + for (p = procs; p; p = p->next) { + if (p->kill_how == kill_after_timeout) { + /* Subprocess may be dead already. Only need the timeout if not. */ + if (kill (p->pid, SIGTERM) != -1) + need_timeout = 1; + } else if (p->kill_how == kill_always) { + kill (p->pid, SIGKILL); + } + } + + /* Sleep only if we have to... */ + + if (need_timeout) sleep (3); + + /* OK, the scripts we just timed out for have had a chance to clean up + * --- now, just get rid of them, and also clean up the system accounting + * goop... + */ + + for (p = procs; p; p = p->next){ + + if (p->kill_how == kill_after_timeout) + kill (p->pid, SIGKILL); + + if (p->kill_how != kill_never) + waitpid (p->pid, &status, 0); + } +} + +#ifdef __EMX__ +int spawn_child_os2 (pool *p, void (*func)(void *), void *data, + enum kill_conditions kill_how, + FILE **pipe_in, FILE **pipe_out, char *buffer, int lenp) +{ + int pid; + int in_fds[2]; + int out_fds[2]; + + block_alarms(); + + if (pipe_in && pipe (in_fds) < 0) + { + unblock_alarms(); + return 0; + } + + if (pipe_out && pipe (out_fds) < 0) { + if (pipe_in) { + close (in_fds[0]); close (in_fds[1]); + } + unblock_alarms(); + return 0; + } + + if ((pid = fork()) < 0) { + if (pipe_in) { + close (in_fds[0]); close (in_fds[1]); + } + if (pipe_out) { + close (out_fds[0]); close (out_fds[1]); + } + unblock_alarms(); + return 0; + } + + if (!pid) { + int stdinpipe[2]; + /* Due to a limitation of EMX, inheriting socket handles is not + allowed so we need to read the input and place it in a pipe and + then pass that handle instead of the socket. */ + + if (lenp > 0) { + pipe(stdinpipe); + write(stdinpipe[1], buffer, lenp); + close(stdinpipe[1]); + in_fds[0] = dup(stdinpipe[0]); + close(stdinpipe[0]); + } + + /* Child process */ + + if (pipe_out) { + close (out_fds[0]); + dup2 (out_fds[1], STDOUT_FILENO); + close (out_fds[1]); + } + + if (pipe_in) { + close (in_fds[1]); + dup2 (in_fds[0], STDIN_FILENO); + close (in_fds[0]); + } + + /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */ + signal (SIGCHLD, SIG_DFL); /* Was that it? */ + + func (data); + exit (0); /* Should never get here... */ + } + + /* Parent process */ + + note_subprocess (p, pid, kill_how); + + if (pipe_out) { + close (out_fds[1]); + /* Need binary mode set for OS/2. */ + *pipe_out = fdopen (out_fds[0], "rb"); + + if (*pipe_out) note_cleanups_for_file (p, *pipe_out); + } + + if (pipe_in) { + close (in_fds[0]); + /* Need binary mode set for OS/2. */ + *pipe_in = fdopen (in_fds[1], "wb"); + + if (*pipe_in) note_cleanups_for_file (p, *pipe_in); + } + + unblock_alarms(); + return pid; +} +#endif diff --git a/RELEASE_1_1_X/src/main/buff.c b/RELEASE_1_1_X/src/main/buff.c new file mode 100644 index 00000000000..263688974e0 --- /dev/null +++ b/RELEASE_1_1_X/src/main/buff.c @@ -0,0 +1,648 @@ +/* ==================================================================== + * Copyright (c) 1996 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see <http://www.apache.org/>. + * + */ + +#include <errno.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +#include "alloc.h" +#include "buff.h" + +#define DEFAULT_BUFSIZE (4096) + +/* + * Buffered I/O routines. + * These are a replacement for the stdio routines. + * Advantages: + * Known semantics for handling of file-descriptors (on close etc.) + * No problems reading and writing simultanously to the same descriptor + * No limits on the number of open file handles. + * Only uses memory resources; no need to ensure the close routine + * is called. + * Extra code could be inserted between the buffered and un-buffered routines. + * Timeouts could be handled by using select or poll before read or write. + * Extra error handling could be introduced; e.g. + * keep an address to which we should longjump(), or + * keep a stack of routines to call on error. + */ + +/* Notes: + * On reading EOF, EOF will set in the flags and no further Input will + * be done. + * + * On an error except for EAGAIN, ERROR will be set in the flags and no + * futher I/O will be done + */ + +static void +doerror(BUFF *fb, int err) +{ + if (err == B_RD) + fb->flags |= B_RDERR; + else + fb->flags |= B_WRERR; + if (fb->error != NULL) (*fb->error)(fb, err, fb->error_data); +} + +/* Buffering routines */ +/* + * Create a new buffered stream + */ +BUFF * +bcreate(pool *p, int flags) +{ + BUFF *fb; + + fb = palloc(p, sizeof(BUFF)); + fb->pool=p; + fb->bufsiz = DEFAULT_BUFSIZE; + fb->flags = flags & B_RDWR; + + if (flags & B_RD) fb->inbase = palloc(p, fb->bufsiz); + else fb->inbase = NULL; + + if (flags & B_WR) fb->outbase = palloc(p, fb->bufsiz); + else fb->inbase = NULL; + + fb->inptr = fb->inbase; + + fb->incnt = 0; + fb->outcnt = 0; + fb->error = NULL; + fb->bytes_sent = 0L; + + fb->fd = -1; + fb->fd_in = -1; + + return fb; +} + +/* + * Push some I/O file descriptors onto the stream + */ +void +bpushfd(BUFF *fb, int fd_in, int fd_out) +{ + fb->fd = fd_out; + fb->fd_in = fd_in; + note_cleanups_for_fd(fb->pool,fb->fd); + if(fb->fd != fb->fd_in) + note_cleanups_for_fd(fb->pool,fb->fd_in); +} + +int +bsetopt(BUFF *fb, int optname, const void *optval) +{ + if (optname == BO_BYTECT) + { + fb->bytes_sent = *(const long int *)optval - (long int)fb->outcnt;; + return 0; + } else + { + errno = EINVAL; + return -1; + } +} + +int +bgetopt(BUFF *fb, int optname, void *optval) +{ + if (optname == BO_BYTECT) + { + long int bs=fb->bytes_sent + fb->outcnt; + if (bs < 0L) bs = 0L; + *(long int *)optval = bs; + return 0; + } else + { + errno = EINVAL; + return -1; + } +} + +/* + * Read up to nbyte bytes into buf. + * If fewer than byte bytes are currently available, then return those. + * Returns 0 for EOF, -1 for error. + */ +int +bread(BUFF *fb, void *buf, int nbyte) +{ + int i, nrd; + + if (fb->flags & B_RDERR) return -1; + if (nbyte == 0) return 0; + + if (!(fb->flags & B_RD)) + { +/* Unbuffered reading */ + do i = read(fb->fd_in, buf, nbyte); + while (i == -1 && errno == EINTR); + if (i == -1 && errno != EAGAIN) doerror(fb, B_RD); + return i; + } + + nrd = fb->incnt; +/* can we fill the buffer */ + if (nrd >= nbyte) + { + memcpy(buf, fb->inptr, nbyte); + fb->incnt = nrd - nbyte; + fb->inptr += nbyte; + return nbyte; + } + + if (nrd > 0) + { + memcpy(buf, fb->inptr, nrd); + nbyte -= nrd; + buf = nrd + (char *)buf; + fb->incnt = 0; + } + if (fb->flags & B_EOF) return nrd; + +/* do a single read */ + if (nbyte >= fb->bufsiz) + { +/* read directly into buffer */ + do i = read(fb->fd_in, buf, nbyte); + while (i == -1 && errno == EINTR); + if (i == -1) + { + if (nrd == 0) + { + if (errno != EAGAIN) doerror(fb, B_RD); + return -1; + } + else return nrd; + } else if (i == 0) fb->flags |= B_EOF; + } else + { +/* read into hold buffer, then memcpy */ + fb->inptr = fb->inbase; + do i = read(fb->fd_in, fb->inptr, fb->bufsiz); + while (i == -1 && errno == EINTR); + if (i == -1) + { + if (nrd == 0) + { + if (errno != EAGAIN) doerror(fb, B_RD); + return -1; + } + else return nrd; + } else if (i == 0) fb->flags |= B_EOF; + fb->incnt = i; + if (i > nbyte) i = nbyte; + memcpy(buf, fb->inptr, i); + fb->incnt -= i; + fb->inptr += i; + } + return nrd + i; +} + + +/* + * Reads from the stream into the array pointed to by buff, until + * a (CR)LF sequence is read, or end-of-file condition is encountered + * or until n-1 bytes have been stored in buff. If a CRLF sequence is + * read, it is replaced by a newline character. The string is then + * terminated with a null character. + * + * Returns the number of bytes stored in buff, or zero on end of + * transmission, or -1 on an error. + * + * Notes: + * If null characters are exepected in the data stream, then + * buff should not be treated as a null terminated C string; instead + * the returned count should be used to determine the length of the + * string. + * CR characters in the byte stream not immediately followed by a LF + * will be preserved. + */ +int +bgets(char *buff, int n, BUFF *fb) +{ + int i, ch, ct; + +/* Can't do bgets on an unbuffered stream */ + if (!(fb->flags & B_RD)) + { + errno = EINVAL; + return -1; + } + if (fb->flags & B_RDERR) return -1; + + ct = 0; + i = 0; + for (;;) + { + if (i == fb->incnt) + { +/* no characters left */ + fb->inptr = fb->inbase; + fb->incnt = 0; + if (fb->flags & B_EOF) break; + do i = read(fb->fd_in, fb->inptr, fb->bufsiz); + while (i == -1 && errno == EINTR); + if (i == -1) + { + buff[ct] = '\0'; + if (ct == 0) + { + if (errno != EAGAIN) doerror(fb, B_RD); + return -1; + } + else return ct; + } + fb->incnt = i; + if (i == 0) + { + fb->flags |= B_EOF; + break; /* EOF */ + } + i = 0; + continue; /* restart with the new data */ + } + + ch = fb->inptr[i++]; + if (ch == '\012') /* got LF */ + { + if (ct == 0) buff[ct++] = '\n'; +/* if just preceeded by CR, replace CR with LF */ + else if (buff[ct-1] == '\015') buff[ct-1] = '\n'; + else if (ct < n-1) buff[ct++] = '\n'; + else i--; /* no room for LF */ + break; + } + if (ct == n-1) + { + i--; /* push back ch */ + break; + } + + buff[ct++] = ch; + } + fb->incnt -= i; + fb->inptr += i; + + buff[ct] = '\0'; + return ct; +} + +/* + * Skip data until a linefeed character is read + * Returns 1 on success, 0 if no LF found, or -1 on error + */ +int +bskiplf(BUFF *fb) +{ + unsigned char *x; + int i; + +/* Can't do bskiplf on an unbuffered stream */ + if (!(fb->flags & B_RD)) + { + errno = EINVAL; + return -1; + } + if (fb->flags & B_RDERR) return -1; + + for (;;) + { + x = memchr(fb->inptr, '\012', fb->incnt); + if (x != NULL) + { + x++; + fb->incnt -= x - fb->inptr; + fb->inptr = x; + return 1; + } + + fb->inptr = fb->inbase; + fb->incnt = 0; + if (fb->flags & B_EOF) return 0; + do i = read(fb->fd_in, fb->inptr, fb->bufsiz); + while (i == -1 && errno == EINTR); + if (i == 0) fb->flags |= B_EOF; + if (i == -1 && errno != EAGAIN) doerror(fb, B_RD); + if (i == 0 || i == -1) return i; + fb->incnt = i; + } +} + +/* + * Emtpy the buffer after putting a single character in it + */ +int +bflsbuf(int c, BUFF *fb) +{ + char ss[1]; + + ss[0] = c; + return bwrite(fb, ss, 1); +} + +/* + * Fill the buffer and read a character from it + */ +int +bfilbuf(BUFF *fb) +{ + int i; + char buf[1]; + + i = bread(fb, buf, 1); + if (i == 0) errno = 0; /* no error; EOF */ + if (i != 1) return EOF; + else return buf[0]; +} + +/* + * Write nbyte bytes. + * Only returns fewer than nbyte if an error ocurred. + * Returns -1 if no bytes were written before the error ocurred. + */ +int +bwrite(BUFF *fb, const void *buf, int nbyte) +{ + int i, nwr; + + if (fb->flags & (B_WRERR|B_EOUT)) return -1; + if (nbyte == 0) return 0; + + if (!(fb->flags & B_WR)) + { +/* unbuffered write */ + do i = write(fb->fd, buf, nbyte); + while (i == -1 && errno == EINTR); + if (i > 0) fb->bytes_sent += i; + if (i == 0) + { + i = -1; /* return of 0 means non-blocking */ + errno = EAGAIN; + } + if (i == -1 && errno != EAGAIN) doerror(fb, B_WR); + return i; + } + +/* + * Whilst there is data in the buffer, keep on adding to it and writing it + * out + */ + nwr = 0; + while (fb->outcnt > 0) + { +/* can we accept some data? */ + i = fb->bufsiz - fb->outcnt; + if (i > 0) + { + if (i > nbyte) i = nbyte; + memcpy(fb->outbase + fb->outcnt, buf, i); + fb->outcnt += i; + nbyte -= i; + buf = i + (const char *)buf; + nwr += i; + if (nbyte == 0) return nwr; /* return if none left */ + } + +/* the buffer must be full */ + do i = write(fb->fd, fb->outbase, fb->bufsiz); + while (i == -1 && errno == EINTR); + if (i > 0) fb->bytes_sent += i; + if (i == 0) + { + i = -1; /* return of 0 means non-blocking */ + errno = EAGAIN; + } + if (i == -1) + { + if (nwr == 0) + { + if (errno != EAGAIN) doerror(fb, B_WR); + return -1; + } + else return nwr; + } + +/* + * we should have written all the data, however if the fd was in a + * strange (non-blocking) mode, then we might not have done so. + */ + if (i < fb->bufsiz) + { + int j, n=fb->bufsiz; + unsigned char *x=fb->outbase; + for (j=i; j < n; j++) x[j-i] = x[j]; + fb->outcnt = fb->bufsiz - i; + } else + fb->outcnt = 0; + } +/* we have emptied the file buffer. Now try to write the data from the + * original buffer until there is less than bufsiz left + */ + while (nbyte > fb->bufsiz) + { + do i = write(fb->fd, buf, nbyte); + while (i == -1 && errno == EINTR); + if (i > 0) fb->bytes_sent += i; + if (i == 0) + { + i = -1; /* return of 0 means non-blocking */ + errno = EAGAIN; + } + if (i == -1) + { + if (nwr == 0) + { + if (errno != EAGAIN) doerror(fb, B_WR); + return -1; + } + else return nwr; + } + + buf = i + (const char *)buf; + nwr += i; + nbyte -= i; + } +/* copy what's left to the file buffer */ + if (nbyte > 0) memcpy(fb->outbase, buf, nbyte); + fb->outcnt = nbyte; + nwr += nbyte; + return nwr; +} + +/* + * Flushes the buffered stream. + * Returns 0 on success or -1 on error + */ +int +bflush(BUFF *fb) +{ + int i, j; + + if (!(fb->flags & B_WR) || (fb->flags & B_EOUT)) return 0; + + if (fb->flags & B_WRERR) return -1; + + while (fb->outcnt > 0) + { +/* the buffer must be full */ + j = fb->outcnt; + do i = write(fb->fd, fb->outbase, fb->outcnt); + while (i == -1 && errno == EINTR); + if (i > 0) fb->bytes_sent += i; + if (i == 0) + { + errno = EAGAIN; + return -1; /* return of 0 means non-blocking */ + } + if (i == -1) + { + if (errno != EAGAIN) doerror(fb, B_WR); + return -1; + } + +/* + * we should have written all the data, however if the fd was in a + * strange (non-blocking) mode, then we might not have done so. + */ + if (i < fb->outcnt) + { + int j, n=fb->outcnt; + unsigned char *x=fb->outbase; + for (j=i; j < n; j++) x[j-i] = x[j]; + } + fb->outcnt -= i; + } + return 0; +} + +/* + * Flushes and closes the file, even if an error occurred. + * Discards an data that was not read, or not written by bflush() + * Sets the EOF flag to indicate no futher data can be read, + * and the EOUT flag to indicate no further data can be written. + */ +int +bclose(BUFF *fb) +{ + int rc1, rc2, rc3; + + if (fb->flags & B_WR) rc1 = bflush(fb); + else rc1 = 0; + rc2 = close(fb->fd); + if (fb->fd_in != fb->fd) rc3 = close(fb->fd_in); + else rc3 = 0; + + fb->inptr = fb->inbase; + fb->incnt = 0; + fb->outcnt = 0; + + fb->flags |= B_EOF | B_EOUT; + fb->fd = -1; + fb->fd_in = -1; + + if (rc1 != 0) return rc1; + else if (rc2 != 0) return rc2; + else return rc3; +} + +/* + * returns the number of bytes written or -1 on error + */ +int +bputs(const char *x, BUFF *fb) +{ + int i, j=strlen(x); + i = bwrite(fb, x, j); + if (i != j) return -1; + else return j; +} + +/* + * returns the number of bytes written or -1 on error + */ +int +bvputs(BUFF *fb, ...) +{ + int i, j, k; + va_list v; + const char *x; + + va_start(v, fb); + for (k=0;;) + { + x = va_arg(v, const char *); + if (x == NULL) break; + j = strlen(x); + i = bwrite(fb, x, j); + if (i != j) + { + va_end(v); + return -1; + } + k += i; + } + + va_end(v); + + return k; +} + +void +bonerror(BUFF *fb, void (*error)(BUFF *, int, void *), void *data) +{ + fb->error = error; + fb->error_data = data; +} diff --git a/RELEASE_1_1_X/src/main/explain.c b/RELEASE_1_1_X/src/main/explain.c new file mode 100644 index 00000000000..6490c940522 --- /dev/null +++ b/RELEASE_1_1_X/src/main/explain.c @@ -0,0 +1,14 @@ +#include <stdio.h> +#include <stdarg.h> +#include "explain.h" + +void _Explain(const char *szFile,int nLine,const char *szFmt,...) + { + va_list vlist; + + fprintf(stderr,"%s(%d): ",szFile,nLine); + va_start(vlist,szFmt); + vfprintf(stderr,szFmt,vlist); + va_end(vlist); + fputc('\n',stderr); + } diff --git a/RELEASE_1_1_X/src/main/http_bprintf.c b/RELEASE_1_1_X/src/main/http_bprintf.c new file mode 100644 index 00000000000..63443d78637 --- /dev/null +++ b/RELEASE_1_1_X/src/main/http_bprintf.c @@ -0,0 +1,590 @@ +/* + * printf() style routines stolen from FastCGI + * Copyright (c) 1996 Open Market, Inc. + */ + +/* + * Modified to work with Apache buffering routines by Ben Laurie + * <ben@algroup.co.uk>. + * + * Modifications Copyright (C) 1996 Ben Laurie. + * + * History: + * 18 May 1996 Initial revision [Ben Laurie] + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#ifndef QNX +#include <memory.h> +#endif +#include <assert.h> +#include <math.h> +#include "alloc.h" +#include "buff.h" + +#if !defined(max) +#define max(a,b) (a > b ? a : b) +#endif + +#define LONG_DOUBLE long double + +#define FALSE 0 +#define TRUE 1 + +#define PRINTF_BUFFLEN 100 + /* + * More than sufficient space for all unmodified conversions + * except %s and %f. + */ +#define FMT_BUFFLEN 25 + /* + * Max size of a format specifier is 1 + 5 + 7 + 7 + 2 + 1 + slop + */ + +/* + * Copy n characters from *srcPtr to *destPtr, then increment + * both *srcPtr and *destPtr by n. + */ +static void CopyAndAdvance(char **destPtr, const char **srcPtr, int n) + { + char *dest = *destPtr; + const char *src = *srcPtr; + int i; + + for (i = 0; i < n; i++) + *dest++ = *src++; + *destPtr = dest; + *srcPtr = src; + } + +int vbprintf(BUFF *bp, const char *format, va_list arg) + { + const char *f,*fStop,*percentPtr,*p; + char *fmtBuffPtr, *buffPtr; + int op, performedOp, sizeModifier, buffLen, specifierLength; + int fastPath, n, buffReqd, minWidth, precision, exp; + int buffCount = 0; + int auxBuffLen = 0; + char *auxBuffPtr = NULL; + int streamCount = 0; + char fmtBuff[FMT_BUFFLEN]; + char buff[PRINTF_BUFFLEN]; + + int intArg; + short shortArg; + long longArg; + unsigned unsignedArg; + unsigned long uLongArg; + unsigned short uShortArg; + char *charPtrArg = NULL; + void *voidPtrArg; + int *intPtrArg; + long *longPtrArg; + short *shortPtrArg; + double doubleArg = 0.0; + LONG_DOUBLE lDoubleArg = 0.0; + + fmtBuff[0] = '%'; + f=format; + fStop = f + strlen(f); + while (f != fStop) + { + percentPtr = memchr(f, '%', fStop - f); + if(percentPtr == NULL) percentPtr = fStop; + if(percentPtr != f) + { + if(bwrite(bp,f,percentPtr - f) < 0) + goto ErrorReturn; + streamCount += percentPtr - f; + f = percentPtr; + if(f == fStop) + break; + } + fastPath = TRUE; + /* + * The following loop always executes either once or twice. + */ + for (;;) + { + if(fastPath) + { + /* + * Fast path: Scan optimistically, hoping that no flags, + * minimum field width, or precision are specified. + * Use the preallocated buffer, which is large enough + * for all fast path cases. If the conversion specifier + * is really more complex, run the loop a second time + * using the slow path. + * Note that fast path execution of %s bypasses the buffer + * and %f is not attempted on the fast path due to + * its large buffering requirements. + */ + op = percentPtr[1]; + switch(op) + { + case 'l': + case 'L': + case 'h': + sizeModifier = op; + op = percentPtr[2]; + fmtBuff[1] = sizeModifier; + fmtBuff[2] = op; + fmtBuff[3] = '\0'; + specifierLength = 3; + break; + default: + sizeModifier = ' '; + fmtBuff[1] = op; + fmtBuff[2] = '\0'; + specifierLength = 2; + break; + } + buffPtr = buff; + buffLen = PRINTF_BUFFLEN; + } + else + { + /* + * Slow path: Scan the conversion specifier and construct + * a new format string, compute an upper bound on the + * amount of buffering that sprintf will require, + * and allocate a larger buffer if necessary. + */ + p = percentPtr + 1; + fmtBuffPtr = &fmtBuff[1]; + /* + * Scan flags + */ + n = strspn(p, "-0+ #"); + if(n > 5) + goto ErrorReturn; + CopyAndAdvance(&fmtBuffPtr, &p, n); + + /* Optimiser bug in SCO 5 - p is not advanced here under -O2. + * -K noinline fixes it. Ben. + */ + + /* + * Scan minimum field width + */ + n = strspn(p, "0123456789"); + if(n == 0) + { + if(*p == '*') + { + minWidth = va_arg(arg, int); + if(abs(minWidth) > 999999) goto ErrorReturn; + /* + * The following use of strlen rather than the + * value returned from sprintf is because SUNOS4 + * returns a char * instead of an int count. + */ + sprintf(fmtBuffPtr, "%d", minWidth); + fmtBuffPtr += strlen(fmtBuffPtr); + p++; + } + else + minWidth = 0; + } + else if(n <= 6) + { + minWidth = strtol(p, NULL, 10); + CopyAndAdvance(&fmtBuffPtr, &p, n); + } + else + goto ErrorReturn; + /* + * Scan precision + */ + if(*p == '.') + { + p++; + n = strspn(p, "0123456789"); + if(n == 0) + { + if(*p == '*') + { + precision = va_arg(arg, int); + if(precision < 0) precision = 0; + if(precision > 999999) goto ErrorReturn; + /* + * The following use of strlen rather than the + * value returned from sprintf is because SUNOS4 + * returns a char * instead of an int count. + */ + sprintf(fmtBuffPtr, ".%d", precision); + fmtBuffPtr += strlen(fmtBuffPtr); + p++; + } + else + precision = 0; + } + else if(n <= 6) + { + precision = strtol(p, NULL, 10); + *fmtBuffPtr++='.'; + CopyAndAdvance(&fmtBuffPtr, &p, n); + } + else + goto ErrorReturn; + } + else + precision = -1; + /* + * Scan size modifier and conversion operation + */ + switch(*p) + { + case 'l': + case 'L': + case 'h': + sizeModifier = *p; + CopyAndAdvance(&fmtBuffPtr, &p, 1); + break; + + default: + sizeModifier = ' '; + break; + } + op = *p; + CopyAndAdvance(&fmtBuffPtr, &p, 1); + assert(fmtBuffPtr - fmtBuff < FMT_BUFFLEN); + *fmtBuffPtr = '\0'; + /* + bwrite(bp,"[",1); + bwrite(bp,fmtBuff,strlen(fmtBuff)); + bwrite(bp,"]",1); + */ + specifierLength = p - percentPtr; + /* + * Bound the required buffer size. For s and f + * conversions this requires examining the argument. + */ + switch(op) + { + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': + case 'c': + case 'p': + buffReqd = max(precision, 46); + break; + + case 's': + charPtrArg = va_arg(arg, char *); + if(precision == -1) + buffReqd = strlen(charPtrArg); + else + { + p = memchr(charPtrArg, '\0', precision); + buffReqd=(p == NULL) ? precision : p - charPtrArg; + } + break; + + case 'f': + switch(sizeModifier) + { + case ' ': + doubleArg = va_arg(arg, double); + frexp(doubleArg, &exp); + break; + + case 'L': + lDoubleArg = va_arg(arg, LONG_DOUBLE); + frexp(lDoubleArg, &exp); + break; + + default: + goto ErrorReturn; + } + if(precision == -1) + precision = 6; + buffReqd = precision + 3 + ((exp > 0) ? exp/3 : 0); + break; + + case 'e': + case 'E': + case 'g': + case 'G': + if(precision == -1) + precision = 6; + buffReqd = precision + 8; + break; + + case 'n': + case '%': + default: + goto ErrorReturn; + } + buffReqd = max(buffReqd + 10, minWidth); + /* + * Allocate the buffer + */ + if(buffReqd <= PRINTF_BUFFLEN) + { + buffPtr = buff; + buffLen = PRINTF_BUFFLEN; + } + else + { + if(auxBuffPtr == NULL || buffReqd > auxBuffLen) + { + if(auxBuffPtr != NULL) free(auxBuffPtr); + auxBuffPtr = malloc(buffReqd); + auxBuffLen = buffReqd; + if(auxBuffPtr == NULL) + goto ErrorReturn; + } + buffPtr = auxBuffPtr; + buffLen = auxBuffLen; + } + } + /* + * This giant switch statement requires the following variables + * to be set up: op, sizeModifier, arg, buffPtr, fmtBuff. + * When fastPath == FALSE and op == 's' or 'f', the argument + * has been read into charPtrArg, doubleArg, or lDoubleArg. + * The statement produces the boolean performedOp, TRUE iff + * the op/sizeModifier were executed and argument consumed; + * if performedOp, the characters written into buffPtr[] + * and the character count buffCount (== EOF meaning error). + * + * The switch cases are arranged in the same order as in the + * description of fprintf in section 15.11 of Harbison and Steele. + */ + performedOp = TRUE; + switch(op) + { + case 'd': + case 'i': + switch(sizeModifier) + { + case ' ': + intArg = va_arg(arg, int); + sprintf(buffPtr, fmtBuff, intArg); + buffCount = strlen(buffPtr); + break; + + case 'l': + longArg = va_arg(arg, long); + sprintf(buffPtr, fmtBuff, longArg); + buffCount = strlen(buffPtr); + break; + + case 'h': + shortArg = va_arg(arg, short); + sprintf(buffPtr, fmtBuff, shortArg); + buffCount = strlen(buffPtr); + break; + + default: + goto ErrorReturn; + } + break; + + case 'u': + case 'o': + case 'x': + case 'X': + switch(sizeModifier) + { + case ' ': + unsignedArg = va_arg(arg, unsigned); + sprintf(buffPtr, fmtBuff, unsignedArg); + buffCount = strlen(buffPtr); + break; + + case 'l': + uLongArg = va_arg(arg, unsigned long); + sprintf(buffPtr, fmtBuff, uLongArg); + buffCount = strlen(buffPtr); + break; + + case 'h': + uShortArg = va_arg(arg, unsigned short); + sprintf(buffPtr, fmtBuff, uShortArg); + buffCount = strlen(buffPtr); + break; + + default: + goto ErrorReturn; + } + break; + + case 'c': + switch(sizeModifier) + { + case ' ': + intArg = va_arg(arg, int); + sprintf(buffPtr, fmtBuff, intArg); + buffCount = strlen(buffPtr); + break; + + case 'l': + /* + * XXX: Allowed by ISO C Amendment 1, but + * many platforms don't yet support wint_t + */ + goto ErrorReturn; + + default: + goto ErrorReturn; + } + break; + + case 's': + switch(sizeModifier) + { + case ' ': + if(fastPath) + { + buffPtr = va_arg(arg, char *); + buffCount = strlen(buffPtr); + buffLen = buffCount + 1; + } + else + { + sprintf(buffPtr, fmtBuff, charPtrArg); + buffCount = strlen(buffPtr); + } + break; + + case 'l': + /* + * XXX: Don't know how to convert a sequence + * of wide characters into a byte stream, or + * even how to predict the buffering required. + */ + goto ErrorReturn; + + default: + goto ErrorReturn; + } + break; + + case 'p': + if(sizeModifier != ' ') + goto ErrorReturn; + voidPtrArg = va_arg(arg, void *); + sprintf(buffPtr, fmtBuff, voidPtrArg); + buffCount = strlen(buffPtr); + break; + + case 'n': + switch(sizeModifier) + { + case ' ': + intPtrArg = va_arg(arg, int *); + *intPtrArg = streamCount; + break; + + case 'l': + longPtrArg = va_arg(arg, long *); + *longPtrArg = streamCount; + break; + + case 'h': + shortPtrArg = va_arg(arg, short *); + *shortPtrArg = streamCount; + break; + + default: + goto ErrorReturn; + } + buffCount = 0; + break; + + case 'f': + if(fastPath) + { + performedOp = FALSE; + break; + } + + switch(sizeModifier) + { + case ' ': + sprintf(buffPtr, fmtBuff, doubleArg); + buffCount = strlen(buffPtr); + break; + + case 'L': + sprintf(buffPtr, fmtBuff, lDoubleArg); + buffCount = strlen(buffPtr); + break; + + default: + goto ErrorReturn; + } + break; + /* FIXME: Used to flow through here? Should it? Ben */ + + case 'e': + case 'E': + case 'g': + case 'G': + switch(sizeModifier) + { + case ' ': + doubleArg = va_arg(arg, double); + sprintf(buffPtr, fmtBuff, doubleArg); + buffCount = strlen(buffPtr); + break; + + case 'L': + lDoubleArg = va_arg(arg, LONG_DOUBLE); + sprintf(buffPtr, fmtBuff, lDoubleArg); + buffCount = strlen(buffPtr); + break; + + default: + goto ErrorReturn; + } + break; + + case '%': + if(sizeModifier != ' ') + goto ErrorReturn; + buff[0] = '%'; + buffCount = 1; + break; + + case '\0': + goto ErrorReturn; + + default: + performedOp = FALSE; + break; + } /* switch(op) */ + + if(performedOp) + break; + if(!fastPath) + goto ErrorReturn; + fastPath = FALSE; + } /* for (;;) */ + assert(buffCount < buffLen); + if(buffCount > 0) + { + if(bwrite(bp,buffPtr,buffCount) < 0) + goto ErrorReturn; + streamCount += buffCount; + } + else if(buffCount < 0) + goto ErrorReturn; + f += specifierLength; + } /* while(f != fStop) */ + goto NormalReturn; +ErrorReturn: + streamCount = -1; +NormalReturn: + if(auxBuffPtr != NULL) + free(auxBuffPtr); + return streamCount; + } diff --git a/RELEASE_1_1_X/src/main/http_config.c b/RELEASE_1_1_X/src/main/http_config.c new file mode 100644 index 00000000000..8d2d58670dd --- /dev/null +++ b/RELEASE_1_1_X/src/main/http_config.c @@ -0,0 +1,782 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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 Shambhala... 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 + * Shambhala 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... */ + +/**************************************************************** + * + * We begin with the functions which deal with the linked list + * of modules which control just about all of server operation in + * Shambhala. + */ + +static int num_modules = 0; +module *top_module = NULL; + +typedef int (*handler)(request_rec *); +typedef void *(*maker)(pool *); +typedef void *(*dir_maker)(pool *, char *); +typedef void *(*merger)(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). + */ + +void * +get_module_config (void *conf_vector, module *m) +{ + void **confv = (void**)conf_vector; + return confv[m->module_index]; +} + +void +set_module_config (void *conf_vector, module *m, void *val) +{ + void **confv = (void**)conf_vector; + confv[m->module_index] = val; +} + +void * +create_empty_config (pool *p) +{ + void **conf_vector = (void **)pcalloc(p, sizeof(void*) * num_modules); + return (void *)conf_vector; +} + +void * +create_default_per_dir_config (pool *p) +{ + void **conf_vector = (void **)pcalloc(p, sizeof(void*) * (num_modules+DYNAMIC_MODULE_LIMIT)); + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + dir_maker df = modp->create_dir_config; + + if (df) conf_vector[modp->module_index] = (*df)(p, NULL); + } + + return (void*)conf_vector; +} + +void * +merge_per_dir_configs (pool *p, void *base, void *new) +{ + void **conf_vector = (void **)pcalloc(p, sizeof(void*) * num_modules); + void **base_vector = (void **) base; + void **new_vector = (void **) new; + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + merger 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; +} + +void * +create_server_config (pool *p, server_rec *s) +{ + void **conf_vector = (void **)pcalloc(p, sizeof(void*) * (num_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; +} + +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 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 *create_connection_config (pool *p) { + return create_empty_config (p); +} + +void *create_request_config (pool *p) { + return create_empty_config (p); +} + +void *create_per_dir_config (pool *p) { + return create_empty_config (p); +} + +/**************************************************************** + * + * 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. + */ + +int +run_method (request_rec *r, int offset, int run_all) +{ + module *modp; + for (modp = top_module; modp; modp = modp->next) { + handler mod_handler = *(handler *)(offset + (char *)(modp)); + + if (mod_handler) { + int result = (*mod_handler)(r); + + if (result != DECLINED && (!run_all || result != OK)) + return result; + } + } + + return run_all ? OK : DECLINED; +} + +int translate_name(request_rec *r) { + return run_method (r, XtOffsetOf (module, translate_handler), 0); +} + +int check_access(request_rec *r) { + return run_method (r, XtOffsetOf (module, access_checker), 1); +} + +int find_types (request_rec *r) { + return run_method (r, XtOffsetOf (module, type_checker), 0); +} + +int run_fixups (request_rec *r) { + return run_method (r, XtOffsetOf (module, fixer_upper), 1); +} + +int log_transaction (request_rec *r) { + return run_method (r, XtOffsetOf (module, logger), 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 check_user_id (request_rec *r) { + return run_method (r, XtOffsetOf (module, check_user_id), 0); +} + +int check_auth (request_rec *r) { + return run_method (r, XtOffsetOf (module, auth_checker), 0); +} + +int invoke_handler (request_rec *r) +{ + module *modp; + handler_rec *handp; + char *content_type = r->content_type ? r->content_type : default_type (r); + char *handler = r->handler ? r->handler : content_type; + + /* Pass one --- direct matches */ + + for (modp = top_module; modp; modp = modp->next) + { + if (!modp->handlers) continue; + + for (handp = modp->handlers; handp->content_type; ++handp) { + if (!strcasecmp (handler, handp->content_type)) { + int result = (*handp->handler)(r); + + if (result != DECLINED) return result; + } + } + } + + /* Pass two --- wildcard matches */ + + for (modp = top_module; modp; modp = modp->next) + { + if (!modp->handlers) continue; + + for (handp = modp->handlers; handp->content_type; ++handp) { + char *starp = strchr (handp->content_type, '*'); + int len; + + if (!starp) continue; + + len = starp - handp->content_type; + + if (!len || !strncasecmp (handler, handp->content_type, len)) + { + int result = (*handp->handler)(r); + + if (result != DECLINED) return result; + } + } + } + + return NOT_IMPLEMENTED; +} + +/* One-time setup for precompiled modules --- NOT to be done on restart */ + +void 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... + */ + + m->next = top_module; + top_module = m; + m->module_index = num_modules++; +} + +void setup_prelinked_modules () +{ + extern module *prelinked_modules[]; + module **m = prelinked_modules; + + while (*m) { + add_module (*m); + ++m; + } +} + +/***************************************************************** + * + * 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... + */ + +char *invoke_cmd(command_rec *cmd, cmd_parms *parms, void *mconfig, char *args) +{ + char *w, *w2, *errmsg; + + if ((parms->override & cmd->req_override) == 0) + return pstrcat (parms->pool, cmd->name, " not allowed here", NULL); + + parms->info = cmd->cmd_data; + + switch (cmd->args_how) { + case RAW_ARGS: + return (*cmd->func) (parms, mconfig, args); + + case NO_ARGS: + if (*args != 0) + return pstrcat (parms->pool, cmd->name, " takes no arguments", + NULL); + + return (*cmd->func) (parms, mconfig); + + case TAKE1: + w = getword_conf (parms->pool, &args); + + if (*w == '\0' || *args != 0) + return pstrcat (parms->pool, cmd->name, " takes one argument", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return (*cmd->func) (parms, mconfig, w); + + case TAKE2: + + w = getword_conf (parms->pool, &args); + w2 = getword_conf (parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return pstrcat (parms->pool, cmd->name, " takes two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return (*cmd->func) (parms, mconfig, w, w2); + + case ITERATE: + + while (*(w = getword_conf (parms->pool, &args)) != '\0') + if ((errmsg = (*cmd->func) (parms, mconfig, w))) + return errmsg; + + return NULL; + + case ITERATE2: + + w = getword_conf (parms->pool, &args); + + if (*w == '\0' || *args == 0) + return pstrcat(parms->pool, cmd->name, + " requires at least two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + + while (*(w2 = getword_conf (parms->pool, &args)) != '\0') + if ((errmsg = (*cmd->func) (parms, mconfig, w, w2))) + return errmsg; + + return NULL; + + case FLAG: + + w = getword_conf (parms->pool, &args); + + if (*w == '\0' || ((!strcasecmp(w, "on")) && (!strcasecmp (w, "off")))) + return pstrcat (parms->pool, cmd->name, " must be On or Off", + NULL); + + return (*cmd->func) (parms, mconfig, strcasecmp (w, "off") != 0); + + default: + + return pstrcat (parms->pool, cmd->name, + " is improperly configured internally (server bug)", + NULL); + } +} + +command_rec *find_command (char *name, command_rec *cmds) +{ + while (cmds->name) + if (!strcasecmp (name, cmds->name)) + return cmds; + else + ++cmds; + + return NULL; +} + +command_rec *find_command_in_modules (char *cmd_name, module **mod) +{ + command_rec *cmdp; + module *modp; + + for (modp = top_module; modp; modp = modp->next) + if (modp->cmds && (cmdp = find_command (cmd_name, modp->cmds))) { + *mod = modp; + return cmdp; + } + + return NULL; +} + +char *handle_command (cmd_parms *parms, void *config, char *l) +{ + char *args, *cmd_name; + command_rec *cmd; + module *mod; + + ++parms->config_line; + if((l[0] == '#') || (!l[0])) return NULL; + + args = l; + cmd_name = getword_conf (parms->temp_pool, &args); + if (*cmd_name == '\0') return NULL; + + if (!(cmd = find_command_in_modules (cmd_name, &mod))) { + return pstrcat (parms->pool, "Invalid command ", cmd_name, NULL); + } + else { + void *mconfig = get_module_config (config, mod); + void *sconfig = get_module_config (parms->server->module_config, mod); + + if (!mconfig && mod->create_dir_config) { + mconfig = (*mod->create_dir_config) (parms->pool, parms->path); + set_module_config (config, mod, mconfig); + } + + if (!sconfig && mod->create_server_config) { + sconfig = (*mod->create_server_config)(parms->pool, parms->server); + set_module_config (parms->server->module_config, mod, sconfig); + } + + return invoke_cmd (cmd, parms, mconfig, args); + } +} + +char *srm_command_loop (cmd_parms *parms, void *config) +{ + char l[MAX_STRING_LEN]; + + while (!(cfg_getline (l, MAX_STRING_LEN, parms->infile))) { + char *errmsg = handle_command (parms, config, l); + if (errmsg) return errmsg; + } + + return NULL; +} + +/* + * Generic command functions... + */ + +char *set_string_slot (cmd_parms *cmd, char *struct_ptr, char *arg) +{ + /* This one's pretty generic... */ + + int offset = (int)cmd->info; + *(char **)(struct_ptr + offset) = pstrdup (cmd->pool, arg); + return NULL; +} + +/***************************************************************** + * + * Reading whole config files... + */ + +cmd_parms default_parms = { NULL, 0, -1, NULL, 0, NULL, NULL, NULL, NULL }; + +char *server_root_relative (pool *p, char *file) +{ +#ifdef __EMX__ + /* Add support for OS/2 drive names */ + if ((file[0] == '/') || (file[1] == ':')) return file; +#else + if (file[0] == '/') return file; +#endif + return make_full_path (p, server_root, file); +} + +void process_resource_config(server_rec *s, char *fname, pool *p, pool *ptemp) +{ + FILE *cfg; + char *errmsg; + cmd_parms parms; + + fname = server_root_relative (p, fname); + + /* GCC's initialization extensions are soooo nice here... */ + + parms = default_parms; + parms.config_file = fname; + parms.pool = p; + parms.temp_pool = ptemp; + parms.server = s; + parms.override = (RSRC_CONF|OR_ALL)&~(OR_AUTHCFG|OR_LIMIT); + + if(!(cfg = fopen(fname, "r"))) { + fprintf(stderr,"httpd: could not open document config file %s\n", + fname); + perror("fopen"); + exit(1); + } + + parms.infile = cfg; + + errmsg = srm_command_loop (&parms, s->lookup_defaults); + + if (errmsg) { + fprintf (stderr, "Syntax error on line %d of %s:\n", + parms.config_line, fname); + fprintf (stderr, "%s\n", errmsg); + exit(1); + } + + fclose(cfg); +} + + +int parse_htaccess(void **result, request_rec *r, int override, + char *d, char *filename) +{ + FILE *f; + cmd_parms parms; + char *errmsg; + const struct htaccess_result *cache; + struct htaccess_result *new; + void *dc; + +/* 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 = d; + + if((f=pfopen(r->pool, filename, "r"))) { + dc = create_per_dir_config (r->pool); + + parms.infile = f; + parms.config_file = filename; + + errmsg = srm_command_loop (&parms, dc); + + pfclose(r->pool, f); + + if (errmsg) { + log_reason (errmsg, filename, r); + return SERVER_ERROR; + } + + *result = dc; + } else + dc = NULL; + +/* cache it */ + new = palloc(r->pool, sizeof(struct htaccess_result)); + new->dir = pstrdup(r->pool, d); + new->override = override; + new->htaccess = dc; +/* add to head of list */ + new->next = r->htaccess; + r->htaccess = new; + + return OK; +} + +/***************************************************************** + * + * Virtual host stuff; note that the commands that invoke this stuff + * are with the command table in http_core.c. + */ + +server_rec *init_virtual_host (pool *p, char *hostname) +{ + server_rec *s = (server_rec *)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 ) + 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->host_addr.s_addr = get_virthost_addr (hostname, &s->host_port); + s->port = s->host_port; /* set them the same, by default */ + s->next = NULL; + + s->is_virtual = 1; + s->virthost = pstrdup(p, hostname); + s->names = NULL; + + s->module_config = create_empty_config (p); + s->lookup_defaults = create_per_dir_config (p); + + return s; +} + +int is_virtual_server (server_rec *s) +{ + return s->is_virtual; +} + +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 = + merge_per_dir_configs (p, main_server->lookup_defaults, + virt->lookup_defaults); + + if (virt->port == 0) + virt->port = main_server->port; + + 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; + } +} + +/***************************************************************** + * + * Getting *everything* configured... + */ + +void init_config_globals (pool *p) +{ + /* ServerRoot, server_confname set in httpd.c */ + + standalone = 1; + user_name = DEFAULT_USER; + user_id = uname2id(DEFAULT_USER); + group_id = gname2id(DEFAULT_GROUP); + daemons_to_start = DEFAULT_START_DAEMON; + daemons_min_free = DEFAULT_MIN_FREE_DAEMON; + daemons_max_free = DEFAULT_MAX_FREE_DAEMON; + daemons_limit = HARD_SERVER_LIMIT; + pid_fname = DEFAULT_PIDLOG; + scoreboard_fname = DEFAULT_SCOREBOARD; + max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; + bind_address.s_addr = htonl(INADDR_ANY); + listeners = NULL; +} + +server_rec *init_server_config(pool *p) +{ + server_rec *s = (server_rec *)pcalloc (p, sizeof (server_rec)); + + s->port = DEFAULT_PORT; + s->server_admin = DEFAULT_ADMIN; + s->server_hostname = NULL; + s->error_fname = DEFAULT_ERRORLOG; + s->srm_confname = RESOURCE_CONFIG_FILE; + s->access_confname = ACCESS_CONFIG_FILE; + s->timeout = DEFAULT_TIMEOUT; + s->keep_alive_timeout = DEFAULT_KEEPALIVE_TIMEOUT; + s->keep_alive = DEFAULT_KEEPALIVE; + s->next = NULL; + s->host_addr.s_addr = htonl (INADDR_ANY); /* NOT virtual host; + * don't match any real network + * interface. + */ + s->host_port = 0; /* matches any port */ + + s->module_config = create_server_config (p, s); + s->lookup_defaults = create_default_per_dir_config (p); + + return s; +} + +server_rec *read_config(pool *p, pool *ptemp, char *confname) +{ + server_rec *s = init_server_config(p); + module *m; + + init_config_globals(p); + + /* All server-wide config files now have the SAME syntax... */ + + process_resource_config (s, confname, p, ptemp); + process_resource_config (s, s->srm_confname, p, ptemp); + process_resource_config (s, s->access_confname, p, ptemp); + + fixup_virtual_hosts (p, s); + + for (m = top_module; m; m = m->next) + if (m->init) + (*m->init) (s, p); + + return s; +} + diff --git a/RELEASE_1_1_X/src/main/http_core.c b/RELEASE_1_1_X/src/main/http_core.c new file mode 100644 index 00000000000..e365be60749 --- /dev/null +++ b/RELEASE_1_1_X/src/main/http_core.c @@ -0,0 +1,950 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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_conf_globals.h" + +#include "http_main.h" /* For the default_handler below... */ +#include "http_log.h" +#include "rfc1413.h" +#include "util_md5.h" +#include "scoreboard.h" + +/* 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. + */ + +void *create_core_dir_config (pool *a, char *dir) +{ + core_dir_config *conf = + (core_dir_config *)pcalloc(a, sizeof(core_dir_config)); + + if (!dir || dir[strlen(dir) - 1] == '/') conf->d = dir; + else conf->d = pstrcat (a, dir, "/", NULL); + + conf->opts = dir ? OPT_UNSET : OPT_ALL; + conf->override = dir ? OR_UNSET : OR_ALL; + + conf->content_md5 = 2; + + conf->hostname_lookups = 2;/* binary, but will use 2 as an "unset = on" */ + conf->do_rfc1413 = DEFAULT_RFC1413 | 2; /* set bit 1 to indicate default */ + return (void *)conf; +} + +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 = + (core_dir_config *)pcalloc (a, sizeof(core_dir_config)); + int i; + + memcpy ((char *)conf, (const char *)base, sizeof(core_dir_config)); + + conf->d = new->d; + + if (new->opts != OPT_UNSET) conf->opts = new->opts; + if (new->override != OR_UNSET) conf->override = new->override; + if (new->default_type) conf->default_type = new->default_type; + + if (new->auth_type) conf->auth_type = new->auth_type; + if (new->auth_name) conf->auth_name = new->auth_name; + if (new->requires) conf->requires = new->requires; + + 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 != 2) + 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; + + return (void*)conf; +} + +void *create_core_server_config (pool *a, server_rec *s) +{ + core_server_config *conf = + (core_server_config *)pcalloc(a, sizeof(core_server_config)); + int is_virtual = s->is_virtual; + + conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME; + conf->document_root = is_virtual ? NULL : DOCUMENT_LOCATION; + conf->sec = make_array (a, 40, sizeof(void *)); + conf->sec_url = make_array (a, 40, sizeof(void *)); + + return (void *)conf; +} + +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 = + (core_server_config *)pcalloc(p, sizeof(core_server_config)); + + *conf = *virt; + if (!conf->access_name) conf->access_name = base->access_name; + if (!conf->document_root) conf->document_root = base->document_root; + conf->sec = append_arrays (p, virt->sec, base->sec); + conf->sec_url = append_arrays (p, virt->sec_url, base->sec_url); + + return conf; +} + +/* Add per-directory configuration entry (for <directory> section); + * these are part of the core server config. + */ + +void add_per_dir_conf (server_rec *s, void *dir_config) +{ + core_server_config *sconf = get_module_config (s->module_config, + &core_module); + void **new_space = (void **) push_array (sconf->sec); + + *new_space = dir_config; +} + +void add_per_url_conf (server_rec *s, void *url_config) +{ + core_server_config *sconf = get_module_config (s->module_config, + &core_module); + void **new_space = (void **) push_array (sconf->sec_url); + + *new_space = url_config; +} + +/***************************************************************** + * + * 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... + */ + +int allow_options (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->opts; +} + +int allow_overrides (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->override; +} + +char *auth_type (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->auth_type; +} + +char *auth_name (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->auth_name; +} + +char *default_type (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->default_type ? conf->default_type : DEFAULT_TYPE; +} + +char *document_root (request_rec *r) /* Don't use this!!! */ +{ + core_server_config *conf = + (core_server_config *)get_module_config(r->server->module_config, + &core_module); + + return conf->document_root; +} + +array_header *requires (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->requires; +} + + +/* 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 *response_code_string (request_rec *r, int error_index) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->response_code_strings[error_index]; +} + +const char * +get_remote_host(conn_rec *conn, void *dir_config, int type) +{ + struct in_addr *iaddr; + struct hostent *hptr; +#ifdef MAXIMUM_DNS + char **haddr; +#endif + core_dir_config *dir_conf; + +/* If we haven't checked the host name, and we want to */ + dir_conf = (core_dir_config *)get_module_config(dir_config, &core_module); + + if (conn->remote_host == NULL && dir_conf->hostname_lookups) + { +#ifdef STATUS + int old_stat = update_child_status(conn->child_num, + SERVER_BUSY_DNS, + (request_rec*)NULL); +#endif /* STATUS */ + iaddr = &(conn->remote_addr.sin_addr); + hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), AF_INET); + if (hptr != NULL) + { + conn->remote_host = pstrdup(conn->pool, (void *)hptr->h_name); + str_tolower (conn->remote_host); + +#ifdef MAXIMUM_DNS + /* Grrr. Check THAT name to make sure it's really the name of the addr. */ + /* Code from Harald Hanche-Olsen <hanche@imf.unit.no> */ + + hptr = gethostbyname(conn->remote_host); + if (hptr) + { + for(haddr=hptr->h_addr_list; *haddr; haddr++) + if(((struct in_addr *)(*haddr))->s_addr == iaddr->s_addr) + break; + } + if((!hptr) || (!(*haddr))) + conn->remote_host = NULL; +#endif + } +/* if failed, set it to the NULL string to indicate error */ + if (conn->remote_host == NULL) conn->remote_host = ""; +#ifdef STATUS + (void)update_child_status(conn->child_num,old_stat,(request_rec*)NULL); +#endif /* STATUS */ + } + +/* + * 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) return NULL; + else return conn->remote_ip; + } +} + +const char * +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 *) + get_module_config(r->per_dir_config, &core_module); + + if (dir_conf->do_rfc1413 & 1) + return rfc1413(r->connection, r->server); + else + return NULL; +} + +/***************************************************************** + * + * Commands... this module handles almost all of the NCSA httpd.conf + * commands, but most of the old srm.conf is in the the modules. + */ + +char *set_access_name (cmd_parms *cmd, void *dummy, char *arg) +{ + void *sconf = cmd->server->module_config; + core_server_config *conf = get_module_config (sconf, &core_module); + + conf->access_name = arg; + return NULL; +} + +char *set_document_root (cmd_parms *cmd, void *dummy, char *arg) +{ + void *sconf = cmd->server->module_config; + core_server_config *conf = get_module_config (sconf, &core_module); + + if (!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->document_root = arg; + return NULL; +} + +char *set_error_document (cmd_parms *cmd, core_dir_config *conf, char *line) +{ + int error_number, index_number; + char *w; + + /* 1st parameter should be a 3 digit number, which we recognize; + * convert it into an array index + */ + + w = getword_conf (cmd->pool, &line); + error_number = atoi(w); + index_number = index_of_response(error_number); + + if (index_number < 0) + return pstrcat (cmd->pool, "Illegal HTTP response code ", w, NULL); + + /* Nuke trailing '"', if present */ + + if (line[strlen(line) - 1] == '"') line[strlen(line) - 1] = '\0'; + + /* Store it... */ + + conf->response_code_strings[index_number] = 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. + */ + +char *set_override (cmd_parms *cmd, core_dir_config *d, char *l) +{ + char *w; + + d->override = OR_NONE; + while(l[0]) { + w = 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 pstrcat (cmd->pool, "Illegal override option ", w, NULL); + } + + return NULL; +} + +char *set_options (cmd_parms *cmd, core_dir_config *d, char *l) +{ + d->opts = OPT_NONE; + while(l[0]) { + char *w = getword_conf(cmd->pool, &l); + if(!strcasecmp(w,"Indexes")) + d->opts |= OPT_INDEXES; + else if(!strcasecmp(w,"Includes")) + d->opts |= OPT_INCLUDES; + else if(!strcasecmp(w,"IncludesNOEXEC")) + d->opts |= (OPT_INCLUDES | OPT_INCNOEXEC); + else if(!strcasecmp(w,"FollowSymLinks")) + d->opts |= OPT_SYM_LINKS; + else if(!strcasecmp(w,"SymLinksIfOwnerMatch")) + d->opts |= OPT_SYM_OWNER; + else if(!strcasecmp(w,"execCGI")) + d->opts |= OPT_EXECCGI; + else if (!strcasecmp(w,"MultiViews")) + d->opts |= OPT_MULTI; + else if (!strcasecmp(w,"RunScripts")) /* AI backcompat. Yuck */ + d->opts |= OPT_MULTI|OPT_EXECCGI; + else if(!strcasecmp(w,"None")) + d->opts = OPT_NONE; + else if(!strcasecmp(w,"All")) + d->opts = OPT_ALL; + else + return pstrcat (cmd->pool, "Illegal option ", w, NULL); + } + + return NULL; +} + +char *require (cmd_parms *cmd, core_dir_config *c, char *arg) +{ + require_line *r; + + if (!c->requires) + c->requires = make_array (cmd->pool, 2, sizeof(require_line)); + + r = (require_line *)push_array (c->requires); + r->requirement = pstrdup (cmd->pool, arg); + r->method_mask = cmd->limited; + return NULL; +} + +char *limit (cmd_parms *cmd, void *dummy, char *arg) +{ + char *limited_methods = getword(cmd->pool,&arg,'>'); + int limited = 0; + + if (cmd->limited > 0) return "Can't nest <Limit> sections"; + + while(limited_methods[0]) { + char *method = getword_conf (cmd->pool, &limited_methods); + if(!strcasecmp(method,"GET")) limited |= (1 << M_GET); + else if(!strcasecmp(method,"PUT")) limited |= (1 << M_PUT); + else if(!strcasecmp(method,"POST")) limited |= (1 << M_POST); + else if(!strcasecmp(method,"DELETE")) limited |= (1 << M_DELETE); + else if(!strcasecmp(method,"CONNECT")) limited |= (1 << M_CONNECT); + else return "unknown method in <Limit>"; + } + + cmd->limited = limited; + return NULL; +} + +char *endlimit (cmd_parms *cmd, void *dummy, void *dummy2) +{ + if (cmd->limited == -1) return "</Limit> unexpected"; + + cmd->limited = -1; + return NULL; +} + +static char *end_dir_magic = "</Directory> outside of any <Directory> section"; + +char *end_dirsection (cmd_parms *cmd, void *dummy) { + return end_dir_magic; +} + +char *dirsection (cmd_parms *cmd, void *dummy, char *arg) +{ + char *errmsg, *endp = strrchr (arg, '>'); + int old_overrides = cmd->override; + char *old_path = cmd->path; + void *new_dir_conf = create_per_dir_config (cmd->pool); + + if (endp) *endp = '\0'; + + if (cmd->path) return "<Directory> sections don't nest"; + if (cmd->limited != -1) return "Can't have <Directory> within <Limit>"; + + cmd->path = getword_conf (cmd->pool, &arg); + cmd->override = OR_ALL|ACCESS_CONF; + + errmsg = srm_command_loop (cmd, new_dir_conf); + add_per_dir_conf (cmd->server, new_dir_conf); + + cmd->path = old_path; + cmd->override = old_overrides; + + if (errmsg == end_dir_magic) return NULL; + return errmsg; +} + +static char *end_url_magic = "</Location> outside of any <Location> section"; + +char *end_urlsection (cmd_parms *cmd, void *dummy) { + return end_url_magic; +} + +char *urlsection (cmd_parms *cmd, void *dummy, char *arg) +{ + char *errmsg, *endp = strrchr (arg, '>'); + int old_overrides = cmd->override; + char *old_path = cmd->path; + core_dir_config *conf; + + void *new_url_conf = create_per_dir_config (cmd->pool); + + if (endp) *endp = '\0'; + + if (cmd->path) return "<Location> sections don't nest"; + if (cmd->limited != -1) return "Can't have <Location> within <Limit>"; + + cmd->path = getword_conf (cmd->pool, &arg); + cmd->override = OR_ALL|ACCESS_CONF; + + errmsg = srm_command_loop (cmd, new_url_conf); + if (errmsg != end_url_magic) return errmsg; + + conf = (core_dir_config *)get_module_config(new_url_conf, &core_module); + conf->d = pstrdup(cmd->pool, cmd->path); /* No mangling, please */ + + add_per_url_conf (cmd->server, new_url_conf); + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +/* httpd.conf commands... beginning with the <VirtualHost> business */ + +char *end_virthost_magic = "</Virtualhost> out of place"; + +char *end_virtualhost_section (cmd_parms *cmd, void *dummy) { + return end_virthost_magic; +} + +char *virtualhost_section (cmd_parms *cmd, void *dummy, char *arg) +{ + server_rec *main_server = cmd->server, *s; + char *errmsg, *endp = strrchr (arg, '>'); + pool *p = cmd->pool, *ptemp = cmd->temp_pool; + + if (endp) *endp = '\0'; + + if (main_server->is_virtual) + return "<VirtualHost> doesn't nest!"; + + s = init_virtual_host (p, arg); + s->next = main_server->next; + main_server->next = s; + + cmd->server = s; + errmsg = srm_command_loop (cmd, s->lookup_defaults); + cmd->server = main_server; + + if (s->srm_confname) + process_resource_config (s, s->srm_confname, p, ptemp); + + if (s->access_confname) + process_resource_config (s, s->access_confname, p, ptemp); + + if (errmsg == end_virthost_magic) return NULL; + return errmsg; +} + +char *set_server_string_slot (cmd_parms *cmd, void *dummy, char *arg) +{ + /* This one's pretty generic... */ + + int offset = (int)cmd->info; + char *struct_ptr = (char *)cmd->server; + + *(char **)(struct_ptr + offset) = pstrdup (cmd->pool, arg); + return NULL; +} + +char *server_type (cmd_parms *cmd, void *dummy, char *arg) +{ + if (!strcasecmp (arg, "inetd")) standalone = 0; + else if (!strcasecmp (arg, "standalone")) standalone = 1; + else return "ServerType must be either 'inetd' or 'standalone'"; + + return NULL; +} + +char *server_port (cmd_parms *cmd, void *dummy, char *arg) { + cmd->server->port = atoi (arg); + return NULL; +} + +char *set_user (cmd_parms *cmd, void *dummy, char *arg) { + user_name = pstrdup (cmd->pool, arg); + user_id = uname2id (user_name); + return NULL; +} + +char *set_group (cmd_parms *cmd, void *dummy, char *arg) { + group_id = gname2id(arg); + return NULL; +} + +char *set_server_root (cmd_parms *cmd, void *dummy, char *arg) { + if (!is_directory (arg)) return "ServerRoot must be a valid directory"; + strcpy (server_root, arg); + return NULL; +} + +char *set_timeout (cmd_parms *cmd, void *dummy, char *arg) { + cmd->server->timeout = atoi (arg); + return NULL; +} + +char *set_keep_alive_timeout (cmd_parms *cmd, void *dummy, char *arg) { + cmd->server->keep_alive_timeout = atoi (arg); + return NULL; +} + +char *set_keep_alive (cmd_parms *cmd, void *dummy, char *arg) { + cmd->server->keep_alive = atoi (arg); + return NULL; +} + +char *set_pidfile (cmd_parms *cmd, void *dummy, char *arg) { + pid_fname = pstrdup (cmd->pool, arg); + return NULL; +} + +char *set_scoreboard (cmd_parms *cmd, void *dummy, char *arg) { + scoreboard_fname = pstrdup (cmd->pool, arg); + return NULL; +} + +char *set_idcheck (cmd_parms *cmd, core_dir_config *d, int arg) { + d->do_rfc1413 = arg; + return NULL; +} + +char *set_hostname_lookups (cmd_parms *cmd, core_dir_config *d, int arg) { + d->hostname_lookups = arg; + return NULL; +} + +char *set_serverpath (cmd_parms *cmd, void *dummy, char *arg) { + cmd->server->path = pstrdup (cmd->pool, arg); + cmd->server->pathlen = strlen (arg); + return NULL; +} + +char *set_content_md5 (cmd_parms *cmd, core_dir_config *d, int arg) { + d->content_md5 = arg; + return NULL; +} + +char *set_daemons_to_start (cmd_parms *cmd, void *dummy, char *arg) { + daemons_to_start = atoi (arg); + return NULL; +} + +char *set_min_free_servers (cmd_parms *cmd, void *dummy, char *arg) { + daemons_min_free = atoi (arg); + if (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"); + daemons_min_free = 1; + } + + return NULL; +} + +char *set_max_free_servers (cmd_parms *cmd, void *dummy, char *arg) { + daemons_max_free = atoi (arg); + return NULL; +} + +char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg) { + daemons_limit = atoi (arg); + if (daemons_limit > HARD_SERVER_LIMIT) + daemons_limit = HARD_SERVER_LIMIT; + return NULL; +} + +char *set_max_requests (cmd_parms *cmd, void *dummy, char *arg) { + max_requests_per_child = atoi (arg); + return NULL; +} + +char *set_bind_address (cmd_parms *cmd, void *dummy, char *arg) { + bind_address.s_addr = get_virthost_addr (arg, NULL); + return NULL; +} + +char *set_listener(cmd_parms *cmd, void *dummy, char *ips) +{ + listen_rec *new; + char *ports; + + if (cmd->server->is_virtual) return "Listen not allowed in <VirtualHost>"; + ports=strchr(ips, ':'); + if (ports != NULL) + { + if (ports == ips) return "Missing IP address"; + else if (ports[0] == '\0') + return "Address must end in :<port-number>"; + *(ports++) = '\0'; + } else + ports = ips; + + new=palloc(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 = get_virthost_addr(ips, NULL); + new->local_addr.sin_port = htons(atoi(ports)); + new->next = listeners; + listeners = new; + return NULL; +} + +/* Note --- ErrorDocument will now work from .htaccess files. + * The AllowOverride of Fileinfo allows webmasters to turn it off + */ + +command_rec core_cmds[] = { + +/* Old access config file commands */ + +{ "<Directory", dirsection, NULL, RSRC_CONF, RAW_ARGS, NULL }, +{ "</Directory>", end_dirsection, NULL, ACCESS_CONF, NO_ARGS, NULL }, +{ "<Location", urlsection, NULL, RSRC_CONF, RAW_ARGS, NULL }, +{ "</Location>", end_urlsection, NULL, ACCESS_CONF, NO_ARGS, NULL }, +{ "<Limit", limit, NULL, OR_ALL, RAW_ARGS, NULL }, +{ "</Limit>", endlimit, NULL, OR_ALL, RAW_ARGS, NULL }, +{ "AuthType", set_string_slot, (void*)XtOffsetOf(core_dir_config, auth_type), + OR_AUTHCFG, TAKE1, "an HTTP authorization type (e.g., \"Basic\")" }, +{ "AuthName", set_string_slot, (void*)XtOffsetOf(core_dir_config, auth_name), + OR_AUTHCFG, RAW_ARGS, NULL }, +{ "Require", require, NULL, OR_AUTHCFG, RAW_ARGS, NULL }, + +/* Old resource config file commands */ + +{ "AccessFileName", set_access_name, NULL, RSRC_CONF, TAKE1, NULL }, +{ "DocumentRoot", set_document_root, NULL, RSRC_CONF, TAKE1, NULL }, +{ "ErrorDocument", set_error_document, NULL, OR_FILEINFO, RAW_ARGS, NULL }, +{ "AllowOverride", set_override, NULL, ACCESS_CONF, RAW_ARGS, NULL }, +{ "Options", set_options, NULL, OR_OPTIONS, RAW_ARGS, NULL }, +{ "DefaultType", set_string_slot, + (void*)XtOffsetOf (core_dir_config, 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, FLAG, NULL }, +{ "User", set_user, NULL, RSRC_CONF, TAKE1, "a username"}, +{ "Group", set_group, NULL, RSRC_CONF, TAKE1, "a group name"}, +{ "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" }, +{ "ServerRoot", set_server_root, NULL, RSRC_CONF, TAKE1, "a directory"}, +{ "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"}, +{ "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_string_slot, + (void *)XtOffsetOf (server_rec, names), 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)"}, +{ "KeepAlive", set_keep_alive, NULL, RSRC_CONF, TAKE1, "Maximum Keep-Alive requests per connection (0 to disable)" }, +{ "IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF, FLAG, NULL }, +{ "ContentDigest", set_content_md5, NULL, RSRC_CONF|ACCESS_CONF|OR_AUTHCFG, FLAG, "whether or not to send a Content-MD5 header with each request" }, +{ "CacheNegotiatedDocs", }, +{ "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1, NULL }, +{ "MinSpareServers", set_min_free_servers, NULL, RSRC_CONF, TAKE1, NULL }, +{ "MaxSpareServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, NULL }, +{ "MaxServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, NULL }, +{ "ServersSafetyLimit", set_server_limit, NULL, RSRC_CONF, TAKE1, NULL }, +{ "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1, NULL }, +{ "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1, NULL }, +{ "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"}, +{ "<VirtualHost", virtualhost_section, NULL, RSRC_CONF, RAW_ARGS, NULL }, +{ "</VirtualHost>", end_virtualhost_section, NULL, RSRC_CONF, NO_ARGS, NULL }, +{ NULL }, +}; + +/***************************************************************** + * + * Core handlers for various phases of server operation... + */ + +int core_translate (request_rec *r) +{ + void *sconf = r->server->module_config; + core_server_config *conf = get_module_config (sconf, &core_module); + + if (r->proxyreq) return NOT_IMPLEMENTED; + if (r->uri[0] != '/') return BAD_REQUEST; + + if (r->server->path && + !strncmp(r->uri, r->server->path, r->server->pathlen)) + r->filename = pstrcat (r->pool, conf->document_root, + (r->uri + r->server->pathlen), NULL); + else + r->filename = pstrcat (r->pool, conf->document_root, r->uri, NULL); + + return OK; +} + +int do_nothing (request_rec *r) { return OK; } + +/* + * Default handler for MIME types without other handlers. Only GET + * 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... + */ + +int default_handler (request_rec *r) +{ + core_dir_config *d = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + int errstatus; + FILE *f; + + if (r->method_number != M_GET) return DECLINED; + + if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) { + log_reason("File does not exist", r->filename, r); + return NOT_FOUND; + } + + if ((errstatus = set_content_length (r, r->finfo.st_size)) + || (errstatus = set_last_modified (r, r->finfo.st_mtime))) + return errstatus; + +#ifdef __EMX__ + /* Need binary mode for OS/2 */ + f = fopen (r->filename, "rb"); +#else + f = fopen (r->filename, "r"); +#endif + + if (f == NULL) { + log_reason("file permissions deny server access", r->filename, r); + return FORBIDDEN; + } + + if (d->content_md5 & 1) { + table_set (r->headers_out, "Content-MD5", md5digest(r->pool, f)); + } + + soft_timeout ("send", r); + + send_http_header (r); + if (!r->header_only) send_fd (f, r); + fclose (f); + return OK; +} + +handler_rec core_handlers[] = { +{ "*/*", default_handler }, +{ NULL } +}; + +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 */ +}; diff --git a/RELEASE_1_1_X/src/main/http_log.c b/RELEASE_1_1_X/src/main/http_log.c new file mode 100644 index 00000000000..51fc2e1ce87 --- /dev/null +++ b/RELEASE_1_1_X/src/main/http_log.c @@ -0,0 +1,193 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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 + * + */ + + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" + +#include <stdarg.h> + +void error_log_child (void *cmd) +{ + /* 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... + */ + + cleanup_for_exec(); + signal (SIGHUP, SIG_IGN); + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); + exit (1); +} + +void open_error_log(server_rec *s, pool *p) +{ + char *fname; + + fname = server_root_relative (p, s->error_fname); + + if (*fname == '|') { + FILE *dummy; + + spawn_child(p, error_log_child, (void *)(fname+1), + kill_after_timeout, &dummy, NULL); + + if (dummy == NULL) { + fprintf (stderr, "Couldn't fork child for ErrorLog process\n"); + exit (1); + } + } else { + if(!(s->error_log = pfopen(p, fname, "a"))) { + fprintf(stderr,"httpd: could not open error log file %s.\n", fname); + perror("fopen"); + exit(1); + } + } +} + +void open_logs (server_rec *s_main, pool *p) +{ + server_rec *virt, *q; + + open_error_log (s_main, p); + + 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; + } +} + +void error_log2stderr(server_rec *s) { + if(fileno(s->error_log) != STDERR_FILENO) + dup2(fileno(s->error_log),STDERR_FILENO); +} + +void log_pid(pool *p, char *pid_fname) { + FILE *pid_file; + + if (!pid_fname) return; + pid_fname = server_root_relative (p, pid_fname); + if(!(pid_file = fopen(pid_fname,"w"))) { + perror("fopen"); + fprintf(stderr,"httpd: could not log pid to file %s\n", pid_fname); + exit(1); + } + fprintf(pid_file,"%ld\n",(long)getpid()); + fclose(pid_file); +} + +void log_error(char *err, server_rec *s) { + fprintf(s->error_log, "[%s] %s\n",get_time(),err); + fflush(s->error_log); +} + +void +log_unixerr(const char *routine, const char *file, const char *msg, + server_rec *s) +{ + const char *p, *q; + + p = strerror(errno); + q = get_time(); + + if (file != NULL) + fprintf(s->error_log, "[%s] %s: %s: %s\n", q, routine, file, p); + else + fprintf(s->error_log, "[%s] %s: %s\n", q, routine, p); + if (msg != NULL) fprintf(s->error_log, "[%s] - %s\n", q, msg); + + fflush(s->error_log); +} + +void +log_printf(const server_rec *s, const char *fmt, ...) +{ + va_list args; + + fprintf(s->error_log, "[%s] ", get_time()); + va_start (args, fmt); + vfprintf (s->error_log, fmt, args); + va_end (args); + + fputc('\n', s->error_log); + fflush(s->error_log); +} + +void log_reason(char *reason, char *file, request_rec *r) { + fprintf (r->server->error_log, + "[%s] access to %s failed for %s, reason: %s\n", + get_time(), file, + get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME), + reason); + fflush (r->server->error_log); +} + diff --git a/RELEASE_1_1_X/src/main/http_main.c b/RELEASE_1_1_X/src/main/http_main.c new file mode 100644 index 00000000000..9d566836283 --- /dev/null +++ b/RELEASE_1_1_X/src/main/http_main.c @@ -0,0 +1,1574 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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 Shambhala. + */ + + +#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 "scoreboard.h" +#include <setjmp.h> +#ifdef HAVE_SHMGET +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#if defined(AUX) /* Aren't defined anyplace */ +extern char *shmat(int, char *, int); +extern int shmctl(int, int, struct shmid_ds *); +extern int shmget(key_t, int, int); +extern char *sbrk(int); +#endif +#endif +#ifdef SecureWare +#include <sys/security.h> +#include <sys/audit.h> +#include <prot.h> +#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 standalone; +uid_t user_id; +char *user_name; +gid_t group_id; +int max_requests_per_child; +char *pid_fname; +char *scoreboard_fname; +char *server_argv0; +struct in_addr bind_address; +listen_rec *listeners; +int daemons_to_start; +int daemons_min_free; +int daemons_max_free; +int daemons_limit; +time_t restart_time; + +char server_root[MAX_STRING_LEN]; +char server_confname[MAX_STRING_LEN]; + +/* *Non*-shared http_main globals... */ + +server_rec *server_conf; +JMP_BUF jmpbuffer; +JMP_BUF restart_buffer; +int sd; +static fd_set listenfds; +static int listenmaxfd; +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 --- Shambhala 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.). + */ + +int one_process = 0; + +#if defined(FCNTL_SERIALIZED_ACCEPT) +static struct flock lock_it = { F_WRLCK, 0, 0, 0 }; +static struct flock unlock_it = { F_UNLCK, 0, 0, 0 }; + +static int lock_fd=-1; + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +void +accept_mutex_init(pool *p) +{ + char lock_fname[30]; + + strcpy(lock_fname, "/usr/tmp/htlock.XXXXXX"); + + if (mktemp(lock_fname) == NULL || lock_fname[0] == '\0') + { + fprintf (stderr, "Cannot assign name to lock file!\n"); + exit (1); + } + + lock_fd = popenf(p, lock_fname, O_CREAT | O_WRONLY, 0644); + if (lock_fd == -1) + { + perror ("open"); + fprintf (stderr, "Cannot open lock file: %s\n", lock_fname); + exit (1); + } + unlink(lock_fname); +} + +void accept_mutex_on() +{ + int ret; + + while ((ret = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + log_unixerr("fcntl", "F_SETLKW", "Error getting accept lock. Exiting!", + server_conf); + exit(1); + } +} + +void accept_mutex_off() +{ + if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0) + { + log_unixerr("fcntl", "F_SETLKW", "Error freeing accept lock. Exiting!", + server_conf); + exit(1); + } +} +#elif defined(FLOCK_SERIALIZED_ACCEPT) + +static int lock_fd=-1; + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +void +accept_mutex_init(pool *p) +{ + char lock_fname[30]; + + strcpy(lock_fname, "/usr/tmp/htlock.XXXXXX"); + + if (mktemp(lock_fname) == NULL || lock_fname[0] == '\0') + { + fprintf (stderr, "Cannot assign name to lock file!\n"); + exit (1); + } + + lock_fd = popenf(p, lock_fname, O_CREAT | O_WRONLY, 0644); + if (lock_fd == -1) + { + perror ("open"); + fprintf (stderr, "Cannot open lock file\n"); + exit (1); + } + unlink(lock_fname); +} + +void accept_mutex_on() +{ + int ret; + + while ((ret = flock(lock_fd, LOCK_EX)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + log_unixerr("flock", "LOCK_EX", "Error getting accept lock. Exiting!", + server_conf); + exit(1); + } +} + +void accept_mutex_off() +{ + if (flock (lock_fd, LOCK_UN) < 0) + { + log_unixerr("flock", "LOCK_UN", "Error freeing accept lock. Exiting!", + server_conf); + exit(1); + } +} +#else +/* Default --- no serialization. Other methods *could* go here, + * as #elifs... + */ +#define accept_mutex_init(x) +#define accept_mutex_on() +#define accept_mutex_off() +#endif + +void usage(char *bin) +{ + fprintf(stderr,"Usage: %s [-d directory] [-f file] [-v]\n",bin); + fprintf(stderr,"-d directory : specify an alternate initial ServerRoot\n"); + fprintf(stderr,"-f file : specify an alternate ServerConfigFile\n"); + 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 conn_rec *current_conn; +static request_rec *timeout_req; +static char *timeout_name = NULL; +static int alarms_blocked = 0; +static int alarm_pending = 0; + +void abort_connection (conn_rec *); + +void timeout(sig) /* Also called on SIGPIPE */ +int sig; +{ + char errstr[MAX_STRING_LEN]; + void *dirconf; + + signal(SIGPIPE, SIG_IGN); /* Block SIGPIPE */ + if (alarms_blocked) { + alarm_pending = 1; + return; + } + + if (!current_conn) { +#ifdef NEXT + longjmp(jmpbuffer,1); +#else + siglongjmp(jmpbuffer,1); +#endif + } + + if (timeout_req != NULL) dirconf = timeout_req->per_dir_config; + else dirconf = current_conn->server->lookup_defaults; + if (sig == SIGPIPE) { + sprintf(errstr,"%s lost connection to client %s", + timeout_name ? timeout_name : "request", + get_remote_host(current_conn, dirconf, REMOTE_NAME)); + } else { + sprintf(errstr,"%s timed out for %s", + timeout_name ? timeout_name : "request", + get_remote_host(current_conn, dirconf, REMOTE_NAME)); + } + + if (!current_conn->keptalive) + log_error(errstr, current_conn->server); + + if (timeout_req) { + /* Someone has asked for this transaction to just be aborted + * if it times out... + */ + + request_rec *log_req = timeout_req; + + 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) + log_transaction(log_req); + + bclose(timeout_req->connection->client); + + if (!standalone) exit(0); +#ifdef NEXT + longjmp(jmpbuffer,1); +#else + siglongjmp(jmpbuffer,1); +#endif + } + else { + abort_connection (current_conn); + } +} + +/* + * 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. + */ + +void block_alarms() { + ++alarms_blocked; +} + +void unblock_alarms() { + --alarms_blocked; + if (alarms_blocked == 0 && alarm_pending) { + alarm_pending = 0; + timeout(0); + } +} + +void hard_timeout (char *name, request_rec *r) +{ + timeout_req = r; + timeout_name = name; + + signal(SIGALRM,(void (*)())timeout); + if (r->connection->keptalive) + alarm (r->server->keep_alive_timeout); + else + alarm (r->server->timeout); +} + +void soft_timeout (char *name, request_rec *r) +{ + timeout_name = name; + + signal(SIGALRM,(void (*)())timeout); + alarm (r->server->timeout); +} + +void kill_timeout (request_rec *dummy) { + alarm (0); + timeout_req = NULL; + timeout_name = NULL; +} + +/* reset_timeout (request_rec *) resets the timeout in effect, + * as long as it hasn't expired already. + */ + +void reset_timeout (request_rec *r) { + int i; + + if (timeout_name) { /* timeout has been set */ + i = alarm(r->server->timeout); + if (i == 0) /* timeout already expired, so set it back to 0 */ + alarm(0); + } +} + +/***************************************************************** + * + * Dealing with the scoreboard... a lot of these variables are global + * only to avoid getting clobbered by the longjmp() that happens when + * a hard timeout expires... + * + * We begin with routines which deal with the file itself... + */ + +#if defined(HAVE_MMAP) +static short_score *scoreboard_image=NULL; + +static void setup_shared_mem(void) +{ + caddr_t m; +#if defined(MAP_ANON) || defined(MAP_FILE) +/* BSD style */ + m = mmap((caddr_t)0, HARD_SERVER_LIMIT*sizeof(short_score), + PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); + if (m == (caddr_t)-1) + { + perror("mmap"); + fprintf(stderr, "httpd: Could not mmap memory\n"); + exit(1); + } +#else +/* Sun style */ + int fd; + + fd = open("/dev/zero", O_RDWR); + if (fd == -1) + { + perror("open"); + fprintf(stderr, "httpd: Could not open /dev/zero\n"); + exit(1); + } + m = mmap((caddr_t)0, HARD_SERVER_LIMIT*sizeof(short_score), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m == (caddr_t)-1) + { + perror("mmap"); + fprintf(stderr, "httpd: Could not mmap /dev/zero\n"); + exit(1); + } + close(fd); +#endif + scoreboard_image = (short_score *)m; +} + +#elif defined(HAVE_SHMGET) +static short_score *scoreboard_image=NULL; +static key_t shmkey = IPC_PRIVATE; +static int shmid = -1; + +static void setup_shared_mem(void) +{ + int score_size = HARD_SERVER_LIMIT*sizeof(short_score); + char errstr[MAX_STRING_LEN]; + struct shmid_ds shmbuf; +#ifdef MOVEBREAK + char *obrk; +#endif + + if ((shmid = shmget(shmkey, score_size, IPC_CREAT|SHM_R|SHM_W)) == -1) + { + perror("shmget"); + fprintf(stderr, "httpd: Could not call shmget\n"); + exit(1); + } + + sprintf(errstr, "created shared memory segment #%d", shmid); + log_error(errstr, server_conf); + +#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"); + fprintf(stderr, "httpd: Could not move break\n"); + } +#endif + +#define BADSHMAT ((short_score*)(-1)) + if ((scoreboard_image = (short_score*)shmat(shmid, 0, 0)) == BADSHMAT) + { + perror("shmat"); + fprintf(stderr, "httpd: Could not call shmat\n"); + /* + * 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) { + perror("shmctl"); + fprintf(stderr, "httpd: Could not stat segment #%d\n", shmid); + } + else + { + shmbuf.shm_perm.uid = user_id; + shmbuf.shm_perm.gid = group_id; + if (shmctl(shmid, IPC_SET, &shmbuf) != 0) { + perror("shmctl"); + fprintf(stderr, "httpd: Could not set segment #%d\n", shmid); + } + } + } + /* + * We must avoid leaving segments in the kernel's + * (small) tables. + */ + if (shmctl(shmid, IPC_RMID, NULL) != 0) { + perror("shmctl"); + fprintf(stderr, "httpd: Could not delete segment #%d\n", shmid); + sprintf(errstr, "could not remove shared memory segment #%d", shmid); + log_unixerr("shmctl","IPC_RMID",errstr, server_conf); + } + if (scoreboard_image == BADSHMAT) /* now bailout */ + exit(1); + +#ifdef MOVEBREAK + if (obrk == (char *)-1) + return; /* nothing else to do */ + if (sbrk(-(MOVEBREAK)) == (char *)-1) + { + perror("sbrk"); + fprintf(stderr, "httpd: Could not move break back\n"); + } +#endif +} + +#else +static short_score scoreboard_image[HARD_SERVER_LIMIT]; +static int have_scoreboard_fname = 0; +static int scoreboard_fd; + +static int force_write (int fd, char *buffer, int bufsz) +{ + int rv, orig_sz = bufsz; + + do { + rv = write (fd, buffer, bufsz); + if (rv > 0) { + buffer += rv; + bufsz -= rv; + } + } while (rv > 0 && bufsz > 0); + + return rv < 0? rv : orig_sz - bufsz; +} + +static int force_read (int fd, char *buffer, int bufsz) +{ + int rv, orig_sz = bufsz; + + do { + rv = read (fd, buffer, bufsz); + if (rv > 0) { + buffer += rv; + bufsz -= rv; + } + } while (rv > 0 && bufsz > 0); + + return rv < 0? rv : orig_sz - bufsz; +} +#endif + +/* Called by parent process */ +void reinit_scoreboard (pool *p) +{ +#if defined(HAVE_SHMGET) || defined(HAVE_MMAP) + if (scoreboard_image == NULL) + { + setup_shared_mem(); + } + memset(scoreboard_image, 0, HARD_SERVER_LIMIT*sizeof(short_score)); +#else + scoreboard_fname = server_root_relative (p, scoreboard_fname); + + have_scoreboard_fname = 1; + + scoreboard_fd = popenf(p, scoreboard_fname, O_CREAT|O_RDWR, 0644); + if (scoreboard_fd == -1) + { + fprintf (stderr, "Cannot open scoreboard file:\n"); + perror (scoreboard_fname); + exit (1); + } + + memset ((char*)scoreboard_image, 0, sizeof(scoreboard_image)); + force_write (scoreboard_fd, (char*)scoreboard_image, + sizeof(scoreboard_image)); +#endif +} + +/* called by child */ +void reopen_scoreboard (pool *p) +{ +#if !defined(HAVE_MMAP) && !defined(HAVE_SHMGET) + if (scoreboard_fd != -1) pclosef (p, scoreboard_fd); + + scoreboard_fd = popenf(p, scoreboard_fname, O_CREAT|O_RDWR, 0666); + if (scoreboard_fd == -1) + { + fprintf (stderr, "Cannot open scoreboard file:\n"); + perror (scoreboard_fname); + exit (1); + } +#endif +} + +void cleanup_scoreboard () +{ +#if !defined(HAVE_MMAP) && !defined(HAVE_SHMGET) + unlink (scoreboard_fname); +#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. + */ + +void sync_scoreboard_image () +{ +#if !defined(HAVE_MMAP) && !defined(HAVE_SHMGET) + lseek (scoreboard_fd, 0L, 0); + force_read (scoreboard_fd, (char*)scoreboard_image, + sizeof(scoreboard_image)); +#endif +} + +int update_child_status (int child_num, int status, request_rec *r) +{ + short_score new_score_rec; + int old_status; + + if (child_num < 0) + return -1; + + memcpy(&new_score_rec,&scoreboard_image[child_num],sizeof new_score_rec); + new_score_rec.pid = getpid(); + old_status = new_score_rec.status; + new_score_rec.status = status; + +#if defined(STATUS) + new_score_rec.last_used=time(NULL); + if (status == SERVER_DEAD) { + /* + * Reset individual counters + */ + new_score_rec.my_access_count = 0L; + new_score_rec.my_bytes_served = 0L; + new_score_rec.conn_count = (unsigned short)0; + new_score_rec.conn_bytes = (unsigned long)0; + } + if (r) { + int slot_size; + conn_rec *c = r->connection; + slot_size = sizeof(new_score_rec.client) - 1; + strncpy(new_score_rec.client, get_remote_host(c, r->per_dir_config, + REMOTE_NAME), slot_size); + new_score_rec.client[slot_size] = '\0'; + slot_size = sizeof(new_score_rec.request) - 1; + strncpy(new_score_rec.request, (r->the_request ? r->the_request : + "NULL"), slot_size); + new_score_rec.request[slot_size] = '\0'; + } +#endif + +#if defined(HAVE_MMAP) || defined(HAVE_SHMGET) + memcpy(&scoreboard_image[child_num], &new_score_rec, sizeof(short_score)); +#else + lseek (scoreboard_fd, (long)child_num * sizeof(short_score), 0); + force_write (scoreboard_fd, (char*)&new_score_rec, sizeof(short_score)); +#endif + + return old_status; +} + +int get_child_status (int child_num) +{ + if (child_num<0 || child_num>=HARD_SERVER_LIMIT) + return -1; + else + return scoreboard_image[child_num].status; +} + +int count_busy_servers () +{ + int i; + int res = 0; + + for (i = 0; i < HARD_SERVER_LIMIT; ++i) + if (scoreboard_image[i].status == SERVER_BUSY_READ || + scoreboard_image[i].status == SERVER_BUSY_WRITE || + scoreboard_image[i].status == SERVER_BUSY_KEEPALIVE || + scoreboard_image[i].status == SERVER_BUSY_LOG || + scoreboard_image[i].status == SERVER_BUSY_DNS) + ++res; + return res; +} + +short_score get_scoreboard_info(int i) +{ + return (scoreboard_image[i]); +} + +#if defined(STATUS) +void increment_counts (int child_num, request_rec *r, int flag) +{ + long int bs=0; + short_score new_score_rec=scoreboard_image[child_num]; + + if (r->sent_bodyct) + bgetopt(r->connection->client, BO_BYTECT, &bs); + + if (flag) { + new_score_rec.conn_count = (unsigned short)0; + new_score_rec.conn_bytes = (unsigned long)0; + } + new_score_rec.access_count ++; + new_score_rec.my_access_count ++; + new_score_rec.conn_count ++; + new_score_rec.bytes_served += (unsigned long)bs; + new_score_rec.my_bytes_served += (unsigned long)bs; + new_score_rec.conn_bytes += (unsigned long)bs; + + times(&new_score_rec.times); + +#if defined(HAVE_MMAP) || defined(HAVE_SHMGET) + memcpy(&scoreboard_image[child_num], &new_score_rec, sizeof(short_score)); +#else + lseek (scoreboard_fd, (long)child_num * sizeof(short_score), 0); + force_write (scoreboard_fd, (char*)&new_score_rec, sizeof(short_score)); +#endif +} +#endif + +int count_idle_servers () +{ + int i; + int res = 0; + + for (i = 0; i < HARD_SERVER_LIMIT; ++i) + if (scoreboard_image[i].status == SERVER_READY + || scoreboard_image[i].status == SERVER_STARTING) + ++res; + + return res; +} + +int find_free_child_num () +{ + int i; + + for (i = 0; i < HARD_SERVER_LIMIT; ++i) + if (scoreboard_image[i].status == SERVER_DEAD) + return i; + + return -1; +} + +int find_child_by_pid (int pid) +{ + int i; + + for (i = 0; i < HARD_SERVER_LIMIT; ++i) + if (scoreboard_image[i].pid == pid) + return i; + + return -1; +} + +void reclaim_child_processes () +{ + int i, status; + int my_pid = getpid(); + + sync_scoreboard_image(); + for (i = 0; i < HARD_SERVER_LIMIT; ++i) { + int pid = scoreboard_image[i].pid; + + if (pid != my_pid && pid != 0) + waitpid (scoreboard_image[i].pid, &status, 0); + } +} + +/* Finally, this routine is used by the caretaker process to wait for + * a while... + */ + +static JMP_BUF wait_timeout_buf; +static int wait_or_timeout_retval = -1; + +static void longjmp_out_of_alarm (int sig) { +#ifdef NEXT + longjmp (wait_timeout_buf, 1); +#else + siglongjmp (wait_timeout_buf, 1); +#endif +} + +int wait_or_timeout (int *status) +{ + wait_or_timeout_retval = -1; + +#if defined(NEXT) + if (setjmp(wait_timeout_buf) != 0) { +#else + if (sigsetjmp(wait_timeout_buf, 1) != 0) { +#endif + errno = ETIMEDOUT; + return wait_or_timeout_retval; + } + + signal (SIGALRM, longjmp_out_of_alarm); + alarm(1); +#if defined(NEXT) + wait_or_timeout_retval = wait((union wait *)status); +#else + wait_or_timeout_retval = wait(status); +#endif + alarm(0); + return wait_or_timeout_retval; +} + + +/***************************************************************** + * Here follows a long bunch of generic server bookkeeping stuff... + */ + +void detach() +{ + int x; + + chdir("/"); + if((x = fork()) > 0) + exit(0); + else if(x == -1) { + fprintf(stderr,"httpd: unable to fork new process\n"); + perror("fork"); + exit(1); + } +#ifndef NO_SETSID + if((pgrp=setsid()) == -1) { + fprintf(stderr,"httpd: setsid failed\n"); + perror("setsid"); + exit(1); + } +#else +#if defined(NEXT) + if(setpgrp(0,getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { + fprintf(stderr,"httpd: setpgrp or getpgrp failed\n"); + perror("setpgrp"); + exit(1); + } +#else +#ifdef __EMX__ + /* OS/2 doesn't support process group IDs */ + pgrp=getpid(); +#else + if((pgrp=setpgrp(getpid(),0)) == -1) { + fprintf(stderr,"httpd: setpgrp failed\n"); + perror("setpgrp"); + exit(1); + } +#endif +#endif +#endif +} + +void sig_term() { + log_error("httpd: caught SIGTERM, shutting down", server_conf); + cleanup_scoreboard(); +#ifndef NO_KILLPG + killpg(pgrp,SIGKILL); +#else + kill(-pgrp,SIGKILL); +#endif + shutdown(sd,2); + close(sd); + exit(1); +} + +void bus_error() { + log_error("httpd: caught SIGBUS, dumping core", server_conf); + chdir(server_root); + abort(); + exit(1); +} + +void seg_fault() { + log_error("httpd: caught SIGSEGV, dumping core", server_conf); + chdir(server_root); + abort(); + exit(1); +} + +void just_die() /* SIGHUP to child process??? */ +{ + exit (0); +} + +/* Reset group privileges, after rereading the config files + * (our uid may have changed, and if so, we want the new perms). + * + * Don't reset the uid yet --- we do that only in the child process, + * so as not to lose any root privs. But we can set the group stuff + * now, once, as opposed to once per each new child. + * + * 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() +{ + if(!geteuid()) { + char *name; + + /* Get username if passed as a uid */ + + if (user_name[0] == '#') { + struct passwd* ent; + uid_t uid=atoi(&user_name[1]); + + if ((ent = getpwuid(uid)) == NULL) { + log_unixerr("getpwuid",NULL,"couldn't determine user name from uid", server_conf); + exit(1); + } + + name = ent->pw_name; + } else name = user_name; + +#ifndef __EMX__ + /* OS/2 dosen't support groups. */ + + /* Reset `groups' attributes. */ + + if (initgroups(name, group_id) == -1) { + log_unixerr("initgroups", NULL, "unable to set groups", server_conf); + exit (1); + } + + if (setgid(group_id) == -1) { + log_unixerr("setgid", NULL, "unable to set group id", server_conf); + exit (1); + } +#endif + } +} + +void restart() { + signal (SIGALRM, SIG_IGN); + alarm (0); +#ifdef NEXT + longjmp(restart_buffer,1); +#else + siglongjmp(restart_buffer,1); +#endif +} + +void set_signals() { +#ifndef NO_USE_SIGACTION + struct sigaction sa; +#endif + if(!one_process) { + signal(SIGSEGV,(void (*)())seg_fault); + signal(SIGBUS,(void (*)())bus_error); + } + +#ifdef NO_USE_SIGACTION + signal(SIGTERM,(void (*)())sig_term); + signal(SIGHUP,(void (*)())restart); +#else + memset(&sa,0,sizeof sa); + sa.sa_handler=(void (*)())sig_term; + if(sigaction(SIGTERM,&sa,NULL) < 0) + log_unixerr("sigaction(SIGTERM)", NULL, NULL, server_conf); + sa.sa_handler=(void (*)())restart; + if(sigaction(SIGHUP,&sa,NULL) < 0) + log_unixerr("sigaction(SIGHUP)", NULL, NULL, server_conf); +#endif +} + +/***************************************************************** + * Connection structures and accounting... + * Should these be global? Only to this file, at least... + */ + +pool *pconf; /* Pool for config stuff */ +pool *ptrans; /* Pool for per-transaction stuff */ + +server_rec *find_virtual_server (struct in_addr server_ip, int port, + server_rec *server) +{ + server_rec *virt; + + for (virt = server->next; virt; virt = virt->next) + if ((virt->is_virtual == 1) && /* VirtualHost */ + (virt->host_addr.s_addr == htonl(INADDR_ANY) || + virt->host_addr.s_addr == server_ip.s_addr) && + (virt->host_port == 0 || virt->host_port == port)) + return virt; + + return server; +} + +void default_server_hostnames(server_rec *s) +{ + struct hostent *h, *main; + char *def_hostname; + int n; + + /* Main host first */ + + if (!s->server_hostname) + s->server_hostname = get_local_host(pconf); + + def_hostname = s->server_hostname; + main = gethostbyname(def_hostname); + + /* Then virtual hosts */ + + for (s = s->next; s; s = s->next) { + /* Check to see if we might be a HTTP/1.1 virtual host - same IP */ + for (n = 0; main->h_addr_list[n] != NULL; n++) { + if (s->host_addr.s_addr == + (((struct in_addr *)(main->h_addr_list[n]))->s_addr)) + s->is_virtual = 2; + } + + if (!s->server_hostname) { + if (s->is_virtual == 2) + s->server_hostname = s->virthost; + else if (s->host_addr.s_addr == htonl(INADDR_ANY)) + s->server_hostname = def_hostname; + else + { + h = gethostbyaddr ((char *)&(s->host_addr), + sizeof (struct in_addr), AF_INET); + if (h != NULL) + s->server_hostname = pstrdup (pconf, (char *)h->h_name); + } + } + } +} + +void abort_connection (conn_rec *c) +{ + /* Make sure further I/O DOES NOT HAPPEN */ + shutdown (c->client->fd, 2); + signal (SIGPIPE, SIG_IGN); /* Ignore further complaints */ + c->aborted = 1; +} + +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 *)pcalloc (p, sizeof(conn_rec)); + + /* Get a connection structure, and initialize what fields we can + * (the rest are zeroed out by pcalloc). + */ + + conn = (conn_rec *)pcalloc(p, sizeof(conn_rec)); + conn->child_num = child_num; + + conn->pool = p; + conn->local_addr = *saddr; + conn->server = find_virtual_server(saddr->sin_addr, ntohs(saddr->sin_port), + server); + conn->client = inout; + + conn->remote_addr = *remaddr; + conn->remote_ip = pstrdup (conn->pool, + inet_ntoa(conn->remote_addr.sin_addr)); + + return conn; +} + +/***************************************************************** + * Child process main loop. + * The following vars are static to avoid getting clobbered by longjmp(); + * they are really private to child_main. + */ + +static int csd; +static int dupped_csd; +static int requests_this_child; +static int child_num; + +void child_main(int child_num_arg) +{ + int clen; + struct sockaddr sa_server; + struct sockaddr sa_client; + + csd = -1; + dupped_csd = -1; + child_num = child_num_arg; + requests_this_child = 0; + reopen_scoreboard (pconf); + (void)update_child_status (child_num, SERVER_READY, (request_rec*)NULL); + + /* Only try to switch if we're running as root */ + if(!geteuid() && setuid(user_id) == -1) { + log_unixerr("setuid", NULL, "unable to change uid", server_conf); + exit (1); + } + +#ifdef NEXT + setjmp(jmpbuffer); +#else + sigsetjmp(jmpbuffer,1); +#endif +#ifndef __EMX__ + signal(SIGURG, timeout); +#endif + + while (1) { + BUFF *conn_io; + request_rec *r; + + alarm(0); /* Cancel any outstanding alarms. */ + timeout_req = NULL; /* No request in progress */ + current_conn = NULL; + signal(SIGPIPE, timeout); + + clear_pool (ptrans); + + sync_scoreboard_image(); + + if ((count_idle_servers() >= daemons_max_free) + || (max_requests_per_child > 0 + && ++requests_this_child >= max_requests_per_child)) + { + exit(0); + } + + clen=sizeof(sa_client); + (void)update_child_status (child_num, SERVER_READY, (request_rec*)NULL); + + accept_mutex_on(); /* Lock around "accept", if necessary */ + + if (listeners != NULL) + { + fd_set fds; + + for (;;) { + memcpy(&fds, &listenfds, sizeof(fd_set)); +#ifdef HPUX + csd = select(listenmaxfd+1, (int*)&fds, NULL, NULL, NULL); +#else + csd = select(listenmaxfd+1, &fds, NULL, NULL, NULL); +#endif + if (csd == -1 && errno != EINTR) + log_unixerr("select",NULL,"select error", server_conf); + if (csd <= 0) continue; + for (sd=listenmaxfd; sd >= 0; sd--) + if (FD_ISSET(sd, &fds)) break; + if (sd < 0) continue; + + clen=sizeof(sa_client); + do csd=accept(sd, &sa_client, &clen); + while (csd == -1 && errno == EINTR); + if (csd != -1) break; + log_unixerr("accept", "(client socket)", NULL, server_conf); + } + } else + while ((csd=accept(sd, &sa_client, &clen)) == -1) + if (errno != EINTR) + log_unixerr("accept",NULL,"socket error: accept failed", server_conf); + + accept_mutex_off(); /* unlock after "accept" */ + + clen = sizeof(sa_server); + if(getsockname(csd, &sa_server, &clen) < 0) { + log_unixerr("getsockname", NULL, NULL, server_conf); + continue; + } + + (void)update_child_status (child_num, SERVER_BUSY_READ, (request_rec*)NULL); + conn_io = bcreate(ptrans, B_RDWR); + dupped_csd = csd; +#if defined(NEED_DUPPED_CSD) + if ((dupped_csd = dup(csd)) < 0) { + log_unixerr("dup", NULL, "couldn't duplicate csd", server_conf); + dupped_csd = csd; /* Oh well... */ + } +#endif + 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); + + r = read_request (current_conn); + (void)update_child_status (child_num, SERVER_BUSY_WRITE, r); + if (r) process_request (r); /* else premature EOF --- ignore */ + +#if defined(STATUS) + if (r) increment_counts(child_num,r,1); +#endif + while (r && current_conn->keepalive) { + bflush(conn_io); + destroy_pool(r->pool); + (void)update_child_status (child_num, SERVER_BUSY_KEEPALIVE, (request_rec*)NULL); + r = read_request (current_conn); + (void)update_child_status (child_num, SERVER_BUSY_WRITE, r); + if (r) process_request (r); + +#if defined(STATUS) + if (r) increment_counts(child_num,r,0); +#endif + } +#if 0 + if (bytes_in_pool (ptrans) > 80000) + log_printf(r->server, + "Memory hog alert: allocated %ld bytes for %s", + bytes_in_pool (ptrans), r->the_request); +#endif + bflush(conn_io); + bclose(conn_io); + } +} + +void make_child(server_rec *server_conf, int child_num) +{ + int pid; + + if (one_process) { + signal (SIGHUP, (void (*)())just_die); + signal (SIGTERM, (void (*)())just_die); + child_main (child_num); + } + + if ((pid = fork()) == -1) { + log_unixerr("fork", NULL, "Unable to fork new process", server_conf); + return; + } + + if (!pid) { + signal (SIGHUP, (void (*)())just_die); + signal (SIGTERM, (void (*)())just_die); + child_main (child_num); + } +} + +static int +make_sock(pool *pconf, const struct sockaddr_in *server) +{ + int s; + const int one = 1; + const int keepalive_value = 1; + + if ((s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) { + perror("socket"); + fprintf(stderr,"httpd: could not get socket\n"); + exit(1); + } + + note_cleanups_for_fd (pconf, s); /* arrange to close on exec or restart */ + + if((setsockopt(s, SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one))) + == -1) { + perror("setsockopt(SO_REUSEADDR)"); + fprintf(stderr,"httpd: could not set socket option SO_REUSEADDR\n"); + exit(1); + } + if((setsockopt(s, SOL_SOCKET,SO_KEEPALIVE,(char *)&keepalive_value, + sizeof(keepalive_value))) == -1) { + perror("setsockopt(SO_KEEPALIVE)"); + fprintf(stderr,"httpd: could not set socket option SO_KEEPALIVE\n"); + exit(1); + } + +#ifdef NEED_LINGER /* If puts don't complete, you could try this. */ + { + struct linger li; + li.l_onoff = 1; + li.l_linger = 900; + + if (setsockopt(s, SOL_SOCKET, SO_LINGER, + (char *)&li, sizeof(struct linger)) < 0) { + perror("setsockopt(SO_LINGER)"); + fprintf(stderr,"httpd: could not set socket option SO_LINGER\n"); + exit(1); + } + } +#endif /* NEED_LINGER */ + + if(bind(s, (struct sockaddr *)server,sizeof(struct sockaddr_in)) == -1) + { + perror("bind"); + if (server->sin_addr.s_addr != htonl(INADDR_ANY)) + fprintf(stderr,"httpd: could not bind to address %s port %d\n", + inet_ntoa(server->sin_addr), ntohs(server->sin_port)); + else + fprintf(stderr,"httpd: could not bind to port %d\n", + ntohs(server->sin_port)); + exit(1); + } + listen(s, 512); + return s; +} + + +/***************************************************************** + * Executive routines. + */ + +static int num_children = 0; + +void standalone_main(int argc, char **argv) +{ + struct sockaddr_in sa_server; + + standalone = 1; + sd = listenmaxfd = -1; + + if (!one_process) detach(); + +#ifdef NEXT + setjmp(restart_buffer); +#else + sigsetjmp(restart_buffer,1); +#endif + + signal (SIGHUP, SIG_IGN); /* Until we're done (re)reading config */ + + if(!one_process) + { +#ifndef NO_KILLPG + if (killpg(pgrp,SIGHUP) < 0) /* Kill 'em off */ +#else + if (kill(-pgrp,SIGHUP) < 0) +#endif + log_unixerr ("killpg SIGHUP", NULL, NULL, server_conf); + } + + if (sd != -1 || listenmaxfd != -1) { + reclaim_child_processes(); /* Not when just starting up */ + log_error ("SIGHUP received. Attempting to restart", server_conf); + } + + restart_time = time(NULL); + clear_pool (pconf); + ptrans = make_sub_pool (pconf); + + server_conf = read_config(pconf, ptrans, server_confname); + open_logs(server_conf, pconf); + set_group_privs(); + accept_mutex_init(pconf); + reinit_scoreboard(pconf); + + default_server_hostnames (server_conf); + + if (listeners == NULL) + { + memset((char *) &sa_server, 0, sizeof(sa_server)); + sa_server.sin_family=AF_INET; + sa_server.sin_addr=bind_address; + sa_server.sin_port=htons(server_conf->port); + + sd = make_sock(pconf, &sa_server); + } else + { + listen_rec *lr; + int fd; + + listenmaxfd = -1; + FD_ZERO(&listenfds); + for (lr=listeners; lr != NULL; lr=lr->next) + { + fd = make_sock(pconf, &lr->local_addr); + FD_SET(fd, &listenfds); + if (fd > listenmaxfd) listenmaxfd = fd; + } + sd = -1; + } + + set_signals(); + log_pid(pconf, pid_fname); + + num_children = 0; + + if (daemons_max_free < daemons_min_free + 1) /* Don't thrash... */ + daemons_max_free = daemons_min_free + 1; + + while (num_children < daemons_to_start) { + make_child(server_conf, num_children++); + } + + log_error ("Server configured -- resuming normal operations", server_conf); + + while (1) { + int status, child_slot; + int pid = wait_or_timeout(&status); + + if (pid >= 0) { + /* Child died... note that it's gone in the scoreboard. */ + sync_scoreboard_image(); + child_slot = find_child_by_pid (pid); + if (child_slot >= 0) + (void)update_child_status (child_slot, SERVER_DEAD, + (request_rec*)NULL); + } + + sync_scoreboard_image(); + if ((count_idle_servers() < daemons_min_free) + && (child_slot = find_free_child_num()) >= 0 + && child_slot <= daemons_limit) { + (void)update_child_status(child_slot,SERVER_STARTING, + (request_rec*)NULL); + make_child(server_conf, child_slot); + } + } + +} /* standalone_main */ + +extern char *optarg; +extern int optind; + +int +main(int argc, char *argv[]) +{ + int c; + +#ifdef AUX + (void)set42sig(); +#endif + +#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 + + init_alloc(); + pconf = permanent_pool; + ptrans = make_sub_pool(pconf); + + server_argv0 = argv[0]; + strcpy (server_root, HTTPD_ROOT); + strcpy (server_confname, SERVER_CONFIG_FILE); + + while((c = getopt(argc,argv,"Xd:f:v")) != -1) { + switch(c) { + case 'd': + strcpy (server_root, optarg); + break; + case 'f': + strcpy (server_confname, optarg); + break; + case 'v': + printf("Server version %s.\n",SERVER_VERSION); + exit(1); + case 'X': + ++one_process; /* Weird debugging mode. */ + break; + case '?': + usage(argv[0]); + } + } + +#ifdef __EMX__ + printf("%s \n",SERVER_VERSION); + printf("OS/2 port by Garey Smiley <garey@slink.com> \n"); +#endif + + setup_prelinked_modules(); + + server_conf = read_config (pconf, ptrans, server_confname); + + if(standalone) { + clear_pool (pconf); /* standalone_main rereads... */ + standalone_main(argc, argv); + } + else { + conn_rec *conn; + request_rec *r; + struct sockaddr sa_server, sa_client; + BUFF *cio; + + open_logs(server_conf, pconf); + set_group_privs(); + default_server_hostnames (server_conf); + + /* Only try to switch if we're running as root */ + if(!geteuid() && setuid(user_id) == -1) { + log_unixerr("setuid", NULL, "unable to change uid", server_conf); + exit (1); + } + + c = sizeof(sa_client); + if ((getpeername(fileno(stdin), &sa_client, &c)) < 0) + { +/* get peername will fail if the input isn't a socket */ + perror("getpeername"); + memset(&sa_client, '\0', sizeof(sa_client)); + } + + c = sizeof(sa_server); + if(getsockname(fileno(stdin), &sa_server, &c) < 0) { + perror("getsockname"); + fprintf(stderr, "Error getting local address\n"); + exit(1); + } + server_conf->port =ntohs(((struct sockaddr_in *)&sa_server)->sin_port); + cio = bcreate(ptrans, B_RDWR); + cio->fd = fileno(stdout); + cio->fd_in = fileno(stdin); + conn = new_connection (ptrans, server_conf, cio, + (struct sockaddr_in *)&sa_client, + (struct sockaddr_in *)&sa_server,-1); + r = read_request (conn); + if (r) process_request (r); /* else premature EOF (ignore) */ + + while (r && conn->keepalive) { + bflush(cio); + destroy_pool(r->pool); + r = read_request (conn); + if (r) process_request (r); + } + + bflush(cio); + bclose(cio); + } + exit (0); +} + + diff --git a/RELEASE_1_1_X/src/main/http_protocol.c b/RELEASE_1_1_X/src/main/http_protocol.c new file mode 100644 index 00000000000..258d49cac77 --- /dev/null +++ b/RELEASE_1_1_X/src/main/http_protocol.c @@ -0,0 +1,960 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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 rst. + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" /* For errors detected in basic auth + * common support code... + */ + +#include <stdarg.h> + +#define SET_BYTES_SENT(r) \ + do { if (r->sent_bodyct) \ + bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \ + } while (0) + +/* Handling of conditional gets (if-modified-since); Roy owes Rob beer. + * This would be considerably easier if strptime or timegm were portable... + */ + +const char month_snames[12][4] = { + "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +int find_month(char *mon) { + register int x; + + for(x=0;x<12;x++) + if(!strcmp(month_snames[x],mon)) + return x; + return -1; +} + +int later_than(struct tm *lms, char *ims) { + char *ip; + char mname[MAX_STRING_LEN]; + int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0, x; + + /* Whatever format we're looking at, it will start with weekday. */ + /* Skip to first space. */ + if(!(ip = strchr(ims,' '))) + return 0; + else + while(isspace(*ip)) + ++ip; + + if(isalpha(*ip)) { + /* ctime */ + sscanf(ip,"%s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year); + } + else if(ip[2] == '-') { + /* RFC 850 (normal HTTP) */ + char t[MAX_STRING_LEN]; + sscanf(ip,"%s %d:%d:%d",t,&hour,&min,&sec); + t[2] = '\0'; + day = atoi(t); + t[6] = '\0'; + strcpy(mname,&t[3]); + x = atoi(&t[7]); + /* Prevent wraparound from ambiguity */ + if(x < 70) + x += 100; + year = 1900 + x; + } + else { + /* RFC 822 */ + sscanf(ip,"%d %s %d %d:%d:%d",&day,mname,&year,&hour,&min,&sec); + } + month = find_month(mname); + + if((x = (1900+lms->tm_year) - year)) + return x < 0; + if((x = lms->tm_mon - month)) + return x < 0; + if((x = lms->tm_mday - day)) + return x < 0; + if((x = lms->tm_hour - hour)) + return x < 0; + if((x = lms->tm_min - min)) + return x < 0; + if((x = lms->tm_sec - sec)) + return x < 0; + + return 1; +} + + +int set_content_length (request_rec *r, long clength) +{ + char ts[MAX_STRING_LEN]; + + sprintf (ts, "%ld", (long)r->finfo.st_size); + table_set (r->headers_out, "Content-length", pstrdup (r->pool, ts)); + return 0; +} + +int set_keepalive(request_rec *r) +{ + char *conn = table_get (r->headers_in, "Connection"); + char *length = table_get (r->headers_out, "Content-length"); + + if (conn && length && !strncasecmp(conn, "Keep-Alive", 10) && + r->server->keep_alive_timeout && + (r->server->keep_alive > r->connection->keepalives)) { + char header[26]; + int left = r->server->keep_alive - r->connection->keepalives; + + r->connection->keepalive = 1; + r->connection->keepalives++; + sprintf(header, "timeout=%d, max=%d", r->server->keep_alive_timeout, + left); + table_set (r->headers_out, "Connection", "Keep-Alive"); + table_set (r->headers_out, "Keep-Alive", pstrdup(r->pool, header)); + + return 1; + } + + return 0; +} + +int set_last_modified(request_rec *r, time_t mtime) +{ + char *ts; + char *if_modified_since = table_get (r->headers_in, "If-modified-since"); + + /* Cacheing proxies use the absence of a Last-modified header + * to indicate that a document is dynamic and shouldn't be cached. + * For the moment, we enforce that here, though it would probably + * work just as well to generate an Expires: header in send_http_header. + * + * However, even in that case, if no_cache is set, we would *not* + * want to send USE_LOCAL_COPY, since the client isn't *supposed* + * to have it cached. + */ + + if (r->no_cache) return OK; + + ts = gm_timestr_822(r->pool, mtime); + table_set (r->headers_out, "Last-modified", ts); + + /* Check for conditional GETs --- note that we only want this check + * to succeed if the GET was successful; ErrorDocuments *always* get sent. + */ + + if (r->status == 200 && + if_modified_since && later_than(gmtime(&mtime), if_modified_since)) + + return USE_LOCAL_COPY; + else + return OK; +} + +/* + * Finally, real protocol stuff. + */ + +static char * +getline(char *s, int n, BUFF *in) +{ + int retval = bgets (s, n, in); + + if (retval == -1) return NULL; + + if (retval > 0 && s[retval-1] == '\n') s[retval-1] = '\0'; + + return s; +} + +void parse_uri (request_rec *r, char *uri) +{ + const char *s; + /* If we ever want to do byte-ranges a la Netscape & Franks, + * this is the place to parse them; with proper support in + * rprintf and rputc, and the sub-request setup and finalizers + * here, it'll all just work, even for vile cases like + * inclusion of byte-ranges of the output of CGI scripts, with + * the client requesting only a byte-range of *that*! + * + * But for now... + */ + +#ifdef __EMX__ + /* Variable for OS/2 fix below. */ + int loop; +#endif + +/* A proxy request contains a ':' early on, but not as first character */ + for (s=uri; s != '\0'; s++) + if (!isalnum(*s) && *s != '+' && *s != '-' && *s != '.') break; + + if (*s == ':' && s != uri) + { + r->proxyreq = 1; + r->uri = uri; + r->args = NULL; + } else + { + r->proxyreq = 0; + r->uri = getword (r->pool, &uri, '?'); + +#ifdef __EMX__ + /* 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. */ + for (loop = 0; loop <= strlen(r->uri); ++loop) { + if (r->uri[loop] == '\\') + r->uri[loop] = '/'; +}; +#endif + + if (*uri) r->args= uri; + else r->args = NULL; + } +} + +char *check_fulluri (request_rec *r, char *uri) { + char *name, *host; + int i, port; + + /* This routine parses full URLs, if they match the server */ + if (strncmp(uri, "http://", 7)) return uri; + name = pstrdup(r->pool, uri + 7); + + /* Find the hostname, assuming a valid request */ + i = ind(name, '/'); + name[i] = '\0'; + + /* Find the port */ + host = getword(r->pool, &name, ':'); + if (*name) port = atoi(name); + else port = 80; + + /* Make sure ports patch */ + if (port != r->server->port) return uri; + + /* Save it for later use */ + r->hostname = pstrdup(r->pool, host); + r->hostlen = 7 + i; + + /* The easy cases first */ + if (!strcasecmp(host, r->server->server_hostname)) { + return (uri + r->hostlen); + } + else if (!strcmp(host, inet_ntoa(r->connection->local_addr.sin_addr))) { + return (uri + r->hostlen); + } + + /* Now things get a bit trickier - check the IP address(es) of the host */ + /* they gave, see if it matches ours. */ + else { + struct hostent *hp; + int n; + + if ((hp = gethostbyname(host))) { + for (n = 0; hp->h_addr_list[n] != NULL; n++) { + if (r->connection->local_addr.sin_addr.s_addr == + (((struct in_addr *)(hp->h_addr_list[n]))->s_addr)) { + return (uri + r->hostlen); + } + } + } + } + + return uri; +} + +int read_request_line (request_rec *r) +{ + char l[HUGE_STRING_LEN]; + char *ll = l, *uri; + conn_rec *conn = r->connection; + int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol*/ + + l[0] = '\0'; + if(!getline(l, HUGE_STRING_LEN, conn->client)) + return 0; + if(!l[0]) + return 0; + + r->the_request = pstrdup (r->pool, l); + r->method = getword(r->pool, &ll,' '); + uri = getword(r->pool, &ll,' '); + uri = check_fulluri(r, uri); + parse_uri (r, uri); + + r->assbackwards = (ll[0] == '\0'); + r->protocol = pstrdup (r->pool, ll[0] ? ll : "HTTP/0.9"); + sscanf(r->protocol, "HTTP/%d.%d", &major, &minor); + r->proto_num = 1000*major + minor; + + return 1; +} + +void get_mime_headers(request_rec *r) +{ + char w[MAX_STRING_LEN]; + char *t; + conn_rec *c = r->connection; + + while(getline(w, MAX_STRING_LEN-1, c->client)) { + if(!w[0]) + return; + if(!(t = strchr(w,':'))) + continue; + *t++ = '\0'; + while(isspace(*t)) ++t; + + table_merge (r->headers_in, w, t); + } +} + +void check_hostalias (request_rec *r) { + char *host = getword(r->pool, &r->hostname, ':'); /* Get rid of port */ + int port = (*r->hostname) ? atoi(r->hostname) : 0; + server_rec *s; + + if (port && (port != r->server->port)) + return; + + if ((host[strlen(host)-1]) == '.') { + host[strlen(host)-1] = '\0'; + } + + r->hostname = host; + + for (s = r->server->next; s; s = s->next) { + char *names = s->names; + + if ((!strcasecmp(host, s->server_hostname)) && + (!port || (port == s->port))) { + r->server = r->connection->server = s; + if (r->hostlen && !strncmp(r->uri, "http://", 7)) { + r->uri += r->hostlen; + parse_uri(r, r->uri); + } + } + + if (!names) continue; + + while (*names) { + char *name = getword_conf (r->pool, &names); + + if ((is_matchexp(name) && !strcasecmp_match(host, name)) || + (!strcasecmp(host, name))) { + r->server = r->connection->server = s; + if (r->hostlen && !strncmp(r->uri, "http://", 7)) { + r->uri += r->hostlen; + r->proxyreq = 0; + } + } + } + } +} + +void check_serverpath (request_rec *r) { + server_rec *s; + + /* This is in conjunction with the ServerPath code in + * http_core, so we get the right host attached to a non- + * Host-sending request. + */ + + for (s = r->server->next; s; s = s->next) { + if (s->path && !strncmp(r->uri, s->path, s->pathlen)) + r->server = r->connection->server = s; + } +} + +request_rec *read_request (conn_rec *conn) +{ + request_rec *r = (request_rec *)pcalloc (conn->pool, sizeof(request_rec)); + + r->connection = conn; + r->server = conn->server; + r->pool = make_sub_pool(conn->pool); + + conn->keptalive = conn->keepalive; + conn->keepalive = 0; + + conn->user = NULL; + conn->auth_type = NULL; + + r->headers_in = make_table (r->pool, 50); + r->subprocess_env = make_table (r->pool, 50); + r->headers_out = make_table (r->pool, 5); + r->err_headers_out = make_table (r->pool, 5); + r->notes = make_table (r->pool, 5); + + r->request_config = create_request_config (r->pool); + r->per_dir_config = r->server->lookup_defaults; /* For now. */ + + r->sent_bodyct = 0; /* bytect isn't for body */ + + r->status = 200; /* Until further notice. + * Only changed by die(), or (bletch!) + * scan_script_header... + */ + + /* Get the request... */ + + hard_timeout ("read", r); + if (!read_request_line (r)) return NULL; + if (!r->assbackwards) get_mime_headers(r); + +/* handle Host header here, to get virtual server */ + + if (r->hostname || (r->hostname = table_get(r->headers_in, "Host"))) + check_hostalias(r); + else + check_serverpath(r); + + kill_timeout (r); + conn->keptalive = 0; /* We now have a request - so no more short timeouts */ + + if(!strcmp(r->method, "HEAD")) { + r->header_only=1; + r->method_number = M_GET; + } + else if(!strcmp(r->method, "GET")) + r->method_number = M_GET; + else if(!strcmp(r->method,"POST")) + r->method_number = M_POST; + else if(!strcmp(r->method,"PUT")) + r->method_number = M_PUT; + else if(!strcmp(r->method,"DELETE")) + r->method_number = M_DELETE; + else if(!strcmp(r->method,"CONNECT")) + r->method_number = M_CONNECT; + else + r->method_number = M_INVALID; /* Will eventually croak. */ + + 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 set_sub_req_protocol (request_rec *rnew, request_rec *r) +{ + rnew->assbackwards = 1; /* Don't send headers from this. */ + rnew->no_cache = 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 = 200; + + rnew->headers_in = r->headers_in; + rnew->subprocess_env = copy_table (rnew->pool, r->subprocess_env); + rnew->headers_out = make_table (rnew->pool, 5); + rnew->err_headers_out = make_table (rnew->pool, 5); + rnew->notes = make_table (rnew->pool, 5); + + rnew->main = r; +} + +void finalize_sub_req_protocol (request_rec *sub) +{ + SET_BYTES_SENT (sub->main); +} + +/* Support for the Basic authentication protocol, and a bit for Digest. + */ + +void note_auth_failure(request_rec *r) +{ + if (!strcasecmp(auth_type(r), "Basic")) + note_basic_auth_failure(r); + else if(!strcasecmp(auth_type(r), "Digest")) + note_digest_auth_failure(r); +} + +void note_basic_auth_failure(request_rec *r) +{ + if (strcasecmp(auth_type(r), "Basic")) + note_auth_failure(r); + else + table_set (r->err_headers_out, "WWW-Authenticate", + pstrcat(r->pool, "Basic realm=\"", auth_name(r), "\"", NULL)); +} + +void note_digest_auth_failure(request_rec *r) +{ + char nonce[10]; + + sprintf(nonce, "%lu", time(NULL)); + table_set (r->err_headers_out, "WWW-Authenticate", + pstrcat(r->pool, "Digest realm=\"", auth_name(r), + "\", nonce=\"", nonce, "\"", NULL)); +} + +int get_basic_auth_pw (request_rec *r, char **pw) +{ + char *auth_line = table_get (r->headers_in, "Authorization"); + char *t; + + if(!(t = auth_type(r)) || strcasecmp(t, "Basic")) + return DECLINED; + + if (!auth_name (r)) { + log_reason ("need AuthName", r->uri, r); + return SERVER_ERROR; + } + + if(!auth_line) { + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + + if (strcmp(getword (r->pool, &auth_line, ' '), "Basic")) { + /* Client tried to authenticate using wrong auth scheme */ + log_reason ("client used wrong authentication scheme", r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + + t = uudecode (r->pool, auth_line); + r->connection->user = getword_nulls (r->pool, &t, ':'); + r->connection->auth_type = "Basic"; + + *pw = t; + + return OK; +} + +#define RESPONSE_CODE_LIST " 200 302 304 400 401 403 404 500 503 501 502 " + +/* New Apache routine to map error responses into array indicies + * e.g. 400 -> 0, 500 -> 1, 502 -> 2 ... + * the indicies have no significance + */ + +char *status_lines[] = { + "200 OK", + "302 Found", + "304 Not Modified", + "400 Bad Request", + "401 Unauthorized", + "403 Forbidden", + "404 Not found", + "500 Server error", + "503 Out of resources", + "501 Not Implemented", + "502 Bad Gateway" +}; + +char *response_titles[] = { + "200 OK", /* Never actually sent, barring die(200,...) */ + "Document moved", /* 302 Redirect */ + "304 Not Modified", /* Never sent... 304 MUST be header only */ + "Bad Request", + "Authorization Required", + "Forbidden", + "File Not found", + "Server Error", + "Out of resources", + "Method not implemented", + "Bad Gateway" +}; + +int index_of_response(int err_no) { + char *cptr, err_string[10]; + static char *response_codes = RESPONSE_CODE_LIST; + int index_number; + + sprintf(err_string,"%3d",err_no); + + cptr = response_codes; + cptr++; + index_number = 0; + while (*cptr && strncmp(cptr, err_string, 3)) { + cptr += 4; + index_number++; + } + if (!*cptr) return -1; + return index_number; +} + + +void basic_http_header (request_rec *r) +{ + BUFF *fd = r->connection->client; + + if (r->assbackwards) return; + + if (!r->status_line) + r->status_line = status_lines[index_of_response(r->status)]; + + bvputs(fd, SERVER_PROTOCOL, " ", r->status_line, "\015\012", NULL); + bvputs(fd,"Date: ",gm_timestr_822 (r->pool, time(NULL)), "\015\012", NULL); + bvputs(fd,"Server: ", SERVER_VERSION, "\015\012", NULL); +} + +char *nuke_mime_parms (pool *p, char *content_type) +{ + /* How marvelous. Arena doesn't *accept* "text/html; level=3" + * as a MIME type, so we have to strip off the parms. + */ + +#ifndef ARENA_BUG_WORKAROUND + return content_type; +#else + + char *cp = strchr(content_type, ';'); + + if (cp) { + content_type = pstrdup (p, content_type); + cp = strchr (content_type, ';'); + + while (cp > content_type && isspace (cp[-1])) + --cp; + *cp = '\0'; + } + + return content_type; +#endif +} + +void send_http_header(request_rec *r) +{ + conn_rec *c = r->connection; + BUFF *fd = c->client; + const long int zero=0L; + array_header *hdrs_arr; + table_entry *hdrs; + int i; + + core_dir_config *dir_conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + char *default_type = dir_conf->default_type; + + if (r->assbackwards) { + bsetopt(fd, BO_BYTECT, &zero); + r->sent_bodyct = 1; + return; + } + + basic_http_header (r); + + set_keepalive (r); + + if (r->content_type) + bvputs(fd, "Content-type: ", + nuke_mime_parms (r->pool, r->content_type), "\015\012", NULL); + else if(default_type) + bvputs(fd, "Content-type: ", default_type, "\015\012", NULL); + + if (r->content_encoding) + bvputs(fd,"Content-encoding: ", r->content_encoding, "\015\012", NULL); + + if (r->content_language) + bvputs(fd,"Content-language: ", r->content_language, "\015\012", NULL); + + hdrs_arr = table_elts(r->headers_out); + hdrs = (table_entry *)hdrs_arr->elts; + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) continue; + bvputs(fd, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL); + } + + hdrs_arr = table_elts(r->err_headers_out); + hdrs = (table_entry *)hdrs_arr->elts; + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) continue; + bvputs(fd, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL); + } + + bputs("\015\012",fd); + + if (c->keepalive) + bflush(r->connection->client); /* For bugs in Netscape, perhaps */ + + bsetopt(fd, BO_BYTECT, &zero); + r->sent_bodyct = 1; /* Whatever follows is real body stuff... */ +} + +long read_client_block (request_rec *r, char *buffer, int bufsiz) +{ + return bread(r->connection->client, buffer, bufsiz); +} + +long send_fd(FILE *f, request_rec *r) +{ + char buf[IOBUFSIZE]; + long total_bytes_sent; + register int n,o,w; + conn_rec *c = r->connection; + + total_bytes_sent = 0; + while (!r->connection->aborted) { + while ((n= fread(buf, sizeof(char), IOBUFSIZE, f)) < 1 + && ferror(f) && errno == EINTR) + continue; + + if (n < 1) { + break; + } + o=0; + total_bytes_sent += n; + + while(n && !r->connection->aborted) { + w=bwrite(c->client, &buf[o], n); + if(w <= 0) + break; + reset_timeout(r); /* reset timeout after successfule write */ + n-=w; + o+=w; + } + } + bflush(c->client); + + SET_BYTES_SENT(r); + return total_bytes_sent; +} + +int rputc (int c, request_rec *r) +{ + if (r->connection->aborted) return EOF; + bputc(c, r->connection->client); + SET_BYTES_SENT(r); + return c; +} + +int +rputs(const char *str, request_rec *r) +{ + if (r->connection->aborted) return EOF; + SET_BYTES_SENT(r); + return bputs(str, r->connection->client); +} + +int rprintf(request_rec *r,const char *fmt,...) + { + va_list vlist; + int n; + + if(r->connection->aborted) return EOF; + va_start(vlist,fmt); + n=vbprintf(r->connection->client,fmt,vlist); + va_end(vlist); + SET_BYTES_SENT(r); + return n; + } + +int +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 = bwrite(fb, x, j); + if (i != j) + { + va_end(args); + return -1; + } + k += i; + } + va_end(args); + + SET_BYTES_SENT(r); + return k; +} + +void send_error_response (request_rec *r, int recursive_error) +{ + conn_rec *c = r->connection; + char *custom_response; + int status = r->status; + int idx = index_of_response (status); + char *location = table_get (r->headers_out, "Location"); + + if (!r->assbackwards) { + int i; + table *err_hdrs_arr = r->err_headers_out; + table_entry *err_hdrs = (table_entry *)err_hdrs_arr->elts; + + basic_http_header (r); + + /* For conditional get's which didn't send anything, *don't* + * send a bogus content-type, or any body --- but must still + * terminate header. + */ + + if (status == USE_LOCAL_COPY) { + if (set_keepalive(r)) + bputs("Connection: Keep-Alive\015\012", c->client); + bputs("\015\012", c->client); + return; + } + + if (status == REDIRECT) + bvputs(c->client, "Location: ", location, "\015\012", NULL); + + for (i = 0; i < err_hdrs_arr->nelts; ++i) { + if (!err_hdrs[i].key) continue; + bvputs(c->client, err_hdrs[i].key, ": ", err_hdrs[i].val, + "\015\012", NULL); + } + + bputs("Content-type: text/html\015\012\015\012", c->client); + } + + if (r->header_only) return; + + if ((custom_response = response_code_string (r, idx))) + bputs(custom_response, c->client); + else { + char *title = response_titles[idx]; + BUFF *fd = c->client; + + bvputs(fd,"<HEAD><TITLE>", title, "\n

", title, + "

\n", NULL); + + switch (r->status) { + case REDIRECT: + bvputs(fd, "The document has moved pool, location), "\">here.

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

\n", fd); + break; + case BAD_REQUEST: + bputs("Your browser sent a query that\n", fd); + bputs("this server could not understand.

\n", fd); + break; + case FORBIDDEN: + bvputs(fd, "You don't have permission to access ", + escape_html(r->pool, r->uri), "\non this server.

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

\n", NULL); + break; + case SERVER_ERROR: + bputs("The server encountered an internal error or\n", fd); + bputs("misconfiguration and was unable to complete\n", fd); + bputs("your request.

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

\n", fd); + break; + case NOT_IMPLEMENTED: + bvputs(fd, escape_html(r->pool, r->method), " to ", + escape_html(r->pool, r->uri), " not supported.

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

\015\012", fd); + break; + } + + if (recursive_error) { + char x[80]; + sprintf (x, "Additionally, an error of type %d was encountered\n", + recursive_error); + bputs(x, fd); + bputs("while trying to use an ErrorDocument to\n", fd); + bputs("handle the request.\n", fd); + } + bputs("\n", fd); + } + +} + +/* Finally, this... it's here to support nph- scripts + * Now what ever are we going to do about them when HTTP-NG packetization + * comes along? + */ + +void client_to_stdout (conn_rec *c) +{ + bflush(c->client); + dup2(c->client->fd, STDOUT_FILENO); +} diff --git a/RELEASE_1_1_X/src/main/http_request.c b/RELEASE_1_1_X/src/main/http_request.c new file mode 100644 index 00000000000..db5b13c93f5 --- /dev/null +++ b/RELEASE_1_1_X/src/main/http_request.c @@ -0,0 +1,870 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * http_request.c: functions to get and process requests + * + * Rob McCool 3/21/93 + * + * Thoroughly revamped by rst for Shambhala. 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_log.h" +#include "http_main.h" +#include "scoreboard.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. + */ + +int check_symlinks (char *d, int opts) +{ + struct stat lfi, fi; + char *lastp; + int res; + +#ifdef __EMX__ + /* OS/2 dosen't have symlinks */ + return OK; +#else + + 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 FORBIDDEN; + + if (stat (d, &fi) < 0) return FORBIDDEN; + + return (fi.st_uid == lfi.st_uid) ? OK : FORBIDDEN; + +#endif +} + +/* Dealing with the file system to get PATH_INFO + */ + +void get_path_info(request_rec *r) +{ + char *cp; + char *path = r->filename; + char *end = &path[strlen(path)]; + char *last_cp = NULL; + int rv; + + /* Advance over trailing slashes ... NOT part of filename */ + + for (cp = end; cp > path && cp[-1] == '/'; --cp) + continue; + + while (cp > path) { + + /* See if the pathname ending here exists... */ + + *cp = '\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 = pstrdup (r->pool, cp); + *cp = '\0'; + return; + } + else { + last_cp = cp; + + while (--cp > path && *cp != '/') + continue; + + while (cp > path && cp[-1] == '/') + --cp; + } + } +} + +int directory_walk (request_rec *r) +{ + core_server_config *sconf = get_module_config (r->server->module_config, + &core_module); + array_header *sec_array = copy_array (r->pool, sconf->sec); + void *per_dir_defaults = r->server->lookup_defaults; + + core_dir_config **sec = (core_dir_config **)sec_array->elts; + int num_sec = sec_array->nelts; + char *test_filename = pstrdup (r->pool, r->filename); + + int num_dirs, res; + int i; + + /* 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 (test_filename == NULL) { + r->filename = 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. + */ + + if (test_filename[0] != '/') + { +/* fake filenames only match Directory sections */ + void *this_conf, *entry_config; + core_dir_config *entry_core; + char *entry_dir; + int j; + + for (j = 0; j < num_sec; ++j) { + + entry_config = sec[j]; + if (!entry_config) continue; + + entry_core =(core_dir_config *) + get_module_config(entry_config, &core_module); + entry_dir = entry_core->d; + + this_conf = NULL; + if (is_matchexp(entry_dir)) { + if (!strcmp_match(test_filename, entry_dir)) + this_conf = entry_config; + } + else if (!strncmp (test_filename, entry_dir, strlen(entry_dir))) + this_conf = entry_config; + + if (this_conf) + per_dir_defaults = merge_per_dir_configs (r->pool, + per_dir_defaults, this_conf); + } + + r->per_dir_config = per_dir_defaults; + + return OK; + } + + no2slash (test_filename); + num_dirs = count_dirs(test_filename); + get_path_info (r); + + if (S_ISDIR (r->finfo.st_mode)) ++num_dirs; + + for (i = 1; i <= num_dirs; ++i) { + core_dir_config *core_dir = + (core_dir_config *)get_module_config(per_dir_defaults, &core_module); + int allowed_here = core_dir->opts; + int overrides_here = core_dir->override; + void *this_conf = NULL, *htaccess_conf = NULL; + char *this_dir = make_dirstr (r->pool, test_filename, i); + char *config_name = make_full_path(r->pool, this_dir, + sconf->access_name); + int j; + + /* Do symlink checks first, because they are done with the + * permissions appropriate to the *parent* directory... + */ + + if ((res = check_symlinks (this_dir, allowed_here))) + { + log_reason("Symbolic link not allowed", this_dir, r); + return res; + } + + /* Begin *this* level by looking for matching sections from + * access.conf. + */ + + for (j = 0; j < num_sec; ++j) { + void *entry_config = sec[j]; + core_dir_config *entry_core; + char *entry_dir; + + if (!entry_config) continue; + + entry_core = + (core_dir_config *)get_module_config(entry_config, &core_module); + entry_dir = entry_core->d; + + if (is_matchexp(entry_dir) && !strcmp_match(this_dir, entry_dir)) { + /* Don't try this wildcard again --- if it ends in '*' + * it'll match again, and subdirectories won't be able to + * override it... + */ + sec[j] = NULL; + this_conf = entry_config; + } + else if (!strcmp (this_dir, entry_dir)) + this_conf = entry_config; + } + + if (this_conf) + { + per_dir_defaults = + merge_per_dir_configs (r->pool, per_dir_defaults, this_conf); + core_dir =(core_dir_config *)get_module_config(per_dir_defaults, + &core_module); + } + overrides_here = core_dir->override; + + /* If .htaccess files are enabled, check for one. + */ + + if (overrides_here) { + res = parse_htaccess (&htaccess_conf, r, overrides_here, + this_dir, config_name); + if (res) return res; + } + + if (htaccess_conf) + per_dir_defaults = + merge_per_dir_configs (r->pool, per_dir_defaults, + htaccess_conf); + + } + + r->per_dir_config = per_dir_defaults; + + if ((res = check_symlinks (r->filename, allow_options(r)))) + { + log_reason("Symbolic link not allowed", r->filename, r); + return res; + } + + return OK; /* Can only "fail" if access denied + * by the symlink goop. + */ +} + +int location_walk (request_rec *r) +{ + core_server_config *sconf = get_module_config (r->server->module_config, + &core_module); + array_header *url_array = copy_array (r->pool, sconf->sec_url); + void *per_dir_defaults = r->per_dir_config; + + core_dir_config **url = (core_dir_config **)url_array->elts; + int num_url = url_array->nelts; + char *test_location = pstrdup (r->pool, r->uri); + + /* Go through the location entries, and check for matches. */ + + if (num_url) { + void *this_conf, *entry_config; + core_dir_config *entry_core; + char *entry_url; + int j; + +/* + * 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]; + if (!entry_config) continue; + + entry_core =(core_dir_config *) + get_module_config(entry_config, &core_module); + entry_url = entry_core->d; + + this_conf = NULL; + if (is_matchexp(entry_url)) { + if (!strcmp_match(test_location, entry_url)) + this_conf = entry_config; + } + else if (!strncmp (test_location, entry_url, strlen(entry_url))) + this_conf = entry_config; + + if (this_conf) + per_dir_defaults = 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 Shambhala 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... + */ + +request_rec *make_sub_request (request_rec *r) +{ + pool *rrp = make_sub_pool (r->pool); + request_rec *rr = pcalloc (rrp, sizeof (request_rec)); + + rr->pool = rrp; + return rr; +} + +request_rec *sub_req_lookup_simple (char *new_file, request_rec *r) +{ + /* This handles the simple case, common to ..._lookup_uri and _file, + * of looking up another file in the same directory. + */ + request_rec *rnew = make_sub_request (r); + pool *rnewp = rnew->pool; + int res; + + char *udir = make_dirstr(rnewp, r->uri, count_dirs(r->uri)); + char *fdir = make_dirstr(rnewp, r->filename, count_dirs(r->filename)); + + *rnew = *r; /* Copy per_dir config, etc. */ + rnew->pool = rnewp; + rnew->uri = make_full_path (rnewp, udir, new_file); + rnew->filename = make_full_path (rnewp, fdir, new_file); + set_sub_req_protocol (rnew, r); + + rnew->finfo.st_mode = 0; + + if ((res = check_symlinks (rnew->filename, allow_options (rnew)))) + { + rnew->status = res; + } + + if (rnew->finfo.st_mode == 0 && stat (rnew->filename, &rnew->finfo) < 0) + rnew->finfo.st_mode = 0; + + if ((rnew->status == 200) && (res = find_types (rnew))) + rnew->status = res; + + if ((rnew->status == 200) && (res = run_fixups (rnew))) + rnew->status = res; + + return rnew; +} + + +static int some_auth_required (request_rec *r); + +request_rec *sub_req_lookup_uri (char *new_file, request_rec *r) +{ + request_rec *rnew; + int res; + char *udir; + + rnew = make_sub_request (r); + rnew->connection = r->connection; + rnew->server = r->server; + rnew->request_config = create_request_config (rnew->pool); + rnew->htaccess = r->htaccess; /* copy htaccess cache */ + set_sub_req_protocol (rnew, r); + + if (new_file[0] == '/') + parse_uri(rnew, new_file); + else + { + udir = make_dirstr (rnew->pool, r->uri, count_dirs (r->uri)); + udir = escape_uri(rnew->pool, udir); /* re-escape it */ + parse_uri (rnew, make_full_path (rnew->pool, udir, new_file)); + } + + res = unescape_url (rnew->uri); + if (res) + { + rnew->status = res; + return rnew; + } + + getparents (rnew->uri); + + res = 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. + */ + + if ((res = directory_walk (rnew)) + || (!some_auth_required (rnew) ? 0 : + ((res = check_user_id (rnew)) || (res = check_auth (rnew)))) + || (res = check_access (rnew)) + || (res = find_types (rnew)) + || (res = run_fixups (rnew)) + ) + { + rnew->status = res; + } + + return rnew; +} + +request_rec *sub_req_lookup_file (char *new_file, request_rec *r) +{ + request_rec *rnew; + int res; + char *fdir; + + /* 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 don't have to redo any access checks. + */ + + if (strchr (new_file, '/') == NULL) + return sub_req_lookup_simple (new_file, r); + + rnew = make_sub_request (r); + fdir = make_dirstr (rnew->pool, r->filename, count_dirs (r->filename)); + + rnew->connection = r->connection; /* For now... */ + rnew->server = r->server; + rnew->request_config = create_request_config (rnew->pool); + rnew->htaccess = r->htaccess; /* copy htaccess cache */ + set_sub_req_protocol (rnew, r); + + rnew->uri = "INTERNALLY GENERATED file-relative req"; + rnew->filename = ((new_file[0] == '/') ? + new_file : + make_full_path (rnew->pool, fdir, new_file)); + + if ((res = directory_walk (rnew)) + || (res = check_access (rnew)) + || (!some_auth_required (rnew) ? 0 : + ((res = check_user_id (rnew)) && (res = check_auth (rnew)))) + || (res = find_types (rnew)) + || (res = run_fixups (rnew)) + ) + { + rnew->status = res; + } + + return rnew; +} + +int run_sub_req (request_rec *r) +{ + int retval = invoke_handler (r); + finalize_sub_req_protocol (r); + return retval; +} + +void destroy_sub_req (request_rec *r) +{ + /* Reclaim the space */ + destroy_pool (r->pool); +} + +/***************************************************************** + * + * Mainline request processing... + */ + +void die(int type, request_rec *r) +{ + int error_index = index_of_response (type); + char *custom_response = response_code_string(r, error_index); + int recursive_error = 0; + + /* 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 != 200) { + recursive_error = type; + + while (r->prev && r->prev->status != 200) + r = r->prev; /* Get back to original error */ + + type = r->status; + custom_response = NULL; /* Do NOT retry the custom thing! */ + } + + r->status = type; + + /* 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 (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; + table_set (r->headers_out, "Location", custom_response); + } else if ( custom_response[0] == '/') { + r->no_cache = 1; /* Do NOT send USE_LOCAL_COPY for + * error documents! + */ + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = pstrdup(r->pool, "GET"); + r->method_number = M_GET; + 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; + log_reason("Invalid error redirection directive", custom_response, + r); + } + } + + send_error_response (r, recursive_error); +} + +static void decl_die (int status, char *phase, request_rec *r) +{ + if (status == DECLINED) { + log_reason (pstrcat (r->pool, + "configuration error: couldn't ", + phase, NULL), + r->uri, + r); + die (SERVER_ERROR, r); + } + else die (status, r); +} + +static int some_auth_required (request_rec *r) +{ + /* Is there a require line configured for the type of *this* req? */ + + array_header *reqs_arr = 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; +} + +void process_request_internal (request_rec *r) +{ + int access_status; + + /* Kludge to be reading the assbackwards field outside of protocol.c, + * but we've got to check for this sort of nonsense somewhere... + */ + + if (r->assbackwards && r->header_only) { + /* Client asked for headers only with HTTP/0.9, which doesn't + * send headers! Have to dink things even to make sure the + * error message comes through... + */ + log_reason ("client sent illegal HTTP/0.9 request", r->uri, r); + r->header_only = 0; + die (BAD_REQUEST, r); + return; + } + + if (!r->hostname && (r->proto_num >= 1001)) { + /* Client sent us a 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 + */ + log_reason ("client sent HTTP/1.1 request without hostname", + r->uri, r); + die (BAD_REQUEST, r); + return; + } + + if (!r->proxyreq) + { + access_status = unescape_url(r->uri); + if (access_status) + { + die(access_status, r); + return; + } + + getparents(r->uri); /* OK --- shrinking transformations... */ + } + + if ((access_status = translate_name (r))) { + decl_die (access_status, "translate", r); + return; + } + + if ((access_status = directory_walk (r))) { + die (access_status, r); + return; + } + + if ((access_status = location_walk (r))) { + die (access_status, r); + return; + } + + if ((access_status = check_access (r)) != 0) { + decl_die (access_status, "check access", r); + return; + } + + if (some_auth_required (r)) { + if ((access_status = check_user_id (r)) != 0) { + decl_die (access_status, "check user. No user file?", r); + return; + } + + if ((access_status = check_auth (r)) != 0) { + decl_die (access_status, "check access. No groups file?", r); + return; + } + } + + if ((access_status = find_types (r)) != 0) { + decl_die (access_status, "find types", r); + return; + } + + if ((access_status = run_fixups (r)) != 0) { + die (access_status, r); + return; + } + + if ((access_status = invoke_handler (r)) != 0) + die (access_status, r); +} + +void process_request (request_rec *r) +{ +#ifdef STATUS + int old_stat; +#endif /* STATUS */ + process_request_internal (r); +#ifdef STATUS + old_stat = update_child_status (r->connection->child_num, SERVER_BUSY_LOG, + r); +#endif /* STATUS */ + log_transaction (r); +#ifdef STATUS + (void)update_child_status (r->connection->child_num, old_stat, r); +#endif /* STATUS */ +} + +table *rename_original_env (pool *p, table *t) +{ + array_header *env_arr = table_elts (t); + table_entry *elts = (table_entry *)env_arr->elts; + table *new = make_table (p, env_arr->nelts); + int i; + + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) continue; + table_set (new, pstrcat (p, "REDIRECT_", elts[i].key, NULL), + elts[i].val); + } + + return new; +} + +request_rec *internal_internal_redirect (char *new_uri, request_rec *r) +{ + request_rec *new = (request_rec *)pcalloc(r->pool, sizeof(request_rec)); + char t[10]; /* Long enough... */ + + new->connection = r->connection; + new->server = r->server; + new->pool = r->pool; + + /* A whole lot of this really ought to be shared with protocol.c... + * another missing cleanup. It's particularly inappropriate to be + * setting header_only, etc., here. + */ + + parse_uri (new, new_uri); + new->request_config = 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->method = r->method; + new->method_number = r->method_number; + + new->status = r->status; + new->assbackwards = r->assbackwards; + new->header_only = r->header_only; + new->protocol = r->protocol; + new->main = r->main; + + new->headers_in = r->headers_in; + new->headers_out = make_table (r->pool, 5); + new->err_headers_out = r->err_headers_out; + new->subprocess_env = rename_original_env (r->pool, r->subprocess_env); + new->notes = make_table (r->pool, 5); + new->htaccess = r->htaccess; /* copy .htaccess cache */ + + new->no_cache = r->no_cache; /* If we've already made up our minds + * about this, don't change 'em back! + */ + + sprintf (t, "%d", r->status); + table_set (new->subprocess_env, "REDIRECT_STATUS", pstrdup (r->pool, t)); + + return new; +} + +void internal_redirect (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. + */ + +void internal_redirect_handler (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); +} diff --git a/RELEASE_1_1_X/src/main/md5c.c b/RELEASE_1_1_X/src/main/md5c.c new file mode 100644 index 00000000000..4ec60cb1a38 --- /dev/null +++ b/RELEASE_1_1_X/src/main/md5c.c @@ -0,0 +1,354 @@ +/* + * This is work is derived from material Copyright RSA Data Security, Inc. + * + * The RSA copyright statement and Licence for that original material is + * included below. This is followed by the Apache copyright statement and + * licence for the modifications made to that material. + */ + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* ==================================================================== + * Copyright (c) 1996 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#include + +#include "md5.h" + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform(UINT4 state[4], const unsigned char block[64]); +static void Encode(unsigned char *output, const UINT4 *input, + unsigned int len); +static void Decode(UINT4 *output, const unsigned char *input, + unsigned int len); + +static unsigned char PADDING[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void +MD5Init(MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void +MD5Update(MD5_CTX *context, const unsigned char *input, unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += (UINT4)inputLen >> 29; + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) + { + memcpy(&context->buffer[index], input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy(&context->buffer[index], &input[i], inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void +MD5Final(unsigned char digest[16], MD5_CTX *context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information. */ + memset(context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ +static void +MD5Transform(UINT4 state[4], const unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + memset(x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void +Encode(unsigned char *output, const UINT4 *input, unsigned int len) +{ + unsigned int i, j; + UINT4 k; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + k = input[i]; + output[j] = (unsigned char)(k & 0xff); + output[j+1] = (unsigned char)((k >> 8) & 0xff); + output[j+2] = (unsigned char)((k >> 16) & 0xff); + output[j+3] = (unsigned char)((k >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void +Decode(UINT4 *output, const unsigned char *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} diff --git a/RELEASE_1_1_X/src/main/rfc1413.c b/RELEASE_1_1_X/src/main/rfc1413.c new file mode 100644 index 00000000000..556cb8aae56 --- /dev/null +++ b/RELEASE_1_1_X/src/main/rfc1413.c @@ -0,0 +1,233 @@ +/* ==================================================================== + * Copyright (c) 1995,1996 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. 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 . + * + */ + + +/* + * 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 Shambhala --- ditch the "sccsid" var if + * compiling with gcc (it *has* changed), include conf.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 */ +#include "http_log.h" /* for log_unixerr */ +#include "rfc1413.h" + +#ifndef _HPUX_SOURCE +#define _HPUX_SOURCE +#endif + +/* System libraries. */ + +#include + +#ifndef SCO +extern char *strchr(); +extern char *inet_ntoa(); +#endif + +/* 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 + +#define RFC1413_TIMEOUT 60 +#define ANY_PORT 0 /* Any old port will do */ +#define FROM_UNKNOWN "unknown" + +int rfc1413_timeout = RFC1413_TIMEOUT;/* Global so it can be changed */ + +static jmp_buf timebuf; + +/* bind_connect - bind both ends of a socket */ + +static int +get_rfc1413(int sock, const struct sockaddr_in *our_sin, + const struct sockaddr_in *rmt_sin, char user[256], 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]; + + /* + * 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) + { + log_unixerr("bind", NULL, "rfc1413: Error binding to local port", srv); + 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 */ + sprintf(buffer, "%u,%u\r\n", ntohs(rmt_sin->sin_port), + ntohs(our_sin->sin_port)); + do i = write(sock, buffer, strlen(buffer)); + while (i == -1 && errno == EINTR); + if (i == -1) + { + log_unixerr("write", NULL, "rfc1413: error sending request", srv); + return -1; + } + + /* + * Read response from server. We assume that all the data + * comes in a single packet. + */ + + do i = read(sock, buffer, RFC1413_MAXDATA); + while (i == -1 && errno == EINTR); + if (i == -1) + { + log_unixerr("read", NULL, "rfc1413: error reading response", srv); + return -1; + } + + buffer[i] = '\0'; +/* RFC1413_USERLEN = 512 */ + 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; +} + +/* timeout - handle timeouts */ +static void +timeout(int sig) +{ + longjmp(timebuf, sig); +} + +/* rfc1413 - return remote user name, given socket structures */ +char * +rfc1413(conn_rec *conn, server_rec *srv) +{ + static char user[RFC1413_USERLEN+1]; /* XXX */ + static char *result; + static int sock; + + result = FROM_UNKNOWN; + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) + { + log_unixerr("socket", NULL, "rfc1413: error creating socket", srv); + conn->remote_logname = result; + } + + /* + * Set up a timer so we won't get stuck while waiting for the server. + */ + if (setjmp(timebuf) == 0) + { + signal(SIGALRM, timeout); + alarm(rfc1413_timeout); + + if (get_rfc1413(sock, &conn->local_addr, &conn->remote_addr, user, + srv) + >= 0) + result = user; + + alarm(0); + } + close(sock); + conn->remote_logname = result; + + return conn->remote_logname; +} diff --git a/RELEASE_1_1_X/src/main/util.c b/RELEASE_1_1_X/src/main/util.c new file mode 100644 index 00000000000..84a782405cf --- /dev/null +++ b/RELEASE_1_1_X/src/main/util.c @@ -0,0 +1,1196 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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 . + * + */ + + +/* + * str.c: string utility things + * + * 3/21/93 Rob McCool + * + */ + + +#include "httpd.h" +#include "http_conf_globals.h" /* for user_id & group_id */ +#ifdef QNX +#include +#endif + +#ifdef NOTDEF +extern char** environ; + +/* taken from bdflush-1.5 for Linux source code */ +void inststr(char *dst[], int argc, char *src) +{ + if (strlen(src) <= strlen(dst[0])) + { + char *ptr; + + for (ptr = dst[0]; *ptr; *(ptr++) = '\0'); + + strcpy(dst[0], src); + } else + { + /* stolen from the source to perl 4.036 (assigning to $0) */ + char *ptr, *ptr2; + int count; + ptr = dst[0] + strlen(dst[0]); + for (count = 1; count < argc; count++) { + if (dst[count] == ptr + 1) + ptr += strlen(++ptr); + } + if (environ[0] == ptr + 1) { + for (count = 0; environ[count]; count++) + if (environ[count] == ptr + 1) + ptr += strlen(++ptr); + } + count = 0; + for (ptr2 = dst[0]; ptr2 <= ptr; ptr2++) { + *ptr2 = '\0'; + count++; + } + strncpy(dst[0], src, count); + } +} +#endif + +char *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); +} + +char *ht_time(pool *p, time_t t, char *fmt, int gmt) { + char ts[MAX_STRING_LEN]; + struct tm *tms; + + tms = (gmt ? gmtime(&t) : localtime(&t)); + + /* check return code? */ + strftime(ts,MAX_STRING_LEN,fmt,tms); + return pstrdup (p, ts); +} + +char *gm_timestr_822(pool *p, time_t sec) { + static const char *const days[7]= + {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + char ts[50]; + struct tm *tms; + + tms = gmtime(&sec); + +/* RFC date format; as strftime '%a, %d %b %Y %T GMT' */ + 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); + + return pstrdup (p, ts); +} + +/* What a pain in the ass. */ +struct tm *get_gmtoff(long *tz) { + time_t tt; + struct tm *t; + + tt = time(NULL); + t = localtime(&tt); +#if defined(HAS_GMTOFF) + *tz = t->tm_gmtoff; +#elif !defined(NO_TIMEZONE) + *tz = - timezone; + if(t->tm_isdst) + *tz += 3600; +#else + { + static struct tm loc_t; + + loc_t = *t; /* save it */ + t = gmtime(&tt); + *tz = mktime(&loc_t) - mktime(t); + t = &loc_t; /* return pointer to saved time */ + } +#endif + return t; +} + + +/* 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? + */ +int strcmp_match(char *str, 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 = 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'); +} + +int strcasecmp_match(char *str, 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 = strcasecmp_match(&str[x++],&exp[y])) != 1) + return ret; + } + return -1; + } else + if((exp[y] != '?') && (tolower(str[x]) != tolower(exp[y]))) + return 1; + } + return (str[x] != '\0'); +} + +int is_matchexp(char *str) { + register int x; + + for(x=0;str[x];x++) + if((str[x] == '*') || (str[x] == '?')) + return 1; + return 0; +} + +/* + * Parse .. so we don't compromise security + */ +void 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'; + } +} + +void no2slash(char *name) { + register int x,y; + + for(x=0; name[x];) + if(x && (name[x-1] == '/') && (name[x] == '/')) + for(y=x+1;name[y-1];y++) + name[y-1] = name[y]; + else x++; +} + +char *make_dirstr(pool *p, 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 = palloc(p, x + 2); + strncpy (res, s, x); + res[x] = '/'; + res[x+1] = '\0'; + return res; + } + } + + if (s[strlen(s) - 1] == '/') + return pstrdup (p, s); + else + return pstrcat (p, s, "/", NULL); +} + +int count_dirs(char *path) { + register int x,n; + + for(x=0,n=0;path[x];x++) + if(path[x] == '/') n++; + return n; +} + + +void chdir_file(char *file) { + int i; + + if((i = rind(file,'/')) == -1) + return; + file[i] = '\0'; + chdir(file); + file[i] = '/'; +} + +char *getword(pool* atrans, char **line, char stop) { + int pos = ind(*line, stop); + char *res; + + if (pos == -1) { + res = pstrdup (atrans, *line); + *line += strlen (*line); + return res; + } + + res = palloc(atrans, pos + 1); + strncpy (res, *line, pos); + res[pos] = '\0'; + + while ((*line)[pos] == stop) ++pos; + + *line += pos; + + return res; +} +char *getword_nulls(pool* atrans, char **line, char stop) { + int pos = ind(*line, stop); + char *res; + + if (pos == -1) { + res = pstrdup (atrans, *line); + *line += strlen (*line); + return res; + } + + res = palloc(atrans, pos + 1); + strncpy (res, *line, pos); + res[pos] = '\0'; + + ++pos; + + *line += pos; + + return res; +} + +/* Get a word, (new) config-file style --- quoted strings and backslashes + * all honored + */ + +char *substring_conf (pool *p, char *start, int len) +{ + char *result = palloc (p, len + 2); + char *resp = result; + int i; + + for (i = 0; i < len; ++i) { + if (start[i] == '\\') + *resp++ = start[++i]; + else + *resp++ = start[i]; + } + + *resp++ = '\0'; + return result; +} + +char *getword_conf(pool* p, char **line) { + char *str = *line, *strend, *res; + char quote; + + while (*str && isspace (*str)) + ++str; + + if (!*str) { + *line = str; + return ""; + } + + if ((quote = *str) == '"' || quote == '\'') { + strend = str + 1; + while (*strend && *strend != quote) { + if (*strend == '\\' && strend[1]) strend += 2; + else ++strend; + } + res = substring_conf (p, str + 1, strend - str - 1); + + if (*strend == quote) ++strend; + } else { + strend = str; + while (*strend && !isspace (*strend)) + if (*strend == '\\' && strend[1]) strend += 2; + else ++strend; + + res = substring_conf (p, str, strend - str); + } + + while (*strend && isspace(*strend)) ++ strend; + *line = strend; + return res; +} + +void cfg_getword(char *word, char *line) { + int x=0,y; + + for(x=0;line[x] && isspace(line[x]);x++); + y=0; + while(1) { + if(!(word[y] = line[x])) + break; + if(isspace(line[x])) + if((!x) || (line[x-1] != '\\')) + break; + if(line[x] != '\\') ++y; + ++x; + } + word[y] = '\0'; + while(line[x] && isspace(line[x])) ++x; + for(y=0;(line[y] = line[x]);++x,++y); +} + +int +cfg_getline(char *s, int n, FILE *f) { + register int i=0, c; + + s[0] = '\0'; + /* skip leading whitespace */ + do { + c = getc(f); + } while (c == '\t' || c == ' '); + + while(1) { + if((c == '\t') || (c == ' ')) { + s[i++] = ' '; + while((c == '\t') || (c == ' ')) + c = getc(f); + } + if(c == CR) { + c = getc(f); + } + if(c == EOF || c == 0x4 || c == LF || i == (n-1)) { + /* blast trailing whitespace */ + while(i && (s[i-1] == ' ')) --i; + s[i] = '\0'; + return (feof(f) ? 1 : 0); + } + s[i] = c; + ++i; + c = getc(f); + } +} + +/* 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. + */ + +char *get_token (pool *p, char **accept_line, int accept_white) +{ + char *ptr = *accept_line; + char *tok_start; + char *token; + int tok_len; + + /* Find first non-white byte */ + + while (*ptr && isspace(*ptr)) + ++ptr; + + tok_start = ptr; + + /* find token end, skipping over quoted strings. + * (comments are already gone). + */ + + while (*ptr && (accept_white || !isspace(*ptr)) + && *ptr != ';' && *ptr != ',') + { + if (*ptr++ == '"') + while (*ptr) + if (*ptr++ == '"') break; + } + + tok_len = ptr - tok_start; + token = palloc (p, tok_len + 1); + strncpy (token, tok_start, tok_len); + token[tok_len] = '\0'; + + /* Advance accept_line pointer to the next non-white byte */ + + while (*ptr && isspace(*ptr)) + ++ptr; + + *accept_line = ptr; + return token; +} + +char *escape_shell_cmd(pool *p, char *s) { + register int x,y,l; + char *cmd; + + l=strlen(s); + cmd = palloc (p, 2 * l + 1); /* Be safe */ + strcpy (cmd, s); + + for(x=0;cmd[x];x++) { + +#ifdef __EMX__ + /* Don't allow '&' in parameters under OS/2. */ + /* This can be used to send commands to the shell. */ + if (cmd[x] == '&') { + cmd[x] = ' '; + } +#endif + + if(ind("&;`'\"|*?~<>^()[]{}$\\\n",cmd[x]) != -1){ + for(y=l+1;y>x;y--) + cmd[y] = cmd[y-1]; + l++; /* length has been increased */ + cmd[x] = '\\'; + x++; /* skip the character */ + } + } + + return cmd; +} + +void plustospace(char *str) { + register int x; + + for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' '; +} + +void spacetoplus(char *str) { + register int x; + + for(x=0;str[x];x++) if(str[x] == ' ') str[x] = '+'; +} + +char x2c(char *what) { + register char digit; + + 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')); + 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 + */ +int +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 (!isxdigit(url[y+1]) || !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; +} + +char *construct_url(pool *p, char *uri, server_rec *s) { + char portnum[10]; /* Long enough. Really! */ + + if (s->port == 80) { + return pstrcat (p, "http://", s->server_hostname, uri, NULL); + } else { + sprintf (portnum, "%d", s->port); + return pstrcat (p, "http://", s->server_hostname, ":", portnum, uri, + NULL); + } +} + +#define c2x(what,where) sprintf(where,"%%%02x",what) + +/* +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 "./"). +*/ + +char *escape_path_segment(pool *p, const char *segment) { + register int x,y; + char *copy = palloc (p, 3 * strlen (segment) + 1); + + for(x=0,y=0; segment[x]; x++,y++) { + char c=segment[x]; + if((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c >'9') + && ind("$-_.+!*'(),:@&=~",c) == -1) + { + c2x(c,©[y]); + y+=2; + } + else + copy[y]=c; + } + copy[y] = '\0'; + return copy; +} + +char *os_escape_path(pool *p,const char *path,int partial) { + char *copy=palloc(p,3*strlen(path)+3); + char *s=copy; + + if(!partial) + { + int colon=ind(path,':'); + int slash=ind(path,'/'); + + if(colon >= 0 && (colon < slash || slash < 0)) + { + *s++='.'; + *s++='/'; + } + } + for( ; *path ; ++path) + { + char c=*path; + if((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c >'9') + && ind("$-_.+!*'(),:@&=/~",c) == -1) + { + c2x(c,s); + s+=3; + } + else + *s++=c; + } + *s='\0'; + return copy; +} + +char *escape_uri(pool *p, char *uri) { + register int x,y; + char *copy = palloc (p, 3 * strlen (uri) + 1); + + for(x=0,y=0; uri[x]; x++,y++) { + if (ind (":% ?+&",(copy[y] = uri[x])) != -1) { + c2x(uri[x],©[y]); + y+=2; + } + } + copy[y] = '\0'; + return copy; +} + +char * +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 pstrdup(p, s); + x = palloc(p, i + j + 1); + for (i=0, j=0; s[i] != '\0'; i++, j++) + if (s[i] == '<') + { + memcpy(&x[j], "<", 4); + j += 3; + } else if (s[i] == '>') + { + memcpy(&x[j], ">", 4); + j += 3; + } else if (s[i] == '&') + { + memcpy(&x[j], "&", 5); + j += 4; + } else + x[j] = s[i]; + + x[j] = '\0'; + return x; +} + +#ifdef NOTDEF + +void escape_url(char *url) { + register int x,y; + register char digit; + char *copy; + + copy = strdup(url); + + for(x=0,y=0;copy[x];x++,y++) { + if(ind("% ?+&",url[y] = copy[x]) != -1) { + c2x(copy[x],&url[y]); + y+=2; + } + } + url[y] = '\0'; + free(copy); +} + +#endif + +int is_directory(char *path) { + struct stat finfo; + + if(stat(path,&finfo) == -1) + return 0; /* in error condition, just return no */ + + return(S_ISDIR(finfo.st_mode)); +} + +char *make_full_path(pool *a, char *src1,char *src2) { + register int x; + + x = strlen(src1); + if (x == 0) return pstrcat (a, "/", src2, NULL); + + if (src1[x - 1] != '/') return pstrcat (a, src1, "/", src2, NULL); + else return pstrcat (a, src1, src2, NULL); +} + +int is_url(char *u) { + register int x; + + for(x=0;u[x] != ':';x++) + if((!u[x]) || (!isalpha(u[x]))) + return 0; + + if((u[x+1] == '/') && (u[x+2] == '/')) + return 1; + else return 0; +} + +int can_exec(struct stat *finfo) { +#ifdef __EMX__ + /* OS/2 dosen't have Users and Groups */ + return (finfo->st_mode & S_IEXEC); +#else + if(user_id == finfo->st_uid) + if(finfo->st_mode & S_IXUSR) + return 1; + if(group_id == finfo->st_gid) + if(finfo->st_mode & S_IXGRP) + return 1; + return (finfo->st_mode & S_IXOTH); +#endif +} + +#ifdef NEED_STRDUP +char *strdup (char *str) +{ + char *dup; + + if(!(dup = (char *)malloc (strlen (str) + 1))) + return NULL; + dup = strcpy (dup, str); + + return dup; +} +#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 = tolower(*p) - 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 = tolower(*p) - tolower(*q); + if (diff) return diff; + } + /*NOTREACHED*/ +} +#endif + + + +#ifdef NEED_INITGROUPS +int initgroups(const char *name, gid_t basegid) +{ +#ifdef QNX +/* QNX does not appear to support supplementary groups. +Ben */ + 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 */ +/* httpd does not use the options so this doesn't implement them */ +int waitpid(pid_t pid, int *statusp, int options) +{ + int tmp_pid; + if ( kill ( pid,0 ) == -1) { + errno=ECHILD; + return -1; + } + while ((( tmp_pid = wait(statusp)) != pid) && ( tmp_pid != -1 )); + return tmp_pid; +} +#endif + +int ind(const char *s, char c) { + register int x; + + for(x=0;s[x];x++) + if(s[x] == c) return x; + + return -1; +} + +int 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; +} + +void str_tolower(char *str) { + while(*str) { + *str = tolower(*str); + ++str; + } +} + +uid_t uname2id(char *name) { + struct passwd *ent; + + if(name[0] == '#') + return(atoi(&name[1])); + + if(!(ent = getpwnam(name))) { + fprintf(stderr,"httpd: bad user name %s\n",name); + exit(1); + } + else return(ent->pw_uid); +} + +gid_t gname2id(char *name) { + struct group *ent; + + if(name[0] == '#') + return(atoi(&name[1])); + + if(!(ent = getgrnam(name))) { + fprintf(stderr,"httpd: bad group name %s\n",name); + exit(1); + } + else return(ent->gr_gid); +} + +#if 0 +int get_portnum(int sd) { + struct sockaddr addr; + int len; + + len = sizeof(struct sockaddr); + if(getsockname(sd,&addr,&len) < 0) + return -1; + return ntohs(((struct sockaddr_in *)&addr)->sin_port); +} + +struct in_addr get_local_addr(int sd) { + struct sockaddr addr; + int len; + + len = sizeof(struct sockaddr); + if(getsockname(sd,&addr,&len) < 0) { + fprintf (stderr, "Can't get local host address!\n"); + perror ("getsockname"); + exit(1); + } + + return ((struct sockaddr_in *)&addr)->sin_addr; +} +#endif + +/* + * Parses a host of the form

[:port] + * :port is permitted if 'port' is not NULL + */ +unsigned long get_virthost_addr (char *w, short int *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); + } + +#ifdef DGUX + my_addr = inet_network(w); +#else + my_addr = inet_addr(w); +#endif + if (my_addr != ((unsigned long) 0xffffffff)) + { + 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; +} + + +#ifdef NOTDEF + +char *get_remote_logname(FILE *fd) { + int len; + char *result; +#if defined(NEXT) || defined(BSD4_4) || defined(SOLARIS2) || defined(LINUX) || defined(__EMX__) + struct sockaddr sa_server, sa_client; +#else + struct sockaddr_in sa_server,sa_client; +#endif + + len = sizeof(sa_client); + if(getpeername(fileno(stdout),&sa_client,&len) != -1) { + len = sizeof(sa_server); + if(getsockname(fileno(stdout),&sa_server,&len) == -1) + result = "unknown"; + else + result = rfc931((struct sockaddr_in *) & sa_client, + (struct sockaddr_in *) & sa_server); + } + else result = "unknown"; + + return result; /* robm=pinhead */ +} +#endif + +static char *find_fqdn(pool *a, struct hostent *p) { + int x; + + if(ind(p->h_name,'.') == -1) { + for(x=0;p->h_aliases[x];++x) { + if((ind(p->h_aliases[x],'.') != -1) && + (!strncmp(p->h_aliases[x],p->h_name,strlen(p->h_name)))) + return pstrdup(a, p->h_aliases[x]); + } + return NULL; + } else return pstrdup(a, (void *)p->h_name); +} + +char *get_local_host(pool *a) +{ + char str[128]; + int len = 128; + char *server_hostname; + + struct hostent *p; + gethostname(str, len); + if((!(p=gethostbyname(str))) || (!(server_hostname = find_fqdn(a, p)))) { + fprintf(stderr,"httpd: cannot determine local host name.\n"); + fprintf(stderr,"Use ServerName to set it manually.\n"); + exit(1); + } + + return server_hostname; +} + +/* aaaack but it's fast and const should make it shared text page. */ +const int pr2six[256]={ + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63, + 52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9, + 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27, + 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64 +}; + +char *uudecode(pool *p, char *bufcoded) { + int nbytesdecoded; + register unsigned char *bufin; + register char *bufplain; + register unsigned char *bufout; + register int nprbytes; + + /* Strip leading whitespace. */ + + while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++; + + /* Figure out how many characters are in the input buffer. + * Allocate this many from the per-transaction pool for the result. + */ + bufin = (unsigned char *)bufcoded; + while(pr2six[*(bufin++)] <= 63); + nprbytes = (char *)bufin - bufcoded - 1; + nbytesdecoded = ((nprbytes+3)/4) * 3; + + bufplain = palloc(p, nbytesdecoded + 1); + bufout = (unsigned char *)bufplain; + + bufin = (unsigned char *)bufcoded; + + while (nprbytes > 0) { + *(bufout++) = + (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); + *(bufout++) = + (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); + *(bufout++) = + (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); + bufin += 4; + nprbytes -= 4; + } + + if(nprbytes & 03) { + if(pr2six[bufin[-2]] > 63) + nbytesdecoded -= 2; + else + nbytesdecoded -= 1; + } + bufplain[nbytesdecoded] = '\0'; + return bufplain; +} + +#ifdef __EMX__ +void os2pathname(char *path) { + char newpath[MAX_STRING_LEN]; + int loop; + int offset; + + offset = 0; + for (loop=0; loop < (strlen(path) + 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); +}; +#endif + + +#ifdef NEED_STRERROR +char * +strerror (int err) { + + char *p; + extern char *const sys_errlist[]; + + p = sys_errlist[err]; + return (p); +} +#endif diff --git a/RELEASE_1_1_X/src/main/util_md5.c b/RELEASE_1_1_X/src/main/util_md5.c new file mode 100644 index 00000000000..4d8fd1cf26f --- /dev/null +++ b/RELEASE_1_1_X/src/main/util_md5.c @@ -0,0 +1,193 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/************************************************************************ + * NCSA HTTPd Server + * Software Development Group + * National Center for Supercomputing Applications + * University of Illinois at Urbana-Champaign + * 605 E. Springfield, Champaign, IL 61820 + * httpd@ncsa.uiuc.edu + * + * Copyright (C) 1995, Board of Trustees of the University of Illinois + * + ************************************************************************ + * + * md5.c: NCSA HTTPd code which uses the md5c.c RSA Code + * + * Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc. + * Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon + * University (see Copyright below). + * Portions of Content-MD5 code Copyright (C) 1991 Bell Communications + * Research, Inc. (Bellcore) (see Copyright below). + * Portions extracted from mpack, John G. Myers - jgm+@cmu.edu + * Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk) + * + */ + + + +/* md5.c --Module Interface to MD5. */ +/* Jeff Hostetler, Spyglass, Inc., 1994. */ + +#include "httpd.h" +#include "util_md5.h" + +char *md5 (pool *p, unsigned char *string) +{ + MD5_CTX my_md5; + unsigned char hash[16]; + char *r, result[33]; + int i; + + /* + * Take the MD5 hash of the string argument. + */ + + MD5Init(&my_md5); + MD5Update(&my_md5, string, strlen((const char *)string)); + MD5Final(hash, &my_md5); + + for (i=0, r=result; i<16; i++, r+=2) + sprintf(r, "%02x", hash[i]); + *r = '\0'; + + return pstrdup(p, result); +} + +/* 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+/"; + +char *md5contextTo64(pool *a, MD5_CTX *context) +{ + unsigned char digest[18]; + char *encodedDigest; + int i; + char *p; + + encodedDigest = (char *)pcalloc(a, 25 * sizeof(char)); + + 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; +} + +char *md5digest(pool *p, FILE *infile) +{ + MD5_CTX context; + unsigned char buf[1000]; + long length = 0; + int nbytes; + + MD5Init(&context); + while ((nbytes = fread(buf, 1, sizeof(buf), infile))) { + length += nbytes; + MD5Update(&context, buf, nbytes); + } + rewind(infile); + return md5contextTo64(p, &context); +} + diff --git a/RELEASE_1_1_X/src/main/util_script.c b/RELEASE_1_1_X/src/main/util_script.c new file mode 100644 index 00000000000..05b33105562 --- /dev/null +++ b/RELEASE_1_1_X/src/main/util_script.c @@ -0,0 +1,364 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + + +#include "httpd.h" +#include "http_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" + +/* + * 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 + +char **create_argv(pool *p, char *av0, char *args) { + register int x,n; + char **av; + char *w; + + for(x=0,n=2;args[x];x++) + if(args[x] == '+') ++n; + + av = (char **)palloc(p, (n+1)*sizeof(char *)); + av[0] = av0; + + for(x=1;xelts; + char **env = (char **)palloc (p, (env_arr->nelts + 2) *sizeof (char *)); + int i, j; + char *tz; + + j = 0; + tz = getenv("TZ"); + if (tz!= NULL) env[j++] = pstrcat(p, "TZ=", tz, NULL); + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) continue; + env[j++] = pstrcat (p, elts[i].key, "=", elts[i].val, NULL); + } + + env[j] = NULL; + return env; +} + +void add_common_vars(request_rec *r) +{ + table *e = r->subprocess_env; + server_rec *s = r->server; + conn_rec *c = r->connection; + const char *rem_logname; + + char port[40],*env_path; + + array_header *hdrs_arr = table_elts (r->headers_in); + table_entry *hdrs = (table_entry *)hdrs_arr->elts; + int i; + + /* 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")) + table_set (e, "CONTENT_TYPE", hdrs[i].val); + else if (!strcasecmp (hdrs[i].key, "Content-length")) + table_set (e, "CONTENT_LENGTH", hdrs[i].val); + else if (!strcasecmp (hdrs[i].key, "Authorization")) + continue; + else + table_set (e, http2env (r->pool, hdrs[i].key), hdrs[i].val); + } + + sprintf(port, "%d", s->port); + + if(!(env_path = getenv("PATH"))) + env_path=DEFAULT_PATH; + + table_set (e, "PATH", env_path); + table_set (e, "SERVER_SOFTWARE", SERVER_VERSION); + table_set (e, "SERVER_NAME", s->server_hostname); + table_set (e, "SERVER_PORT", port); + table_set (e, "REMOTE_HOST", + get_remote_host(c, r->per_dir_config, REMOTE_NAME)); + table_set (e, "REMOTE_ADDR", c->remote_ip); + table_set (e, "DOCUMENT_ROOT", document_root(r)); /* Apache */ + table_set (e, "SERVER_ADMIN", s->server_admin); /* Apache */ + table_set (e, "SCRIPT_FILENAME", r->filename); /* Shambhala */ + + if (c->user) table_set(e, "REMOTE_USER", c->user); + if (c->auth_type) table_set(e, "AUTH_TYPE", c->auth_type); + rem_logname = get_remote_logname(r); + if (rem_logname) table_set(e, "REMOTE_IDENT", rem_logname); + + /* Apache custom error responses. If we have redirected set two new vars */ + + if (r->prev) { + if (r->prev->args) table_set(e,"REDIRECT_QUERY_STRING", r->prev->args); + if (r->prev->uri) table_set (e, "REDIRECT_URL", r->prev->uri); + } +} + + +void add_cgi_vars(request_rec *r) +{ + table *e = r->subprocess_env; + + table_set (e, "GATEWAY_INTERFACE","CGI/1.1"); + table_set (e, "SERVER_PROTOCOL", r->protocol); + table_set (e, "REQUEST_METHOD", r->method); + table_set (e, "QUERY_STRING", r->args ? r->args : ""); + + /* 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 (!r->path_info || !*r->path_info || !strcmp (r->protocol, "INCLUDED")) { + table_set (e, "SCRIPT_NAME", r->uri); + } else { + int path_info_start = strlen (r->uri) - strlen (r->path_info); + + r->uri[path_info_start] = '\0'; + table_set (e, "SCRIPT_NAME", r->uri); + r->uri[path_info_start] = '/'; + } + + 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 = sub_req_lookup_uri( + escape_uri(r->pool, r->path_info), r); + + table_set (e, "PATH_INFO", r->path_info); + + /* Don't bother destroying pa_req --- it's only created in + * child processes which are about to jettison their address + * space anyway. BTW, we concatenate filename and path_info + * from the sub_request to be compatible in case the PATH_INFO + * is pointing to an object which doesn't exist. + */ + + if (pa_req->filename) + table_set (e, "PATH_TRANSLATED", + pstrcat (r->pool, pa_req->filename, pa_req->path_info, + NULL)); + } +} + +int scan_script_header(request_rec *r, FILE *f) +{ + char w[MAX_STRING_LEN]; + char *l; + int p; + + hard_timeout ("read script header", r); + + while(1) { + + if (fgets(w, MAX_STRING_LEN-1, f) == NULL) { + log_reason ("Premature end of script headers", r->filename, r); + return 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(w[0] == '\0') { + kill_timeout (r); + return OK; + } + + /* if we see a bogus header don't ignore it. Shout and scream */ + + 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); + /* Soak up all the script output --- may save an outright kill */ + while (fgets(w, MAX_STRING_LEN-1, f) != NULL) + continue; + + kill_timeout (r); + log_reason (malformed, r->filename, r); + return SERVER_ERROR; + } + + *l++ = '\0'; + while (*l && isspace (*l)) ++l; + + if(!strcasecmp(w,"Content-type")) { + + /* Nuke trailing whitespace */ + + char *endp = l + strlen(l) - 1; + while (endp > l && isspace(*endp)) *endp-- = '\0'; + + r->content_type = pstrdup (r->pool, l); + } + else if(!strcasecmp(w,"Status")) { + sscanf(l, "%d", &r->status); + r->status_line = pstrdup(r->pool, l); + } + else if(!strcasecmp(w,"Location")) { + table_set (r->headers_out, w, l); + } + +/* 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. + */ + else if(!strcasecmp(w, "Set-Cookie")) { + table_add(r->err_headers_out, w, l); + } + else { + table_merge (r->err_headers_out, w, l); + } + } +} + +void send_size(size_t size, request_rec *r) { + char ss[20]; + + if(size == -1) + strcpy(ss, " -"); + else if(!size) + strcpy(ss, " 0k"); + else if(size < 1024) + strcpy(ss, " 1k"); + else if(size < 1048576) + sprintf(ss, "%4dk", size / 1024); + else + sprintf(ss, "%4dM", size / 1048576); + rputs(ss, r); +} + +#ifdef __EMX__ +char **create_argv_cmd(pool *p, char *av0, 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 **)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. + * + */ + + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" + +/* Why are you even thinking about using this? */ + +int ai_backcompat_kludge (request_rec *r) +{ + if (r->method_number != M_GET || + (r->args && r->args[0]) || (r->path_info && r->path_info[0])) + { + char *doit_filename = pstrcat (r->pool, r->filename, ".doit", NULL); + struct stat finfo; + + if (stat (doit_filename, &finfo) >= 0) { + r->filename = doit_filename; + r->finfo = finfo; + } + } + + return DECLINED; +} + +module ai_backcompat_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + NULL, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + ai_backcompat_kludge, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/mod_cookies.c b/RELEASE_1_1_X/src/mod_cookies.c new file mode 100644 index 00000000000..241c10b800b --- /dev/null +++ b/RELEASE_1_1_X/src/mod_cookies.c @@ -0,0 +1,299 @@ + +/* ==================================================================== + * Copyright (c) 1995, 1996 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. 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 . + * + */ + + +/* User Tracking Module + * + * This Apache module is designed to track users paths through a site. + * It uses the client-side state ("Cookie") protocol developed by Netscape. + * It is known to work on Netscape browsers, Microsoft Internet + * Explorer and others currently being developed. + * + * Each time a page is requested we look to see if the browser is sending + * us a Cookie: header that we previously generated. + * + * If we don't find one then the user hasn't been to this site since + * starting their browser or their browser doesn't support cookies. So + * we generate a unique Cookie for the transaction and send it back to + * the browser (via a "Set-Cookie" header) + * Future requests from the same browser should keep the same Cookie line. + * + * The cookie and request are logged to a file. Use the directive + * "CookieLog somefilename" in one of the config files to enable the Cookie + * module. By matching up all the requests with the same cookie you can + * work out exactly what path a user took through your site. + * + * Notes: + * 1. This code doesn't log the initial transaction (the one that created + * the cookie to start with). If it did then the cookie log file would + * be bigger than a normal access log. + * 2. This module has been designed to not interfere with other Cookies + * your site may be using; just avoid sending out cookies with + * the name "Apache=" or things will get confused. + * 3. If you want you can modify the Set-Cookie line so that the Cookie + * never expires. You would then get the same Cookie each time the + * user revisits your site. + * + * Mark Cox, mark@ukweb.com, http://www.ukweb.com/~mark/, 6 July 95 + * + * 6.12.95 MJC Now be more friendly. Allow our cookies to overlap with + * others the site may be using. Use a more descriptive + * cookie name. + * + * 18.3.96 MJC Generate cookies for EVERY request no matter what the + * browser. We never know when a new browser writer will + * add cookie support. + * + * 96/03/31 -JimC Allow the log to be sent to a pipe. Copies the relevant + * code from mod_log_agent.c. + * + * 24.5.96 MJC Improved documentation after receiving comments from users + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include + +module cookies_module; + +typedef struct { + char *fname; + int log_fd; + int always; +} cookie_log_state; + +/* Make Cookie: Now we have to generate something that is going to be + * pretty unique. We can base it on the pid, time, hostip */ + +#define COOKIE_NAME "Apache=" + +void make_cookie(request_rec *r) +{ + struct timeval tv; + char new_cookie[100]; /* blurgh */ + char *dot; + const char *rname = pstrdup(r->pool, + get_remote_host(r->connection, r->per_dir_config, + REMOTE_NAME)); + + struct timezone tz = { 0 , 0 }; + + if ((dot = strchr(rname,'.'))) *dot='\0'; /* First bit of hostname */ + gettimeofday(&tv, &tz); + sprintf(new_cookie,"%s%s%d%ld%d; path=/", + COOKIE_NAME, rname, + (int)getpid(), + (long)tv.tv_sec, (int)tv.tv_usec/1000 ); + + table_set(r->headers_out,"Set-Cookie",new_cookie); + return; +} + +int spot_cookie(request_rec *r) +{ + char *cookie; + + if ((cookie = table_get (r->headers_in, "Cookie"))) + if (strstr(cookie,COOKIE_NAME)) + return DECLINED; /* Theres already a cookie, no new one */ + make_cookie(r); + return OK; /* We set our cookie */ +} + +static int cookie_flags = ( O_WRONLY | O_APPEND | O_CREAT ); + +#ifdef __EMX__ +/* OS/2 lacks support for users and groups */ +static mode_t cookie_mode = ( S_IREAD | S_IWRITE ); +#else +static mode_t cookie_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); +#endif + +void *make_cookie_log_state (pool *p, server_rec *s) +{ + cookie_log_state *cls = + (cookie_log_state *)palloc (p, sizeof (cookie_log_state)); + + cls->fname = ""; + cls->log_fd = -1; + + return (void *)cls; +} + +char *set_cookie_log (cmd_parms *parms, void *dummy, char *arg) +{ + cookie_log_state *cls = get_module_config (parms->server->module_config, + &cookies_module); + cls->fname = arg; + return NULL; +} + +command_rec cookie_log_cmds[] = { +{ "CookieLog", set_cookie_log, NULL, RSRC_CONF, TAKE1, + "the filename of the cookie log" }, +{ NULL } +}; + +void cookie_log_child (void *cmd) +{ + /* Child process code for 'CookieLog "|..."'; + * may want a common framework for this, since I expect it will + * be common for other foo-loggers to want this sort of thing... + */ + + cleanup_for_exec(); + signal (SIGHUP, SIG_IGN); + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); + exit (1); +} + +void open_cookie_log (server_rec *s, pool *p) +{ + cookie_log_state *cls = get_module_config (s->module_config, + &cookies_module); + char *fname = server_root_relative (p, cls->fname); + + if (cls->log_fd > 0) return; + + if (*cls->fname == '|') { + FILE *dummy; + + spawn_child(p, cookie_log_child, (void *)(cls->fname+1), + kill_after_timeout, &dummy, NULL); + + if (dummy == NULL) { + fprintf (stderr, "Couldn't fork child for CookieLog process\n"); + exit (1); + } + + cls->log_fd = fileno (dummy); + } + else if(*cls->fname != '\0') { + if((cls->log_fd = popenf(p, fname, cookie_flags, cookie_mode)) < 0) { + fprintf(stderr, "httpd: could not open cookie log file %s.\n", fname); + perror("open"); + exit(1); + } + } +} + +void init_cookie_log (server_rec *s, pool *p) +{ + for (; s; s = s->next) open_cookie_log (s, p); +} + +int cookie_log_transaction(request_rec *orig) +{ + cookie_log_state *cls = get_module_config (orig->server->module_config, + &cookies_module); + char *str; + long timz; + struct tm *t; + char tstr[MAX_STRING_LEN],sign; + request_rec *r; + char *cookie,*cookiebuf,*cookieend; + char *value; + + for (r = orig; r->next; r = r->next) + continue; + if (*cls->fname == '\0') /* Don't log cookies */ + return DECLINED; + + if (!(cookie = table_get (r->headers_in, "Cookie"))) + return DECLINED; /* Theres no cookie, don't bother logging */ + value=strstr(cookie,COOKIE_NAME); + if (!value) /* Only log cookies we generated! */ + return DECLINED; + value+=strlen(COOKIE_NAME); + cookiebuf=pstrdup( r->pool, value ); + cookieend=strchr(cookiebuf,';'); + if (cookieend) *cookieend='\0'; /* Ignore anything after a ; */ + + t = get_gmtoff(&timz); + sign = (timz < 0 ? '-' : '+'); + if(timz < 0) + timz = -timz; + + strftime(tstr,MAX_STRING_LEN,"\" [%d/%b/%Y:%H:%M:%S ",t); + if (r->status != -1) + sprintf(&tstr[strlen(tstr)], "%c%02ld%02ld] %d\n", sign, timz/3600, + timz%3600, r->status); + sprintf(&tstr[strlen(tstr)], "%c%02ld%02ld] -\n", sign, timz/3600, + timz%3600); + + str = pstrcat(orig->pool, cookiebuf, " \"", orig->the_request, tstr, NULL); + + write(cls->log_fd, str, strlen(str)); + + return OK; +} + + +module cookies_module = { + STANDARD_MODULE_STUFF, + init_cookie_log, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + make_cookie_log_state, /* server config */ + NULL, /* merge server configs */ + cookie_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + spot_cookie, /* fixups */ + cookie_log_transaction, /* logger */ +}; diff --git a/RELEASE_1_1_X/src/mod_log_common.c b/RELEASE_1_1_X/src/mod_log_common.c new file mode 100644 index 00000000000..a18d97674a0 --- /dev/null +++ b/RELEASE_1_1_X/src/mod_log_common.c @@ -0,0 +1,220 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + + +#include "httpd.h" +#include "http_core.h" +#include "http_config.h" + +module common_log_module; + +static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT ); + +#ifdef __EMX__ +/* OS/2 lacks support for users and groups */ +static mode_t xfer_mode = ( S_IREAD | S_IWRITE ); +#else +static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); +#endif + +typedef struct { + char *fname; + int log_fd; +} common_log_state; + +void *make_common_log_state (pool *p, server_rec *s) +{ + common_log_state *cls = + (common_log_state *)palloc (p, sizeof (common_log_state)); + + cls->fname = DEFAULT_XFERLOG; + cls->log_fd = -1; + + return (void *)cls; +} + +char *set_common_log (cmd_parms *parms, void *dummy, char *arg) +{ + common_log_state *cls = get_module_config (parms->server->module_config, + &common_log_module); + + cls->fname = arg; + return NULL; +} + +command_rec common_log_cmds[] = { +{ "TransferLog", set_common_log, NULL, RSRC_CONF, TAKE1, + "the filename of the access log" }, +{ NULL } +}; + +void common_log_child (void *cmd) +{ + /* 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... + */ + + cleanup_for_exec(); + signal (SIGHUP, SIG_IGN); + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); + fprintf (stderr, "Exec of shell for logging failed!!!\n"); + exit (1); +} + +void open_common_log (server_rec *s, pool *p) +{ + common_log_state *cls = get_module_config (s->module_config, + &common_log_module); + + char *fname = server_root_relative (p, cls->fname); + + if (cls->log_fd > 0) return; /* virtual log shared w/main server */ + + if (*cls->fname == '|') { + FILE *dummy; + + spawn_child(p, common_log_child, (void *)(cls->fname+1), + kill_after_timeout, &dummy, NULL); + + if (dummy == NULL) { + fprintf (stderr, "Couldn't fork child for TransferLog process\n"); + exit (1); + } + + cls->log_fd = fileno (dummy); + } + else if((cls->log_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) { + fprintf(stderr,"httpd: could not open transfer log file %s.\n", fname); + perror("open"); + exit(1); + } +} + +void init_common_log (server_rec *s, pool *p) +{ + for (; s; s = s->next) open_common_log (s, p); +} + +int common_log_transaction(request_rec *orig) +{ + common_log_state *cls = get_module_config (orig->server->module_config, + &common_log_module); + + char *str; + long timz; + struct tm *t; + const char *rem_logname; + char tstr[MAX_STRING_LEN], status[MAX_STRING_LEN], sign; + conn_rec *c = orig->connection; + request_rec *r; + + /* Common log format records an unholy melange of the original request + * and whatever it was that we actually served. Just stay compatible + * here; the whole point of the module scheme is to allow people to + * create better alternatives, but screwing up is an option we wish + * to preserve... + */ + + for (r = orig; r->next; r = r->next) + continue; + + t = get_gmtoff(&timz); + sign = (timz < 0 ? '-' : '+'); + if(timz < 0) + timz = -timz; + + sprintf(tstr, " [%.2d/%s/%d:%.2d:%.2d:%.2d %c%02ld%02ld] \"", t->tm_mday, + month_snames[t->tm_mon], t->tm_year + 1900, t->tm_hour, t->tm_min, + t->tm_sec, sign, timz/3600, timz%3600); + + if (r->status != -1) sprintf(status,"%d ", r->status); + else strcpy(status, "- "); + + if (r->bytes_sent > 0) + sprintf(&status[strlen(status)], "%ld\n", r->bytes_sent); + else + strcat(status, "-\n"); + + rem_logname = get_remote_logname(r); + if (rem_logname == NULL) rem_logname = "-"; + + str = pstrcat(orig->pool, + get_remote_host(c, r->per_dir_config, REMOTE_NAME), " ", + rem_logname, " ", (c->user != NULL ? c->user : "-"), tstr, + (orig->the_request != NULL ? orig->the_request : "NULL"), + "\" ", status, NULL); + + write(cls->log_fd, str, strlen(str)); + + return OK; +} + +module common_log_module = { + STANDARD_MODULE_STUFF, + init_common_log, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + make_common_log_state, /* server config */ + NULL, /* merge server config */ + common_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + common_log_transaction /* logger */ +}; diff --git a/RELEASE_1_1_X/src/mod_proxy.c b/RELEASE_1_1_X/src/mod_proxy.c new file mode 100644 index 00000000000..9eb695c305f --- /dev/null +++ b/RELEASE_1_1_X/src/mod_proxy.c @@ -0,0 +1,3262 @@ +/* ==================================================================== + * Copyright (c) 1996 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* +Note that the Explain() stuff is not yet complete. +Also note numerous FIXMEs and CHECKMEs which should be eliminated. + +If TESTING is set, then garbage collection doesn't delete ... probably a good +idea when hacking. + +This code is still experimental! + +Things to do: + +1. Make it garbage collect in the background, not while someone is waiting for +a response! + +2. Check the logic thoroughly. + +3. Empty directories are only removed the next time round (but this does avoid +two passes). Consider doing them the first time round. + +Ben Laurie 30 Mar 96 + +More changes: + +0) tested w/SOCKS proxy for http +1) fixed IP address formation in host2addr() +2) fixed SIGALRM on big cache cleanup +3) fixed temp files #tmp not removed +4) changed PF_INET to AF_INET in socket() calls +5) installed CONNECT code from Troy Morrison for testing +6) added NoCache config directive to disallow caching for selected hosts + +Chuck Murcko 2 Jun 96 + +*/ + +#define TESTING 0 + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "http_main.h" +#include "http_protocol.h" + +#include "md5.h" + +#include + +#include "explain.h" + +DEF_Explain + +#define SEC_ONE_DAY 86400 /* one day, in seconds */ +#define SEC_ONE_HR 3600 /* one hour, in seconds */ + +#define DEFAULT_FTP_DATA_PORT 20 +#define DEFAULT_FTP_PORT 21 +#define DEFAULT_GOPHER_PORT 70 +#define DEFAULT_NNTP_PORT 119 +#define DEFAULT_WAIS_PORT 210 +#define DEFAULT_HTTPS_PORT 443 +#define DEFAULT_SNEWS_PORT 563 +#define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */ + +/* Some WWW schemes and their default ports; this is basically /etc/services */ +static struct +{ + const char *scheme; + int port; +} defports[]={ + { "ftp", DEFAULT_FTP_PORT}, + { "gopher", DEFAULT_GOPHER_PORT}, + { "http", DEFAULT_PORT}, + { "nntp", DEFAULT_NNTP_PORT}, + { "wais", DEFAULT_WAIS_PORT}, + { "https", DEFAULT_HTTPS_PORT}, + { "snews", DEFAULT_SNEWS_PORT}, + { "prospero", DEFAULT_PROSPERO_PORT}, + { NULL, -1} /* unknown port */ +}; + + +/* static information about a remote proxy */ +struct proxy_remote +{ + const char *scheme; /* the schemes handled by this proxy, or '*' */ + const char *protocol; /* the scheme used to talk to this proxy */ + const char *hostname; /* the hostname of this proxy */ + int port; /* the port for this proxy */ +}; + +struct proxy_alias { + char *real; + char *fake; +}; + +struct nocache_entry { + char *name; +}; + +#define DEFAULT_CACHE_SPACE 5 +#define DEFAULT_CACHE_MAXEXPIRE SEC_ONE_DAY +#define DEFAULT_CACHE_EXPIRE SEC_ONE_HR +#define DEFAULT_CACHE_LMFACTOR (0.1) + +/* static information about the local cache */ +struct cache_conf +{ + const char *root; /* the location of the cache directory */ + int space; /* Maximum cache size (in 1024 bytes) */ + int maxexpire; /* Maximum time to keep cached files in secs */ + int defaultexpire; /* default time to keep cached file in secs */ + double lmfactor; /* factor for estimating expires date */ + int gcinterval; /* garbage collection interval, in seconds */ + int dirlevels; /* Number of levels of subdirectories */ + int dirlength; /* Length of subdirectory names */ +}; + +typedef struct +{ + + struct cache_conf cache; /* cache configuration */ + array_header *proxies; + array_header *aliases; + array_header *nocaches; + int req; /* true if proxy requests are enabled */ +} proxy_server_conf; + +/* + * A Web proxy module. Stages: + * + * translate_name: set filename to proxy: + * type_checker: set type to PROXY_MAGIC_TYPE if filename begins proxy: + * fix_ups: convert the URL stored in the filename to the + * canonical form. + * handler: handle proxy requests + */ + +struct hdr_entry +{ + char *field; + char *value; +}; + +/* caching information about a request */ +struct cache_req +{ + request_rec *req; /* the request */ + char *url; /* the URL requested */ + char *filename; /* name of the cache file, or NULL if no cache */ + char *tempfile; /* name of the temporary file, of NULL if not caching */ + time_t ims; /* if-modified-since date of request; -1 if no header */ + BUFF *fp; /* the cache file descriptor if the file is cached + and may be returned, or NULL if the file is + not cached (or must be reloaded) */ + time_t expire; /* calculated expire date of cached entity */ + time_t lmod; /* last-modified date of cached entity */ + time_t date; /* the date the cached file was last touched */ + int version; /* update count of the file */ + unsigned int len; /* content length */ + char *protocol; /* Protocol, and major/minor number, e.g. HTTP/1.1 */ + int status; /* the status of the cached file */ + char *resp_line; /* the whole status like (protocol, code + message) */ + array_header *hdrs; /* the HTTP headers of the file */ +}; + + +extern module proxy_module; + + +static int http_canon(request_rec *r, char *url, const char *scheme, + int def_port); +static int ftp_canon(request_rec *r, char *url); + +static int http_handler(request_rec *r, struct cache_req *c, char *url, + const char *proxyhost, int proxyport); +static int ftp_handler(request_rec *r, struct cache_req *c, char *url); + +static int connect_handler(request_rec *r, struct cache_req *c, char *url); + +static BUFF *cache_error(struct cache_req *r); + +/* -------------------------------------------------------------- */ +/* Translate the URL into a 'filename' */ + +static int +alias_match(char *uri, char *alias_fakename) +{ + char *end_fakename = alias_fakename + strlen (alias_fakename); + char *aliasp = alias_fakename, *urip = uri; + + while (aliasp < end_fakename) + { + if (*aliasp == '/') + { + /* any number of '/' in the alias matches any number in + * the supplied URI, but there must be at least one... + */ + if (*urip != '/') return 0; + + while (*aliasp == '/') ++ aliasp; + while (*urip == '/') ++ urip; + } + else { + /* Other characters are compared literally */ + if (*urip++ != *aliasp++) return 0; + } + } + + /* Check last alias path component matched all the way */ + + if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') + return 0; + + /* Return number of characters from URI which matched (may be + * greater than length of alias, since we may have matched + * doubled slashes) + */ + + return urip - uri; +} + +static int +proxy_trans(request_rec *r) +{ + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + + if (r->proxyreq) + { + if (!conf->req) return DECLINED; + + r->filename = pstrcat(r->pool, "proxy:", r->uri, NULL); + r->handler = "proxy-server"; + return OK; + } else + { + int i, len; + struct proxy_alias *ent=(struct proxy_alias *)conf->aliases->elts; + + for (i=0; i < conf->aliases->nelts; i++) + { + len = alias_match(r->uri, ent[i].fake); + + if (len > 0) + { + r->filename = pstrcat(r->pool, "proxy:", ent[i].real, + r->uri + len, NULL); + r->handler = "proxy-server"; + return OK; + } + } + return DECLINED; + } +} + +/* -------------------------------------------------------------- */ +/* Fixup the filename */ + +/* + * Canonicalise the URL + */ +static int +proxy_fixup(request_rec *r) +{ + char *url, *p; + int i; + + if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; + + url = &r->filename[6]; +/* lowercase the scheme */ + p = strchr(url, ':'); + if (p == NULL || p == url) return BAD_REQUEST; + for (i=0; i != p - url; i++) url[i] = tolower(url[i]); + +/* canonicalise each specific scheme */ + if (strncmp(url, "http:", 5) == 0) + return http_canon(r, url+5, "http", DEFAULT_PORT); + else if (strncmp(url, "ftp:", 4) == 0) return ftp_canon(r, url+4); + else return OK; /* otherwise; we've done the best we can */ +} + +/* already called in the knowledge that the characters are hex digits */ +static int +hex2c(const char *x) +{ + int i, ch; + + ch = x[0]; + if (isdigit(ch)) i = ch - '0'; + else if (isupper(ch)) i = ch - ('A' - 10); + else i = ch - ('a' - 10); + i <<= 4; + + ch = x[1]; + if (isdigit(ch)) i += ch - '0'; + else if (isupper(ch)) i += ch - ('A' - 10); + else i += ch - ('a' - 10); + return i; +} + + +static void +c2hex(int ch, char *x) +{ + int i; + + x[0] = '%'; + i = (ch & 0xF0) >> 4; + if (i >= 10) x[1] = ('A' - 10) + i; + else x[1] = '0' + i; + + i = ch & 0x0F; + if (i >= 10) x[2] = ('A' - 10) + i; + else x[2] = '0' + i; +} + +/* + * canonicalise a URL-encoded string + */ + +enum enctype { enc_path, enc_search, enc_user, enc_fpath, enc_parm }; + +/* + * Decodes a '%' escaped string, and returns the number of characters + */ +static int +decodeenc(char *x) +{ + int i, j, ch; + + if (x[0] == '\0') return 0; /* special case for no characters */ + for (i=0, j=0; x[i] != '\0'; i++, j++) + { +/* decode it if not already done */ + ch = x[i]; + if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2])) + { + ch = hex2c(&x[i+1]); + i += 2; + } + x[j] = ch; + } + x[j] = '\0'; + return j; +} + + +/* + * Convert a URL-encoded string to canonical form. + * It decodes characters which need not be encoded, + * and encodes those which must be encoded, and does not touch + * those which must not be touched. + */ +static char * +canonenc(pool *p, const char *x, int len, enum enctype t, int isenc) +{ + int i, j, ispath, ch; + char *y; + const char *allowed; /* characters which should not be encoded */ + const char *reserved; /* characters which much not be en/de-coded */ + +/* N.B. in addition to :@&=, this allows ';' in an http path + * and '?' in an ftp path -- this may be revised + * + * Also, it makes a '+' character in a search string reserved, as + * it may be form-encoded. (Although RFC 1738 doesn't allow this - + * it only permits ; / ? : @ = & as reserved chars.) + */ + if (t == enc_path) allowed = "$-_.+!*'(),;:@&="; + else if (t == enc_search) allowed = "$-_.!*'(),;:@&="; + else if (t == enc_user) allowed = "$-_.+!*'(),;@&="; + else if (t == enc_fpath) allowed = "$-_.+!*'(),?:@&="; + else /* if (t == enc_parm) */ allowed = "$-_.+!*'(),?/:@&="; + + if (t == enc_path) reserved = "/"; + else if (t == enc_search) reserved = "+"; + else reserved = ""; + + y = palloc(p, 3*len+1); + ispath = (t == enc_path); + + for (i=0, j=0; i < len; i++, j++) + { +/* always handle '/' first */ + ch = x[i]; + if (ind(reserved, ch) != -1) + { + y[j] = ch; + continue; + } +/* decode it if not already done */ + if (isenc && ch == '%') + { + if (!isxdigit(x[i+1]) || !isxdigit(x[i+2])) + return NULL; + ch = hex2c(&x[i+1]); + i += 2; + if (ch != 0 && ind(reserved, ch) != -1) + { /* keep it encoded */ + c2hex(ch, &y[j]); + j += 2; + continue; + } + } +/* recode it, if necessary */ + if (!isalnum(ch) && ind(allowed, ch) == -1) + { + c2hex(ch, &y[j]); + j += 2; + } else y[j] = ch; + } + y[j] = '\0'; + return y; +} + +/* + * Parses network-location. + * urlp on input the URL; on output the path, after the leading / + * user NULL if no user/password permitted + * password holder for password + * host holder for host + * port port number; only set if one is supplied. + * + * Returns an error string. + */ +static char * +canon_netloc(pool *pool, char **const urlp, char **userp, char **passwordp, + char **hostp, int *port) +{ + int i; + char *p, *host, *url=*urlp; + + if (url[0] != '/' || url[1] != '/') return "Malformed URL"; + host = url + 2; + url = strchr(host, '/'); + if (url == NULL) + url = ""; + else + *(url++) = '\0'; /* skip seperating '/' */ + + if (userp != NULL) + { + char *user=NULL, *password = NULL; + p = strchr(host, '@'); + + if (p != NULL) + { + *p = '\0'; + user = host; + host = p + 1; + +/* find password */ + p = strchr(user, ':'); + if (p != NULL) + { + *p = '\0'; + password = canonenc(pool, p+1, strlen(p+1), enc_user, 1); + if (password == NULL) + return "Bad %-escape in URL (password)"; + } + + user = canonenc(pool, user, strlen(user), enc_user, 1); + if (user == NULL) return "Bad %-escape in URL (username)"; + } + *userp = user; + *passwordp = password; + } + + p = strchr(host, ':'); + if (p != NULL) + { + *(p++) = '\0'; + + for (i=0; p[i] != '\0'; i++) + if (!isdigit(p[i])) break; + + if (i == 0 || p[i] != '\0') + return "Bad port number in URL"; + *port = atoi(p); + if (*port > 65535) return "Port number in URL > 65535"; + } + str_tolower(host); /* DNS names are case-insensitive */ + if (*host == '\0') return "Missing host in URL"; +/* check hostname syntax */ + for (i=0; host[i] != '\0'; i++) + if (!isdigit(host[i]) && host[i] != '.') + break; + /* must be an IP address */ + if (host[i] == '\0' && (inet_addr(host) == -1 || inet_network(host) == -1)) + return "Bad IP address in URL"; + + *urlp = url; + *hostp = host; + + return NULL; +} + +/* + * checks an encoded ftp string for bad characters, namely, CR, LF or + * non-ascii character + */ +static int +ftp_check_string(const char *x) +{ + int i, ch; + + for (i=0; x[i] != '\0'; i++) + { + ch = x[i]; + if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2])) + { + ch = hex2c(&x[i+1]); + i += 2; + } + if (ch == '\015' || ch == '\012' || (ch & 0x80)) return 0; + } + return 1; +} + +/* + * Canonicalise ftp URLs. + */ +static int +ftp_canon(request_rec *r, char *url) +{ + char *user, *password, *host, *path, *parms, *p, sport[7]; + const char *err; + int port; + + port = DEFAULT_FTP_PORT; + err = canon_netloc(r->pool, &url, &user, &password, &host, &port); + if (err) return BAD_REQUEST; + if (user != NULL && !ftp_check_string(user)) return BAD_REQUEST; + if (password != NULL && !ftp_check_string(password)) return BAD_REQUEST; + +/* now parse path/parameters args, according to rfc1738 */ +/* N.B. if this isn't a true proxy request, then the URL path + * (but not query args) has already been decoded. + * This gives rise to the problem of a ; being decoded into the + * path. + */ + p = strchr(url, ';'); + if (p != NULL) + { + *(p++) = '\0'; + parms = canonenc(r->pool, p, strlen(p), enc_parm, r->proxyreq); + if (parms == NULL) return BAD_REQUEST; + } else + parms = ""; + + path = canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq); + if (path == NULL) return BAD_REQUEST; + if (!ftp_check_string(path)) return BAD_REQUEST; + + if (!r->proxyreq && r->args != NULL) + { + if (p != NULL) + { + p = canonenc(r->pool, r->args, strlen(r->args), enc_parm, 1); + if (p == NULL) return BAD_REQUEST; + parms = pstrcat(r->pool, parms, "?", p, NULL); + } + else + { + p = canonenc(r->pool, r->args, strlen(r->args), enc_path, 1); + if (p == NULL) return BAD_REQUEST; + path = pstrcat(r->pool, path, "?", p, NULL); + } + r->args = NULL; + } + +/* now, rebuild URL */ + + if (port != DEFAULT_FTP_PORT) sprintf(sport, ":%d", port); + else sport[0] = '\0'; + + r->filename = pstrcat(r->pool, "proxy:ftp://", (user != NULL) ? user : "", + (password != NULL) ? ":" : "", + (password != NULL) ? password : "", + (user != NULL) ? "@" : "", host, sport, "/", path, + (parms[0] != '\0') ? ";" : "", parms, NULL); + + return OK; +} + + +/* + * Canonicalise http-like URLs. + * scheme is the scheme for the URL + * url is the URL starting with the first '/' + * def_port is the default port for this scheme. + */ +static int +http_canon(request_rec *r, char *url, const char *scheme, int def_port) +{ + char *host, *path, *search, *p, sport[7]; + const char *err; + int port; + +/* do syntatic check. + * We break the URL into host, port, path, search + */ + port = def_port; + err = canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) return BAD_REQUEST; + +/* now parse path/search args, according to rfc1738 */ +/* N.B. if this isn't a true proxy request, then the URL _path_ + * has already been decoded + */ + if (r->proxyreq) + { + p = strchr(url, '?'); + if (p != NULL) *(p++) = '\0'; + } else + p = r->args; + +/* process path */ + path = canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq); + if (path == NULL) return BAD_REQUEST; + +/* process search */ + if (p != NULL) + { + search = p; + if (search == NULL) return BAD_REQUEST; + } else + search = ""; + + if (port != def_port) sprintf(sport, ":%d", port); + else sport[0] = '\0'; + + r->filename = pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/", + path, (search[0] != '\0') ? "?" : "", search, NULL); + return OK; +} + +static const char *lwday[7]= +{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; +static const char *wday[7]= +{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +static const char *months[12]= +{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", + "Dec"}; + +/* + * If the date is a valid RFC 850 date or asctime() date, then it + * is converted to the RFC 1123 format, otherwise it is not modified. + * This routine is not very fast at doing conversions, as it uses + * sscanf and sprintf. However, if the date is already correctly + * formatted, then it exits very quickly. + */ +static char * +date_canon(pool *p, char *x) +{ + int wk, mday, year, hour, min, sec, mon; + char *q, month[4], zone[4], week[4]; + + q = strchr(x, ','); + /* check for RFC 850 date */ + if (q != NULL && q - x > 3 && q[1] == ' ') + { + *q = '\0'; + for (wk=0; wk < 7; wk++) + if (strcmp(x, lwday[wk]) == 0) break; + *q = ','; + if (wk == 7) return x; /* not a valid date */ + if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' || + q[17] != ':' || strcmp(&q[20], " GMT") != 0) return x; + if (sscanf(q+2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year, + &hour, &min, &sec, zone) != 7) return x; + if (year < 70) year += 2000; + else year += 1900; + } else + { +/* check for acstime() date */ + if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' || + x[16] != ':' || x[19] != ' ' || x[24] != '\0') return x; + if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour, + &min, &sec, &year) != 7) return x; + for (wk=0; wk < 7; wk++) + if (strcmp(week, wday[wk]) == 0) break; + if (wk == 7) return x; + } + +/* check date */ + for (mon=0; mon < 12; mon++) if (strcmp(month, months[mon]) == 0) break; + if (mon == 12) return x; +/* + * it doesn't do any harm to convert an invalid date from one format to + * another + */ +#if 0 + if (hour > 23 || min > 60 || sec > 62 || mday == 0 || mday > 31) return x; + if (mday == 31 && (mon == 1 || mon == 3 || mon == 5 || mon == 8 || mon == 10)) + return x; + if (mday > 29 && mon == 1) return x; + if (mday == 29 && mon == 1) + if (year%4 != 0 || (year%100 == 0 && year%400 != 0)) return x; +#endif + + if (strlen(x) < 31) x = palloc(p, 31); + sprintf(x, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", wday[wk], mday, + months[mon], year, hour, min, sec); + return x; +} + + +/* -------------------------------------------------------------- */ +/* Invoke handler */ + +/* Utility routines */ + +/* + * Reads headers from a connection and returns an array of headers. + * Returns NULL on file error + */ +static array_header * +read_headers(pool *pool, char *buffer, int size, BUFF *f) +{ + int gotcr, len, i, j; + array_header *resp_hdrs; + struct hdr_entry *hdr; + char *p; + + resp_hdrs = make_array(pool, 10, sizeof(struct hdr_entry)); + hdr = NULL; + + gotcr = 1; + for (;;) + { + len = bgets(buffer, size, f); + if (len == -1) return NULL; + if (len == 0) break; + if (buffer[len-1] == '\n') + { + buffer[--len] = '\0'; + i = 1; + } else + i = 0; + + if (!gotcr || buffer[0] == ' ' || buffer[0] == '\t') + { + /* a continuation header */ + if (hdr == NULL) + { + /* error!! */ + if (!i) + { + i = bskiplf(f); + if (i == -1) return NULL; + } + gotcr = 1; + continue; + } + hdr->value = pstrcat(pool, hdr->value, buffer, NULL); + } + else if (gotcr && len == 0) break; + else + { + p = strchr(buffer, ':'); + if (p == NULL) + { + /* error!! */ + if (!gotcr) + { + i = bskiplf(f); + if (i == -1) return NULL; + } + gotcr = 1; + hdr = NULL; + continue; + } + hdr = push_array(resp_hdrs); + *(p++) = '\0'; + hdr->field = pstrdup(pool, buffer); + while (*p == ' ' || *p == '\t') p++; + hdr->value = pstrdup(pool, p); + gotcr = i; + } + } + + hdr = (struct hdr_entry *)resp_hdrs->elts; + for (i=0; i < resp_hdrs->nelts; i++) + { + p = hdr[i].value; + j = strlen(p); + while (j > 0 && (p[j-1] == ' ' || p[j-1] == '\t')) j--; + p[j] = '\0'; + } + + return resp_hdrs; +} + +static long int +send_fb(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c) +{ + char buf[IOBUFSIZE]; + long total_bytes_sent; + register int n,o,w; + conn_rec *con = r->connection; + + total_bytes_sent = 0; + while (!con->aborted) { + n = bread(f, buf, IOBUFSIZE); + if (n == -1) /* input error */ + { + if (f2 != NULL) f2 = cache_error(c); + break; + } + if (n == 0) break; /* EOF */ + o=0; + total_bytes_sent += n; + + if (f2 != NULL) + if (bwrite(f2, buf, n) != n) f2 = cache_error(c); + + while(n && !r->connection->aborted) { + w = bwrite(con->client, &buf[o], n); + if (w <= 0) + break; + reset_timeout(r); /* reset timeout after successfule write */ + n-=w; + o+=w; + } + } + bflush(con->client); + + return total_bytes_sent; +} + +/* + * Read a header from the array, returning the first entry + */ +static struct hdr_entry * +get_header(array_header *hdrs_arr, const char *name) +{ + struct hdr_entry *hdrs; + int i; + + hdrs = (struct hdr_entry *)hdrs_arr->elts; + for (i = 0; i < hdrs_arr->nelts; i++) + if (hdrs[i].field != NULL && strcasecmp(name, hdrs[i].field) == 0) + return &hdrs[i]; + + return NULL; +} + +#define HDR_APP (0) +#define HDR_REP (1) + +/* + * Add to the header reply, either concatenating, or replacing existin + * headers. It stores the pointers provided, so make sure the data + * is not subsequently overwritten + */ +static struct hdr_entry * +add_header(array_header *hdrs_arr, char *field, char *value, + int rep) +{ + int i; + struct hdr_entry *hdrs; + + hdrs = (struct hdr_entry *)hdrs_arr->elts; + if (rep) + for (i = 0; i < hdrs_arr->nelts; i++) + if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0) + { + hdrs[i].value = value; + return hdrs; + } + + hdrs = push_array(hdrs_arr); + hdrs->field = field; + hdrs->value = value; + + return hdrs; +} + +#ifdef NEEDED +static void +del_header(array_header *hdrs_arr, const char *field) +{ + int i; + struct hdr_entry *hdrs; + + hdrs = (struct hdr_entry *)hdrs_arr->elts; + + for (i = 0; i < hdrs_arr->nelts; i++) + if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0) + hdrs[i].value = NULL; +} +#endif + +/* + * Sends response line and headers + */ +static void +send_headers(BUFF *fp, const char *respline, array_header *hdrs_arr) +{ + struct hdr_entry *hdrs; + int i; + + hdrs = (struct hdr_entry *)hdrs_arr->elts; + + bputs(respline, fp); + bputs("\015\012", fp); + for (i = 0; i < hdrs_arr->nelts; i++) + { + if (hdrs[i].field == NULL) continue; + bvputs(fp, hdrs[i].field, ": ", hdrs[i].value, "\015\012", NULL); + } + + bputs("\015\012", fp); +} + + +/* + * list is a comma-separated list of case-insensitive tokens, with + * optional whitespace around the tokens. + * The return returns 1 if the token val is found in the list, or 0 + * otherwise. + */ +static int +liststr(const char *list, const char *val) +{ + int len, i; + const char *p; + + len = strlen(val); + + while (list != NULL) + { + p = strchr(list, ','); + if (p != NULL) + { + i = p - list; + do p++; while (isspace(*p)); + } + else + i = strlen(list); + + while (i > 0 && isspace(list[i-1])) i--; + if (i == len && strncasecmp(list, val, len) == 0) return 1; + list = p; + } + return 0; +} + +/* number of characters in the hash */ +#define HASH_LEN (22*2) + +static void +hash(const char *it, char *val,int ndepth,int nlength) +{ + MD5_CTX context; + unsigned char digest[16]; + char tmp[22]; + int i, k, d; + unsigned int x; + static const char table[64]= +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@"; + + MD5Init(&context); + MD5Update(&context, (const unsigned char *)it, strlen(it)); + MD5Final(digest, &context); + +/* encode 128 bits as 22 characters, using a modified uuencoding */ +/* the encoding is 3 bytes -> 4 characters + * i.e. 128 bits is 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters + */ + for (i=0, k=0; i < 15; i += 3) + { + x = (digest[i] << 16) | (digest[i+1] << 8) | digest[i+2]; + tmp[k++] = table[x >> 18]; + tmp[k++] = table[(x >> 12) & 0x3f]; + tmp[k++] = table[(x >> 6) & 0x3f]; + tmp[k++] = table[x & 0x3f]; + } +/* one byte left */ + x = digest[15]; + tmp[k++] = table[x >> 2]; /* use up 6 bits */ + tmp[k++] = table[(x << 4) & 0x3f]; + /* now split into directory levels */ + + for(i=k=d=0 ; d < ndepth ; ++d) + { + strncpy(&val[i],&tmp[k],nlength); + k+=nlength; + val[i+nlength]='/'; + i+=nlength+1; + } + memcpy(&val[i],&tmp[k],22-k); + val[i+22-k]='\0'; +} + +/* + * Compare a string to a mask + * Mask characters: + * @ - uppercase letter + * # - lowercase letter + * & - hex digit + * # - digit + * * - swallow remaining characters + * - exact match for any other character + */ +static int +checkmask(const char *data, const char *mask) +{ + int i, ch, d; + + for (i=0; mask[i] != '\0' && mask[i] != '*'; i++) + { + ch = mask[i]; + d = data[i]; + if (ch == '@') + { + if (!isupper(d)) return 0; + } else if (ch == '$') + { + if (!islower(d)) return 0; + } else if (ch == '#') + { + if (!isdigit(d)) return 0; + } else if (ch == '&') + { + if (!isxdigit(d)) return 0; + } else if (ch != d) return 0; + } + + if (mask[i] == '*') return 1; + else return (data[i] == '\0'); +} + +/* + * This routine converts a tm structure into the number of seconds + * since 1st January 1970 UT + * + * The return value is a non-negative integer on success or -1 if the + * input date is out of the domain Thu, 01 Jan 1970 00:00:00 to + * Tue, 19 Jan 2038 03:14:07 inclusive + * + * Notes + * This routine has been tested on 1000000 valid dates generated + * at random by gmtime(). + * + * This routine is very fast, much faster than mktime(). + */ +static int +tm2sec(const struct tm *t) +{ + int days, year; + static const int dayoffs[12]= + {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275}; + + year = t->tm_year; +/* shift new year to 1st March; which is where it should be */ + if (t->tm_mon < 2) year--; /* now years and months since 1st March 1900 */ + days = t->tm_mday - 1 + dayoffs[t->tm_mon]; + +/* find the number of days since 1st March 1900 (in the Gregorian calendar) */ + days += year * 365 + year/4 - year/100 + (year/100 + 3)/4; + 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 (year < 69 || year > 138 || days < 0) /* must have overflowed */ + return -1; + else + return days; +} + +/* + * Parses a standard HTTP date. + * + * The restricted HTTP syntax is + * rfc1123-date = day "," SP 2DIGIT SP date SP time SP "GMT" + * date = 2DIGIT SP month SP 4DIGIT + * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT + * + * day = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun" + * + * month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | + * "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec" + * + * The spec is not clear as to whether the day and months are + * case-sensitive or not. This code assumes they are. + * + * It fills in the year, month, mday, hour, min, sec and is_dst fields of + * date. It does not set the wday or yday fields. + * On failure is sets the year to 0. + * + * It also returns the number of seconds since 1 Jan 1970 UT, or + * -1 if this would be out of range or if the date is invalid. + * + * Notes + * This routine has been tested on 100000 valid dates generated + * at random by strftime(). + * + * This routine is very fast; it would be 10x slower if it + * used sscanf. + */ +static int +parsedate(const char *date, struct tm *d) +{ + int mint, mon, year; + struct tm x; + 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 (d == NULL) d = &x; + + d->tm_year = 0; /* bad date */ + if (!checkmask(date, "@$$, ## @$$ #### ##:##:## GMT")) return -1; + +/* we don't test the weekday */ + d->tm_mday = (date[5] - '0') * 10 + (date[6] - '0'); + if (d->tm_mday == 0 || d->tm_mday > 31) return -1; + + mint = (date[8] << 16) | (date[9] << 8) | date[10]; + for (mon=0; mon < 12; mon++) if (mint == months[mon]) break; + if (mon == 12) return -1; + + d->tm_mon = mon; + year = date[12] * 1000 + date[13] * 100 + date[14] * 10 + date[15] - + ('0' * 1111); + d->tm_hour = date[17] * 10 + date[18] - '0' * 11; + d->tm_min = date[20] * 10 + date[21] - '0' * 11; + d->tm_sec = date[23] * 10 + date[24] - '0' * 11; + + if (d->tm_hour > 23 || d->tm_min > 59 || d->tm_sec > 61) return -1; + + if (d->tm_mday == 31 && (mon == 1 || mon == 3 || mon == 5 || mon == 8 || + mon == 10)) return -1; + if (d->tm_mday > 29 && mon == 1) return -1; + if (d->tm_mday == 29 && mon == 1) + if (year%4 != 0 || (year%100 == 0 && year%400 != 0)) return -1; + + d->tm_year = year - 1900; + d->tm_isdst = 0; + return tm2sec(d); +} + +/* + * 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; +} + +/* + * Converts a time integer to 8 hex digits + */ +static void +sec2hex(int t, char *y) +{ + int i, ch; + unsigned int j=t; + + for (i=7; i >= 0; i--) + { + ch = j & 0xF; + j >>= 4; + if (ch >= 10) y[i] = ch + ('A' - 10); + else y[i] = ch + '0'; + } + y[8] = '\0'; +} + + +static void +log_uerror(const char *routine, const char *file, const char *err, + server_rec *s) +{ + char *p, *q; + + q = get_time(); + p = strerror(errno); + + if (err != NULL) + { + fprintf(s->error_log, "[%s] %s\n", q, err); + if (file != NULL) + fprintf(s->error_log, "- %s: %s: %s\n", routine, file, p); + else + fprintf(s->error_log, "- %s: %s\n", routine, p); + } else + { + if (file != NULL) + fprintf(s->error_log, "[%s] %s: %s: %s\n", q, routine, file, p); + else + fprintf(s->error_log, "[%s] %s: %s\n", q, routine, p); + } + + fflush(s->error_log); +} + +struct gc_ent +{ + unsigned long int len; + time_t expire; + char file[HASH_LEN+1]; + +}; + +static int +gcdiff(const void *ap, const void *bp) +{ + const struct gc_ent *a=*(struct gc_ent **)ap, *b=*(struct gc_ent **)bp; + + if (a->expire > b->expire) return 1; + else if (a->expire < b->expire) return -1; + else return 0; +} + +static int curbytes, cachesize, every; +static unsigned long int curblocks; +static time_t now, expire; +static char *filename; + +static int sub_garbage_coll(request_rec *r,array_header *files, + const char *cachedir,const char *cachesubdir); + +static void garbage_coll(request_rec *r) + { + const char *cachedir; + void *sconf = r->server->module_config; + proxy_server_conf *pconf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + const struct cache_conf *conf=&pconf->cache; + array_header *files; + struct stat buf; + struct gc_ent *fent,**elts; + int i; + static time_t lastcheck=-1; /* static data!!! */ + + cachedir = conf->root; + cachesize = conf->space; + every = conf->gcinterval; + + if (cachedir == NULL || every == -1) return; + now = time(NULL); + if (now != -1 && lastcheck != -1 && now < lastcheck + every) return; + + block_alarms(); /* avoid SIGALRM on big cache cleanup */ + + filename = palloc(r->pool, strlen(cachedir) + HASH_LEN + 2); + strcpy(filename, cachedir); + strcat(filename, "/.time"); + if (stat(filename, &buf) == -1) /* does not exist */ + { + if (errno != ENOENT) + { + log_uerror("stat", filename, NULL, r->server); + return; + } + if (creat(filename, 0666) == -1) + { + if (errno != EEXIST) + log_uerror("creat", filename, NULL, r->server); + else + lastcheck = now; /* someone else got in there */ + return; + } + } else + { + lastcheck = buf.st_mtime; /* save the time */ + if (now < lastcheck + every) return; + if (utime(filename, NULL) == -1) + log_uerror("utimes", filename, NULL, r->server); + } + files = make_array(r->pool, 100, sizeof(struct gc_ent *)); + curblocks = 0; + curbytes = 0; + + sub_garbage_coll(r,files,cachedir,"/"); + + if (curblocks < cachesize || curblocks + curbytes <= cachesize) + return; + + qsort(files->elts, files->nelts, sizeof(struct gc_ent *), gcdiff); + + elts = (struct gc_ent **)files->elts; + for (i=0; i < files->nelts; i++) + { + fent = elts[i]; + sprintf(filename, "%s%s", cachedir, fent->file); + Explain3("GC Unlinking %s (expiry %ld, now %ld)",filename,fent->expire,now); +#if TESTING + fprintf(stderr,"Would unlink %s\n",filename); +#else + if (unlink(filename) == -1) + { + if (errno != ENOENT) + log_uerror("unlink", filename, NULL, r->server); + } + else +#endif + { + curblocks -= fent->len >> 10; + curbytes -= fent->len & 0x3FF; + if (curbytes < 0) + { + curbytes += 1024; + curblocks--; + } + if (curblocks < cachesize || curblocks + curbytes <= cachesize) + break; + } + } + unblock_alarms(); +} + +static int sub_garbage_coll(request_rec *r,array_header *files, + const char *cachebasedir,const char *cachesubdir) +{ + char line[27]; + char cachedir[HUGE_STRING_LEN]; + struct stat buf; + int fd,i; + DIR *dir; +#if defined(NEXT) + struct DIR_TYPE *ent; +#else + struct dirent *ent; +#endif + struct gc_ent *fent; + int nfiles=0; + + sprintf(cachedir,"%s%s",cachebasedir,cachesubdir); + Explain1("GC Examining directory %s",cachedir); + dir = opendir(cachedir); + if (dir == NULL) + { + log_uerror("opendir", cachedir, NULL, r->server); + return 0; + } + + while ((ent = readdir(dir)) != NULL) + { + if (ent->d_name[0] == '.') continue; + sprintf(filename, "%s%s", cachedir, ent->d_name); + Explain1("GC Examining file %s",filename); +/* is it a temporary file? */ + if (strncmp(ent->d_name, "#tmp", 4) == 0) + { +/* then stat it to see how old it is; delete temporary files > 1 day old */ + if (stat(filename, &buf) == -1) + { + if (errno != ENOENT) + log_uerror("stat", filename, NULL, r->server); + } else if (now != -1 && buf.st_atime < now - SEC_ONE_DAY && + buf.st_mtime < now - SEC_ONE_DAY) + { + Explain1("GC unlink %s",filename); +#if TESTING + fprintf(stderr,"Would unlink %s\n",filename); +#else + unlink(filename); +#endif + } + continue; + } + ++nfiles; +/* is it another file? */ + /* FIXME: Shouldn't any unexpected files be deleted? */ + /* if (strlen(ent->d_name) != HASH_LEN) continue; */ + +/* read the file */ + fd = open(filename, O_RDONLY); + if (fd == -1) + { + if (errno != ENOENT) log_uerror("open", filename,NULL, r->server); + continue; + } + if (fstat(fd, &buf) == -1) + { + log_uerror("fstat", filename, NULL, r->server); + close(fd); + continue; + } + if(S_ISDIR(buf.st_mode)) + { + char newcachedir[HUGE_STRING_LEN]; + close(fd); + sprintf(newcachedir,"%s%s/",cachesubdir,ent->d_name); + if(!sub_garbage_coll(r,files,cachebasedir,newcachedir)) + { + sprintf(newcachedir,"%s%s",cachedir,ent->d_name); +#if TESTING + fprintf(stderr,"Would remove directory %s\n",newcachedir); +#else + rmdir(newcachedir); +#endif + --nfiles; + } + continue; + } + + i = read(fd, line, 26); + if (i == -1) + { + log_uerror("read", filename, NULL, r->server); + close(fd); + continue; + } + close(fd); + line[i] = '\0'; + expire = hex2sec(line+18); + if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&&") || expire == -1) + { + /* bad file */ + if (now != -1 && buf.st_atime > now + SEC_ONE_DAY && + buf.st_mtime > now + SEC_ONE_DAY) + { + log_error("proxy: deleting bad cache file", r->server); +#if TESTING + fprintf(stderr,"Would unlink bad file %s\n",filename); +#else + unlink(filename); +#endif + } + continue; + } + +/* + * we need to calculate an 'old' factor, and remove the 'oldest' files + * so that the space requirement is met; sort by the expires date of the + * file. + * + */ + /* FIXME: We should make the array an array of gc_ents, not gc_ent *s + */ + fent = palloc(r->pool, sizeof(struct gc_ent)); + fent->len = buf.st_size; + fent->expire = expire; + strcpy(fent->file,cachesubdir); + strcat(fent->file, ent->d_name); + *(struct gc_ent **)push_array(files) = fent; + +/* accumulate in blocks, to cope with directories > 4Gb */ + curblocks += buf.st_size >> 10; /* Kbytes */ + curbytes += buf.st_size & 0x3FF; + if (curbytes >= 1024) + { + curbytes -= 1024; + curblocks++; + } + } + + closedir(dir); + + return nfiles; + +} + +/* + * read a cache file; + * returns 1 on success, + * 0 on failure (bad file or wrong URL) + * -1 on UNIX error + */ +static int +rdcache(pool *pool, BUFF *cachefp, struct cache_req *c) +{ + char urlbuff[1034], *p; + int len; +/* read the data from the cache file */ +/* format + * date SP lastmod SP expire SP count SP content-length CRLF + * dates are stored as hex seconds since 1970 + */ + len = bgets(urlbuff, 1034, cachefp); + if (len == -1) return -1; + if (len == 0 || urlbuff[len-1] != '\n') return 0; + urlbuff[len-1] = '\0'; + + if (!checkmask(urlbuff, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&")) + return 0; + + c->date = hex2sec(urlbuff); + c->lmod = hex2sec(urlbuff+9); + c->expire = hex2sec(urlbuff+18); + c->version = hex2sec(urlbuff+27); + c->len = hex2sec(urlbuff+36); + +/* check that we have the same URL */ + len = bgets(urlbuff, 1034, cachefp); + if (len == -1) return -1; + if (len == 0 || strncmp(urlbuff, "X-URL: ", 7) != 0 || + urlbuff[len-1] != '\n') + return 0; + urlbuff[len-1] = '\0'; + if (strcmp(urlbuff+7, c->url) != 0) return 0; + +/* What follows is the message */ + len = bgets(urlbuff, 1034, cachefp); + if (len == -1) return -1; + if (len == 0 || urlbuff[len-1] != '\n') return 0; + urlbuff[--len] = '\0'; + + c->resp_line = pstrdup(pool, urlbuff); + p = strchr(urlbuff, ' '); + if (p == NULL) return 0; + + c->status = atoi(p); + c->hdrs = read_headers(pool, urlbuff, 1034, cachefp); + if (c->hdrs == NULL) return -1; + if (c->len != -1) /* add a content-length header */ + { + struct hdr_entry *q; + q = get_header(c->hdrs, "Content-Length"); + if (q == NULL) + { + p = palloc(pool, 15); + sprintf(p, "%u", c->len); + add_header(c->hdrs, "Content-Length", p, HDR_REP); + } + } + return 1; +} + + +/* + * Call this to test for a resource in the cache + * Returns DECLINED if we need to check the remote host + * or an HTTP status code if successful + * + * Functions: + * if URL is cached then + * if cached file is not expired then + * if last modified after if-modified-since then send body + * else send 304 Not modified + * else + * if last modified after if-modified-since then add + * last modified date to request + */ +static int +cache_check(request_rec *r, char *url, struct cache_conf *conf, + struct cache_req **cr) +{ + char hashfile[33], *imstr, *pragma, *p, *auth; + struct cache_req *c; + time_t now; + BUFF *cachefp; + int cfd, i; + const long int zero=0L; + void *sconf = r->server->module_config; + proxy_server_conf *pconf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + + c = pcalloc(r->pool, sizeof(struct cache_req)); + *cr = c; + c->req = r; + c->url = pstrdup(r->pool, url); + +/* get the If-Modified-Since date of the request */ + c->ims = -1; + imstr = table_get(r->headers_in, "If-Modified-Since"); + if (imstr != NULL) + { +/* this may modify the value in the original table */ + imstr = date_canon(r->pool, imstr); + c->ims = parsedate(imstr, NULL); + if (c->ims == -1) /* bad or out of range date; remove it */ + table_set(r->headers_in, "If-Modified-Since", NULL); + } + +/* find the filename for this cache entry */ + hash(url, hashfile,pconf->cache.dirlevels,pconf->cache.dirlength); + if (conf->root != NULL) + c->filename = pstrcat(r->pool, conf->root, "/", hashfile, NULL); + else + c->filename = NULL; + + cachefp = NULL; +/* find out about whether the request can access the cache */ + pragma = table_get(r->headers_in, "Pragma"); + auth = table_get(r->headers_in, "Authorization"); + Explain4("Request for %s, pragma=%s, auth=%s, ims=%ld",url,pragma,auth,c->ims); + if (c->filename != NULL && r->method_number == M_GET && + strlen(url) < 1024 && !liststr(pragma, "no-cache") && auth == NULL) + { + Explain1("Check file %s",c->filename); + cfd = open(c->filename, O_RDWR); + if (cfd != -1) + { + note_cleanups_for_fd(r->pool, cfd); + cachefp = bcreate(r->pool, B_RD | B_WR); + bpushfd(cachefp, cfd, cfd); + } else if (errno != ENOENT) + log_uerror("open", c->filename, "proxy: error opening cache file", + r->server); + else + Explain1("File %s not found",c->filename); + } + + if (cachefp != NULL) + { + i = rdcache(r->pool, cachefp, c); + if (i == -1) + log_uerror("read", c->filename, "proxy: error reading cache file", + r->server); + else if (i == 0) + log_error("proxy: bad cache file", r->server); + if (i != 1) + { + pclosef(r->pool, cachefp->fd); + cachefp = NULL; + } + } + if (cachefp == NULL) + c->hdrs = make_array(r->pool, 2, sizeof(struct hdr_entry)); + /* FIXME: Shouldn't we check the URL somewhere? */ + now = time(NULL); +/* Ok, have we got some un-expired data? */ + if (cachefp != NULL && c->expire != -1 && now < c->expire) + { + Explain0("Unexpired data available"); +/* check IMS */ + if (c->lmod != -1 && c->ims != -1 && c->ims >= c->lmod) + { +/* has the cached file changed since this request? */ + if (c->date == -1 || c->date > c->ims) + { +/* No, but these header values may have changed, so we send them with the + * 304 response + */ + /* CHECKME: surely this was wrong? (Ben) + p = table_get(r->headers_in, "Expires"); + */ + p = table_get(c->hdrs, "Expires"); + if (p != NULL) table_set(r->headers_out, "Expires", p); + } + pclosef(r->pool, cachefp->fd); + Explain0("Use local copy, cached file hasn't changed"); + return USE_LOCAL_COPY; + } + +/* Ok, has been modified */ + Explain0("Local copy modified, send it"); + r->status_line = strchr(c->resp_line, ' ') + 1; + r->status = c->status; + soft_timeout ("send", r); + if (!r->assbackwards) + send_headers(r->connection->client, c->resp_line, c->hdrs); + bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; + if (!r->header_only) send_fb (cachefp, r, NULL, NULL); + pclosef(r->pool, cachefp->fd); + return OK; + } + +/* if we already have data and a last-modified date, and it is not a head + * request, then add an If-Modified-Since + */ + + if (cachefp != NULL && c->lmod != -1 && !r->header_only) + { +/* + * use the later of the one from the request and the last-modified date + * from the cache + */ + if (c->ims == -1 || c->ims < c->lmod) + { + struct hdr_entry *q; + + q = get_header(c->hdrs, "Last-Modified"); + + if (q != NULL && q->value != NULL) + table_set(r->headers_in, "If-Modified-Since", + (char *)q->value); + } + } + c->fp = cachefp; + + Explain0("Local copy not present or expired. Declining."); + + return DECLINED; +} + +/* + * Having read the response from the client, decide what to do + * If the response is not cachable, then delete any previously cached + * response, and copy data from remote server to client. + * Functions: + * parse dates + * check for an uncachable response + * calculate an expiry date, if one is not provided + * if the remote file has not been modified, then return the document + * from the cache, maybe updating the header line + * otherwise, delete the old cached file and open a new temporary file + */ +static int +cache_update(struct cache_req *c, array_header *resp_hdrs, + const char *protocol, int nocache) +{ + request_rec *r=c->req; + char *p; + int i; + struct hdr_entry *expire, *dates, *lmods, *clen; + time_t expc, date, lmod, now; + char buff[46]; + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + const long int zero=0L; + + c->tempfile = NULL; + +/* we've received the response */ +/* read expiry date; if a bad date, then leave it so the client can + * read it + */ + expire = get_header(resp_hdrs, "Expire"); + if (expire != NULL) expc = parsedate(expire->value, NULL); + else expc = -1; + +/* + * read the last-modified date; if the date is bad, then delete it + */ + lmods = get_header(resp_hdrs, "Last-Modified"); + if (lmods != NULL) + { + lmod = parsedate(lmods->value, NULL); + if (lmod == -1) + { +/* kill last modified date */ + lmods->value = NULL; + lmods = NULL; + } + } else + lmod = -1; + +/* + * what responses should we not cache? + * Unknown status responses and those known to be uncacheable + * 304 response when we have no valid cache file, or + * 200 response from HTTP/1.0 and up without a Last-Modified header, or + * HEAD requests, or + * requests with an Authorization header, or + * protocol requests nocache (e.g. ftp with user/password) + */ + if ((r->status != 200 && r->status != 301 && r->status != 304) || + (expire != NULL && expc == -1) || + (r->status == 304 && c->fp == NULL) || + (r->status == 200 && lmods == NULL && + strncmp(protocol, "HTTP/1.", 7) == 0) || + r->header_only || + table_get(r->headers_in, "Authorization") != NULL || + nocache) + { + Explain1("Response is not cacheable, unlinking %s",c->filename); +/* close the file */ + if (c->fp != NULL) + { + pclosef(r->pool, c->fp->fd); + c->fp = NULL; + } +/* delete the previously cached file */ + unlink(c->filename); + return DECLINED; /* send data to client but not cache */ + } + +/* otherwise, we are going to cache the response */ +/* + * Read the date. Generate one if one is not supplied + */ + dates = get_header(resp_hdrs, "Date"); + if (dates != NULL) date = parsedate(dates->value, NULL); + else date = -1; + + now = time(NULL); + + if (date == -1) /* No, or bad date */ + { +/* no date header! */ +/* add one; N.B. use the time _now_ rather than when we were checking the cache + */ + date = now; + p = gm_timestr_822(r->pool, now); + dates = add_header(resp_hdrs, "Date", p, HDR_REP); + Explain0("Added date header"); + } + +/* check last-modified date */ + if (lmod != -1 && lmod > date) +/* if its in the future, then replace by date */ + { + lmod = date; + lmods->value = dates->value; + Explain0("Last modified is in the future, replacing with now"); + } +/* if the response did not contain the header, then use the cached version */ + if (lmod == -1 && c->fp != NULL) + { + lmod = c->lmod; + Explain0("Reusing cached last modified"); + } + +/* we now need to calculate the expire data for the object. */ + if (expire == NULL && c->fp != NULL) /* no expiry data sent in response */ + { + expire = get_header(c->hdrs, "Expires"); + if (expire != NULL) expc = parsedate(expire->value, NULL); + } +/* so we now have the expiry date */ +/* if no expiry date then + * if lastmod + * expiry date = now + min((date - lastmod) * factor, maxexpire) + * else + * expire date = now + defaultexpire + */ + Explain1("Expiry date is %ld",expc); + if (expc == -1) + { + if (lmod != -1) + { + double x = (double)(date - lmod)*conf->cache.lmfactor; + double maxex=conf->cache.maxexpire; + if (x > maxex) x = maxex; + expc = now + (int)x; + } else + expc = now + conf->cache.defaultexpire; + Explain1("Expiry date calculated %ld",expc); + } + +/* get the content-length header */ + clen = get_header(c->hdrs, "Content-Length"); + if (clen == NULL) c->len = -1; + else c->len = atoi(clen->value); + + sec2hex(date, buff); + buff[8] = ' '; + sec2hex(lmod, buff+9); + buff[17] = ' '; + sec2hex(expc, buff+18); + buff[26] = ' '; + sec2hex(c->version++, buff+27); + buff[35] = ' '; + sec2hex(c->len, buff+36); + buff[44] = '\n'; + buff[45] = '\0'; + +/* if file not modified */ + if (r->status == 304) + { + if (c->ims != -1 && lmod != -1 && lmod <= c->ims) + { +/* set any changed headers somehow */ +/* update dates and version, but not content-length */ + if (lmod != c->lmod || expc != c->expire || date != c->date) + { + off_t curpos=lseek(c->fp->fd, 0, SEEK_SET); + if (curpos == -1) + log_uerror("lseek", c->filename, + "proxy: error seeking on cache file",r->server); + else if (write(c->fp->fd, buff, 35) == -1) + log_uerror("write", c->filename, + "proxy: error updating cache file", r->server); + } + pclosef(r->pool, c->fp->fd); + Explain0("Remote document not modified, use local copy"); + /* CHECKME: Is this right? Shouldn't we check IMS again here? */ + return USE_LOCAL_COPY; + } else + { +/* return the whole document */ + Explain0("Remote document updated, sending"); + r->status_line = strchr(c->resp_line, ' ') + 1; + r->status = c->status; + soft_timeout ("send", r); + if (!r->assbackwards) + send_headers(r->connection->client, c->resp_line, c->hdrs); + bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; + if (!r->header_only) send_fb (c->fp, r, NULL, NULL); +/* set any changed headers somehow */ +/* update dates and version, but not content-length */ + if (lmod != c->lmod || expc != c->expire || date != c->date) + { + off_t curpos=lseek(c->fp->fd, 0, SEEK_SET); + + if (curpos == -1) + log_uerror("lseek", c->filename, + "proxy: error seeking on cache file",r->server); + else if (write(c->fp->fd, buff, 35) == -1) + log_uerror("write", c->filename, + "proxy: error updating cache file", r->server); + } + pclosef(r->pool, c->fp->fd); + return OK; + } + } +/* new or modified file */ + if (c->fp != NULL) + { + pclosef(r->pool, c->fp->fd); + c->fp->fd = -1; + } + c->version = 0; + sec2hex(0, buff+27); + buff[35] = ' '; + +/* open temporary file */ +#define TMPFILESTR "/#tmpXXXXXX" + c->tempfile=palloc(r->pool,strlen(conf->cache.root)+sizeof TMPFILESTR-1); + strcpy(c->tempfile,conf->cache.root); + /* + p = strrchr(c->tempfile, '/'); + if (p == NULL) return DECLINED; + strcpy(p, TMPFILESTR); + */ + strcat(c->tempfile,TMPFILESTR); +#undef TMPFILESTR + p = mktemp(c->tempfile); + if (p == NULL) return DECLINED; + + Explain1("Create temporary file %s",c->tempfile); + + i = open(c->tempfile, O_WRONLY | O_CREAT | O_EXCL, 0622); + if (i == -1) + { + log_uerror("open", c->tempfile, "proxy: error creating cache file", + r->server); + return DECLINED; + } + note_cleanups_for_fd(r->pool, i); + c->fp = bcreate(r->pool, B_WR); + bpushfd(c->fp, -1, i); + + if (bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1) + { + log_uerror("write", c->tempfile, "proxy: error writing cache file", + r->server); + pclosef(r->pool, c->fp->fd); + unlink(c->tempfile); + c->fp = NULL; + } + return DECLINED; +} + +static void +cache_tidy(struct cache_req *c) +{ + server_rec *s=c->req->server; + long int bc; + + if (c->fp == NULL) return; + + bgetopt(c->req->connection->client, BO_BYTECT, &bc); + + if (c->len != -1) + { +/* file lengths don't match; don't cache it */ + if (bc != c->len) + { + pclosef(c->req->pool, c->fp->fd); /* no need to flush */ + unlink(c->tempfile); + return; + } + } else + { +/* update content-length of file */ + char buff[9]; + off_t curpos; + + c->len = bc; + bflush(c->fp); + sec2hex(c->len, buff); + curpos = lseek(c->fp->fd, 36, SEEK_SET); + if (curpos == -1) + log_uerror("lseek", c->tempfile, + "proxy: error seeking on cache file", s); + else if (write(c->fp->fd, buff, 8) == -1) + log_uerror("write", c->tempfile, + "proxy: error updating cache file", s); + } + + if (bflush(c->fp) == -1) + { + log_uerror("write", c->tempfile, "proxy: error writing to cache file", + s); + pclosef(c->req->pool, c->fp->fd); + unlink(c->tempfile); + return; + } + + if (pclosef(c->req->pool, c->fp->fd) == -1) + { + log_uerror("close", c->tempfile, "proxy: error closing cache file", s); + unlink(c->tempfile); + return; + } + + if (unlink(c->filename) == -1 && errno != ENOENT) + { + log_uerror("unlink", c->filename, + "proxy: error deleting old cache file", s); + } else + { + char *p; + proxy_server_conf *conf= + (proxy_server_conf *)get_module_config(s->module_config,&proxy_module); + + for(p=c->filename+strlen(conf->cache.root)+1 ; ; ) + { + p=strchr(p,'/'); + if(!p) + break; + *p='\0'; + if(mkdir(c->filename,S_IREAD|S_IWRITE|S_IEXEC) < 0 && errno != EEXIST) + log_uerror("mkdir",c->filename,"proxy: error creating cache directory",s); + *p='/'; + ++p; + } +#ifdef __EMX__ + /* Under OS/2 use rename. */ + if (rename(c->tempfile, c->filename) == -1) + log_uerror("rename", c->filename, "proxy: error renaming cache file", s); +} +#else + + if (link(c->tempfile, c->filename) == -1) + log_uerror("link", c->filename, "proxy: error linking cache file", s); + } + + if (unlink(c->tempfile) == -1) + log_uerror("unlink", c->tempfile, "proxy: error deleting temp file",s); +#endif + + garbage_coll(c->req); +} + +static BUFF * +cache_error(struct cache_req *c) +{ + log_uerror("write", c->tempfile, "proxy: error writing to cache file", + c->req->server); + pclosef(c->req->pool, c->fp->fd); + c->fp = NULL; + unlink(c->tempfile); + return NULL; +} + +static int +proxy_handler(request_rec *r) +{ + char *url, *scheme, *lenp, *p; + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + array_header *proxies=conf->proxies; + struct proxy_remote *ents=(struct proxy_remote *)proxies->elts; + int i, rc; + struct cache_req *cr; + + if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; + + lenp = table_get (r->headers_in, "Content-length"); + if ((r->method_number == M_POST || r->method_number == M_PUT) + && lenp == NULL) + return BAD_REQUEST; + + url = r->filename + 6; + p = strchr(url, ':'); + if (p == NULL) return BAD_REQUEST; + + rc = cache_check(r, url, &conf->cache, &cr); + if (rc != DECLINED) return rc; + + *p = '\0'; + scheme = pstrdup(r->pool, url); + *p = ':'; + +/* firstly, try a proxy */ + + for (i=0; i < proxies->nelts; i++) + { + p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */ + if (strcmp(ents[i].scheme, "*") == 0 || + (p == NULL && strcmp(scheme, ents[i].scheme) == 0) || + (p != NULL && + strncmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) + { +/* we only know how to handle communication to a proxy via http */ + if (strcmp(ents[i].protocol, "http") == 0) + rc = http_handler(r, cr, url, ents[i].hostname, ents[i].port); + else rc = DECLINED; + + /* an error or success */ + if (rc != DECLINED && rc != BAD_GATEWAY) return rc; + /* we failed to talk to the upstream proxy */ + } + } + +/* otherwise, try it direct */ +/* N.B. what if we're behind a firewall, where we must use a proxy or + * give up?? + */ + /* handle the scheme */ + if (r->method_number == M_CONNECT) return connect_handler(r, cr, url); + if (strcmp(scheme, "http") == 0) return http_handler(r, cr, url, NULL, 0); + if (strcmp(scheme, "ftp") == 0) return ftp_handler(r, cr, url); + else return NOT_IMPLEMENTED; +} + + +static int +proxyerror(request_rec *r, const char *message) +{ + r->status = SERVER_ERROR; + r->status_line = "500 Proxy Error"; + r->content_type = "text/html"; + + send_http_header(r); + rvputs(r, "\015\012\ +Proxy Error\015\012

Proxy Error\ +

\015\012The proxy server could not handle this request.\ +\015\012

\015\012Reason: ", message, "\015\012\015\012", + NULL); + return OK; +} + +/* + * This routine returns its own error message + */ +static const char * +host2addr(const char *host, struct in_addr *addr) +{ + int i; + unsigned long ipaddr; + + for (i=0; host[i] != '\0'; i++) + if (!isdigit(host[i]) && host[i] != '.') + break; + + if (host[i] != '\0') + { + struct hostent *hp; + + hp = gethostbyname(host); + if (hp == NULL) return "Host not found"; + memcpy(addr, hp->h_addr, sizeof(struct in_addr)); + } else + { + if ((ipaddr = inet_addr(host)) == -1) + return "Bad IP address"; + memcpy(addr, &ipaddr, sizeof(unsigned long)); + } + return NULL; +} + +/* + * Returns the ftp status code; + * or -1 on I/O error, 0 on data error + */ +int +ftp_getrc(BUFF *f) +{ + int i, len, status; + char linebuff[100], buff[5]; + + len = bgets(linebuff, 100, f); + if (len == -1) return -1; +/* check format */ + if (len < 5 || !isdigit(linebuff[0]) || !isdigit(linebuff[1]) || + !isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-')) + return 0; + status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0'; + + if (linebuff[len-1] != '\n') + { + i = bskiplf(f); + if (i != 1) return i; + } + +/* skip continuation lines */ + if (linebuff[3] == '-') + { + memcpy(buff, linebuff, 3); + buff[3] = ' '; + do + { + len = bgets(linebuff, 100, f); + if (len == -1) return -1; + if (len < 5) return 0; + if (linebuff[len-1] != '\n') + { + i = bskiplf(f); + if (i != 1) return i; + } + } while (memcmp(linebuff, buff, 4) != 0); + } + + return status; +} + +static int +doconnect(int sock, struct sockaddr_in *addr, request_rec *r) +{ + int i; + + hard_timeout ("proxy connect", r); + do i = connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); + while (i == -1 && errno == EINTR); + if (i == -1) log_uerror("connect", NULL, NULL, r->server); + kill_timeout(r); + + return i; +} + +/* + * Handles direct access of ftp:// URLs + */ +static int +ftp_handler(request_rec *r, struct cache_req *c, char *url) +{ + char *host, *path, *p, *user, *password, *parms; + const char *err; + int port, userlen, passlen, i, len, sock, dsock, csd, rc, nocache; + struct sockaddr_in server; + struct hdr_entry *hdr; + array_header *resp_hdrs; + BUFF *f, *cache, *data; + pool *pool=r->pool; + const int one=1; + const long int zero=0L; + +/* we only support GET and HEAD */ + if (r->method_number != M_GET) return NOT_IMPLEMENTED; + + host = pstrdup(r->pool, url+6); +/* We break the URL into host, port, path-search */ + port = DEFAULT_FTP_PORT; + path = strchr(host, '/'); + if (path == NULL) path = ""; + else *(path++) = '\0'; + + user = password = NULL; + nocache = 0; + passlen=0; /* not actually needed, but it shuts the compiler up */ + p = strchr(host, '@'); + if (p != NULL) + { + (*p++) = '\0'; + user = host; + host = p; +/* find password */ + p = strchr(user, ':'); + if (p != NULL) + { + *(p++) = '\0'; + password = p; + passlen = decodeenc(password); + } + userlen = decodeenc(user); + nocache = 1; /* don't cache when a username is supplied */ + } else + { + user = "anonymous"; + userlen = 9; + + password = "proxy_user@host"; + passlen = strlen(password); + } + + p = strchr(host, ':'); + if (p != NULL) + { + *(p++) = '\0'; + port = atoi(p); + } + + parms = strchr(path, ';'); + if (parms != NULL) *(parms++) = '\0'; + + memset(&server,'\0',sizeof server); + server.sin_family=AF_INET; + server.sin_port = htons(port); + err = host2addr(host, &server.sin_addr); + if (err != NULL) return proxyerror(r, err); /* give up */ + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) + { + log_uerror("socket", NULL, "proxy: error creating socket", r->server); + return SERVER_ERROR; + } + note_cleanups_for_fd(pool, sock); + + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, + sizeof(int)) == -1) + { + log_uerror("setsockopt", NULL, "proxy: error setting reuseaddr option", + r->server); + pclosef(pool, sock); + return SERVER_ERROR; + } + + i = doconnect(sock, &server, r); + if (i == -1) return proxyerror(r, "Could not connect to remote machine"); + + f = bcreate(pool, B_RDWR); + bpushfd(f, sock, sock); +/* shouldn't we implement telnet control options here? */ + +/* possible results: 120, 220, 421 */ + hard_timeout ("proxy ftp", r); + i = ftp_getrc(f); + if (i == -1) return proxyerror(r, "Error reading from remote server"); + if (i != 220) return BAD_GATEWAY; + + bputs("USER ", f); + bwrite(f, user, userlen); + bputs("\015\012", f); + bflush(f); /* capture any errors */ + +/* possible results; 230, 331, 332, 421, 500, 501, 530 */ +/* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */ + i = ftp_getrc(f); + if (i == -1) return proxyerror(r, "Error sending to remote server"); + if (i == 530) return FORBIDDEN; + else if (i != 230 && i != 331) return BAD_GATEWAY; + + if (i == 331) /* send password */ + { + if (password == NULL) return FORBIDDEN; + bputs("PASS ", f); + bwrite(f, password, passlen); + bputs("\015\012", f); + bflush(f); +/* possible results 202, 230, 332, 421, 500, 501, 503, 530 */ + i = ftp_getrc(f); + if (i == -1) return proxyerror(r, "Error sending to remote server"); + if (i == 332 || i == 530) return FORBIDDEN; + else if (i != 230 && i != 202) return BAD_GATEWAY; + } + +/* set the directory */ +/* this is what we must do if we don't know the OS type of the remote + * machine + */ + for (;;) + { + p = strchr(path, '/'); + if (p == NULL) break; + *p = '\0'; + + len = decodeenc(path); + bputs("CWD ", f); + bwrite(f, path, len); + bputs("\015\012", f); + bflush(f); +/* responses: 250, 421, 500, 501, 502, 530, 550 */ +/* 1,3 error, 2 success, 4,5 failure */ + i = ftp_getrc(f); + if (i == -1) return proxyerror(r, "Error sending to remote server"); + else if (i == 550) return NOT_FOUND; + else if (i != 250) return BAD_GATEWAY; + + path = p + 1; + } + + if (parms != NULL && strncmp(parms, "type=", 5) == 0) + { + parms += 5; + if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') || + parms[1] != '\0') parms = ""; + } + else parms = ""; + + if (parms[0] == 'i') + { + /* set type to image */ + bputs("TYPE I", f); + bflush(f); +/* responses: 200, 421, 500, 501, 504, 530 */ + i = ftp_getrc(f); + if (i == -1) return proxyerror(r, "Error sending to remote server"); + else if (i != 200 && i != 504) return BAD_GATEWAY; +/* Allow not implemented */ + else if (i == 504) parms[0] = '\0'; + } + +/* set up data connection */ + len = sizeof(struct sockaddr_in); + if (getsockname(sock, (struct sockaddr *)&server, &len) < 0) + { + log_uerror("getsockname", NULL,"proxy: error getting socket address", + r->server); + pclosef(pool, sock); + return SERVER_ERROR; + } + + dsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (dsock == -1) + { + log_uerror("socket", NULL, "proxy: error creating socket", r->server); + pclosef(pool, sock); + return SERVER_ERROR; + } + note_cleanups_for_fd(pool, dsock); + + if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, + sizeof(int)) == -1) + { + log_uerror("setsockopt", NULL, "proxy: error setting reuseaddr option", + r->server); + pclosef(pool, dsock); + pclosef(pool, sock); + return SERVER_ERROR; + } + + if (bind(dsock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == + -1) + { + char buff[22]; + + sprintf(buff, "%s:%d", inet_ntoa(server.sin_addr), server.sin_port); + log_uerror("bind", buff, "proxy: error binding to ftp data socket", + r->server); + pclosef(pool, sock); + pclosef(pool, dsock); + } + listen(dsock, 2); /* only need a short queue */ + +/* set request */ + len = decodeenc(path); + if (parms[0] == 'd') + { + if (len != 0) bputs("NLST ", f); + else bputs("NLST", f); + } + else bputs("RETR ", f); + bwrite(f, path, len); + bputs("\015\012", f); + bflush(f); +/* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550 + NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, 530 */ + rc = ftp_getrc(f); + if (rc == -1) return proxyerror(r, "Error sending to remote server"); + if (rc == 550) return NOT_FOUND; + if (rc != 125 && rc != 150 && rc != 226 && rc != 250) return BAD_GATEWAY; + kill_timeout(r); + + r->status = 200; + r->status_line = "200 OK"; + + resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry)); + if (parms[0] == 'd') + add_header(resp_hdrs, "Content-Type", "text/plain", HDR_REP); + i = cache_update(c, resp_hdrs, "FTP", nocache); + if (i != DECLINED) + { + pclosef(pool, dsock); + pclosef(pool, sock); + return i; + } + cache = c->fp; + +/* wait for connection */ + hard_timeout ("proxy ftp data connect", r); + len = sizeof(struct sockaddr_in); + do csd = accept(dsock, (struct sockaddr *)&server, &len); + while (csd == -1 && errno == EINTR); /* SHUDDER on SOCKS - cdm */ + if (csd == -1) + { + log_uerror("accept", NULL, "proxy: failed to accept data connection", + r->server); + pclosef(pool, dsock); + pclosef(pool, sock); + cache_error(c); + return BAD_GATEWAY; + } + note_cleanups_for_fd(pool, csd); + data = bcreate(pool, B_RD); + bpushfd(data, csd, -1); + kill_timeout(r); + + hard_timeout ("proxy receive", r); +/* send response */ +/* write status line */ + if (!r->assbackwards) + rvputs(r, SERVER_PROTOCOL, " ", r->status_line, "\015\012", NULL); + if (cache != NULL) + if (bvputs(cache, SERVER_PROTOCOL, " ", r->status_line, "\015\012", + NULL) == -1) + cache = cache_error(c); + +/* send headers */ + len = resp_hdrs->nelts; + hdr = (struct hdr_entry *)resp_hdrs->elts; + for (i=0; i < len; i++) + { + if (hdr[i].field == NULL || hdr[i].value == NULL || + hdr[i].value[0] == '\0') continue; + if (!r->assbackwards) + rvputs(r, hdr[i].field, ": ", hdr[i].value, "\015\012", NULL); + if (cache != NULL) + if (bvputs(cache, hdr[i].field, ": ", hdr[i].value, "\015\012", + NULL) == -1) + cache = cache_error(c); + } + + if (!r->assbackwards) rputs("\015\012", r); + if (cache != NULL) + if (bputs("\015\012", cache) == -1) cache = cache_error(c); + + bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; +/* send body */ + if (!r->header_only) + { + send_fb(data, r, cache, c); + if (rc == 125 || rc == 150) rc = ftp_getrc(f); + if (rc != 226 && rc != 250) cache_error(c); + } + else + { +/* abort the transfer */ + bputs("ABOR\015\012", f); + bflush(f); +/* responses: 225, 226, 421, 500, 501, 502 */ + i = ftp_getrc(f); + } + + cache_tidy(c); + +/* finish */ + bputs("QUIT\015\012", f); + bflush(f); +/* responses: 221, 500 */ + + pclosef(pool, csd); + pclosef(pool, dsock); + pclosef(pool, sock); + + return OK; +} + +/* + * This handles Netscape CONNECT method secure proxy requests. + * A connection is opened to the specified host and data is + * passed through between the WWW site and the browser. + * + * This code is based on the INTERNET-DRAFT document + * "Tunneling SSL Through a WWW Proxy" currently at + * http://www.mcom.com/newsref/std/tunneling_ssl.html. + * + * FIXME: this is bad, because it does its own socket I/O + * instead of using the I/O in buff.c. However, + * the I/O in buff.c blocks on reads, and because + * this function doesn't know how much data will + * be sent either way (or when) it can't use blocking + * I/O. This may be very implementation-specific + * (to Linux). Any suggestions? + * FIXME: this doesn't log the number of bytes sent, but + * that may be okay, since the data is supposed to + * be transparent. In fact, this doesn't log at all + * yet. 8^) + * FIXME: doesn't check any headers initally sent from the + * client. + * FIXME: should allow authentication, but hopefully the + * generic proxy authentication is good enough. + * FIXME: no check for r->assbackwards, whatever that is. + */ + +static int +connect_handler(request_rec *r, struct cache_req *c, char *url) +{ + struct sockaddr_in server; + const char *host, *err; + char *p; + int port, sock; + char buffer[HUGE_STRING_LEN]; + int nbytes, i; + fd_set fds; + + memset(&server, '\0', sizeof(server)); + server.sin_family=AF_INET; + + /* Break the URL into host:port pairs */ + + host = url; + p = strchr(url, ':'); + if (p==NULL) port = DEFAULT_HTTPS_PORT; + else + { + port = atoi(p+1); + *p='\0'; + } + + switch (port) + { + case DEFAULT_HTTPS_PORT: + case DEFAULT_SNEWS_PORT: + break; + default: + return SERVICE_UNAVAILABLE; + } + + Explain2("CONNECT to %s on port %d", host, port); + + server.sin_port = htons(port); + err = host2addr(host, &server.sin_addr); + if (err != NULL) return proxyerror(r, err); /* give up */ + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) + { + log_error("proxy: error creating socket", r->server); + return SERVER_ERROR; + } + note_cleanups_for_fd(r->pool, sock); + + i = doconnect(sock, &server, r); + if (i == -1 ) + return proxyerror(r, "Could not connect to remote machine"); + + Explain0("Returning 200 OK Status"); + + rvputs(r, "HTTP/1.0 200 Connection established\015\012", NULL); + rvputs(r, "Proxy-agent: ", SERVER_VERSION, "\015\012\015\012", NULL); + bflush(r->connection->client); + + while (1) /* Infinite loop until error (one side closes the connection) */ + { + FD_ZERO(&fds); + FD_SET(sock, &fds); + FD_SET(r->connection->client->fd, &fds); + + Explain0("Going to sleep (select)"); + i = select((r->connection->client->fd > sock ? + r->connection->client->fd+1 : +#ifdef HPUX + sock+1), (int*)&fds, NULL, NULL, NULL); +#else + sock+1), &fds, NULL, NULL, NULL); +#endif + Explain1("Woke from select(), i=%d",i); + + if (i) + { + if (FD_ISSET(sock, &fds)) + { + Explain0("sock was set"); + if((nbytes=read(sock,buffer,HUGE_STRING_LEN))!=0) + { + if(nbytes==-1) break; + if(write(r->connection->client->fd, buffer, nbytes)==EOF)break; + Explain1("Wrote %d bytes to client", nbytes); + } + else break; + } + else if (FD_ISSET(r->connection->client->fd, &fds)) + { + Explain0("client->fd was set"); + if((nbytes=read(r->connection->client->fd,buffer, + HUGE_STRING_LEN))!=0) + { + if(nbytes==-1) break; + if(write(sock,buffer,nbytes)==EOF) break; + Explain1("Wrote %d bytes to server", nbytes); + } + else break; + } + else break; /* Must be done waiting */ + } + else break; + } + + pclosef(r->pool,sock); + + return OK; +} + +/* + * This handles http:// URLs, and other URLs using a remote proxy over http + * If proxyhost is NULL, then contact the server directly, otherwise + * go via the proxy. + * Note that if a proxy is used, then URLs other than http: can be accessed, + * also, if we have trouble which is clearly specific to the proxy, then + * we return DECLINED so that we can try another proxy. (Or the direct + * route.) + */ +static int +http_handler(request_rec *r, struct cache_req *c, char *url, + const char *proxyhost, int proxyport) +{ + char *p; + const char *err, *host; + int port, i, sock, len; + array_header *reqhdrs_arr, *resp_hdrs; + table_entry *reqhdrs; + struct sockaddr_in server; + BUFF *f, *cache; + struct hdr_entry *hdr; + char buffer[HUGE_STRING_LEN], inprotocol[9], outprotocol[9]; + pool *pool=r->pool; + const long int zero=0L; + + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + struct nocache_entry *ent=(struct nocache_entry *)conf->nocaches->elts; + int nocache = 0; + + memset(&server, '\0', sizeof(server)); + server.sin_family = AF_INET; + + if (proxyhost != NULL) + { + server.sin_port = htons(proxyport); + err = host2addr(proxyhost, &server.sin_addr); + if (err != NULL) return DECLINED; /* try another */ + host = proxyhost; + } else + { + url += 7; /* skip http:// */ +/* We break the URL into host, port, path-search */ + port = DEFAULT_PORT; + p = strchr(url, '/'); + if (p == NULL) + { + host = pstrdup(pool, url); + url = "/"; + } else + { + char *q = palloc(pool, p-url+1); + memcpy(q, url, p-url); + q[p-url] = '\0'; + url = p; + host = q; + } + + p = strchr(host, ':'); + if (p != NULL) + { + *(p++) = '\0'; + port = atoi(p); + } + server.sin_port = htons(port); + err = host2addr(host, &server.sin_addr); + if (err != NULL) return proxyerror(r, err); /* give up */ + } + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) + { + log_error("proxy: error creating socket", r->server); + return SERVER_ERROR; + } + note_cleanups_for_fd(pool, sock); + + i = doconnect(sock, &server, r); + if (i == -1) + { + if (proxyhost != NULL) return DECLINED; /* try again another way */ + else return proxyerror(r, "Could not connect to remote machine"); + } + + f = bcreate(pool, B_RDWR); + bpushfd(f, sock, sock); + + hard_timeout ("proxy send", r); + bvputs(f, r->method, " ", url, " HTTP/1.0\015\012", NULL); + + reqhdrs_arr = table_elts (r->headers_in); + reqhdrs = (table_entry *)reqhdrs_arr->elts; + for (i=0; i < reqhdrs_arr->nelts; i++) + { + if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL) continue; + if (!strcasecmp(reqhdrs[i].key, "Connection")) continue; + bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, "\015\012", NULL); + } + + bputs("\015\012", f); +/* send the request data, if any. N.B. should we trap SIGPIPE ? */ + + if (r->method_number == M_POST || r->method_number == M_PUT) + { + len = atoi(table_get (r->headers_in, "Content-length")); + + while (len > 0) + { + i = len; + if (i > HUGE_STRING_LEN) i = HUGE_STRING_LEN; + + i = read_client_block(r, buffer, i); + bwrite(f, buffer, i); + + len -= i; + } + } + bflush(f); + kill_timeout(r); + + hard_timeout ("proxy receive", r); + + len = bgets(buffer, HUGE_STRING_LEN-1, f); + if (len == -1 || len == 0) + { + pclosef(pool, sock); + return proxyerror(r, "Error reading from remote server"); + } + +/* Is it an HTTP/1 response? */ + if (checkmask(buffer, "HTTP/#.# ### *")) + { +/* If not an HTTP/1 messsage or if the status line was > 8192 bytes */ + if (buffer[5] != '1' || buffer[len-1] != '\n') + { + pclosef(pool, sock); + return BAD_GATEWAY; + } + buffer[--len] = '\0'; + memcpy(inprotocol, buffer, 8); + inprotocol[8] = '\0'; + +/* we use the same protocol on output as on input */ + strcpy(outprotocol, inprotocol); + buffer[12] = '\0'; + r->status = atoi(&buffer[9]); + buffer[12] = ' '; + r->status_line = pstrdup(pool, &buffer[9]); + +/* read the headers. */ +/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */ +/* Also, take care with headers with multiple occurences. */ + + resp_hdrs = read_headers(pool, buffer, HUGE_STRING_LEN, f); + } else + { +/* an http/0.9 response */ + strcpy(inprotocol, "HTTP/0.9"); + strcpy(outprotocol, "HTTP/1.0"); + r->status = 200; + r->status_line = "200 OK"; + +/* no headers */ + resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry)); + } + + kill_timeout(r); + +/* + * HTTP/1.0 requires us to accept 3 types of dates, but only generate + * one type + */ + + len = resp_hdrs->nelts; + hdr = (struct hdr_entry *)resp_hdrs->elts; + for (i=0; i < len; i++) + { + if (hdr[i].value[0] == '\0') continue; + p = hdr[i].field; + if (strcasecmp(p, "Date") == 0 || + strcasecmp(p, "Last-Modified") == 0 || + strcasecmp(p, "Expires") == 0) + hdr[i].value = date_canon(pool, hdr[i].value); + } + +/* check if NoCache directive on this host */ + for (i=0; i < conf->nocaches->nelts; i++) + { + if (ent[i].name != NULL && strstr(host, ent[i].name) != NULL) + nocache = 1; + } + + i = cache_update(c, resp_hdrs, inprotocol, nocache); + if (i != DECLINED) + { + pclosef(pool, sock); + return i; + } + + cache = c->fp; + + hard_timeout ("proxy receive", r); + +/* write status line */ + if (!r->assbackwards) + rvputs(r, "HTTP/1.0 ", r->status_line, "\015\012", NULL); + if (cache != NULL) + if (bvputs(cache, outprotocol, " ", r->status_line, "\015\012", NULL) + == -1) + cache = cache_error(c); + +/* send headers */ + len = resp_hdrs->nelts; + for (i=0; i < len; i++) + { + if (hdr[i].field == NULL || hdr[i].value == NULL || + hdr[i].value[0] == '\0') continue; + if (!r->assbackwards) + rvputs(r, hdr[i].field, ": ", hdr[i].value, "\015\012", NULL); + if (cache != NULL) + if (bvputs(cache, hdr[i].field, ": ", hdr[i].value, "\015\012", + NULL) == -1) + cache = cache_error(c); + } + + if (!r->assbackwards) rputs("\015\012", r); + if (cache != NULL) + if (bputs("\015\012", cache) == -1) cache = cache_error(c); + + bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; +/* Is it an HTTP/0.9 respose? If so, send the extra data */ + if (strcmp(inprotocol, "HTTP/0.9") == 0) + { + bwrite(r->connection->client, buffer, len); + if (cache != NULL) + if (bwrite(f, buffer, len) != len) cache = cache_error(c); + } + +/* send body */ +/* if header only, then cache will be NULL */ +/* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */ + if (!r->header_only) send_fb(f, r, cache, c); + + cache_tidy(c); + + pclosef(pool, sock); + + return OK; +} + +static handler_rec proxy_handlers[] = { +{ "proxy-server", proxy_handler }, +{ NULL } +}; + + +static void * +create_proxy_config(pool *p, server_rec *s) +{ + proxy_server_conf *ps = pcalloc(p, sizeof(proxy_server_conf)); + + ps->proxies = make_array(p, 10, sizeof(struct proxy_remote)); + ps->aliases = make_array(p, 10, sizeof(struct proxy_alias)); + ps->nocaches = make_array(p, 10, sizeof(struct nocache_entry)); + ps->req = 0; + + ps->cache.root = NULL; + ps->cache.space = DEFAULT_CACHE_SPACE; + ps->cache.maxexpire = DEFAULT_CACHE_MAXEXPIRE; + ps->cache.defaultexpire = DEFAULT_CACHE_EXPIRE; + ps->cache.lmfactor = DEFAULT_CACHE_LMFACTOR; + ps->cache.gcinterval = -1; + /* at these levels, the cache can have 2^18 directories (256,000) */ + ps->cache.dirlevels=3; + ps->cache.dirlength=1; + + return ps; +} + +static char * +add_proxy(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(s->module_config,&proxy_module); + struct proxy_remote *new; + char *p, *q; + int port; + + p = strchr(r, ':'); + if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') + return "Bad syntax for a remote proxy server"; + q = strchr(p + 3, ':'); + if (q != NULL) + { + if (sscanf(q+1, "%u", &port) != 1 || port > 65535) + return "Bad syntax for a remote proxy server (bad port number)"; + *q = '\0'; + } else port = -1; + *p = '\0'; + if (strchr(f, ':') == NULL) str_tolower(f); /* lowercase scheme */ + str_tolower(p + 3); /* lowercase hostname */ + + if (port == -1) + { + int i; + for (i=0; defports[i].scheme != NULL; i++) + if (strcmp(defports[i].scheme, r) == 0) break; + port = defports[i].port; + } + + new = push_array (conf->proxies); + new->scheme = f; + new->protocol = r; + new->hostname = p + 3; + new->port = port; + return NULL; +} + +static char * +add_pass(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(s->module_config,&proxy_module); + struct proxy_alias *new; + + new = push_array (conf->aliases); + new->fake = f; + new->real = r; + return NULL; +} + +static char * +set_proxy_req(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + + psf->req = flag; + return NULL; +} + + +static char * +set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + int val; + + if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer"; + psf->cache.space = val; + return NULL; +} + +static char * +set_cache_root(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + + psf->cache.root = arg; + + return NULL; +} + +static char * +set_cache_factor(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float"; + psf->cache.lmfactor = val; + + return NULL; +} + +static char * +set_cache_maxex(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float"; + psf->cache.maxexpire = (int)(val * (double)SEC_ONE_HR); + return NULL; +} + +static char * +set_cache_defex(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float"; + psf->cache.defaultexpire = (int)(val * (double)SEC_ONE_HR); + return NULL; +} + +static char * +set_cache_gcint(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float"; + psf->cache.gcinterval = (int)(val * (double)SEC_ONE_HR); + return NULL; +} + +static char * +set_cache_dirlevels(cmd_parms *parms, char *struct_ptr, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + int val; + + if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer"; + psf->cache.dirlevels = val; + return NULL; +} + +static char * +set_cache_dirlength(cmd_parms *parms, char *struct_ptr, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + int val; + + if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer"; + psf->cache.dirlength = val; + return NULL; +} + +static char * +set_cache_exclude(cmd_parms *parms, void *dummy, char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + get_module_config (s->module_config, &proxy_module); + struct nocache_entry *new; + struct nocache_entry *list=(struct nocache_entry*)conf->nocaches->elts; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i=0; i < conf->nocaches->nelts; i++) + { + if (strcmp(arg, list[i].name) == 0) + found = 1; + } + + if (!found) + { + new = push_array (conf->nocaches); + new->name = arg; + } + return NULL; +} + +static command_rec proxy_cmds[] = { +{ "ProxyRequests", set_proxy_req, NULL, RSRC_CONF, FLAG, + "on if the true proxy requests should be accepted"}, +{ "ProxyRemote", add_proxy, NULL, RSRC_CONF, TAKE2, + "a scheme, partial URL or '*' and a proxy server"}, +{ "ProxyPass", add_pass, NULL, RSRC_CONF, TAKE2, + "a virtual path and a URL"}, +{ "CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1, + "The directory to store cache files"}, +{ "CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1, + "The maximum disk space used by the cache in Kb"}, +{ "CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1, + "The maximum time in hours to cache a document"}, +{ "CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1, + "The default time in hours to cache a document"}, +{ "CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1, + "The factor used to estimate Expires date from LastModified date"}, +{ "CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1, + "The interval between garbage collections, in hours"}, +{ "CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF, TAKE1, + "The number of levels of subdirectories in the cache" }, +{ "CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF, TAKE1, + "The number of characters in subdirectory names" }, +{ "NoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE, + "A list of hosts or domains for which caching is *not* provided" }, +{ NULL } +}; + + +module proxy_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + create_proxy_config, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + proxy_cmds, /* command table */ + proxy_handlers, /* handlers */ + proxy_trans, /* translate_handler */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + proxy_fixup, /* pre-run fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_access.c b/RELEASE_1_1_X/src/modules/standard/mod_access.c new file mode 100644 index 00000000000..2d91de2ff66 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_access.c @@ -0,0 +1,251 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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 . + * + */ + + +/* + * Security options etc. + * + * Module derived from code originally written by Rob McCool + * + */ + +#include "httpd.h" +#include "http_core.h" +#include "http_config.h" +#include "http_log.h" + +typedef struct { + char *from; + int limited; +} allowdeny; + +/* things in the 'order' array */ +#define DENY_THEN_ALLOW 0 +#define ALLOW_THEN_DENY 1 +#define MUTUAL_FAILURE 2 + +typedef struct { + int order[METHODS]; + array_header *allows; + array_header *denys; +} access_dir_conf; + +module access_module; + +void *create_access_dir_config (pool *p, char *dummy) +{ + access_dir_conf *conf = + (access_dir_conf *)pcalloc(p, sizeof(access_dir_conf)); + int i; + + for (i = 0; i < METHODS; ++i) conf->order[i] = DENY_THEN_ALLOW; + conf->allows = make_array (p, 1, sizeof (allowdeny)); + conf->denys = make_array (p, 1, sizeof (allowdeny)); + + return (void *)conf; +} + +char *order (cmd_parms *cmd, void *dv, char *arg) +{ + access_dir_conf *d = (access_dir_conf *)dv; + int i, order; + + if (!strcasecmp (arg, "allow,deny")) order = ALLOW_THEN_DENY; + else if (!strcasecmp (arg, "deny,allow")) order = DENY_THEN_ALLOW; + else if (!strcasecmp (arg, "mutual-failure")) order = MUTUAL_FAILURE; + else return "unknown order"; + + for (i = 0; i < METHODS; ++i) + if (cmd->limited & (1 << i)) + d->order[i] = order; + + return NULL; +} + +char *allow_cmd (cmd_parms *cmd, void *dv, char *from, char *where) +{ + access_dir_conf *d = (access_dir_conf *)dv; + allowdeny *a; + + if (strcasecmp (from, "from")) + return "allow and deny must be followed by 'from'"; + + a = (allowdeny *)push_array (cmd->info ? d->allows : d->denys); + a->from = pstrdup (cmd->pool, where); + a->limited = cmd->limited; + return NULL; +} + +static char its_an_allow; + +command_rec access_cmds[] = { +{ "order", order, NULL, OR_LIMIT, TAKE1, + "'allow,deny', 'deny,allow', or 'mutual-failure'" }, +{ "allow", allow_cmd, &its_an_allow, OR_LIMIT, ITERATE2, + "'from' followed by hostnames or IP-address wildcards" }, +{ "deny", allow_cmd, NULL, OR_LIMIT, ITERATE2, + "'from' followed by hostnames or IP-address wildcards" }, +{NULL} +}; + +int in_domain(const char *domain, const char *what) { + int dl=strlen(domain); + int wl=strlen(what); + + if((wl-dl) >= 0) { + if (strcmp(domain,&what[wl-dl]) != 0) return 0; + + /* Make sure we matched an *entire* subdomain --- if the user + * said 'allow from good.com', we don't want people from nogood.com + * to be able to get in. + */ + + if (wl == dl) return 1; /* matched whole thing */ + else return (domain[0] == '.' || what[wl - dl - 1] == '.'); + } else + return 0; +} + +int in_ip(char *domain, char *what) { + + /* Check a similar screw case to the one checked above --- + * "allow from 204.26.2" shouldn't let in people from 204.26.23 + */ + + int l = strlen(domain); + if (strncmp(domain,what,l) != 0) return 0; + if (domain[l - 1] == '.') return 1; + return (what[l] == '\0' || what[l] == '.'); +} + +int find_allowdeny (request_rec *r, array_header *a, int method) +{ + allowdeny *ap = (allowdeny *)a->elts; + int mmask = (1 << method); + int i, gothost=0; + const char *remotehost=NULL; + + for (i = 0; i < a->nelts; ++i) { + if (!(mmask & ap[i].limited)) + continue; + if (!strcmp (ap[i].from, "all")) + return 1; + if (!gothost) + { + remotehost = get_remote_host(r->connection, r->per_dir_config, + REMOTE_HOST); + gothost = 1; + } + if (remotehost != NULL && isalpha(remotehost[0])) + if (in_domain(ap[i].from, remotehost)) + return 1; + if (in_ip (ap[i].from, r->connection->remote_ip)) + return 1; + } + + return 0; +} + +int check_dir_access (request_rec *r) +{ + int method = r->method_number; + access_dir_conf *a = + (access_dir_conf *) + get_module_config (r->per_dir_config, &access_module); + int ret = OK; + + if (a->order[method] == ALLOW_THEN_DENY) { + ret = FORBIDDEN; + if (find_allowdeny (r, a->allows, method)) + ret = OK; + if (find_allowdeny (r, a->denys, method)) + ret = FORBIDDEN; + } else if (a->order[method] == DENY_THEN_ALLOW) { + if (find_allowdeny (r, a->denys, method)) + ret = FORBIDDEN; + if (find_allowdeny (r, a->allows, method)) + ret = OK; + } + else { + if (find_allowdeny(r, a->allows, method) + && !find_allowdeny(r, a->denys, method)) + ret = OK; + else + ret = FORBIDDEN; + } + + if (ret == FORBIDDEN) + log_reason ("Client denied by server configuration", r->filename, r); + + return ret; +} + + + +module access_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_access_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + access_cmds, + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + check_dir_access, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_actions.c b/RELEASE_1_1_X/src/modules/standard/mod_actions.c new file mode 100644 index 00000000000..c2868608a6a --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_actions.c @@ -0,0 +1,212 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_actions.c: executes scripts based on MIME type + * + * by Alexei Kosut; based on mod_cgi.c, mod_mime.c and mod_includes.c, + * adapted by rst from original NCSA code by Rob McCool + * + * Usage instructions: + * + * Action mime/type /cgi-bin/script + * + * will activate /cgi-bin/script when a file of content type mime/type. It + * sends the URL and file path of the requested document using the standard + * CGI PATH_INFO and PATH_TRANSLATED environment variables. + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#include "util_script.h" + +typedef struct { + table *action_types; /* Added with Action... */ + char *get; /* Added with Script GET */ + char *post; /* Added with Script POST */ + char *put; /* Added with Script PUT */ + char *delete; /* Added with Script DELETE */ +} action_dir_config; + +module action_module; + +void *create_action_dir_config (pool *p, char *dummy) +{ + action_dir_config *new = + (action_dir_config *) palloc (p, sizeof(action_dir_config)); + + new->action_types = make_table (p, 4); + new->get = NULL; + new->post = NULL; + new->put = NULL; + new->delete = NULL; + + return new; +} + +void *merge_action_dir_configs (pool *p, void *basev, void *addv) +{ + action_dir_config *base = (action_dir_config *)basev; + action_dir_config *add = (action_dir_config *)addv; + action_dir_config *new = + (action_dir_config *)palloc (p, sizeof(action_dir_config)); + + new->action_types = overlay_tables (p, add->action_types, + base->action_types); + + new->get = add->get ? add->get : base->get; + new->post = add->post ? add->post : base->post; + new->put = add->put ? add->put : base->put; + new->delete = add->delete ? add->delete : base->delete; + + return new; +} + +char *add_action(cmd_parms *cmd, action_dir_config *m, char *type, char *script) +{ + table_set (m->action_types, type, script); + return NULL; +} + +char *set_script (cmd_parms *cmd, action_dir_config *m, char *method, + char *script) +{ + if (!strcmp(method, "GET")) + m->get = pstrdup(cmd->pool, script); + else if (!strcmp(method, "POST")) + m->post = pstrdup(cmd->pool, script); + else if (!strcmp(method, "PUT")) + m->put = pstrdup(cmd->pool, script); + else if (!strcmp(method, "DELETE")) + m->delete = pstrdup(cmd->pool, script); + else + return "Unknown method type for Script"; + + return NULL; +} + +command_rec action_cmds[] = { +{ "Action", add_action, NULL, OR_FILEINFO, TAKE2, + "a media type followed by a script name" }, +{ "Script", set_script, NULL, ACCESS_CONF|RSRC_CONF, TAKE2, + "a method followed by a script name" }, +{ NULL } +}; + +int action_handler (request_rec *r) +{ + action_dir_config *conf = + (action_dir_config *)get_module_config(r->per_dir_config,&action_module); + char *t, *action = r->handler ? r->handler : r->content_type; + char *script = NULL; + + /* First, check for the method-handling scripts */ + if ((r->method_number == M_GET) && r->args && conf->get) + script = conf->get; + else if ((r->method_number == M_POST) && conf->post) + script = conf->post; + else if ((r->method_number == M_PUT) && conf->put) + script = conf->put; + else if ((r->method_number == M_DELETE) && conf->delete) + script = conf->delete; + + /* Check for looping, which can happen if the CGI script isn't */ + if (script && r->prev && r->prev->prev) + return DECLINED; + + /* Second, check for actions (which override the method scripts) */ + if ((action || default_type(r)) && (t = table_get(conf->action_types, + action ? action : default_type(r)))) { + script = t; + if (r->finfo.st_mode == 0) { + log_reason("File does not exist", r->filename, r); + return NOT_FOUND; + } + } + + if (script == NULL) + return DECLINED; + + internal_redirect_handler(pstrcat(r->pool, script, escape_uri(r->pool, + r->uri), r->args ? "?" : NULL, r->args, NULL), r); + return OK; +} + +handler_rec action_handlers[] = { +{ "*/*", action_handler }, +{ NULL } +}; + +module action_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_action_dir_config, /* dir config creater */ + merge_action_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + action_cmds, /* command table */ + action_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_alias.c b/RELEASE_1_1_X/src/modules/standard/mod_alias.c new file mode 100644 index 00000000000..2e7b89f6c78 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_alias.c @@ -0,0 +1,259 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * http_alias.c: Stuff for dealing with directory aliases + * + * Original by Rob McCool, rewritten in succession by David Robinson + * and rst. + * + */ + +#include "httpd.h" +#include "http_config.h" + +typedef struct { + char *real; + char *fake; + char *handler; +} alias_entry; + +typedef struct { + array_header *aliases; + array_header *redirects; +} alias_server_conf; + +module alias_module; + +void *create_alias_config (pool *p, server_rec *s) +{ + alias_server_conf *a = + (alias_server_conf *)pcalloc (p, sizeof(alias_server_conf)); + + a->aliases = make_array (p, 20, sizeof(alias_entry)); + a->redirects = make_array (p, 20, sizeof(alias_entry)); + return a; +} + +void *merge_alias_config (pool *p, void *basev, void *overridesv) +{ + alias_server_conf *a = + (alias_server_conf *)pcalloc (p, sizeof(alias_server_conf)); + alias_server_conf *base = (alias_server_conf *)basev, + *overrides = (alias_server_conf *)overridesv; + + a->aliases = append_arrays (p, overrides->aliases, base->aliases); + a->redirects = append_arrays (p, overrides->redirects, base->redirects); + return a; +} + +char *add_alias(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + server_rec *s = cmd->server; + alias_server_conf *conf = + (alias_server_conf *)get_module_config(s->module_config,&alias_module); + alias_entry *new = push_array (conf->aliases); + + /* XX r can NOT be relative to DocumentRoot here... compat bug. */ + + new->fake = f; new->real = r; new->handler = cmd->info; + return NULL; +} + +char *add_redirect(cmd_parms *cmd, void *dummy, char *f, char *url) +{ + server_rec *s = cmd->server; + alias_server_conf *conf = + (alias_server_conf *)get_module_config(s->module_config,&alias_module); + alias_entry *new = push_array (conf->redirects); + + if (!is_url (url)) return "Redirect to non-URL"; + + new->fake = f; new->real = url; + return NULL; +} + +command_rec alias_cmds[] = { +{ "Alias", add_alias, NULL, RSRC_CONF, TAKE2, + "a fakename and a realname"}, +{ "ScriptAlias", add_alias, "cgi-script", RSRC_CONF, TAKE2, + "a fakename and a realname"}, +{ "Redirect", add_redirect, NULL, OR_FILEINFO, TAKE2, + "a document to be redirected, then the destination URL" }, +{ NULL } +}; + +int alias_matches (char *uri, char *alias_fakename) +{ + char *end_fakename = alias_fakename + strlen (alias_fakename); + char *aliasp = alias_fakename, *urip = uri; + + while (aliasp < end_fakename) { + if (*aliasp == '/') { + /* any number of '/' in the alias matches any number in + * the supplied URI, but there must be at least one... + */ + if (*urip != '/') return 0; + + while (*aliasp == '/') ++ aliasp; + while (*urip == '/') ++ urip; + } + else { + /* Other characters are compared literally */ + if (*urip++ != *aliasp++) return 0; + } + } + + /* Check last alias path component matched all the way */ + + if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') + return 0; + + /* Return number of characters from URI which matched (may be + * greater than length of alias, since we may have matched + * doubled slashes) + */ + + return urip - uri; +} + +char *try_alias_list (request_rec *r, array_header *aliases, int doesc) +{ + alias_entry *entries = (alias_entry *)aliases->elts; + int i; + + for (i = 0; i < aliases->nelts; ++i) { + alias_entry *p = &entries[i]; + int l = alias_matches (r->uri, p->fake); + + if (l > 0) { + if (p->handler) { /* Set handler, and leave a note for mod_cgi */ + r->handler = pstrdup(r->pool, p->handler); + table_set (r->notes, "alias-forced-type", p->handler); + } + + if (doesc) { + char *escurl; + escurl = os_escape_path(r->pool, r->uri + l, 1); + return pstrcat(r->pool, p->real, escurl, NULL); + } else + return pstrcat(r->pool, p->real, r->uri + l, NULL); + } + } + + return NULL; +} + +int translate_alias_redir(request_rec *r) +{ + void *sconf = r->server->module_config; + alias_server_conf *conf = + (alias_server_conf *)get_module_config(sconf, &alias_module); + char *ret; + +#ifdef __EMX__ + /* Add support for OS/2 drive names */ + if ((r->uri[0] != '/' && r->uri[0] != '\0') && r->uri[1] != ':') +#else + if (r->uri[0] != '/' && r->uri[0] != '\0') +#endif + return BAD_REQUEST; + + if ((ret = try_alias_list (r, conf->redirects, 1)) != NULL) { + table_set (r->headers_out, "Location", ret); + return REDIRECT; + } + + if ((ret = try_alias_list (r, conf->aliases, 0)) != NULL) { + r->filename = ret; + return OK; + } + + return DECLINED; +} + +int fixup_redir(request_rec *r) +{ + void *sconf = r->server->module_config; + alias_server_conf *conf = + (alias_server_conf *)get_module_config(sconf, &alias_module); + char *ret; + + /* It may have changed since last time, so try again */ + + if ((ret = try_alias_list (r, conf->redirects, 1)) != NULL) { + table_set (r->headers_out, "Location", ret); + return REDIRECT; + } + + return DECLINED; +} + +module alias_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_alias_config, /* server config */ + merge_alias_config, /* merge server configs */ + alias_cmds, /* command table */ + NULL, /* handlers */ + translate_alias_redir, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + fixup_redir, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_asis.c b/RELEASE_1_1_X/src/modules/standard/mod_asis.c new file mode 100644 index 00000000000..a429ccab0a1 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_asis.c @@ -0,0 +1,128 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" +#include "http_log.h" +#include "util_script.h" +#include "http_main.h" +#include "http_request.h" + +int asis_handler (request_rec *r) +{ + FILE *f; + char *location; + + if (r->method_number != M_GET) return DECLINED; + if (r->finfo.st_mode == 0) { + log_reason("File does not exist", r->filename, r); + return NOT_FOUND; + } + + f = fopen (r->filename, "r"); + + if (f == NULL) { + log_reason("file permissions deny server access", r->filename, r); + return FORBIDDEN; + } + + scan_script_header (r, f); + location = table_get (r->headers_out, "Location"); + + if (location && location[0] == '/' && + (r->status == 200 || r->status == 301 || r->status == 302)) { + + r->status = 200; /* Assume 200 status on whatever we're pointing to */ + + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = pstrdup(r->pool, "GET"); + r->method_number = M_GET; + + internal_redirect_handler (location, r); + return OK; + } + + soft_timeout ("send", r); + send_http_header (r); + if (!r->header_only) send_fd (f, r); + fclose (f); + return OK; +} + +handler_rec asis_handlers[] = { +{ ASIS_MAGIC_TYPE, asis_handler }, +{ "send-as-is", asis_handler }, +{ NULL } +}; + +module asis_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command table */ + asis_handlers, /* handlers */ + NULL, /* translate_handler */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* pre-run fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_auth.c b/RELEASE_1_1_X/src/modules/standard/mod_auth.c new file mode 100644 index 00000000000..7c628c8093b --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_auth.c @@ -0,0 +1,267 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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_auth: authentication + * + * Rob McCool + * + * Adapted to Shambhala by rst. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" + +typedef struct auth_config_struct { + char *auth_pwfile; + char *auth_grpfile; +} auth_config_rec; + +void *create_auth_dir_config (pool *p, char *d) +{ + return pcalloc (p, sizeof(auth_config_rec)); +} + +command_rec auth_cmds[] = { +{ "AuthUserFile", set_string_slot, + (void*)XtOffsetOf(auth_config_rec,auth_pwfile), OR_AUTHCFG, TAKE1, NULL }, +{ "AuthGroupFile", set_string_slot, + (void*)XtOffsetOf(auth_config_rec,auth_grpfile), OR_AUTHCFG, TAKE1, NULL }, +{ NULL } +}; + +module auth_module; + +char *get_pw(request_rec *r, char *user, char *auth_pwfile) +{ + FILE *f; + char l[MAX_STRING_LEN]; + char *rpw, *w; + + if(!(f=pfopen(r->pool, auth_pwfile, "r"))) { + log_reason ("Could not open password file", auth_pwfile, r); + return NULL; + } + while(!(cfg_getline(l,MAX_STRING_LEN,f))) { + if((l[0] == '#') || (!l[0])) continue; + rpw = l; + w = getword(r->pool, &rpw, ':'); + + if(!strcmp(user,w)) { + pfclose(r->pool, f); + return pstrdup (r->pool, rpw); + } + } + pfclose(r->pool, f); + return NULL; +} + +table *groups_for_user (pool *p, char *user, char *grpfile) { + FILE *f; + table *grps = make_table (p, 15); + pool *sp; + char l[MAX_STRING_LEN]; + char *group_name, *ll, *w; + + if(!(f=pfopen(p, grpfile, "r"))) + return NULL; + + sp = make_sub_pool (p); + + while(!(cfg_getline(l,MAX_STRING_LEN,f))) { + if((l[0] == '#') || (!l[0])) continue; + ll = l; + clear_pool (sp); + + group_name = getword(sp, &ll, ':'); + + while(ll[0]) { + w = getword_conf (sp, &ll); + if(!strcmp(w,user)) { + table_set (grps, group_name, "in"); + break; + } + } + } + pfclose(p, f); + destroy_pool (sp); + return grps; +} + +/* These functions return 0 if client is OK, and proper error status + * if not... either AUTH_REQUIRED, if we made a check, and it failed, or + * SERVER_ERROR, if things are so totally confused that we couldn't + * figure out how to tell if the client is authorized or not. + * + * If they return DECLINED, and all other modules also decline, that's + * treated by the server core as a configuration error, logged and + * reported as such. + */ + +/* Determine user ID, and check if it really is that user, for HTTP + * basic authentication... + */ + +int authenticate_basic_user (request_rec *r) +{ + auth_config_rec *sec = + (auth_config_rec *)get_module_config (r->per_dir_config, &auth_module); + conn_rec *c = r->connection; + char *sent_pw, *real_pw; + char errstr[MAX_STRING_LEN]; + int res; + + if ((res = get_basic_auth_pw (r, &sent_pw))) return res; + + if(!sec->auth_pwfile) + return DECLINED; + + if (!(real_pw = get_pw(r, c->user, sec->auth_pwfile))) { + sprintf(errstr,"user %s not found",c->user); + log_reason (errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + /* anyone know where the prototype for crypt is? */ + if(strcmp(real_pw,(char *)crypt(sent_pw,real_pw))) { + sprintf(errstr,"user %s: password mismatch",c->user); + log_reason (errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + return OK; +} + +/* Checking ID */ + +int check_user_access (request_rec *r) { + auth_config_rec *sec = + (auth_config_rec *)get_module_config (r->per_dir_config, &auth_module); + char *user = r->connection->user; + int m = r->method_number; + int method_restricted = 0; + register int x; + char *t, *w; + table *grpstatus; + array_header *reqs_arr = requires (r); + require_line *reqs; + + /* BUG FIX: tadc, 11-Nov-1995. If there is no "requires" directive, + * then any user will do. + */ + if (!reqs_arr) + return (OK); + reqs = (require_line *)reqs_arr->elts; + + if(sec->auth_grpfile) + grpstatus = groups_for_user (r->pool, user, sec->auth_grpfile); + else + grpstatus = NULL; + + for(x=0; x < reqs_arr->nelts; x++) { + + if (! (reqs[x].method_mask & (1 << m))) continue; + + method_restricted = 1; + + t = reqs[x].requirement; + w = getword(r->pool, &t, ' '); + if(!strcmp(w,"valid-user")) + return OK; + if(!strcmp(w,"user")) { + while(t[0]) { + w = getword_conf (r->pool, &t); + if(!strcmp(user,w)) + return OK; + } + } + else if(!strcmp(w,"group")) { + if(!grpstatus) + return DECLINED; /* DBM group? Something else? */ + + while(t[0]) { + w = getword_conf(r->pool, &t); + if(table_get (grpstatus, w)) + return OK; + } + } + } + + if (!method_restricted) + return OK; + + note_basic_auth_failure (r); + return AUTH_REQUIRED; +} + +module auth_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_auth_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + auth_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + authenticate_basic_user, /* check_user_id */ + check_user_access, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_auth_anon.c b/RELEASE_1_1_X/src/modules/standard/mod_auth_anon.c new file mode 100644 index 00000000000..49b736a8c51 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_auth_anon.c @@ -0,0 +1,298 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * http_auth: authentication + * + * Rob McCool & Brian Behlendorf. + * + * Adapted to Shambhala by rst. + * + * Version 0.5 May 1996 + * + * Brutally raped by Dirk.vanGulik@jrc.it to + * + * Adapted to allow anonymous logins, just like with Anon-FTP, when + * one gives the magic user name 'anonymous' and ones email address + * as the password. + * + * Just add the following tokes to your setup: + * + * Anonymous magic-user-id [magic-user-id]... + * + * Anonymous_MustGiveEmail [ on | off ] default = off + * Anonymous_LogEmail [ on | off ] default = on + * Anonymous_VerifyEmail [ on | off ] default = off + * Anonymous_NoUserId [ on | off ] default = off + * Anonymous_Authorative [ on | off ] default = off + * + * The magic user id is something like 'anonymous', it is NOT case sensitive. + * + * The MustGiveEmail flag can be used to force users to enter something + * in the password field (like an email address). Default is off. + * + * Furthermore the 'NoUserID' flag can be set to allow completely empty + * usernames in as well; this can be is convenient as a single return + * in broken GUIs like W95 is often given by the user. The Default is off. + * + * Dirk.vanGulik@jrc.it; http://ewse.ceo.org; http://me-www.jrc.it/~dirkx + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" + +typedef struct auth_anon { + char *password; + struct auth_anon * next; + } auth_anon; + +typedef struct { + + auth_anon *auth_anon_passwords; + int auth_anon_nouserid; + int auth_anon_logemail; + int auth_anon_verifyemail; + int auth_anon_mustemail; + int auth_anon_authorative; + +} anon_auth_config_rec; + +void *create_anon_auth_dir_config (pool *p, char *d) +{ + anon_auth_config_rec * sec = (anon_auth_config_rec *) + pcalloc (p, sizeof(anon_auth_config_rec)); + + if (!sec) return NULL; /* no memory... */ + + /* just to illustrate the defaults really. */ + sec -> auth_anon_passwords =NULL; + + sec -> auth_anon_nouserid =0; + sec -> auth_anon_logemail =1; + sec -> auth_anon_verifyemail =0; + sec -> auth_anon_mustemail =1; + sec -> auth_anon_authorative =0; + return sec; +} + +char *anon_set_passwd_flag (cmd_parms *cmd, + anon_auth_config_rec *sec, int arg) { + sec->auth_anon_mustemail=arg; + return NULL; +} + +char *anon_set_userid_flag (cmd_parms *cmd, + anon_auth_config_rec *sec, int arg) { + sec->auth_anon_nouserid=arg; + return NULL; +} +char *anon_set_logemail_flag (cmd_parms *cmd, + anon_auth_config_rec *sec, int arg) { + sec->auth_anon_logemail=arg; + return NULL; +} +char *anon_set_verifyemail_flag (cmd_parms *cmd, + anon_auth_config_rec *sec, int arg) { + sec->auth_anon_verifyemail=arg; + return NULL; +} +char *anon_set_authorative_flag (cmd_parms *cmd, + anon_auth_config_rec *sec, int arg) { + sec->auth_anon_authorative=arg; + return NULL; +} + +char *anon_set_string_slots (cmd_parms *cmd, + anon_auth_config_rec *sec, char *arg) { + + auth_anon * first; + + if (!(*arg)) + return "Anonymous string cannot be empty, use Anonymous_NoUserId instead"; + + /* squeeze in a record */ + first = sec->auth_anon_passwords; + + if ( + (!(sec->auth_anon_passwords=(auth_anon *) palloc(cmd -> pool, sizeof(auth_anon)))) || + (!(sec->auth_anon_passwords->password = pstrdup(cmd -> pool, arg))) + ) return "Failed to claim memory for an anonymous password..."; + + /* and repair the next */ + sec->auth_anon_passwords->next = first; + + return NULL; +} + +command_rec anon_auth_cmds[] = { +{ "Anonymous", anon_set_string_slots, + NULL,OR_AUTHCFG, ITERATE, NULL }, +{ "Anonymous_MustGiveEmail", anon_set_passwd_flag, NULL, OR_AUTHCFG, FLAG, + "Limited to 'on' or 'off'" }, +{ "Anonymous_NoUserId", anon_set_userid_flag, NULL, OR_AUTHCFG, FLAG, + "Limited to 'on' or 'off'" }, +{ "Anonymous_VerifyEmail", anon_set_verifyemail_flag, NULL, OR_AUTHCFG, FLAG, + "Limited to 'on' or 'off'" }, +{ "Anonymous_LogEmail", anon_set_logemail_flag, NULL, OR_AUTHCFG, FLAG, + "Limited to 'on' or 'off'" }, +{ "Anonymous_Authorative", anon_set_authorative_flag, NULL, OR_AUTHCFG, FLAG, + "Limited to 'on' or 'off'" }, + +{ NULL } +}; + +module anon_auth_module; + +int anon_authenticate_basic_user (request_rec *r) +{ + anon_auth_config_rec *sec = + (anon_auth_config_rec *)get_module_config (r->per_dir_config, + &anon_auth_module); + conn_rec *c = r->connection; + char *send_pw; + char errstr[MAX_STRING_LEN]; + int res=DECLINED; + + + if ((res=get_basic_auth_pw (r,&send_pw))) + return res; + + /* Ignore if we are not configured */ + if (!sec->auth_anon_passwords) return DECLINED; + + /* Do we allow an empty userID and/or is it the magic one + */ + + if ( (!(c->user[0])) && (sec->auth_anon_nouserid) ) { + res=OK; + } else { + auth_anon *p=sec->auth_anon_passwords; + res=DECLINED; + while ((res == DECLINED) && (p !=NULL)) { + if (!(strcasecmp(c->user,p->password))) + res=OK; + p=p->next; + } + } + if ( + /* username is OK */ + (res == OK) && + /* password been filled out ? */ + ( (!sec->auth_anon_mustemail) || strlen(send_pw) ) && + /* does the password look like an email address ? */ + ( (!sec->auth_anon_verifyemail) || + (strpbrk("@",send_pw) != NULL) || + (strpbrk(".",send_pw) != NULL) + ) + ) { + if (sec->auth_anon_logemail) { + sprintf(errstr,"Anonymous: Passwd <%s> Accepted", + send_pw ? send_pw : "\'none\'"); + log_error (errstr, r->server ); + } + return OK; + } else { + if (sec->auth_anon_authorative) { + sprintf(errstr,"Anonymous: Authorative, Passwd <%s> not accepted", + send_pw ? send_pw : "\'none\'"); + log_error(errstr,r->server); + return AUTH_REQUIRED; + } + /* Drop out the bottom to return DECLINED */ + } + + + return DECLINED; +} + +int check_anon_access (request_rec *r) { + +#ifdef NOTYET + conn_rec *c = r->connection; + anon_auth_config_rec *sec = + (anon_auth_config_rec *)get_module_config (r->per_dir_config, + &anon_auth_module); + + if (!sec->auth_anon) return DECLINED; + + if ( strcasecmp(r->connection->user,sec->auth_anon )) + return DECLINED; + + return OK; +#endif + return DECLINED; +} + + +module anon_auth_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_anon_auth_dir_config, /* dir config creater */ + NULL, /* dir merger ensure strictness */ + NULL, /* server config */ + NULL, /* merge server config */ + anon_auth_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + anon_authenticate_basic_user,/* check_user_id */ + check_anon_access, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_auth_db.c b/RELEASE_1_1_X/src/modules/standard/mod_auth_db.c new file mode 100644 index 00000000000..34dd2fec14b --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_auth_db.c @@ -0,0 +1,267 @@ +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * mod_auth_db: authentication + * + * Original work by Rob McCool & Brian Behlendorf. + * + * Adapted to Apache by rst (mod_auth_dbm) + * + * Adapted for Berkeley DB by Andrew Cohen + * + * mod_auth_db was based on mod_auth_dbm. + * + * Warning, this is not a drop in replacement for mod_auth_dbm, + * for people wanting to switch from dbm to Berkeley DB. + * It requires the use of AuthDBUserFile and AuthDBGroupFile + * instead of AuthDBMUserFile AuthDBMGroupFile + * + * Also, in the configuration file you need to specify + * db_auth_module rather than dbm_auth_module + * + * On some BSD systems (e.g. FreeBSD and NetBSD) dbm is automatically + * mapped to Berkeley DB. You can use either mod_auth_dbm or + * mod_auth_db. The latter makes it more obvious that it's Berkeley. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include + +typedef struct { + + char *auth_dbpwfile; + char *auth_dbgrpfile; + +} db_auth_config_rec; + +void *create_db_auth_dir_config (pool *p, char *d) +{ + return pcalloc (p, sizeof(db_auth_config_rec)); +} + +command_rec db_auth_cmds[] = { +{ "AuthDBUserFile", set_string_slot, + (void*)XtOffsetOf(db_auth_config_rec, auth_dbpwfile), + OR_AUTHCFG, TAKE1, NULL }, +{ "AuthDBGroupFile", set_string_slot, + (void*)XtOffsetOf(db_auth_config_rec, auth_dbgrpfile), + OR_AUTHCFG, TAKE1, NULL }, +{ NULL } +}; + +module db_auth_module; + +char *get_db_pw(request_rec *r, char *user, const char *auth_dbpwfile) { + DB *f; + DBT d, q; + char *pw = NULL; + + q.data = user; + q.size = strlen(q.data); + + if(!(f=dbopen(auth_dbpwfile,O_RDONLY,0664,DB_HASH,NULL))) { + log_reason ("could not open db auth file", (char *) auth_dbpwfile, r); + return NULL; + } + + if (!((f->get)(f,&q,&d,0))) { + pw = palloc (r->pool, d.size + 1); + strncpy(pw,d.data,d.size); + pw[d.size] = '\0'; /* Terminate the string */ + } + + (f->close)(f); + return pw; +} + +/* We do something strange with the group file. If the group file + * contains any : we assume the format is + * key=username value=":"groupname [":"anything here is ignored] + * otherwise we now (0.8.14+) assume that the format is + * key=username value=groupname + * The first allows the password and group files to be the same + * physical DB file; key=username value=password":"groupname[":"anything] + * + * mark@telescope.org, 22Sep95 + */ + +char *get_db_grp(request_rec *r, char *user, const char *auth_dbgrpfile) { + char *grp_data = get_db_pw (r, user, auth_dbgrpfile); + char *grp_colon; char *grp_colon2; + + if (grp_data == NULL) return NULL; + + if ((grp_colon = strchr(grp_data, ':'))!=NULL) { + grp_colon2 = strchr(++grp_colon, ':'); + if (grp_colon2) *grp_colon2='\0'; + return grp_colon; + } + return grp_data; +} + +int db_authenticate_basic_user (request_rec *r) +{ + db_auth_config_rec *sec = + (db_auth_config_rec *)get_module_config (r->per_dir_config, + &db_auth_module); + conn_rec *c = r->connection; + char *sent_pw, *real_pw, *colon_pw; + char errstr[MAX_STRING_LEN]; + int res; + + if ((res = get_basic_auth_pw (r, &sent_pw))) + return res; + + if(!sec->auth_dbpwfile) + return DECLINED; + + if(!(real_pw = get_db_pw(r, c->user, sec->auth_dbpwfile))) { + sprintf(errstr,"DB user %s not found", c->user); + log_reason (errstr, r->filename, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + /* Password is up to first : if exists */ + colon_pw = strchr(real_pw,':'); + if (colon_pw) *colon_pw='\0'; + /* anyone know where the prototype for crypt is? */ + if(strcmp(real_pw,(char *)crypt(sent_pw,real_pw))) { + sprintf(errstr,"user %s: password mismatch",c->user); + log_reason (errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + return OK; +} + +/* Checking ID */ + +int db_check_auth(request_rec *r) { + db_auth_config_rec *sec = + (db_auth_config_rec *)get_module_config (r->per_dir_config, + &db_auth_module); + char *user = r->connection->user; + int m = r->method_number; + char errstr[MAX_STRING_LEN]; + + array_header *reqs_arr = requires (r); + require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL; + + register int x; + char *t, *w; + + if (!sec->auth_dbgrpfile) return DECLINED; + if (!reqs_arr) return DECLINED; + + for(x=0; x < reqs_arr->nelts; x++) { + + if (! (reqs[x].method_mask & (1 << m))) continue; + + t = reqs[x].requirement; + w = getword(r->pool, &t, ' '); + + if(!strcmp(w,"group") && sec->auth_dbgrpfile) { + char *orig_groups,*groups,*v; + + if (!(groups = get_db_grp(r, user, sec->auth_dbgrpfile))) { + sprintf(errstr,"user %s not in DB group file %s", + user, sec->auth_dbgrpfile); + log_reason (errstr, r->filename, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + orig_groups = groups; + while(t[0]) { + w = getword(r->pool, &t, ' '); + groups = orig_groups; + while(groups[0]) { + v = getword(r->pool, &groups,','); + if(!strcmp(v,w)) + return OK; + } + } + sprintf(errstr,"user %s not in right group",user); + log_reason (errstr, r->filename, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + } + } + + return DECLINED; +} + + +module db_auth_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_db_auth_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + db_auth_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + db_authenticate_basic_user, /* check_user_id */ + db_check_auth, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; + + diff --git a/RELEASE_1_1_X/src/modules/standard/mod_auth_dbm.c b/RELEASE_1_1_X/src/modules/standard/mod_auth_dbm.c new file mode 100644 index 00000000000..4a2a61854ea --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_auth_dbm.c @@ -0,0 +1,252 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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_auth: authentication + * + * Rob McCool & Brian Behlendorf. + * + * Adapted to Shambhala by rst. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include + +typedef struct { + + char *auth_dbmpwfile; + char *auth_dbmgrpfile; + +} dbm_auth_config_rec; + +void *create_dbm_auth_dir_config (pool *p, char *d) +{ + return pcalloc (p, sizeof(dbm_auth_config_rec)); +} + +command_rec dbm_auth_cmds[] = { +{ "AuthDBMUserFile", set_string_slot, + (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmpwfile), + OR_AUTHCFG, TAKE1, NULL }, +{ "AuthDBMGroupFile", set_string_slot, + (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmgrpfile), + OR_AUTHCFG, TAKE1, NULL }, +{ NULL } +}; + +module dbm_auth_module; + +char *get_dbm_pw(request_rec *r, char *user, char *auth_dbmpwfile) { + DBM *f; + datum d, q; + char *pw = NULL; + + q.dptr = user; + q.dsize = strlen(q.dptr); + + if(!(f=dbm_open(auth_dbmpwfile,O_RDONLY,0664))) { + log_reason ("could not open dbm auth file", auth_dbmpwfile, r); + return NULL; + } + + d = dbm_fetch(f, q); + + if (d.dptr) { + pw = palloc (r->pool, d.dsize + 1); + strncpy(pw,d.dptr,d.dsize); + pw[d.dsize] = '\0'; /* Terminate the string */ + } + + dbm_close(f); + return pw; +} + +/* We do something strange with the group file. If the group file + * contains any : we assume the format is + * key=username value=":"groupname [":"anything here is ignored] + * otherwise we now (0.8.14+) assume that the format is + * key=username value=groupname + * The first allows the password and group files to be the same + * physical DBM file; key=username value=password":"groupname[":"anything] + * + * mark@telescope.org, 22Sep95 + */ + +char *get_dbm_grp(request_rec *r, char *user, char *auth_dbmgrpfile) { + char *grp_data = get_dbm_pw (r, user, auth_dbmgrpfile); + char *grp_colon; char *grp_colon2; + + if (grp_data == NULL) return NULL; + + if ((grp_colon = strchr(grp_data, ':'))!=NULL) { + grp_colon2 = strchr(++grp_colon, ':'); + if (grp_colon2) *grp_colon2='\0'; + return grp_colon; + } + return grp_data; +} + +int dbm_authenticate_basic_user (request_rec *r) +{ + dbm_auth_config_rec *sec = + (dbm_auth_config_rec *)get_module_config (r->per_dir_config, + &dbm_auth_module); + conn_rec *c = r->connection; + char *sent_pw, *real_pw, *colon_pw; + char errstr[MAX_STRING_LEN]; + int res; + + if ((res = get_basic_auth_pw (r, &sent_pw))) + return res; + + if(!sec->auth_dbmpwfile) + return DECLINED; + + if(!(real_pw = get_dbm_pw(r, c->user, sec->auth_dbmpwfile))) { + sprintf(errstr,"DBM user %s not found", c->user); + log_reason (errstr, r->filename, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + /* Password is up to first : if exists */ + colon_pw = strchr(real_pw,':'); + if (colon_pw) *colon_pw='\0'; + /* anyone know where the prototype for crypt is? */ + if(strcmp(real_pw,(char *)crypt(sent_pw,real_pw))) { + sprintf(errstr,"user %s: password mismatch",c->user); + log_reason (errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + return OK; +} + +/* Checking ID */ + +int dbm_check_auth(request_rec *r) { + dbm_auth_config_rec *sec = + (dbm_auth_config_rec *)get_module_config (r->per_dir_config, + &dbm_auth_module); + char *user = r->connection->user; + int m = r->method_number; + char errstr[MAX_STRING_LEN]; + + array_header *reqs_arr = requires (r); + require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL; + + register int x; + char *t, *w; + + if (!sec->auth_dbmgrpfile) return DECLINED; + if (!reqs_arr) return DECLINED; + + for(x=0; x < reqs_arr->nelts; x++) { + + if (! (reqs[x].method_mask & (1 << m))) continue; + + t = reqs[x].requirement; + w = getword(r->pool, &t, ' '); + + if(!strcmp(w,"group") && sec->auth_dbmgrpfile) { + char *orig_groups,*groups,*v; + + if (!(groups = get_dbm_grp(r, user, sec->auth_dbmgrpfile))) { + sprintf(errstr,"user %s not in DBM group file %s", + user, sec->auth_dbmgrpfile); + log_reason (errstr, r->filename, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + orig_groups = groups; + while(t[0]) { + w = getword(r->pool, &t, ' '); + groups = orig_groups; + while(groups[0]) { + v = getword(r->pool, &groups,','); + if(!strcmp(v,w)) + return OK; + } + } + sprintf(errstr,"user %s not in right group",user); + log_reason (errstr, r->filename, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + } + } + + return DECLINED; +} + + +module dbm_auth_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_dbm_auth_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + dbm_auth_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + dbm_authenticate_basic_user, /* check_user_id */ + dbm_check_auth, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_auth_msql.c b/RELEASE_1_1_X/src/modules/standard/mod_auth_msql.c new file mode 100644 index 00000000000..b8d0e21d2d9 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_auth_msql.c @@ -0,0 +1,971 @@ +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * mod_auth_msql: authentication + * + * Rob McCool & Brian Behlendorf. + * + * Adapted to Shambhala by rst. + * + * Addapted for use with the mSQL database + * (see ftp:/ftp.bond.edu.au/pub/Minerva/mSQL) + * + * Version 1.0 May 1996 - Blame: Dirk.vanGulik@jrc.it. + * + * A (sometimes more up to date) version of the documentation + * can be found at the http://www.apache.org site or at + * http://me-www.jrc.it/~dirkx/mod_auth_msql.html. + * + * Outline: + * + * This module allows access control using the public domain + * mSQL database; a fast but limted SQL engine which can be + * contacted over an internal unix domain protocol as well as + * over normal inter-machine tcp/ip socket communication. + * + * An example table could be: + * + * create table user_records ( + * User_id char(32) primary key, + * Cpasswd char(32), + * [ Xgroup char(32) ] + * ) \g + * + * The user_id can be as long as desired; however some of the + * popular web browsers truncate, or stop the user from entering + * names longer than 32 characters. Furthermore the 'crypt' function + * on your platform might impose further limits. Also use of + * the 'require users uid [uid..]' directive in the access.conf file, + * where the user ids are separated by spaces can possibly prohibit the + * use of spaces in your user-names. Also, not the MAX_FIELD_LEN define + * somewhere below. + * + * To use the above, the following example could be in your access.conf + * file. Also there is a more elaborate description afther this example. + * + * + * + * Auth_MSQLhost localhost + * or + * Auth_MSQLhost datab.machine.your.org + * + * If this directive is ommited, or set to + * localhost, the machine on which apache + * runs is assumed, and the faster /dev/msql + * communication channel will be used. Otherwise + * it is the machine to contact by tcp/ip. + * + * Auth_MSQLdatabase www + * + * The name of the database on the above machine, + * which contains *both* the tables for group and + * for user/passwords. Currently it is not possible + * to have these split over two databases. Make + * sure that the msql.acl (access control file) of + * mSQL does indeed allow the effective uid of the + * web server read access to this database. Check the + * httpd.conf file for this uid. + * + * Auth_MSQLpwd_table user_records + * + * Here the table which contain the uid/password combination + * is specified. + * + * Auth_MSQLuid_field User_id + * Auth_MSQLpwd_field Cpasswd + * + * These two directive specify the field names in the 'user_record' + * table. If this module is compiled with the BACKWARD_VITEK + * compatibility switch, the defaults 'user' and 'password' are + * assumed if you do not specify them. Currently the user_id field + * *MUST* be a primary key or one must ensure that each user only + * occurs *once* in the table. If a UID occurs twice access is + * denied by default. + * + * Auth_MSQLgrp_table user_records + * Auth_MSQLgrp_field Xgroup + * + * Optionaly one can also specify a table which contains the + * user/group combinations. This can be the same table which + * also contains the username/password combinations. However + * if a user belongs to two or more groups, one will have to + * use a differt table with multiple entries. + * + * Auth_MSQL_nopasswd off + * Auth_MSQL_Authorative on + * Auth_MSQL_EncryptedPasswords on + * + * These three optional fields (all set to the sensible defaults, + * so you really do not have to enter them) are described in more + * detail below. If you choose to set these to any other values than + * the above be very sure you understand the security implications and + * do verify that apache does what you exect it to do. + * + * AuthName example mSQL realm + * AuthType basic + * + * Normal apache/ncsa tokens for access control + * + * + * order deny,allow + * allow from all + * + * require valid-user + * 'valid-user'; allow in any user which has a valid uid/passwd + * pair in the above pwd_table. + * or + * require user smith jones + * Limit access to users who have a valid uid/passwd pair in the + * above pwd_table AND whose uid is 'smith' or 'jones'. Do note that + * the uid's are separated by 'spaces' for historic (ncsa) reasons. + * So allowing uids with spaces might cause problems. + * + * require group has_paid + * Optionally also ensure that the uid has the value 'has_paid' in the group + * field in the group table. + * + * + * + * End of the example + * + * - full description of all tokens: - + * + * Directives: + * + * Auth_MSQLhost Hostname of the machine running + * the mSQL demon. The effective uid + * of the server should be allowed + * access. If not given, or if it is + * the magic name 'localhost', it is + * passed to the mSQL libary as a null + * pointer. This effectively forces it + * to use /dev/msql rather than the + * (slower) socket communication. + * + * Auth_MSQLdatabase Name of the database in which the following + * table(s) are contained. + * + * Auth_MSQLpwd_table Contains at least the fields with the + * username and the (encrypted) password. Each + * uid should only occur once in this table and + * for performance reasons should be a primary key. + * Normally this table is compulsory, but it is + * possible to use a fall-through to other methods + * and use the mSQL module for group control only; + * see the Authorative directive below. + * + * Auth_MSQLgrp_table Contains at least the fields with the + * username and the groupname. A user which + * is in multiple groups has therefore + * multiple entries; this might be some per- + * formance problems associated with this; and one + * might consider to have separate tables for each + * group (rather than all groups in one table) if + * your directory structure allows for it. + * One only needs to specify this table when doing + * group control. + * + * Auth_MSQLuid_field Name of the field containing the username + * Auth_MSQLpwd_field Fieldname for the passwords + * Auth_MSQLgrp_field Fieldname for the groupname + * + * Only the fields used need to be specified. When this + * module is compiled with the BACKWARD_VITEK option the + * uid and pwd field names default to 'user' and 'password'. + * + * + * Auth_MSQL_nopasswd + * skip password comparison if passwd field is + * empty; i.e. allow 'any' password. This is off + * by default; thus to ensure that an empty field + * in the mSQL table does not allow people in by + * default with a random password. + * + * Auth_MSQL_Authorative + * default is 'on'. When set on, there is no + * fall through to other authorization methods. So if a + * user is not in the mSQL dbase table (and perhaps + * not in the right group) or has the password wrong, then + * he or she is denied access. When this directive is set to + * 'off' control is passed on to any other authorization + * modules, such as the basic auth module wih the htpasswd + * file and or the unix-(g)dbm modules. + * The default is 'ON' to avoid nasty 'fall-through' sur- + * prizes. Do be sure you know what you decide to switch + * it off. + * + * Auth_MSQL_EncryptedPasswords + * default is on. When set on, the values in the + * pwd_field are assumed to be crypted using *your* + * machines 'crypt' function; and the incoming password + * is 'crypt'ed before comparison. When this function is + * off, the comparison is done directly with the plaintext + * entered password. (Yes; http-basic-auth does send the + * password as plaintext over the wire :-( ). The default + * is a sensible 'on', and I personally thing that it is + * a *very-bad-idea* to change this. However a multi + * vendor or international environment (which sometimes + * leads to different crypts functions) might force you to. + * + * Dirk.vanGulik@jrc.it; http://ewse.ceo.org; http://me-www.jrc.it/~dirkx + * 23 Nov 1995, 24 Feb 1996, 16 May 1996. + * + * Version 0.0 First release + * 0.1 Update to apache 1.00 + * 0.2 added lines which got missing god knows when + * and which did the valid-user authentification + * no good at all ! + * 0.3 Added 'Auth_MSQL_nopasswd' option + * 0.4 Cleaned out the error messages mess. + * 0.6 Inconsistency with gid/grp in comment/token/source + * Make sure you really use 'Auth_MSQLgrp_field' as + * indicated above. + * 0.7 *host to host fixed. Credits go to Rob Stout, + * for spotting this one. + * 0.8 Authorative directive added. See above. + * 0.9 palloc return code check(s), should be backward compatible with + * 1.11 version of Vivek Khera msql module, + * fixed broken err msg in group control, changed command table + * messages to make more sense when displayed in that new module + * management tool. Added EncryptedPassword on/off functionality. + * msqlClose() statements added upon error. Support for persistent + * connections with the mSQL database (riscy). Escaping of ' and \. + * Replaced some MAX_STRING_LENGTH claims. + * 1.0 removed some error check as they where already done elsehwere + * NumFields -> NumRows (Thanks Vitek). More stack memory. + */ + + +#define ONLY_ONCE 1 +/* + * If the mSQL table containing the uid/passwd combination does + * not have the uid field as a primary key, it is possible for the + * uid to occur more than once in the table with possibly different + * passwords. When this module is compiled with the ONLY_ONCE directive + * set, access is denied if the uid occures more than once in the + * uid/passwd table. If you choose not to set it, the software takes + * the first pair returned and ignores any further pairs. The SQL + * statement used for this is + * + * "select password form pwd_table where user='uid'" + * + * this might lead to unpredictable results. For this reason as well + * as for performance reasons you are strongly adviced to make the + * uid field a primary key. Use at your own peril :-) + */ + +#undef KEEP_MSQL_CONNECTION_OPEN +/* + * Normally the (tcp/ip) connection with the database is opened and + * closed for each SQL query. When the httpd-server and the database + * are on the same machine, and /dev/msql is used this does not + * cause a serious overhead. However when your platform does not + * support this (see the mSQL documentation) or when the web server + * and the database are on different machines the overhead can be + * considerable. When the above is set defined the server leaves the + * connection open; i.e. no call to msqlClose(). If an error occures + * an attempt is made to re-open the connection for the next http-rq. + * + * This has a number of very serious drawbacks + * - It costs 2 already rare filedescriptors for each child. + * - It costs msql-connections, typically one per child. The (compiled in) + * number of connections mSQL can handle is low, typically 6 or 12. + * which might prohibit access to the mSQL database for later + * processes. + * - when a child dies, it might not free that connection properly + * or quick enough. + * - When errors start to occur, connection/file-descr resources might + * become exausted very quickly. + * + * In short; use this at your own peril and only in a highly controled and + * monitored environment + */ + +#define BACKWARD_VITEK +#define VITEX_uid_name "user" +#define VITEX_gid_name "passwd" +/* A second mSQL auth module for apache has also been developed by + * Vivek Khera and was subsequently distributed + * with some early versions of Apache. It can be optained from + * ftp://ftp.kcilink.com/pub/mod_auth_msql.c*. Older 'vitek' versions had + * the field/table names compiled in; newer versions, v.1.11 have + * more access.conf configuration options; however these where + * choosen not to be in line the 'ewse' version of this module. Also, + * the 'vitek' module does not give group control or 'empty' password + * control. + * + * To get things slightly more in line this version (0.9) should + * be backward compatible with the vitek module by: + * + * - adding support for the EncryptedPassword on/off functionality + * + * - adding support for the different spelling fo the 4 configuration + * tokens for user-table-name, user/password-field-name and dbase-name. + * + * - setting some field names to a default which used to be hard + * coded in in older vitek modules. + * + * If this troubles you; remove the 'BACKWARD_VITEX' define. + */ + +/* get some sensible values; rather than that big MAX_STRING_LEN, + */ + +/* Max field value length limit; well above the limit of some browsers :-) + */ +#define MAX_FIELD_LEN (64) +/* the next two values can be pulled from msql_priv.c, which is *NOT* copied to your + * /usr/local/include as part of the normal install procedure which comes with + * mSQL. + */ +#define MSQL_FIELD_NAME_LEN (19) +#define MSQL_TABLE_NAME_LEN (19) +/* We only do the following two queries: + * + * - for the user/passwd combination + * select PWDFIELD from PWDTABEL where USERFIELD='UID' + * + * - optionally for the user/group combination: + * select GROUPFIELD from GROUPTABLE where USERFIELD='UID' and GROUPFIELD='GID' + * + * This leads to the following limits: (we are ignoring escaping a wee bit bit here + * assuming not more than 24 escapes.) + */ + +#define MAX_QUERY_LEN (32+24+MAX_FIELD_LEN*2+3*MSQL_FIELD_NAME_LEN+1*MSQL_TABLE_NAME_LEN) + + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include +#ifdef HAVE_CRYPT_H +#include +#endif + +typedef struct { + + char *auth_msql_host; + char *auth_msql_database; + + char *auth_msql_pwd_table; + char *auth_msql_grp_table; + + char *auth_msql_pwd_field; + char *auth_msql_uname_field; + char *auth_msql_grp_field; + + int auth_msql_nopasswd; + int auth_msql_authorative; + int auth_msql_encrypted; + +} msql_auth_config_rec; + +void *create_msql_auth_dir_config (pool *p, char *d) +{ + msql_auth_config_rec * sec= (msql_auth_config_rec *) pcalloc (p, sizeof(msql_auth_config_rec)); + + sec->auth_msql_host = NULL; /* just to enforce the default 'localhost' behaviour */ + + /* just in case, to be nice... */ + sec->auth_msql_database = NULL; + sec->auth_msql_pwd_table = NULL; + sec->auth_msql_grp_table = NULL; + sec->auth_msql_pwd_field = NULL; + sec->auth_msql_uname_field = NULL; + sec->auth_msql_grp_field = NULL; + + + sec->auth_msql_authorative = 1; /* set some defaults, just in case... */ + sec->auth_msql_encrypted = 1; + sec->auth_msql_nopasswd = 0; + +#ifdef BACKWARD_VITEK + /* these are for backward compatibility with the Vivek + * msql module, as it used to have compile-time defaults. + */ + sec->auth_msql_uname_field = VITEX_uid_name; + sec->auth_msql_pwd_field = VITEX_gid_name; +#endif + + return sec; +} + +char *set_passwd_flag (cmd_parms *cmd, msql_auth_config_rec *sec, int arg) { + sec->auth_msql_nopasswd=arg; + return NULL; +} + +char *set_authorative_flag (cmd_parms *cmd, msql_auth_config_rec *sec, int arg) { + sec->auth_msql_authorative=arg; + return NULL; +} + +char *set_crypted_password_flag (cmd_parms *cmd, msql_auth_config_rec *sec , int arg) { + sec->auth_msql_encrypted = arg; + return NULL; +} + +char *msql_set_string_slot (cmd_parms *cmd, char *struct_ptr, char *arg) { + int offset = (int)cmd->info; + *(char **)(struct_ptr + offset) = pstrdup (cmd->pool, arg); + return NULL; +} + + +command_rec msql_auth_cmds[] = { +{ "Auth_MSQLhost", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_host), + OR_AUTHCFG, TAKE1, "Host on which the mSQL database engine resides (defaults to localhost)" }, + +{ "Auth_MSQLdatabase", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_database), + OR_AUTHCFG, TAKE1, "Name of the mSQL database which contains the password (and possibly the group) tables. " }, + +{ "Auth_MSQLpwd_table", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_table), + OR_AUTHCFG, TAKE1, "Name of the mSQL table containing the password/user-name combination" }, + +{ "Auth_MSQLgrp_table", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_table), + OR_AUTHCFG, TAKE1, "Name of the mSQL table containing the group-name/user-name combination; can be the same as the password-table." }, + +{ "Auth_MSQLpwd_field", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_field), + OR_AUTHCFG, TAKE1, "The name of the field in the mSQL password table" }, + +{ "Auth_MSQLuid_field", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_uname_field), + OR_AUTHCFG, TAKE1, "The name of the user-name field in the mSQL password (and possibly group) table(s)." }, + +{ "Auth_MSQLgrp_field", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_field), + OR_AUTHCFG, TAKE1, + "The name of the group field in the mSQL group table; must be set if you want to use groups." }, + +{ "Auth_MSQL_nopasswd", set_passwd_flag, NULL, OR_AUTHCFG, FLAG, + "Enable (on) or disable (off) empty password strings; in which case any user password is accepted." }, + +{ "Auth_MSQL_Authorative", set_authorative_flag, NULL, OR_AUTHCFG, FLAG, + "When 'on' the mSQL database is taken to be authorative and access control is not passed along to other db or access modules." }, + +{ "Auth_MSQL_EncryptedPasswords", set_crypted_password_flag, NULL, OR_AUTHCFG, FLAG, + "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." }, + +#ifdef BACKWARD_VITEK +/* These 'altenative' tokens should ensure backward compatibility + * with viteks mSQL module. The only difference is the spelling. + * Note that these tokens do not allow group configuration. + */ +{ "AuthMSQLHost", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_host), + OR_AUTHCFG, TAKE1, "mSQL server hostname" }, +{ "AuthMSQLDB", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_database), + OR_AUTHCFG, TAKE1, "mSQL database name" }, +{ "AuthMSQLUserTable", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_table), + OR_AUTHCFG, TAKE1, "mSQL user table name" }, +{ "AuthMSQLGroupTable", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_table), + OR_AUTHCFG, TAKE1, "mSQL group table name" }, +{ "AuthMSQLNameField", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_uname_field), + OR_AUTHCFG, TAKE1, "mSQL User ID field name within table" }, +{ "AuthMSQLGroupField", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_field), + OR_AUTHCFG, TAKE1, "mSQL Group field name within table" }, +{ "AuthMSQLPasswordField", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_field), + OR_AUTHCFG, TAKE1, "mSQL Password field name within table" }, +{ "AuthMSQLCryptedPasswords", set_crypted_password_flag, NULL, + OR_AUTHCFG, FLAG, "mSQL passwords are stored encrypted if On" }, + +#endif + +{ NULL } +}; + +module msql_auth_module; + +/* boring little routine which escapes the ' and \ in the + * SQL query. See the mSQL FAQ for more information :-) on + * this very popular subject in the msql-mailing list. + */ +char *msql_escape(char *out, char *in, char *msql_errstr) { + + register int i=0,j=0; + + do { + /* do we need to escape */ + if ( (in[i] == '\'') || (in[i] == '\\')) { + + /* does this fit ? */ + if (j >= (MAX_FIELD_LEN-1)) { + sprintf(msql_errstr,"Could not escape '%s', longer than %d",in,MAX_FIELD_LEN); + return NULL; + }; + + out[j++] = '\\'; /* insert that escaping slash for good measure */ + }; + + /* Do things still fit ? */ + if (j >= MAX_FIELD_LEN) return NULL; + + } while ( ( out[j++] = in[i++]) != '\0' ); + + return out; +} + +/* get the password for uname=user, and copy it + * into r. Assume that user is a string and stored + * as such in the mSQL database + */ +char *do_msql_query(request_rec *r, char *query, msql_auth_config_rec *sec, int once , char *msql_errstr) { + + static int sock=-1; + int hit; + m_result *results; + m_row currow; + + char *result=NULL; + char *host=sec->auth_msql_host; + +#ifndef KEEP_MSQL_CONNECTION_OPEN + sock=-1; +#endif + + /* force fast access over /dev/msql */ + + if ((host) && (!(strcasecmp(host,"localhost")))) + host=NULL; + + /* (re) open if nessecary + */ + if (sock==-1) if ((sock=msqlConnect(host)) == -1) { + sprintf (msql_errstr, + "mSQL: Could not connect to Msql DB %s (%s)", + (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"), + msqlErrMsg); + return NULL; + } + + /* we always do this, as it avoids book-keeping + * and is quite cheap anyway + */ + if (msqlSelectDB(sock,sec->auth_msql_database) == -1 ) { + sprintf (msql_errstr,"mSQL: Could not select Msql Table \'%s\' on host \'%s\'(%s)", + (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"), + (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"), + msqlErrMsg); + msqlClose(sock); + sock=-1; + return NULL; + } + + if (msqlQuery(sock,query) == -1 ) { + sprintf (msql_errstr,"mSQL: Could not Query database '%s' on host '%s' (%s) with query [%s]", + (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"), + (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"), + msqlErrMsg, + ( query ? query : "\'unset!\'") ); + msqlClose(sock); + sock=-1; + return NULL; + } + + if (!(results=msqlStoreResult())) { + sprintf (msql_errstr,"mSQL: Could not get the results from mSQL database \'%s\' on \'%s\' (%s) with query [%s]", + (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"), + (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"), + msqlErrMsg, + ( query ? query : "\'unset!\'") ); + msqlClose(sock); + sock=-1; + return NULL; + }; + + hit=msqlNumRows(results); + + if (( once ) && ( hit >1 )) { + /* complain if there are to many + * matches. + */ + sprintf (msql_errstr,"mSQL: More than %d matches (%d) whith query [%s]", + once,hit,( query ? query : "\'unset!\'") ); + } else + /* if we have a it, try to get it + */ + if ( hit ) { + if ( (currow=msqlFetchRow(results)) != NULL) { + /* copy the first matching field value */ + if (!(result=palloc(r->pool,strlen(currow[0])+1))) { + sprintf (msql_errstr,"mSQL: Could not get memory for mSQL %s (%s) with [%s]", + (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"), + msqlErrMsg, + ( query ? query : "\'unset!\'") ); + /* do not return right away, to ensure Free/Close. + */ + } else { + strcpy(result,currow[0]); + }; + } + }; + + /* ignore errors, functions are voids anyway. */ + msqlFreeResult(results); + +#ifndef KEEP_MSQL_CONNECTION_OPEN + /* close the connection, unless explicitly told not to. Do note that + * we do not have a decent closing option of child termination due + * the lack of hooks in the API (or my understanding thereof) + */ + msqlClose(sock); + sock=-1; +#endif + + return result; +} + +char *get_msql_pw(request_rec *r, char *user, msql_auth_config_rec *sec ,char *msql_errstr) { + char query[MAX_QUERY_LEN]; + char esc_user[MAX_FIELD_LEN]; + + /* do we have enough information to build a query */ + if ( + (!sec->auth_msql_pwd_table) || + (!sec->auth_msql_pwd_field) || + (!sec->auth_msql_uname_field) + ) { + sprintf(msql_errstr, + "mSQL: Missing parameters for password lookup: %s%s%s", + (sec->auth_msql_pwd_table ? "" : "Password table "), + (sec->auth_msql_pwd_field ? "" : "Password field name "), + (sec->auth_msql_uname_field ? "" : "UserID field name ") + ); + return NULL; + }; + + if (!(msql_escape(esc_user, user, msql_errstr))) { + sprintf(msql_errstr, + "mSQL: Could not cope/escape the '%s' user_id value; ",user); + return NULL; + }; + sprintf(query,"select %s from %s where %s='%s'", + sec->auth_msql_pwd_field, + sec->auth_msql_pwd_table, + sec->auth_msql_uname_field, + esc_user + ); + + return do_msql_query(r,query,sec,ONLY_ONCE,msql_errstr); +} + +char *get_msql_grp(request_rec *r, char *group,char *user, msql_auth_config_rec *sec, char *msql_errstr) { + char query[MAX_QUERY_LEN]; + + char esc_user[MAX_FIELD_LEN]; + char esc_group[MAX_FIELD_LEN]; + + /* do we have enough information to build a query */ + if ( + (!sec->auth_msql_grp_table) || + (!sec->auth_msql_grp_field) || + (!sec->auth_msql_uname_field) + ) { + sprintf(msql_errstr, + "mSQL: Missing parameters for group lookup: %s%s%s", + (sec->auth_msql_grp_table ? "" : "Group table "), + (sec->auth_msql_grp_field ? "" : "GroupID field name "), + (sec->auth_msql_uname_field ? "" : "UserID field name ") + ); + return NULL; + }; + + if (!(msql_escape(esc_user, user,msql_errstr))) { + sprintf(msql_errstr, + "mSQL: Could not cope/escape the '%s' user_id value",user); + + return NULL; + }; + if (!(msql_escape(esc_group, group,msql_errstr))) { + sprintf(msql_errstr, + "mSQL: Could not cope/escape the '%s' group_id value",group); + + return NULL; + }; + + sprintf(query,"select %s from %s where %s='%s' and %s='%s'", + sec->auth_msql_grp_field, + sec->auth_msql_grp_table, + sec->auth_msql_uname_field,esc_user, + sec->auth_msql_grp_field, esc_group + ); + + return do_msql_query(r,query,sec,0,msql_errstr); +} + + +int msql_authenticate_basic_user (request_rec *r) +{ + msql_auth_config_rec *sec = + (msql_auth_config_rec *)get_module_config (r->per_dir_config, + &msql_auth_module); + char msql_errstr[MAX_STRING_LEN]; + conn_rec *c = r->connection; + char *sent_pw, *real_pw; + int res; + msql_errstr[0]='\0'; + + if ((res = get_basic_auth_pw (r, &sent_pw))) + return res; + + /* if mSQL *password* checking is configured in any way, i.e. then + * handle it, if not decline and leave it to the next in line.. + * We do not check on dbase, group, userid or host name, as it is + * perfectly possible to only do group control with mSQL and leave + * user control to the next (dbm) guy in line. + */ + if ( + (!sec->auth_msql_pwd_table) && + (!sec->auth_msql_pwd_field) + ) return DECLINED; + + if(!(real_pw = get_msql_pw(r, c->user, sec,msql_errstr ))) { + if ( msql_errstr[0] ) { + res = SERVER_ERROR; + } else { + if (sec->auth_msql_authorative) { + /* insist that the user is in the database + */ + sprintf(msql_errstr,"mSQL: Password for user %s not found", c->user); + note_basic_auth_failure (r); + res = AUTH_REQUIRED; + } else { + /* pass control on to the next authorization module. + */ + return DECLINED; + }; /* if authorative */ + }; /* if no error */ + log_reason (msql_errstr, r->filename, r); + return res; + } + + /* allow no password, if the flag is set and the password + * is empty. But be sure to log this. + */ + + if ((sec->auth_msql_nopasswd) && (!strlen(real_pw))) { + sprintf(msql_errstr,"mSQL: user %s: Empty/'any' password accepted",c->user); + log_reason (msql_errstr, r->uri, r); + return OK; + }; + + /* if the flag is off however, keep that kind of stuff at + * an arms length. + */ + if ((!strlen(real_pw)) || (!strlen(sent_pw))) { + sprintf(msql_errstr,"mSQL: user %s: Empty Password(s) Rejected",c->user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + }; + + if(sec->auth_msql_encrypted) { + /* anyone know where the prototype for crypt is? + * + * PLEASE NOTE: + * The crypt function (at least under FreeBSD 2.0.5) returns + * a ptr to a *static* array (max 120 chars) and does *not* + * modify the string pointed at by sent_pw ! + */ + sent_pw=(char *)crypt(sent_pw,real_pw); + }; + + if (strcmp(real_pw,sent_pw)) { + sprintf(msql_errstr,"mSQL user %s: password mismatch",c->user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + return OK; +} + +/* Checking ID */ + +int msql_check_auth (request_rec *r) { + int user_result=DECLINED,group_result=DECLINED; + + msql_auth_config_rec *sec = + (msql_auth_config_rec *)get_module_config (r->per_dir_config, + &msql_auth_module); + char msql_errstr[MAX_STRING_LEN]; + char *user = r->connection->user; + int m = r->method_number; + array_header *reqs_arr = requires (r); + require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL; + + register int x; + char *t, *w; + msql_errstr[0]='\0'; + + if (!reqs_arr) { + if (sec->auth_msql_authorative) { + sprintf(msql_errstr,"user %s denied, no access rules specified (MSQL-Authorative) ",user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + }; + return DECLINED; + }; + + for(x=0; (x < reqs_arr->nelts) ; x++) { + + if (! (reqs[x].method_mask & (1 << m))) continue; + + t = reqs[x].requirement; + w = getword(r->pool, &t, ' '); + + if ((user_result != OK) && (!strcmp(w,"user"))) { + user_result=AUTH_REQUIRED; + while(t[0]) { + w = getword_conf (r->pool, &t); + if (!strcmp(user,w)) { + user_result= OK; + break; + }; + } + if ((sec->auth_msql_authorative) && ( user_result != OK)) { + sprintf(msql_errstr,"User %s not found (MSQL-Auhtorative)",user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + }; + } + + if ( (group_result != OK) && + (!strcmp(w,"group")) && + (sec->auth_msql_grp_table) && + (sec->auth_msql_grp_field) + ) { + /* look up the membership for each of the groups in the table + */ + group_result=AUTH_REQUIRED; + while ( (t[0]) && (group_result != OK) && (!msql_errstr[0]) ) { + if (get_msql_grp(r,getword(r->pool, &t, ' '),user,sec,msql_errstr)) { + group_result= OK; + break; + }; + }; + + if (msql_errstr[0]) { + log_reason (msql_errstr, r->filename, r); + return SERVER_ERROR; + }; + + if ( (sec->auth_msql_authorative) && (group_result != OK) ) { + sprintf(msql_errstr,"user %s not in right groups (MSQL-Authorative) ",user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + }; + }; + + if(!strcmp(w,"valid-user")) { + user_result= OK; + }; + } + + /* we do not have to check the valid-ness of the group result as + * have not (yet) a 'valid-group' token + */ + if ( (user_result != OK) && (sec->auth_msql_authorative) ) { + sprintf(msql_errstr,"User %s denied, no access rules applied (MSQL-Authorative) ",user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + }; + + + /* if the user is DECLINED, it is up to the group_result to tip + * the balance. But if the group result is AUTH_REQUIRED it should + * always override. A SERVER_ERROR should not get here. + */ + if ( (user_result == DECLINED) || (group_result == AUTH_REQUIRED)) + return group_result; + + return user_result; +} + + +module msql_auth_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_msql_auth_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + msql_auth_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + msql_authenticate_basic_user,/* check_user_id */ + msql_check_auth, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* pre-run fixups */ + NULL /* logger */ +}; + diff --git a/RELEASE_1_1_X/src/modules/standard/mod_autoindex.c b/RELEASE_1_1_X/src/modules/standard/mod_autoindex.c new file mode 100644 index 00000000000..c30b57c58d3 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_autoindex.c @@ -0,0 +1,846 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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_dir.c: Handles the on-the-fly html index generation + * + * Rob McCool + * 3/23/93 + * + * Adapted to Shambhala by rst. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" + +module dir_module; + +/**************************************************************** + * + * Handling configuration directives... + */ + +#define FANCY_INDEXING 1 /* Indexing options */ +#define ICONS_ARE_LINKS 2 +#define SCAN_HTML_TITLES 4 +#define SUPPRESS_LAST_MOD 8 +#define SUPPRESS_SIZE 16 +#define SUPPRESS_DESC 32 + +struct item { + char *type; + char *apply_to; + char *apply_path; + char *data; +}; + +typedef struct dir_config_struct { + + char *default_icon; + char *index_names; + + array_header *icon_list, *alt_list, *desc_list, *ign_list; + array_header *hdr_list, *rdme_list, *opts_list; + +} dir_config_rec; + +char c_by_encoding, c_by_type, c_by_path; + +#define BY_ENCODING &c_by_encoding +#define BY_TYPE &c_by_type +#define BY_PATH &c_by_path + +void push_item(array_header *arr, char *type, char *to, char *path, char *data) +{ + struct item *p = (struct item *)push_array(arr); + + if (!to) to = ""; + if (!path) path = ""; + + p->type = type; + p->data = data ? pstrdup(arr->pool, data): NULL; + p->apply_path = pstrcat(arr->pool, path, "*", NULL); + + if((type == BY_PATH) && (!is_matchexp(to))) + p->apply_to = pstrcat (arr->pool, "*", to, NULL); + else if (to) + p->apply_to = pstrdup (arr->pool, to); + else + p->apply_to = NULL; +} + +char *add_alt(cmd_parms *cmd, void *d, char *alt, char *to) +{ + if (cmd->info == BY_PATH) + if(!strcmp(to,"**DIRECTORY**")) + to = "^^DIRECTORY^^"; + + push_item(((dir_config_rec *)d)->alt_list, cmd->info, to, cmd->path, alt); + return NULL; +} + +char *add_icon(cmd_parms *cmd, void *d, char *icon, char *to) +{ + char *iconbak = pstrdup (cmd->pool, icon); + + if(icon[0] == '(') { + char *alt = getword (cmd->pool, &iconbak, ','); + iconbak[strlen(iconbak) - 1] = '\0'; /* Lose closing paren */ + add_alt(cmd, d, &alt[1], to); + } + if(cmd->info == BY_PATH) + if(!strcmp(to,"**DIRECTORY**")) + to = "^^DIRECTORY^^"; + + push_item(((dir_config_rec *)d)->icon_list, cmd->info, to, cmd->path, + iconbak); + return NULL; +} + +char *add_desc(cmd_parms *cmd, void *d, char *desc, char *to) +{ + push_item(((dir_config_rec *)d)->desc_list, cmd->info, to, cmd->path,desc); + return NULL; +} + +char *add_ignore(cmd_parms *cmd, void *d, char *ext) { + push_item(((dir_config_rec *)d)->ign_list, 0, ext, cmd->path, NULL); + return NULL; +} + +char *add_header(cmd_parms *cmd, void *d, char *name) { + push_item(((dir_config_rec *)d)->hdr_list, 0, NULL, cmd->path, name); + return NULL; +} + +char *add_readme(cmd_parms *cmd, void *d, char *name) { + push_item(((dir_config_rec *)d)->rdme_list, 0, NULL, cmd->path, name); + return NULL; +} + + +char *add_opts_int(cmd_parms *cmd, void *d, int opts) { + push_item(((dir_config_rec *)d)->opts_list, (char*)opts, NULL, + cmd->path, NULL); + return NULL; +} + +char *fancy_indexing (cmd_parms *cmd, void *d, int arg) +{ + return add_opts_int (cmd, d, arg? FANCY_INDEXING : 0); +} + +char *add_opts(cmd_parms *cmd, void *d, char *optstr) { + char *w; + int opts = 0; + + while(optstr[0]) { + w = getword_conf(cmd->pool, &optstr); + if(!strcasecmp(w,"FancyIndexing")) + opts |= FANCY_INDEXING; + else if(!strcasecmp(w,"IconsAreLinks")) + opts |= ICONS_ARE_LINKS; + else if(!strcasecmp(w,"ScanHTMLTitles")) + opts |= SCAN_HTML_TITLES; + else if(!strcasecmp(w,"SuppressLastModified")) + opts |= SUPPRESS_LAST_MOD; + else if(!strcasecmp(w,"SuppressSize")) + opts |= SUPPRESS_SIZE; + else if(!strcasecmp(w,"SuppressDescription")) + opts |= SUPPRESS_DESC; + else if(!strcasecmp(w,"None")) + opts = 0; + else + return "Invalid directory indexing option"; + } + return add_opts_int(cmd, d, opts); +} + +#define DIR_CMD_PERMS OR_INDEXES + +command_rec dir_cmds[] = { +{ "AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more filenames" }, +{ "AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more MIME types" }, +{ "AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more content encodings" }, +{ "AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more filenames" }, +{ "AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more MIME types" }, +{ "AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more content encodings" }, +{ "IndexOptions", add_opts, NULL, DIR_CMD_PERMS, RAW_ARGS, + "one or more index options" }, +{ "IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, ITERATE, + "one or more file extensions" }, +{ "AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "Descriptive text followed by one or more filenames" }, +{ "HeaderName", add_header, NULL, DIR_CMD_PERMS, TAKE1, "a filename" }, +{ "ReadmeName", add_readme, NULL, DIR_CMD_PERMS, TAKE1, "a filename" }, +{ "FancyIndexing", fancy_indexing, NULL, DIR_CMD_PERMS, FLAG, NULL }, +{ "DefaultIcon", set_string_slot, + (void*)XtOffsetOf(dir_config_rec, default_icon), + DIR_CMD_PERMS, TAKE1, "an icon URL"}, +{ "DirectoryIndex", set_string_slot, + (void*)XtOffsetOf(dir_config_rec, index_names), + DIR_CMD_PERMS, RAW_ARGS, NULL }, +{ NULL } +}; + +void *create_dir_config (pool *p, char *dummy) +{ + dir_config_rec *new = + (dir_config_rec *) pcalloc (p, sizeof(dir_config_rec)); + + new->index_names = NULL; + new->icon_list = make_array (p, 4, sizeof (struct item)); + new->alt_list = make_array (p, 4, sizeof (struct item)); + new->desc_list = make_array (p, 4, sizeof (struct item)); + new->ign_list = make_array (p, 4, sizeof (struct item)); + new->hdr_list = make_array (p, 4, sizeof (struct item)); + new->rdme_list = make_array (p, 4, sizeof (struct item)); + new->opts_list = make_array (p, 4, sizeof (struct item)); + + return (void *)new; +} + +void *merge_dir_configs (pool *p, void *basev, void *addv) +{ + dir_config_rec *new=(dir_config_rec*)pcalloc (p, sizeof(dir_config_rec)); + dir_config_rec *base = (dir_config_rec *)basev; + dir_config_rec *add = (dir_config_rec *)addv; + + new->default_icon = add->default_icon?add->default_icon:base->default_icon; + new->index_names = add->index_names? add->index_names: base->index_names; + + new->alt_list = append_arrays (p, add->alt_list, base->alt_list); + new->ign_list = append_arrays (p, add->ign_list, base->ign_list); + new->hdr_list = append_arrays (p, add->hdr_list, base->hdr_list); + new->desc_list = append_arrays (p, add->desc_list, base->desc_list); + new->icon_list = append_arrays (p, add->icon_list, base->icon_list); + new->rdme_list = append_arrays (p, add->rdme_list, base->rdme_list); + new->opts_list = append_arrays (p, add->opts_list, base->opts_list); + + return new; +} + +/**************************************************************** + * + * Looking things up in config entries... + */ + +/* Structure used to hold entries when we're actually building an index */ + +struct ent { + char *name; + char *icon; + char *alt; + char *desc; + size_t size; + time_t lm; + struct ent *next; +}; + +char *find_item(request_rec *r, array_header *list, int path_only) { + char *content_type = r->content_type; + char *content_encoding = r->content_encoding; + char *path = r->filename; + + struct item *items = (struct item *)list->elts; + int i; + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + + /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */ + if((path[0] == '^') || (!strcmp_match(path,p->apply_path))) { + if(!*(p->apply_to)) + return p->data; + else if(p->type == BY_PATH || path[0] == '^') { + if(!strcmp_match(path,p->apply_to)) + return p->data; + } else if(!path_only) { + if(!content_encoding) { + if(p->type == BY_TYPE) { + if(content_type && !strcmp_match(content_type,p->apply_to)) + return p->data; + } + } else { + if(p->type == BY_ENCODING) { + if(!strcmp_match(content_encoding,p->apply_to)) + return p->data; + } + } + } + } + } + return NULL; +} + +#define find_icon(d,p,t) find_item(p,d->icon_list,t) +#define find_alt(d,p,t) find_item(p,d->alt_list,t) +#define find_desc(d,p) find_item(p,d->desc_list,0) +#define find_header(d,p) find_item(p,d->hdr_list,0) +#define find_readme(d,p) find_item(p,d->rdme_list,0) + +char *find_default_icon (dir_config_rec *d, char *bogus_name) +{ + request_rec r; + + /* Bleah. I tried to clean up find_item, and it lead to this bit + * of ugliness. Note that the fields initialized are precisely + * those that find_item looks at... + */ + + r.filename = bogus_name; + r.content_type = r.content_encoding = NULL; + + return find_item (&r, d->icon_list, 1); +} + +int ignore_entry(dir_config_rec *d, char *path) { + array_header *list = d->ign_list; + struct item *items = (struct item *)list->elts; + char *tt; + int i; + + if((tt=strrchr(path,'/')) == NULL) + tt=path; + else { + tt++; + } + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + char *ap; + + if((ap=strrchr(p->apply_to,'/')) == NULL) + ap=p->apply_to; + else + ap++; + + if(!strcmp_match(path,p->apply_path) && !strcmp_match(tt,ap)) + return 1; + } + return 0; +} + +int find_opts(dir_config_rec *d, request_rec *r) { + char *path = r->filename; + array_header *list = d->opts_list; + struct item *items = (struct item *)list->elts; + int i; + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + + if(!strcmp_match(path,p->apply_path)) + return (int)p->type; + } + return 0; +} + +/***************************************************************** + * + * Actually generating output + */ + + +int insert_readme(char *name, char *readme_fname, int rule, request_rec *r) { + char *fn; + FILE *f; + struct stat finfo; + int plaintext=0; + + fn = make_full_path(r->pool, name, readme_fname); + fn = pstrcat(r->pool, fn, ".html", NULL); + if(stat(fn,&finfo) == -1) { + /* A brief fake multiviews search for README.html */ + fn[strlen(fn)-5] = '\0'; + if(stat(fn,&finfo) == -1) + return 0; + plaintext=1; + if(rule) rputs("


\n", r); + rputs("
\n", r);
+    }
+    else if (rule) rputs("
\n", r); + if(!(f = pfopen(r->pool,fn,"r"))) + return 0; + if (!plaintext) + send_fd(f, r); + else + { + char buf[IOBUFSIZE+1]; + int i, n, c, ch; + while (!feof(f)) + { + do n = fread(buf, sizeof(char), IOBUFSIZE, f); + while (n == -1 && ferror(f) && errno == EINTR); + if (n == -1 || n == 0) break; + buf[n] = '\0'; + c = 0; + while (c < n) + { + for (i=c; i < n; i++) + if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') break; + ch = buf[i]; + buf[i] = '\0'; + rputs(&buf[c], r); + if (ch == '<') rputs("<", r); + else if (ch == '>') rputs(">", r); + else if (ch == '&') rputs("&", r); + c = i + 1; + } + } + } + pfclose(r->pool, f); + if(plaintext) + rputs("
\n", r); + return 1; +} + + +char *find_title(request_rec *r) { + char titlebuf[MAX_STRING_LEN], *find = ""; + FILE *thefile = NULL; + int x,y,n,p; + + if (r->content_type && !strcmp(r->content_type,"text/html") && !r->content_encoding) { + if(!(thefile = pfopen(r->pool, r->filename,"r"))) + return NULL; + n = fread(titlebuf,sizeof(char),MAX_STRING_LEN - 1,thefile); + titlebuf[n] = '\0'; + for(x=0,p=0;titlebuf[x];x++) { + if(toupper(titlebuf[x]) == find[p]) { + if(!find[++p]) { + if((p = ind(&titlebuf[++x],'<')) != -1) + titlebuf[x+p] = '\0'; + /* Scan for line breaks for Tanmoy's secretary */ + for(y=x;titlebuf[y];y++) + if((titlebuf[y] == CR) || (titlebuf[y] == LF)) + titlebuf[y] = ' '; + pfclose (r->pool, thefile); + return pstrdup(r->pool, &titlebuf[x]); + } + } else p=0; + } + pfclose(r->pool, thefile); + } + return NULL; +} + +struct ent *make_dir_entry(char *name, int dir_opts, + dir_config_rec *d, request_rec *r) +{ + struct ent *p; + + if((name[0] == '.') && (!name[1])) + return(NULL); + + if (ignore_entry(d, make_full_path (r->pool, r->filename, name))) + return(NULL); + + p=(struct ent *)pcalloc(r->pool, sizeof(struct ent)); + p->name = pstrdup (r->pool, name); + p->size = -1; + p->icon = NULL; + p->alt = NULL; + p->desc = NULL; + p->lm = -1; + + if(dir_opts & FANCY_INDEXING) { + request_rec *rr = sub_req_lookup_file (name, r); + + if (rr->finfo.st_mode != 0) { + p->lm = rr->finfo.st_mtime; + if(S_ISDIR(rr->finfo.st_mode)) { + if(!(p->icon = find_icon(d,rr,1))) + p->icon = find_default_icon(d,"^^DIRECTORY^^"); + if(!(p->alt = find_alt(d,rr,1))) + p->alt = "DIR"; + p->size = -1; + p->name = pstrcat (r->pool, name, "/", NULL); + } + else { + p->icon = find_icon(d, rr, 0); + p->alt = find_alt(d, rr, 0); + p->size = rr->finfo.st_size; + } + } + + p->desc = find_desc(d, rr); + + if((!p->desc) && (dir_opts & SCAN_HTML_TITLES)) + p->desc = pstrdup (r->pool, find_title(rr)); + + destroy_sub_req (rr); + } + return(p); +} + +char *terminate_description(dir_config_rec *d, char *desc, int dir_opts) { + int maxsize = 23; + register int x; + + if(dir_opts & SUPPRESS_LAST_MOD) maxsize += 17; + if(dir_opts & SUPPRESS_SIZE) maxsize += 7; + + for(x=0;desc[x] && maxsize;x++) { + if(desc[x] == '<') { + while(desc[x] != '>') { + if(!desc[x]) { + maxsize = 0; + break; + } + ++x; + } + } + else --maxsize; + } + if(!maxsize) { + desc[x-1] = '>'; /* Grump. */ + desc[x] = '\0'; /* Double Grump! */ + } + return desc; +} + +void output_directories(struct ent **ar, int n, + dir_config_rec *d, request_rec *r, int dir_opts) +{ + int x, len; + char *name = r->uri; + char *tp; + pool *scratch = make_sub_pool (r->pool); + + if(name[0] == '\0') name = "/"; + + if(dir_opts & FANCY_INDEXING) { + rputs("<PRE>", r); + if((tp = find_default_icon(d,"^^BLANKICON^^"))) + rvputs(r, "<IMG SRC=\"", escape_html(scratch, tp), + "\" ALT=\" \"> ", NULL); + rputs("Name ", r); + if(!(dir_opts & SUPPRESS_LAST_MOD)) + rputs("Last modified ", r); + if(!(dir_opts & SUPPRESS_SIZE)) + rputs("Size ", r); + if(!(dir_opts & SUPPRESS_DESC)) + rputs("Description", r); + rputs("\n<HR>\n", r); + } + else { + rputs("<UL>", r); + } + + for(x=0;x<n;x++) { + char *anchor = NULL, *t = NULL, *t2 = NULL; + + clear_pool (scratch); + + if((!strcmp(ar[x]->name,"../")) || (!strcmp(ar[x]->name,".."))) { + char *t = make_full_path (scratch, name, "../"); + getparents(t); + if(t[0] == '\0') t = "/"; + anchor = pstrcat (scratch, "<A HREF=\"", + escape_html(scratch, os_escape_path(scratch, t, 0)), + "\">", NULL); + t2 = "Parent Directory</A> "; + } + else { + t = ar[x]->name; + len = strlen(t); + if(len > 23) { + t2 = pstrdup(scratch, t); + t2[21] = '.'; + t2[22] = '.'; + t2[23] = '\0'; + t2 = escape_html(scratch, t2); + t2 = pstrcat(scratch, t2, "</A>", NULL); + } else + { + char buff[24]=" "; + t2 = escape_html(scratch, t); + buff[23-len] = '\0'; + t2 = pstrcat(scratch, t2, "</A>", buff, NULL); + } + anchor = pstrcat (scratch, "<A HREF=\"", + escape_html(scratch, os_escape_path(scratch, t, 0)), + "\">", NULL); + } + + if(dir_opts & FANCY_INDEXING) { + if(dir_opts & ICONS_ARE_LINKS) + rputs(anchor, r); + if((ar[x]->icon) || d->default_icon) { + rvputs(r, "<IMG SRC=\"", + escape_html(scratch, ar[x]->icon ? + ar[x]->icon : d->default_icon), + "\" ALT=\"[", (ar[x]->alt ? ar[x]->alt : " "), + "]\">", NULL); + } + if(dir_opts & ICONS_ARE_LINKS) + rputs("</A>", r); + + rvputs(r," ", anchor, t2, NULL); + if(!(dir_opts & SUPPRESS_LAST_MOD)) { + if(ar[x]->lm != -1) { + char time[MAX_STRING_LEN]; + struct tm *ts = localtime(&ar[x]->lm); + strftime(time,MAX_STRING_LEN,"%d-%b-%y %H:%M ",ts); + rputs(time, r); + } + else { + rputs(" ", r); + } + } + if(!(dir_opts & SUPPRESS_SIZE)) { + send_size(ar[x]->size,r); + rputs(" ", r); + } + if(!(dir_opts & SUPPRESS_DESC)) { + if(ar[x]->desc) { + rputs(terminate_description(d, ar[x]->desc, dir_opts), r); + } + } + } + else + rvputs(r, "<LI> ", anchor," ", t2, NULL); + rputc('\n', r); + } + if(dir_opts & FANCY_INDEXING) { + rputs("</PRE>", r); + } + else { + rputs("</UL>", r); + } +} + + +int dsortf(struct ent **s1,struct ent **s2) +{ + return(strcmp((*s1)->name,(*s2)->name)); +} + + +int index_directory(request_rec *r, dir_config_rec *dir_conf) +{ + char *title_name = escape_html(r->pool, r->uri); + char *title_endp; + char *name = r->filename; + + DIR *d; + struct DIR_TYPE *dstruct; + int num_ent=0,x; + struct ent *head,*p; + struct ent **ar; + char *tmp; + int dir_opts = find_opts(dir_conf, r); + + if(!(d=opendir(name))) return FORBIDDEN; + + r->content_type = "text/html"; + + soft_timeout ("send directory", r); + send_http_header(r); + + if (r->header_only) { + closedir (d); + return 0; + } + + /* Spew HTML preamble */ + + title_endp = title_name + strlen(title_name) - 1; + + while (title_endp > title_name && *title_endp == '/') + *title_endp-- = '\0'; + + rvputs(r, "<HEAD><TITLE>Index of ", title_name, "\n", + NULL); + + if((!(tmp = find_header(dir_conf,r))) || (!(insert_readme(name,tmp,0,r)))) + rvputs(r, "

Index of ", title_name, "

\n", NULL); + + /* + * Since we don't know how many dir. entries there are, put them into a + * linked list and then arrayificate them so qsort can use them. + */ + head=NULL; + while((dstruct=readdir(d))) { + if((p = make_dir_entry(dstruct->d_name, dir_opts, dir_conf, r))) { + p->next=head; + head=p; + num_ent++; + } + } + ar=(struct ent **) palloc(r->pool, num_ent*sizeof(struct ent *)); + p=head; + x=0; + while(p) { + ar[x++]=p; + p = p->next; + } + + qsort((void *)ar,num_ent,sizeof(struct ent *), +#ifdef ULTRIX_BRAIN_DEATH + (int (*))dsortf); +#else + (int (*)(const void *,const void *))dsortf); +#endif + output_directories(ar, num_ent, dir_conf, r, dir_opts); + closedir(d); + + if (dir_opts & FANCY_INDEXING) + if((tmp = find_readme(dir_conf, r))) + insert_readme(name,tmp,1,r); + else { + rputs("", r); + } + + rputs("", r); + return 0; +} + +/* The formal handler... */ + +int handle_dir (request_rec *r) +{ + dir_config_rec *d = + (dir_config_rec *)get_module_config (r->per_dir_config, &dir_module); + char *names_ptr = d->index_names ? d->index_names : DEFAULT_INDEX; + int allow_opts = allow_options (r); + + if (r->uri[0] == '\0' || r->uri[strlen(r->uri)-1] != '/') { + char* ifile; + if (r->args != NULL) + ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri), + "/", "?", r->args, NULL); + else + ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri), + "/", NULL); + + table_set (r->headers_out, "Location", + construct_url(r->pool, ifile, r->server)); + return REDIRECT; + } + + /* KLUDGE --- make the sub_req lookups happen in the right directory. + * Fixing this in the sub_req_lookup functions themselves is difficult, + * and would probably break virtual includes... + */ + + r->filename = pstrcat (r->pool, r->filename, "/", NULL); + + while (*names_ptr) { + + char *name_ptr = getword_conf (r->pool, &names_ptr); + request_rec *rr = sub_req_lookup_uri (name_ptr, r); + + if (rr->status == 200 && rr->finfo.st_mode != 0) { + char* new_uri = escape_uri(r->pool, rr->uri); + + if (rr->args != NULL) + new_uri = pstrcat(r->pool, new_uri, "?", rr->args, NULL); + else if (r->args != NULL) + new_uri = pstrcat(r->pool, new_uri, "?", r->args, NULL); + + destroy_sub_req (rr); + internal_redirect (new_uri, r); + return OK; + } + + destroy_sub_req (rr); + } + + if (r->method_number != M_GET) return NOT_IMPLEMENTED; + + /* OK, nothing easy. Trot out the heavy artillery... */ + + if (allow_opts & OPT_INDEXES) + return index_directory (r, d); + else + return FORBIDDEN; +} + + +handler_rec dir_handlers[] = { +{ DIR_MAGIC_TYPE, handle_dir }, +{ NULL } +}; + +module dir_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_dir_config, /* dir config creater */ + merge_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + dir_cmds, /* command table */ + dir_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_cern_meta.c b/RELEASE_1_1_X/src/modules/standard/mod_cern_meta.c new file mode 100644 index 00000000000..c16bde4e63d --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_cern_meta.c @@ -0,0 +1,330 @@ +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_cern_meta.c + * version 0.0.5 + * status beta + * + * Andrew Wilson 25.Jan.96 + * + * Emulate the CERN HTTPD Meta file semantics. Meta files are HTTP + * headers that can be output in addition to the normal range of + * headers for each file accessed. They appear rather like the Apache + * .asis files, and are able to provide a crude way of influencing + * the Expires: header, as well as providing other curiosities. + * There are many ways to manage meta information, this one was + * chosen because there is already a large number of CERN users + * who can exploit this module. It should be noted that there are probably + * more sensitive ways of managing the Expires: header specifically. + * + * The module obeys the following directives, which can only appear + * in the server's .conf files and not in any .htaccess file. + * + * MetaDir + * + * specifies the name of the directory in which Apache can find + * meta information files. The directory is usually a 'hidden' + * subdirectory of the directory that contains the file being + * accessed. eg: + * + * # .meta files are in the *same* directory as the + * # file being accessed + * MetaDir . + * + * the default is to look in a '.web' subdirectory. This is the + * same as for CERN 3.+ webservers and behaviour is the same as + * for the directive: + * + * MetaDir .web + * + * MetaSuffix + * + * specifies the file name suffix for the file containing the + * meta information. eg: + * + * # our meta files are suffixed with '.cern_meta' + * MetaSuffix .cern_meta + * + * the default is to look for files with the suffix '.meta'. This + * behaviour is the same as for the directive: + * + * MetaSuffix .meta + * + * When accessing the file + * + * DOCUMENT_ROOT/somedir/index.html + * + * this module will look for the file + * + * DOCUMENT_ROOT/somedir/.web/index.html.meta + * + * and will use its contents to generate additional MIME header + * information. + * + * For more information on the CERN Meta file semantics see: + * + * http://www.w3.org/hypertext/WWW/Daemon/User/Config/General.html#MetaDir + * + * Change-log: + * 29.Jan.96 pfopen/pfclose instead of fopen/fclose + * DECLINE when real file not found, we may be checking each + * of the index.html/index.shtml/index.htm variants and don't + * need to report missing ones as spurious errors. + * 31.Jan.96 log_error reports about a malformed .meta file, rather + * than a script error. + * + */ + +#include "httpd.h" +#include "http_config.h" +#include +#include +#include "util_script.h" +#include "http_log.h" + +#define DEFAULT_METADIR ".web" +#define DEFAULT_METASUFFIX ".meta" + +module cern_meta_module; + +typedef struct { + char *metadir; + char *metasuffix; +} cern_meta_config; + +void *create_cern_meta_config (pool *p, server_rec *dummy) +{ + cern_meta_config *new = + (cern_meta_config *) palloc (p, sizeof(cern_meta_config)); + + new->metadir = DEFAULT_METADIR; + new->metasuffix = DEFAULT_METASUFFIX; + + return new; +} + +char *set_metadir (cmd_parms *parms, void *dummy, char *arg) +{ + cern_meta_config *cmc ; + + cmc = get_module_config (parms->server->module_config, + &cern_meta_module); + cmc->metadir = arg; + return NULL; +} + +char *set_metasuffix (cmd_parms *parms, void *dummy, char *arg) +{ + cern_meta_config *cmc ; + + cmc = get_module_config (parms->server->module_config, + &cern_meta_module); + cmc->metasuffix = arg; + return NULL; +} + +command_rec cern_meta_cmds[] = { +{ "MetaDir", set_metadir, NULL, RSRC_CONF, TAKE1, + "the name of the directory containing meta files"}, +{ "MetaSuffix", set_metasuffix, NULL, RSRC_CONF, TAKE1, + "the filename suffix for meta files"}, +{ NULL } +}; + +int scan_meta_file(request_rec *r, FILE *f) +{ + char w[MAX_STRING_LEN]; + char *l; + int p; + + while( fgets(w, MAX_STRING_LEN-1, f) != NULL ) { + + /* Delete terminal (CR?)LF */ + + p = strlen(w); + if (p > 0 && w[p-1] == '\n') + { + if (p > 1 && w[p-2] == '\015') w[p-2] = '\0'; + else w[p-1] = '\0'; + } + + if(w[0] == '\0') { + return OK; + } + + /* if we see a bogus header don't ignore it. Shout and scream */ + + if(!(l = strchr(w,':'))) { + log_reason ("malformed header in meta file", r->filename, r); + return SERVER_ERROR; + } + + *l++ = '\0'; + while (*l && isspace (*l)) ++l; + + if(!strcasecmp(w,"Content-type")) { + + /* Nuke trailing whitespace */ + + char *endp = l + strlen(l) - 1; + while (endp > l && isspace(*endp)) *endp-- = '\0'; + + r->content_type = pstrdup (r->pool, l); + } + else if(!strcasecmp(w,"Status")) { + sscanf(l, "%d", &r->status); + r->status_line = pstrdup(r->pool, l); + } + else { + table_set (r->headers_out, w, l); + } + } + return OK; +} + +int add_cern_meta_data(request_rec *r) +{ + char *metafilename; + char *last_slash; + char *real_file; + char *scrap_book; + struct stat meta_stat; + FILE *f; + cern_meta_config *cmc ; + int rv; + + cmc = get_module_config (r->server->module_config, + &cern_meta_module); + + /* if ./.web/$1.meta exists then output 'asis' */ + + if (r->finfo.st_mode == 0) { + return DECLINED; + }; + + /* does uri end in a trailing slash? */ + if ( r->uri[strlen(r->uri) - 1] == '/' ) { + return DECLINED; + }; + + /* what directory is this file in? */ + scrap_book = pstrdup( r->pool, r->filename ); + /* skip leading slash, recovered in later processing */ + scrap_book++; + last_slash = strrchr( scrap_book, '/' ); + if ( last_slash != NULL ) { + /* skip over last slash */ + real_file = last_slash; + real_file++; + *last_slash = '\0'; + } else { + /* no last slash, buh?! */ + log_reason("internal error in mod_cern_meta", r->filename, r); + /* should really barf, but hey, let's be friends... */ + return DECLINED; + }; + + metafilename = pstrcat(r->pool, "/", scrap_book, "/", cmc->metadir, "/", real_file, cmc->metasuffix, NULL); + + /* + * stat can legitimately fail for a bewildering number of reasons, + * only one of which implies the file isn't there. A hardened + * version of this module should test for all conditions, but later... + */ + if (stat(metafilename, &meta_stat) == -1) { + /* stat failed, possibly file missing */ + return DECLINED; + }; + + /* + * this check is to be found in other Jan/96 Apache code, I've + * not been able to find any corroboration in the man pages but + * I've been wrong before so I'll put it in anyway. Never + * admit to being clueless... + */ + if ( meta_stat.st_mode == 0 ) { + /* stat failed, definately file missing */ + return DECLINED; + }; + + f = pfopen (r->pool, metafilename, "r"); + + if (f == NULL) { + log_reason("meta file permissions deny server access", metafilename, r); + return FORBIDDEN; + }; + + /* read the headers in */ + rv = scan_meta_file(r, f); + pfclose( r->pool, f ); + + return rv; +} + +module cern_meta_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_cern_meta_config, /* server config */ + NULL, /* merge server configs */ + cern_meta_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + add_cern_meta_data, /* fixups */ + NULL, /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_cgi.c b/RELEASE_1_1_X/src/modules/standard/mod_cgi.c new file mode 100644 index 00000000000..0a869427e0e --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_cgi.c @@ -0,0 +1,412 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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_script: keeps all script-related ramblings together. + * + * Compliant to CGI/1.1 spec + * + * Adapted by rst from original NCSA code by Rob McCool + * + * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for + * custom error responses, and DOCUMENT_ROOT because we found it useful. + * It also adds SERVER_ADMIN - useful for scripts to know who to mail when + * they fail. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#include "util_script.h" + +/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI + * in ScriptAliased directories, which means we need to know if this + * request came through ScriptAlias or not... so the Alias module + * leaves a note for us. + */ + +int is_scriptaliased (request_rec *r) +{ + char *t = table_get (r->notes, "alias-forced-type"); + return t && (!strcmp (t, "cgi-script")); +} + +/**************************************************************** + * + * Actual CGI handling... + */ + + +struct cgi_child_stuff { + request_rec *r; + int nph; + char *argv0; +}; + +void cgi_child (void *child_stuff) +{ + struct cgi_child_stuff *cld = (struct cgi_child_stuff *)child_stuff; + request_rec *r = cld->r; + char *argv0 = cld->argv0; + int nph = cld->nph; + +#ifdef DEBUG_CGI +#ifdef __EMX__ + /* Under OS/2 need to use device con. */ + FILE *dbg = fopen ("con", "w"); +#else + FILE *dbg = fopen ("/dev/tty", "w"); +#endif + int i; +#endif + + char **env; + char err_string[HUGE_STRING_LEN]; + +#ifdef DEBUG_CGI + fprintf (dbg, "Attempting to exec %s as %sCGI child (argv0 = %s)\n", + r->filename, nph ? "NPH " : "", argv0); +#endif + + add_cgi_vars (r); + env = create_environment (r->pool, r->subprocess_env); + +#ifdef DEBUG_CGI + fprintf (dbg, "Environment: \n"); + for (i = 0; env[i]; ++i) fprintf (dbg, "'%s'\n", env[i]); +#endif + + chdir_file (r->filename); + error_log2stderr (r->server); + +#ifndef __EMX__ + if (nph) client_to_stdout (r->connection); +#endif + + /* Transumute outselves into the script. + * NB only ISINDEX scripts get decoded arguments. + */ + + cleanup_for_exec(); + +#ifdef __EMX__ + if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0)) { + int emxloop; + char *emxtemp; + + /* For OS/2 place the variables in the current + enviornment then it will be inherited. This way + the program will also get all of OS/2's other SETs. */ + for (emxloop=0; ((emxtemp = env[emxloop]) != NULL); emxloop++) + putenv(emxtemp); + + if (strstr(strupr(r->filename), ".CMD") > 0) { + /* Special case to allow use of REXX commands as scripts. */ + os2pathname(r->filename); + execl("CMD.EXE", "CMD.EXE", "/C", r->filename, NULL); + } else { + execl(r->filename, argv0, NULL); + } + } else { + int emxloop; + char *emxtemp; + + /* For OS/2 place the variables in the current + enviornment then it will be inherited. This way + the program will also get all of OS/2's other SETs. */ + for (emxloop=0; ((emxtemp = env[emxloop]) != NULL); emxloop++) + putenv(emxtemp); + + if (strstr(strupr(r->filename), ".CMD") > 0) { + /* Special case to allow use of REXX commands as scripts. */ + os2pathname(r->filename); + execv("CMD.EXE", create_argv_cmd(r->pool, argv0, r->args, r->filename)); + } else { + execv(r->filename, create_argv(r->pool, argv0, r->args)); + } + } +#else + if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0)) + execle(r->filename, argv0, NULL, env); + else + execve(r->filename, create_argv(r->pool, argv0, r->args), env); +#endif + + /* Uh oh. Still here. Where's the kaboom? There was supposed to be an + * EARTH-shattering kaboom! + * + * Oh, well. Muddle through as best we can... + * + * (NB we can't use log_error, or anything like that, because we + * just closed the file descriptor which r->server->error_log + * was tied to in cleanup_for_exec(). It's only available on stderr + * now, so that's what we use). + */ + + sprintf(err_string, + "exec of %s failed, errno is %d\n", r->filename, errno); + write(2, err_string, strlen(err_string)); + exit(0); +} + +int cgi_handler (request_rec *r) +{ + int nph; + char *argv0; + FILE *script_out, *script_in; + char argsbuffer[HUGE_STRING_LEN]; + int is_included = !strcmp (r->protocol, "INCLUDED"); + char *lenp = table_get (r->headers_in, "Content-length"); + + struct cgi_child_stuff cld; + + if((argv0 = strrchr(r->filename,'/')) != NULL) + argv0++; + else argv0 = r->filename; + + nph = !(strncmp(argv0,"nph-",4)); + + if (!(allow_options (r) & OPT_EXECCGI) && !is_scriptaliased (r)) { + log_reason("Options ExecCGI is off in this directory", r->filename, r); + return FORBIDDEN; + } + if (nph && is_included) { + log_reason("attempt to include NPH CGI script", r->filename, r); + return FORBIDDEN; + } + + if (S_ISDIR(r->finfo.st_mode)) { + log_reason("attempt to invoke directory as script", r->filename, r); + return FORBIDDEN; + } + if (r->finfo.st_mode == 0) { + log_reason("script not found or unable to stat", r->filename, r); + return NOT_FOUND; + } + if(!can_exec(&r->finfo)) { + log_reason("file permissions deny server execution", r->filename, r); + return FORBIDDEN; + } + if ((r->method_number == M_POST || r->method_number == M_PUT) + && !lenp) { + log_reason("POST or PUT without Content-length:", r->filename, r); + return BAD_REQUEST; + } + + add_common_vars (r); + cld.argv0 = argv0; cld.r = r; cld.nph = nph; + +#ifdef __EMX__ + if (r->method_number == M_POST || r->method_number == M_PUT) { + int len_to_read = atoi (lenp); + + if (len_to_read > HUGE_STRING_LEN) len_to_read = HUGE_STRING_LEN; + read_client_block (r, argsbuffer, len_to_read); + + if (!spawn_child_os2 (r->connection->pool, cgi_child, (void *)&cld, + nph ? just_wait : kill_after_timeout, + &script_out, &script_in, argsbuffer, atoi(lenp))) { + log_reason ("couldn't spawn child process", r->filename, r); + return SERVER_ERROR; + } + } else { + if (!spawn_child (r->connection->pool, cgi_child, (void *)&cld, + nph ? just_wait : kill_after_timeout, + &script_out, &script_in)) { + log_reason ("couldn't spawn child process", r->filename, r); + return SERVER_ERROR; + } + } + +#else + if (!spawn_child (r->connection->pool, cgi_child, (void *)&cld, + nph ? just_wait : kill_after_timeout, + &script_out, nph ? NULL : &script_in)) { + log_reason ("couldn't spawn child process", r->filename, r); + return SERVER_ERROR; + } +#endif + + /* Transfer any put/post args, CERN style... + * Note that if a buggy script fails to read everything we throw + * at it, or a buggy client sends too much, we get a SIGPIPE, so + * we have to ignore SIGPIPE while doing this. CERN does the same + * (and in fact, they pretty nearly guarantee themselves a SIGPIPE + * on every invocation by chasing the real client data with a + * spurious newline). + */ + +#ifndef __EMX__ + if (r->method_number == M_POST || r->method_number == M_PUT) { + void (*handler)(); + int remaining = atoi (lenp); + + hard_timeout ("copy script args", r); + handler = signal (SIGPIPE, SIG_IGN); + + while ((remaining > 0)) + { + int len_read, len_to_read = remaining; + + if (len_to_read > HUGE_STRING_LEN) len_to_read = HUGE_STRING_LEN; + + len_read = read_client_block (r, argsbuffer, len_to_read); + if (len_read == 0) + break; + if (fwrite (argsbuffer, 1, len_read, script_out) == 0) + break; + remaining -= len_read; + } + + /* If script stopped reading early, soak up remaining stuff from + * client... + */ + + while (remaining > 0) { + int len_read, len_to_read = remaining; + if (len_to_read > HUGE_STRING_LEN) len_to_read = HUGE_STRING_LEN; + + len_read = read_client_block (r, argsbuffer, len_to_read); + if (len_read == 0) break; + } + + fflush (script_out); + signal (SIGPIPE, handler); + + kill_timeout (r); + } +#endif + + pfclose (r->connection->pool, script_out); + + /* Handle script return... */ + if (script_in && !nph) { + char *location; + int ret; + + if ((ret = scan_script_header(r, script_in))) + return ret; + + location = table_get (r->headers_out, "Location"); + + if (location && location[0] == '/' && r->status == 200) { + + /* Soak up all the script output */ + hard_timeout ("read from script", r); + while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL) + continue; + kill_timeout (r); + + + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = pstrdup(r->pool, "GET"); + r->method_number = M_GET; + + internal_redirect_handler (location, r); + return OK; + } + else if (location && r->status == 200) { + /* XX Note that if a script wants to produce its own Redirect + * body, it now has to explicitly *say* "Status: 302" + */ + return REDIRECT; + } + + hard_timeout ("send script output", r); + send_http_header(r); + if(!r->header_only) send_fd (script_in, r); + kill_timeout (r); + pfclose (r->connection->pool, script_in); + } + +#ifdef __EMX__ + if (nph) { + while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL) { + bputs(argsbuffer, r->connection->client); + } + } +#endif + + return OK; /* NOT r->status, even if it has changed. */ +} + +handler_rec cgi_handlers[] = { +{ CGI_MAGIC_TYPE, cgi_handler }, +{ "cgi-script", cgi_handler }, +{ NULL } +}; + +module cgi_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + NULL, /* command table */ + cgi_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_digest.c b/RELEASE_1_1_X/src/modules/standard/mod_digest.c new file mode 100644 index 00000000000..3ac81be4aff --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_digest.c @@ -0,0 +1,356 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * mod_digest: MD5 digest authentication + * + * by Alexei Kosut + * based on mod_auth, by Rob McCool and Robert S. Thau + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include "util_md5.h" + +typedef struct digest_config_struct { + char *pwfile; +} digest_config_rec; + +typedef struct digest_header_struct { + char *username; + char *realm; + char *nonce; + char *requested_uri; + char *digest; +} digest_header_rec; + +void *create_digest_dir_config (pool *p, char *d) +{ + return pcalloc (p, sizeof(digest_config_rec)); +} + +command_rec digest_cmds[] = { +{ "AuthDigestFile", set_string_slot, + (void*)XtOffsetOf(digest_config_rec,pwfile), OR_AUTHCFG, TAKE1, NULL }, +{ NULL } +}; + +module digest_module; + +char *get_hash(request_rec *r, char *user, char *auth_pwfile) +{ + FILE *f; + char l[MAX_STRING_LEN]; + char *rpw, *w, *x; + + if(!(f=pfopen(r->pool, auth_pwfile, "r"))) { + log_reason ("Could not open password file", auth_pwfile, r); + return NULL; + } + while(!(cfg_getline(l,MAX_STRING_LEN,f))) { + if((l[0] == '#') || (!l[0])) continue; + rpw = l; + w = getword(r->pool, &rpw, ':'); + x = getword(r->pool, &rpw, ':'); + + if(x && w && !strcmp(user,w) && !strcmp(auth_name(r), x)) { + pfclose(r->pool, f); + return pstrdup (r->pool, rpw); + } + } + pfclose(r->pool, f); + return NULL; +} + +/* Parse the Authorization header, if it exists */ + +int get_digest_rec(request_rec *r, digest_header_rec *response) { + char *auth_line = table_get(r->headers_in, "Authorization"); + int l; + int s = 0, vk = 0, vv = 0; + char *t, *key, *value; + + if (!(t = auth_type(r)) || strcasecmp(t, "Digest")) + return DECLINED; + + if (!auth_name (r)) { + log_reason ("need AuthName", r->uri, r); + return SERVER_ERROR; + } + + if (!auth_line) { + note_digest_auth_failure (r); + return AUTH_REQUIRED; + } + + if (strcmp(getword (r->pool, &auth_line, ' '), "Digest")) { + /* Client tried to authenticate using wrong auth scheme */ + log_reason ("client used wrong authentication scheme", r->uri, r); + note_digest_auth_failure (r); + return AUTH_REQUIRED; + } + + l = strlen(auth_line); + + key=palloc(r->pool,l); + value=palloc(r->pool,l); + + /* There's probably a better way to do this, but for the time being... */ + +#define D_KEY 0 +#define D_VALUE 1 +#define D_STRING 2 +#define D_EXIT -1 + + while (s != D_EXIT) { + switch (s) { + case D_STRING: + if (auth_line[0] == '\"') { + s = D_VALUE; + } + else { + value[vv] = auth_line[0]; + vv++; + } + auth_line++; + break; + + case D_VALUE: + if (isalnum(auth_line[0])) { + value[vv] = auth_line[0]; + vv++; + } + else if (auth_line[0] == '\"') { + s = D_STRING; + } + else { + value[vv] = '\0'; + + if (!strcasecmp(key, "username")) + response->username = pstrdup(r->pool, value); + else if (!strcasecmp(key, "realm")) + response->realm = pstrdup(r->pool, value); + else if (!strcasecmp(key, "nonce")) + response->nonce = pstrdup(r->pool, value); + else if (!strcasecmp(key, "uri")) + response->requested_uri = pstrdup(r->pool, value); + else if (!strcasecmp(key, "response")) + response->digest = pstrdup(r->pool, value); + + vv = 0; + s = D_KEY; + } + auth_line++; + break; + + case D_KEY: + if (isalnum(auth_line[0])) { + key[vk] = auth_line[0]; + vk++; + } + else if (auth_line[0] == '=') { + key[vk] = '\0'; + vk = 0; + s = D_VALUE; + } + auth_line++; + break; + } + + if (auth_line[-1] == '\0') + s = D_EXIT; + } + + if (!response->username || !response->realm || !response->nonce || + !response->requested_uri || !response->digest) { + note_digest_auth_failure (r); + return AUTH_REQUIRED; + } + + r->connection->user = response->username; + r->connection->auth_type = "Digest"; + + return OK; +} + +/* The actual MD5 code... whee */ + +char *find_digest(request_rec *r, digest_header_rec *h, char *a1) { + return md5(r->pool, + (unsigned char *)pstrcat(r->pool, a1, ":", h->nonce, ":", + md5(r->pool, + (unsigned char *)pstrcat(r->pool,r->method,":", + h->requested_uri,NULL)), + NULL)); +} + +/* These functions return 0 if client is OK, and proper error status + * if not... either AUTH_REQUIRED, if we made a check, and it failed, or + * SERVER_ERROR, if things are so totally confused that we couldn't + * figure out how to tell if the client is authorized or not. + * + * If they return DECLINED, and all other modules also decline, that's + * treated by the server core as a configuration error, logged and + * reported as such. + */ + +/* Determine user ID, and check if it really is that user, for HTTP + * basic authentication... + */ + +int authenticate_digest_user (request_rec *r) +{ + digest_config_rec *sec = + (digest_config_rec *)get_module_config (r->per_dir_config, + &digest_module); + digest_header_rec *response = pcalloc (r->pool, sizeof(digest_header_rec)); + conn_rec *c = r->connection; + char *a1; + char errstr[MAX_STRING_LEN]; + int res; + + if ((res = get_digest_rec (r, response))) return res; + + if(!sec->pwfile) + return DECLINED; + + if (!(a1 = get_hash(r, c->user, sec->pwfile))) { + sprintf(errstr,"user %s not found",c->user); + log_reason (errstr, r->uri, r); + note_digest_auth_failure (r); + return AUTH_REQUIRED; + } + /* anyone know where the prototype for crypt is? */ + if(strcmp(response->digest, find_digest(r, response, a1))) { + sprintf(errstr,"user %s: password mismatch",c->user); + log_reason (errstr, r->uri, r); + note_digest_auth_failure (r); + return AUTH_REQUIRED; + } + return OK; +} + +/* Checking ID */ + +int digest_check_auth (request_rec *r) { + char *user = r->connection->user; + int m = r->method_number; + int method_restricted = 0; + register int x; + char *t, *w; + array_header *reqs_arr; + require_line *reqs; + + if (!(t = auth_type(r)) || strcasecmp(t, "Digest")) + return DECLINED; + + reqs_arr = requires (r); + /* If there is no "requires" directive, + * then any user will do. + */ + if (!reqs_arr) + return OK; + reqs = (require_line *)reqs_arr->elts; + + for(x=0; x < reqs_arr->nelts; x++) { + + if (! (reqs[x].method_mask & (1 << m))) continue; + + method_restricted = 1; + + t = reqs[x].requirement; + w = getword(r->pool, &t, ' '); + if(!strcmp(w,"valid-user")) + return OK; + else if(!strcmp(w,"user")) { + while(t[0]) { + w = getword_conf (r->pool, &t); + if(!strcmp(user,w)) + return OK; + } + } + else + return DECLINED; + } + + if (!method_restricted) + return OK; + + note_digest_auth_failure(r); + return AUTH_REQUIRED; +} + +module digest_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_digest_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + digest_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + authenticate_digest_user, /* check_user_id */ + digest_check_auth, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; + + diff --git a/RELEASE_1_1_X/src/modules/standard/mod_dir.c b/RELEASE_1_1_X/src/modules/standard/mod_dir.c new file mode 100644 index 00000000000..c30b57c58d3 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_dir.c @@ -0,0 +1,846 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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_dir.c: Handles the on-the-fly html index generation + * + * Rob McCool + * 3/23/93 + * + * Adapted to Shambhala by rst. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" + +module dir_module; + +/**************************************************************** + * + * Handling configuration directives... + */ + +#define FANCY_INDEXING 1 /* Indexing options */ +#define ICONS_ARE_LINKS 2 +#define SCAN_HTML_TITLES 4 +#define SUPPRESS_LAST_MOD 8 +#define SUPPRESS_SIZE 16 +#define SUPPRESS_DESC 32 + +struct item { + char *type; + char *apply_to; + char *apply_path; + char *data; +}; + +typedef struct dir_config_struct { + + char *default_icon; + char *index_names; + + array_header *icon_list, *alt_list, *desc_list, *ign_list; + array_header *hdr_list, *rdme_list, *opts_list; + +} dir_config_rec; + +char c_by_encoding, c_by_type, c_by_path; + +#define BY_ENCODING &c_by_encoding +#define BY_TYPE &c_by_type +#define BY_PATH &c_by_path + +void push_item(array_header *arr, char *type, char *to, char *path, char *data) +{ + struct item *p = (struct item *)push_array(arr); + + if (!to) to = ""; + if (!path) path = ""; + + p->type = type; + p->data = data ? pstrdup(arr->pool, data): NULL; + p->apply_path = pstrcat(arr->pool, path, "*", NULL); + + if((type == BY_PATH) && (!is_matchexp(to))) + p->apply_to = pstrcat (arr->pool, "*", to, NULL); + else if (to) + p->apply_to = pstrdup (arr->pool, to); + else + p->apply_to = NULL; +} + +char *add_alt(cmd_parms *cmd, void *d, char *alt, char *to) +{ + if (cmd->info == BY_PATH) + if(!strcmp(to,"**DIRECTORY**")) + to = "^^DIRECTORY^^"; + + push_item(((dir_config_rec *)d)->alt_list, cmd->info, to, cmd->path, alt); + return NULL; +} + +char *add_icon(cmd_parms *cmd, void *d, char *icon, char *to) +{ + char *iconbak = pstrdup (cmd->pool, icon); + + if(icon[0] == '(') { + char *alt = getword (cmd->pool, &iconbak, ','); + iconbak[strlen(iconbak) - 1] = '\0'; /* Lose closing paren */ + add_alt(cmd, d, &alt[1], to); + } + if(cmd->info == BY_PATH) + if(!strcmp(to,"**DIRECTORY**")) + to = "^^DIRECTORY^^"; + + push_item(((dir_config_rec *)d)->icon_list, cmd->info, to, cmd->path, + iconbak); + return NULL; +} + +char *add_desc(cmd_parms *cmd, void *d, char *desc, char *to) +{ + push_item(((dir_config_rec *)d)->desc_list, cmd->info, to, cmd->path,desc); + return NULL; +} + +char *add_ignore(cmd_parms *cmd, void *d, char *ext) { + push_item(((dir_config_rec *)d)->ign_list, 0, ext, cmd->path, NULL); + return NULL; +} + +char *add_header(cmd_parms *cmd, void *d, char *name) { + push_item(((dir_config_rec *)d)->hdr_list, 0, NULL, cmd->path, name); + return NULL; +} + +char *add_readme(cmd_parms *cmd, void *d, char *name) { + push_item(((dir_config_rec *)d)->rdme_list, 0, NULL, cmd->path, name); + return NULL; +} + + +char *add_opts_int(cmd_parms *cmd, void *d, int opts) { + push_item(((dir_config_rec *)d)->opts_list, (char*)opts, NULL, + cmd->path, NULL); + return NULL; +} + +char *fancy_indexing (cmd_parms *cmd, void *d, int arg) +{ + return add_opts_int (cmd, d, arg? FANCY_INDEXING : 0); +} + +char *add_opts(cmd_parms *cmd, void *d, char *optstr) { + char *w; + int opts = 0; + + while(optstr[0]) { + w = getword_conf(cmd->pool, &optstr); + if(!strcasecmp(w,"FancyIndexing")) + opts |= FANCY_INDEXING; + else if(!strcasecmp(w,"IconsAreLinks")) + opts |= ICONS_ARE_LINKS; + else if(!strcasecmp(w,"ScanHTMLTitles")) + opts |= SCAN_HTML_TITLES; + else if(!strcasecmp(w,"SuppressLastModified")) + opts |= SUPPRESS_LAST_MOD; + else if(!strcasecmp(w,"SuppressSize")) + opts |= SUPPRESS_SIZE; + else if(!strcasecmp(w,"SuppressDescription")) + opts |= SUPPRESS_DESC; + else if(!strcasecmp(w,"None")) + opts = 0; + else + return "Invalid directory indexing option"; + } + return add_opts_int(cmd, d, opts); +} + +#define DIR_CMD_PERMS OR_INDEXES + +command_rec dir_cmds[] = { +{ "AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more filenames" }, +{ "AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more MIME types" }, +{ "AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more content encodings" }, +{ "AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more filenames" }, +{ "AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more MIME types" }, +{ "AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more content encodings" }, +{ "IndexOptions", add_opts, NULL, DIR_CMD_PERMS, RAW_ARGS, + "one or more index options" }, +{ "IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, ITERATE, + "one or more file extensions" }, +{ "AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "Descriptive text followed by one or more filenames" }, +{ "HeaderName", add_header, NULL, DIR_CMD_PERMS, TAKE1, "a filename" }, +{ "ReadmeName", add_readme, NULL, DIR_CMD_PERMS, TAKE1, "a filename" }, +{ "FancyIndexing", fancy_indexing, NULL, DIR_CMD_PERMS, FLAG, NULL }, +{ "DefaultIcon", set_string_slot, + (void*)XtOffsetOf(dir_config_rec, default_icon), + DIR_CMD_PERMS, TAKE1, "an icon URL"}, +{ "DirectoryIndex", set_string_slot, + (void*)XtOffsetOf(dir_config_rec, index_names), + DIR_CMD_PERMS, RAW_ARGS, NULL }, +{ NULL } +}; + +void *create_dir_config (pool *p, char *dummy) +{ + dir_config_rec *new = + (dir_config_rec *) pcalloc (p, sizeof(dir_config_rec)); + + new->index_names = NULL; + new->icon_list = make_array (p, 4, sizeof (struct item)); + new->alt_list = make_array (p, 4, sizeof (struct item)); + new->desc_list = make_array (p, 4, sizeof (struct item)); + new->ign_list = make_array (p, 4, sizeof (struct item)); + new->hdr_list = make_array (p, 4, sizeof (struct item)); + new->rdme_list = make_array (p, 4, sizeof (struct item)); + new->opts_list = make_array (p, 4, sizeof (struct item)); + + return (void *)new; +} + +void *merge_dir_configs (pool *p, void *basev, void *addv) +{ + dir_config_rec *new=(dir_config_rec*)pcalloc (p, sizeof(dir_config_rec)); + dir_config_rec *base = (dir_config_rec *)basev; + dir_config_rec *add = (dir_config_rec *)addv; + + new->default_icon = add->default_icon?add->default_icon:base->default_icon; + new->index_names = add->index_names? add->index_names: base->index_names; + + new->alt_list = append_arrays (p, add->alt_list, base->alt_list); + new->ign_list = append_arrays (p, add->ign_list, base->ign_list); + new->hdr_list = append_arrays (p, add->hdr_list, base->hdr_list); + new->desc_list = append_arrays (p, add->desc_list, base->desc_list); + new->icon_list = append_arrays (p, add->icon_list, base->icon_list); + new->rdme_list = append_arrays (p, add->rdme_list, base->rdme_list); + new->opts_list = append_arrays (p, add->opts_list, base->opts_list); + + return new; +} + +/**************************************************************** + * + * Looking things up in config entries... + */ + +/* Structure used to hold entries when we're actually building an index */ + +struct ent { + char *name; + char *icon; + char *alt; + char *desc; + size_t size; + time_t lm; + struct ent *next; +}; + +char *find_item(request_rec *r, array_header *list, int path_only) { + char *content_type = r->content_type; + char *content_encoding = r->content_encoding; + char *path = r->filename; + + struct item *items = (struct item *)list->elts; + int i; + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + + /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */ + if((path[0] == '^') || (!strcmp_match(path,p->apply_path))) { + if(!*(p->apply_to)) + return p->data; + else if(p->type == BY_PATH || path[0] == '^') { + if(!strcmp_match(path,p->apply_to)) + return p->data; + } else if(!path_only) { + if(!content_encoding) { + if(p->type == BY_TYPE) { + if(content_type && !strcmp_match(content_type,p->apply_to)) + return p->data; + } + } else { + if(p->type == BY_ENCODING) { + if(!strcmp_match(content_encoding,p->apply_to)) + return p->data; + } + } + } + } + } + return NULL; +} + +#define find_icon(d,p,t) find_item(p,d->icon_list,t) +#define find_alt(d,p,t) find_item(p,d->alt_list,t) +#define find_desc(d,p) find_item(p,d->desc_list,0) +#define find_header(d,p) find_item(p,d->hdr_list,0) +#define find_readme(d,p) find_item(p,d->rdme_list,0) + +char *find_default_icon (dir_config_rec *d, char *bogus_name) +{ + request_rec r; + + /* Bleah. I tried to clean up find_item, and it lead to this bit + * of ugliness. Note that the fields initialized are precisely + * those that find_item looks at... + */ + + r.filename = bogus_name; + r.content_type = r.content_encoding = NULL; + + return find_item (&r, d->icon_list, 1); +} + +int ignore_entry(dir_config_rec *d, char *path) { + array_header *list = d->ign_list; + struct item *items = (struct item *)list->elts; + char *tt; + int i; + + if((tt=strrchr(path,'/')) == NULL) + tt=path; + else { + tt++; + } + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + char *ap; + + if((ap=strrchr(p->apply_to,'/')) == NULL) + ap=p->apply_to; + else + ap++; + + if(!strcmp_match(path,p->apply_path) && !strcmp_match(tt,ap)) + return 1; + } + return 0; +} + +int find_opts(dir_config_rec *d, request_rec *r) { + char *path = r->filename; + array_header *list = d->opts_list; + struct item *items = (struct item *)list->elts; + int i; + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + + if(!strcmp_match(path,p->apply_path)) + return (int)p->type; + } + return 0; +} + +/***************************************************************** + * + * Actually generating output + */ + + +int insert_readme(char *name, char *readme_fname, int rule, request_rec *r) { + char *fn; + FILE *f; + struct stat finfo; + int plaintext=0; + + fn = make_full_path(r->pool, name, readme_fname); + fn = pstrcat(r->pool, fn, ".html", NULL); + if(stat(fn,&finfo) == -1) { + /* A brief fake multiviews search for README.html */ + fn[strlen(fn)-5] = '\0'; + if(stat(fn,&finfo) == -1) + return 0; + plaintext=1; + if(rule) rputs("
\n", r); + rputs("
\n", r);
+    }
+    else if (rule) rputs("
\n", r); + if(!(f = pfopen(r->pool,fn,"r"))) + return 0; + if (!plaintext) + send_fd(f, r); + else + { + char buf[IOBUFSIZE+1]; + int i, n, c, ch; + while (!feof(f)) + { + do n = fread(buf, sizeof(char), IOBUFSIZE, f); + while (n == -1 && ferror(f) && errno == EINTR); + if (n == -1 || n == 0) break; + buf[n] = '\0'; + c = 0; + while (c < n) + { + for (i=c; i < n; i++) + if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') break; + ch = buf[i]; + buf[i] = '\0'; + rputs(&buf[c], r); + if (ch == '<') rputs("<", r); + else if (ch == '>') rputs(">", r); + else if (ch == '&') rputs("&", r); + c = i + 1; + } + } + } + pfclose(r->pool, f); + if(plaintext) + rputs("
\n", r); + return 1; +} + + +char *find_title(request_rec *r) { + char titlebuf[MAX_STRING_LEN], *find = ""; + FILE *thefile = NULL; + int x,y,n,p; + + if (r->content_type && !strcmp(r->content_type,"text/html") && !r->content_encoding) { + if(!(thefile = pfopen(r->pool, r->filename,"r"))) + return NULL; + n = fread(titlebuf,sizeof(char),MAX_STRING_LEN - 1,thefile); + titlebuf[n] = '\0'; + for(x=0,p=0;titlebuf[x];x++) { + if(toupper(titlebuf[x]) == find[p]) { + if(!find[++p]) { + if((p = ind(&titlebuf[++x],'<')) != -1) + titlebuf[x+p] = '\0'; + /* Scan for line breaks for Tanmoy's secretary */ + for(y=x;titlebuf[y];y++) + if((titlebuf[y] == CR) || (titlebuf[y] == LF)) + titlebuf[y] = ' '; + pfclose (r->pool, thefile); + return pstrdup(r->pool, &titlebuf[x]); + } + } else p=0; + } + pfclose(r->pool, thefile); + } + return NULL; +} + +struct ent *make_dir_entry(char *name, int dir_opts, + dir_config_rec *d, request_rec *r) +{ + struct ent *p; + + if((name[0] == '.') && (!name[1])) + return(NULL); + + if (ignore_entry(d, make_full_path (r->pool, r->filename, name))) + return(NULL); + + p=(struct ent *)pcalloc(r->pool, sizeof(struct ent)); + p->name = pstrdup (r->pool, name); + p->size = -1; + p->icon = NULL; + p->alt = NULL; + p->desc = NULL; + p->lm = -1; + + if(dir_opts & FANCY_INDEXING) { + request_rec *rr = sub_req_lookup_file (name, r); + + if (rr->finfo.st_mode != 0) { + p->lm = rr->finfo.st_mtime; + if(S_ISDIR(rr->finfo.st_mode)) { + if(!(p->icon = find_icon(d,rr,1))) + p->icon = find_default_icon(d,"^^DIRECTORY^^"); + if(!(p->alt = find_alt(d,rr,1))) + p->alt = "DIR"; + p->size = -1; + p->name = pstrcat (r->pool, name, "/", NULL); + } + else { + p->icon = find_icon(d, rr, 0); + p->alt = find_alt(d, rr, 0); + p->size = rr->finfo.st_size; + } + } + + p->desc = find_desc(d, rr); + + if((!p->desc) && (dir_opts & SCAN_HTML_TITLES)) + p->desc = pstrdup (r->pool, find_title(rr)); + + destroy_sub_req (rr); + } + return(p); +} + +char *terminate_description(dir_config_rec *d, char *desc, int dir_opts) { + int maxsize = 23; + register int x; + + if(dir_opts & SUPPRESS_LAST_MOD) maxsize += 17; + if(dir_opts & SUPPRESS_SIZE) maxsize += 7; + + for(x=0;desc[x] && maxsize;x++) { + if(desc[x] == '<') { + while(desc[x] != '>') { + if(!desc[x]) { + maxsize = 0; + break; + } + ++x; + } + } + else --maxsize; + } + if(!maxsize) { + desc[x-1] = '>'; /* Grump. */ + desc[x] = '\0'; /* Double Grump! */ + } + return desc; +} + +void output_directories(struct ent **ar, int n, + dir_config_rec *d, request_rec *r, int dir_opts) +{ + int x, len; + char *name = r->uri; + char *tp; + pool *scratch = make_sub_pool (r->pool); + + if(name[0] == '\0') name = "/"; + + if(dir_opts & FANCY_INDEXING) { + rputs("<PRE>", r); + if((tp = find_default_icon(d,"^^BLANKICON^^"))) + rvputs(r, "<IMG SRC=\"", escape_html(scratch, tp), + "\" ALT=\" \"> ", NULL); + rputs("Name ", r); + if(!(dir_opts & SUPPRESS_LAST_MOD)) + rputs("Last modified ", r); + if(!(dir_opts & SUPPRESS_SIZE)) + rputs("Size ", r); + if(!(dir_opts & SUPPRESS_DESC)) + rputs("Description", r); + rputs("\n<HR>\n", r); + } + else { + rputs("<UL>", r); + } + + for(x=0;x<n;x++) { + char *anchor = NULL, *t = NULL, *t2 = NULL; + + clear_pool (scratch); + + if((!strcmp(ar[x]->name,"../")) || (!strcmp(ar[x]->name,".."))) { + char *t = make_full_path (scratch, name, "../"); + getparents(t); + if(t[0] == '\0') t = "/"; + anchor = pstrcat (scratch, "<A HREF=\"", + escape_html(scratch, os_escape_path(scratch, t, 0)), + "\">", NULL); + t2 = "Parent Directory</A> "; + } + else { + t = ar[x]->name; + len = strlen(t); + if(len > 23) { + t2 = pstrdup(scratch, t); + t2[21] = '.'; + t2[22] = '.'; + t2[23] = '\0'; + t2 = escape_html(scratch, t2); + t2 = pstrcat(scratch, t2, "</A>", NULL); + } else + { + char buff[24]=" "; + t2 = escape_html(scratch, t); + buff[23-len] = '\0'; + t2 = pstrcat(scratch, t2, "</A>", buff, NULL); + } + anchor = pstrcat (scratch, "<A HREF=\"", + escape_html(scratch, os_escape_path(scratch, t, 0)), + "\">", NULL); + } + + if(dir_opts & FANCY_INDEXING) { + if(dir_opts & ICONS_ARE_LINKS) + rputs(anchor, r); + if((ar[x]->icon) || d->default_icon) { + rvputs(r, "<IMG SRC=\"", + escape_html(scratch, ar[x]->icon ? + ar[x]->icon : d->default_icon), + "\" ALT=\"[", (ar[x]->alt ? ar[x]->alt : " "), + "]\">", NULL); + } + if(dir_opts & ICONS_ARE_LINKS) + rputs("</A>", r); + + rvputs(r," ", anchor, t2, NULL); + if(!(dir_opts & SUPPRESS_LAST_MOD)) { + if(ar[x]->lm != -1) { + char time[MAX_STRING_LEN]; + struct tm *ts = localtime(&ar[x]->lm); + strftime(time,MAX_STRING_LEN,"%d-%b-%y %H:%M ",ts); + rputs(time, r); + } + else { + rputs(" ", r); + } + } + if(!(dir_opts & SUPPRESS_SIZE)) { + send_size(ar[x]->size,r); + rputs(" ", r); + } + if(!(dir_opts & SUPPRESS_DESC)) { + if(ar[x]->desc) { + rputs(terminate_description(d, ar[x]->desc, dir_opts), r); + } + } + } + else + rvputs(r, "<LI> ", anchor," ", t2, NULL); + rputc('\n', r); + } + if(dir_opts & FANCY_INDEXING) { + rputs("</PRE>", r); + } + else { + rputs("</UL>", r); + } +} + + +int dsortf(struct ent **s1,struct ent **s2) +{ + return(strcmp((*s1)->name,(*s2)->name)); +} + + +int index_directory(request_rec *r, dir_config_rec *dir_conf) +{ + char *title_name = escape_html(r->pool, r->uri); + char *title_endp; + char *name = r->filename; + + DIR *d; + struct DIR_TYPE *dstruct; + int num_ent=0,x; + struct ent *head,*p; + struct ent **ar; + char *tmp; + int dir_opts = find_opts(dir_conf, r); + + if(!(d=opendir(name))) return FORBIDDEN; + + r->content_type = "text/html"; + + soft_timeout ("send directory", r); + send_http_header(r); + + if (r->header_only) { + closedir (d); + return 0; + } + + /* Spew HTML preamble */ + + title_endp = title_name + strlen(title_name) - 1; + + while (title_endp > title_name && *title_endp == '/') + *title_endp-- = '\0'; + + rvputs(r, "<HEAD><TITLE>Index of ", title_name, "\n", + NULL); + + if((!(tmp = find_header(dir_conf,r))) || (!(insert_readme(name,tmp,0,r)))) + rvputs(r, "

Index of ", title_name, "

\n", NULL); + + /* + * Since we don't know how many dir. entries there are, put them into a + * linked list and then arrayificate them so qsort can use them. + */ + head=NULL; + while((dstruct=readdir(d))) { + if((p = make_dir_entry(dstruct->d_name, dir_opts, dir_conf, r))) { + p->next=head; + head=p; + num_ent++; + } + } + ar=(struct ent **) palloc(r->pool, num_ent*sizeof(struct ent *)); + p=head; + x=0; + while(p) { + ar[x++]=p; + p = p->next; + } + + qsort((void *)ar,num_ent,sizeof(struct ent *), +#ifdef ULTRIX_BRAIN_DEATH + (int (*))dsortf); +#else + (int (*)(const void *,const void *))dsortf); +#endif + output_directories(ar, num_ent, dir_conf, r, dir_opts); + closedir(d); + + if (dir_opts & FANCY_INDEXING) + if((tmp = find_readme(dir_conf, r))) + insert_readme(name,tmp,1,r); + else { + rputs("", r); + } + + rputs("", r); + return 0; +} + +/* The formal handler... */ + +int handle_dir (request_rec *r) +{ + dir_config_rec *d = + (dir_config_rec *)get_module_config (r->per_dir_config, &dir_module); + char *names_ptr = d->index_names ? d->index_names : DEFAULT_INDEX; + int allow_opts = allow_options (r); + + if (r->uri[0] == '\0' || r->uri[strlen(r->uri)-1] != '/') { + char* ifile; + if (r->args != NULL) + ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri), + "/", "?", r->args, NULL); + else + ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri), + "/", NULL); + + table_set (r->headers_out, "Location", + construct_url(r->pool, ifile, r->server)); + return REDIRECT; + } + + /* KLUDGE --- make the sub_req lookups happen in the right directory. + * Fixing this in the sub_req_lookup functions themselves is difficult, + * and would probably break virtual includes... + */ + + r->filename = pstrcat (r->pool, r->filename, "/", NULL); + + while (*names_ptr) { + + char *name_ptr = getword_conf (r->pool, &names_ptr); + request_rec *rr = sub_req_lookup_uri (name_ptr, r); + + if (rr->status == 200 && rr->finfo.st_mode != 0) { + char* new_uri = escape_uri(r->pool, rr->uri); + + if (rr->args != NULL) + new_uri = pstrcat(r->pool, new_uri, "?", rr->args, NULL); + else if (r->args != NULL) + new_uri = pstrcat(r->pool, new_uri, "?", r->args, NULL); + + destroy_sub_req (rr); + internal_redirect (new_uri, r); + return OK; + } + + destroy_sub_req (rr); + } + + if (r->method_number != M_GET) return NOT_IMPLEMENTED; + + /* OK, nothing easy. Trot out the heavy artillery... */ + + if (allow_opts & OPT_INDEXES) + return index_directory (r, d); + else + return FORBIDDEN; +} + + +handler_rec dir_handlers[] = { +{ DIR_MAGIC_TYPE, handle_dir }, +{ NULL } +}; + +module dir_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_dir_config, /* dir config creater */ + merge_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + dir_cmds, /* command table */ + dir_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_dld.c b/RELEASE_1_1_X/src/modules/standard/mod_dld.c new file mode 100644 index 00000000000..a0bc13c8647 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_dld.c @@ -0,0 +1,191 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. 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 . + * + */ + + +/* + * A first stab at dynamic loading, using the GNU dld library + * (or at least, an embarassingly old version of it...). + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_conf_globals.h" /* server_argv0. Sigh... */ +#include + +/* + * The hard part of implementing LoadModule is deciding what to do about + * rereading the config files. This proof-of-concept implementation takes the + * cheap way out: we only actually load the modules the first time through. + */ + +static int been_there_done_that = 0; /* Loaded the modules yet? */ +static int have_symbol_table = 0; + +char *insure_dld_sane() +{ + int errcode; + char *bin_name; + + if (have_symbol_table) return NULL; + + bin_name = dld_find_executable (server_argv0); + + if ((errcode = dld_init (bin_name))) { + dld_perror (server_argv0); + return "Cannot find server binary (needed for dynamic linking)."; + } + + have_symbol_table = 1; + return NULL; +} + +char *link_file (pool *p, char *filename) +{ + int errcode; + + filename = server_root_relative (p, filename); + if ((errcode = dld_link (filename))) { + dld_perror (server_argv0); + return pstrcat (p, "Cannot load ", filename, " into server", NULL); + } + return NULL; +} + +char *load_module (cmd_parms *cmd, void *dummy, char *modname, char *filename) +{ + char *errname; + module *modp; + + if (been_there_done_that) return NULL; + + if ((errname = insure_dld_sane())) return errname; + if ((errname = link_file (cmd->pool, filename))) return errname; + if (!(modp = (module *)dld_get_symbol (modname))) { + return pstrcat (cmd->pool, "Can't find module ", modname, + " in file ", filename, NULL); + } + + add_module (modp); + + /* Alethea Patch (rws,djw2) - need to run configuration functions + in new modules */ + + if (modp->create_server_config) + ((void**)cmd->server->module_config)[modp->module_index]= + (*modp->create_server_config)(cmd->pool, cmd->server); + + if (modp->create_dir_config) + ((void**)cmd->server->lookup_defaults)[modp->module_index]= + (*modp->create_dir_config)(cmd->pool, NULL); + + + return NULL; +} + +char *load_file (cmd_parms *cmd, void *dummy, char *filename) +{ + char *errname; + + if (been_there_done_that) return NULL; + + if ((errname = insure_dld_sane())) return errname; + if ((errname = link_file (cmd->pool, filename))) return errname; + return NULL; +} + +void check_loaded_modules (server_rec *dummy, pool *p) +{ + if (been_there_done_that) return; + + if (dld_undefined_sym_count > 0) { + /* Screwup. Do the best we can to inform the user, and exit */ + char **bad_syms = dld_list_undefined_sym(); + int i; + + fprintf(stderr, "Dynamic linking error --- symbols left undefined.\n"); + fprintf(stderr, "(It may help to relink libraries).\n"); + fprintf(stderr, "Undefined symbols follow:\n"); + + for (i = 0; i < dld_undefined_sym_count; ++i) + fprintf (stderr, "%s\n", bad_syms[i]); + + exit (1); + } + + been_there_done_that = 1; +} + +command_rec dld_cmds[] = { +{ "LoadModule", load_module, NULL, RSRC_CONF, TAKE2, + "a module name, and the name of a file to load it from"}, +{ "LoadFile", load_file, NULL, RSRC_CONF, ITERATE, + "files or libraries to link into the server at runtime"}, +{ NULL } +}; + +module dld_module = { + STANDARD_MODULE_STUFF, + check_loaded_modules, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + NULL, /* server config */ + NULL, /* merge server config */ + dld_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_env.c b/RELEASE_1_1_X/src/modules/standard/mod_env.c new file mode 100644 index 00000000000..d16acccaa68 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_env.c @@ -0,0 +1,257 @@ +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_env.c + * version 0.0.5 + * status beta + * Pass environment variables to CGI/SSI scripts. + * + * Andrew Wilson 06.Dec.95 + * + * Change log: + * 08.Dec.95 Now allows PassEnv directive to appear more than once in + * conf files. + * 10.Dec.95 optimisation. getenv() only called at startup and used + * to build a fast-to-access table. table used to build + * per-server environment for each request. + * robustness. better able to handle errors in configuration + * files: + * 1) PassEnv directive present, but no environment variable listed + * 2) PassEnv FOO present, but $FOO not present in environment + * 3) no PassEnv directive present + * 23.Dec.95 Now allows SetEnv directive with same semantics as 'sh' setenv: + * SetEnv Var sets Var to the empty string + * SetEnv Var Val sets Var to the value Val + * Values containing whitespace should be quoted, eg: + * SetEnv Var "this is some text" + * Environment variables take their value from the last instance + * of PassEnv / SetEnv to be reached in the configuration file. + * For example, the sequence: + * PassEnv PATH + * SetEnv PATH /special/path + * Causes PATH to take the value '/special/path'. + * 23.Feb.96 Added UnsetEnv directive to allow environment variables + * to be removed. + * Virtual hosts now 'inherit' parent server environment which + * they're able to overwrite with their own directives or + * selectively ignore with UnsetEnv. + * *** IMPORTANT - the way that virtual hosts inherit their *** + * *** environment variables from the default server's *** + * *** configuration has changed. You should test your *** + * *** configuration carefully before accepting this *** + * *** version of the module in a live webserver which used *** + * *** older versions of the module. *** + */ + +#include "httpd.h" +#include "http_config.h" + +typedef struct { + table *vars; + char *unsetenv; + int vars_present; +} env_server_config_rec; + +module env_module; + +void *create_env_server_config (pool *p, server_rec *dummy) +{ + env_server_config_rec *new = + (env_server_config_rec *) palloc (p, sizeof(env_server_config_rec)); + new->vars = make_table (p, 50); + new->unsetenv = ""; + new->vars_present = 0; + return (void *) new; +} + +void *merge_env_server_configs (pool *p, void *basev, void *addv) +{ + env_server_config_rec *base = (env_server_config_rec *)basev; + env_server_config_rec *add = (env_server_config_rec *)addv; + env_server_config_rec *new = + (env_server_config_rec *)palloc (p, sizeof(env_server_config_rec)); + + table *new_table; + table_entry *elts; + + int i; + char *uenv, *copy; + + /* + * new_table = copy_table( p, base->vars ); + * foreach $element ( @add->vars ) { + * table_set( new_table, $element.key, $element.val ); + * }; + * foreach $unsetenv ( @UNSETENV ) { + * table_unset( new_table, $unsetenv ); + * } + */ + + new_table = copy_table( p, base->vars ); + + elts = (table_entry *) add->vars->elts; + + for ( i = 0; i < add->vars->nelts; ++i ) { + table_set( new_table, elts[i].key, elts[i].val ); + }; + + copy = pstrdup( p, add->unsetenv ); + uenv = getword_conf( p, © ); + while ( uenv[0] != '\0' ) { + table_unset( new_table, uenv ); + uenv = getword_conf( p, © ); + }; + + new->vars = new_table; + + new->vars_present = base->vars_present || add->vars_present; + + return new; +} + +char *add_env_module_vars_passed (cmd_parms *cmd, char *struct_ptr, char *arg) +{ + env_server_config_rec *sconf = + get_module_config (cmd->server->module_config, &env_module); + table *vars = sconf->vars; + char *env_var; + char *name_ptr; + + while (*arg) { + name_ptr = getword_conf (cmd->pool, &arg); + env_var = getenv(name_ptr); + if ( env_var != NULL ) { + sconf->vars_present = 1; + table_set (vars, name_ptr, env_var); + }; + } + return NULL; +} + +char *add_env_module_vars_set (cmd_parms *cmd, char *struct_ptr, char *arg) +{ + env_server_config_rec *sconf = + get_module_config (cmd->server->module_config, &env_module); + table *vars = sconf->vars; + char *name, *value; + + name = getword_conf( cmd->pool, &arg ); + value = getword_conf( cmd->pool, &arg ); + + /* name is mandatory, value is optional. no value means + * set the variable to an empty string + */ + + + if ( (*name == '\0') || (*arg != '\0')) { + return "SetEnv takes one or two arguments. An environment variable name and an optional value to pass to CGI." ; + }; + + sconf->vars_present = 1; + table_set (vars, name, value); + + return NULL; +} + +char *add_env_module_vars_unset (cmd_parms *cmd, char *struct_ptr, char *arg) +{ + env_server_config_rec *sconf = + get_module_config (cmd->server->module_config, &env_module); + sconf->unsetenv = sconf->unsetenv ? + pstrcat( cmd->pool, sconf->unsetenv, " ", arg, NULL ) : + pstrdup( cmd->pool, arg ); + return NULL; +} + +command_rec env_module_cmds[] = { +{ "PassEnv", add_env_module_vars_passed, NULL, + RSRC_CONF, RAW_ARGS, "a list of environment variables to pass to CGI." }, +{ "SetEnv", add_env_module_vars_set, NULL, + RSRC_CONF, RAW_ARGS, "an environment variable name and a value to pass to CGI." }, +{ "UnsetEnv", add_env_module_vars_unset, NULL, + RSRC_CONF, RAW_ARGS, "a list of variables to remove from the CGI environment." }, +{ NULL }, +}; + +int fixup_env_module(request_rec *r) +{ + table *e = r->subprocess_env; + server_rec *s = r->server; + env_server_config_rec *sconf = get_module_config (s->module_config, + &env_module); + table *vars = sconf->vars; + + if ( !sconf->vars_present ) return DECLINED; + + r->subprocess_env = overlay_tables( r->pool, e, vars ); + + return OK; +} + +module env_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_env_server_config, /* server config */ + merge_env_server_configs, /* merge server configs */ + env_module_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + fixup_env_module, /* fixups */ + NULL, /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_imap.c b/RELEASE_1_1_X/src/modules/standard/mod_imap.c new file mode 100644 index 00000000000..2eddcf14771 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_imap.c @@ -0,0 +1,829 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * This imagemap module started as a port of the original imagemap.c + * written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu). + * This version includes the mapping algorithms found in version 1.3 + * of imagemap.c. + * + * Contributors to this code include: + * + * Kevin Hughes, kevinh@pulua.hcc.hawaii.edu + * + * Eric Haines, erich@eye.com + * "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com + * + * Randy Terbush, randy@zyzzyva.com + * port to Apache module format, "base_uri" and support for relative URLs + * + * James H. Cloos, Jr., cloos@jhcloos.com + * Added point datatype, using code in NCSA's version 1.8 imagemap.c + * program, as distributed with version 1.4.1 of their server. + * The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu + * + * Nathan Kurz, nate@tripod.com + * Rewrite/reorganization. New handling of default, base and relative URLs. + * New Configuration directives: + * ImapMenu {none, formatted, semiformatted, unformatted} + * ImapDefault {error, nocontent, referer, menu, URL} + * ImapBase {map, referer, URL} + * Support for creating non-graphical menu added. (backwards compatible): + * Old: directive URL [x,y ...] + * New: directive URL "Menu text" [x,y ...] + * or: directive URL x,y ... "Menu text" + * Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca. + * + * Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#include "util_script.h" + +#define IMAP_MAGIC_TYPE "application/x-httpd-imap" +#define LARGEBUF 500 +#define SMALLBUF 100 +#define MAXVERTS 100 +#define X 0 +#define Y 1 + +#define IMAP_MENU_DEFAULT "formatted" +#define IMAP_DEFAULT_DEFAULT "nocontent" +#define IMAP_BASE_DEFAULT "map" + +#ifdef SUNOS4 +double strtod(); /* SunOS needed this */ +#endif + +module imap_module; + +typedef struct { + char *imap_menu; + char *imap_default; + char *imap_base; +} imap_conf_rec; + +void *create_imap_dir_config (pool *p, char *dummy) { + imap_conf_rec *icr = + (imap_conf_rec *)palloc(p, sizeof(imap_conf_rec)); + + icr->imap_menu = NULL; + icr->imap_default = NULL; + icr->imap_base = NULL; + + return icr; +} + +void *merge_imap_dir_configs (pool *p, void *basev, void *addv) +{ + imap_conf_rec *new=(imap_conf_rec *)pcalloc (p, sizeof(imap_conf_rec)); + imap_conf_rec *base = (imap_conf_rec *)basev; + imap_conf_rec *add = (imap_conf_rec *)addv; + + new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu; + new->imap_default=add->imap_default ? add->imap_default : base->imap_default; + new->imap_base =add-> imap_base ? add->imap_base : base->imap_base; + + return new; +} + + +command_rec imap_cmds[] = { +{ "ImapMenu", set_string_slot, + (void*)XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES, TAKE1, + "the type of menu generated: none, formatted, semiformatted, unformatted"}, +{ "ImapDefault", set_string_slot, + (void*)XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES, TAKE1, + "the action taken if no match: error, nocontent, referer, menu, URL" }, +{ "ImapBase", set_string_slot, + (void*)XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES, TAKE1, + "the base for all URL's: map, referer, URL (or start of)" }, +{ NULL } +}; + +int pointinrect(double point[2], double coords[MAXVERTS][2]) +{ + double max[2], min[2]; + if (coords[0][X] > coords[1][X]) { + max[0] = coords[0][X]; + min[0] = coords[1][X]; + } else { + max[0] = coords[1][X]; + min[0] = coords[0][X]; + } + + if (coords[0][Y] > coords[1][Y]) { + max[1] = coords[0][Y]; + min[1] = coords[1][Y]; + } else { + max[1] = coords[1][Y]; + min[1] = coords[0][Y]; + } + + return ((point[X] >= min[0] && point[X] <= max[0]) && + (point[Y] >= min[1] && point[Y] <= max[1])); +} + +int pointincircle(double point[2], double coords[MAXVERTS][2]) +{ + int radius1, radius2; + + radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y])) + + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X])); + + radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y])) + + ((coords[0][X] - point[X]) * (coords[0][X] - point[X])); + + return (radius2 <= radius1); +} + +int pointinpoly(double point[2], double pgon[MAXVERTS][2]) +{ + int i, numverts, inside_flag, xflag0; + int crossings; + double *p, *stop; + double tx, ty, y; + + for (i = 0; pgon[i][X] != -1 && i < MAXVERTS; i++); + + numverts = i; + crossings = 0; + + tx = point[X]; + ty = point[Y]; + y = pgon[numverts - 1][Y]; + + p = (double *) pgon + 1; + if ((y >= ty) != (*p >= ty)) { + + if ((xflag0 = (pgon[numverts - 1][X] >= tx)) == (*(double *) pgon >= tx)) { + if (xflag0) + crossings++; + } + else { + crossings += (pgon[numverts - 1][X] - (y - ty) * + (*(double *) pgon - pgon[numverts - 1][X]) / + (*p - y)) >= tx; + } + } + + stop = pgon[numverts]; + + for (y = *p, p += 2; p < stop; y = *p, p += 2) { + + if (y >= ty) { + + while ((p < stop) && (*p >= ty)) + p += 2; + + if (p >= stop) + break; + if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) { + + if (xflag0) + crossings++; + } + else { + crossings += (*(p - 3) - (*(p - 2) - ty) * + (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx; + } + } + else { + while ((p < stop) && (*p < ty)) + p += 2; + + if (p >= stop) + break; + + if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) { + if (xflag0) + crossings++; + } + else { + crossings += (*(p - 3) - (*(p - 2) - ty) * + (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx; + } + } + } + + inside_flag = crossings & 0x01; + return (inside_flag); +} + + +int is_closer(double point[2], double coords[MAXVERTS][2], double *closest) +{ + double dist_squared =((point[X] - coords[0][X]) * (point[X] - coords[0][X])) + + ((point[Y] - coords[0][Y]) * (point[Y] - coords[0][Y])); + + if (point[X] < 0 || point[Y] < 0 ) + return(0); /* don't mess around with negative coordinates */ + + if ( *closest < 0 || dist_squared < *closest ) { + *closest = dist_squared; + return(1); /* if this is the first point or is the closest yet + set 'closest' equal to this distance^2 */ + } + + return(0); /* if it's not the first or closest */ + +} + +double get_x_coord(char *args) +{ + char *endptr; /* we want it non-null */ + double x_coord = -1; /* -1 is returned if no coordinate is given */ + + if (args == NULL) + return(-1); /* in case we aren't passed anything */ + + while( *args && !isdigit(*args) && *args != ',') + args++; /* jump to the first digit, but not past a comma or end */ + + x_coord = strtod(args, &endptr); + + if (endptr > args) /* if a conversion was made */ + return(x_coord); + + return(-1); /* else if no conversion was made, or if no args was given */ +} + +double get_y_coord(char *args) +{ + char *endptr; /* we want it non-null */ + char *start_of_y = NULL; + double y_coord = -1; /* -1 is returned on error */ + + if (args == NULL) + return(-1); /* in case we aren't passed anything */ + + start_of_y = strchr(args, ','); /* the comma */ + + if (start_of_y) { + + start_of_y++; /* start looking at the character after the comma */ + + while( *start_of_y && !isdigit(*start_of_y)) + start_of_y++; /* jump to the first digit, but not past the end */ + + y_coord = strtod(start_of_y, &endptr); + + if (endptr > start_of_y) + return(y_coord); + } + + return(-1); /* if no conversion was made, or no comma was found in args */ +} + + +int read_quoted(char *string, char *quoted_part) +{ + char *starting_pos = string; + + while ( isspace(*string) ) + string++; /* go along string until non-whitespace */ + + if ( *string == '"' ) { /* if that character is a double quote */ + + string++; /* step over it */ + + while ( *string && *string != '"' ) { + *quoted_part++ = *string++; /* copy the quoted portion */ + } + + *quoted_part = '\0'; /* end the string with a SNUL */ + + string++; /* step over the last double quote */ + } + + return(string - starting_pos); /* return the total characters read */ +} + + +void imap_url(request_rec *r, char *base, char *value, char *url) +{ +/* translates a value into a URL. */ + int slen, clen; + char *string_pos = NULL; + char *directory = NULL; + char *referer = NULL; + char my_base[SMALLBUF] = {'\0'}; + + if ( ! strcasecmp(value, "map" ) || ! strcasecmp(value, "menu") ) { + if (r->server->port == 80 ) { + sprintf(url, "http://%s%s", r->server->server_hostname, r->uri); + } + else { + sprintf(url, "http://%s:%d%s", r->server->server_hostname, + r->server->port, r->uri); + } + return; + } + + if ( ! strcasecmp(value, "nocontent") || ! strcasecmp(value, "error") ) { + strcpy(url, value); + return; /* these are handled elsewhere, so just copy them */ + } + + if ( ! strcasecmp(value, "referer" ) ) { + referer = table_get(r->headers_in, "Referer"); + if ( referer && *referer ) { + strcpy(url, referer); + return; + } + else { + *value = '\0'; /* if 'referer' but no referring page, null the value */ + } + } + + string_pos = value; + while ( isalpha(*string_pos) ) + string_pos++; /* go along the URL from the map until a non-letter */ + if ( *string_pos == ':' ) { + strcpy(url, value); /* if letters and then a colon (like http:) */ + return; /* it's an absolute URL, so use it! */ + } + + if ( ! base || ! *base ) { + if ( value && *value ) { + strcpy(url, value); /* no base: use what is given */ + } + else { + if (r->server->port == 80 ) { + sprintf(url, "http://%s/", r->server->server_hostname); + } + if (r->server->port != 80 ) { + sprintf(url, "http://%s:%d/", r->server->server_hostname, + r->server->port); + } /* no base, no value: pick a simple default */ + } + return; + } + + strcpy(my_base, base); /* must be a relative URL to be combined with base */ + string_pos = my_base; + while (*string_pos) { + if (*string_pos == '/' && *(string_pos+1) == '/') { + string_pos += 2; /* if there are two slashes, jump over them */ + continue; + } + if (*string_pos == '/') { /* the first single slash */ + if ( value[0] == '/' ) { + *string_pos = '\0'; + } /* if the URL from the map starts from root, end the + base URL string at the first single slash */ + else { + directory = string_pos; /* save the start of the directory portion */ + + string_pos = strrchr(string_pos, '/'); /* now reuse string_pos */ + string_pos++; /* step over that last slash */ + *string_pos = '\0'; + } /* but if the map url is relative, leave the + slash on the base (if there is one) */ + break; + } + string_pos++; /* until we get to the end of my_base without finding + a slash by itself */ + } + + while ( ! strncmp(value, "../", 3) || ! strcmp(value, "..") ) { + + if (directory && (slen = strlen (directory))) { + + /* for each '..', knock a directory off the end + by ending the string right at the last slash. + But only consider the directory portion: don't eat + into the server name. And only try if a directory + portion was found */ + + clen = slen - 1; + + while ((slen - clen) == 1) { + + if ((string_pos = strrchr(directory, '/'))) + *string_pos = '\0'; + clen = strlen (directory); + if (clen == 0) break; + } + + value += 2; /* jump over the '..' that we found in the value */ + } + + if (! strncmp(value, "/../", 4) || ! strcmp(value, "/..") ) + + value++; /* step over the '/' if there are more '..' to do. + this way, we leave the starting '/' on value after + the last '..', but get rid of it otherwise */ + + } /* by this point, value does not start with '..' */ + + if ( value && *value ) { + sprintf(url, "%s%s", my_base, value); + } + else { + sprintf(url, "%s", my_base); + } + return; +} + +int imap_reply(request_rec *r, char *redirect) +{ + if ( ! strcasecmp(redirect, "error") ) { + return SERVER_ERROR; /* they actually requested an error! */ + } + if ( ! strcasecmp(redirect, "nocontent") ) { + r->status_line = pstrdup(r->pool, "204 No Content"); + soft_timeout ("send no content", r); + send_http_header(r); + return OK; /* tell the client to keep the page it has */ + } + if (redirect && *redirect ) { + table_set(r->headers_out, "Location", redirect); + return REDIRECT; /* must be a URL, so redirect to it */ + } + return SERVER_ERROR; +} + +void menu_header(request_rec *r, char *menu) +{ + if (! strcasecmp(menu, "formatted")) { + r->content_type = "text/html"; + soft_timeout ("send menu", r); + send_http_header(r); + rvputs(r, "\nMenu for ", r->uri, + "\n\n\n", NULL); + rvputs(r, "

Menu for ", r->uri, "

\n
\n\n", NULL); + } + if (! strcasecmp(menu, "semiformatted")) { + r->content_type = "text/html"; + soft_timeout ("send menu", r); + send_http_header(r); + rvputs(r, "\nMenu for ", r->uri, + "\n\n\n", NULL); + } + if (! strcasecmp(menu, "unformatted")) { + r->content_type = "text/html"; + soft_timeout ("send menu", r); + send_http_header(r); + rvputs(r, "\nMenu for ", r->uri, + "\n\n\n", NULL); + } + return; +} + +void menu_blank(request_rec *r, char *menu) +{ + if (! strcasecmp(menu, "formatted") ) { + rputs("\n", r); + } + if (! strcasecmp(menu, "semiformatted") ) { + rputs("
\n", r); + } + if (! strcasecmp(menu, "unformatted") ) { + rputs("\n", r); + } + return; +} + +void menu_comment(request_rec *r, char *menu, char *comment) +{ + if (! strcasecmp(menu, "formatted") ) { + rputs("\n", r); /* print just a newline if 'formatted' */ + } + if (! strcasecmp(menu, "semiformatted") && *comment ) { + rvputs(r, comment, "\n", NULL); + } + if (! strcasecmp(menu, "unformatted") && *comment ) { + rvputs(r, comment, "\n", NULL); + } + return; /* comments are ignored in the 'formatted' form */ +} + +void menu_default(request_rec *r, char *menu, char *href, char *text) +{ + if ( ! strcasecmp(href, "error") || ! strcasecmp(href, "nocontent") ) { + return; /* don't print such lines, these aren'te really href's */ + } + if ( ! strcasecmp(menu, "formatted" ) ) { + rvputs(r, "
(Default) ", text, "
\n", + NULL); + } + if ( ! strcasecmp(menu, "semiformatted" ) ) { + rvputs(r, "
(Default) ", text, "
\n", + NULL); + } + if ( ! strcasecmp(menu, "unformatted" ) ) { + rvputs(r, "", text, "", NULL); + } + return; +} + +void menu_directive(request_rec *r, char *menu, char *href, char *text) +{ + if ( ! strcasecmp(href, "error") || ! strcasecmp(href, "nocontent") ) { + return; /* don't print such lines, as this isn't really an href */ + } + if ( ! strcasecmp(menu, "formatted" ) ) { + rvputs(r, "
          ", text, "
\n", + NULL); + } + if ( ! strcasecmp(menu, "semiformatted" ) ) { + rvputs(r, "
          ", text, "
\n", + NULL); + } + if ( ! strcasecmp(menu, "unformatted" ) ) { + rvputs(r, "", text, "", NULL); + } + return; +} + +void menu_footer(request_rec *r) +{ + rputs("\n\n\n\n", r); /* finish the menu */ +} + +int imap_handler(request_rec *r) +{ + char input[LARGEBUF] = {'\0'}; + char href_text[SMALLBUF] = {'\0'}; + char base[SMALLBUF] = {'\0'}; + char redirect[SMALLBUF] = {'\0'}; + char directive[SMALLBUF] = {'\0'}; + char value[SMALLBUF] = {'\0'}; + char mapdflt[SMALLBUF] = {'\0'}; + char closest[SMALLBUF] = {'\0'}; + double closest_yet = -1; + + double testpoint[2] = { -1,-1 }; + double pointarray[MAXVERTS + 1][2] = { {-1,-1} }; + int vertex = 0; + + char *string_pos = NULL; + int chars_read = 0; + int showmenu = 0; + + imap_conf_rec *icr = get_module_config(r->per_dir_config, &imap_module); + + char *imap_menu = icr->imap_menu ? + icr->imap_menu : IMAP_MENU_DEFAULT; + char *imap_default = icr->imap_default ? + icr->imap_default : IMAP_DEFAULT_DEFAULT; + char *imap_base = icr->imap_base ? + icr->imap_base : IMAP_BASE_DEFAULT; + + FILE *imap = pfopen(r->pool, r->filename, "r"); + + if ( ! imap ) + return NOT_FOUND; + + imap_url(r, NULL, imap_base, base); /* set base according to default */ + imap_url(r, NULL, imap_default, mapdflt); /* and default to global default */ + + testpoint[X] = get_x_coord(r->args); + testpoint[Y] = get_y_coord(r->args); + + if ((testpoint[X] == -1 || testpoint[Y] == -1) || + (testpoint[X] == 0 && testpoint[Y] == 0) ) { + /* if either is -1 or if both are zero (new Lynx) */ + /* we don't have valid coordinates */ + testpoint[X] = -1; + testpoint[Y] = -1; + if ( strncasecmp(imap_menu, "none", 2) ) + showmenu = 1; /* show the menu _unless_ ImapMenu is 'none' or 'no' */ + } + + if (showmenu) { /* send start of imagemap menu if we're going to */ + menu_header(r, imap_menu); + } + + while (!cfg_getline(input, LARGEBUF, imap)) { + string_pos = input; /* always start at the beginning of line */ + + directive[0] = '\0'; + value[0] = '\0'; + href_text[0] = '\0'; + redirect[0] = '\0'; + chars_read = 0; /* clear these before using */ + + if ( ! input[0] ) { + if (showmenu) { + menu_blank(r, imap_menu); + } + continue; + } + + if ( input[0] == '#' ) { + if (showmenu) { + menu_comment(r, imap_menu, input + 1); + } + continue; + } /* blank lines and comments are ignored if we aren't printing a menu */ + + + if (sscanf(input, "%s %s", directive, value) != 2) { + continue; /* make sure we read two fields */ + } + /* Now skip what we just read... we can't use ANSIism %n */ + while (!(isspace(*string_pos))) /* past directive */ + string_pos++; + while (isspace(*string_pos)) /* and whitespace */ + string_pos++; + while (!(isspace(*string_pos))) /* and value... have to watch it */ + string_pos++; /* can have punctuation and stuff */ + + if ( ! strncasecmp(directive, "base", 4 ) ) { /* base, base_uri */ + imap_url(r, NULL, value, base); + continue; /* base is never printed to a menu */ + } + + chars_read = read_quoted(string_pos, href_text); + string_pos += chars_read; /* read the quoted href text if present */ + + if ( ! strcasecmp(directive, "default" ) ) { /* default */ + imap_url(r, NULL, value, mapdflt); + if (showmenu) { /* print the default if there's a menu */ + if (! *href_text) { /* if we didn't find a "href text" */ + strcpy(href_text, mapdflt); /* use the href itself as text */ + } + imap_url(r, base, mapdflt, redirect); + menu_default(r, imap_menu, redirect, href_text); + } + continue; + } + + vertex = 0; + while ( vertex < MAXVERTS && + sscanf(string_pos, "%lf,%lf", + &pointarray[vertex][X], &pointarray[vertex][Y]) == 2) + { + /* Now skip what we just read... we can't use ANSIism %n */ + while(isspace(*string_pos)) /* past whitespace */ + string_pos++; + while(isdigit(*string_pos)) /* and the 1st number */ + string_pos++; + string_pos++; /* skip the ',' */ + while(isdigit(*string_pos)) /* 2nd number */ + string_pos++; + vertex++; + } /* so long as there are more vertices to read, and + we have room, read them in. We start where we left + off of the last sscanf, not at the beginning.*/ + + pointarray[vertex][X] = -1; /* signals the end of vertices */ + + if (showmenu) { + read_quoted(string_pos, href_text); /* href text could be here instead */ + if (! *href_text) { /* if we didn't find a "href text" */ + strcpy(href_text, value); /* use the href itself in the menu */ + } + imap_url(r, base, value, redirect); + menu_directive(r, imap_menu, redirect, href_text); + continue; + } + /* note that we don't make it past here if we are making a menu */ + + if (testpoint[X] == -1 || pointarray[0][X] == -1 ) + continue; /* don't try the following tests if testpoints + are invalid, or if there are no coordinates */ + + if ( ! strcasecmp(directive, "poly" ) ) { /* poly */ + + if (pointinpoly (testpoint, pointarray) ) { + pfclose(r->pool, imap); + imap_url(r, base, value, redirect); + return (imap_reply(r, redirect)); + } + continue; + } + + if ( ! strcasecmp(directive, "circle" ) ) { /* circle */ + + if (pointincircle (testpoint, pointarray) ) { + pfclose(r->pool, imap); + imap_url(r, base, value, redirect); + return (imap_reply(r, redirect)); + } + continue; + } + + if ( ! strcasecmp(directive, "rect" ) ) { /* rect */ + + if (pointinrect (testpoint, pointarray) ) { + pfclose(r->pool, imap); + imap_url(r, base, value, redirect); + return (imap_reply(r, redirect)); + } + continue; + } + + if ( ! strcasecmp(directive, "point" ) ) { /* point */ + + if (is_closer(testpoint, pointarray, &closest_yet) ) { + strcpy(closest, value); /* if the closest point yet save it */ + } + + continue; + } /* move on to next line whether it's closest or not */ + + } /* nothing matched, so we get another line! */ + + pfclose(r->pool, imap); /* we are done with the map file, so close it */ + + if (showmenu) { + menu_footer(r); /* finish the menu and we are done */ + return OK; + } + + if (*closest) { /* if a 'point' directive has been seen */ + imap_url(r, base, closest, redirect); + return (imap_reply(r, redirect)); + } + + if (*mapdflt ) { /* a default should be defined, even if only 'nocontent'*/ + imap_url(r, base, mapdflt, redirect); + return(imap_reply(r, redirect)); + } + + return SERVER_ERROR; /* If we make it this far, we failed. They lose! */ +} + + +handler_rec imap_handlers[] = { +{ IMAP_MAGIC_TYPE, imap_handler }, +{ "imap-file", imap_handler }, +{ NULL } +}; + +module imap_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_imap_dir_config, /* dir config creater */ + merge_imap_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + imap_cmds, /* command table */ + imap_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_include.c b/RELEASE_1_1_X/src/modules/standard/mod_include.c new file mode 100644 index 00000000000..dcd5b0fd5ee --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_include.c @@ -0,0 +1,908 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * http_include.c: Handles the server-parsed HTML documents + * + * Original by Rob McCool; substantial fixups by David Robinson; + * incorporated into the Shambhala module framework by rst. + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" + +#define STARTING_SEQUENCE "" +#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]" +#define DEFAULT_TIME_FORMAT "%A, %d-%b-%y %T %Z" +#define SIZEFMT_BYTES 0 +#define SIZEFMT_KMG 1 + +static void decodehtml(char *s); +static char *get_tag(pool *p, FILE *in, char *tag, int tag_len, int dodecode); +static int get_directive(FILE *in, char *d, pool *p); + +/* ------------------------ Environment function -------------------------- */ + +void add_include_vars(request_rec *r, char *timefmt) +{ + struct passwd *pw; + table *e = r->subprocess_env; + char *t; + time_t date = time(NULL); + + table_set(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0)); + table_set(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1)); + table_set(e, "LAST_MODIFIED",ht_time(r->pool,r->finfo.st_mtime,timefmt,0)); + table_set(e, "DOCUMENT_URI", r->uri); + table_set(e, "DOCUMENT_PATH_INFO", r->path_info); + pw = getpwuid(r->finfo.st_uid); + if (pw) { + table_set(e, "USER_NAME", pw->pw_name); + } else { + char uid[16]; + sprintf(uid, "user#%ld", (unsigned long)r->finfo.st_uid); + table_set(e, "USER_NAME", uid); + } + + if((t = strrchr(r->filename, '/'))) + table_set (e, "DOCUMENT_NAME", ++t); + else + table_set (e, "DOCUMENT_NAME", r->uri); + if (r->args) { + unescape_url (r->args); + table_set (e, "QUERY_STRING_UNESCAPED", + escape_shell_cmd (r->pool, r->args)); + } +} + +#define GET_CHAR(f,c,r,p) \ + { \ + int i = getc(f); \ + if(feof(f) || ferror(f) || (i == -1)) { \ + pfclose(p, f); \ + return r; \ + } \ + c = (char)i; \ + } + +/* --------------------------- Parser functions --------------------------- */ + +/* Grrrr... rputc makes this slow as all-get-out. Elsewhere, it doesn't + * matter much, but this is an inner loop... + */ + +int find_string(FILE *in,char *str, request_rec *r) { + int x,l=strlen(str),p; + char c; + + p=0; + while(1) { + GET_CHAR(in,c,1,r->pool); + if(c == str[p]) { + if((++p) == l) + return 0; + } + else { + if(r) { + if(p) { + for(x=0;x= 11 && val <= 31) || + (val >= 127 && val <= 160) || val >= 256) + p--; /* no data to output */ + else + *p = val; + } else{ + j = i-1; + if (i-1 > MAXENTLEN || entlist[i-1] == NULL) { /* wrong length */ + *p = '&'; + continue; /* skip it */ + } + for (ents=entlist[i-1]; *ents != '\0'; ents += i) + if (strncmp(s+1, ents, i-1) == 0) break; + + if (*ents == '\0') + *p = '&'; /* unknown */ + else { + *p = ((const unsigned char *)ents)[i-1]; + s += i; + } + } + } + + *p = '\0'; +} + +/* + * extract the next tag name and value. + * if there are no more tags, set the tag name to 'done' + * the tag value is html decoded if dodecode is non-zero + */ + +static char * +get_tag(pool *p, FILE *in, char *tag, int tagbuf_len, int dodecode) { + char *t = tag, *tag_val, c, term; + int n; + + n = 0; + + do { /* skip whitespace */ + GET_CHAR(in,c,NULL,p); + } while (isspace(c)); + + /* tags can't start with - */ + if(c == '-') { + GET_CHAR(in,c,NULL,p); + if(c == '-') { + do { + GET_CHAR(in,c,NULL,p); + } while (isspace(c)); + if(c == '>') { + strcpy(tag,"done"); + return tag; + } + } + return NULL; /* failed */ + } + + /* find end of tag name */ + while(1) { + if(++n == tagbuf_len) { + t[tagbuf_len - 1] = '\0'; + return NULL; + } + if(c == '=' || isspace(c)) break; + *(t++) = tolower(c); + GET_CHAR(in,c,NULL,p); + } + + *t++ = '\0'; + tag_val = t; + + while (isspace(c)) GET_CHAR(in, c, NULL,p); /* space before = */ + if (c != '=') return NULL; + + do { + GET_CHAR(in,c,NULL,p); /* space after = */ + } while (isspace(c)); + + /* we should allow a 'name' as a value */ + + if (c != '"' && c != '\'') return NULL; + term = c; + while(1) { + GET_CHAR(in,c,NULL,p); + if(++n == tagbuf_len) { + t[tagbuf_len - 1] = '\0'; + return NULL; + } + if (c == term) break; + *(t++) = c; + } + *t = '\0'; + if (dodecode) decodehtml(tag_val); + return pstrdup (p, tag_val); +} + +/* the pool is required to allow GET_CHAR to call pfclose */ +static int +get_directive(FILE *in, char *d, pool *p) { + char c; + + /* skip initial whitespace */ + while(1) { + GET_CHAR(in,c,1,p); + if(!isspace(c)) + break; + } + /* now get directive */ + while(1) { + *d++ = tolower(c); + GET_CHAR(in,c,1,p); + if(isspace(c)) + break; + } + *d = '\0'; + return 0; +} + +/* --------------------------- Action handlers ---------------------------- */ + +int include_cgi(char *s, request_rec *r) +{ + request_rec *rr = sub_req_lookup_uri (s, r); + + if (rr->status != 200) return -1; + + /* No hardwired path info or query allowed */ + + if ((rr->path_info && rr->path_info[0]) || rr->args) return -1; + if (rr->finfo.st_mode == 0) return -1; + + /* Script gets parameters of the *document*, for back compatibility */ + + rr->path_info = r->path_info; /* painful to get right; see mod_cgi.c */ + rr->args = r->args; + + /* Force sub_req to be treated as a CGI request, even if ordinary + * typing rules would have called it something else. + */ + + rr->content_type = CGI_MAGIC_TYPE; + + /* Run it. */ + + if (run_sub_req (rr) == REDIRECT) { + char *location = table_get (rr->headers_out, "Location"); + location = escape_html(rr->pool, location); + rvputs(r,"", location, "", NULL); + } + + destroy_sub_req (rr); + + return 0; +} + +int handle_include(FILE *in, request_rec *r, char *error, int noexec) { + char tag[MAX_STRING_LEN]; + char *tag_val; + + while(1) { + if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) + return 1; + if(!strcmp(tag,"file") || !strcmp (tag, "virtual")) { + request_rec *rr=NULL; + char *error_fmt = NULL; + + if (tag[0] == 'f') + { /* be safe; only files in this directory or below allowed */ + char tmp[MAX_STRING_LEN+2]; + sprintf(tmp, "/%s/", tag_val); + if (tag_val[0] == '/' || strstr(tmp, "/../") != NULL) + error_fmt = "unable to include file %s in parsed file %s"; + else + rr = sub_req_lookup_file (tag_val, r); + } else + rr = sub_req_lookup_uri (tag_val, r); + + if (!error_fmt && rr->status != 200) + error_fmt = "unable to include %s in parsed file %s"; + + if (!error_fmt && noexec && rr->content_type + && (strncmp (rr->content_type, "text/", 5))) + error_fmt = + "unable to include potential exec %s in parsed file %s"; + + if (error_fmt == NULL) + { + request_rec *p; + + for (p=r; p != NULL; p=p->main) + if (strcmp(p->filename, rr->filename) == 0) break; + if (p != NULL) + error_fmt = "Recursive include of %s in parsed file %s"; + } + + if (!error_fmt && run_sub_req (rr)) + error_fmt = "unable to include %s in parsed file %s"; + + if (error_fmt) { + log_printf(r->server, error_fmt, tag_val, r->filename); + rputs(error, r); + } + + if (rr != NULL) destroy_sub_req (rr); + } + else if(!strcmp(tag,"done")) + return 0; + else { + log_printf(r->server, "unknown parameter %s to tag include in %s", + tag, r->filename); + rputs(error, r); + } + } +} + +typedef struct { + request_rec *r; + char *s; +} include_cmd_arg; + +void include_cmd_child (void *arg) +{ + request_rec *r = ((include_cmd_arg *)arg)->r; + char *s = ((include_cmd_arg *)arg)->s; + table *env = r->subprocess_env; +#ifdef DEBUG_INCLUDE_CMD +#ifdef __EMX__ + /* under OS/2 /dev/tty is referenced as con */ + FILE *dbg = fopen ("con", "w"); +#else + FILE *dbg = fopen ("/dev/tty", "w"); +#endif +#endif + char err_string [MAX_STRING_LEN]; + +#ifdef DEBUG_INCLUDE_CMD + fprintf (dbg, "Attempting to include command '%s'\n", s); +#endif + + if (r->path_info && r->path_info[0] != '\0') + { + request_rec *pa_req; + + table_set (env, "PATH_INFO", escape_shell_cmd (r->pool, r->path_info)); + + pa_req = sub_req_lookup_uri(escape_uri(r->pool, r->path_info), r); + if (pa_req->filename) + table_set(env, "PATH_TRANSLATED", + pstrcat(r->pool, pa_req->filename, pa_req->path_info, + NULL)); + } + + if (r->args) { + table_set (env, "QUERY_STRING", r->args); + unescape_url (r->args); + table_set (env, "QUERY_STRING_UNESCAPED", + escape_shell_cmd (r->pool, r->args)); + } + + error_log2stderr (r->server); + +#ifdef DEBUG_INCLUDE_CMD + fprintf (dbg, "Attempting to exec '%s'\n", s); +#endif + cleanup_for_exec(); + execle(SHELL_PATH, SHELL_PATH, "-c", s, NULL, + create_environment (r->pool, env)); + + /* Oh, drat. We're still here. The log file descriptors are closed, + * so we have to whimper a complaint onto stderr... + */ + +#ifdef DEBUG_INCLUDE_CMD + fprintf (dbg, "Exec failed\n"); +#endif + sprintf(err_string, "httpd: exec of %s failed, errno is %d\n", + SHELL_PATH,errno); + write (2, err_string, strlen(err_string)); + exit(0); +} + +int include_cmd(char *s, request_rec *r) { + include_cmd_arg arg; + FILE *f; + + arg.r = r; arg.s = s; + + if (!spawn_child (r->connection->pool, include_cmd_child, &arg, + kill_after_timeout, NULL, &f)) + return -1; + + send_fd(f,r); + pfclose(r->pool, f); /* will wait for zombie when + * r->pool is cleared + */ + return 0; +} + + +int handle_exec(FILE *in, request_rec *r, char *error) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + char *file = r->filename; + + while(1) { + if(!(tag_val = get_tag (r->pool, in, tag, MAX_STRING_LEN, 1))) + return 1; + if(!strcmp(tag,"cmd")) { + if(include_cmd(tag_val, r) == -1) { + log_printf(r->server, "failed command exec %s in %s", + tag_val, file); + rputs(error, r); + } + /* just in case some stooge changed directories */ + chdir_file(r->filename); + } + else if(!strcmp(tag,"cgi")) { + if(include_cgi(tag_val, r) == -1) { + log_printf(r->server, "invalid CGI ref %s in %s",tag_val,file); + rputs(error, r); + } + /* grumble groan */ + chdir_file(r->filename); + } + else if(!strcmp(tag,"done")) + return 0; + else { + log_printf(r->server, "unknown parameter %s to tag exec in %s", + tag, file); + rputs(error, r); + } + } + +} + +int handle_echo (FILE *in, request_rec *r, char *error) { + char tag[MAX_STRING_LEN]; + char *tag_val; + + while(1) { + if(!(tag_val = get_tag (r->pool, in, tag, MAX_STRING_LEN, 1))) + return 1; + if(!strcmp(tag,"var")) { + char *val = table_get (r->subprocess_env, tag_val); + + if (val) rputs(val, r); + else rputs("(none)", r); + } else if(!strcmp(tag,"done")) + return 0; + else { + log_printf(r->server, "unknown parameter %s to tag echo in %s", + tag, r->filename); + rputs(error, r); + } + } +} + +int handle_config(FILE *in, request_rec *r, char *error, char *tf, + int *sizefmt) { + char tag[MAX_STRING_LEN]; + char *tag_val; + table *env = r->subprocess_env; + + while(1) { + if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 0))) + return 1; + if(!strcmp(tag,"errmsg")) + strcpy(error,tag_val); + else if(!strcmp(tag,"timefmt")) { + time_t date = time(NULL); + strcpy(tf,tag_val); + table_set (env, "DATE_LOCAL", ht_time(r->pool,date,tf,0)); + table_set (env, "DATE_GMT", ht_time(r->pool,date,tf,1)); + table_set (env, "LAST_MODIFIED", ht_time(r->pool,r->finfo.st_mtime,tf,0)); + } + else if(!strcmp(tag,"sizefmt")) { + decodehtml(tag_val); + if(!strcmp(tag_val,"bytes")) + *sizefmt = SIZEFMT_BYTES; + else if(!strcmp(tag_val,"abbrev")) + *sizefmt = SIZEFMT_KMG; + } + else if(!strcmp(tag,"done")) + return 0; + else { + log_printf(r->server, "unknown parameter %s to tag config in %s", + tag, r->filename); + rputs(error, r); + } + } +} + + + +int find_file(request_rec *r, char *directive, char *tag, + char *tag_val, struct stat *finfo, char *error) +{ + char dir[MAX_STRING_LEN]; + char *to_send; + + if(!strcmp(tag,"file")) { + getparents(tag_val); /* get rid of any nasties */ + getwd(dir); + to_send = make_full_path (r->pool, dir, tag_val); + if(stat(to_send,finfo) == -1) { + log_printf(r->server, + "unable to get information about %s in parsed file %s", + to_send, r->filename); + rputs(error, r); + return -1; + } + return 0; + } + else if(!strcmp(tag,"virtual")) { + request_rec *rr = sub_req_lookup_uri (tag_val, r); + + if (rr->status == 200 && rr->finfo.st_mode != 0) { + memcpy ((char*)finfo, (const char *)&rr->finfo, sizeof (struct stat)); + destroy_sub_req (rr); + return 0; + } else { + log_printf(r->server, + "unable to get information about %s in parsed file %s", + tag_val, r->filename); + rputs(error, r); + destroy_sub_req (rr); + return -1; + } + } + else { + log_printf(r->server, "unknown parameter %s to tag %s in %s", + tag, directive, r->filename); + rputs(error, r); + return -1; + } +} + + +int handle_fsize(FILE *in, request_rec *r, char *error, int sizefmt) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + struct stat finfo; + + while(1) { + if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) + return 1; + else if(!strcmp(tag,"done")) + return 0; + else if(!find_file(r,"fsize",tag,tag_val,&finfo,error)) { + if(sizefmt == SIZEFMT_KMG) { + send_size(finfo.st_size, r); + } + else { + int l,x; +#if defined(BSD) && BSD > 199305 + sprintf(tag,"%qd",finfo.st_size); +#else + sprintf(tag,"%ld",finfo.st_size); +#endif + l = strlen(tag); /* grrr */ + for(x=0;xpool, in, tag, MAX_STRING_LEN, 1))) + return 1; + else if(!strcmp(tag,"done")) + return 0; + else if(!find_file(r,"flastmod",tag,tag_val,&finfo,error)) + rputs(ht_time(r->pool, finfo.st_mtime, tf, 0), r); + } +} + + + +/* -------------------------- The main function --------------------------- */ + +/* This is a stub which parses a file descriptor. */ + +void send_parsed_content(FILE *f, request_rec *r) +{ + char directive[MAX_STRING_LEN], error[MAX_STRING_LEN]; + char timefmt[MAX_STRING_LEN]; + int noexec = allow_options (r) & OPT_INCNOEXEC; + int ret, sizefmt; + + strcpy(error,DEFAULT_ERROR_MSG); + strcpy(timefmt,DEFAULT_TIME_FORMAT); + sizefmt = SIZEFMT_KMG; + + chdir_file (r->filename); + + while(1) { + if(!find_string(f,STARTING_SEQUENCE,r)) { + if(get_directive(f,directive,r->pool)) + return; + if(!strcmp(directive,"exec")) { + if(noexec) { + log_printf(r->server, + "httpd: exec used but not allowed in %s", + r->filename); + rputs(error, r); + ret = find_string(f,ENDING_SEQUENCE,NULL); + } else + ret=handle_exec(f, r, error); + } + else if(!strcmp(directive,"config")) + ret=handle_config(f, r, error, timefmt, &sizefmt); + else if(!strcmp(directive,"include")) + ret=handle_include(f, r, error, noexec); + else if(!strcmp(directive,"echo")) + ret=handle_echo(f, r, error); + else if(!strcmp(directive,"fsize")) + ret=handle_fsize(f, r, error, sizefmt); + else if(!strcmp(directive,"flastmod")) + ret=handle_flastmod(f, r, error, timefmt); + else { + log_printf(r->server, + "httpd: unknown directive %s in parsed doc %s", + directive, r->filename); + rputs(error, r); + ret=find_string(f,ENDING_SEQUENCE,NULL); + } + if(ret) { + log_printf(r->server, "httpd: premature EOF in parsed file %s", + r->filename); + return; + } + } else + return; + } +} + +/***************************************************************** + * + * XBITHACK. Sigh... NB it's configurable per-directory; the compile-time + * option only changes the default. + */ + +module includes_module; +enum xbithack { xbithack_off, xbithack_on, xbithack_full }; + +#ifdef XBITHACK +#define DEFAULT_XBITHACK xbithack_full +#else +#define DEFAULT_XBITHACK xbithack_off +#endif + +void *create_includes_dir_config (pool *p, char *dummy) +{ + enum xbithack *result = (enum xbithack*)palloc(p, sizeof (enum xbithack)); + *result = DEFAULT_XBITHACK; + return result; +} + +char *set_xbithack (cmd_parms *cmd, void *xbp, char *arg) +{ + enum xbithack *state = (enum xbithack *)xbp; + + if (!strcasecmp (arg, "off")) *state = xbithack_off; + else if (!strcasecmp (arg, "on")) *state = xbithack_on; + else if (!strcasecmp (arg, "full")) *state = xbithack_full; + else return "XBitHack must be set to Off, On, or Full"; + + return NULL; +} + +int send_parsed_file(request_rec *r) +{ + FILE *f; + enum xbithack *state = + (enum xbithack *)get_module_config(r->per_dir_config,&includes_module); + int errstatus; + + if (!(allow_options (r) & OPT_INCLUDES)) return DECLINED; + if (r->method_number != M_GET) return DECLINED; + if (r->finfo.st_mode == 0) return NOT_FOUND; + + if (*state == xbithack_full +#ifndef __EMX__ + /* OS/2 dosen't support Groups. */ + && (r->finfo.st_mode & S_IXGRP) +#endif + && (errstatus = set_last_modified (r, r->finfo.st_mtime))) + return errstatus; + + if(!(f=pfopen(r->pool, r->filename, "r"))) { + log_reason("file permissions deny server access", r->filename, r); + return FORBIDDEN; + } + + hard_timeout ("send", r); + send_http_header(r); + + if (r->header_only) { + kill_timeout (r); + pfclose (r->pool, f); + return OK; + } + + if (r->main) { + /* Kludge --- for nested includes, we want to keep the + * subprocess environment of the base document (for compatibility); + * that means torquing our own last_modified date as well so that + * the LAST_MODIFIED variable gets reset to the proper value if + * the nested document resets + */ + r->subprocess_env = r->main->subprocess_env; + r->finfo.st_mtime= r->main->finfo.st_mtime; + } else { + add_common_vars (r); + add_cgi_vars(r); + add_include_vars (r, DEFAULT_TIME_FORMAT); + } + + send_parsed_content (f, r); + + kill_timeout (r); + return OK; +} + +int send_shtml_file (request_rec *r) +{ + r->content_type = "text/html"; + return send_parsed_file(r); +} + +int xbithack_handler (request_rec *r) +{ + enum xbithack *state; + +#ifdef __EMX__ + /* OS/2 dosen't currently support the xbithack. This is being worked on. */ + return DECLINED; +#else + + if (!(r->finfo.st_mode & S_IXUSR)) return DECLINED; + + state = (enum xbithack *)get_module_config(r->per_dir_config, + &includes_module); + + if (*state == xbithack_off) return DECLINED; + return send_parsed_file (r); +#endif +} + +command_rec includes_cmds[] = { +{ "XBitHack", set_xbithack, NULL, OR_OPTIONS, TAKE1, "Off, On, or Full" }, +{ NULL } +}; + +handler_rec includes_handlers[] = { +{ INCLUDES_MAGIC_TYPE, send_shtml_file }, +{ INCLUDES_MAGIC_TYPE3, send_shtml_file }, +{ "server-parsed", send_parsed_file }, +{ "text/html", xbithack_handler }, +{ NULL } +}; + +module includes_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_includes_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + includes_cmds, /* command table */ + includes_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_info.c b/RELEASE_1_1_X/src/modules/standard/mod_info.c new file mode 100644 index 00000000000..644d519c141 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_info.c @@ -0,0 +1,426 @@ +/* ==================================================================== + * Copyright (c) 1995, 1996 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. 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 . + * + */ + +/* + * Info Module. Display configuration information for the server and + * all included modules. + * + * + * SetHandler server-info + * + * + * GET /info - Returns full configuration page for server and all modules + * GET /info?server - Returns server configuration only + * GET /info?module_name - Returns configuration for a single module + * GET /info?list - Returns quick list of included modules + * + * Rasmus Lerdorf , May 1996 + * + * 05.01.96 Initial Version + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_main.h" +#include "http_protocol.h" +#include "util_script.h" + +typedef struct mod_info_config_lines { + char *cmd; + char *line; + struct mod_info_config_lines *next; +} mod_info_config_lines; + +module info_module; +extern module *top_module; + +char *mod_info_html_cmd_string(char *string) { + char *s,*t; + static char ret[64]; /* What is the max size of a command? */ + + ret[0]='\0'; + s = string; + t=ret; + while(*s) { + if(*s=='<') { strcat(t,"<"); t+=4*sizeof(char); } + else if(*s=='>') { strcat(t,">"); t+=4*sizeof(char); } + else *t++=*s; + s++; + *t='\0'; + } + return(ret); +} + +mod_info_config_lines *mod_info_load_config(pool *p, char *filename) { + char s[MAX_STRING_LEN]; + FILE *fp; + mod_info_config_lines *new, *ret=NULL, *prev=NULL; + char *t,*tt,o; + + fp = pfopen(p,filename,"r"); + if(!fp) return NULL; + while(!cfg_getline(s,MAX_STRING_LEN,fp)) { + if(*s=='#') continue; /* skip comments */ + new = palloc(p,sizeof(struct mod_info_config_lines)); + new->next = NULL; + if(!ret) ret=new; + if(prev) prev->next=new; + t=strchr(s,' '); + tt=strchr(s,'\t'); + if(t && tt) t = (tcmd = pstrdup(p,s); + new->line = pstrdup(p,t+1); + *t=o; + } else { + new->cmd = pstrdup(p,s); + new->line = NULL; + } + prev=new; + } + pfclose(p,fp); + return(ret); +} + +void mod_info_module_cmds(request_rec *r, mod_info_config_lines *cfg, command_rec *cmds,char *label) { + command_rec *cmd=cmds; + mod_info_config_lines *li=cfg,*li_st=NULL,*li_se=NULL,*block_start=NULL; + int lab=0, nest=0; + + while(li) { + if(!strncasecmp(li->cmd,"cmd,"cmd,"next; + nest++; + continue; + } else if(nest && (!strncasecmp(li->cmd,"cmd,"cmd,"",r); + if(nest==2) rputs("  ",r); + rputs(mod_info_html_cmd_string(li->cmd),r); + rputs(" ",r); + if(li->line) rputs(mod_info_html_cmd_string(li->line),r); + rputs("\n",r); + nest--; + if(!nest) { + block_start=NULL; + li_st=NULL; + } else { + block_start=li_st; + } + li_se=NULL; + } else { + nest--; + if(!nest) { + li_st=NULL; + } + li_se=NULL; + } + } else { + nest--; + if(!nest) { + li_st=NULL; + } + li_se=NULL; + } + li=li->next; + continue; + } + cmd = cmds; + while(cmd) { + if(cmd->name) { + if(!strcasecmp(cmd->name,li->cmd)) { + if(!lab) { + rputs("
",r); + rputs(label,r); + rputs("\n",r); + lab=1; + } + if(((nest && block_start==NULL) || (nest==2 && block_start==li_st)) + && (strncasecmp(li->cmd,"cmd,"cmd,"cmd,"cmd,"cmd,"",r); + rputs(mod_info_html_cmd_string(li_st->cmd),r); + rputs(" ",r); + if(li_st->line) rputs(mod_info_html_cmd_string(li_st->line),r); + rputs("\n",r); + block_start=li_st; + if(li_se) { + rputs("
  ",r); + rputs(mod_info_html_cmd_string(li_se->cmd),r); + rputs(" ",r); + if(li_se->line) rputs(mod_info_html_cmd_string(li_se->line),r); + rputs("\n",r); + block_start=li_se; + } + } + rputs("
",r); + if(nest) rputs("  ",r); + if(nest==2) rputs("  ",r); + rputs(mod_info_html_cmd_string(li->cmd),r); + if(li->line) { + rputs(" ",r); + rputs(mod_info_html_cmd_string(li->line),r); + rputs("",r); + } + } + } else break; + cmd++; + } + li = li->next; + } +} + +int display_info(request_rec *r) { + module *modp = NULL; + char buf[256]; + extern char *module_names[]; + char **names = module_names; + command_rec *cmd=NULL; + handler_rec *hand=NULL; + server_rec *serv = r->server; + int comma=0; + mod_info_config_lines *mod_info_cfg_httpd=NULL; + mod_info_config_lines *mod_info_cfg_srm=NULL; + mod_info_config_lines *mod_info_cfg_access=NULL; + extern int standalone; + extern uid_t user_id; + extern char *user_name; + extern gid_t group_id; + extern int max_requests_per_child; + extern char *pid_fname; + extern char *scoreboard_fname; + extern int daemons_to_start; + extern int daemons_min_free; + extern int daemons_max_free; + extern int daemons_limit; + extern char server_root[MAX_STRING_LEN]; + extern char server_confname[MAX_STRING_LEN]; + + /* Init timeout */ + soft_timeout ("send server info", r); + r->content_type = "text/html"; + send_http_header(r); + if(r->header_only) { + return 0; + } + + rputs("Server Information\n",r); + rputs("

Apache Server Information

\n",r); + if(!r->args || strcasecmp(r->args,"list")) { + sprintf(buf,"%s/%s",server_root,server_confname); + mod_info_cfg_httpd = mod_info_load_config(r->pool,buf); + sprintf(buf,"%s/%s",server_root,serv->srm_confname); + mod_info_cfg_srm = mod_info_load_config(r->pool,buf); + sprintf(buf,"%s/%s",server_root,serv->access_confname); + mod_info_cfg_access = mod_info_load_config(r->pool,buf); + if(!r->args) { + rputs("Server Settings, ",r); + for(modp = top_module, names=module_names; modp; modp = modp->next, names++) { + sprintf(buf,"%s",*names,*names); + rputs(buf, r); + if(modp->next) rputs(", ",r); + } + rputs("
",r); + + } + if(!r->args || !strcasecmp(r->args,"server")) { + sprintf(buf,"Server Version: %s
\n",SERVER_VERSION); + rputs(buf,r); + sprintf(buf,"API Version: %d
\n",MODULE_MAGIC_NUMBER); + rputs(buf,r); + sprintf(buf,"Run Mode: %s
\n",standalone?"standalone":"inetd"); + rputs(buf,r); + sprintf(buf,"User/Group: %s(%d)/%d
\n",user_name,(int)user_id,(int)group_id); + rputs(buf,r); + sprintf(buf,"Hostname/port: %s:%d
\n",serv->server_hostname,serv->port); + rputs(buf,r); + sprintf(buf,"Daemons: start: %d    min idle: %d    max idle: %d    max: %d
\n",daemons_to_start,daemons_min_free,daemons_max_free,daemons_limit); + rputs(buf,r); + sprintf(buf,"Max Requests: per child: %d    per connection: %d
\n",max_requests_per_child,serv->keep_alive); + rputs(buf,r); + sprintf(buf,"Timeouts: connection: %d    keep-alive: %d
",serv->timeout,serv->keep_alive_timeout); + rputs(buf,r); + sprintf(buf,"Server Root: %s
\n",server_root); + rputs(buf,r); + sprintf(buf,"Config File: %s
\n",server_confname); + rputs(buf,r); + sprintf(buf,"PID File: %s
\n",pid_fname); + rputs(buf,r); + sprintf(buf,"Scoreboard File: %s
\n",scoreboard_fname); + rputs(buf,r); + } + rputs("
",r); + for(modp = top_module, names=module_names; modp; modp = modp->next, names++) { + if(!r->args || !strcasecmp(*names,r->args)) { + sprintf(buf,"
Module Name: %s\n",*names,*names); + rputs(buf,r); + rputs("
Content-types affected:",r); + hand = modp->handlers; + if(hand) { + while(hand) { + if(hand->content_type) { + sprintf(buf," %s\n",hand->content_type); + rputs(buf,r); + } else break; + hand++; + if(hand && hand->content_type) rputs(",",r); + } + } else { + rputs(" none",r); + } + rputs("
Module Groups: \n",r); + if(modp->translate_handler) { + rputs("Translate Handler\n",r); + comma=1; + } + if(modp->check_user_id) { + if(comma) rputs(", ",r); + rputs("User ID Checking\n",r); + comma=1; + } + if(modp->auth_checker) { + if(comma) rputs(", ",r); + rputs("Authentication Checking\n",r); + comma=1; + } + if(modp->access_checker) { + if(comma) rputs(", ",r); + rputs("Access Checking\n",r); + comma=1; + } + if(modp->type_checker) { + if(comma) rputs(", ",r); + rputs("Type Checking\n",r); + comma=1; + } + if(modp->fixer_upper) { + if(comma) rputs(", ",r); + rputs("Header Fixer\n",r); + comma=1; + } + if(modp->logger) { + if(comma) rputs(", ",r); + rputs("Logging\n",r); + comma=1; + } + if(!comma) rputs(" none",r); + comma=0; + rputs("
Module Configuration Commands: ",r); + cmd = modp->cmds; + if(cmd) { + while(cmd) { + if(cmd->name) { + sprintf(buf,"
%s - ",mod_info_html_cmd_string(cmd->name)); + rputs(buf,r); + if(cmd->errmsg) rputs(cmd->errmsg,r); + rputs("\n",r); + } else break; + cmd++; + } + rputs("
Current Configuration:\n",r); + mod_info_module_cmds(r,mod_info_cfg_httpd,modp->cmds,"httpd.conf"); + mod_info_module_cmds(r,mod_info_cfg_srm,modp->cmds,"srm.conf"); + mod_info_module_cmds(r,mod_info_cfg_access,modp->cmds,"access.conf"); + } else { + rputs(" none\n",r); + } + rputs("

\n",r); + if(r->args) break; + } + } + if(!modp && r->args && strcasecmp(r->args,"server")) rputs("No such module\n",r); + } else { + for(modp = top_module; modp; modp = modp->next, names++) { + rputs(*names,r); + if(modp->next) rputs("
",r); + } + } + rputs("
\n",r); + /* Done, turn off timeout, close file and return */ + return 0; +} + +handler_rec info_handlers[] = { + { "server-info", display_info }, + { NULL } +}; + +module info_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + NULL, /* command table */ + info_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_log_agent.c b/RELEASE_1_1_X/src/modules/standard/mod_log_agent.c new file mode 100644 index 00000000000..14c185b9fa5 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_log_agent.c @@ -0,0 +1,193 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + + +#include "httpd.h" +#include "http_config.h" + +module agent_log_module; + +static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT ); +#ifdef __EMX__ +/* OS/2 dosen't support users and groups */ +static mode_t xfer_mode = ( S_IREAD | S_IWRITE ); +#else +static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); +#endif + +typedef struct { + char *fname; + int agent_fd; +} agent_log_state; + +void *make_agent_log_state (pool *p, server_rec *s) +{ + agent_log_state *cls = + (agent_log_state *)palloc (p, sizeof (agent_log_state)); + + cls->fname = ""; + cls->agent_fd = -1; + + + return (void *)cls; +} + +char *set_agent_log (cmd_parms *parms, void *dummy, char *arg) +{ + agent_log_state *cls = get_module_config (parms->server->module_config, + &agent_log_module); + + cls->fname = arg; + return NULL; +} + +command_rec agent_log_cmds[] = { +{ "AgentLog", set_agent_log, NULL, RSRC_CONF, TAKE1, + "the filename of the agent log" }, +{ NULL } +}; + +void agent_log_child (void *cmd) +{ + /* Child process code for 'AgentLog "|..."'; + * may want a common framework for this, since I expect it will + * be common for other foo-loggers to want this sort of thing... + */ + + cleanup_for_exec(); + signal (SIGHUP, SIG_IGN); + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); + exit (1); +} + +void open_agent_log (server_rec *s, pool *p) +{ + agent_log_state *cls = get_module_config (s->module_config, + &agent_log_module); + + char *fname = server_root_relative (p, cls->fname); + + if (cls->agent_fd > 0) return; /* virtual log shared w/main server */ + + if (*cls->fname == '|') { + FILE *dummy; + + spawn_child(p, agent_log_child, (void *)(cls->fname+1), + kill_after_timeout, &dummy, NULL); + + if (dummy == NULL) { + fprintf (stderr, "Couldn't fork child for AgentLog process\n"); + exit (1); + } + + cls->agent_fd = fileno (dummy); + } + else if(*cls->fname != '\0') { + if((cls->agent_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) { + fprintf(stderr,"httpd: could not open agent log file %s.\n", fname); + perror("open"); + exit(1); + } + } +} + +void init_agent_log (server_rec *s, pool *p) +{ + for (; s; s = s->next) open_agent_log (s, p); +} + +int agent_log_transaction(request_rec *orig) +{ + agent_log_state *cls = get_module_config (orig->server->module_config, + &agent_log_module); + + char str[HUGE_STRING_LEN]; + char *agent; + request_rec *r; + + if(cls->agent_fd <0) + return OK; + + for (r = orig; r->next; r = r->next) + continue; + if (*cls->fname == '\0') /* Don't log agent */ + return DECLINED; + + agent = table_get(orig->headers_in, "User-Agent"); + if(agent != NULL) + { + sprintf(str, "%s\n", agent); + write(cls->agent_fd, str, strlen(str)); + } + + return OK; +} + +module agent_log_module = { + STANDARD_MODULE_STUFF, + init_agent_log, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + make_agent_log_state, /* server config */ + NULL, /* merge server config */ + agent_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + agent_log_transaction /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_log_config.c b/RELEASE_1_1_X/src/modules/standard/mod_log_config.c new file mode 100644 index 00000000000..3084cccafae --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_log_config.c @@ -0,0 +1,573 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * This is module implements the TransferLog directive (same as the + * common log module), and an additional directive, LogFormat. + * + * The argument to LogFormat is a string, which can include literal + * characters copied into the log files, and '%' directives as follows: + * + * %...h: remote host + * %...l: remote logname (from identd, if supplied) + * %...u: remote user (from auth; may be bogus if return status (%s) is 401) + * %...t: time, in common log format time format + * %...r: first line of request + * %...s: status. For requests that got internally redirected, this + * is status of the *original* request --- %...>s for the last. + * %...b: bytes sent. + * %...{Foobar}i: The contents of Foobar: header line(s) in the request + * sent to the client. + * %...{Foobar}o: The contents of Foobar: header line(s) in the reply. + * + * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can + * indicate conditions for inclusion of the item (which will cause it + * to be replaced with '-' if the condition is not met). Note that + * there is no escaping performed on the strings from %r, %...i and + * %...o; some with long memories may remember that I thought this was + * a bad idea, once upon a time, and I'm still not comfortable with + * it, but it is difficult to see how to "do the right thing" with all + * of '%..i', unless we URL-escape everything and break with CLF. + * + * The forms of condition are a list of HTTP status codes, which may + * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs + * User-agent: on 400 errors and 501 errors (Bad Request, Not + * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all + * requests which did *not* return some sort of normal status. + * + * The default LogFormat reproduces CLF; see below. + * + * The way this is supposed to work with virtual hosts is as follows: + * a virtual host can have its own LogFormat, or its own TransferLog. + * If it doesn't have its own LogFormat, it inherits from the main + * server. If it doesn't have its own TransferLog, it writes to the + * same descriptor (meaning the same process for "| ..."). + * + * That means that you can do things like: + * + * + * LogFormat "hosta ..." + * ... + * + * + * + * LogFormat "hostb ..." + * ... + * + * + * ... to have different virtual servers write into the same log file, + * but have some indication which host they came from, though a %v + * directive may well be a better way to handle this. + * + * --- rst */ + +#define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %s %b" + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" /* For REMOTE_NAME */ + +module config_log_module; + +static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT ); +#ifdef __EMX__ +/* OS/2 dosen't support users and groups */ +static mode_t xfer_mode = ( S_IREAD | S_IWRITE ); +#else +static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); +#endif + +typedef struct { + char *fname; + array_header *format; + int log_fd; +} config_log_state; + +/* + * Format items... + */ + +typedef char *(*item_key_func)(request_rec *, char *); + +typedef struct { + item_key_func func; + char *arg; + int condition_sense; + int want_orig; + array_header *conditions; +} log_format_item; + +char *format_integer(pool *p, int i) +{ + char dummy[40]; + sprintf (dummy, "%d", i); + return pstrdup (p, dummy); +} + +static char *pfmt(pool *p, int i) +{ + if (i <= 0) return "-"; + else return format_integer (p, i); +} + +char *constant_item (request_rec *dummy, char *stuff) { return stuff; } + +char *log_remote_host (request_rec *r, char *a) +{ return (char *)get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME); } + +char *log_remote_logname(request_rec *r, char *a) +{return (char *)get_remote_logname(r);} + +char *log_remote_user (request_rec *r, char *a) +{ return r->connection->user; } + +char *log_request_line (request_rec *r, char *a) +{ return r->the_request; } + +char *log_status (request_rec *r, char *a) +{ return pfmt(r->pool, r->status); } + +char *log_bytes_sent (request_rec *r, char *a) +{ + if (!r->sent_bodyct) return "-"; + else + { + long int bs; + char dummy[40]; + bgetopt(r->connection->client, BO_BYTECT, &bs); + sprintf(dummy, "%ld", bs); + return pstrdup(r->pool, dummy); + } +} + +char *log_header_in (request_rec *r, char *a) +{ return table_get (r->headers_in, a); } + +char *log_header_out (request_rec *r, char *a) +{ + char *cp = table_get (r->headers_out, a); + if (cp) return cp; + return table_get (r->err_headers_out, a); +} + +char *log_env_var (request_rec *r, char *a) +{ return table_get (r->subprocess_env, a); } + +char *log_request_time (request_rec *r, char *a) +{ + long timz; + struct tm *t; + char tstr[MAX_STRING_LEN],sign; + + t = get_gmtoff(&timz); + sign = (timz < 0 ? '-' : '+'); + if(timz < 0) + timz = -timz; + + strftime(tstr,MAX_STRING_LEN,"[%d/%b/%Y:%H:%M:%S ",t); + + sprintf (tstr + strlen(tstr), "%c%02ld%02ld]", + sign, timz/3600, timz%3600); + + return pstrdup (r->pool, tstr); +} + +/***************************************************************** + * + * Parsing the log format string + */ + +struct log_item_list { + char ch; + item_key_func func; + int want_orig_default; +} log_item_keys[] = { + { 'h', log_remote_host, 0 }, + { 'l', log_remote_logname, 0 }, + { 'u', log_remote_user, 0 }, + { 't', log_request_time, 0 }, + { 'r', log_request_line, 1 }, + { 's', log_status, 1 }, + { 'b', log_bytes_sent, 0 }, + { 'i', log_header_in, 0 }, + { 'o', log_header_out, 0 }, + { 'e', log_env_var, 0 }, + { '\0' } +}; + +struct log_item_list *find_log_func (char k) +{ + int i; + + for (i = 0; log_item_keys[i].ch; ++i) + if (k == log_item_keys[i].ch) + return &log_item_keys[i]; + + return NULL; +} + +char *log_format_substring (pool *p, char *start, char *end) +{ + char *res = palloc (p, end - start + 1); + strncpy (res, start, end - start); + res[end - start] = '\0'; + return res; +} + +char *parse_log_misc_string (pool *p, log_format_item *it, char **sa) +{ + char *s = *sa; + + it->func = constant_item; + it->conditions = NULL; + + while (*s && *s != '%') ++s; + it->arg = log_format_substring (p, *sa, s); + *sa = s; + + return NULL; +} + +char *parse_log_item (pool *p, log_format_item *it, char **sa) +{ + char *s = *sa; + if (*s != '%') return parse_log_misc_string (p, it, sa); + + ++s; + it->condition_sense = 0; + it->conditions = NULL; + it->want_orig = -1; + it->arg = ""; /* For safety's sake... */ + + while (*s) { + int i; + struct log_item_list *l; + + switch (*s) { + case '!': + ++s; + it->condition_sense = !it->condition_sense; + break; + + case '<': + ++s; + it->want_orig = 1; + break; + + case '>': + ++s; + it->want_orig = 0; + break; + + case ',': + ++s; + break; + + case '{': + ++s; + it->arg = getword (p, &s, '}'); + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + i = *s - '0'; + while (isdigit (*++s)) i = i * 10 + (*s) - '0'; + if (!it->conditions) + it->conditions = make_array (p, 4, sizeof(int)); + *(int *)push_array(it->conditions) = i; + break; + + default: + l = find_log_func (*s++); + if (!l) { + char dummy[] = { '\0', '\0'}; + dummy[0] = s[-1]; + return pstrcat (p, "Unrecognized LogFormat directive %", + dummy, NULL); + } + it->func = l->func; + if (it->want_orig == -1) it->want_orig = l->want_orig_default; + *sa = s; + return NULL; + } + } + + return "Ran off end of LogFormat parsing args to some directive"; +} + +array_header *parse_log_string (pool *p, char *s, char **err) +{ + array_header *a = make_array (p, 30, sizeof (log_format_item)); + char *res; + + while (*s) { + if ((res = parse_log_item (p, (log_format_item *)push_array(a), &s))) { + *err = res; + return NULL; + } + } + + s = "\n"; + parse_log_item (p, (log_format_item *)push_array(a), &s); + return a; +} + +/***************************************************************** + * + * Actually logging. + */ + +char *process_item(request_rec *r, request_rec *orig, log_format_item *item) +{ + char *cp; + + /* First, see if we need to process this thing at all... */ + + if (item->conditions && item->conditions->nelts != 0) { + int i; + int *conds = (int *)item->conditions->elts; + int in_list = 0; + + for (i = 0; i < item->conditions->nelts; ++i) + if (r->status == conds[i]) { + in_list = 1; + break; + } + + if ((item->condition_sense && in_list) + || (!item->condition_sense && !in_list)) + { + return "-"; + } + } + + /* We do. Do it... */ + + cp = (*item->func)(item->want_orig ? orig : r, item->arg); + return cp ? cp : "-"; +} + +int config_log_transaction(request_rec *r) +{ + config_log_state *cls = get_module_config (r->server->module_config, + &config_log_module); + + array_header *strsa= make_array(r->pool, cls->format->nelts,sizeof(char*)); + log_format_item *items = (log_format_item *)cls->format->elts; + char *str, **strs, *s; + request_rec *orig; + int i; + int len = 0; + + orig = r; + while (orig->prev) orig = orig->prev; + while (r->next) r = r->next; + + for (i = 0; i < cls->format->nelts; ++i) + *((char**)push_array (strsa)) = process_item (r, orig, &items[i]); + + strs = (char **)strsa->elts; + + for (i = 0; i < cls->format->nelts; ++i) + len += strlen (strs[i]); + + str = palloc (r->pool, len + 1); + + for (i = 0, s = str; i < cls->format->nelts; ++i) { + strcpy (s, strs[i]); + s += strlen (strs[i]); + } + + write(cls->log_fd, str, strlen(str)); + + return OK; +} + +/***************************************************************** + * + * Module glue... + */ + +void *make_config_log_state (pool *p, server_rec *s) +{ + config_log_state *cls = + (config_log_state *)palloc (p, sizeof (config_log_state)); + + cls->fname = NULL; + cls->format = NULL; + cls->log_fd = -1; + + return (void *)cls; +} + +char *set_config_log (cmd_parms *parms, void *dummy, char *arg) +{ + config_log_state *cls = get_module_config (parms->server->module_config, + &config_log_module); + + cls->fname = arg; + return NULL; +} + +char *log_format (cmd_parms *cmd, void *dummy, char *arg) +{ + char *err_string = NULL; + config_log_state *cls = get_module_config (cmd->server->module_config, + &config_log_module); + + cls->format = parse_log_string (cmd->pool, arg, &err_string); + return err_string; +} + +command_rec config_log_cmds[] = { +{ "TransferLog", set_config_log, NULL, RSRC_CONF, TAKE1, + "the filename of the access log" }, +{ "LogFormat", log_format, NULL, RSRC_CONF, TAKE1, + "a log format string (see docs)" }, +{ NULL } +}; + +void config_log_child (void *cmd) +{ + /* 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... + */ + + cleanup_for_exec(); + signal (SIGHUP, SIG_IGN); + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); + fprintf (stderr, "Exec of shell for logging failed!!!\n"); + exit (1); +} + +config_log_state *open_config_log (server_rec *s, pool *p, + config_log_state *defaults) +{ + config_log_state *cls = get_module_config (s->module_config, + &config_log_module); + + if (cls->log_fd > 0) return cls; /* virtual config shared w/main server */ + + if (cls->format == NULL) { + char *dummy; + + if (defaults) cls->format = defaults->format; + else cls->format = parse_log_string (p, DEFAULT_LOG_FORMAT, &dummy); + } + + if (cls->fname == NULL) { + if (defaults) { + cls->log_fd = defaults->log_fd; + return cls; + } + else cls->fname = DEFAULT_XFERLOG; + } + + if (*cls->fname == '|') { + FILE *dummy; + + spawn_child(p, config_log_child, (void *)(cls->fname+1), + kill_after_timeout, &dummy, NULL); + + if (dummy == NULL) { + fprintf (stderr, "Couldn't fork child for TransferLog process\n"); + exit (1); + } + + cls->log_fd = fileno (dummy); + } + else { + char *fname = server_root_relative (p, cls->fname); + if((cls->log_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) { + fprintf (stderr, + "httpd: could not open transfer log file %s.\n", fname); + perror("open"); + exit(1); + } + } + + return cls; +} + +void init_config_log (server_rec *s, pool *p) +{ + /* First, do "physical" server, which gets default log fd and format + * for the virtual servers, if they don't override... + */ + + config_log_state *default_conf = open_config_log (s, p, NULL); + + /* Then, virtual servers */ + + for (s = s->next; s; s = s->next) open_config_log (s, p, default_conf); +} + +module config_log_module = { + STANDARD_MODULE_STUFF, + init_config_log, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + make_config_log_state, /* server config */ + NULL, /* merge server config */ + config_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + config_log_transaction /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_log_referer.c b/RELEASE_1_1_X/src/modules/standard/mod_log_referer.c new file mode 100644 index 00000000000..26ac28d0a5a --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_log_referer.c @@ -0,0 +1,231 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + + +#include "httpd.h" +#include "http_config.h" + +module referer_log_module; + +static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT ); + +#ifdef __EMX__ +/* OS/2 lacks support for users and groups */ +static mode_t xfer_mode = ( S_IREAD | S_IWRITE ); +#else +static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); +#endif + +typedef struct { + char *fname; + int referer_fd; + array_header *referer_ignore_list; +} referer_log_state; + +void *make_referer_log_state (pool *p, server_rec *s) +{ + referer_log_state *cls = + (referer_log_state *)palloc (p, sizeof (referer_log_state)); + + cls->fname = ""; + cls->referer_fd = -1; + cls->referer_ignore_list = make_array(p, 1, sizeof(char *)); + return (void *)cls; +} + +char *set_referer_log (cmd_parms *parms, void *dummy, char *arg) +{ + referer_log_state *cls = get_module_config (parms->server->module_config, + &referer_log_module); + + cls->fname = arg; + return NULL; +} + +char *add_referer_ignore (cmd_parms *parms, void *dummy, char *arg) +{ + char **addme; + referer_log_state *cls = get_module_config (parms->server->module_config, + &referer_log_module); + + addme = push_array(cls->referer_ignore_list); + *addme = pstrdup(cls->referer_ignore_list->pool, arg); + return NULL; +} + +command_rec referer_log_cmds[] = { +{ "RefererLog", set_referer_log, NULL, RSRC_CONF, TAKE1, + "the filename of the referer log" }, +{ "RefererIgnore", add_referer_ignore, NULL, RSRC_CONF, ITERATE, + "referer hostnames to ignore" }, +{ NULL } +}; + +void referer_log_child (void *cmd) +{ + /* Child process code for 'RefererLog "|..."'; + * may want a common framework for this, since I expect it will + * be common for other foo-loggers to want this sort of thing... + */ + + cleanup_for_exec(); + signal (SIGHUP, SIG_IGN); + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); + fprintf (stderr, "Exec of shell for logging failed!!!\n"); + exit (1); +} + +void open_referer_log (server_rec *s, pool *p) +{ + referer_log_state *cls = get_module_config (s->module_config, + &referer_log_module); + + char *fname = server_root_relative (p, cls->fname); + + if (cls->referer_fd > 0) return; /* virtual log shared w/main server */ + + if (*cls->fname == '|') { + FILE *dummy; + + spawn_child(p, referer_log_child, (void *)(cls->fname+1), + kill_after_timeout, &dummy, NULL); + + if (dummy == NULL) { + fprintf (stderr, "Couldn't fork child for RefererLog process\n"); + exit (1); + } + + cls->referer_fd = fileno (dummy); + } + else if(*cls->fname != '\0') { + if((cls->referer_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) { + fprintf(stderr,"httpd: could not open referer log file %s.\n", fname); + perror("open"); + exit(1); + } + } +} + +void init_referer_log (server_rec *s, pool *p) +{ + for (; s; s = s->next) open_referer_log (s, p); +} + +int referer_log_transaction(request_rec *orig) +{ + char **ptrptr, **ptrptr2; + referer_log_state *cls = get_module_config (orig->server->module_config, + &referer_log_module); + + char *str; + char *referer; + request_rec *r; + + if(cls->referer_fd <0) + return OK; + + for (r = orig; r->next; r = r->next) + continue; + if (*cls->fname == '\0') /* Don't log referer */ + return DECLINED; + + referer = table_get(orig->headers_in, "Referer"); + if(referer != NULL) + { + + + /* The following is an upsetting mess of pointers, I'm sorry + Anyone with the motiviation and/or the time should feel free + to make this cleaner... */ + + ptrptr2 = (char **) (cls->referer_ignore_list->elts + + (cls->referer_ignore_list->nelts * + cls->referer_ignore_list->elt_size)); + + /* Go through each element of the ignore list and compare it to the + referer_host. If we get a match, return without logging */ + + for(ptrptr = (char **) cls->referer_ignore_list->elts; + ptrptr < ptrptr2; + ptrptr = (char **)((char *)ptrptr + cls->referer_ignore_list->elt_size)) + { + if(strstr(referer, *ptrptr)) + return OK; + } + + + str = pstrcat(orig->pool, referer, " -> ", r->uri, "\n", NULL); + write(cls->referer_fd, str, strlen(str)); + } + + return OK; +} + +module referer_log_module = { + STANDARD_MODULE_STUFF, + init_referer_log, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + make_referer_log_state, /* server config */ + NULL, /* merge server config */ + referer_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + referer_log_transaction /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_mime.c b/RELEASE_1_1_X/src/modules/standard/mod_mime.c new file mode 100644 index 00000000000..74393f42abb --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_mime.c @@ -0,0 +1,315 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * http_mime.c: Sends/gets MIME headers for requests + * + * Rob McCool + * + */ + +#define MIME_PRIVATE + +#include "httpd.h" +#include "http_config.h" + +typedef struct { + table *forced_types; /* Additional AddTyped stuff */ + table *encoding_types; /* Added with AddEncoding... */ + table *language_types; /* Added with AddLanguage... */ + table *handlers; /* Added with AddHandler... */ + + char *type; /* Type forced with ForceType */ + char *handler; /* Handler forced with SetHandler */ +} mime_dir_config; + +module mime_module; + +void *create_mime_dir_config (pool *p, char *dummy) +{ + mime_dir_config *new = + (mime_dir_config *) palloc (p, sizeof(mime_dir_config)); + + new->forced_types = make_table (p, 4); + new->encoding_types = make_table (p, 4); + new->language_types = make_table (p, 4); + new->handlers = make_table (p, 4); + + new->type = NULL; + new->handler = NULL; + + return new; +} + +void *merge_mime_dir_configs (pool *p, void *basev, void *addv) +{ + mime_dir_config *base = (mime_dir_config *)basev; + mime_dir_config *add = (mime_dir_config *)addv; + mime_dir_config *new = + (mime_dir_config *)palloc (p, sizeof(mime_dir_config)); + + new->forced_types = overlay_tables (p, add->forced_types, + base->forced_types); + new->encoding_types = overlay_tables (p, add->encoding_types, + base->encoding_types); + new->language_types = overlay_tables (p, add->language_types, + base->language_types); + new->handlers = overlay_tables (p, add->handlers, + base->handlers); + + new->type = add->type ? add->type : base->type; + new->handler = add->handler ? add->handler : base->handler; + + return new; +} + +char *add_type(cmd_parms *cmd, mime_dir_config *m, char *ct, char *ext) +{ + if (*ext == '.') ++ext; + table_set (m->forced_types, ext, ct); + return NULL; +} + +char *add_encoding(cmd_parms *cmd, mime_dir_config *m, char *enc, char *ext) +{ + if (*ext == '.') ++ext; + table_set (m->encoding_types, ext, enc); + return NULL; +} + +char *add_language(cmd_parms *cmd, mime_dir_config *m, char *lang, char *ext) +{ + if (*ext == '.') ++ext; + table_set (m->language_types, ext, lang); + return NULL; +} + +char *add_handler(cmd_parms *cmd, mime_dir_config *m, char *hdlr, char *ext) +{ + if (*ext == '.') ++ext; + table_set (m->handlers, ext, hdlr); + return NULL; +} + +/* The sole bit of server configuration that the MIME module has is + * the name of its config file, so... + */ + +char *set_types_config (cmd_parms *cmd, void *dummy, char *arg) +{ + set_module_config (cmd->server->module_config, &mime_module, + pstrdup (cmd->pool, arg)); + return NULL; +} + +command_rec mime_cmds[] = { +{ "AddType", add_type, NULL, OR_FILEINFO, ITERATE2, + "a mime type followed by one or more file extensions" }, +{ "AddEncoding", add_encoding, NULL, OR_FILEINFO, ITERATE2, + "an encoding (e.g., gzip), followed by one or more file extensions" }, +{ "AddLanguage", add_language, NULL, OR_FILEINFO, ITERATE2, + "a language (e.g., fr), followed by one or more file extensions" }, +{ "AddHandler", add_handler, NULL, OR_FILEINFO, ITERATE2, + "a handler name followed by one or more file extensions" }, +{ "ForceType", set_string_slot, (void*)XtOffsetOf(mime_dir_config, type), + OR_FILEINFO, TAKE1, "a media type" }, +{ "SetHandler", set_string_slot, (void*)XtOffsetOf(mime_dir_config, handler), + OR_FILEINFO, TAKE1, "a handler name" }, +{ "TypesConfig", set_types_config, NULL, RSRC_CONF, TAKE1, + "the MIME types config file" }, +{ NULL } +}; + +/* Hash table --- only one of these per daemon; virtual hosts can + * get private versions through AddType... + */ + +#define MIME_HASHSIZE 27 +#define hash(i) (isalpha(i) ? (tolower(i)) - 'a' : 26) + +static table *hash_buckets[MIME_HASHSIZE]; + +void init_mime (server_rec *s, pool *p) +{ + FILE *f; + char l[MAX_STRING_LEN]; + int x; + char *types_confname = get_module_config (s->module_config, &mime_module); + + if (!types_confname) types_confname = TYPES_CONFIG_FILE; + + types_confname = server_root_relative (p, types_confname); + + if(!(f = fopen(types_confname,"r"))) { + fprintf(stderr,"httpd: could not open mime types file %s\n", + types_confname); + perror("fopen"); + exit(1); + } + + for(x=0;x<27;x++) + hash_buckets[x] = make_table (p, 10); + + while(!(cfg_getline(l,MAX_STRING_LEN,f))) { + char *ll = l, *ct; + + if(l[0] == '#') continue; + ct = getword_conf (p, &ll); + + while(ll[0]) { + char *ext = getword_conf (p, &ll); + str_tolower (ext); /* ??? */ + table_set (hash_buckets[hash(ext[0])], ext, ct); + } + } + fclose(f); +} + +int find_ct(request_rec *r) +{ + char *fn = strrchr(r->filename, '/'); + mime_dir_config *conf = + (mime_dir_config *)get_module_config(r->per_dir_config, &mime_module); + char *ext, *type, *orighandler = r->handler; + + if (S_ISDIR(r->finfo.st_mode)) { + r->content_type = DIR_MAGIC_TYPE; + return OK; + } + + /* TM -- FIXME + * + * if r->filename does not contain a '/', the following passes a null + * pointer to getword, causing a SEGV .. + */ + + if(fn == NULL) fn = r->filename; + + /* Parse filename extensions, which can be in any order */ + while ((ext = getword(r->pool, &fn, '.')) && *ext) { + int found = 0; + + /* Check for Content-Type */ + if ((type = table_get (conf->forced_types, ext)) + || (type = table_get (hash_buckets[hash(*ext)], ext))) { + r->content_type = type; + found = 1; + } + + /* Check for Content-Language */ + if ((type = table_get (conf->language_types, ext))) { + r->content_language = type; + found = 1; + } + + /* Check for Content-Encoding */ + if ((type = table_get (conf->encoding_types, ext))) { + if (!r->content_encoding) + r->content_encoding = type; + else + r->content_encoding = pstrcat(r->pool, r->content_encoding, + ", ", type, NULL); + found = 1; + } + + /* Check for a special handler, but not for proxy request */ + if ((type = table_get (conf->handlers, ext)) && !r->proxyreq) { + r->handler = type; + found = 1; + } + + /* This is to deal with cases such as foo.gif.bak, which we want + * to not have a type. So if we find an unknown extension, we + * zap the type/language/encoding and reset the handler + */ + + if (!found) { + r->content_type = NULL; + r->content_language = NULL; + r->content_encoding = NULL; + r->handler = orighandler; + } + + } + + /* Check for overrides with ForceType/SetHandler */ + + if (conf->type && strcmp(conf->type, "none")) + r->content_type = pstrdup(r->pool, conf->type); + if (conf->handler && strcmp(conf->handler, "none")) + r->handler = pstrdup(r->pool, conf->handler); + + if (!r->content_type) return DECLINED; + + return OK; +} + + +module mime_module = { + STANDARD_MODULE_STUFF, + init_mime, /* initializer */ + create_mime_dir_config, + merge_mime_dir_configs, + NULL, /* server config */ + NULL, /* merge server config */ + mime_cmds, + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + find_ct, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_negotiation.c b/RELEASE_1_1_X/src/modules/standard/mod_negotiation.c new file mode 100644 index 00000000000..77f1cbd4961 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_negotiation.c @@ -0,0 +1,1122 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * mod_negotiation.c: keeps track of MIME types the client is willing to + * accept, and contains code to handle type arbitration. + * + * rst + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_log.h" + +/* Commands --- configuring document caching on a per (virtual?) + * server basis... + */ + +typedef struct { + array_header *language_priority; +} neg_dir_config; + +module negotiation_module; + +void *create_neg_dir_config (pool *p, char *dummy) +{ + neg_dir_config *new = + (neg_dir_config *) palloc (p, sizeof (neg_dir_config)); + + new->language_priority = make_array (p, 4, sizeof (char *)); + return new; +} + +void *merge_neg_dir_configs (pool *p, void *basev, void *addv) +{ + neg_dir_config *base = (neg_dir_config *)basev; + neg_dir_config *add = (neg_dir_config *)addv; + neg_dir_config *new = + (neg_dir_config *) palloc (p, sizeof (neg_dir_config)); + + /* give priority to the config in the subdirectory */ + new->language_priority = append_arrays (p, add->language_priority, + base->language_priority); + return new; +} + +char *set_language_priority (cmd_parms *cmd, void *n, char *lang) +{ + array_header *arr = ((neg_dir_config *) n)->language_priority; + char **langp = (char **) push_array (arr); + + *langp = pstrdup (arr->pool, lang); + return NULL; +} + +char *cache_negotiated_docs (cmd_parms *cmd, void *dummy, char *dummy2) +{ + void *server_conf = cmd->server->module_config; + + set_module_config (server_conf, &negotiation_module, "Cache"); + return NULL; +} + +int do_cache_negotiated_docs (server_rec *s) +{ + return (get_module_config (s->module_config, &negotiation_module) != NULL); +} + +command_rec negotiation_cmds[] = { +{ "CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF, RAW_ARGS, + NULL }, +{ "LanguagePriority", set_language_priority, NULL, OR_FILEINFO, ITERATE, + NULL }, +{ NULL } +}; + +/* + * TO DO --- error code 406. Unfortunately, the specification for + * a 406 reply in the current draft standard is unworkable; + * we return 404 for these pending a workable spec. + */ + +/* Record of available info on a media type specified by the client + * (we also use 'em for encodings and languages) + */ + +typedef struct accept_rec { + char *type_name; + float quality; + float max_bytes; + float level; +} accept_rec; + +/* Record of available info on a particular variant + * + * Note that a few of these fields are updated by the actual negotiation + * code. These are: + * + * quality --- initialized to the value of qs, and subsequently jiggered + * to reflect the client's preferences. In particular, it + * gets zeroed out if the variant has an unacceptable content + * encoding, or if it is in a language which the client + * doesn't accept and some other variant *is* in a language + * the client accepts. + * + * level_matched --- initialized to zero. Set to the value of level + * if the client actually accepts this media type at that + * level (and *not* if it got in on a wildcard). See level_cmp + * below. + */ + +typedef struct var_rec { + request_rec *sub_req; /* May be NULL (is, for map files) */ + char *type_name; + char *file_name; + char *content_encoding; + char *content_language; + float level; /* Auxiliary to content-type... */ + float qs; + float bytes; + int lang_index; + int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */ + + /* Above are all written-once properties of the variant. The + * three fields below are changed during negotiation: + */ + + float quality; + float level_matched; + int mime_stars; +} var_rec; + +/* Something to carry around the state of negotiation (and to keep + * all of this thread-safe)... + */ + +typedef struct { + pool *pool; + request_rec *r; + char *dir_name; + + array_header *accepts; /* accept_recs */ + array_header *accept_encodings; /* accept_recs */ + array_header *accept_langs; /* accept_recs */ + array_header *avail_vars; /* available variants */ +} negotiation_state; + +/* A few functions to manipulate var_recs. + * Cleaning out the fields... + */ + +void clean_var_rec (var_rec *mime_info) +{ + mime_info->sub_req = NULL; + mime_info->type_name = ""; + mime_info->file_name = ""; + mime_info->content_encoding = ""; + mime_info->content_language = ""; + + mime_info->is_pseudo_html = 0; + mime_info->level = 0.0; + mime_info->level_matched = 0.0; + mime_info->qs = 0.0; + mime_info->quality = 0.0; + mime_info->bytes = 0; + mime_info->lang_index = -1; + mime_info->mime_stars = 0; +} + +/* Initializing the relevant fields of a variant record from the + * accept_info read out of its content-type, one way or another. + */ + +void set_mime_fields (var_rec *var, accept_rec *mime_info) +{ + var->type_name = mime_info->type_name; + var->qs = mime_info->quality; + var->quality = mime_info->quality; /* Initial quality is just qs */ + var->level = mime_info->level; + + var->is_pseudo_html = + (!strcmp (var->type_name, "text/html") + || !strcmp (var->type_name, INCLUDES_MAGIC_TYPE) + || !strcmp (var->type_name, INCLUDES_MAGIC_TYPE3)); +} + +/***************************************************************** + * + * Parsing (lists of) media types and their parameters, as seen in + * HTTPD header lines and elsewhere. + */ + +/* + * Get a single mime type entry --- one media type and parameters; + * enter the values we recognize into the argument accept_rec + */ + +char *get_entry (pool *p, accept_rec *result, char *accept_line) +{ + result->quality = 1.0; + result->max_bytes = 0.0; + result->level = 0.0; + + /* Note that this handles what I gather is the "old format", + * + * Accept: text/html text/plain moo/zot + * + * without any compatibility kludges --- if the token after the + * MIME type begins with a semicolon, we know we're looking at parms, + * otherwise, we know we aren't. (So why all the pissing and moaning + * in the CERN server code? I must be missing something). + */ + + result->type_name = get_token (p, &accept_line, 0); + str_tolower (result->type_name); /* You want case-insensitive, + * you'll *get* case-insensitive. + */ + + + /* KLUDGE!!! Default HTML to level 2.0 unless the browser + * *explicitly* says something else. + */ + + if (!strcmp (result->type_name, "text/html") + && result->level == 0.0) + result->level = 2.0; + else if (!strcmp (result->type_name, INCLUDES_MAGIC_TYPE)) + result->level = 2.0; + else if (!strcmp (result->type_name, INCLUDES_MAGIC_TYPE3)) + result->level = 3.0; + + while (*accept_line == ';') { + /* Parameters ... */ + + char *parm; + char *cp; + + ++accept_line; + parm = get_token (p, &accept_line, 1); + + /* Look for 'var = value' --- and make sure the var is in lcase. */ + + for (cp = parm; *cp && !isspace(*cp) && *cp != '='; ++cp) + *cp = tolower(*cp); + + if (!*cp) continue; /* No '='; just ignore it. */ + + *cp++ = '\0'; /* Delimit var */ + while (*cp && (isspace(*cp) || *cp == '=')) + ++cp; + + if (*cp == '"') ++cp; + + if (parm[0] == 'q' + && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) + result->quality = atof(cp); + else if (parm[0] == 'm' && parm[1] == 'x' && + parm[2] == 'b' && parm[3] == '\0') + result->max_bytes = atof(cp); + else if (parm[0] == 'l' && !strcmp (&parm[1], "evel")) + result->level = atof(cp); + } + + if (*accept_line == ',') ++accept_line; + + return accept_line; +} + + +/***************************************************************** + * + * Dealing with header lines ... + */ + +array_header *do_header_line (pool *p, char *accept_line) +{ + array_header *accept_recs = make_array (p, 40, sizeof (accept_rec)); + + if (!accept_line) return accept_recs; + + while (*accept_line) { + accept_rec *new = (accept_rec *)push_array (accept_recs); + accept_line = get_entry (p, new, accept_line); + } + + return accept_recs; +} + +/***************************************************************** + * + * Handling header lines from clients... + */ + +negotiation_state *parse_accept_headers (request_rec *r) +{ + negotiation_state *new = + (negotiation_state *)palloc (r->pool, sizeof (negotiation_state)); + table *hdrs = r->headers_in; + + new->pool = r->pool; + new->r = r; + new->dir_name = make_dirstr(r->pool, r->filename, count_dirs(r->filename)); + + new->accepts = do_header_line (r->pool, table_get (hdrs, "Accept")); + new->accept_encodings = + do_header_line (r->pool, table_get (hdrs, "Accept-encoding")); + new->accept_langs = + do_header_line (r->pool, table_get (hdrs, "Accept-language")); + new->avail_vars = make_array (r->pool, 40, sizeof (var_rec)); + + return new; +} + +/* Sometimes clients will give us no Accept info at all; this routine sets + * up the standard default for that case, and also arranges for us to be + * willing to run a CGI script if we find one. (In fact, we set up to + * dramatically prefer CGI scripts in cases where that's appropriate, + * e.g., POST). + */ + +void maybe_add_default_encodings(negotiation_state *neg, int prefer_scripts) +{ + accept_rec *new_accept = (accept_rec *)push_array (neg->accepts); + + new_accept->type_name = CGI_MAGIC_TYPE; + new_accept->quality = prefer_scripts ? 1e-20 : 1e20; + new_accept->level = 0.0; + new_accept->max_bytes = 0.0; + + if (neg->accepts->nelts > 1) return; + + new_accept = (accept_rec *)push_array (neg->accepts); + + new_accept->type_name = "*/*"; + new_accept->quality = 1.0; + new_accept->level = 0.0; + new_accept->max_bytes = 0.0; +} + +/***************************************************************** + * + * Parsing type-map files, in Roy's meta/http format augmented with + * #-comments. + */ + +/* Reading RFC822-style header lines, ignoring #-comments and + * handling continuations. + */ + +enum header_state { header_eof, header_seen, header_sep }; + +enum header_state get_header_line (char *buffer, int len, FILE *map) +{ + char *buf_end = buffer + len; + char *cp; + int c; + + /* Get a noncommented line */ + + do { + if (fgets(buffer, MAX_STRING_LEN, map) == NULL) + return header_eof; + } while (buffer[0] == '#'); + + /* If blank, just return it --- this ends information on this variant */ + + for (cp = buffer; *cp && isspace (*cp); ++cp) + continue; + + if (*cp == '\0') return header_sep; + + /* If non-blank, go looking for header lines, but note that we still + * have to treat comments specially... + */ + + cp += strlen(cp); + + while ((c = getc(map)) != EOF) + { + if (c == '#') { + /* Comment line */ + while ((c = getc(map)) != EOF && c != '\n') + continue; + } else if (isspace(c)) { + /* Leading whitespace. POSSIBLE continuation line + * Also, possibly blank --- if so, we ungetc() the final newline + * so that we will pick up the blank line the next time 'round. + */ + + while (c != EOF && c != '\n' && isspace(c)) + c = getc(map); + + ungetc (c, map); + + if (c == '\n') return header_seen; /* Blank line */ + + /* Continuation */ + + while (cp < buf_end - 2 && (c = getc(map)) != EOF && c != '\n') + *cp++ = c; + + *cp++ = '\n'; + *cp = '\0'; + } else { + + /* Line beginning with something other than whitespace */ + + ungetc (c, map); + return header_seen; + } + } + + return header_seen; +} + +/* Stripping out RFC822 comments */ + +void strip_paren_comments (char *hdr) +{ + /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */ + + while (*hdr) { + if (*hdr == '"') { + while (*++hdr && *hdr != '"') + continue; + ++hdr; + } + else if (*hdr == '(') { + while (*hdr && *hdr != ')') *hdr++ = ' '; + + if (*hdr) *hdr++ = ' '; + } + else ++hdr; + } +} + +/* Getting to a header body from the header */ + +char *lcase_header_name_return_body (char *header, request_rec *r) +{ + char *cp = header; + + while (*cp && *cp != ':') + *cp++ = tolower(*cp); + + if (!*cp) { + log_reason ("Syntax error in type map --- no ':'", r->filename, r); + return NULL; + } + + do ++cp; while (*cp && isspace (*cp)); + + if (!*cp) { + log_reason ("Syntax error in type map --- no header body", + r->filename, r); + return NULL; + } + + return cp; +} + +int read_type_map (negotiation_state *neg, char *map_name) +{ + request_rec *r = neg->r; + FILE *map = pfopen (neg->pool, map_name, "r"); + + char buffer[MAX_STRING_LEN]; + enum header_state hstate; + struct var_rec mime_info; + + if (map == NULL) { + log_reason("cannot access type map file", map_name, r); + return FORBIDDEN; + } + + clean_var_rec (&mime_info); + + do { + hstate = get_header_line (buffer, MAX_STRING_LEN, map); + + if (hstate == header_seen) { + char *body = lcase_header_name_return_body (buffer, neg->r); + + if (body == NULL) return SERVER_ERROR; + + strip_paren_comments (body); + + if (!strncmp (buffer, "uri:", 4)) { + mime_info.file_name = get_token (neg->pool, &body, 0); + } + else if (!strncmp (buffer, "content-type:", 13)) { + struct accept_rec accept_info; + + get_entry (neg->pool, &accept_info, body); + set_mime_fields (&mime_info, &accept_info); + } + else if (!strncmp (buffer, "content-length:", 15)) { + mime_info.bytes = atoi(body); + } + else if (!strncmp (buffer, "content-language:", 17)) { + mime_info.content_language = get_token (neg->pool, &body, 0); + str_tolower (mime_info.content_language); + } + else if (!strncmp (buffer, "content-encoding:", 17)) { + mime_info.content_encoding = get_token (neg->pool, &body, 0); + str_tolower (mime_info.content_encoding); + } + } else { + if (mime_info.quality > 0) { + void *new_var = push_array (neg->avail_vars); + memcpy (new_var, (void *)&mime_info, sizeof (var_rec)); + } + + clean_var_rec(&mime_info); + } + } while (hstate != header_eof); + + pfclose (neg->pool, map); + return OK; +} + +/***************************************************************** + * + * Same, except we use a filtered directory listing as the map... + */ + +int read_types_multi (negotiation_state *neg) +{ + request_rec *r = neg->r; + + char *filp; + int prefix_len; + DIR *dirp; + struct DIR_TYPE *dir_entry; + struct var_rec mime_info; + struct accept_rec accept_info; + void *new_var; + + clean_var_rec (&mime_info); + + if (!(filp = strrchr (r->filename, '/'))) return DECLINED; /* Weird... */ + + ++filp; + prefix_len = strlen (filp); + + dirp = opendir (neg->dir_name); /* Not pool protected; sigh... */ + + if (dirp == NULL) { + log_reason("cannot read directory for multi", neg->dir_name, r); + return FORBIDDEN; + } + + while ((dir_entry = readdir (dirp))) { + + request_rec *sub_req; + + /* Do we have a match? */ + + if (strncmp (dir_entry->d_name, filp, prefix_len)) continue; + if (dir_entry->d_name[prefix_len] != '.') continue; + + /* Yep. See if it's something which we have access to, and + * which has a known type and encoding (as opposed to something + * which we'll be slapping default_type on later). + */ + + sub_req = sub_req_lookup_file (dir_entry->d_name, r); + + /* If it has a handler, we'll pretend it's a CGI script, + * since that's a good indication of the sort of thing it + * might be doing. + */ + if (sub_req->handler && !sub_req->content_type) + sub_req->content_type = CGI_MAGIC_TYPE; + + if (sub_req->status != 200 || !sub_req->content_type) continue; + + /* If it's a map file, we use that instead of the map + * we're building... + */ + + if (((sub_req->content_type) && + !strcmp (sub_req->content_type, MAP_FILE_MAGIC_TYPE)) || + ((sub_req->handler) && + !strcmp (sub_req->handler, "type-map"))) { + closedir(dirp); + + neg->avail_vars->nelts = 0; + return read_type_map (neg, sub_req->filename); + } + + /* Have reasonable variant --- gather notes. + */ + + mime_info.sub_req = sub_req; + mime_info.file_name = pstrdup(neg->pool, dir_entry->d_name); + mime_info.content_encoding = sub_req->content_encoding; + mime_info.content_language = sub_req->content_language; + + get_entry (neg->pool, &accept_info, sub_req->content_type); + set_mime_fields (&mime_info, &accept_info); + + new_var = push_array (neg->avail_vars); + memcpy (new_var, (void *)&mime_info, sizeof (var_rec)); + + clean_var_rec(&mime_info); + } + + closedir(dirp); + return OK; +} + + +/***************************************************************** + * And now for the code you've been waiting for... actually + * finding a match to the client's requirements. + */ + +/* Matching MIME types ... the star/star and foo/star commenting conventions + * are implemented here. (You know what I mean by star/star, but just + * try mentioning those three characters in a C comment). Using strcmp() + * is legit, because everything has already been smashed to lowercase. + * + * Note also that if we get an exact match on the media type, we update + * level_matched for use in level_cmp below... + * + * We also give a value for mime_stars, which is used later. It should + * be 1 for star/star, 2 for type/star and 3 for type/subtype. + */ + +int mime_match (accept_rec *accept, var_rec *avail) +{ + char *accept_type = accept->type_name; + char *avail_type = avail->type_name; + int len = strlen(accept_type); + + if (accept_type[0] == '*') { /* Anything matches star/star */ + if (avail->mime_stars < 1) + avail->mime_stars = 1; + return 1; + } + else if ((accept_type[len - 1] == '*') && + !strncmp (accept_type, avail_type, len - 2)) { + if (avail->mime_stars < 2) + avail->mime_stars = 2; + return 1; + } + else if (!strcmp (accept_type, avail_type) + || (!strcmp (accept_type, "text/html") + && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE) + || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) { + if (accept->level >= avail->level) { + avail->level_matched = avail->level; + avail->mime_stars = 3; + return 1; + } + } + + return OK; +} + +/* This code implements a piece of the tie-breaking algorithm between + * variants of equal quality. This piece is the treatment of variants + * of the same base media type, but different levels. What we want to + * return is the variant at the highest level that the client explicitly + * claimed to accept. + * + * If all the variants available are at a higher level than that, or if + * the client didn't say anything specific about this media type at all + * and these variants just got in on a wildcard, we prefer the lowest + * level, on grounds that that's the one that the client is least likely + * to choke on. + * + * (This is all motivated by treatment of levels in HTML --- we only + * want to give level 3 to browsers that explicitly ask for it; browsers + * that don't, including HTTP/0.9 browsers that only get the implicit + * "Accept: * / *" [space added to avoid confusing cpp --- no, that + * syntax doesn't really work] should get HTML2 if available). + * + * (Note that this code only comes into play when we are choosing among + * variants of equal quality, where the draft standard gives us a fair + * bit of leeway about what to do. It ain't specified by the standard; + * rather, it is a choice made by this server about what to do in cases + * where the standard does not specify a unique course of action). + */ + +int level_cmp (var_rec *var1, var_rec *var2) +{ + /* Levels are only comparable between matching media types */ + + if (var1->is_pseudo_html && !var2->is_pseudo_html) + return 0; + + if (!var1->is_pseudo_html && strcmp (var1->type_name, var2->type_name)) + return 0; + + /* Take highest level that matched, if either did match. */ + + if (var1->level_matched > var2->level_matched) return 1; + if (var1->level_matched < var2->level_matched) return -1; + + /* Neither matched. Take lowest level, if there's a difference. */ + + if (var1->level < var2->level) return 1; + if (var1->level > var2->level) return -1; + + /* Tied */ + + return 0; +} + +/* Finding languages. Note that we only match the substring specified + * by the Accept: line --- this is to allow "en" to match all subvariants + * of English. + * + * Again, strcmp() is legit because we've ditched case already. + */ + +int find_lang_index (array_header *accept_langs, char *lang) +{ + accept_rec *accs; + int i; + + if (!lang) + return -1; + + accs = (accept_rec *)accept_langs->elts; + + for (i = 0; i < accept_langs->nelts; ++i) + if (!strncmp (lang, accs[i].type_name, strlen(accs[i].type_name))) + return i; + + return -1; +} + +/* This function returns the priority of a given language + * according to LanguagePriority. It is used in case of a tie + * between several languages. + */ + +int find_default_index (neg_dir_config *conf, char *lang) +{ + array_header *arr; + int nelts; + char **elts; + int i; + + if (!lang) + return -1; + + arr = conf->language_priority; + nelts = arr->nelts; + elts = (char **) arr->elts; + + for (i = 0; i < nelts; ++i) + if (!strcasecmp (elts[i], lang)) + return i; + + return -1; +} + +void find_lang_indexes (negotiation_state *neg) +{ + var_rec *var_recs = (var_rec*)neg->avail_vars->elts; + int i; + int found_any = 0; + neg_dir_config *conf = NULL; + int naccept = neg->accept_langs->nelts; + + if (naccept == 0) + conf = (neg_dir_config *) get_module_config (neg->r->per_dir_config, + &negotiation_module); + + for (i = 0; i < neg->avail_vars->nelts; ++i) + if (var_recs[i].quality > 0) { + int index; + if (naccept == 0) /* Client doesn't care */ + index = find_default_index (conf, + var_recs[i].content_language); + else /* Client has Accept-Language */ + index = find_lang_index (neg->accept_langs, + var_recs[i].content_language); + + var_recs[i].lang_index = index; + if (index >= 0) found_any = 1; + } + + /* If we have any variants in a language acceptable to the client, + * blow away everything that isn't. + */ + + if (found_any) + for (i = 0; i < neg->avail_vars->nelts; ++i) + if (var_recs[i].lang_index < 0) + var_recs[i].quality = 0; +} + +/* Finding content encodings. Note that we assume that the client + * accepts the trivial encodings. Strcmp() is legit because... aw, hell. + */ + +int is_identity_encoding (char *enc) +{ + return (!enc || !enc[0] || !strcmp (enc, "7bit") || !strcmp (enc, "8bit") + || !strcmp (enc, "binary")); +} + +int find_encoding (array_header *accept_encodings, char *enc) +{ + accept_rec *accs = (accept_rec *)accept_encodings->elts; + int i; + + if (is_identity_encoding(enc)) return 1; + + for (i = 0; i < accept_encodings->nelts; ++i) + if (!strcmp (enc, accs[i].type_name)) + return 1; + + return 0; +} + +void do_encodings (negotiation_state *neg) +{ + var_rec *var_recs = (var_rec*)neg->avail_vars->elts; + int i; + + /* If no Accept-Encoding is present, everything is acceptable */ + + if (!neg->accept_encodings->nelts) + return; + + /* Lose any variant with an unacceptable content encoding */ + + for (i = 0; i < neg->avail_vars->nelts; ++i) + if (var_recs[i].quality > 0 + && !find_encoding (neg->accept_encodings, + var_recs[i].content_encoding)) + + var_recs[i].quality = 0; +} + +/* Determining the content length --- if the map didn't tell us, + * we have to do a stat() and remember for next time. + * + * Grump. For shambhala, even the first stat here may well be + * redundant (for multiviews) with a stat() done by the sub_req + * machinery. At some point, that ought to be fixed. + */ + +int find_content_length(negotiation_state *neg, var_rec *variant) +{ + struct stat statb; + + if (variant->bytes == 0) { + char *fullname = make_full_path (neg->pool, neg->dir_name, + variant->file_name); + + if (stat (fullname, &statb) >= 0) variant->bytes = statb.st_size; + } + + return variant->bytes; +} + +/* The main event. */ + +var_rec *best_match(negotiation_state *neg) +{ + int i, j; + var_rec *best = NULL; + float best_quality = 0.0; + int levcmp; + + accept_rec *accept_recs = (accept_rec *)neg->accepts->elts; + var_rec *avail_recs = (var_rec *)neg->avail_vars->elts; + + /* Nuke variants which are unsuitable due to a content encoding, + * or possibly a language, which the client doesn't accept. + * (If we haven't *got* a variant in a language the client accepts, + * find_lang_indexes keeps 'em all, so we still wind up serving + * something...). + */ + + do_encodings (neg); + find_lang_indexes (neg); + + for (i = 0; i < neg->accepts->nelts; ++i) { + + accept_rec *type = &accept_recs[i]; + + for (j = 0; j < neg->avail_vars->nelts; ++j) { + + var_rec *variant = &avail_recs[j]; + float q = type->quality * variant->quality; + + /* If we've already rejected this variant, don't waste time */ + + if (q == 0.0) continue; + + /* If media types don't match, forget it. + * (This includes the level check). + */ + + if (!mime_match(type, variant)) continue; + + /* Check maxbytes */ + + if (type->max_bytes > 0 + && (find_content_length(neg, variant) + > type->max_bytes)) + continue; + + /* If it lasted this far, consider it --- + * If better quality than our current best, take it. + * If equal quality, *maybe* take it. + * + * Note that the current http draft specifies no particular + * behavior for variants which tie in quality; the server + * can, at its option, return a 300 response listing all + * of them (and perhaps the others), or choose one of the + * tied variants by whatever means it likes. This server + * breaks ties as follows, in order: + * + * By perferring non-wildcard entries to those with + * wildcards. The spec specifically says we should + * do this, and it makes a lot of sense. + * + * By order of languages in Accept-language, to give the + * client a way to specify a language preference. I'd prefer + * to give this precedence over media type, but the standard + * doesn't allow for that. + * + * By level preference, as defined by level_cmp above. + * + * By order of Accept: header matched, so that the order in + * which media types are named by the client functions as a + * preference order, if the client didn't give us explicit + * quality values. + * + * Finally, by content_length, so that among variants which + * have the same quality, language and content_type (including + * level) we ship the one that saps the least bandwidth. + */ + + if (q > best_quality + || (q == best_quality + && ((variant->mime_stars > best->mime_stars) + || (variant->lang_index < best->lang_index + || (variant->lang_index == best->lang_index + && ((levcmp = level_cmp (variant, best)) == 1 + || (levcmp == 0 + && !strcmp (variant->type_name, + best->type_name) + && (find_content_length(neg, variant) + < + find_content_length(neg, best))))))))) + { + best = variant; + best_quality = q; + } + } + } + + return best; +} + +/**************************************************************** + * + * Executive... + */ + +int handle_map_file (request_rec *r) +{ + negotiation_state *neg = parse_accept_headers (r); + var_rec *best; + int res; + + char *udir; + + if ((res = read_type_map (neg, r->filename))) return res; + + maybe_add_default_encodings(neg, 0); + + if (!(best = best_match(neg))) { + /* Should be a 406 */ + log_reason ("no acceptable variant", r->filename, r); + return NOT_FOUND; + } + + if (!do_cache_negotiated_docs(r->server)) r->no_cache = 1; + udir = make_dirstr (r->pool, r->uri, count_dirs (r->uri)); + udir = escape_uri(r->pool, udir); + internal_redirect (make_full_path (r->pool, udir, best->file_name), r); + return OK; +} + +int handle_multi (request_rec *r) +{ + negotiation_state *neg; + var_rec *best; + request_rec *sub_req; + int res; + + if (r->finfo.st_mode != 0 || !(allow_options (r) & OPT_MULTI)) + return DECLINED; + + neg = parse_accept_headers (r); + + if ((res = read_types_multi (neg))) return res; + + maybe_add_default_encodings(neg, + r->method_number != M_GET + || r->args || r->path_info); + + if (neg->avail_vars->nelts == 0) return DECLINED; + + if (!(best = best_match(neg))) { + /* Should be a 406 */ + log_reason ("no acceptable variant", r->filename, r); + return NOT_FOUND; + } + + if (! (sub_req = best->sub_req)) { + /* We got this out of a map file, so we don't actually have + * a sub_req structure yet. Get one now. + */ + + sub_req = sub_req_lookup_file (best->file_name, r); + if (sub_req->status != 200) return sub_req->status; + } + + /* BLETCH --- don't multi-resolve non-ordinary files */ + + if (!S_ISREG(sub_req->finfo.st_mode)) return NOT_FOUND; + + /* Otherwise, use it. */ + + if (!do_cache_negotiated_docs(r->server)) r->no_cache = 1; + r->filename = sub_req->filename; + r->content_type = sub_req->content_type; + r->content_encoding = sub_req->content_encoding; + r->content_language = sub_req->content_language; + r->finfo = sub_req->finfo; + + return OK; +} + +handler_rec negotiation_handlers[] = { +{ MAP_FILE_MAGIC_TYPE, handle_map_file }, +{ "type-map", handle_map_file }, +{ NULL } +}; + +module negotiation_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_neg_dir_config, /* dir config creater */ + merge_neg_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + negotiation_cmds, /* command table */ + negotiation_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + handle_multi, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/modules/standard/mod_status.c b/RELEASE_1_1_X/src/modules/standard/mod_status.c new file mode 100644 index 00000000000..5a324fa66d6 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_status.c @@ -0,0 +1,591 @@ +/* ==================================================================== + * Copyright (c) 1995, 1996 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* Status Module. Provide a way of getting at the internal Apache + * status information without having to worry where the scoreboard is + * held. + * + * AddType application/x-httpd-status .status + * You might like to do this in a .htaccess in a protected directory only + * + * GET /.status - Returns pretty page for system admin user + * GET /.status?refresh - Returns page with 1 second refresh + * GET /.status?refresh=6 - Returns page with refresh every 6 seconds + * GET /.status?auto - Returns page with data for automatic parsing + * GET /.status?notable - Returns page with no table niceties + * + * Mark Cox, mark@ukweb.com, November 1995 + * + * 12.11.95 Initial version for telescope.org + * 13.3.96 Updated to remove rprintf's [Mark] + * 18.3.96 Added CPU usage, process information, and tidied [Ben Laurie] + * 18.3.96 Make extra Scoreboard variables #definable + * 25.3.96 Make short report have full precision [Ben Laurie suggested] + * 25.3.96 Show uptime better [Mark/Ben Laurie] + * 29.3.96 Better HTML and explanation [Mark/Rob Hartill suggested] + * 09.4.96 Added message for non-STATUS compiled version + * 18.4.96 Added per child and per slot counters [Jim Jagielski] + * 01.5.96 Table format, cleanup, even more spiffy data [Chuck Murcko/Jim J.] + * 18.5.96 Adapted to use new rprintf() routine, incidentally fixing a missing + * piece in short reports [Ben Laurie] + * 21.5.96 Additional Status codes (DNS and LOGGING only enabled if + extended STATUS is enabled) [George Burgyan/Jim J.] + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "util_script.h" +#include +#include "scoreboard.h" + +#ifdef NEXT +#include +#endif + +#define STATUS_MAXLINE 50 + +#define KBYTE 1024 +#define MBYTE 1048576L +#define GBYTE 1073741824L + +module status_module; + +/* Format the number of bytes nicely */ + +void format_byte_out(request_rec *r,unsigned long bytes) +{ + if (bytes < (5 * KBYTE)) + rprintf(r,"%d B",(int)bytes); + else if (bytes < (MBYTE / 2)) + rprintf(r,"%.1f kB",(float)bytes/KBYTE); + else if (bytes < (GBYTE / 2)) + rprintf(r,"%.1f MB",(float)bytes/MBYTE); + else + rprintf(r,"%.1f GB",(float)bytes/GBYTE); +} + +void show_time(request_rec *r,time_t tsecs) +{ + long days,hrs,mins,secs; + char buf[100]; + char *s; + + secs=tsecs%60; + tsecs/=60; + mins=tsecs%60; + tsecs/=60; + hrs=tsecs%24; + days=tsecs/24; + s=buf; + *s='\0'; + if(days) + rprintf(r," %ld day%s",days,days==1?"":"s"); + if(hrs) + rprintf(r," %ld hour%s",hrs,hrs==1?"":"s"); + if(mins) + rprintf(r," %ld minute%s",mins,mins==1?"":"s"); + if(secs) + rprintf(r," %ld second%s",secs,secs==1?"":"s"); +} + +#if defined(SUNOS4) +double +difftime(time1, time0) + time_t time1, time0; +{ + return(time1 - time0); +} +#endif + +/* Main handler for x-httpd-status requests */ + +/* ID values for command table */ + +#define STAT_OPT_END -1 +#define STAT_OPT_REFRESH 0 +#define STAT_OPT_NOTABLE 1 +#define STAT_OPT_AUTO 2 + +struct stat_opt +{ + int id; + char *form_data_str; + char *hdr_out_str; +}; + +int status_handler (request_rec *r) +{ + struct stat_opt options[] = /* see #defines above */ + { + { STAT_OPT_REFRESH, "refresh", "Refresh" }, + { STAT_OPT_NOTABLE, "notable", NULL }, + { STAT_OPT_AUTO, "auto", NULL }, + { STAT_OPT_END, NULL, NULL } + }; + char *loc; + time_t nowtime=time(NULL); + time_t up_time; + int i,res; + int ready=0; + int busy=0; +#if defined(STATUS) + unsigned long count=0; + unsigned long lres,bytes; + unsigned long my_lres,my_bytes,conn_bytes; + unsigned short conn_lres; + unsigned long bcount=0; +#ifdef NEXT + float tick=HZ; +#else + float tick=sysconf(_SC_CLK_TCK); +#endif +#endif /* STATUS */ + int short_report=0; + int no_table_report=0; + server_rec *server = r->server; + short_score score_record; + char status[]="??????????"; + char stat_buffer[HARD_SERVER_LIMIT]; + clock_t tu,ts,tcu,tcs; + + tu=ts=tcu=tcs=0; + + status[SERVER_DEAD]='.'; /* We don't want to assume these are in */ + status[SERVER_READY]='_'; /* any particular order in scoreboard.h */ + status[SERVER_STARTING]='S'; + status[SERVER_BUSY_READ]='R'; + status[SERVER_BUSY_WRITE]='W'; + status[SERVER_BUSY_KEEPALIVE]='K'; + status[SERVER_BUSY_LOG]='L'; + status[SERVER_BUSY_DNS]='D'; + + if (r->method_number != M_GET) return NOT_IMPLEMENTED; + r->content_type = "text/html"; + + /* + * Simple table-driven form data set parser that lets you alter the header + */ + + if (r->args) + { + i = 0; + while (options[i].id != STAT_OPT_END) + { + if ((loc = strstr(r->args,options[i].form_data_str)) != NULL) + { + switch (options[i].id) + { + case STAT_OPT_REFRESH: + if(*(loc + strlen(options[i].form_data_str)) == '=') + table_set(r->headers_out,options[i].hdr_out_str, + loc+strlen(options[i].hdr_out_str)+1); + else + table_set(r->headers_out,options[i].hdr_out_str,"1"); + break; + case STAT_OPT_NOTABLE: + no_table_report = 1; + break; + case STAT_OPT_AUTO: + r->content_type = "text/plain"; + short_report = 1; + break; + } + } + i++; + } + } + + soft_timeout ("send status info", r); + send_http_header(r); + + if (r->header_only) + return 0; + + sync_scoreboard_image(); + for (i = 0; iApache Status\n",r); + rputs("

Apache Server Status for ",r); + rvputs(r,server->server_hostname,"

\n\n",NULL); + rvputs(r,"Current Time: ",asctime(localtime(&nowtime)),"
\n",NULL); + rvputs(r,"Restart Time: ",asctime(localtime(&restart_time)),"
\n", + NULL); + rputs("Server uptime: ",r); + show_time(r,up_time); + rputs("
\n",r); + } + +#if defined(STATUS) + if (short_report) + { + rprintf(r,"Total Accesses: %lu\nTotal Bytes: %lu\n",count,bcount); + + if(ts || tu || tcu || tcs) + rprintf(r,"CPULoad: %g\n",(tu+ts+tcu+tcs)/tick/up_time*100.); + + rprintf(r,"Uptime: %ld\n",(long)(up_time)); + if (up_time>0) + rprintf(r,"ReqPerSec: %g\n",(float)count/(float)up_time); + + if (up_time>0) + rprintf(r,"BytesPerSec: %g\n",(float)bcount/(float)up_time); + + if (count>0) + rprintf(r,"BytesPerReq: %g\n",(float)bcount/(float)count); + } else /* !short_report */ + { + rprintf(r,"Total accesses: %lu - Total Traffic: ", count); + format_byte_out(r,bcount); + rputs("
\n",r); + rprintf(r,"CPU Usage: u%g s%g cu%g cs%g", + tu/tick,ts/tick,tcu/tick,tcs/tick); + + if(ts || tu || tcu || tcs) + rprintf(r," - %.3g%% CPU load",(tu+ts+tcu+tcs)/tick/up_time*100.); + + rputs("
\n",r); + + if (up_time>0) + rprintf(r,"%.3g requests/sec - ", + (float)count/(float)up_time); + + if (up_time>0) + { + format_byte_out(r,(float)bcount/(float)up_time); + rputs("/second - ",r); + } + + if (count>0) + { + format_byte_out(r,(float)bcount/(float)count); + rputs("/request",r); + } + + rputs("

\n",r); + } /* short_report */ +#endif /* STATUS */ + + /* send the scoreboard 'table' out */ + + rputs("Scoreboard: \n",r); + + if(!short_report) + rputs("

",r);
+
+    rputs("\n",r);
+
+    for (i = 0; i\n",r);
+	rputs("Key:
\n",r); + rputs("\"_\" Waiting for Connection, \n",r); + rputs("\"S\" Starting up,
\n",r); + rputs("\"R\" Reading Request, \n",r); + rputs("\"W\" Sending Reply,
\n",r); + rputs("\"K\" Keepalive (read), \n",r); + rputs("\"D\" DNS Lookup, \n",r); + rputs("\"L\" Logging

\n",r); + rprintf(r,"\n%d requests currently being processed, %d idle servers\n" + ,busy,ready); + } + +#if defined(STATUS) + if (!short_report) + if(no_table_report) + rputs("


Server Details

\n\n",r); + else + rputs("

\n\n\n\n",r); + + + for (i = 0; iServer %d (%d): %d|%lu|%lu [", + i,(int)score_record.pid,(int)conn_lres,my_lres,lres); + + switch (score_record.status) + { + case SERVER_READY: + rputs("Ready",r); + break; + case SERVER_STARTING: + rputs("Starting",r); + break; + case SERVER_BUSY_READ: + rputs("Read",r); + break; + case SERVER_BUSY_WRITE: + rputs("Write",r); + break; + case SERVER_BUSY_KEEPALIVE: + rputs("Keepalive",r); + break; + case SERVER_BUSY_LOG: + rputs("Logging",r); + break; + case SERVER_BUSY_DNS: + rputs("DNS lookup",r); + break; + case SERVER_DEAD: + rputs("Dead",r); + break; + } + rprintf(r,"] u%g s%g cu%g cs%g\n %s (", + score_record.times.tms_utime/tick, + score_record.times.tms_stime/tick, + score_record.times.tms_cutime/tick, + score_record.times.tms_cstime/tick, + asctime(localtime(&score_record.last_used))); + format_byte_out(r,conn_bytes); + rputs("|",r); + format_byte_out(r,my_bytes); + rputs("|",r); + format_byte_out(r,bytes); + rputs(")\n",r); + rprintf(r," %s {%s}
\n\n", + score_record.client, score_record.request); + } + else /* !no_table_report */ + { + rprintf(r,"
\n\n", + score_record.client, score_record.request); + } /* no_table_report */ + } /* !short_report */ + } /* if () */ + } /* for () */ + + if (!(short_report || no_table_report)) + { + rputs("
SrvPIDAccMCPU\nSSConnChildSlotHostRequest
%d%d%d/%lu/%lu", + i,(int)score_record.pid,(int)conn_lres,my_lres,lres); + + switch (score_record.status) + { + case SERVER_READY: + rputs("_",r); + break; + case SERVER_STARTING: + rputs("S",r); + break; + case SERVER_BUSY_READ: + rputs("R",r); + break; + case SERVER_BUSY_WRITE: + rputs("W",r); + break; + case SERVER_BUSY_KEEPALIVE: + rputs("K",r); + break; + case SERVER_BUSY_LOG: + rputs("L",r); + break; + case SERVER_BUSY_DNS: + rputs("D",r); + break; + case SERVER_DEAD: + rputs(".",r); + break; + } + rprintf(r,"\n%.2f%.0f", + (score_record.times.tms_utime + + score_record.times.tms_stime + + score_record.times.tms_cutime + + score_record.times.tms_cstime)/tick, + difftime(nowtime, score_record.last_used)); + rprintf(r,"%-1.1f%-2.2f%-2.2f\n", + (float)conn_bytes/KBYTE, (float)my_bytes/MBYTE, + (float)bytes/MBYTE); + rprintf(r,"%s%s
\n \ +


\ +\n \ +
SrvServer number\n \ +
PIDOS process ID\n \ +
AccNumber of accesses this connection / this child / this slot\n \ +
MMode of operation\n \ +
CPUCPU usage, number of seconds\n \ +
SSSeconds since beginning of most recent request\n \ +
ConnKilobytes transferred this connection\n \ +
ChildMegabytes transferred this child\n \ +
SlotTotal megabytes transferred this slot\n \ +
\n",r); + } + +#else /* !defined(STATUS) */ + + rputs("
To obtain a full report with current status information \n",r); + rputs("you need to recompile Apache adding the -DSTATUS \n",r); + rputs("directive on the CFLAGS line in the \n",r); + rputs("Configuration file.\n",r); + rputs("DNS and LOGGING status \n",r); + rputs("also requires the -DSTATUS directive. \n",r); + +#endif /* STATUS */ + + if (!short_report) + rputs("",r); + return 0; +} + +handler_rec status_handlers[] = +{ +{ STATUS_MAGIC_TYPE, status_handler }, +{ "server-status", status_handler }, +{ NULL } +}; + +module status_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + NULL, /* command table */ + status_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RELEASE_1_1_X/src/modules/standard/mod_userdir.c b/RELEASE_1_1_X/src/modules/standard/mod_userdir.c new file mode 100644 index 00000000000..320f8699209 --- /dev/null +++ b/RELEASE_1_1_X/src/modules/standard/mod_userdir.c @@ -0,0 +1,206 @@ + +/* ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +/* + * mod_userdir... implement the UserDir command. Broken away from the + * Alias stuff for a couple of good and not-so-good reasons: + * + * 1) It shows a real minimal working example of how to do something like + * this. + * 2) I know people who are actually interested in changing this *particular* + * aspect of server functionality without changing the rest of it. That's + * what this whole modular arrangement is supposed to be good at... + * + * Modified by Alexei Kosut to support the following constructs + * (server running at www.foo.com, request for /~bar/one/two.html) + * + * UserDir public_html -> ~bar/public_html/one/two.html + * UserDir /usr/web -> /usr/web/bar/one/two.html + * UserDir /home/ * /www -> /home/bar/www/one/two.html + * NOTE: theses ^ ^ space only added allow it to work in a comment, ignore + * UserDir http://x/users -> (302) http://x/users/bar/one/two.html + * UserDir http://x/ * /y -> (302) http://x/bar/y/one/two.html + * NOTE: here also ^ ^ + * + * In addition, you can use multiple entries, to specify alternate + * user directories (a la Directory Index). For example: + * + * UserDir public_html /usr/web http://www.xyz.com/users + * + */ + +#include "httpd.h" +#include "http_config.h" + +module userdir_module; + +/* + * Sever config for this module is a little unconventional... + * It's just one string anyway, so why pretend? + */ + +void *create_userdir_config (pool *dummy, server_rec *s) { + return (void*)DEFAULT_USER_DIR; +} + +char *set_user_dir (cmd_parms *cmd, void *dummy, char *arg) +{ + void *server_conf = cmd->server->module_config; + + set_module_config (server_conf, &userdir_module, pstrdup (cmd->pool, arg)); + return NULL; +} + +command_rec userdir_cmds[] = { +{ "UserDir", set_user_dir, NULL, RSRC_CONF, RAW_ARGS, + "the public subdirectory in users' home directories, or 'disabled'" }, +{ NULL } +}; + +int translate_userdir (request_rec *r) +{ + void *server_conf = r->server->module_config; + char *userdirs = (char *)get_module_config(server_conf, &userdir_module); + char *name = r->uri; + char *w, *dname, *redirect; + char *x = NULL; + + if (userdirs == NULL || !strcasecmp(userdirs, "disabled") || + (name[0] != '/') || (name[1] != '~')) { + return DECLINED; + } + + while (*userdirs) { + char *userdir = getword_conf (r->pool, &userdirs); + char *filename = NULL; + + dname = name + 2; + w = getword(r->pool, &dname, '/'); + + if (!strcmp(w, "")) + return DECLINED; + + /* The 'dname' funny business involves backing it up to capture + * the '/' delimiting the "/~user" part from the rest of the URL, + * in case there was one (the case where there wasn't being just + * "GET /~user HTTP/1.0", for which we don't want to tack on a + * '/' onto the filename). + */ + + if (dname[-1] == '/') --dname; + + if (strchr(userdir, '*')) + x = getword(r->pool, &userdir, '*'); + + if (userdir[0] == '/') { + if (x) { + if (strchr(x, ':')) { + redirect = pstrcat(r->pool, x, w, userdir, dname, NULL); + table_set (r->headers_out, "Location", redirect); + return REDIRECT; + } + else + filename = pstrcat (r->pool, x, w, userdir, NULL); + } + else + filename = pstrcat (r->pool, userdir, "/", w, NULL); + } + else if (strchr(userdir, ':')) { + redirect = pstrcat(r->pool, userdir, "/", w, dname, NULL); + table_set (r->headers_out, "Location", redirect); + return REDIRECT; + } + else { + struct passwd *pw; + if((pw=getpwnam(w))) +#ifdef __EMX__ + /* Need to manually add user name for OS/2 */ + filename = pstrcat (r->pool, pw->pw_dir, w, "/", userdir, NULL); +#else + filename = pstrcat (r->pool, pw->pw_dir, "/", userdir, NULL); +#endif + + } + + /* Now see if it exists, or we're at the last entry. If we are at the + last entry, then use the filename generated (if there is one) anyway, + in the hope that some handler might handle it. This can be used, for + example, to run a CGI script for the user. + */ + if (filename && (!*userdirs || stat(filename, &r->finfo) != -1)) { + r->filename = pstrcat(r->pool, filename, dname, NULL); + return OK; + } + } + + return DECLINED; +} + +module userdir_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_userdir_config, /* server config */ + NULL, /* merge server config */ + userdir_cmds, /* command table */ + NULL, /* handlers */ + translate_userdir, /*filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL /* logger */ +}; diff --git a/RELEASE_1_1_X/src/support/.cvsignore b/RELEASE_1_1_X/src/support/.cvsignore new file mode 100644 index 00000000000..0eada2a266d --- /dev/null +++ b/RELEASE_1_1_X/src/support/.cvsignore @@ -0,0 +1,6 @@ +rotatelogs +htpasswd +htdigest +unescape +inc2shtml +httpd_monitor diff --git a/RELEASE_1_1_X/src/support/Makefile b/RELEASE_1_1_X/src/support/Makefile new file mode 100755 index 00000000000..5234181807f --- /dev/null +++ b/RELEASE_1_1_X/src/support/Makefile @@ -0,0 +1,76 @@ +# For gcc +CC= gcc +# For ANSI compilers +#CC= cc + +#For Optimization +#CFLAGS= -O2 +#For debugging +CFLAGS= -g +# For SCO ODT +#EXTRA_LIBS= -lcrypt_i +# For OS/2 port +#EXTRA_LIBS= -llibufc + + +INCLUDES= -I../src + +RM= /bin/rm -f +#--- You shouldn't have to edit anything else. --- + +.c.o: + $(CC) -c $(CFLAGS) $(INCLUDES) $< + +TARGETS=htpasswd htdigest httpd_monitor rotatelogs logresolve + +all: $(TARGETS) + +aux: + make all CC=gcc CFLAGS=-O2 + +ibm: + make all CC=gcc + +sun: + make all CC=gcc + +hp: + make all CC=gcc + +sgi: + make all CC=cc + +decmips: + make all CC=cc + +decaxp: + make all CC=cc + +sco5: + make all CC=cc + +sco3: + make all CC=cc EXTRA_LIBS=-lcrypt_i + + +tar: htpasswd + $(RM) htpasswd + +htpasswd: htpasswd.c + $(CC) $(CFLAGS) htpasswd.c -o htpasswd $(EXTRA_LIBS) + +htdigest: htdigest.c + $(CC) $(CFLAGS) htdigest.c -o htdigest + +httpd_monitor: httpd_monitor.c + $(CC) $(INCLUDES) $(CFLAGS) httpd_monitor.c -o httpd_monitor + +rotatelogs: rotatelogs.c + $(CC) $(INCLUDES) $(CFLAGS) rotatelogs.c -o rotatelogs + +logresolve: logresolve.c + $(CC) $(INCLUDES) $(CFLAGS) logresolve.c -o logresolve + +clean: + rm -f $(TARGETS) + diff --git a/RELEASE_1_1_X/src/support/cls.c b/RELEASE_1_1_X/src/support/cls.c new file mode 100644 index 00000000000..2c553cec93d --- /dev/null +++ b/RELEASE_1_1_X/src/support/cls.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include + +/* + * Compare a string to a mask + * Mask characters: + * @ - uppercase letter + * # - lowercase letter + * & - hex digit + * # - digit + * * - swallow remaining characters + * - exact match for any other character + */ +static int +checkmask(const char *data, const char *mask) +{ + int i, ch, d; + + for (i=0; mask[i] != '\0' && mask[i] != '*'; i++) + { + ch = mask[i]; + d = data[i]; + if (ch == '@') + { + if (!isupper(d)) return 0; + } else if (ch == '$') + { + if (!islower(d)) return 0; + } else if (ch == '#') + { + if (!isdigit(d)) return 0; + } else if (ch == '&') + { + if (!isxdigit(d)) return 0; + } else if (ch != d) return 0; + } + + if (mask[i] == '*') return 1; + else return (data[i] == '\0'); +} + +/* + * Converts 8 hex digits to a time integer + */ +static int +hex2sec(const char *x) +{ + int i, ch; + unsigned int j; + + for (i=0, j=0; i < 8; i++) + { + ch = x[i]; + j <<= 4; + if (isdigit(ch)) j |= ch - '0'; + else if (isupper(ch)) j |= ch - ('A' - 10); + else j |= ch - ('a' - 10); + } + if (j == 0xffffffff) return -1; /* so that it works with 8-byte ints */ + else return j; +} + +int +main(int argc, char **argv) +{ + int i, ver; + DIR *d; + struct dirent *e; + const char *s; + FILE *fp; + char path[FILENAME_MAX+1]; + char line[1035]; + time_t date, lmod, expire; + unsigned int len; + struct tm ts; + char sdate[30], slmod[30], sexpire[30]; + const char time_format[]="%e %b %Y %R"; + + if (argc != 2) + { + printf("Usage: cls directory\n"); + exit(0); + } + + d = opendir(argv[1]); + if (d == NULL) + { + perror("opendir"); + exit(1); + } + + for (;;) + { + e = readdir(d); + if (e == NULL) break; + s = e->d_name; + if (s[0] == '.' || s[0] == '#') continue; + sprintf(path, "%s/%s", argv[1], s); + fp = fopen(path, "r"); + if (fp == NULL) + { + perror("fopen"); + continue; + } + if (fgets(line, 1034, fp) == NULL) + { + perror("fgets"); + fclose(fp); + continue; + } + if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&\n")) + { + fprintf(stderr, "Bad cache file\n"); + fclose(fp); + continue; + } + date = hex2sec(line); + lmod = hex2sec(line+9); + expire = hex2sec(line+18); + ver = hex2sec(line+27); + len = hex2sec(line+35); + if (fgets(line, 1034, fp) == NULL) + { + perror("fgets"); + fclose(fp); + continue; + } + fclose(fp); + i = strlen(line); + if (strncmp(line, "X-URL: ", 7) != 0 || line[i-1] != '\n') + { + fprintf(stderr, "Bad cache file\n"); + continue; + } + line[i-1] = '\0'; + if (date != -1) + { + ts = *gmtime(&date); + strftime(sdate, 30, time_format, &ts); + } else + strcpy(sdate, "-"); + + if (lmod != -1) + { + ts = *gmtime(&lmod); + strftime(slmod, 30, time_format, &ts); + } else + strcpy(slmod, "-"); + + if (expire != -1) + { + ts = *gmtime(&expire); + strftime(sexpire, 30, time_format, &ts); + } else + strcpy(sexpire, "-"); + + printf("%s: %d; %s %s %s\n", line+7, ver, sdate, slmod, sexpire); + } + + closedir(d); + return 0; +} diff --git a/RELEASE_1_1_X/src/support/dbmmanage b/RELEASE_1_1_X/src/support/dbmmanage new file mode 100644 index 00000000000..c337ddf545b --- /dev/null +++ b/RELEASE_1_1_X/src/support/dbmmanage @@ -0,0 +1,123 @@ +#!/usr/local/bin/perl + +# ==================================================================== +# Copyright (c) 1995 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. 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 . + + +# usage: dbmmanage +# +# commands: add, delete, view, adduser +# +# no values needed for delete, no keys or values needed for view. +# to change a value, simply use "add". +# adduser encrypts the password: +# dbmmanage adduser +# +# is optional, and may also be supplied to add the user +# to a specified group: +# dbmmanage adduser + +if (scalar(@ARGV) < 2) { + print "Too few arguments.\n"; + exit; +} + +# ugly - this should be changed to be random. +$salt="XX"; +$file=$ARGV[0]; +$command=$ARGV[1]; +$key=$ARGV[2]; +$value=$ARGV[3]; +$group=$ARGV[4]; + +if ($command eq "add") { + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + $value .= ":$group" if $group ne ""; + $DB{$key} = $value; + dbmclose(%DB); + print "Entry $key added with value $value.\n"; + exit; +} + +if ($command eq "adduser") { + $hash = crypt($value, "$salt"); + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + $hash .= ":$group" if $group ne ""; + $value .= ":$group" if $group ne ""; + $DB{$key} = $hash; + dbmclose(%DB); + print "User $key added with password $value, encrypted to $hash\n"; + exit; +} + +if ($command eq "delete") { + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + delete($DB{$key}); + dbmclose(%DB); + exit; +} + +if ($command eq "view") { + dbmopen(%DB, $file, undef) || die "Error: $!\n"; + $return_status = 1; + unless ($key) { + while (($nkey,$val) = each %DB) { + print "$nkey = $val\n"; + } + } else { + $return_status = 0 if defined $DB{$key}; + print "$key = $DB{$key}\n"; + } + dbmclose(%DB); + exit($return_status); +} + +print "Command unrecognized - must be one of: view, add, adduser, delete.\n"; + diff --git a/RELEASE_1_1_X/src/support/dbmmanage.new b/RELEASE_1_1_X/src/support/dbmmanage.new new file mode 100644 index 00000000000..dd07b08ebd6 --- /dev/null +++ b/RELEASE_1_1_X/src/support/dbmmanage.new @@ -0,0 +1,133 @@ +#!/usr/local/bin/perl + +# ==================================================================== +# Copyright (c) 1995 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. 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 . + + +# usage: dbmmanage +# +# commands: add, delete, view, adduser +# +# no values needed for delete, no keys or values needed for view. +# to change a value, simply use "add". +# adduser encrypts the password: +# dbmmanage adduser + +die "Too few arguments.\n" if (@ARGV < 2); + +($file,$command,$key,$value) = @ARGV; + +$file =~ s/\.db.?$//; # remove ".db" or ".dbX" extension if any +$file =~ s/\.pag$//; # remove ".pag" and ".dir" as well. +$file =~ s/\.dir$//; # these are all common DBM extensions. + +if ($command eq "add") { + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + $DB{$key} = $value; + dbmclose(%DB); + print "Entry $key added with value $value.\n"; +} elsif ($command eq "adduser") { + srand; # needs to be done only once. + $salt = &compute_salt(0); # change to compute_salt(1) for new crypt() + $hash = crypt($value, $salt); + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + $DB{$key} = $hash; + dbmclose(%DB); + print "User $key added with password ``$value'', encrypted to $hash\n"; +} elsif ($command eq "delete") { + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + delete($DB{$key}); + dbmclose(%DB); +} elsif ($command eq "view") { + dbmopen(%DB, $file, undef) || die "Error: $!\n"; + unless ($key) { + while (($nkey,$val) = each %DB) { + print "$nkey = $val\n"; + } + } else { + print "$key = $DB{$key}\n"; + } + dbmclose(%DB); +} else { + print "Command unrecognized - must be one of: view, add, adduser, delete.\n"; +} + +exit(0); + +# if $newstyle is 1, 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. +sub compute_salt { + local($newstyle) = @_; + local($salt); + if ($newstyle) { + $salt = "_" . &randchar(1) . "a.." . &randchar(4); + } else { + $salt = &randchar(2); + } + $salt; +} + +# return $count random characters +sub randchar { + local($count) = @_; + local($str) = ""; + local($enc) = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + while ($count--) { + # 64 = length($enc) in call to rand() below + $str .= substr($enc,int(rand(64))+1,1); + } + $str; +} diff --git a/RELEASE_1_1_X/src/support/dbmmanage.readme b/RELEASE_1_1_X/src/support/dbmmanage.readme new file mode 100644 index 00000000000..3862096b1eb --- /dev/null +++ b/RELEASE_1_1_X/src/support/dbmmanage.readme @@ -0,0 +1,6 @@ +Two versions of the dbmmanage script are included with this release. +One is the old faithful version, which should continue to work if you've +been using it; the other is a newer cut, which can be easily modified to +support the newer extended crypt routines which are present on some +systems (including 4.4BSD derivatives); this newer version is, for the +nonce, experimental... diff --git a/RELEASE_1_1_X/src/support/htdigest.c b/RELEASE_1_1_X/src/support/htdigest.c new file mode 100644 index 00000000000..ab8b712701f --- /dev/null +++ b/RELEASE_1_1_X/src/support/htdigest.c @@ -0,0 +1,182 @@ +/* + * htdigest.c: simple program for manipulating digest passwd file for Apache + * + * by Alexei Kosut, based on htpasswd.c, by Rob McCool + */ + +#include +#include +#include +#include +#include +#include + +/* This is probably the easiest way to do it */ +#include "../src/md5c.c" + +#define LF 10 +#define CR 13 + +#define MAX_STRING_LEN 256 + +char *tn; + +char *strd(char *s) { + char *d; + + d=(char *)malloc(strlen(s) + 1); + strcpy(d,s); + return(d); +} + +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++])); +} + +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; + } +} + +void putline(FILE *f,char *l) { + int x; + + for(x=0;l[x];x++) fputc(l[x],f); + fputc('\n',f); +} + + +void add_password(char *user, char *realm, FILE *f) { + char *pw, *cpw; + MD5_CTX context; + unsigned char digest[16]; + char string[MAX_STRING_LEN]; + unsigned int i; + + pw = strd((char *) getpass("New password:")); + if(strcmp(pw,(char *) getpass("Re-type new password:"))) { + fprintf(stderr,"They don't match, sorry.\n"); + if(tn) + unlink(tn); + exit(1); + } + fprintf(f,"%s:%s:",user,realm); + + /* Do MD5 stuff */ + sprintf(string, "%s:%s:%s", user, realm, pw); + + MD5Init (&context); + MD5Update (&context, string, strlen(string)); + MD5Final (digest, &context); + + for (i = 0; i < 16; i++) + fprintf(f, "%02x", digest[i]); + + fprintf(f, "\n"); +} + +void usage() { + fprintf(stderr,"Usage: htdigest [-c] passwordfile realm username\n"); + fprintf(stderr,"The -c flag creates a new file.\n"); + exit(1); +} + +void interrupted() { + fprintf(stderr,"Interrupted.\n"); + if(tn) unlink(tn); + exit(1); +} + +void 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 (*)())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); + sprintf(command,"cp %s %s",tn,argv[1]); + system(command); + unlink(tn); +} diff --git a/RELEASE_1_1_X/src/support/htpasswd.c b/RELEASE_1_1_X/src/support/htpasswd.c new file mode 100644 index 00000000000..239617056fb --- /dev/null +++ b/RELEASE_1_1_X/src/support/htpasswd.c @@ -0,0 +1,180 @@ +/* + * htpasswd.c: simple program for manipulating password file for NCSA httpd + * + * Rob McCool + */ + +#include +#include +#include +#include +#include +#include + +#define LF 10 +#define CR 13 + +#define MAX_STRING_LEN 256 + +char *tn; + +char *strd(char *s) { + char *d; + + d=(char *)malloc(strlen(s) + 1); + strcpy(d,s); + return(d); +} + +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++]); +} + +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; + } +} + +void putline(FILE *f,char *l) { + int x; + + for(x=0;l[x];x++) fputc(l[x],f); + fputc('\n',f); +} + + +/* From local_passwd.c (C) Regents of Univ. of California blah blah */ +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +to64(s, v, n) + register char *s; + register long v; + register int n; +{ + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +char *crypt(char *pw, char *salt); /* why aren't these prototyped in include */ + +void add_password(char *user, FILE *f) { + char *pw, *cpw, salt[3]; + + pw = strd((char *) getpass("New password:")); + if(strcmp(pw,(char *) getpass("Re-type new password:"))) { + fprintf(stderr,"They don't match, sorry.\n"); + if(tn) + unlink(tn); + exit(1); + } + (void)srand((int)time((time_t *)NULL)); + to64(&salt[0],rand(),2); + cpw = crypt(pw,salt); + free(pw); + fprintf(f,"%s:%s\n",user,cpw); +} + +void usage() { + fprintf(stderr,"Usage: htpasswd [-c] passwordfile username\n"); + fprintf(stderr,"The -c flag creates a new file.\n"); + exit(1); +} + +void interrupted() { + fprintf(stderr,"Interrupted.\n"); + if(tn) unlink(tn); + exit(1); +} + +main(int argc, char *argv[]) { + FILE *tfp,*f; + char user[MAX_STRING_LEN]; + char line[MAX_STRING_LEN]; + char l[MAX_STRING_LEN]; + char w[MAX_STRING_LEN]; + char command[MAX_STRING_LEN]; + int found; + + tn = NULL; + signal(SIGINT,(void (*)())interrupted); + if(argc == 4) { + 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.\n",argv[3]); + add_password(argv[3],tfp); + fclose(tfp); + exit(0); + } else if(argc != 3) 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[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,':'); + if(strcmp(user,w)) { + putline(tfp,line); + continue; + } + else { + printf("Changing password for user %s\n",user); + add_password(user,tfp); + found = 1; + } + } + if(!found) { + printf("Adding user %s\n",user); + add_password(user,tfp); + } + fclose(f); + fclose(tfp); + sprintf(command,"cp %s %s",tn,argv[1]); + system(command); + unlink(tn); +} diff --git a/RELEASE_1_1_X/src/support/httpd.1m b/RELEASE_1_1_X/src/support/httpd.1m new file mode 100644 index 00000000000..e3db0b49874 --- /dev/null +++ b/RELEASE_1_1_X/src/support/httpd.1m @@ -0,0 +1,108 @@ +.TH httpd 1m "October 1995" +.\" Copyright (c) 1995 David Robinson. 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. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +httpd \- Apache hypertext transfer protocol server +.SH SYNOPSIS +.B httpd +[ +.B \-vX? +] [ +.BI \-d " serverroot" +] [ +.BI \-f " config" +] +.SH DESCRIPTION +.B httpd +is the Apache HyperText Transfer Protocol (HTTP) server process. The server may +be invoked by the Internet daemon inetd(1M) each time a connection to the +HTTP service is made, or alternatively it may run as a daemon. +.SH OPTIONS +.TP 12 +.BI \-d " serverroot" +Set the initial value for the ServerRoot variable to \fIserverroot\fP. This +can be overridden by the ServerRoot command in the configuration file. The +default is \fB/usr/local/etc/httpd\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 +.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 httpd, and then exit. +.TP +.B \-? +Print a list of the httpd options, and then exit. +.SH FILES +.PD 0 +.B /usr/local/etc/httpd/conf/httpd.conf +.br +.B /usr/local/etc/httpd/conf/srm.conf +.br +.B /usr/local/etc/httpd/conf/access.conf +.br +.B /usr/local/etc/httpd/conf/mime.types +.br +.B /usr/local/etc/httpd/logs/error_log +.br +.B /usr/local/etc/httpd/logs/access_log +.br +.B /usr/local/etc/httpd/logs/httpd.pid +.PD +.SH SEE ALSO +.BR inetd (1m). +.PP +Documentation for the Apache http server is available from +http://www.apache.org diff --git a/RELEASE_1_1_X/src/support/httpd_monitor.c b/RELEASE_1_1_X/src/support/httpd_monitor.c new file mode 100644 index 00000000000..c9a87eef28f --- /dev/null +++ b/RELEASE_1_1_X/src/support/httpd_monitor.c @@ -0,0 +1,302 @@ +/* + * ==================================================================== + * Copyright (c) 1995 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. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + + + * simple script to monitor the child Apache processes + * Usage: + * httpd_monitor -p pid_file -s sleep_time + * Will give you an update ever sleep_time seconds + * using pid_file as the location of the PID file. + * If you choose 0, it might chew up lots of CPU time. + * + * Output explanation.. + * + * s = sleeping but "ready to go" child + * R = active child + * _ = dead child (no longer needed) + * t = just starting + * + * + * Jim Jagielski + * v1.0 Notes: + * This code is much more ugly and complicated than it + * needs to be. + * + * v1.1: + * Minor fixes + */ + +#include +#include +#include +#include +#include "scoreboard.h" +#include "httpd.h" + +#define PIDFILE_OPT "PidFile" +#define SCORE_OPT "ScoreBoardFile" +#define DEFAULT_SLEEPTIME 2 +#define ASIZE 1024 +#define MAX_PROC 40 + +int +main(argc, argv) +int argc; +char **argv; +{ + short_score scoreboard_image; + FILE *afile; + char conf_name[ASIZE]; + char pid_name[ASIZE]; + char score_name[ASIZE]; + char tbuf[ASIZE]; + char *ptmp; + static char kid_stat[] = { '_', 's', 'R', 't' }; + char achar; + long thepid; + int score_fd; + int sleep_time = DEFAULT_SLEEPTIME; + int last_len = 0; + int kiddies; + int running, dead, total, loop; + short got_config = 0; + struct stat statbuf; + time_t last_time = 0; + extern char *optarg; + extern int optind, opterr; + void lookfor(); + + int usage(); + + /* + * Handle the options. Using getopt() is most probably overkill, + * but let's think about the future! + */ + strcpy(conf_name, HTTPD_ROOT); + while((achar = getopt(argc,argv,"s:d:f:")) != -1) { + switch(achar) { + case 'd': + strcpy(conf_name, optarg); + break; + case 'f': + strcpy(conf_name, optarg); + got_config = 1; + break; + case 's': + sleep_time = atoi(optarg); + break; + case '?': + usage(argv[0]); + } + } + + /* + * Now build the name of the httpd.conf file + */ + if (!got_config) { + strcat(conf_name, "/"); + strcat(conf_name, SERVER_CONFIG_FILE); + } + + /* + * Make sure we have the right file... Barf if not + */ + if (!(afile = fopen(conf_name, "r"))) { + perror("httpd_monitor"); + fprintf(stderr, "Can't open config file: %s\n", conf_name); + exit(1); + } + /* + * now scan thru the ConfigFile to look for the items that + * interest us + */ + lookfor(pid_name, score_name, afile); + fclose(afile); + + /* + * now open the PidFile and then the ScoreBoardFile + */ + if (!(afile = fopen(pid_name, "r"))) { + perror("httpd_monitor"); + fprintf(stderr, "Can't open PIDfile: %s\n", pid_name); + exit(1); + } + fscanf(afile, "%ld", &thepid); + fclose(afile); + + /* + * Enough taters, time for the MEAT! + */ + for(;;sleep(sleep_time)) { + if (stat(score_name, &statbuf)) { + perror("httpd_monitor"); + fprintf(stderr, "Can't stat scoreboard file: %s\n", score_name); + exit(1); + } + if (last_time == statbuf.st_mtime) + continue; /* tricky ;) */ + last_time = statbuf.st_mtime; /* for next time */ + if ((score_fd = open(score_name, 0)) == -1 ) { + perror("httpd_monitor"); + fprintf(stderr, "Can't open scoreboard file: %s\n", score_name); + exit(1); + } + /* + * all that for _this_ + */ + running = dead = total = 0; + ptmp = tbuf; + *ptmp = '\0'; + for(kiddies=0;kiddies strlen(tbuf)) { + for(loop=1;loop<=last_len;loop++) + putchar(' '); + for(loop=1;loop<=last_len;loop++) + putchar('\010'); + } + printf("%s", tbuf); + fflush(stdout); + last_len = strlen(tbuf); + } /* for */ +} + +int +usage(arg) +char *arg; +{ + printf("httpd_monitor: Usage\n"); + printf(" httpd_monitor [ -d config-dir] [ -s sleep-time ]\n"); + printf(" Defaults: config-dir = %s\n", HTTPD_ROOT); + printf(" sleep-time = %d seconds\n", DEFAULT_SLEEPTIME); + exit(0); +} + +/* + * This function uses some hard-wired knowledge about the + * Apache httpd.conf file setup (basically names of the 3 + * parameters we are interested in) + * + * We basically scan thru the file and grab the 3 values we + * need. This could be done better... + */ +void +lookfor(pidname, scorename, thefile) +char *pidname, *scorename; +FILE *thefile; +{ + char line[ASIZE], param[ASIZE], value[ASIZE]; + char sroot[ASIZE], pidfile[ASIZE], scorefile[ASIZE]; + + *sroot = *pidfile = *scorefile = '\0'; + while (!(feof(thefile))) { + fgets(line, ASIZE-1, thefile); + *value = '\0'; /* protect braindead sscanf() */ + sscanf(line, "%s %s", param, value); + if (strcmp(param, "PidFile")==0 && *value) + strcpy(pidfile, value); + if (strcmp(param, "ScoreBoardFile")==0 && *value) + strcpy(scorefile, value); + if (strcmp(param, "ServerRoot")==0 && *value) + strcpy(sroot, value); + } + + /* + * We've reached EOF... we should have encountered the + * ServerRoot line... if not, we bail out + */ + if (!*sroot) { + perror("httpd_monitor"); + fprintf(stderr, "Can't find ServerRoot!\n"); + exit(1); + } + + /* + * Not finding PidFile or ScoreBoardFile is OK, since + * we have defaults for them + */ + if (!*pidfile) + strcpy(pidfile, DEFAULT_PIDLOG); + if (!*scorefile) + strcpy(scorefile, DEFAULT_SCOREBOARD); + + /* + * Relative or absolute? Handle both + */ + if (*pidfile == '/') + strcpy(pidname, pidfile); + else { + strcpy(pidname, sroot); + strcat(pidname, "/"); + strcat(pidname, pidfile); + } + if (*scorefile == '/') + strcpy(scorename, scorefile); + else { + strcpy(scorename, sroot); + strcat(scorename, "/"); + strcat(scorename, scorefile); + } +} + diff --git a/RELEASE_1_1_X/src/support/log_server_status b/RELEASE_1_1_X/src/support/log_server_status new file mode 100755 index 00000000000..ed75197f085 --- /dev/null +++ b/RELEASE_1_1_X/src/support/log_server_status @@ -0,0 +1,110 @@ +#!/usr/local/bin/perl + +# ==================================================================== +# Copyright (c) 1995 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. 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 . + + +# 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/960312" +$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 + +{ + $date=`date +%y%m%d:%H%M%S`; + chop($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 () { + $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/RELEASE_1_1_X/src/support/logresolve.c b/RELEASE_1_1_X/src/support/logresolve.c new file mode 100644 index 00000000000..c66a34467f1 --- /dev/null +++ b/RELEASE_1_1_X/src/support/logresolve.c @@ -0,0 +1,352 @@ +/*** ***\ + + 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 +#include +#include +#include +#include + +#include +#include + +#include +#include + +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 + +/* + * 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 + */ + +/* 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(ipnum, string, check) +struct in_addr ipnum; +char *string; +int check; +{ + struct nsrec **current, *new; + struct hostent *hostdata; + char *name; + extern int h_errno; /* some machines don't have this in their headers */ + + 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++; + + strcpy(string, (*current)->hostname); +} + +/* + * prints various statistics to output + */ + +static void +stats(output) +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(s, n) +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(argc, argv) +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"); + 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/RELEASE_1_1_X/src/support/rotatelogs.c b/RELEASE_1_1_X/src/support/rotatelogs.c new file mode 100644 index 00000000000..e446841fa33 --- /dev/null +++ b/RELEASE_1_1_X/src/support/rotatelogs.c @@ -0,0 +1,82 @@ +/* + +Simple program to rotate Apache logs without having to kill the server. + +Contributed by Ben Laurie + +12 Mar 1996 + +*/ + +#define BUFSIZE 65536 +#define MAX_PATH 1024 + +#include +#include +#include +#include + +void main(int argc,char **argv) + { + char buf[BUFSIZE],buf2[MAX_PATH]; + time_t tLogEnd; + time_t tRotation; + int nLogFD=-1; + int nRead; + char *szLogRoot; + + if(argc != 3) + { + fprintf(stderr,"%s \n\n",argv[0]); +#ifdef __EMX__ + 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\n"); + fprintf(stderr,"system time at which the log nominally starts (N.B. this time will always be a\n"); + fprintf(stderr,"multiple of the rotation time, so you can synchronize cron scripts with it).\n"); + fprintf(stderr,"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.%d",szLogRoot,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/RELEASE_1_1_X/src/test/cls.c b/RELEASE_1_1_X/src/test/cls.c new file mode 100644 index 00000000000..2c553cec93d --- /dev/null +++ b/RELEASE_1_1_X/src/test/cls.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include + +/* + * Compare a string to a mask + * Mask characters: + * @ - uppercase letter + * # - lowercase letter + * & - hex digit + * # - digit + * * - swallow remaining characters + * - exact match for any other character + */ +static int +checkmask(const char *data, const char *mask) +{ + int i, ch, d; + + for (i=0; mask[i] != '\0' && mask[i] != '*'; i++) + { + ch = mask[i]; + d = data[i]; + if (ch == '@') + { + if (!isupper(d)) return 0; + } else if (ch == '$') + { + if (!islower(d)) return 0; + } else if (ch == '#') + { + if (!isdigit(d)) return 0; + } else if (ch == '&') + { + if (!isxdigit(d)) return 0; + } else if (ch != d) return 0; + } + + if (mask[i] == '*') return 1; + else return (data[i] == '\0'); +} + +/* + * Converts 8 hex digits to a time integer + */ +static int +hex2sec(const char *x) +{ + int i, ch; + unsigned int j; + + for (i=0, j=0; i < 8; i++) + { + ch = x[i]; + j <<= 4; + if (isdigit(ch)) j |= ch - '0'; + else if (isupper(ch)) j |= ch - ('A' - 10); + else j |= ch - ('a' - 10); + } + if (j == 0xffffffff) return -1; /* so that it works with 8-byte ints */ + else return j; +} + +int +main(int argc, char **argv) +{ + int i, ver; + DIR *d; + struct dirent *e; + const char *s; + FILE *fp; + char path[FILENAME_MAX+1]; + char line[1035]; + time_t date, lmod, expire; + unsigned int len; + struct tm ts; + char sdate[30], slmod[30], sexpire[30]; + const char time_format[]="%e %b %Y %R"; + + if (argc != 2) + { + printf("Usage: cls directory\n"); + exit(0); + } + + d = opendir(argv[1]); + if (d == NULL) + { + perror("opendir"); + exit(1); + } + + for (;;) + { + e = readdir(d); + if (e == NULL) break; + s = e->d_name; + if (s[0] == '.' || s[0] == '#') continue; + sprintf(path, "%s/%s", argv[1], s); + fp = fopen(path, "r"); + if (fp == NULL) + { + perror("fopen"); + continue; + } + if (fgets(line, 1034, fp) == NULL) + { + perror("fgets"); + fclose(fp); + continue; + } + if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&\n")) + { + fprintf(stderr, "Bad cache file\n"); + fclose(fp); + continue; + } + date = hex2sec(line); + lmod = hex2sec(line+9); + expire = hex2sec(line+18); + ver = hex2sec(line+27); + len = hex2sec(line+35); + if (fgets(line, 1034, fp) == NULL) + { + perror("fgets"); + fclose(fp); + continue; + } + fclose(fp); + i = strlen(line); + if (strncmp(line, "X-URL: ", 7) != 0 || line[i-1] != '\n') + { + fprintf(stderr, "Bad cache file\n"); + continue; + } + line[i-1] = '\0'; + if (date != -1) + { + ts = *gmtime(&date); + strftime(sdate, 30, time_format, &ts); + } else + strcpy(sdate, "-"); + + if (lmod != -1) + { + ts = *gmtime(&lmod); + strftime(slmod, 30, time_format, &ts); + } else + strcpy(slmod, "-"); + + if (expire != -1) + { + ts = *gmtime(&expire); + strftime(sexpire, 30, time_format, &ts); + } else + strcpy(sexpire, "-"); + + printf("%s: %d; %s %s %s\n", line+7, ver, sdate, slmod, sexpire); + } + + closedir(d); + return 0; +}