summaryrefslogtreecommitdiffstats
path: root/usr.sbin/httpd/src
diff options
context:
space:
mode:
authorbeck <beck@openbsd.org>1998-03-25 07:08:28 +0000
committerbeck <beck@openbsd.org>1998-03-25 07:08:28 +0000
commitc031d3d96950de940122b79f85411f295db3b7ea (patch)
tree4ef378a704eec86b2cfb6e65b5dd7019a7f125b8 /usr.sbin/httpd/src
parent Make directories for /var/www - your basic www root (diff)
downloadwireguard-openbsd-c031d3d96950de940122b79f85411f295db3b7ea.tar.xz
wireguard-openbsd-c031d3d96950de940122b79f85411f295db3b7ea.zip
Initial import from apache 1.2.6
Diffstat (limited to 'usr.sbin/httpd/src')
-rw-r--r--usr.sbin/httpd/src/.indent.pro1
-rw-r--r--usr.sbin/httpd/src/CHANGES2167
-rw-r--r--usr.sbin/httpd/src/Configuration292
-rw-r--r--usr.sbin/httpd/src/Configuration.tmpl292
-rw-r--r--usr.sbin/httpd/src/Configure688
-rw-r--r--usr.sbin/httpd/src/INSTALL71
-rw-r--r--usr.sbin/httpd/src/Makefile.bsd-wrapper48
-rw-r--r--usr.sbin/httpd/src/Makefile.tmpl162
-rw-r--r--usr.sbin/httpd/src/PORTING257
-rw-r--r--usr.sbin/httpd/src/README147
-rw-r--r--usr.sbin/httpd/src/alloc.c1142
-rw-r--r--usr.sbin/httpd/src/alloc.h252
-rw-r--r--usr.sbin/httpd/src/buff.c1011
-rw-r--r--usr.sbin/httpd/src/buff.h139
-rw-r--r--usr.sbin/httpd/src/conf.h763
-rw-r--r--usr.sbin/httpd/src/explain.c14
-rw-r--r--usr.sbin/httpd/src/explain.h23
-rw-r--r--usr.sbin/httpd/src/helpers/CutRule7
-rw-r--r--usr.sbin/httpd/src/helpers/GuessOS234
-rw-r--r--usr.sbin/httpd/src/helpers/PrintPath46
-rw-r--r--usr.sbin/httpd/src/htconf.63425
-rw-r--r--usr.sbin/httpd/src/http_bprintf.c602
-rw-r--r--usr.sbin/httpd/src/http_conf_globals.h86
-rw-r--r--usr.sbin/httpd/src/http_config.c1213
-rw-r--r--usr.sbin/httpd/src/http_config.h298
-rw-r--r--usr.sbin/httpd/src/http_core.c1413
-rw-r--r--usr.sbin/httpd/src/http_core.h205
-rw-r--r--usr.sbin/httpd/src/http_log.c197
-rw-r--r--usr.sbin/httpd/src/http_log.h62
-rw-r--r--usr.sbin/httpd/src/http_main.c2620
-rw-r--r--usr.sbin/httpd/src/http_main.h99
-rw-r--r--usr.sbin/httpd/src/http_protocol.c2047
-rw-r--r--usr.sbin/httpd/src/http_protocol.h191
-rw-r--r--usr.sbin/httpd/src/http_request.c1152
-rw-r--r--usr.sbin/httpd/src/http_request.h92
-rw-r--r--usr.sbin/httpd/src/httpd.h766
-rw-r--r--usr.sbin/httpd/src/md5.h99
-rw-r--r--usr.sbin/httpd/src/md5c.c354
-rw-r--r--usr.sbin/httpd/src/mod_access.c283
-rw-r--r--usr.sbin/httpd/src/mod_actions.c219
-rw-r--r--usr.sbin/httpd/src/mod_alias.c329
-rw-r--r--usr.sbin/httpd/src/mod_asis.c131
-rw-r--r--usr.sbin/httpd/src/mod_auth.c298
-rw-r--r--usr.sbin/httpd/src/mod_auth_anon.c299
-rw-r--r--usr.sbin/httpd/src/mod_auth_db.c303
-rw-r--r--usr.sbin/httpd/src/mod_auth_dbm.c291
-rw-r--r--usr.sbin/httpd/src/mod_auth_msql.c996
-rw-r--r--usr.sbin/httpd/src/mod_browser.c189
-rw-r--r--usr.sbin/httpd/src/mod_cern_meta.c325
-rw-r--r--usr.sbin/httpd/src/mod_cgi.c575
-rw-r--r--usr.sbin/httpd/src/mod_digest.c363
-rw-r--r--usr.sbin/httpd/src/mod_dir.c911
-rw-r--r--usr.sbin/httpd/src/mod_dld.c190
-rw-r--r--usr.sbin/httpd/src/mod_env.c261
-rw-r--r--usr.sbin/httpd/src/mod_expires.c477
-rw-r--r--usr.sbin/httpd/src/mod_headers.c253
-rw-r--r--usr.sbin/httpd/src/mod_imap.c877
-rw-r--r--usr.sbin/httpd/src/mod_include.c2329
-rw-r--r--usr.sbin/httpd/src/mod_info.c455
-rw-r--r--usr.sbin/httpd/src/mod_log_agent.c198
-rw-r--r--usr.sbin/httpd/src/mod_log_config.c787
-rw-r--r--usr.sbin/httpd/src/mod_log_referer.c235
-rw-r--r--usr.sbin/httpd/src/mod_mime.c324
-rw-r--r--usr.sbin/httpd/src/mod_negotiation.c2052
-rw-r--r--usr.sbin/httpd/src/mod_rewrite.c3335
-rw-r--r--usr.sbin/httpd/src/mod_rewrite.h400
-rw-r--r--usr.sbin/httpd/src/mod_status.c643
-rw-r--r--usr.sbin/httpd/src/mod_userdir.c214
-rw-r--r--usr.sbin/httpd/src/mod_usertrack.c328
-rw-r--r--usr.sbin/httpd/src/modules/example/Makefile107
-rw-r--r--usr.sbin/httpd/src/modules/example/README53
-rw-r--r--usr.sbin/httpd/src/modules/example/mod_example.c1110
-rw-r--r--usr.sbin/httpd/src/modules/proxy/Makefile89
-rw-r--r--usr.sbin/httpd/src/modules/proxy/mod_proxy.c554
-rw-r--r--usr.sbin/httpd/src/modules/proxy/mod_proxy.h273
-rw-r--r--usr.sbin/httpd/src/modules/proxy/proxy_cache.c933
-rw-r--r--usr.sbin/httpd/src/modules/proxy/proxy_connect.c228
-rw-r--r--usr.sbin/httpd/src/modules/proxy/proxy_ftp.c1015
-rw-r--r--usr.sbin/httpd/src/modules/proxy/proxy_http.c413
-rw-r--r--usr.sbin/httpd/src/modules/proxy/proxy_util.c750
-rw-r--r--usr.sbin/httpd/src/regex/COPYRIGHT20
-rw-r--r--usr.sbin/httpd/src/regex/Makefile133
-rw-r--r--usr.sbin/httpd/src/regex/README32
-rw-r--r--usr.sbin/httpd/src/regex/WHATSNEW92
-rw-r--r--usr.sbin/httpd/src/regex/cclass.h31
-rw-r--r--usr.sbin/httpd/src/regex/cname.h102
-rw-r--r--usr.sbin/httpd/src/regex/debug.c242
-rw-r--r--usr.sbin/httpd/src/regex/engine.c1019
-rw-r--r--usr.sbin/httpd/src/regex/main.c510
-rw-r--r--usr.sbin/httpd/src/regex/mkh76
-rw-r--r--usr.sbin/httpd/src/regex/regcomp.c1546
-rw-r--r--usr.sbin/httpd/src/regex/regerror.c124
-rw-r--r--usr.sbin/httpd/src/regex/regex.3502
-rw-r--r--usr.sbin/httpd/src/regex/regex.7233
-rw-r--r--usr.sbin/httpd/src/regex/regex.h73
-rw-r--r--usr.sbin/httpd/src/regex/regex2.h132
-rw-r--r--usr.sbin/httpd/src/regex/regexec.c140
-rw-r--r--usr.sbin/httpd/src/regex/regfree.c37
-rw-r--r--usr.sbin/httpd/src/regex/split.c316
-rw-r--r--usr.sbin/httpd/src/regex/tests475
-rw-r--r--usr.sbin/httpd/src/regex/utils.h22
-rw-r--r--usr.sbin/httpd/src/rfc1413.c226
-rw-r--r--usr.sbin/httpd/src/rfc1413.h53
-rw-r--r--usr.sbin/httpd/src/scoreboard.h110
-rw-r--r--usr.sbin/httpd/src/util.c1369
-rw-r--r--usr.sbin/httpd/src/util_date.c296
-rw-r--r--usr.sbin/httpd/src/util_date.h63
-rw-r--r--usr.sbin/httpd/src/util_md5.c192
-rw-r--r--usr.sbin/httpd/src/util_md5.h58
-rw-r--r--usr.sbin/httpd/src/util_script.c641
-rw-r--r--usr.sbin/httpd/src/util_script.h69
-rw-r--r--usr.sbin/httpd/src/util_snprintf.c949
112 files changed, 53555 insertions, 0 deletions
diff --git a/usr.sbin/httpd/src/.indent.pro b/usr.sbin/httpd/src/.indent.pro
new file mode 100644
index 00000000000..77b65f3d6a7
--- /dev/null
+++ b/usr.sbin/httpd/src/.indent.pro
@@ -0,0 +1 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs
diff --git a/usr.sbin/httpd/src/CHANGES b/usr.sbin/httpd/src/CHANGES
new file mode 100644
index 00000000000..f7cb6411f45
--- /dev/null
+++ b/usr.sbin/httpd/src/CHANGES
@@ -0,0 +1,2167 @@
+Changes with Apache 1.2.6
+
+ *) Increase the robustness of the child_main loop. When unexpected
+ select() or accept() errors occur we exit() the child. This deals
+ with many reported problems where apache would fill the error_log
+ with messages. [Dean Gaudet] PR#1747, 1107, 588, 1787, 987, 588
+
+ *) PORT: Add -lm to LIBS for HPUX. [Dean Gaudet] PR#1639
+
+ *) SECURITY: "UserDir /abspath" without a * in the path would allow
+ remote users to access "/~.." and bypass access restrictions
+ (but note /~../.. was handled properly).
+ [Lauri Jesmin <jesmin@ut.ee>] PR#1701
+
+ *) mod_rewrite's RewriteLog should behave like mod_log_config, it
+ shouldn't force hostname lookups. [Dean Gaudet] PR#1684
+
+ *) mod_include when using XBitHack Full would send ETags in addition to
+ sending Last-Modifieds. This is incorrect HTTP/1.1 behaviour.
+ [Dean Gaudet] PR#1133
+
+ *) SECURITY: When a client connects to a particular port/addr, and
+ gives a Host: header ensure that the virtual host requested can
+ actually be reached via that port/addr. [Ed Korthof <ed@organic.com>]
+
+ *) Support virtual hosts with wildcard port and/or multiple ports
+ properly. [Ed Korthof <ed@organic.com>]
+
+ *) Fixed some case-sensitivity issues according to RFC2068.
+ [Dean Gaudet]
+
+ *) Set r->allowed properly in mod_asis.c, mod_dir.c, mod_info.c,
+ and mod_include.c. [Dean Gaudet]
+
+ *) Variable 'cwd' was being used pointlessly before being set.
+ [Ken Coar] PR#1738
+
+ *) SIGURG doesn't exist on all platforms.
+ [Mark Andrew Heinrich <heinrich@tinderbox.Stanford.EDU>]
+
+ *) When an error occurs during a POST, or other operation with a
+ request body, the body has to be read from the net before allowing
+ a keepalive session to continue. [Roy Fielding] PR#1399
+
+ *) When an error occurs in fcntl() locking suggest the user look up
+ the docs for LockFile. [Dean Gaudet]
+
+ *) table_set() and table_unset() did not deal correctly with
+ multiple occurrences of the same key. [Stephen Scheck
+ <sscheck@infonex.net>, Ben Laurie] PR#1604
+
+ *) send_fd_length() did not calculate total_bytes_sent properly in error
+ cases. [Ben Reser <breser@regnow.com>] PR#1366
+
+ *) r->connection->user was allocated in the wrong pool causing corruption
+ in some cases when used with mod_cern_meta. [Dean Gaudet] PR#1500
+
+ *) mod_proxy was sending HTTP/1.1 responses to ftp requests by mistake.
+ Also removed the auto-generated link to www.apache.org that was the
+ source of so many misdirected bug reports. [Roy Fielding, Marc Slemko]
+
+ *) Multiple "close" tokens may have been set in the "Connection"
+ header, not an error, but a waste.
+ [Ronald.Tschalaer@psi.ch] PR#1683
+
+ *) "basic" and "digest" auth tokens should be tested case-insensitive.
+ [Ronald.Tschalaer@psi.ch] PR#1599, PR#1666
+
+ *) It appears the "257th byte" bug (see
+ htdocs/manual/misc/known_client_problems.html#257th-byte) can happen
+ at the 256th byte as well. Fixed. [Dean Gaudet]
+
+ *) mod_rewrite would not handle %3f properly in some situations.
+ [Ralf Engelschall]
+
+ *) Apache could generate improperly chunked HTTP/1.1 responses when
+ the bputc() or rputc() functions were used by modules (such as
+ mod_include). [Dean Gaudet]
+
+ *) #ifdef wrap a few #defines in httpd.h to make life easier on
+ some ports. [Ralf Engelschall]
+
+ *) Fix MPE compilation error in mod_usertrack.c. [Mark Bixby]
+
+ *) Quote CC='$(CC)' to improve recurse make calls. [Martin Kraemer]
+
+ *) Avoid B_ERROR redeclaration on sysvr4 systems. [Martin Kraemer]
+
+Changes with Apache 1.2.5
+
+ *) SECURITY: Fix a possible buffer overflow in logresolve. This is
+ only an issue on systems without a MAXDNAME define or where
+ the resolver returns domain names longer than MAXDNAME. [Marc Slemko]
+
+ *) Fix an improper length in an ap_snprintf call in proxy_date_canon().
+ [Marc Slemko]
+
+ *) Fix core dump in the ftp proxy when reading incorrectly formatted
+ directory listings. [Marc Slemko]
+
+ *) SECURITY: Fix possible minor buffer overflow in the proxy cache.
+ [Marc Slemko]
+
+ *) SECURITY: Eliminate possible buffer overflow in cfg_getline, which
+ is used to read various types of files such as htaccess and
+ htpasswd files. [Marc Slemko]
+
+ *) SECURITY: Ensure that the buffer returned by ht_time is always
+ properly null terminated. [Marc Slemko]
+
+ *) SECURITY: General mod_include cleanup, including fixing several
+ possible buffer overflows and a possible infinite loop. This cleanup
+ was done against 1.3 code and then backported to 1.2, the result
+ is a large difference (due to indentation cleanup in 1.3 code).
+ Users interested in seeing a smaller set of relevant differences
+ should consider comparing against src/modules/standard/mod_include.c
+ from the 1.3b3 release. Non-indentation changes to mod_include
+ between 1.2 and 1.3 were minimal. [Dean Gaudet, Marc Slemko]
+
+ *) SECURITY: Numerous changes to mod_imap in a general cleanup
+ including fixing a possible buffer overflow. This cleanup also
+ was done with 1.3 code as a basis, see the the previous note
+ about mod_include. [Dean Gaudet]
+
+ *) SECURITY: If a htaccess file can not be read due to bad
+ permissions, deny access to the directory with a HTTP_FORBIDDEN.
+ The previous behavior was to ignore the htaccess file if it could not
+ be read. This change may make some setups with unreadable
+ htaccess files stop working. PR#817 [Marc Slemko]
+
+ *) SECURITY: no2slash() was O(n^2) in the length of the input.
+ Make it O(n). This inefficiency could be used to mount a denial
+ of service attack against the Apache server. Thanks to
+ Michal Zalewski <lcamtuf@boss.staszic.waw.pl> for reporting
+ this. [Dean Gaudet]
+
+ *) mod_include used uninitialized data for some uses of && and ||.
+ [Brian Slesinsky <bslesins@wired.com>] PR#1139
+
+ *) mod_imap should decline all non-GET methods.
+ [Jay Bloodworth <jay@pathways.sde.state.sc.us>]
+
+ *) suexec.c wouldn't build without -DLOG_EXEC. [Jason A. Dour]
+
+ *) mod_userdir was modifying r->finfo in cases where it wasn't setting
+ r->filename. Since those two are meant to be in sync with each other
+ this is a bug. ["Paul B. Henson" <henson@intranet.csupomona.edu>]
+
+ *) mod_include did not properly handle all possible redirects from sub-
+ requests. [Ken Coar]
+
+ *) Inetd mode (which is buggy) uses timeouts without having setup the
+ jmpbuffer. [Dean Gaudet] PR#1064
+
+ *) Work around problem under Linux where a child will start looping
+ reporting a select error over and over.
+ [Rick Franchuk <rickf@transpect.net>] PR#1107
+
+Changes with Apache 1.2.4
+
+ *) The ProxyRemote change in 1.2.3 introduced a bug resulting in the proxy
+ always making requests with the full-URI instead of just the URI path.
+ [Marc Slemko, Roy Fielding]
+
+ *) Add -lm for AIX versions >= 4.2 to allow Apache to link properly
+ on this platform. [Marc Slemko]
+
+Changes with Apache 1.2.3
+
+ *) The request to a remote proxy was mangled if it was generated as the
+ result of a ProxyPass directive. URL schemes other than http:// were not
+ supported when ProxyRemote was used. PR#260, PR#656, PR#699, PR#713,
+ PR#812 [Lars Eilebrecht]
+
+ *) Fixed proxy-pass-through feature of mod_rewrite; Added error logging
+ information for case where proxy module is not available. [Marc Slemko]
+
+ *) Force proxy to always respond as HTTP/1.0, which it was failing to
+ do for errors and cached responses. [Roy Fielding]
+
+ *) PORT: Improved support for ConvexOS 11. [Jeff Venters]
+
+Changes with Apache 1.2.2 [not released]
+
+ *) Fixed another long-standing bug in sub_req_lookup_file where it would
+ happily skip past access checks on subdirectories looked up with relative
+ paths. (It's used by mod_dir, mod_negotiation, and mod_include.)
+ [Dean Gaudet]
+
+ *) Add lockfile name to error message printed out when
+ USE_FLOCK_SERIALIZED_ACCEPT is defined.
+ [Marc Slemko]
+
+ *) Enhanced the chunking and error handling inside the buffer functions.
+ [Dean Gaudet, Roy Fielding]
+
+ *) When merging the main server's <Directory> and <Location> sections into
+ a vhost, put the main server's first and the vhost's second. Otherwise
+ the vhost can't override the main server. [Dean Gaudet] PR#717
+
+ *) The <Directory> code would merge and re-merge the same section after
+ a match was found, possibly causing problems with some modules.
+ [Dean Gaudet]
+
+ *) Fixed an infinite loop in mod_imap for references above the server root.
+ [Dean Gaudet] PR#748
+
+ *) mod_include cleanup showed that handle_else was being used to handle
+ endif. It didn't cause problems, but it was cleaned up too.
+ [Howard Fear]
+
+ *) Last official synchonisation of mod_rewrite with author version (because
+ mod_rewrite is now directly developed by the author at the Apache Group):
+ o added diff between mod_rewrite 3.0.6+ and 3.0.9
+ minus WIN32/NT stuff, but plus copyright removement.
+ In detail:
+ - workaround for detecting infinite rewriting loops
+ - fixed setting of env vars when "-" is used as subst string
+ - fixed forced response code on redirects (PR#777)
+ - fixed cases where r->args is ""
+ - kludge to disable locking on pipes under braindead SunOS
+ - fix for rewritelog in cases where remote hostname is unknown
+ - fixed totally damaged request_rec walk-back loop
+ o remove static from local data and add static to global ones.
+ o replaced ugly proxy finding stuff by simple
+ find_linked_module("mod_proxy") call.
+ o added missing negation char on rewritelog()
+ o fixed a few comment typos
+ [Ralf S. Engelschall]
+
+ *) Anonymous_LogEmail was logging on each subrequest.
+ [Dean Gaudet] PR#421, PR#868
+
+ *) "force-response-1.0" now only applies to requests which are HTTP/1.0 to
+ begin with. "nokeepalive" now works for HTTP/1.1 clients. Added
+ "downgrade-1.0" which causes Apache to pretend it received a 1.0.
+ Additionally mod_browser now triggers during translate_name to workaround
+ a deficiency in the header_parse phase.
+ [Dean Gaudet] PR#875
+
+ *) get_client_block() returns wrong length if policy is
+ REQUEST_CHUNKED_DECHUNK.
+ [Kenichi Hori <ken@d2.bs1.fc.nec.co.jp>] PR#815
+
+ *) Properly treat <files> container like other containers in mod_info.
+ [Marc Slemko] PR#848
+
+ *) The proxy didn't treat the "Host:" keyword of the host header as case-
+ insensitive. The proxy would corrupt the first line of a response from
+ an HTTP/0.9 server. [Kenichi Hori <ken@d2.bs1.fc.nec.co.jp>] PR#813,814
+
+ *) mod_include would log some bogus values occasionally.
+ [Skip Montanaro <skip@calendar.com>, Marc Slemko] PR#797
+
+ *) PORT: The slack fd changes in 1.2.1 introduced a problem with SIGHUP
+ under Solaris 2.x (up through 2.5.1). It has been fixed.
+ [Dean Gaudet] PR#832
+
+ *) API: In HTTP/1.1, whether or not a request message contains a body
+ is independent of the request method and based solely on the presence
+ of a Content-Length or Transfer-Encoding. Therefore, our default
+ handlers need to be prepared to read a body even if they don't know
+ what to do with it; otherwise, the body would be mistaken for the
+ next request on a persistent connection. discard_request_body()
+ has been added to take care of that. [Roy Fielding] PR#378
+
+ *) API: Symbol APACHE_RELEASE provides a numeric form of the Apache
+ release version number, such that it always increases along the
+ same lines as our source code branching. [Roy Fielding]
+
+ *) Minor oversight on multiple variants fixed. [Paul Sutton] PR#94
+
+Changes with Apache 1.2.1
+
+ *) SECURITY: Don't serve file system objects unless they are plain files,
+ symlinks, or directories. This prevents local users from using pipes
+ or named sockets to invoke programs for an extremely crude form of
+ CGI. [Dean Gaudet]
+
+ *) SECURITY: HeaderName and ReadmeName were settable in .htaccess and
+ could contain "../" allowing a local user to "publish" any file on
+ the system. No slashes are allowed now. [Dean Gaudet]
+
+ *) SECURITY: It was possible to violate the symlink Options using mod_dir
+ (headers, readmes, titles), mod_negotiation (type maps), or
+ mod_cern_meta (meta files). [Dean Gaudet]
+
+ *) SECURITY: Apache will refuse to run as "User root" unless
+ BIG_SECURITY_HOLE is defined at compile time. [Dean Gaudet]
+
+ *) CONFIG: If a symlink pointed to a directory then it would be disallowed
+ if it contained a .htaccess disallowing symlinks. This is contrary
+ to the rule that symlink permissions are tested with the symlink
+ options of the parent directory. [Dean Gaudet] PR#353
+
+ *) CONFIG: The LockFile directive can be used to place the serializing
+ lockfile in any location. It previously defaulted to /usr/tmp/htlock.
+ [Somehow it took four of us: Randy Terbush, Jim Jagielski, Dean Gaudet,
+ Marc Slemko]
+
+ *) Request processing now retains state of whether or not the request
+ body has been read, so that internal redirects and subrequests will
+ not try to read it twice (and block). [Roy Fielding]
+
+ *) Add a placeholder in modules/Makefile to avoid errors with certain
+ makes. [Marc Slemko]
+
+ *) QUERY_STRING was unescaped in mod_include, it shouldn't be.
+ [Dean Gaudet] PR#644
+
+ *) mod_include was not properly changing the current directory.
+ [Marc Slemko] PR#742
+
+ *) Attempt to work around problems with third party libraries that do not
+ handle high numbered descriptors (examples include bind, and
+ solaris libc). On all systems apache attempts to keep all permanent
+ descriptors above 15 (called the low slack line). Solaris users
+ can also benefit from adding -DHIGH_SLACK_LINE=256 to EXTRA_CFLAGS
+ which keeps all non-FILE * descriptors above 255. On all systems
+ this should make supporting large numbers of vhosts with many open
+ log files more feasible. If this causes trouble please report it,
+ you can disable this workaround by adding -DNO_SLACK to EXTRA_CFLAGS.
+ [Dean Gaudet] various PRs
+
+ *) Related to the last entry, network sockets are now opened before
+ log files are opened. The only known case where this can cause
+ problems is under Solaris with many virtualhosts and many Listen
+ directives. But using -DHIGH_SLACK_LINE=256 described above will
+ work around this problem. [Dean Gaudet]
+
+ *) USE_FLOCK_SERIALIZED_ACCEPT is now default for FreeBSD, A/UX, and
+ SunOS 4.
+
+ *) Improved unix error response logging. [Marc Slemko]
+
+ *) Update mod_rewrite from 3.0.5 to 3.0.6. New ruleflag
+ QSA=query_string_append. Also fixed a nasty bug in per-dir context:
+ when a URL http://... was used in concunction with a special
+ redirect flag, e.g. R=permanent, the permanent status was lost.
+ [Ronald Tschalaer <Ronald.Tschalaer@psi.ch>, Ralf S. Engelschall]
+
+ *) If an object has multiple variants that are otherwise equal Apache
+ would prefer the last listed variant rather than the first.
+ [Paul Sutton] PR#94
+
+ *) "make clean" at the top level now removes *.o. [Dean Gaudet] PR#752
+
+ *) mod_status dumps core in inetd mode. [Marc Slemko and Roy Fielding]
+ PR#566
+
+ *) pregsub had an off-by-1 in its error checking code. [Alexei Kosut]
+
+ *) PORT: fix rlim_t problems with AIX 4.2. [Marc Slemko] PR#333
+
+ *) PORT: Update Unixware support for 2.1.2.
+ [Lawrence Rosenman <ler@lerctr.org>] PR#511
+
+ *) PORT: NonStop-UX [Joachim Schmitz <schmitz_joachim@tandem.com>] PR#327
+
+ *) PORT: Update ConvexOS support for 11.5.
+ [David DeSimone <fox@convex.com>] PR#399
+
+ *) PORT: Support for dec cc compiler under ultrix.
+ ["P. Alejandro Lopez-Valencia" <alejolo@ideam.gov.co>] PR#388
+
+ *) PORT: Support for Maxion/OS SVR4.2 Real Time Unix. [no name given] PR#383
+
+ *) PORT: Workaround for AIX 3.x compiler bug in http_bprintf.c.
+ [Marc Slemko] PR#725
+
+ *) PORT: fix problem compiling http_bprintf.c with gcc under SCO
+ [Marc Slemko] PR#695
+
+Changes with Apache 1.2
+
+Changes with Apache 1.2b11
+
+ *) Fixed open timestamp fd in proxy_cache.c [Chuck Murcko]
+
+ *) Added undocumented perl SSI mechanism for -DUSE_PERL_SSI and mod_perl.
+ [Rob Hartill]
+
+ *) Proxy needs to use hard_timeout instead of soft_timeout when it is
+ reading from one buffer and writing to another, at least until it has
+ a custom timeout handler. [Roy Fielding and Petr Lampa]
+
+ *) Fixed problem on Irix with servers hanging in IdentityCheck,
+ apparently due to a mismatch between sigaction and setjmp.
+ [Roy Fielding] PR#502
+
+ *) Log correct status code if we timeout before receiving a request (408)
+ or if we received a request-line that was too long to process (414).
+ [Ed Korthof and Roy Fielding] PR#601
+
+ *) Virtual hosts with the same ServerName, but on different ports, were
+ not being selected properly. [Ed Korthof]
+
+ *) Added code to return the requested IP address from proxy_host2addr()
+ if gethostbyaddr() fails due to reverse DNS lookup problems. Original
+ change submitted by Jozsef Hollosi <hollosi@sbcm.com>.
+ [Chuck Murcko] PR#614
+
+ *) If multiple requests on a single connection are used to retrieve
+ data from different virtual hosts, the virtual host list would be
+ scanned starting with the most recently used VH instead of the first,
+ causing most virtual hosts to be ignored.
+ [Paul Sutton and Martin Mares] PR#610
+
+ *) The OS/2 handling of process group was broken by a porting patch for
+ MPE, so restored prior code for OS/2. [Roy Fielding and Garey Smiley]
+
+ *) Inherit virtual server port from main server if none (or "*") is
+ given for VirtualHost. [Dean Gaudet] PR#576
+
+ *) If the lookup for a DirectoryIndex name with content negotiation
+ has found matching variants, but none are acceptable, return the
+ negotiation result if there are no more DirectoryIndex names to lookup.
+ [Petr Lampa and Roy Fielding]
+
+ *) If a soft_timeout occurs after keepalive is set, then the main child
+ loop would try to read another request even though the connection
+ has been aborted. [Roy Fielding]
+
+ *) Configure changes: Allow for whitespace at the start of a
+ Module declaration. Also, be more understanding about the
+ CC=/OPTIM= format in Configuration. Finally, fix compiler
+ flags if using HP-UX's cc compiler. [Jim Jagielski]
+
+ *) Subrequests and internal redirects now inherit the_request from the
+ original request-line. [Roy Fielding]
+
+ *) Test for error conditions before creating output header fields, since
+ we don't want the error message to include those fields. Likewise,
+ reset the content_language(s) and content_encoding of the response
+ before generating or redirecting to an error message, since the new
+ message will have its own Content-* definitions. [Dean Gaudet]
+
+ *) Restored the semantics of headers_out (headers sent only with 200..299
+ and 304 responses) and err_headers_out (headers sent with all responses).
+ Avoid the overhead of copying tables if err_headers_out is empty
+ (the usual case). [Roy Fielding]
+
+ *) Fixed a couple places where a check for the default Content-Type was
+ not properly checking both the value configured by the DefaultType
+ directive and the DEFAULT_TYPE symbol in httpd.h. Changed the value
+ of DEFAULT_TYPE to match the documented default (text/plain).
+ [Dean Gaudet] PR#506
+
+ *) Escape the HTML-sensitive characters in the Request-URI that is
+ output for each child by mod_status. [Dean Gaudet and Ken Coar] PR#501
+
+ *) Properly initialize the flock structures used by the mutex locking
+ around accept() when USE_FCNTL_SERIALIZED_ACCEPT is defined.
+ [Marc Slemko]
+
+ *) The method for determining PATH_INFO has been restored to the pre-1.2b
+ (and NCSA httpd) definition wherein it was the extra path info beyond
+ the CGI script filename. The environment variable FILEPATH_INFO has
+ been removed, and instead we supply the original REQUEST_URI to any
+ script that wants to be Apache-specific and needs the real URI path.
+ This solves a problem with existing scripts that use extra path info
+ in the ScriptAlias directive to pass options to the CGI script.
+ [Roy Fielding]
+
+ *) The _default_ change in 1.2b10 will change the behaviour on configs
+ that use multiple Listen statements for listening on multiple ports.
+ But that change is necessary to make _default_ consistent with other
+ forms of <VirtualHost>. It requires such configs to be modified
+ to use <VirtualHost _default_:*>. The documentation has been
+ updated. [Dean Gaudet] PR#530
+
+ *) If an ErrorDocument CGI script is used to respond to an error
+ generated by another CGI script which has already read the message
+ body of the request, the server would block trying to read the
+ message body again. [Rob Hartill]
+
+ *) signal() replacement conflicted with a define on QNX (and potentially
+ other platforms). Fixed. [Ben Laurie] PR#512
+
+Changes with Apache 1.2b10
+
+ *) Allow HTTPD_ROOT, SERVER_CONFIG_FILE, DEFAULT_PATH, and SHELL_PATH
+ to be configured via -D in Configuration. [Dean Gaudet] PR#449
+
+ *) <VirtualHost _default_:portnum> didn't work properly. [Dean Gaudet]
+
+ *) Added prototype for mktemp() for SUNOS4 [Marc Slemko]
+
+ *) In mod_proxy.c, check return values for proxy_host2addr() when reading
+ config, in case the hostent struct returned is trash.
+ [Chuck Murcko] PR #491
+
+ *) Fixed the fix in 1.2b9 for parsing URL query info into args for CGI
+ scripts. [Dean Gaudet, Roy Fielding, Marc Slemko]
+
+Changes with Apache 1.2b9 [never announced]
+
+ *) Reset the MODULE_MAGIC_NUMBER to account for the unsigned port
+ changes and in anticipation of 1.2 final release. [Roy Fielding]
+
+ *) Fix problem with scripts not receiving a SIGPIPE when client drops
+ the connection (e.g., when user presses Stop). Apache will now stop
+ trying to send a message body immediately after an error from write.
+ [Roy Fielding and Nathan Kurz] PR#335
+
+ *) Rearrange Configuration.tmpl so that mod_rewrite has higher priority
+ than mod_alias, and mod_alias has higher priority than mod_proxy;
+ rearranged other modules to enhance understanding of their purpose
+ and relative order (and maybe even reduce some overhead).
+ [Roy Fielding and Sameer Parekh]
+
+ *) Fix graceful restart. Eliminate many signal-related race
+ conditions in both forms of restart, and in SIGTERM. See
+ htdocs/manual/stopping.html for details on stopping and
+ restarting the parent. [Dean Gaudet]
+
+ *) Fix memory leaks in mod_rewrite, mod_browser, mod_include. Tune
+ memory allocator to avoid a behaviour that required extra blocks to
+ be allocated. [Dean Gaudet]
+
+ *) Allow suexec to access files relative to current directory but not
+ above. (Excluding leading / or any .. directory.) [Ken Coar]
+ PR#269, 319, 395
+
+ *) Fix suexec segfault when group doesn't exist. [Gregory Neil Shapiro]
+ PR#367, 368, 354, 453
+
+ *) Fix the above fix: if suexec is enabled, avoid destroying r->url
+ while obtaining the /~user and save the username in a separate data
+ area so that it won't be overwritten by the call to getgrgid(), and
+ fix some misuse of the pool string allocation functions. Also fixes
+ a general problem with parsing URL query info into args for CGI scripts.
+ [Roy Fielding] PR#339, 367, 354, 453
+
+ *) Fix IRIX warning about bzero undefined. [Marc Slemko]
+
+ *) Fix problem with <Directory proxy:...>. [Martin Kraemer] PR#271
+
+ *) Corrected spelling of "authoritative". AuthDBAuthoratative became
+ AuthDBAuthoritative. [Marc Slemko] PR#420
+
+ *) MaxClients should be at least 1. [Lars Eilebrecht] PR#375
+
+ *) The default handler now logs invalid methods or URIs (i.e. PUT on an
+ object that can't be PUT, or FOOBAR for some method FOOBAR that
+ apache doesn't know about at all). Log 404s that occur in mod_include.
+ [Paul Sutton, John Van Essen]
+
+ *) If a soft timeout (or lingerout) occurs while trying to flush a
+ buffer or write inside buff.c or fread'ing from a CGI's output,
+ then the timeout would be ignored. [Roy Fielding] PR#373
+
+ *) Work around a bug in Netscape Navigator versions 2.x, 3.x and 4.0b2's
+ parsing of headers. If the terminating empty-line CRLF occurs starting
+ at the 256th or 257th byte of output, then Navigator will think a normal
+ image is invalid. We are guessing that this is because their initial
+ read of a new request uses a 256 byte buffer. We check the bytes written
+ so far and, if we are about to tickle the bug, we instead insert a
+ padding header of eminent bogosity. [Roy Fielding and Dean Gaudet] PR#232
+
+ *) Fixed SIGSEGV problem when a DirectoryIndex file is also the source
+ of an external redirection. [Roy Fielding and Paul Sutton]
+
+ *) Configure would create a broken Makefile if the configuration file
+ contained a commented-out Rule. [Roy Fielding]
+
+ *) Promote per_dir_config and subprocess_env from the subrequest to the
+ main request in mod_negotiation. In particular this fixes a bug
+ where <Files> sections wouldn't properly apply to negotiated content.
+ [Dean Gaudet]
+
+ *) Fix a potential deadlock in mod_cgi script_err handling.
+ [Ralf S. Engelschall]
+
+ *) rotatelogs zero-pads the logfile names to improve alphabetic sorting.
+ [Mitchell Blank Jr]
+
+ *) Updated mod_rewrite to 3.0.4: Fixes HTTP redirects from within
+ .htaccess files because the RewriteBase was not replaced correctly.
+ Updated mod_rewrite to 3.0.5: Fixes problem with rewriting inside
+ <Directory> sections missing a trailing /. [Ralf S. Engelschall]
+
+ *) Clean up Linux settings in conf.h by detecting 2.x versus 1.x. For
+ 1.x the settings are those of pre-1.2b8. For 2.x we include
+ HAVE_SHMGET (scoreboard in shared memory rather than file) and
+ HAVE_SYS_RESOURCE_H (enable the RLimit commands).
+ [Dean Gaudet] PR#336, PR#340
+
+ *) Redirect did not preserve ?query_strings when present in the client's
+ request. [Dean Gaudet]
+
+ *) Configure was finding non-modules on EXTRA_LIBS. [Frank Cringle] PR#380
+
+ *) Use /bin/sh5 on ultrix. [P. Alejandro Lopez-Valencia] PR#369
+
+ *) Add UnixWare compile/install instructions. [Chuck Murcko]
+
+ *) Add mod_example (illustration of API techniques). [Ken Coar]
+
+ *) Add macro for memmove to conf.h for SUNOS4. [Marc Slemko]
+
+ *) Improve handling of directories when filenames have spaces in them.
+ [Chuck Murcko]
+
+ *) For hosts with multiple IP addresses, try all additional addresses if
+ necessary to get a connect. Fail only if hostent address list is
+ exhausted. [Chuck Murcko]
+
+ *) More signed/unsigned port fixes. [Dean Gaudet]
+
+ *) HARD_SERVER_LIMIT can be defined in the Configuration file now.
+ [Dean Gaudet]
+
+Changes with Apache 1.2b8
+
+ *) suexec.c doesn't close the log file, allowing CGIs to continue writing
+ to it. [Marc Slemko]
+
+ *) The addition of <Location> and <File> directives made the
+ sub_req_lookup_simple() function bogus, so we now handle
+ the special cases directly. [Dean Gaudet]
+
+ *) We now try to log where the server is dumping core when a fatal
+ signal is received. [Ken Coar]
+
+ *) Improved lingering_close by adding a special timeout, removing the
+ spurious log messages, removing the nonblocking settings (they
+ are not needed with the better timeout), and adding commentary
+ about the NO_LINGCLOSE and USE_SO_LINGER issues. NO_LINGCLOSE is
+ now the default for SunOS4, Unixware, NeXT, and Irix. [Roy Fielding]
+
+ *) Send error messages about setsockopt failures to the server error
+ log instead of stderr. [Roy Fielding]
+
+ *) Fix loopholes in proxy cache expiry vis a vis alarms. [Brian Moore]
+
+ *) Stopgap solution for CGI 3-second delay with server-side includes: if
+ processing a subrequest, allocate memory from r->main->pool instead
+ of r->pool so that we can avoid waiting for free_proc_chain to cleanup
+ in the middle of an SSI request. [Dean Gaudet] PR #122
+
+ *) Fixed status of response when POST is received for a nonexistant URL
+ (was sending 405, now 404) and when any method is sent with a
+ full-URI that doesn't match the server and the server is not acting
+ as a proxy (was sending 501, now 403). [Roy Fielding]
+
+ *) Host port changed to unsigned short. [Ken Coar] PR #276
+
+ *) Fix typo in command definition of AuthAuthoritative. [Ken Coar] PR #246
+
+ *) Defined HAVE_SHMGET for shared memory on Linux. [Dean Gaudet]
+
+ *) Report extra info from errno with many errors that cause httpd to exit.
+ spawn_child, popenf, and pclosef now have valid errno returns in the
+ event of an error. Correct problems where errno was stomped on
+ before being reported. [Dean Gaudet]
+
+ *) In the proxy, if the cache filesystem was full, garbage_coll() was
+ never called, and thus the filesystem would remain full indefinitely.
+ We now also remove incomplete cache files left if the origin server
+ didn't send a Content-Length header and either the client has aborted
+ transfer or bwrite() to client has failed. [Petr Lampa]
+
+ *) Fixed the handling of module and script-added header fields.
+ Improved the interface for sending header fields and reduced
+ the duplication of code between sending okay responses and errors.
+ We now always send both headers_out and err_headers_out, and
+ ensure that the server-reserved fields are not being overridden,
+ while not overriding those that are not reserved. [Roy Fielding]
+
+ *) Moved transparent content negotiation fields to err_headers_out
+ to reflect above changes. [Petr Lampa]
+
+ *) Fixed the determination of whether or not we should make the
+ connection persistent for all of the cases where some other part
+ of the server has already indicated that we should not. Also
+ improved the ordering of the test so that chunked encoding will
+ be set whenever it is desired instead of only when KeepAlive
+ is enabled. Added persistent connection capability for most error
+ responses (those that do not indicate a bad input stream) when
+ accessed by an HTTP/1.1 client. [Roy Fielding]
+
+ *) Added missing timeouts for sending header fields, error responses,
+ and the last chunk of chunked encoding, each of which could have
+ resulted in a process being stuck in write forever. Using soft_timeout
+ requires that the sender check for an aborted connection rather than
+ continuing after an EINTR. Timeouts that used to be initiated before
+ send_http_header (and never killed) are now initiated only within or
+ around the routines that actually do the sending, and not allowed to
+ propagate above the caller. [Roy Fielding]
+
+ *) mod_auth_anon required an @ or a . in the email address, not both.
+ [Dirk vanGulik]
+
+ *) per_dir_defaults weren't set correctly until directory_walk for
+ name-based vhosts. This fixes an obscure bug with the wrong config
+ info being used for vhosts that share the same ip as the server.
+ [Dean Gaudet]
+
+ *) Improved generation of modules/Makefile to be more generic for
+ new module directories. [Ken Coar, Chuck Murcko, Roy Fielding]
+
+ *) Generate makefile dependency for Configuration based on the actual
+ name given when running the Configure process. [Dean Gaudet]
+
+ *) Fixed problem with vhost error log not being set prior to
+ initializing virtual hosts. [Dean Gaudet]
+
+ *) Fixed infinite loop when a trailing slash is included after a type map
+ file URL (extra path info). [Petr Lampa]
+
+ *) Fixed server status updating of per-connection counters. [Roy Fielding]
+
+ *) Add documentation for DNS issues (reliability and security), and try
+ to explain the virtual host matching process. [Dean Gaudet]
+
+ *) Try to continue gracefully by disabling the vhost if a DNS lookup
+ fails while parsing the configuration file. [Dean Gaudet]
+
+ *) Improved calls to setsockopt. [Roy Fielding]
+
+ *) Negotiation changes: Don't output empty content-type in variant list;
+ Output charset in variant list; Return sooner from handle_multi() if
+ no variants found; Add handling of '*' wildcard in Accept-Charset.
+ [Petr Lampa and Paul Sutton]
+
+ *) Fixed overlaying of request/sub-request notes and headers in
+ mod_negotiation. [Dean Gaudet]
+
+ *) If two variants' charset quality are equal and one is the default
+ charset (iso-8859-1), then prefer the variant that was specifically
+ listed in Accept-Charset instead of the default. [Petr Lampa]
+
+ *) Memory allocation problem in push_array() -- it would corrupt memory
+ when nalloc==0. [Kai Risku <krisku@tf.hut.fi> and Roy Fielding]
+
+ *) invoke_handler() doesn't handle mime arguments in content-type
+ [Petr Lampa] PR#160
+
+ *) Reduced IdentityCheck timeout to 30 seconds, as per RFC 1413 minimum.
+ [Ken Coar]
+
+ *) Fixed problem with ErrorDocument not working for virtual hosts
+ due to one of the performance changes in 1.2b7. [Dean Gaudet]
+
+ *) Log an error message if we get a request header that is too long,
+ since it may indicate a buffer overflow attack. [Marc Slemko]
+
+ *) Made is_url() allow "[-.+a-zA-Z0-9]+:" as a valid scheme and
+ not reject URLs without a double-slash, as per RFC2068 section 3.2.
+ [Ken Coar] PR #146, #187
+
+ *) Added table entry placeholder for new header_parser callback
+ in all of the distributed modules. [Ken Coar] PR #191
+
+ *) Allow for cgi files without the .EXE extension on them under OS/2.
+ [Garey Smiley] PR #59
+
+ *) Fixed error message when resource is not found and URL contains
+ path info. [Petr Lampa and Dean Gaudet] PR #40
+
+ *) Fixed user and server confusion over what should be a virtual host
+ and what is the main server, resulting in access to something
+ other than the name defined in the virtualhost directive (but
+ with the same IP address) failing. [Dean Gaudet]
+
+ *) Updated mod_rewrite to version 3.0.2, which: fixes compile error on
+ AIX; improves the redirection stuff to enable the users to generally
+ redirect to http, https, gopher and ftp; added TIME variable for
+ RewriteCond which expands to YYYYMMDDHHMMSS strings and added the
+ special patterns >STRING, <STRING and =STRING to RewriteCond, which
+ can be used in conjunction with %{TIME} or other variables to create
+ time-dependent rewriting rules. [Ralf S. Engelschall]
+
+ *) bpushfd() no longer notes cleanups for the file descriptors it is handed.
+ Module authors may need to adjust their code for proper cleanup to take
+ place (that is, call note_cleanups_for_fd()). This change fixes problems
+ with file descriptors being erroneously closed when the proxy module was
+ in use. [Ben Laurie]
+
+ *) Fix bug in suexec reintroduced by changes in 1.2b7 which allows
+ initgroups() to hose the group information needed for later
+ comparisons. [Randy Terbush]
+
+ *) Remove unnecessary call to va_end() in create_argv() which
+ caused a SEGV on some systems.
+
+ *) Use proper MAXHOSTNAMELEN symbol for limiting length of server name.
+ [Dean Gaudet]
+
+ *) Clear memory allocated for listeners. [Randy Terbush]
+
+ *) Improved handling of IP address as a virtualhost address and
+ introduced "_default_" as a synonym for the default vhost config.
+ [Dean Gaudet] PR #212
+
+Changes with Apache 1.2b7
+
+ *) Port to UXP/DS(V20) [Toshiaki Nomura <nom@yk.fujitsu.co.jp>]
+
+ *) unset Content-Length if chunked (RFC-2068) [Petr Lampa]
+
+ *) mod_negotiation fixes [Petr Lampa] PR#157, PR#158, PR#159
+ - replace protocol response numbers with symbols
+ - save variant-list into main request notes
+ - free allocated memory from subrequests
+ - merge notes, headers_out and err_headers_out
+
+ *) changed status check mask in proxy_http.c from "HTTP/#.# ### *" to
+ "HTTP/#.# ###*" to be more lenient about what we accept.
+ [Chuck Murcko]
+
+ *) more proxy FTP bug fixes:
+ - Changed send_dir() to remove user/passwd from displayed URL.
+ - Changed login error messages to be more descriptive.
+ - remove setting of SO_DEBUG socket option
+ - Make ftp_getrc() more lenient about multiline responses,
+ specifically, 230 responses which don't have continuation 230-
+ on each line). These seem to be all NT FTP servers, and while
+ perhaps questionable, they appear to be legal by RFC 959.
+ - Add missing kill_timeout() after transfer to user completes.
+ [Chuck Murcko]
+
+ *) Fixed problem where a busy server could hang when restarting
+ after being sent a SIGHUP due to child processes not exiting.
+ [Marc Slemko]
+
+ *) Modify mod_include escaping so a '\' only signifies an escaped
+ character if the next character is one that needs
+ escaping. [Ben Laurie]
+
+ *) Eliminated possible infinite loop in mod_imap when relative URLs are
+ used with a 'base' directive that does not have a '/' in it.
+ [Marc Slemko, reported by Onno Witvliet <onno@tc.hsa.nl>]
+
+ *) Reduced the default timeout from 1200 seconds to 300, and the
+ one in the sample configfile from 400 to 300. [Marc Slemko]
+
+ *) Stop vbprintf from crashing if given a NULL string pointer;
+ print (null) instead. [Ken Coar]
+
+ *) Don't disable Nagle algorithm if system doesn't have TCP_NODELAY.
+ [Marc Slemko and Roy Fielding]
+
+ *) Fixed problem with mod_cgi-generated internal redirects trying to
+ read the request message-body twice. [Archie Cobbs and Roy Fielding]
+
+ *) Reduced timeout on lingering close, removed possibility of a blocked
+ read causing the child to hang, and stopped logging of errors if
+ the socket is not connected (reset by client). [Roy Fielding]
+
+ *) Rearranged main child loop to remove duplication of code in
+ select/accept and keep-alive requests, fixed several bugs regarding
+ checking scoreboard_image for exit indication and failure to
+ account for all success conditions and trap all error conditions,
+ prevented multiple flushes before closing the socket; close the entire
+ socket buffer instead of just one descriptor, prevent logging of
+ EPROTO and ECONNABORTED on platforms where supported, and generally
+ improved readability. [Roy Fielding]
+
+ *) Extensive performance improvements. Cleaned up inefficient use of
+ auto initializers, multiple is_matchexp calls on a static string,
+ and excessive merging of response_code_strings. [Dean Gaudet]
+
+ *) Added double-buffering to mod_include to improve performance on
+ server-side includes. [Marc Slemko]
+
+ *) Several fixes for suexec wrapper. [Randy Terbush]
+ - Make wrapper work for files on NFS filesystem.
+ - Fix portability problem of MAXPATHLEN.
+ - Fix array overrun problem in clean_env().
+ - Fix allocation of PATH environment variable
+
+ *) Removed extraneous blank line is description of mod_status chars.
+ [Kurt Kohler]
+
+ *) Logging of errors from the call_exec routine simply went nowhere,
+ since the logfile fd has been closed, so now we send them to stderr.
+ [Harald T. Alvestrand]
+
+ *) Fixed core dump when DocumentRoot is a CGI.
+ [Ben Laurie, reported by geddis@tesserae.com]
+
+ *) Fixed potential file descriptor leak in mod_asis; updated it and
+ http_core to use pfopen/pfclose instead of fopen/fclose.
+ [Randy Terbush and Roy Fielding]
+
+ *) Fixed handling of unsigned ints in ap_snprintf() on some chips such
+ as the DEC Alpha which is 64-bit but uses 32-bit ints.
+ [Dean Gaudet and Ken Coar]
+
+ *) Return a 302 response code to the client when sending a redirect
+ due to a missing trailing '/' on a directory instead of a 301; now
+ it is cacheable. [Markus Gyger]
+
+ *) Fix condition where, if a bad directive occurs in .htaccess, and
+ sub_request() goes first to this directory, then log_reason() will
+ SIGSEGV because it doesn't have initialized r->per_dir_config.
+ [PR#162 from Petr Lampa, fix by Marc Slemko and Dean Gaudet]
+
+ *) Fix handling of lang_index in is_variant_better(). This was
+ causing problems which resulted in the server sending the
+ wrong language document in some cases. [Petr Lampa]
+
+ *) Remove free() from clean_env() in suexec wrapper. This was nuking
+ the clean environment on some systems.
+
+ *) Tweak byteserving code (e.g. serving PDF files) to work around
+ bugs in Netscape Navigator and Microsoft Internet Explorer.
+ Emit Content-Length header when sending multipart/byteranges.
+ [Alexei Kosut]
+
+ *) Port to HI-UX/WE2. [Nick Maclaren]
+
+ *) Port to HP MPE operating system for HP 3000 machines
+ [Mark Bixby <markb@cccd.edu>]
+
+ *) Fixed bug which caused a segmentation fault if only one argument
+ given to RLimit* directives. [Ed Korthof]
+
+ *) Continue persistent connection after 204 or 304 response. [Dean Gaudet]
+
+ *) Improved buffered output to the client by delaying the flush decision
+ until the BUFF code is actually about to read the next request.
+ This fixes a problem introduced in 1.2b5 with clients that send
+ an extra CRLF after a POST request. Also improved chunked output
+ performance by combining writes using writev() and removing as
+ many bflush() calls as possible. NOTE: Platforms without writev()
+ must add -DNO_WRITEV to the compiler CFLAGS, either in Configuration
+ or Configure, unless we have already done so. [Dean Gaudet]
+
+ *) Fixed mod_rewrite bug which truncated the rewritten URL [Marc Slemko]
+
+ *) Fixed mod_info output corruption bug introduced by buffer overflow
+ fixes. [Dean Gaudet]
+
+ *) Fixed http_protocol to correctly output all HTTP/1.1 headers, including
+ for the special case of a 304 response. [Paul Sutton]
+
+ *) Improved handling of TRACE method by bypassing normal method handling
+ and header parsing routines; fixed Allow response to always allow TRACE.
+ [Dean Gaudet]
+
+ *) Fixed compiler warnings in the regex library. [Dean Gaudet]
+
+ *) Cleaned-up some of the generated HTML. [Ken Coar]
+
+Changes with Apache 1.2b6
+
+ *) Allow whitespace in imagemap mapfile coordinates. [Marc Slemko]
+
+ *) Fix typo introduced in fix for potential infinite loop around
+ accept() in child_main(). This change caused the rev to 1.2b6.
+ 1.2b5 was never a public beta.
+
+Changes with Apache 1.2b5
+
+ *) Change KeepAlive semantics (On|Off instead of a number), add
+ MaxKeepAliveRequests directive. [Alexei Kosut]
+
+ *) Various NeXT compilation patches, as well as a change in
+ regex/regcomp.c since that file also used a NEXT define.
+ [Andreas Koenig]
+
+ *) Allow * to terminate the end of a directory match in mod_dir.
+ Allows /~* to match for both /~joe and /~joe/. [David Bronder]
+
+ *) Don't call can_exec() if suexec_enabled. Calling this requires
+ scripts executed by the suexec wrapper to be world executable, which
+ defeats one of the advantages of running the wrapper. [Randy Terbush]
+
+ *) Portability Fix: IRIX complained with 'make clean' about *pure* (removed)
+ [Jim Jagielski]
+
+ *) Migration from sprintf() to snprintf() to avoid buffer
+ overflows. [Marc Slemko]
+
+ *) Provide portable snprintf() implementation (ap_snprintf)
+ as well as *cvt family. [Jim Jagielski]
+
+ *) Portability Fix: NeXT lacks unistd.h so we wrap it's inclusion
+ [Jim Jagielski]
+
+ *) Remove mod_fastcgi.c from the distribution. This module appears
+ to be maintained more through the Open Market channels and should
+ continue to be easily available at http://www.fastcgi.com/
+
+ *) Fixed bug in modules/Makefile that wouldn't allow building in more
+ than one subdirectory (or cleaning, either). [Jeremy Laidman]
+
+ *) mod_info assumed that the config files were relative to ServerRoot.
+ [Ken the Rodent]
+
+ *) CGI scripts called as an error document resulting from failed
+ CGI execution would hang waiting for POST'ed data. [Rob Hartill]
+
+ *) Log reason when mod_dir returns access HTTP_FORBIDDEN
+ [Ken the Rodent]
+
+ *) Properly check errno to prevent display of a directory index
+ when server receives a long enough URL to confuse stat().
+ [Marc Slemko]
+
+ *) Several security enhancements to suexec wrapper. It is _highly_
+ recommended that previously installed versions of the wrapper
+ be replaced with this version. [Randy Terbush, Jason Dour]
+
+ - ~user execution now properly restricted to ~user's home
+ directory and below.
+ - execution restricted to UID/GID > 100
+ - restrict passed environment to known variables
+ - call setgid() before initgroups() (portability fix)
+ - remove use of setenv() (portability fix)
+
+ *) Add HTTP/1.0 response forcing. [Ben Laurie]
+
+ *) Add access control via environment variables. [Ben Laurie]
+
+ *) Add rflush() function. [Alexei Kosut]
+
+ *) remove duplicate pcalloc() call in new_connection().
+
+ *) Fix incorrect comparison which could allow number of children =
+ MaxClients + 1 if less than HARD_SERVER_LIMIT. Also fix potential
+ problem if StartServers > HARD_SERVER_LIMIT. [Ed Korthof]
+
+ *) Updated support for OSes (MachTen, ULTRIX, Paragon, ISC, OpenBSD
+ AIX PS/2, CONVEXOS. [Jim Jagielski]
+
+ *) Replace instances of inet_ntoa() with inet_addr() for ProxyBlock.
+ It's more portable. [Martin Kraemer]
+
+ *) Replace references to make in Makefile.tmpl with $(MAKE).
+ [Chuck Murcko]
+
+ *) Add ProxyBlock directive w/IP address caching. Add IP address
+ caching to NoCache directive as well. ProxyBlock works with all
+ handlers; NoCache now also works with FTP for anonymous logins.
+ Still more code cleanup. [Chuck Murcko]
+
+ *) Add "header parse" API hook [Ben Laurie]
+
+ *) Fix byte ordering problems for REMOTE_PORT [Chuck Murcko]
+
+ *) suEXEC wrapper was freeing memory that had not been malloc'ed.
+
+ *) Correctly allow access and auth directives in <Files> sections in
+ server config files. [Alexei Kosut]
+
+ *) Fix bug with ServerPath that could cause certain files to be not
+ found by the server. [Alexei Kosut]
+
+ *) Fix handling of ErrorDocument so that it doesn't remove a trailing
+ double-quote from text and so that it properly checks for unsupported
+ status codes using the new index_of_response interface. [Roy Fielding]
+
+ *) Multiple fixes to the lingering_close code in order to avoid being
+ interrupted by a stray timeout, to avoid lingering on a connection
+ that has already been aborted or never really existed, to ensure that
+ we stop lingering as soon as any error condition is received, and to
+ prevent being stuck indefinitely if the read blocks. Also improves
+ reporting of error conditions. [Marc Slemko and Roy Fielding]
+
+ *) Fixed initialization of parameter structure for sigaction.
+ [mgyger@itr.ch, Adrian Filipi-Martin]
+
+ *) Fixed reinitializing the parameters before each call to accept and
+ select, and removed potential for infinite loop in accept.
+ [Roy Fielding, after useful PR from adrian@virginia.edu]
+
+ *) Fixed condition where, if a child fails to fork, the scoreboard would
+ continue to say SERVER_STARTING forever. Eventually, the main process
+ would refuse to start new children because count_idle_servers() will
+ count those SERVER_STARTING entries and will always report that there
+ are enough idle servers. [Phillip Vandry]
+
+ *) Fixed bug in bcwrite regarding failure to account for partial writes.
+ Avoided calling bflush() when the client is pipelining requests.
+ Removed unnecessary flushes from http_protocol. [Dean Gaudet]
+
+ *) Added description of "." mode in server-status [Jim Jagielski]
+
+
+Changes with Apache 1.2b4:
+
+ *) Fix possible race condition in accept_mutex_init() that
+ could leave a small security hole open allowing files to be
+ overwritten in cases where the server UID has write permissions.
+ [Marc Slemko]
+
+ *) Fix awk compatibilty problem in Configure. [Jim Jagielski]
+
+ *) Fix portablity problem in util_script where ARG_MAX may not be
+ defined for some systems.
+
+ *) Add changes to allow compilation on Machten 4.0.3 for PowerPC.
+ [Randal Schwartz]
+
+ *) OS/2 changes to support an MMAP style scoreboard file and UNIX
+ style magic #! token for better script portability. [Garey Smiley]
+
+ *) Fix bug in suexec wrapper introduced in b3 that would cause failed
+ execution for ~userdir CGI. [Jason Dour]
+
+ *) Fix initgroups() business in suexec wrapper. [Jason Dour]
+
+ *) Fix month off by one in suexec wrapper logging.
+
+Changes with Apache 1.2b3:
+
+ *) Fix error in mod_cgi which could cause resources not to be properly
+ freed, or worse. [Dean Gaudet]
+
+ *) Fix find_string() NULL pointer dereference. [Howard Fear]
+
+ *) Add set_flag_slot() at the request of Dirk and others.
+ [Dirk vanGulik]
+
+ *) Sync mod_rewrite with patch level 10. [Ralf Engelschall]
+
+ *) Add changes to improve the error message given for invalid
+ ServerName parameters. [Dirk vanGulik]
+
+ *) Add "Authoritative" directive for Auth modules that don't
+ currently have it. This gives admin control to assign authoritative
+ control to an authentication scheme and allow "fall through" for
+ those authentication modules that aren't "Authoritative" thereby
+ allowing multiple authentication mechanisms to be chained.
+ [Dirk vanGulik]
+
+ *) Remove requirement for ResourceConfig/AccessConfig if not using
+ the three config file layout. [Randy Terbush]
+
+ *) Add PASV mode to mod_proxy FTP handler. [Chuck Murcko]
+
+ *) Changes to suexec wrapper to fix the following problems:
+ 1. symlinked homedirs will kill ~userdirs.
+ 2. initgroups() on Linux 2.0.x clobbers gr->grid.
+ 3. CGI command lines paramters problems
+ 4. pw-pwdir for "docroot check" still the httpd user's pw record.
+ [Randy Terbush, Jason Dour]
+
+ *) Change create_argv() to accept variable arguments. This fixes
+ a problem where arguments were not getting passed to the CGI via
+ argv[] when the suexec wrapper was active. [Randy Terbush, Jake Buchholz]
+
+ *) Collapse multiple slashes in path URLs to properly apply
+ handlers defined by <Location>. [Alexei Kosut]
+
+ *) Define a sane set of DEFAULT_USER and DEFAULT_GROUP values for AIX.
+
+ *) Improve the accuracy of request duration timings by setting
+ r->request_time in read_request_line() instead of read_request().
+ [Dean Gaudet]
+
+ *) Reset timeout while reading via get_client_block() in mod_cgi.c
+ Fixes problem with timed out transfers of large files. [Rasmus Lerdorf]
+
+ *) Add the ability to pass different Makefile.tmpl files to Configure
+ using the -make flag. [Rob Hartill]
+
+ *) Fix coredump triggered when sending a SIGHUP to the server caused
+ by an assertion failure, in turn caused by an uninitialised field in a
+ listen_rec.
+ [Ben Laurie]
+
+ *) Add FILEPATH_INFO variable to CGI environment, which is equal to
+ PATH_INFO from previous versions of Apache (in certain situations,
+ Apache 1.2's PATH_INFO will be different than 1.1's). [Alexei Kosut]
+ [later removed in 1.2b11]
+
+ *) Add rwrite() function to API to allow for sending strings of
+ arbitrary length. [Doug MacEachern]
+
+ *) Remove rlim_t typedef for NetBSD. Do older versions need this?
+
+ *) Defined rlim_t and WANTHSREGEX=yes and fixed waitpid() substitute for
+ NeXT. [Jim Jagielski]
+
+ *) Removed recent modification to promote the status code on internal
+ redirects, since the correct fix was to change the default log format
+ in mod_log_config so that it outputs the original status. [Rob Hartill]
+
+Changes with Apache 1.2b2:
+
+ *) Update set_signals() to use sigaction() for setting handlers.
+ This appears to fix a re-entrant problem in the seg_fault()
+ bus_error() handlers. [Randy Terbush]
+
+ *) Changes to allow mod_status compile for OS/2 [Garey Smiley]
+
+ *) changes for DEC AXP running OSF/1 v3.0. [Marc Evans]
+
+ *) proxy_http.c bugfixes: [Chuck Murcko]
+ 1) fixes possible NULL pointer reference w/NoCache
+ 2) fixes NoCache behavior when using ProxyRemote (ProxyRemote
+ host would cache nothing if it was in the local domain,
+ and the local domain was in the NoCache list)
+ 3) Adds Host: header when not available
+ 4) Some code cleanup and clarification
+
+ *) mod_include.c bugfixes:
+ 1) Fixed an ommission that caused include variables to not
+ be parsed in config errmsg directives [Howard Fear]
+ 2) Remove HAVE_POSIX_REGEX cruft [Alexei Kosut]
+ 3) Patch to fix compiler warnings [perrot@lal.in2p3.fr]
+ 4) Allow backslash-escaping to all quoted text
+ [Ben Yoshino <ben@wiliki.eng.hawaii.edu>]
+ 5) Pass variable to command line if not set in XSSI's env
+ [Howard Fear]
+
+ *) Fix infinite loop when processing Content-language lines in
+ type-map files. [Alexei Kosut]
+
+ *) Closed file-globbing hole in test-cgi script. [Brian Behlendorf]
+
+ *) Fixed problem in set_[user|group] that prevented CGI execution
+ for non-virtualhosts when suEXEC was enabled. [Randy Terbush]
+
+ *) Added PORTING information file. [Jim Jagielski]
+
+ *) Added definitions for S_IWGRP and S_IWOTH to conf.h [Ben Laurie]
+
+ *) Changed default group to "nogroup" instead of "nobody" [Randy Terbush]
+
+ *) Fixed define typo of FCNTL_SERIALIZED_ACCEPT where
+ USE_FCNTL_SERIALIZED_ACCEPT was intended.
+
+ *) Fixed additional uses of 0xffffffff where INADDR_NONE was intended,
+ which caused problems of systems where socket s_addr is >32bits.
+
+ *) Added comment to explain (r->chunked = 1) side-effect in
+ http_protocol.c [Roy Fielding]
+
+ *) Replaced use of index() in mod_expires.c with more appropriate
+ and portable isdigit() test. [Ben Laurie]
+
+ *) Updated Configure for ...
+ OS/2 (DEF_WANTHSREGEX=yes, other code changes)
+ *-dg-dgux* (bad pattern match)
+ QNX (DEF_WANTHSREGEX=yes)
+ *-sunos4* (DEF_WANTHSREGEX=yes, -DUSEBCOPY)
+ *-ultrix (new)
+ *-unixware211 (new)
+ and added some user diagnostic info. [Ben Laurie]
+
+ *) In helpers/CutRule, replaced "cut" invocation with "awk" invocation
+ for better portability. [Jim Jagielski]
+
+ *) Updated helpers/GuessOS for ...
+ SCO 5 (recognize minor releases)
+ SCO Unixware (braindamaged uname, whatever-whatever-unixware2)
+ SCO UnixWare 2.1.1 (requires a separate set of #defines in conf.h)
+ IRIX64 (-sgi-irix64)
+ ULTRIX (-unknown-ultrix)
+ SINIX (-whatever-sysv4)
+ NCR Unix (-ncr-sysv4)
+ and fixed something in helpers/PrintPath [Ben Laurie]
+
+Changes with Apache 1.2b1:
+
+ *) Not listed. See <http://www.apache.org/docs/new_features_1_2.html>
+
+Changes with Apache 1.1.1:
+
+ *) Fixed bug where Cookie module would make two entries in the
+ logfile for each access [Mark Cox]
+
+ *) Fixed bug where Redirect in .htaccess files would cause memory
+ leak. [Nathan Neulinger]
+
+ *) MultiViews now works correctly with AddHandler [Alexei Kosut]
+
+ *) Problems with mod_auth_msql fixed [Dirk vanGulik]
+
+ *) Fix misspelling of "Anonymous_Authorative" directive in mod_auth_anon.
+
+Changes with Apache 1.1.0:
+
+ *) 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 <title> 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-cased to not merge multiple
+ instances, since certain popular browsers can not handle multiple
+ Set-Cookie instructions in a single header. [Paul Sutton]
+
+ *) 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:
+
+ *) Not listed. See <http://www.apache.org/docs/new_features_1_1.html>
+
+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 [Sameer 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 license. [David Robinson]
+
+Changes with Apache 1.0.0
+
+ *) Not listed. See <http://www.apache.org/docs/new_features_1_0.html>
+
+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 negotiation 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]
+
+ *) Clarified 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-compatibility, as the old
+ NCSA code did not check Options ExecCGI in ScriptAlias 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-compatibility. (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 Jagielski]
+
+ *) 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 Jagielski]
+
+ *) 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 Jagielski]
+
+ *) 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-compatibility). 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 independent 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] 10 Apr 1995
+
+ *) 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 ;-).
+
+Changes with 0.4 02 Apr 1995
+
+ *) Patches by Brian Behlendorf, Andrew Wilson, Robert Thau,
+ and Rob Hartill.
+
+Changes with 0.3 24 Mar 1995
+
+ *) Patches by Robert Thau, David Robinson, Rob Hartill, and
+ Carlos Varela
+
+Changes with 0.2 18 Mar 1995
+
+ *) Based on NCSA httpd 1.3 by Rob McCool and patches by CERT,
+ Roy Fielding, Robert Thau, Nicolas Pioch, David Robinson,
+ Brian Behlendorf, Rob Hartill, and Cliff Skolnick
diff --git a/usr.sbin/httpd/src/Configuration b/usr.sbin/httpd/src/Configuration
new file mode 100644
index 00000000000..27baba12831
--- /dev/null
+++ b/usr.sbin/httpd/src/Configuration
@@ -0,0 +1,292 @@
+# Config file for the Apache httpd.
+
+# Configuration.tmpl is the template for Configuration. Configuration should
+# be edited to select the modules to be included as well as various flags
+# for Makefile.
+
+# The template should only be changed when a new system or module is added,
+# or an existing one modified. This will also most likely require some minor
+# changes to Configure to recognize those changes.
+
+# There are 5 types of lines here:
+
+# '#' comments, distinguished by having a '#' as the first non-blank character
+#
+# Makefile options, such as CC=gcc, etc...
+#
+# Rules, distinquished by having "Rule" at the front. These are used to
+# control Configure's behavior as far as how to create Makefile.
+#
+# Module selection lines, distinguished by having 'Module' at the front.
+# These list the configured modules, in reverse priority order (lowest
+# priority first). They're down at the bottom.
+#
+# Optional module selection lines, distinguished by having `%Module'
+# at the front. These specify a module that is to be compiled in (but
+# not enabled). The AddModule directive can be used to enable such a
+# module. By default no such modules are defined.
+
+
+################################################################
+# Makefile configuration
+#
+# These are added to the general flags determined by Configure.
+# Edit these to work around Configure if needed. The EXTRA_* family
+# will be added to the regular Makefile flags. For example, if you
+# want to compile with -Wall, then add that to EXTRA_CFLAGS. These
+# will be added to whatever flags Configure determines as appropriate
+# and needed for your platform.
+#
+# You can also set the compiler and Optimization used here as well.
+# Settings here have priority; If not set, Configure will attempt to guess
+# the C compiler, and set OPTIM to '-O2'
+#
+# REDHAT LINUX 5.0 USERS PLEASE NOTE! You have to add -lcrypt to
+# EXTRA_LIBS. This is fixed in 1.3 but will not be fixed in 1.2.
+#
+EXTRA_CFLAGS=
+EXTRA_LFLAGS=
+EXTRA_LIBS=
+EXTRA_INCLUDES=
+
+#CC=
+#OPTIM=-O2
+#RANLIB=
+
+################################################################
+# Rules configuration
+#
+# These are used to let Configure know that we want certain
+# functions. The format is: Rule RULE=value
+#
+# At present, only the following RULES are known: WANTHSREGEX, SOCKS4,
+# STATUS, and IRIXNIS.
+#
+# For all Rules, if set to "yes", then Configure knows we want that
+# capability and does what is required to add it in. If set to "default"
+# then Configure makes a "best guess"; if set to anything else, or not
+# present, then nothing is done.
+#
+# SOCKS4:
+# If SOCKS4 is set to 'yes', be sure that you add the sock library
+# location to EXTRA_LIBS, otherwise Configure will assume
+# "-L/usr/local/lib -lsocks"
+#
+# STATUS:
+# If Configure determines that you are using the status_module,
+# it will automatically enable full status information if set
+# to 'yes'. If the status module is not included, having STATUS
+# set to 'yes' has no impact.
+#
+# IRIXNIS:
+# Only takes effect if Configure determines that you are running
+# SGI IRIX. If you are, and you are using NIS, you should set this
+# to 'yes'
+#
+
+Rule STATUS=yes
+Rule SOCKS4=no
+Rule IRIXNIS=no
+
+# The following rules should be set automatically by Configure. However, if
+# they are not set by Configure (because we don't know the correct value for
+# your platform), or are set incorrectly, you may override them here.
+# If you have to do this, please let us know what you set and what your
+# platform is, by filling out a problem report form at the Apache web site:
+# <http://www.apache.org/bugdb.cgi>. If your browser is forms-incapable,
+# you can get the information to us by sending mail to apache-bugs@apache.org.
+#
+# WANTHSREGEX:
+# Apache requires a POSIX regex implementation. Henry Spencer's
+# excellent regex package is included with Apache and can be used
+# if desired. If your OS has a decent regex, you can elect to
+# not use this one by setting WANTHSREGEX to 'no' or commenting
+# out the Rule. The "default" action is "no" unless overruled
+# by OS specifics
+
+Rule WANTHSREGEX=default
+
+################################################################
+# 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.
+
+##
+## Config manipulation modules
+##
+## mod_env sets up additional or restricted environment variables to be
+## passed to CGI/SSI scripts. It is listed first (lowest priority) since
+## it does not do per-request stuff.
+
+Module env_module mod_env.o
+
+## mod_dld defines commands that allow other modules to be loaded
+## dynamically (at runtime). This module is for experimental use only.
+
+# Module dld_module mod_dld.o
+
+##
+## Request logging modules
+##
+
+Module config_log_module mod_log_config.o
+
+## Optional modules for NCSA user-agent/referer logging compatibility
+## We recommend, however, that you just use the configurable access_log.
+
+# Module agent_log_module mod_log_agent.o
+# Module referer_log_module mod_log_referer.o
+
+##
+## Type checking modules
+##
+## mod_mime maps filename extensions to content types, encodings, and
+## magic type handlers (the latter is obsoleted by mod_actions).
+## mod_negotiation allows content selection based on the Accept* headers.
+
+Module mime_module mod_mime.o
+Module negotiation_module mod_negotiation.o
+
+##
+## Content delivery modules
+##
+## The status module allows the server to display current details about
+## how well it is performing and what it is doing. Consider also enabling
+## STATUS=yes (see the Rules section near the start of this 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
+
+## mod_include translates server-side include (SSI) statements in text files.
+## mod_dir handles requests on directories and directory indexes.
+## mod_cgi handles CGI scripts.
+
+Module includes_module mod_include.o
+Module dir_module mod_dir.o
+Module cgi_module mod_cgi.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
+
+##
+## URL translation modules.
+##
+## The UserDir module for selecting resource directories by user name
+## and a common prefix, e.g., /~<user> , /usr/web/<user> , etc.
+
+Module userdir_module mod_userdir.o
+
+## The proxy module enables the server to act as a proxy for outside
+## http and ftp services. It's not as complete as it could be yet.
+## NOTE: You do not want this module UNLESS you are running a proxy;
+## it is not needed for normal (origin server) operation.
+
+#Module proxy_module modules/proxy/libproxy.a
+
+## The Alias module provides simple URL translation and redirection.
+
+Module alias_module mod_alias.o
+
+## mod_rewrite allows for powerful URI-to-URI and URI-to-filename mapping,
+## using regular expressions.
+
+# Module rewrite_module mod_rewrite.o
+
+##
+## Access control and authentication modules.
+##
+Module access_module mod_access.o
+Module auth_module mod_auth.o
+
+## 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 and possibly adjust EXTRA_LIBS. (This may be
+## done by Configure at a later date)
+
+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 EXTRA_LIBS line, like
+##
+## -lmsql -L/usr/local/lib -L/usr/local/Minerva/lib
+##
+## This depends on your installation of mSQL. (This may be done by Configure
+## at a later date)
+
+# 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
+
+## Optional response header manipulation modules.
+##
+## cern_meta mimics the behavior of the CERN web server with regards to
+## metainformation files.
+
+# Module cern_meta_module mod_cern_meta.o
+
+## The expires module can apply Expires: headers to resources,
+## as a function of access time or modification time.
+
+# Module expires_module mod_expires.o
+
+## The headers module can set arbitrary HTTP response headers,
+## as configured in server, vhost, access.conf or .htaccess configs
+
+# Module headers_module mod_headers.o
+
+## Miscellaneous modules
+##
+## mod_usertrack.c is the new name for mod_cookies.c. This module
+## uses Netscape cookies to automatically construct and log
+## click-trails from Netscape cookies, or compatible clients who
+## aren't coming in via proxy.
+##
+## You do not need this, or any other module to allow your site
+## to use Cookies. This module is for user tracking only
+
+# Module usertrack_module mod_usertrack.o
+
+## The example module, which demonstrates the use of the API. See
+## the file modules/example/README for details. This module should
+## only be used for testing -- DO NOT ENABLE IT on a production server.
+
+# Module example_module modules/example/mod_example.o
+
+## mod_browser lets you set environment variables based on the User-Agent
+## string in the request; this is useful for conditional HTML, for example.
+## Since it is also used to detect buggy browsers for workarounds, it
+## should be the last (highest priority) module.
+
+Module browser_module mod_browser.o
diff --git a/usr.sbin/httpd/src/Configuration.tmpl b/usr.sbin/httpd/src/Configuration.tmpl
new file mode 100644
index 00000000000..178308124ed
--- /dev/null
+++ b/usr.sbin/httpd/src/Configuration.tmpl
@@ -0,0 +1,292 @@
+# Config file for the Apache httpd.
+
+# Configuration.tmpl is the template for Configuration. Configuration should
+# be edited to select the modules to be included as well as various flags
+# for Makefile.
+
+# The template should only be changed when a new system or module is added,
+# or an existing one modified. This will also most likely require some minor
+# changes to Configure to recognize those changes.
+
+# There are 5 types of lines here:
+
+# '#' comments, distinguished by having a '#' as the first non-blank character
+#
+# Makefile options, such as CC=gcc, etc...
+#
+# Rules, distinquished by having "Rule" at the front. These are used to
+# control Configure's behavior as far as how to create Makefile.
+#
+# Module selection lines, distinguished by having 'Module' at the front.
+# These list the configured modules, in reverse priority order (lowest
+# priority first). They're down at the bottom.
+#
+# Optional module selection lines, distinguished by having `%Module'
+# at the front. These specify a module that is to be compiled in (but
+# not enabled). The AddModule directive can be used to enable such a
+# module. By default no such modules are defined.
+
+
+################################################################
+# Makefile configuration
+#
+# These are added to the general flags determined by Configure.
+# Edit these to work around Configure if needed. The EXTRA_* family
+# will be added to the regular Makefile flags. For example, if you
+# want to compile with -Wall, then add that to EXTRA_CFLAGS. These
+# will be added to whatever flags Configure determines as appropriate
+# and needed for your platform.
+#
+# You can also set the compiler and Optimization used here as well.
+# Settings here have priority; If not set, Configure will attempt to guess
+# the C compiler, and set OPTIM to '-O2'
+#
+# REDHAT LINUX 5.0 USERS PLEASE NOTE! You have to add -lcrypt to
+# EXTRA_LIBS. This is fixed in 1.3 but will not be fixed in 1.2.
+#
+EXTRA_CFLAGS=
+EXTRA_LFLAGS=
+EXTRA_LIBS=
+EXTRA_INCLUDES=
+
+#CC=
+#OPTIM=-O2
+#RANLIB=
+
+################################################################
+# Rules configuration
+#
+# These are used to let Configure know that we want certain
+# functions. The format is: Rule RULE=value
+#
+# At present, only the following RULES are known: WANTHSREGEX, SOCKS4,
+# STATUS, and IRIXNIS.
+#
+# For all Rules, if set to "yes", then Configure knows we want that
+# capability and does what is required to add it in. If set to "default"
+# then Configure makes a "best guess"; if set to anything else, or not
+# present, then nothing is done.
+#
+# SOCKS4:
+# If SOCKS4 is set to 'yes', be sure that you add the sock library
+# location to EXTRA_LIBS, otherwise Configure will assume
+# "-L/usr/local/lib -lsocks"
+#
+# STATUS:
+# If Configure determines that you are using the status_module,
+# it will automatically enable full status information if set
+# to 'yes'. If the status module is not included, having STATUS
+# set to 'yes' has no impact.
+#
+# IRIXNIS:
+# Only takes effect if Configure determines that you are running
+# SGI IRIX. If you are, and you are using NIS, you should set this
+# to 'yes'
+#
+
+Rule STATUS=yes
+Rule SOCKS4=no
+Rule IRIXNIS=no
+
+# The following rules should be set automatically by Configure. However, if
+# they are not set by Configure (because we don't know the correct value for
+# your platform), or are set incorrectly, you may override them here.
+# If you have to do this, please let us know what you set and what your
+# platform is, by filling out a problem report form at the Apache web site:
+# <http://www.apache.org/bugdb.cgi>. If your browser is forms-incapable,
+# you can get the information to us by sending mail to apache-bugs@apache.org.
+#
+# WANTHSREGEX:
+# Apache requires a POSIX regex implementation. Henry Spencer's
+# excellent regex package is included with Apache and can be used
+# if desired. If your OS has a decent regex, you can elect to
+# not use this one by setting WANTHSREGEX to 'no' or commenting
+# out the Rule. The "default" action is "no" unless overruled
+# by OS specifics
+
+Rule WANTHSREGEX=default
+
+################################################################
+# 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.
+
+##
+## Config manipulation modules
+##
+## mod_env sets up additional or restricted environment variables to be
+## passed to CGI/SSI scripts. It is listed first (lowest priority) since
+## it does not do per-request stuff.
+
+Module env_module mod_env.o
+
+## mod_dld defines commands that allow other modules to be loaded
+## dynamically (at runtime). This module is for experimental use only.
+
+# Module dld_module mod_dld.o
+
+##
+## Request logging modules
+##
+
+Module config_log_module mod_log_config.o
+
+## Optional modules for NCSA user-agent/referer logging compatibility
+## We recommend, however, that you just use the configurable access_log.
+
+# Module agent_log_module mod_log_agent.o
+# Module referer_log_module mod_log_referer.o
+
+##
+## Type checking modules
+##
+## mod_mime maps filename extensions to content types, encodings, and
+## magic type handlers (the latter is obsoleted by mod_actions).
+## mod_negotiation allows content selection based on the Accept* headers.
+
+Module mime_module mod_mime.o
+Module negotiation_module mod_negotiation.o
+
+##
+## Content delivery modules
+##
+## The status module allows the server to display current details about
+## how well it is performing and what it is doing. Consider also enabling
+## STATUS=yes (see the Rules section near the start of this 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
+
+## mod_include translates server-side include (SSI) statements in text files.
+## mod_dir handles requests on directories and directory indexes.
+## mod_cgi handles CGI scripts.
+
+Module includes_module mod_include.o
+Module dir_module mod_dir.o
+Module cgi_module mod_cgi.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
+
+##
+## URL translation modules.
+##
+## The UserDir module for selecting resource directories by user name
+## and a common prefix, e.g., /~<user> , /usr/web/<user> , etc.
+
+Module userdir_module mod_userdir.o
+
+## The proxy module enables the server to act as a proxy for outside
+## http and ftp services. It's not as complete as it could be yet.
+## NOTE: You do not want this module UNLESS you are running a proxy;
+## it is not needed for normal (origin server) operation.
+
+# Module proxy_module modules/proxy/libproxy.a
+
+## The Alias module provides simple URL translation and redirection.
+
+Module alias_module mod_alias.o
+
+## mod_rewrite allows for powerful URI-to-URI and URI-to-filename mapping,
+## using regular expressions.
+
+# Module rewrite_module mod_rewrite.o
+
+##
+## Access control and authentication modules.
+##
+Module access_module mod_access.o
+Module auth_module mod_auth.o
+
+## 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 and possibly adjust EXTRA_LIBS. (This may be
+## done by Configure at a later date)
+
+# 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 EXTRA_LIBS line, like
+##
+## -lmsql -L/usr/local/lib -L/usr/local/Minerva/lib
+##
+## This depends on your installation of mSQL. (This may be done by Configure
+## at a later date)
+
+# 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
+
+## Optional response header manipulation modules.
+##
+## cern_meta mimics the behavior of the CERN web server with regards to
+## metainformation files.
+
+# Module cern_meta_module mod_cern_meta.o
+
+## The expires module can apply Expires: headers to resources,
+## as a function of access time or modification time.
+
+# Module expires_module mod_expires.o
+
+## The headers module can set arbitrary HTTP response headers,
+## as configured in server, vhost, access.conf or .htaccess configs
+
+# Module headers_module mod_headers.o
+
+## Miscellaneous modules
+##
+## mod_usertrack.c is the new name for mod_cookies.c. This module
+## uses Netscape cookies to automatically construct and log
+## click-trails from Netscape cookies, or compatible clients who
+## aren't coming in via proxy.
+##
+## You do not need this, or any other module to allow your site
+## to use Cookies. This module is for user tracking only
+
+# Module usertrack_module mod_usertrack.o
+
+## The example module, which demonstrates the use of the API. See
+## the file modules/example/README for details. This module should
+## only be used for testing -- DO NOT ENABLE IT on a production server.
+
+# Module example_module modules/example/mod_example.o
+
+## mod_browser lets you set environment variables based on the User-Agent
+## string in the request; this is useful for conditional HTML, for example.
+## Since it is also used to detect buggy browsers for workarounds, it
+## should be the last (highest priority) module.
+
+Module browser_module mod_browser.o
diff --git a/usr.sbin/httpd/src/Configure b/usr.sbin/httpd/src/Configure
new file mode 100644
index 00000000000..9b47e26d157
--- /dev/null
+++ b/usr.sbin/httpd/src/Configure
@@ -0,0 +1,688 @@
+#!/bin/sh
+trap 'rm -f $tmpfile; exit' 0 1 2 3 15
+
+# Apache configuration script, first cut --- rst.
+# Dont like it? Inspired to do something better? Go for it.
+
+# second cut --- jmj
+# At this point we change what Configuration contains. It maintain
+# contains comments, specific compiler flags, a list of included
+# modules and "rules". These rules are used to allow Configure to
+# be totally configured from Configuration
+#
+# Uses 3 supplemental scripts located in ./helpers: CutRule,
+# GuessOS and PrintPath
+#
+
+file=Configuration
+tmpfile=htconf.$$
+makefile_tmpl=Makefile.tmpl
+
+while [ "x$1" != "x" ]; do
+ if [ "x$1" = "x-file" ] ; then
+ shift 1; file=$1; shift 1
+ if [ ! -r $file ]; then
+ echo "$file does not exist or is not readable."
+ exit 1
+ fi
+ elif [ "x$1" = "x-make" ] ; then
+ shift 1; makefile_tmpl=$1; shift 1
+ if [ ! -r $makefile_tmpl ]; then
+ echo "$makefile_tmpl does not exist or is not readable."
+ exit 1
+ fi
+ else
+ echo "Ignoring command line option '$1'"
+ shift 1
+ fi
+done
+echo "Using config file: $file"
+echo "Using Makefile template file: $makefile_tmpl"
+
+if [ ! -r $file ]; then
+ echo "Can't see or read \"$file\""
+ exit 1
+fi
+
+# First, strip comments and blank lines and then change Rules to comments
+# and then remove whitespace before Module declarations
+
+sed 's/#.*//' $file | \
+ sed '/^[ ]*$/d' | \
+ sed 's/[ ]*$//' | \
+ sed 's/^Rule[ ]*/##Rule:/' | \
+ sed 's/^[ ]*Module/Module/' | \
+ sed 's/^[ ]*%Module/%Module/' > $tmpfile
+
+# Check for syntax errors...
+
+if egrep -v '^%?Module[ ]+[A-Za-z0-9_]+[ ]+[^ ]+$' $tmpfile \
+ | grep -v = > /dev/null
+then
+ echo "Syntax error --- The configuration file is used only to"
+ echo "define the list of included modules or to set Makefile"
+ echo "options or Configure rules, and I don't see that at all:"
+ egrep -v '^Module[ ]+[A-Za-z0-9_]+[ ]+[^ ]+$' $tmpfile | \
+ grep -v =
+ 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
+
+sed -e 's/_module//' $tmpfile | awk >modules.c '\
+ BEGIN { modules[n++] = "core" ; pmodules[pn++] = "core"} \
+ /^Module/ { modules[n++] = $2 ; pmodules[pn++] = $2 } \
+ /^%Module/ { pmodules[pn++] = $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 < pn; ++i) { \
+ printf ("extern module %s_module;\n", pmodules[i]); \
+ } \
+ print ""; \
+ print "module *prelinked_modules[] = {"; \
+ for (i = 0; i < n; ++i) { \
+ printf " &%s_module,\n", modules[i]; \
+ } \
+ print " NULL"; \
+ print "};"; \
+ print "module *preloaded_modules[] = {"; \
+ for (i = 0; i < pn; ++i) { \
+ printf " &%s_module,\n", pmodules[i]; \
+ } \
+ print " NULL"; \
+ print "};"; \
+ }'
+
+#
+# Add module set only
+#
+echo "#" > Makefile
+echo "# Makefile automatically generated from $makefile_tmpl" >> Makefile
+echo "# and configuration file by Apache config script." >> Makefile
+echo "# Hand-edited changes will be lost if the config script" >> Makefile
+echo "# is re-run" >> Makefile
+echo "#" >> Makefile
+
+awk >>Makefile <$tmpfile '\
+ /^Module/ { modules[n++] = $3 } \
+ /^%Module/ { modules[n++] = $3 } \
+ 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 "" \
+ }'
+#
+# Now add Makefile additions and Rules
+#
+awk >>Makefile <$tmpfile '\
+ BEGIN { print "# Makefile options inherited from Configure"; \
+ print "###############"; \
+ } \
+ /\=/ { print } \
+ END { print "###############"; }'
+
+#
+# Extract the rules.
+#
+RULE_WANTHSREGEX=`./helpers/CutRule WANTHSREGEX $file`
+RULE_STATUS=`./helpers/CutRule STATUS $file`
+RULE_SOCKS4=`./helpers/CutRule SOCKS4 $file`
+RULE_IRIXNIS=`./helpers/CutRule IRIXNIS $file`
+
+#
+# Now we determine the OS/Platform automagically, thanks to
+# GuessOS, a home-brewed OS-determiner ala config.guess
+#
+# We adjust CFLAGS, LIBS, LFLAGS and INCLUDES (and other Makefile
+# options) as required. Setting CC and OPTIM here has no effect
+# if they were set in Configure.
+#
+# Also, we set DEF_WANTHSREGEX and to the appropriate
+# value for each platform.
+#
+# As more PLATFORMs are added to Configuration.tmpl, be sure to
+# add the required lines below.
+#
+
+PLAT=`./helpers/GuessOS`
+
+# Preset DBM_LIB. Can be overridden on a per-platform basis.
+
+DBM_LIB="-ldbm"
+
+#
+# Look for ranlib. Do it early in case we want to override it below
+#
+if ./helpers/PrintPath -s ranlib; then
+ RANLIB="ranlib"
+else
+ RANLIB="true"
+fi
+
+#
+# We now look for popular compilers. As with ranlib, we
+# do this early because some options may depend
+# on which compiler we use/find
+#
+for compilers in "gcc" "cc" "acc" "c89"
+do
+ lookedfor="$lookedfor $compilers"
+ if ./helpers/PrintPath -s $compilers; then
+ COMPILER="$compilers"
+ break
+ fi
+done
+
+#
+SHELL="/bin/sh"
+
+case "$PLAT" in
+ *MPE/iX*)
+ OS='MPE/iX'
+ CFLAGS="$CFLAGS -DMPE -D_POSIX_SOURCE -D_SOCKET_SOURCE"
+ LIBS="$LIBS -lsocket"
+ LFLAGS="$LFLAGS -Xlinker \"-WL,cap=ia,ba,ph,pm;nmstack=1024000\""
+ ;;
+ *-apple-aux3*)
+ OS='A/UX 3.1.x'
+ CFLAGS="$CFLAGS -DAUX -D_POSIX_SOURCE"
+ LIBS="$LIBS -lposix -lbsd"
+ LFLAGS="$LFLAGS -s"
+ DEF_WANTHSREGEX=no
+ ;;
+ i386-ibm-aix*)
+ OS='IBM AIX PS/2'
+ CFLAGS="$CFLAGS -DAIX -U__STR__ -DUSEBCOPY"
+ DEF_WANTHSREGEX=no
+ ;;
+ *-ibm-aix[1-3].*|*-ibm-aix4.[0-1])
+ OS='IBM AIX < v4.2'
+ CFLAGS="$CFLAGS -DAIX -DNEED_RLIM_T -U__STR__"
+ ;;
+ *-ibm-aix*)
+ OS='IBM AIX >= 4.2'
+ CFLAGS="$CFLAGS -DAIX -U__STR__"
+ LFLAGS="$LFLAGS -lm"
+ ;;
+ *-apollo-*)
+ OS='Apollo Domain'
+ CFLAGS="$CFLAGS -DAPOLLO"
+ ;;
+ *-dg-dgux*)
+ OS='DG/UX 5.4'
+ CFLAGS="$CFLAGS -DDGUX"
+ DEF_WANTHSREGEX=yes
+ ;;
+ *OS/2*)
+ DEF_WANTHSREGEX=yes
+ OS='EMX OS/2'
+ CFLAGS="$CFLAGS -Zbsd-signals -Zbin-files -DTCPIPV4 -g"
+ LIBS="$LIBS -lsocket -llibufc -lbsd"
+ DBM_LIB="-lgdbm"
+ ;;
+ *-hi-hiux)
+ OS='HI-UX'
+ CFLAGS="$CFLAGS -DHIUX"
+ # if we're using the HIUX compiler, add a few flags.
+ if [ "$CC" = "cc" ] || [ "$COMPILER" = "cc" ]; then
+ CFLAGS="$CFLAGS -Aa -D_HIUX_SOURCE"
+ OPTIM=" "
+ fi
+ ;;
+ *-hp-hpux10.*)
+ OS='HP-UX 10'
+ CFLAGS="$CFLAGS -DHPUX10"
+ # if we're using the HPUX compiler, add a few flags.
+ if [ "$CC" = "cc" ] || [ "$COMPILER" = "cc" ]; then
+ CFLAGS="$CFLAGS -Aa -D_HPUX_SOURCE"
+ OPTIM=" "
+ fi
+ ;;
+ *-hp-hpux*)
+ OS='HP-UX'
+ CFLAGS="$CFLAGS -DHPUX"
+ if [ "$CC" = "cc" ] || [ "$COMPILER" = "cc" ]; then
+ CFLAGS="$CFLAGS -Aa -D_HPUX_SOURCE"
+ OPTIM=" "
+ fi
+ LIBS="$LIBS -lm"
+ ;;
+ *-sgi-irix64)
+# Note: We'd like to see patches to compile 64-bit, but for now...
+ echo "You are running 64-bit Irix. For now, we will compile 32-bit"
+ echo "but if you would care to port to 64-bit, send us the patches."
+ CFLAGS="$CFLAGS -n32"
+ LFLAGS="$LFLAGS -n32"
+ DEF_WANTHSREGEX=yes
+ DBM_LIB=""
+ if [ "$RULE_IRIXNIS" = "yes" ]; then
+ OS='SGI IRIX w/NIS'
+ CFLAGS="$CFLAGS -DIRIX"
+ LIBS="$LIBS -lsun"
+ else
+ OS='SGI IRIX'
+ CFLAGS="$CFLAGS -DIRIX"
+ fi
+ ;;
+ *-sgi-irix)
+ DEF_WANTHSREGEX=yes
+ DBM_LIB=""
+ if [ "$RULE_IRIXNIS" = "yes" ]; then
+ OS='SGI IRIX w/NIS'
+ CFLAGS="$CFLAGS -DIRIX"
+ LIBS="$LIBS -lsun"
+ else
+ OS='SGI IRIX'
+ CFLAGS="$CFLAGS -DIRIX"
+ fi
+ ;;
+ alpha-*-linux2)
+ DEF_WANTHSREGEX=yes
+ OS='Linux'
+ CFLAGS="$CFLAGS -DLINUX=2"
+ LIBS="$LIBS -lcrypt"
+ ;;
+ sparc-*-linux2)
+ DEF_WANTHSREGEX=yes
+ OS='Linux'
+ CFLAGS="$CFLAGS -DLINUX=2"
+ LIBS="$LIBS -lm"
+ ;;
+ *-linux2)
+ DEF_WANTHSREGEX=yes
+ OS='Linux'
+ CFLAGS="$CFLAGS -DLINUX=2"
+ ;;
+ *-linux1)
+ DEF_WANTHSREGEX=yes
+ OS='Linux'
+ CFLAGS="$CFLAGS -DLINUX=1"
+ ;;
+ *-lynx-lynxos*)
+ OS='LynxOS'
+ CFLAGS="$CFLAGS -DLYNXOS"
+ LIBS="$LIBS -lbsd -ldes -lc_p"
+ ;;
+ *486-*-bsdi*)
+ OS='BSDI w/486'
+ CFLAGS="$CFLAGS -m486"
+ DBM_LIB=""
+ ;;
+ *-bsdi*)
+ OS='BSDI'
+ DBM_LIB=""
+ ;;
+ *486-*-freebsd*|*486-*-netbsd*)
+ OS='FreeBSD/NETBSD on 486'
+ LIBS="$LIBS -lcrypt"
+ DBM_LIB=""
+ ;;
+ *-freebsd*|*-netbsd*)
+ OS='FreeBSD/NetBSD'
+ LIBS="$LIBS -lcrypt"
+ DBM_LIB=""
+ ;;
+ *-openbsd*)
+ OS='OpenBSD'
+ DBM_LIB=""
+ ;;
+ *-next-nextstep*)
+ OS='NeXT'
+ CFLAGS="$CFLAGS -DNEXT"
+ DEF_WANTHSREGEX=yes
+ RANLIB="sleep 5; /bin/ranlib"
+ # ranlib on most NeXTs sets the time wrong. 5 secs wait does much good
+ ;;
+ *-dec-osf*)
+ OS='DEC OSF/1'
+ CFLAGS="$CFLAGS -DOSF1"
+ LIBS="$LIBS -lm"
+ ;;
+ *-qnx)
+ OS='QNX'
+ CFLAGS="$CFLAGS -DQNX"
+ LIBS="$LIBS -N128k -lsocket"
+ DEF_WANTHSREGEX=yes
+ ;;
+ *-qnx32)
+ OS='QNX32'
+ CFLAGS="$CFLAGS -DQNX -mf -3"
+ LIBS="$LIBS -N128k -lsocket"
+ DEF_WANTHSREGEX=yes
+ ;;
+ *-isc4*)
+ OS='ISC 4'
+ CC='gcc'
+ CFLAGS="$CFLAGS -posix -DISC"
+ LFLAGS="$LFLAGS -posix"
+ LIBS="$LIBS -linet"
+ DEF_WANTHSREGEX=yes
+ ;;
+ *-sco3*)
+ OS='SCO 3'
+ CFLAGS="$CFLAGS -DSCO -Oacgiltz"
+ LIBS="$LIBS -lPW -lsocket -lmalloc -lcrypt_i"
+ DEF_WANTHSREGEX=yes
+ ;;
+ *-sco5*)
+ OS='SCO 5'
+ CFLAGS="$CFLAGS -DSCO5"
+ LIBS="$LIBS -lsocket -lmalloc -lprot"
+ if [ "$CC" = "cc" ] || [ "$COMPILER" = "cc" ]; then
+ OSBPRINTF="-K noinline"
+ fi
+ DEF_WANTHSREGEX=no
+ ;;
+ *-solaris2*)
+ OS='Solaris 2'
+ CFLAGS="$CFLAGS -DSOLARIS2"
+ LIBS="$LIBS -lsocket -lnsl"
+ DBM_LIB=""
+ DEF_WANTHSREGEX=yes
+ ;;
+ *-sunos4*)
+ OS='SunOS 4'
+ CFLAGS="$CFLAGS -DSUNOS4 -DUSEBCOPY"
+ DEF_WANTHSREGEX=yes
+ ;;
+ *-unixware1)
+ DEF_WANTHSREGEX=yes
+ OS='Unixware'
+ CFLAGS="$CFLAGS -DSVR4 -DNO_LINGCLOSE"
+ LIBS="$LIBS -lsocket -lnsl -lcrypt"
+ ;;
+ *-unixware2)
+ DEF_WANTHSREGEX=yes
+ OS='Unixware'
+ CFLAGS="$CFLAGS -DSVR4 -DNO_LINGCLOSE"
+ LIBS="$LIBS -lsocket -lnsl -lcrypt"
+ ;;
+ *-unixware211)
+ OS='Unixware 2.1.1'
+ CFLAGS="$CFLAGS -DUW"
+ LIBS="$LIBS -lsocket -lnsl -lcrypt"
+ ;;
+ *-unixware212)
+ OS='Unixware 2.1.2'
+ CFLAGS="$CFLAGS -DUW"
+ LIBS="$LIBS -lsocket -lnsl -lcrypt"
+ DBM_LIB=""
+ ;;
+ maxion-*-sysv4*)
+ OS='SVR4'
+ CFLAGS="$CFLAGS -DSVR4"
+ DEF_WANTHSREGEX=yes
+ LIBS="$LIBS -lsocket -lnsl -lc -lgen"
+ ;;
+ *-sni-sysv4*)
+ OS='SVR4'
+ CFLAGS="$CFLAGS -DSVR4 -D_XPG_IV -DUSE_MMAP_FILES"
+ DEF_WANTHSREGEX=yes
+ LIBS="$LIBS -lsocket -lnsl -lc"
+ ;;
+ DS/90\ 7000-*-sysv4*)
+ OS='UXP/DS'
+ CFLAGS="$CFLAGS -DUXPDS"
+ LIBS="$LIBS -lsocket -lnsl"
+ DEF_WANTHSREGEX=yes
+ ;;
+ *-tandem-sysv4*)
+ OS='SVR4'
+ CFLAGS="$CFLAGS -DSVR4"
+ LIBS="$LIBS -lsocket -lnsl"
+ DEF_WANTHSREGEX=yes
+ ;;
+ *-sysv4*)
+ OS='SVR4'
+ CFLAGS="$CFLAGS -DSVR4"
+ LIBS="$LIBS -lsocket -lnsl -lc"
+ ;;
+ *-uts*)
+ OS='Amdahl UTS'
+ CFLAGS="$CFLAGS -Xa -eft -DUTS21"
+ LIBS="$LIBS -lsocket -lbsd -la"
+ ;;
+ *-ultrix)
+ OS='ULTRIX'
+ CFLAGS="-DULTRIX"
+ DEF_WANTHSREGEX=yes
+ SHELL="/bin/sh5"
+ if [ "$CC" = "cc" ] || [ "$COMPILER" = "cc" ]; then
+ CFLAGS="$CFLAGS -std"
+ fi
+ ;;
+ *powerpc-tenon-machten*)
+ OS='MachTen PPC'
+ LFLAGS="$LFLAGS -Xlstack=0x14000 -Xldelcsect"
+ ;;
+ *-machten*)
+ OS='MachTen 68K'
+ LFLAGS="$LFLAGS -stack 0x14000"
+ DEF_WANTHSREGEX=yes
+ ;;
+ *convex-v11*)
+ OS='CONVEXOS11'
+ CFLAGS="$CFLAGS -ext -DCONVEXOS11"
+ OPTIM="-O1" # scalar optimization only
+ CC='cc'
+ DEF_WANTHSREGEX=yes
+ ;;
+ i860-intel-osf1)
+ DEF_WANTHSREGEX=yes
+ OS='Paragon OSF/1'
+ CFLAGS="$CFLAGS -DPARAGON"
+ ;;
+ *) # default: Catch systems we don't know about
+ echo Sorry, but we cannot grok \"$PLAT\"
+ echo uname -m
+ uname -m
+ echo uname -r
+ uname -r
+ echo uname -s
+ uname -s
+ echo uname -v
+ uname -v
+ echo uname -X
+ uname -X
+ echo Ideally, read the file PORTING, do what it says, and send the
+ echo resulting patches to The Apache Group by filling out a report
+ echo form at http://www.apache.org/bugdb.cgi - or, if your browser
+ echo isn\'t forms-capable, you can send them via email to
+ echo apache-bugs@apache.org. If you don\'t wish to do the port
+ echo yourself, please submit this output rather than the patches.
+ echo Thank you
+ exit 1
+ ;;
+esac
+
+#
+# See if we need to override WANTHSREGEX
+#
+if [ "$RULE_WANTHSREGEX" = "default" ]; then
+ if [ "x$DEF_WANTHSREGEX" = "x" ]; then
+ RULE_WANTHSREGEX=no
+ else
+ RULE_WANTHSREGEX=$DEF_WANTHSREGEX
+ fi
+fi
+
+# Show the final values of the rules
+
+echo "###############" > Makefile.config
+echo "# Platform: $OS" >> Makefile.config
+echo "# Final Rules:" >> Makefile.config
+echo "# Rule WANTHSREGEX=$RULE_WANTHSREGEX" >> Makefile.config
+echo "###############" >> Makefile.config
+
+#
+# Now that _that's_ done, get on with it
+#
+
+echo " + configured for $OS platform"
+
+#
+# Now we determine the C-compiler and optimization level
+# to use. Settings of CC and OPTIM in Configuration have
+# the highest precedence; next comes any settings from
+# the above "OS-specific" section. If still unset,
+# then we use the "found" location of COMPILERS above
+# and set a "safe" optimization level
+#
+
+if egrep "^CC[ ]*=" Makefile > /dev/null; then
+ CC="" # clear it just in case
+else
+ if [ "x$CC" = "x" ]; then
+ if [ "x$COMPILER" = "x" ]; then
+ echo "Error: could not find any of these C compilers"
+ echo " anywhere in your PATH: $lookedfor"
+ echo "Configure terminated"
+ exit 1
+ fi
+ CC=$COMPILER
+ fi
+ echo " + setting C compiler to $CC"
+fi
+
+#
+# Ditto for optimization
+#
+if egrep "^OPTIM[ ]*=" Makefile > /dev/null; then
+ OPTIM="" # ditto
+else
+ if [ "x$OPTIM" = "x" ]; then
+ OPTIM="-O2"
+ fi
+ echo " + setting C compiler optimization-level to $OPTIM"
+fi
+
+#
+# Are they using the status monitor module? If so, check
+# for STATUS rule...
+#
+STAT_MOD="mod_status"
+if grep "$STAT_MOD" Makefile > /dev/null; then
+ if [ "$RULE_STATUS" = "yes" ]; then
+ CFLAGS="$CFLAGS -DSTATUS"
+ fi
+fi
+
+#
+# Are they using dbm auth? If so, add DBM library.
+#
+if grep mod_auth_dbm Makefile > /dev/null; then
+ LIBS="$LIBS $DBM_LIB"
+fi
+
+#
+# Now HS's POSIX regex implementation if needed/wanted
+#
+if [ "$RULE_WANTHSREGEX" = "yes" ]; then
+ REGLIB="regex/libregex.a"
+ INCLUDES="$INCLUDES -Iregex"
+fi
+
+#
+# Now SOCKS4.
+# NOTE: We assume that if they are using SOCKS4, then they've
+# adjusted EXTRA_LIBS and/or EXTRA_LFLAGS as required,
+# otherwise we assume "-L/usr/local/lib -lsocks"
+#
+if [ "$RULE_SOCKS4" = "yes" ]; then
+ # Set flag and check Makefile for -lsocks line
+ CFLAGS="$CFLAGS -Dconnect=Rconnect -Dselect=Rselect"
+ CFLAGS="$CFLAGS -Dgethostbyname=Rgethostbyname"
+ if [ "$OS" = "Solaris 2" ]; then
+ LIBS="$LIBS -lresolv"
+ fi
+ if grep "EXTRA_" Makefile | grep "\-lsocks" > /dev/null; then : ;
+ else
+ LIBS="$LIBS -L/usr/local/lib -lsocks"
+ fi
+fi
+
+#
+# Good enough
+#
+echo >> Makefile
+if [ "x$CC" != "x" ]; then
+ echo "CC=$CC" >> Makefile.config
+fi
+if [ "x$OPTIM" != "x" ]; then
+ echo "OPTIM=$OPTIM" >> Makefile.config
+fi
+echo "CFLAGS1=$CFLAGS">> Makefile.config
+echo "INCLUDES1=$INCLUDES">> Makefile.config
+echo "LIBS1=$LIBS">> Makefile.config
+echo "LFLAGS1=$LFLAGS">> Makefile.config
+echo "BROKEN_BPRINTF_FLAGS=$OSBPRINTF">> Makefile.config
+echo "REGLIB=$REGLIB">> Makefile.config
+echo "RANLIB=$RANLIB">> Makefile.config
+echo "SHELL=$SHELL">> Makefile.config
+echo >> Makefile.config
+echo "#### End of Configure created section ####">> Makefile.config
+
+
+# Now (finish) creating the makefiles
+cat Makefile.config >> Makefile
+sed -e "s#@@Configuration@@#$file#" "$makefile_tmpl" >>Makefile
+awk >>Makefile <$tmpfile \
+ '($1 == "Module" && $3 ~ /modules\//) { printf "%s: modules/last-built ; @cat /dev/null\n\n", $3, $3}'
+#cat Makefile.config ../support/Makefile.tmpl > ../support/Makefile
+
+cat << EOF > modules/Makefile
+#
+# Simple Makefile for modules in src/modules.
+# Generated by src/Configure according to rules in src/Configuration;
+# hand-edit at your own risk!
+#
+
+SHELL=$SHELL
+EOF
+
+if [ "$RULE_WANTHSREGEX" = "yes" ]; then
+ INCLUDES2="-I../../regex"
+fi
+
+echo "INCLUDES2=$INCLUDES2">> modules/Makefile
+echo "MOD_CFLAGS=\$(INCLUDES2) \$(AUX_CFLAGS)">> modules/Makefile
+
+awk >> modules/Makefile < $tmpfile '\
+ BEGIN {printf "MODULES="} \
+ ($1 == "Module" && $3 ~ /modules\//) {split ($3, pp, "/"); printf "%s ", pp[2]} \
+ END {printf "\n"}'
+
+awk >> modules/Makefile < $tmpfile '\
+ BEGIN {printf "CLEANERS="} \
+ ($1 == "Module" && $3 ~ /modules\//) {split ($3, pp, "/"); printf "%s_clean ", pp[2]} \
+ END {printf "\n"}'
+
+cat << EOF >> modules/Makefile
+
+default: \$(MODULES)
+ @echo "Done building module subdirectories"
+
+clean: \$(CLEANERS)
+ @echo "Done cleaning module subdirectories"
+
+placeholder \$(MODULES): ForceMe
+ (cd \$@; \$(MAKE) CC='\$(CC)' AUX_CFLAGS='\$(MOD_CFLAGS)' RANLIB='\$(RANLIB)')
+
+ForceMe:
+
+EOF
+
+awk >>modules/Makefile <$tmpfile \
+ '($1 == "Module" && $3 ~ /modules\//) { split ($3, pp, "/"); \
+ printf "%s_clean:\n\t(cd %s; $(MAKE) clean)\n\n", pp[2], pp[2]}'
+
diff --git a/usr.sbin/httpd/src/INSTALL b/usr.sbin/httpd/src/INSTALL
new file mode 100644
index 00000000000..a83de509c29
--- /dev/null
+++ b/usr.sbin/httpd/src/INSTALL
@@ -0,0 +1,71 @@
+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 list and settings of various
+ "Rules" and 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) Adjust the Rules and EXTRA_CFLAGS|LIBS|LFLAGS|INCLUDES if
+ you feel so inclined.
+
+ 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 config file: Configuration
+ Using Makefile template file: Makefile.tmpl
+ + configured for <whatever> platform
+ + setting C compiler to <whatever> *
+ + setting C compiler optimization-level to <whatever> *
+ %
+
+ 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 config file: Configuration.ai
+ Using Makefile template file: Makefile.tmpl
+ + configured for <whatever> platform
+ + setting C compiler to <whatever> *
+ + setting C compiler optimization-level to <whatever> *
+ %
+
+ (*: Depending on Configuration and your system, Configure
+ may not print these lines. That's OK.)
+
+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.
+
+If during compilation you get a warning about a missing 'regex.h', set
+WANTHSREGEX=yes in the 'Configuration', and let The Apache Group know
+you needed to do this for your OS by filling out a problem report form
+at <http://www.apache.org/bugdb.cgi>, or by sending a mail message to
+apache-bugs@apache.org. Include the output of the command "uname -a".
+
+
+--------------------------------------------------------------------
+
+Now that you have compiled Apache, go back to the README file in the
+top-level directory of this distribution to continue the installation.
diff --git a/usr.sbin/httpd/src/Makefile.bsd-wrapper b/usr.sbin/httpd/src/Makefile.bsd-wrapper
new file mode 100644
index 00000000000..bc836a9c85b
--- /dev/null
+++ b/usr.sbin/httpd/src/Makefile.bsd-wrapper
@@ -0,0 +1,48 @@
+
+.include <bsd.own.mk>
+
+PROG=httpd
+BINDIR=/usr/sbin
+BINOWN=root
+BINGRP=daemon
+
+all: Makefile
+ ${MAKE}
+
+.FORCE: .IGNORE
+
+helpers/GuessOS:
+ lndir -e Makefile.bsd-wrapper -e obj ${.CURDIR} ${.OBJDIR}
+
+config: .FORCE
+ sh ${.CURDIR}/Configure -file ${.CURDIR}/Configuration -make ${.CURDIR}/Makefile.tmpl
+
+Makefile: helpers/GuessOS
+ sh ${.CURDIR}/Configure -file ${.CURDIR}/Configuration -make ${.CURDIR}/Makefile.tmpl
+
+# apache has no man pages in the dist
+
+maninistall:
+ @echo No man pages for apache
+
+install: maninistall
+ ${INSTALL} ${INSTALL_COPY} ${INSTALL_STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} ${PROG} ${BINDIR}
+
+clean cleandir:
+.if exists(${.OBJDIR}/Makefile)
+ ${MAKE} clean
+.endif
+ /bin/rm -f Makefile
+ /bin/rm -f modules/Makefile
+ /bin/rm -f Makefile.config
+ /bin/rm -f modules.c
+
+depend:
+ # Nothing here yet
+lint:
+ #Nothing here yet
+tags:
+ #Nothing here yet
+
+.include<bsd.obj.mk>
+.include<bsd.subdir.mk>
diff --git a/usr.sbin/httpd/src/Makefile.tmpl b/usr.sbin/httpd/src/Makefile.tmpl
new file mode 100644
index 00000000000..e546fd11f69
--- /dev/null
+++ b/usr.sbin/httpd/src/Makefile.tmpl
@@ -0,0 +1,162 @@
+# Apache makefile template (well, suffix).
+
+# This is combined with the information in the "Configuration" file
+# by the configure script to make the actual Makefile.
+
+CFLAGS=$(OPTIM) $(CFLAGS1) $(EXTRA_CFLAGS)
+LIBS=$(EXTRA_LIBS) $(LIBS1)
+INCLUDES=$(INCLUDES1) $(EXTRA_INCLUDES)
+LFLAGS=$(LFLAGS1) $(EXTRA_LFLAGS)
+
+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 util_date.o util_snprintf.o\
+ $(MODULES)
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(SPACER) $<
+
+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: $(REGLIB) $(OBJS)
+ $(CC) $(LFLAGS) -o httpd $(OBJS) $(REGLIB) $(LIBS)
+
+regex/libregex.a:
+ (cd regex; $(MAKE) lib CC='$(CC)' AUX_CFLAGS='$(CFLAGS)' RANLIB='$(RANLIB)')
+
+modules/last-built:
+ (cd modules; \
+ $(MAKE) CC='$(CC)' AUX_CFLAGS='$(CFLAGS)' RANLIB='$(RANLIB)')
+
+clean:
+ rm -f httpd *.o core
+ cd regex; $(MAKE) clean
+ cd modules; $(MAKE) clean
+
+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 $(INCLUDES) $(CFLAGS) $(BROKEN_BPRINTF_FLAGS) http_bprintf.c
+
+# We really don't expect end users to use this rule. It works only with
+# gcc, and rebuilds Makefile.tmpl. You have to re-run Configure after
+# using it.
+depend:
+ sed -ne '1,/^# DO NOT REMOVE/p' Makefile.tmpl > Makefile.new \
+ && $(CC) -MM $(INCLUDES) $(CFLAGS) *.c >> Makefile.new \
+ && mv Makefile.tmpl Makefile.tmpl.bak \
+ && mv Makefile.new Makefile.tmpl
+
+#Dependencies
+
+$(OBJS): Makefile
+
+# DO NOT REMOVE
+alloc.o: alloc.c httpd.h conf.h alloc.h buff.h
+buff.o: buff.c conf.h alloc.h buff.h
+explain.o: explain.c explain.h
+http_bprintf.o: http_bprintf.c conf.h alloc.h buff.h
+http_config.o: http_config.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_core.h http_log.h http_request.h \
+ http_conf_globals.h explain.h
+http_core.o: http_core.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_core.h http_protocol.h http_conf_globals.h http_main.h \
+ http_log.h rfc1413.h util_md5.h md5.h scoreboard.h
+http_log.o: http_log.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_core.h http_log.h
+http_main.o: http_main.c httpd.h conf.h alloc.h buff.h http_main.h \
+ http_log.h http_config.h http_protocol.h http_request.h \
+ http_conf_globals.h http_core.h scoreboard.h explain.h
+http_protocol.o: http_protocol.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_core.h http_protocol.h http_main.h http_log.h \
+ util_date.h
+http_request.o: http_request.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_request.h http_core.h http_protocol.h http_log.h \
+ http_main.h scoreboard.h
+md5c.o: md5c.c md5.h
+mod_access.o: mod_access.c httpd.h conf.h alloc.h buff.h http_core.h \
+ http_config.h http_log.h http_request.h
+mod_actions.o: mod_actions.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_request.h http_core.h http_protocol.h http_main.h \
+ http_log.h util_script.h
+mod_alias.o: mod_alias.c httpd.h conf.h alloc.h buff.h http_config.h
+mod_asis.o: mod_asis.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_protocol.h http_log.h util_script.h http_main.h http_request.h
+mod_auth.o: mod_auth.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_core.h http_log.h http_protocol.h
+mod_auth_anon.o: mod_auth_anon.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_core.h http_log.h http_protocol.h
+mod_auth_db.o: mod_auth_db.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_core.h http_log.h http_protocol.h
+mod_auth_dbm.o: mod_auth_dbm.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_core.h http_log.h http_protocol.h
+mod_auth_msql.o: mod_auth_msql.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_core.h http_log.h http_protocol.h
+mod_browser.o: mod_browser.c httpd.h conf.h alloc.h buff.h \
+ http_config.h
+mod_cern_meta.o: mod_cern_meta.c httpd.h conf.h alloc.h buff.h \
+ http_config.h util_script.h http_log.h http_request.h
+mod_cgi.o: mod_cgi.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_request.h http_core.h http_protocol.h http_main.h http_log.h \
+ util_script.h http_conf_globals.h
+mod_digest.o: mod_digest.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_core.h http_log.h http_protocol.h util_md5.h md5.h
+mod_dir.o: mod_dir.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_core.h http_request.h http_protocol.h http_log.h http_main.h \
+ util_script.h
+mod_dld.o: mod_dld.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_conf_globals.h
+mod_env.o: mod_env.c httpd.h conf.h alloc.h buff.h http_config.h
+mod_expires.o: mod_expires.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_log.h
+mod_headers.o: mod_headers.c httpd.h conf.h alloc.h buff.h \
+ http_config.h
+mod_imap.o: mod_imap.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_request.h http_core.h http_protocol.h http_main.h http_log.h \
+ util_script.h
+mod_include.o: mod_include.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_request.h http_core.h http_protocol.h http_log.h \
+ http_main.h util_script.h
+mod_info.o: mod_info.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_core.h http_log.h http_main.h http_protocol.h util_script.h
+mod_log_agent.o: mod_log_agent.c httpd.h conf.h alloc.h buff.h \
+ http_config.h
+mod_log_config.o: mod_log_config.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_core.h
+mod_log_referer.o: mod_log_referer.c httpd.h conf.h alloc.h buff.h \
+ http_config.h
+mod_mime.o: mod_mime.c httpd.h conf.h alloc.h buff.h http_config.h
+mod_negotiation.o: mod_negotiation.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_request.h http_core.h http_log.h util_script.h
+mod_rewrite.o: mod_rewrite.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_request.h http_core.h http_log.h mod_rewrite.h
+mod_status.o: mod_status.c httpd.h conf.h alloc.h buff.h http_config.h \
+ http_core.h http_protocol.h http_main.h util_script.h scoreboard.h \
+ http_log.h
+mod_userdir.o: mod_userdir.c httpd.h conf.h alloc.h buff.h \
+ http_config.h
+mod_usertrack.o: mod_usertrack.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_core.h
+modules.o: modules.c httpd.h conf.h alloc.h buff.h http_config.h
+rfc1413.o: rfc1413.c httpd.h conf.h alloc.h buff.h http_log.h \
+ rfc1413.h
+util.o: util.c httpd.h conf.h alloc.h buff.h http_conf_globals.h
+util_date.o: util_date.c util_date.h
+util_md5.o: util_md5.c httpd.h conf.h alloc.h buff.h util_md5.h md5.h
+util_script.o: util_script.c httpd.h conf.h alloc.h buff.h \
+ http_config.h http_conf_globals.h http_main.h http_log.h \
+ http_protocol.h http_core.h http_request.h util_script.h
+util_snprintf.o: util_snprintf.c conf.h
diff --git a/usr.sbin/httpd/src/PORTING b/usr.sbin/httpd/src/PORTING
new file mode 100644
index 00000000000..24c7c4f3771
--- /dev/null
+++ b/usr.sbin/httpd/src/PORTING
@@ -0,0 +1,257 @@
+The Semi-Official Guide to Porting Apache
+
+-------------
+Introduction:
+-------------
+Apache has been ported to a wide variety of platforms, from multiple
+UNIX varients to OS/2. Nonetheless, there are most likely a few
+platforms out there that currently are not "officially" supported
+under Apache. Porting Apache to these platforms can be quite simple
+depending on the "genericness" of the OS. This doc will provide
+some basic guidelines to help the potential porter.
+
+-------------
+Requirements:
+-------------
+One of the basic requirements for a potential Apache platform is
+a robust TCP/IP implementation. Just about any UNIX out there
+nowadays, even some ancient ones, have a TCP/IP stack that will
+work. In particular, the UNIX should provide for sockets and the
+basic controlling functions for them (like accept(), bind(), etc).
+
+The source for Apache is written in ANSI-C, so an ANSI-C compiler
+is required. However, Apache does not use or require ANSI-only
+functions or options (eg: the "%n" parameter in the scanf()
+family); the source basically uses ANSI function prototyping but
+no other specific ANSIisms. Thus, an ANSI-to-K&R filter _may_
+work, although as far as I know it has not yet been tried. If you
+attempt this, let the Apache team know (mailto: new-httpd@hyperreal.com).
+
+-------------------
+The Starting Point:
+-------------------
+The first thing to look at is the output of the ./helpers/GuessOS
+script. This is a simple script that attempts to determine the
+platform and OS you are running on. The output of this script
+is used by Configure to set some basic compilation parameters.
+
+The output of ./helpers/GuessOS was designed to be GNUconfig.guess
+compatible (from GNU/autoconf). The format of the output string
+is:
+
+ machine-vendor-OS
+
+This string is returned to the main Configure script as the
+shell variable $PLAT. If Configure is not "aware" of that platform
+(or cannot correctly parse it), it will complain and die.
+
+----------------------
+Configure cannot Grok:
+----------------------
+If this happens to you, then it means that Configure doesn't
+know how to configure and compile Apache for your OS. The first
+course of action is the easiest: Look in Configure and see if
+there are any OSs which is similar to yours.
+
+For example, let's say that your OS is similar to HP-UX, but that
+GuessOS returns "foobar-intel-hubble". You would then edit
+Configure as follows:
+
+ *-hp-hpux*|*-*-hubble)
+ OS='HP-UX'
+ CFLAGS="$CFLAGS -DHPUX"
+ ;;
+
+The '|*-*-hubble' was added to the switch statement for HP-UX.
+
+Another fix may involve editing the GuessOS helper script. Let's
+say, for example, that your system is SysV4-based, but that
+GuessOS does not return that info. You could then add a switch
+to the script that does something like:
+
+ *WeirdSystem*)
+ echo "${MACHINE}-whatever-sysv4"; exit 0
+ ;;
+
+In this case, we force GuessOS to return a string that includes
+the "sysv4" cookie for Configure to recognize.
+
+Unfortunately, unless you are running a very generic BSD or SysV
+system, no "supported" OS will be close enough in all aspects to
+allow for a clear (and possibly workable) build of Apache. If this
+is the case, you will need to port Apache to your OS.
+
+-------------------
+Porting for Apache:
+-------------------
+When all else fails, it's time to hack some code. The source itself
+is generic enough that most ports are incredibly easy. No matter
+what, however, there are 2 source files that need to be updated
+for the port:
+
+ Configure
+ conf.h
+
+Configure:
+==========
+Configure concerns itself with determining the OS-type for the
+build and setting up a few Makefile variables for the build. The
+most important is 'OS' and 'CFLAGS'. For example, when Configure
+determines a build for A/UX, it runs the following lines:
+
+ case "$PLAT" in
+ *-apple-aux3*)
+ OS='A/UX 3.1.x'
+ CFLAGS="$CFLAGS -DAUX -D_POSIX_SOURCE"
+ LIBS="$LIBS -lposix -lbsd"
+ LFLAGS="$LFLAGS -s"
+ DEF_WANTHSREGEX=no
+ ;;
+
+The 'OS' variable is used to define the system Apache is being built
+for. You will also note that 'CFLAGS' defines "-DAUX". In this case,
+'AUX' is a magic cookie used by the Apache code (mainly conf.h [see
+below]) to handle OS-specific code. Each code that has and requires
+such OS-specific code will require a unique "system cookie" defined
+in 'CFLAGS'. You will also note that Configure also goes ahead and
+predefines the LIBS and LFLAGS Makefile variables (DEF_WANTHSREGEX is
+explained below).
+
+conf.h:
+=======
+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
+supported OS. One of the hardest parts about the porting process is
+determining which of the following are applicable for your system and
+setup. This time using the example of AIX, we see:
+
+ #elif defined(AIX)
+ #undef HAVE_GMTOFF
+ #undef NO_KILLPG
+ #undef NO_SETSID
+ #define HAVE_SYS_SELECT_H
+ #define JMP_BUF sigjmp_buf
+ #define HAVE_MMAP
+ typedef int rlim_t;
+
+The above lines describe which functions, capabilities and specifics
+are required for Apache to build and run under IBM AIX (the #undefs
+are not strictly required, but are a Good Idea anyway).
+
+The following several lines provide a list and short description
+of these #defines. By correcting #defining the ones you need in conf.h
+(wrapped by the above mentioned "system cookie"), you can fine tune the
+build for your OS.
+
+--
+
+ 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:
+--
+
+ HAVE_*:
+ Does this OS have/support this capability?
+
+ HAVE_GMTOFF:
+ Define if the OS's tm struct has the tm_gmtoff element
+
+ HAVE_RESOURCE:
+ Define 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.
+
+ HAVE_SYS_SELECT_H:
+ Define if the OS has the <sys/select.h> header file.
+
+ HAVE_SYS_RESOURCE_H:
+ Define if the OS has and supports the getrlimit/setrlimit
+ family. Apache uses this to determine if RLIMIT_CPU|VMEM|DATA|RLIMIT
+ is found and used.
+
+ HAVE_SNPRINTF:
+ Apache makes extensive use of the snprintf() function. many
+ platforms do not provide this function. If your platform
+ does provide it _and_ it's reliable (most are not) then
+ define this to use the OS version. Otherwise, Apache will
+ use it's own.
+--
+
+ USE_*:
+ These #defines are used for functions and ability that aren't exactly
+ required but should be used.
+
+ USE_FCNTL_SERIALIZED_ACCEPT:
+ Define if the OS requires a mutex "lock" around the socket accept()
+ call. Use fcntl() locking.
+
+ USE_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).
+
+ USE_LONGJMP:
+ use the longjmp() call instead of siglongjmp()
+ (as well as setjmp() instead of sigsetjmp())
+
+--
+
+ NO_*:
+ These are defined if the OS does NOT have the specified function or if
+ we should not use it.
+
+ NO_UNISTD_H:
+ NO_KILLPG:
+ NO_SETSID:
+ NO_USE_SIGACTION:
+ Do not use the sigaction() call, even if we have it.
+ NO_LINGCLOSE:
+ Do not use Apache's soft, "lingering" close feature to
+ terminate connections.
+ NO_SLACK:
+ Do not use the "slack" fd feature which requires a working fcntl
+ F_DUPFD.
+--
+
+ MISC #DEFINES:
+ Various other #defines used in the code.
+
+ JMP_BUF:
+ The variable-type for siglongjmp() or longjmp() call.
+
+ MOVEBREAK:
+ Amount to move sbrk() breakpoint, if required, before attaching
+ shared-memory segment.
+
+-----------
+Conclusion:
+-----------
+The above hints, and a good understanding of your OS and Apache, will
+go a LONG way in helping you get Apache built and running on your
+OS. If you have a port, PLEASE send Email to 'new-httpd@hyperreal.com'
+with the patches so that we may add them to the official version.
+If you hit a rough spot in the porting process, you can also try
+sending Email to that address as well and, if you are lucky, someone
+will respond. Another good source is the 'comp.infosystems.www.servers.unix'
+Usenet group as well.
+
+Good luck and happy porting!
diff --git a/usr.sbin/httpd/src/README b/usr.sbin/httpd/src/README
new file mode 100644
index 00000000000..4abb59f4bb7
--- /dev/null
+++ b/usr.sbin/httpd/src/README
@@ -0,0 +1,147 @@
+The following document was written by Robert S. Thau (rst@ai.mit.edu) on the
+release of Apache 1.0. Some details may have changed since then regarding the
+functions and names of modules, but the basic ideas are still intact.
+=================================================
+
+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_log_config.c --- logging in configurable or 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.
+
diff --git a/usr.sbin/httpd/src/alloc.c b/usr.sbin/httpd/src/alloc.c
new file mode 100644
index 00000000000..d002a6f8f8c
--- /dev/null
+++ b/usr.sbin/httpd/src/alloc.c
@@ -0,0 +1,1142 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 "httpd.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. Note that we rely on
+malloc() to provide aligned memory. */
+
+union block_hdr *malloc_block (int size)
+{
+ union block_hdr *blok =
+ (union block_hdr *)malloc(size + sizeof(union block_hdr));
+
+ if (blok == NULL) {
+ fprintf (stderr, "Ouch! malloc failed in malloc_block()\n");
+ exit (1);
+ }
+ 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...
+ */
+
+ while (blok != NULL) {
+ if (min_size + BLOCK_MINFREE <= 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. */
+
+ min_size += BLOCK_MINFREE;
+ return malloc_block((min_size > BLOCK_MINALLOC) ? min_size : BLOCK_MINALLOC);
+}
+
+
+
+/* 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(reqsize <= 0)
+ return NULL;
+
+ 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) {
+ int new_size = (arr->nalloc <= 0) ? 1 : arr->nalloc * 2;
+ char *new_data;
+
+ new_data = pcalloc (arr->pool, arr->elt_size * new_size);
+
+ memcpy (new_data, arr->elts, arr->nalloc * arr->elt_size);
+ arr->elts = new_data;
+ arr->nalloc = new_size;
+ }
+
+ ++arr->nelts;
+ return arr->elts + (arr->elt_size * (arr->nelts - 1));
+}
+
+void array_cat (array_header *dst, const array_header *src)
+{
+ int elt_size = dst->elt_size;
+
+ if (dst->nelts + src->nelts > dst->nalloc) {
+ int new_size = (dst->nalloc <= 0) ? 1 : dst->nalloc * 2;
+ char *new_data;
+
+ 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, const 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, const 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,
+ const array_header *first,
+ const 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, const table *t) {
+ return copy_array (p, t);
+}
+
+void clear_table (table *t)
+{
+ t->nelts = 0;
+}
+
+array_header *table_elts (table *t) { return t; }
+
+char *table_get (const table *t, const 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)
+{
+ register int i, j, k;
+ table_entry *elts = (table_entry *)t->elts;
+ int done = 0;
+
+ for (i = 0; i < t->nelts;) {
+ if (!strcasecmp (elts[i].key, key)) {
+ if (!done) {
+ elts[i].val = pstrdup(t->pool, val);
+ done = 1;
+ ++i;
+ }
+ else { /* delete an extraneous element */
+ for (j = i, k = i + 1; k < t->nelts; ++j, ++k) {
+ elts[j].key = elts[k].key;
+ elts[j].val = elts[k].val;
+ }
+ --t->nelts;
+ }
+ }
+ else {
+ ++i;
+ }
+ }
+
+ if (!done) {
+ elts = (table_entry *)push_array(t);
+ elts->key = pstrdup (t->pool, key);
+ elts->val = pstrdup (t->pool, val);
+ }
+}
+
+void table_unset( table *t, const char *key )
+{
+ register int i, j, k;
+ table_entry *elts = (table_entry *)t->elts;
+
+ for (i = 0; i < t->nelts;) {
+ if (!strcasecmp (elts[i].key, key)) {
+
+ /* found an 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*...
+ */
+ for (j = i, k = i + 1; k < t->nelts; ++j, ++k) {
+ elts[j].key = elts[k].key;
+ elts[j].val = elts[k].val;
+ }
+ --t->nelts;
+ }
+ else {
+ ++i;
+ }
+ }
+}
+
+void table_merge (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 = 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, const char *key, const 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, const table *overlay, const table *base)
+{
+ return append_arrays (p, overlay, base);
+}
+
+/* And now for something completely abstract ...
+ *
+ * For each key value given as a vararg:
+ * run the function pointed to as
+ * int comp(void *r, char *key, char *value);
+ * on each valid key-value pair in the table t that matches the vararg key,
+ * or once for every valid key-value pair if the vararg list is empty,
+ * until the function returns false (0) or we finish the table.
+ *
+ * Note that we restart the traversal for each vararg, which means that
+ * duplicate varargs will result in multiple executions of the function
+ * for each matching key. Note also that if the vararg list is empty,
+ * only one traversal will be made and will cut short if comp returns 0.
+ *
+ * Note that the table_get and table_merge functions assume that each key in
+ * the table is unique (i.e., no multiple entries with the same key). This
+ * function does not make that assumption, since it (unfortunately) isn't
+ * true for some of Apache's tables.
+ *
+ * Note that rec is simply passed-on to the comp function, so that the
+ * caller can pass additional info for the task.
+ */
+void table_do (int (*comp)(void *, const char *, const char *), void *rec,
+ const table *t, ...)
+{
+ va_list vp;
+ char *argp;
+ table_entry *elts = (table_entry *)t->elts;
+ int rv, i;
+
+ va_start(vp, t);
+
+ argp = va_arg(vp, char *);
+
+ do {
+ for (rv = 1, i = 0; rv && (i < t->nelts); ++i) {
+ if (elts[i].key && (!argp || !strcasecmp(elts[i].key, argp))) {
+ rv = (*comp)(rec, elts[i].key, elts[i].val);
+ }
+ }
+ } while (argp && ((argp = va_arg(vp, char *)) != NULL));
+
+ va_end(vp);
+}
+
+/*****************************************************************
+ *
+ * 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);
+}
+
+void kill_cleanups_for_fd(pool *p,int fd)
+ {
+ kill_cleanup(p,(void *)fd,fd_cleanup);
+ }
+
+int popenf(pool *a, const char *name, int flg, int mode)
+{
+ int fd;
+ int save_errno;
+
+ block_alarms();
+ fd = open(name, flg, mode);
+ save_errno = errno;
+ if (fd >= 0) {
+ fd = ap_slack (fd, AP_SLACK_HIGH);
+ note_cleanups_for_fd (a, fd);
+ }
+ unblock_alarms();
+ errno = save_errno;
+ return fd;
+}
+
+int pclosef(pool *a, int fd)
+{
+ int res;
+ int save_errno;
+
+ block_alarms();
+ res = close(fd);
+ save_errno = errno;
+ kill_cleanup(a, (void *)fd, fd_cleanup);
+ unblock_alarms();
+ errno = save_errno;
+ 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 (pool *p, FILE *fp) {
+ register_cleanup (p, (void *)fp, file_cleanup, file_child_cleanup);
+}
+
+FILE *pfopen(pool *a, const char *name, const char *mode)
+{
+ FILE *fd = NULL;
+ int baseFlag, desc;
+
+ block_alarms();
+
+ if (*mode == 'a') {
+ /* Work around faulty implementations of fopen */
+ baseFlag = (*(mode+1) == '+') ? O_RDWR : O_WRONLY;
+ desc = open(name, baseFlag | O_APPEND | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ if (desc >= 0) {
+ desc = ap_slack(desc, AP_SLACK_LOW);
+ fd = fdopen(desc, mode);
+ }
+ } else {
+ fd = fopen(name, mode);
+ }
+
+ if (fd != NULL) note_cleanups_for_file (a, fd);
+ unblock_alarms();
+ return fd;
+}
+
+FILE *pfdopen(pool *a,int fd, const 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(pool *a, FILE *fd)
+{
+ int res;
+
+ block_alarms();
+ res = fclose(fd);
+ kill_cleanup(a, (void *)fd, file_cleanup);
+ unblock_alarms();
+ return res;
+}
+
+/*
+ * Here's a pool-based interface to POSIX regex's regcomp().
+ * Note that we return regex_t instead of being passed one.
+ * The reason is that if you use an already-used regex_t structure,
+ * the memory that you've already allocated gets forgotten, and
+ * regfree() doesn't clear it. So we don't allow it.
+ */
+
+static void regex_cleanup (void *preg) { regfree ((regex_t *)preg); }
+
+regex_t *pregcomp(pool *p, const char *pattern, int cflags) {
+ regex_t *preg = palloc(p, sizeof(regex_t));
+
+ if (regcomp(preg, pattern, cflags))
+ return NULL;
+
+ register_cleanup (p, (void *)preg, regex_cleanup, regex_cleanup);
+
+ return preg;
+}
+
+
+void pregfree(pool *p, regex_t *reg)
+{
+ block_alarms();
+ regfree (reg);
+ kill_cleanup (p, (void *)reg, regex_cleanup);
+ unblock_alarms();
+}
+
+/*****************************************************************
+ *
+ * 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_err (pool *p, void (*func)(void *), void *data,
+ enum kill_conditions kill_how,
+ FILE **pipe_in, FILE **pipe_out, FILE **pipe_err)
+{
+ int pid;
+ int in_fds[2];
+ int out_fds[2];
+ int err_fds[2];
+ int save_errno;
+
+ block_alarms();
+
+ if (pipe_in && pipe (in_fds) < 0)
+ {
+ save_errno = errno;
+ unblock_alarms();
+ errno = save_errno;
+ return 0;
+ }
+
+ if (pipe_out && pipe (out_fds) < 0) {
+ save_errno = errno;
+ if (pipe_in) {
+ close (in_fds[0]); close (in_fds[1]);
+ }
+ unblock_alarms();
+ errno = save_errno;
+ return 0;
+ }
+
+ if (pipe_err && pipe (err_fds) < 0) {
+ save_errno = errno;
+ if (pipe_in) {
+ close (in_fds[0]); close (in_fds[1]);
+ }
+ if (pipe_out) {
+ close (out_fds[0]); close (out_fds[1]);
+ }
+ unblock_alarms();
+ errno = save_errno;
+ return 0;
+ }
+
+ if ((pid = fork()) < 0) {
+ save_errno = errno;
+ if (pipe_in) {
+ close (in_fds[0]); close (in_fds[1]);
+ }
+ if (pipe_out) {
+ close (out_fds[0]); close (out_fds[1]);
+ }
+ if (pipe_err) {
+ close (err_fds[0]); close (err_fds[1]);
+ }
+ unblock_alarms();
+ errno = save_errno;
+ 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]);
+ }
+
+ if (pipe_err) {
+ close (err_fds[0]);
+ dup2 (err_fds[1], STDERR_FILENO);
+ close (err_fds[1]);
+ }
+
+ /* 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);
+ }
+
+ if (pipe_err) {
+ close (err_fds[1]);
+#ifdef __EMX__
+ /* Need binary mode set for OS/2. */
+ *pipe_err = fdopen (err_fds[0], "rb");
+#else
+ *pipe_err = fdopen (err_fds[0], "r");
+#endif
+
+ if (*pipe_err) note_cleanups_for_file (p, *pipe_err);
+ }
+
+ 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);
+ }
+}
+
diff --git a/usr.sbin/httpd/src/alloc.h b/usr.sbin/httpd/src/alloc.h
new file mode 100644
index 00000000000..858bf2cb549
--- /dev/null
+++ b/usr.sbin/httpd/src/alloc.h
@@ -0,0 +1,252 @@
+
+/* ====================================================================
+ * Copyright (c) 1995-1997 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, const array_header *src);
+array_header *append_arrays (pool *, const array_header *,
+ const 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, const array_header *src);
+array_header *copy_array_hdr (pool *p, const 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, const table *);
+void clear_table (table *);
+char *table_get (const table *, const char *);
+void table_set (table *, const char *name, const char *val);
+void table_merge (table *, const char *name, const char *more_val);
+void table_unset (table *, const char *key);
+void table_add (table *, const char *name, const char *val);
+void table_do (int (*comp)(void *, const char *, const char *), void *rec,
+ const table *t, ...);
+
+table *overlay_tables (pool *p, const table *overlay, const table *base);
+
+array_header *table_elts (table *);
+
+#define is_empty_table(t) (((t) == NULL)||((t)->nelts == 0))
+
+/* 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 *));
+void run_cleanup (pool *p, void *data, void (*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 *, const char *name, const char *fmode);
+FILE *pfdopen(struct pool *, int fd, const char *fmode);
+int popenf(struct pool *, const char *name, int flg, int mode);
+
+void note_cleanups_for_file (pool *, FILE *);
+void note_cleanups_for_fd (pool *, int);
+void kill_cleanups_for_fd (pool *p, int fd);
+
+regex_t *pregcomp (pool *p, const char *pattern, int cflags);
+void pregfree (pool *p, regex_t *reg);
+
+/* 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_err (pool *, void (*)(void *), void *,
+ enum kill_conditions, FILE **pipe_in, FILE **pipe_out,
+ FILE **pipe_err);
+#define spawn_child(p,f,v,k,in,out) spawn_child_err(p,f,v,k,in,out,NULL)
+
+/* magic numbers --- min free bytes to consider a free pool block useable,
+ * and the min amount to allocate if we have to go to malloc() */
+
+#define BLOCK_MINFREE 4096
+#define BLOCK_MINALLOC 8192
+
+/* Finally, some accounting */
+
+long bytes_in_pool(pool *p);
+long bytes_in_free_blocks();
diff --git a/usr.sbin/httpd/src/buff.c b/usr.sbin/httpd/src/buff.c
new file mode 100644
index 00000000000..48872043359
--- /dev/null
+++ b/usr.sbin/httpd/src/buff.c
@@ -0,0 +1,1011 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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 "conf.h"
+#include "alloc.h"
+#include "buff.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#ifndef NO_UNISTD_H
+#include <unistd.h>
+#endif
+#ifndef NO_WRITEV
+#include <sys/types.h>
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_BSTRING_H
+#include <bstring.h> /* for IRIX, FD_SET calls bzero() */
+#endif
+
+#define DEFAULT_BUFSIZE (4096)
+/* This must be enough to represent (DEFAULT_BUFSIZE - 3) in hex,
+ * plus two extra characters.
+ */
+#define CHUNK_HEADER_SIZE (5)
+
+/*
+ * 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)
+{
+ int errsave = errno; /* Save errno to prevent overwriting it below */
+
+ if (err == B_RD)
+ fb->flags |= B_RDERR;
+ else
+ fb->flags |= B_WRERR;
+ if (fb->error != NULL) (*fb->error)(fb, err, fb->error_data);
+
+ errno = errsave;
+}
+
+/* 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;
+
+ /* overallocate so that we can put a chunk trailer of CRLF into this
+ * buffer */
+ if (flags & B_WR) fb->outbase = palloc(p, fb->bufsiz + 2);
+ else fb->outbase = NULL;
+
+ fb->inptr = fb->inbase;
+
+ fb->incnt = 0;
+ fb->outcnt = 0;
+ fb->outchunk = -1;
+ 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;
+}
+
+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;
+ }
+}
+
+
+static int bflush_core(BUFF *fb);
+
+/*
+ * Start chunked encoding.
+ *
+ * Note that in order for bputc() to be an efficient macro we have to
+ * guarantee that start_chunk() has always been called on the buffer before we
+ * leave any routine in this file. Said another way, if a routine here uses
+ * end_chunk() and writes something on the wire, then it has to call
+ * start_chunk() or set an error condition before returning.
+ */
+static void
+start_chunk( BUFF *fb )
+{
+ if (fb->outchunk != -1) {
+ /* already chunking */
+ return;
+ }
+ if (!(fb->flags & B_WR) || (fb->flags & (B_WRERR|B_EOUT))) {
+ /* unbuffered writes */
+ return;
+ }
+
+ /* we need at least the header_len + at least 1 data byte
+ * remember that we've overallocated fb->outbase so that we can always
+ * fit the two byte CRLF trailer
+ */
+ if( fb->bufsiz - fb->outcnt < CHUNK_HEADER_SIZE + 1 ) {
+ bflush_core(fb);
+ }
+ /* assume there's enough space now */
+ fb->outchunk = fb->outcnt;
+ fb->outcnt += CHUNK_HEADER_SIZE;
+}
+
+
+/*
+ * end a chunk -- tweak the chunk_header from start_chunk, and add a trailer
+ */
+static void
+end_chunk( BUFF *fb )
+{
+ int i;
+ char *strp;
+
+ if( fb->outchunk == -1 ) {
+ /* not chunking */
+ return;
+ }
+
+ if( fb->outchunk + CHUNK_HEADER_SIZE == fb->outcnt ) {
+ /* nothing was written into this chunk, and we can't write a 0 size
+ * chunk because that signifies EOF, so just erase it
+ */
+ fb->outcnt = fb->outchunk;
+ fb->outchunk = -1;
+ return;
+ }
+
+ /* we know this will fit because of how we wrote it in start_chunk() */
+ i = ap_snprintf( (char *)&fb->outbase[fb->outchunk],
+ CHUNK_HEADER_SIZE,
+ "%x", fb->outcnt - fb->outchunk - CHUNK_HEADER_SIZE );
+
+ /* we may have to tack some trailing spaces onto the number we just wrote
+ * in case it was smaller than our estimated size. We've also written
+ * a \0 into the buffer with ap_snprintf so we might have to put a
+ * \r back in.
+ */
+ strp = &fb->outbase[fb->outchunk + i];
+ while (i < CHUNK_HEADER_SIZE - 2) {
+ *strp++ = ' ';
+ ++i;
+ }
+ *strp++ = '\015';
+ *strp = '\012';
+
+ /* tack on the trailing CRLF, we've reserved room for this */
+ fb->outbase[fb->outcnt++] = '\015';
+ fb->outbase[fb->outcnt++] = '\012';
+
+ fb->outchunk = -1;
+}
+
+
+/*
+ * Set a flag on (1) or off (0).
+ */
+int bsetflag(BUFF *fb, int flag, int value)
+{
+ if (value) {
+ fb->flags |= flag;
+ if( flag & B_CHUNK ) {
+ start_chunk(fb);
+ }
+ } else {
+ fb->flags &= ~flag;
+ if( flag & B_CHUNK ) {
+ end_chunk(fb);
+ }
+ }
+ return value;
+}
+
+
+/*
+ * This is called instead of read() everywhere in here. It implements
+ * the B_SAFEREAD functionality -- which is to force a flush() if a read()
+ * would block. It also deals with the EINTR errno result from read().
+ * return code is like read() except EINTR is eliminated.
+ */
+static int
+saferead( BUFF *fb, void *buf, int nbyte )
+{
+ int rv;
+
+ if( fb->flags & B_SAFEREAD ) {
+ fd_set fds;
+ struct timeval tv;
+
+ /* test for a block */
+ do {
+ FD_ZERO( &fds );
+ FD_SET( fb->fd_in, &fds );
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+#ifdef SELECT_NEEDS_CAST
+ rv = select( fb->fd_in + 1, (int *)&fds, NULL, NULL, &tv );
+#else
+ rv = select( fb->fd_in + 1, &fds, NULL, NULL, &tv );
+#endif
+ } while( rv < 0 && errno == EINTR );
+ /* treat any error as if it would block as well */
+ if( rv != 1 ) {
+ bflush(fb);
+ }
+ }
+ do {
+ rv = read( fb->fd_in, buf, nbyte );
+ } while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT));
+ return( rv );
+}
+
+
+/*
+ * 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 */
+ i = saferead( fb, buf, nbyte );
+ 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 */
+ i = saferead( fb, buf, nbyte );
+ 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;
+ i = saferead( fb, fb->inptr, fb->bufsiz );
+ 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;
+ i = saferead( fb, fb->inptr, fb->bufsiz );
+ 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;
+}
+
+/*
+ * Looks at the stream fb and places the first character into buff
+ * without removing it from the stream buffer.
+ *
+ * Returns 1 on success, zero on end of transmission, or -1 on an error.
+ *
+ */
+int blookc(char *buff, BUFF *fb)
+{
+ int i;
+
+ *buff = '\0';
+
+ if (!(fb->flags & B_RD)) { /* Can't do blookc on an unbuffered stream */
+ errno = EINVAL;
+ return -1;
+ }
+ if (fb->flags & B_RDERR) return -1;
+
+ if (fb->incnt == 0) { /* no characters left in stream buffer */
+ fb->inptr = fb->inbase;
+ if (fb->flags & B_EOF)
+ return 0;
+
+ i = saferead( fb, fb->inptr, fb->bufsiz );
+
+ if (i == -1) {
+ if (errno != EAGAIN)
+ doerror(fb, B_RD);
+ return -1;
+ }
+ if (i == 0) {
+ fb->flags |= B_EOF;
+ return 0; /* EOF */
+ }
+ else fb->incnt = i;
+ }
+
+ *buff = fb->inptr[0];
+ return 1;
+}
+
+/*
+ * 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 = (unsigned char *)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;
+ i = saferead( fb, fb->inptr, fb->bufsiz );
+ 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];
+}
+
+
+/*
+ * When doing chunked encodings we really have to write everything in the
+ * chunk before proceeding onto anything else. This routine either writes
+ * nbytes and returns 0 or returns -1 indicating a failure.
+ *
+ * This is *seriously broken* if used on a non-blocking fd. It will poll.
+ */
+static int
+write_it_all(BUFF *fb, const void *buf, int nbyte)
+{
+ int i;
+
+ if (fb->flags & (B_WRERR|B_EOUT))
+ return -1;
+
+ while (nbyte > 0) {
+ i = write(fb->fd, buf, nbyte);
+ if (i < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ doerror (fb, B_WR);
+ return -1;
+ }
+ }
+ else {
+ nbyte -= i;
+ buf = i + (const char *)buf;
+ }
+ if (fb->flags & B_EOUT)
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * A hook to write() that deals with chunking. This is really a protocol-
+ * level issue, but we deal with it here because it's simpler; this is
+ * an interim solution pending a complete rewrite of all this stuff in
+ * 2.0, using something like sfio stacked disciplines or BSD's funopen().
+ */
+static int
+bcwrite(BUFF *fb, const void *buf, int nbyte)
+{
+ char chunksize[16]; /* Big enough for practically anything */
+ int rv;
+#ifndef NO_WRITEV
+ struct iovec vec[3];
+ int i;
+#endif
+
+ if (fb->flags & (B_WRERR|B_EOUT))
+ return -1;
+
+ if (!(fb->flags & B_CHUNK)) {
+ do rv = write(fb->fd, buf, nbyte);
+ while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT));
+ if (rv == -1) {
+ if (errno != EAGAIN) {
+ doerror (fb, B_WR);
+ }
+ return -1;
+ } else if (rv == 0) {
+ errno = EAGAIN;
+ }
+ return rv;
+ }
+
+#ifdef NO_WRITEV
+ /* without writev() this has poor performance, too bad */
+
+ ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012", nbyte);
+ if (write_it_all(fb, chunksize, strlen(chunksize)) == -1)
+ return -1;
+ if (write_it_all(fb, buf, nbyte) == -1)
+ return -1;
+ if (write_it_all(fb, "\015\012", 2) == -1)
+ return -1;
+ return nbyte;
+#else
+
+#define NVEC (sizeof(vec)/sizeof(vec[0]))
+
+ vec[0].iov_base = chunksize;
+ vec[0].iov_len = ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012",
+ nbyte);
+ vec[1].iov_base = (void *)buf; /* cast is to avoid const warning */
+ vec[1].iov_len = nbyte;
+ vec[2].iov_base = "\r\n";
+ vec[2].iov_len = 2;
+ /* while it's nice an easy to build the vector and crud, it's painful
+ * to deal with a partial writev()
+ */
+ for( i = 0; i < NVEC; ) {
+ do {
+ rv = writev( fb->fd, &vec[i], NVEC - i );
+ } while ((rv <= 0)
+ && !(fb->flags & B_EOUT)
+ && (errno == EINTR || errno == EAGAIN || rv == 0));
+ if (rv == -1) {
+ doerror (fb, B_WR);
+ return -1;
+ }
+ /* recalculate vec to deal with partial writes */
+ while (rv > 0) {
+ if( rv <= vec[i].iov_len ) {
+ vec[i].iov_base = (char *)vec[i].iov_base + rv;
+ vec[i].iov_len -= rv;
+ rv = 0;
+ if( vec[i].iov_len == 0 ) {
+ ++i;
+ }
+ } else {
+ rv -= vec[i].iov_len;
+ ++i;
+ }
+ }
+ if (fb->flags & B_EOUT)
+ return -1;
+ }
+ /* if we got here, we wrote it all */
+ return nbyte;
+#undef NVEC
+#endif
+}
+
+
+/*
+ * Write nbyte bytes.
+ * Only returns fewer than nbyte if an error ocurred.
+ * Returns -1 if no bytes were written before the error ocurred.
+ * It is worth noting that if an error occurs, the buffer is in an unknown
+ * state.
+ */
+int
+bwrite(BUFF *fb, const void *buf, int nbyte)
+{
+ int i, nwr, useable_bufsiz;
+
+ if (fb->flags & (B_WRERR|B_EOUT)) return -1;
+ if (nbyte == 0) return 0;
+
+ if (!(fb->flags & B_WR))
+ {
+/* unbuffered write -- have to use bcwrite since we aren't taking care
+ * of chunking any other way */
+ i = bcwrite(fb, buf, nbyte);
+ if (i <= 0) {
+ return -1;
+ }
+ fb->bytes_sent += i;
+ if (fb->flags & B_EOUT)
+ return -1;
+ else
+ 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 */
+ if (fb->flags & B_CHUNK) {
+ end_chunk(fb);
+ /* it is just too painful to try to re-cram the buffer while
+ * chunking
+ */
+ if (write_it_all(fb, fb->outbase, fb->outcnt) == -1) {
+ /* we cannot continue after a chunked error */
+ return -1;
+ }
+ fb->bytes_sent += fb->outcnt;
+ fb->outcnt = 0;
+ break;
+ }
+ do {
+ i = write(fb->fd, fb->outbase, fb->outcnt);
+ } while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT));
+ if (i <= 0) {
+ if (i == 0) /* return of 0 means non-blocking */
+ errno = EAGAIN;
+ if (nwr == 0) {
+ if (errno != EAGAIN) doerror(fb, B_WR);
+ return -1;
+ }
+ else return nwr;
+ }
+ fb->bytes_sent += i;
+
+ /* deal with a partial write */
+ 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;
+ }
+ else
+ fb->outcnt = 0;
+
+ if (fb->flags & B_EOUT)
+ return -1;
+ }
+/* we have emptied the file buffer. Now try to write the data from the
+ * original buffer until there is less than bufsiz left. Note that we
+ * use bcwrite() to do this for us, it will do the chunking so that
+ * we don't have to dink around building a chunk in our own buffer.
+ * Remember we may not be able to use the entire buffer if we're
+ * chunking.
+ */
+ useable_bufsiz = fb->bufsiz;
+ if (fb->flags & B_CHUNK) useable_bufsiz -= CHUNK_HEADER_SIZE;
+ while (nbyte >= useable_bufsiz)
+ {
+ i = bcwrite(fb, buf, nbyte);
+ if (i <= 0) {
+ if (nwr == 0) {
+ return -1;
+ }
+ else return nwr;
+ }
+ fb->bytes_sent += i;
+
+ buf = i + (const char *)buf;
+ nwr += i;
+ nbyte -= i;
+
+ if (fb->flags & B_EOUT)
+ return -1;
+ }
+/* copy what's left to the file buffer */
+ fb->outcnt = 0;
+ if( fb->flags & B_CHUNK ) start_chunk( fb );
+ if (nbyte > 0) memcpy(fb->outbase + fb->outcnt, buf, nbyte);
+ fb->outcnt += nbyte;
+ nwr += nbyte;
+ return nwr;
+}
+
+static int bflush_core(BUFF *fb)
+{
+ int i;
+
+ while (fb->outcnt > 0)
+ {
+ do {
+ i = write(fb->fd, fb->outbase, fb->outcnt);
+ } while ((i <= 0)
+ && !(fb->flags & B_EOUT)
+ && (errno == EINTR || errno == EAGAIN || i == 0));
+
+ if (i == 0) {
+ errno = EAGAIN;
+ return -1; /* return of 0 means B_EOUT and non-blocking */
+ }
+ else if (i < 0) {
+ if (errno != EAGAIN) doerror(fb, B_WR);
+ return -1;
+ }
+ fb->bytes_sent += i;
+
+ /*
+ * We should have written all the data, but 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;
+
+ /* If a soft timeout occurs while flushing, the handler should
+ * have set the buffer flag B_EOUT.
+ */
+ if (fb->flags & B_EOUT)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Flushes the buffered stream.
+ * Returns 0 on success or -1 on error
+ */
+int bflush(BUFF *fb)
+{
+ int ret;
+
+ if (!(fb->flags & B_WR) || (fb->flags & B_EOUT)) return 0;
+
+ if (fb->flags & B_WRERR) return -1;
+
+ if (fb->flags & B_CHUNK) end_chunk(fb);
+
+ ret = bflush_core(fb);
+
+ if (ret == 0 && (fb->flags & B_CHUNK)) {
+ start_chunk(fb);
+ }
+
+ return ret;
+}
+
+/*
+ * 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/usr.sbin/httpd/src/buff.h b/usr.sbin/httpd/src/buff.h
new file mode 100644
index 00000000000..aa1926723d3
--- /dev/null
+++ b/usr.sbin/httpd/src/buff.h
@@ -0,0 +1,139 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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)
+#ifdef B_ERROR /* in SVR4: sometimes defined in /usr/include/sys/buf.h */
+#undef B_ERROR /* avoid "warning: `B_ERROR' redefined" */
+#endif
+#define B_ERROR (48)
+/* Use chunked writing */
+#define B_CHUNK (64)
+/* bflush() if a read would block */
+#define B_SAFEREAD (128)
+
+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 outchunk; /* location of chunk header when chunking */
+ 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 bsetflag(BUFF *fb, int flag, int value);
+extern int bclose(BUFF *fb);
+
+#define bgetflag(fb, flag) ((fb)->flags & (flag))
+
+/* 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 blookc(char *buff, 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/usr.sbin/httpd/src/conf.h b/usr.sbin/httpd/src/conf.h
new file mode 100644
index 00000000000..03c90748dfe
--- /dev/null
+++ b/usr.sbin/httpd/src/conf.h
@@ -0,0 +1,763 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 PORTING for a listing of what they mean
+ */
+
+#if !defined(QNX) && !defined(MPE)
+#include <sys/param.h>
+#endif
+
+/* Define one of these according to your system. */
+#if defined(MPE)
+#include <sys/times.h>
+#define JMP_BUF sigjmp_buf
+#define NO_SETSID
+#define NO_KILLPG
+#define NO_WRITEV
+#define NEED_INITGROUPS
+#define NEED_STRCASECMP
+#define NEED_STRDUP
+#define NEED_STRNCASECMP
+extern void GETPRIVMODE();
+extern void GETUSERMODE();
+extern char *inet_ntoa();
+#define NO_SLACK
+
+#elif defined(SUNOS4)
+#define HAVE_GMTOFF
+#define HAVE_SYS_RESOURCE_H
+#undef NO_KILLPG
+#undef NO_SETSID
+char *crypt(const char *pw, const char *salt);
+char *mktemp(char *template);
+#define JMP_BUF sigjmp_buf
+#define HAVE_MMAP
+#include <sys/time.h>
+#define NEED_STRERROR
+typedef int rlim_t;
+#define memmove(a,b,c) bcopy(b,a,c)
+#define NO_LINGCLOSE
+#define USE_FLOCK_SERIALIZED_ACCEPT
+
+#elif defined(SOLARIS2)
+#undef HAVE_GMTOFF
+#define NO_KILLPG
+#undef NO_SETSID
+#define HAVE_SYS_RESOURCE_H
+#define bzero(a,b) memset(a,0,b)
+#define JMP_BUF sigjmp_buf
+#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_MMAP
+#define HAVE_CRYPT_H
+int gethostname(char *name, int namelen);
+
+#elif defined(IRIX)
+#undef HAVE_GMTOFF
+/* IRIX has killpg, but it's only in _BSD_COMPAT, so don't use it in case
+ * there's some weird conflict with non-BSD signals */
+#define NO_KILLPG
+#undef NO_SETSID
+#define JMP_BUF sigjmp_buf
+#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_SHMGET
+#define HAVE_CRYPT_H
+#define NO_LONG_DOUBLE
+#define HAVE_BSTRING_H
+#define NO_LINGCLOSE
+
+#elif defined(HIUX)
+#define HAVE_SYS_RESOURCE_H
+#undef HAVE_GMTOFF
+#define NO_KILLPG
+#undef NO_SETSID
+#ifndef _HIUX_SOURCE
+#define _HIUX_SOURCE
+#endif
+#define JMP_BUF sigjmp_buf
+#define HAVE_SHMGET
+#define SELECT_NEEDS_CAST
+
+#elif defined(HPUX) || defined(HPUX10)
+#define HAVE_SYS_RESOURCE_H
+#undef HAVE_GMTOFF
+#define NO_KILLPG
+#undef NO_SETSID
+#ifndef _HPUX_SOURCE
+#define _HPUX_SOURCE
+#endif
+#define JMP_BUF sigjmp_buf
+#define HAVE_SHMGET
+#ifndef HPUX10
+#define SELECT_NEEDS_CAST
+typedef int rlim_t;
+#endif
+
+#elif defined(AIX)
+#undef HAVE_GMTOFF
+#undef NO_KILLPG
+#undef NO_SETSID
+#define HAVE_SYS_SELECT_H
+#define JMP_BUF sigjmp_buf
+#ifndef __ps2__
+#define HAVE_MMAP
+#define DEFAULT_GROUP "nobody"
+#endif
+#define DEFAULT_USER "nobody"
+#ifdef NEED_RLIM_T
+typedef int rlim_t;
+#endif
+
+#elif defined(ULTRIX)
+#define HAVE_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 HAVE_GMTOFF
+#undef NO_KILLPG
+#undef NO_SETSID
+#define JMP_BUF sigjmp_buf
+#define HAVE_MMAP
+#define HAVE_CRYPT_H
+#define NO_LONG_DOUBLE
+
+#elif defined(PARAGON)
+#define HAVE_GMTOFF
+#undef NO_KILLPG
+#undef NO_SETSID
+#define JMP_BUF sigjmp_buf
+#define HAVE_MMAP
+#define HAVE_CRYPT_H
+#define NO_LONG_DOUBLE
+typedef int rlim_t;
+
+#elif defined(SEQUENT)
+#define HAVE_GMTOFF
+#undef NO_KILLPG
+#define NO_SETSID
+#define NEED_STRDUP
+#define tolower(c) (isupper(c) ? tolower(c) : c)
+
+#elif defined(NEXT)
+typedef unsigned short mode_t;
+#define HAVE_GMTOFF
+#undef NO_KILLPG
+#define NO_SETSID
+#define NEED_STRDUP
+#define NO_LINGCLOSE
+#define NO_UNISTD_H
+#undef _POSIX_SOURCE
+#ifndef FD_CLOEXEC
+#define FD_CLOEXEC 1
+#endif
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m)&(S_IFMT)) == (S_IFDIR))
+#endif
+#ifndef S_ISREG
+#define S_ISREG(m) (((m)&(S_IFMT)) == (S_IFREG))
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR 00100
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP 00040
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 00010
+#endif
+#ifndef S_IROTH
+#define S_IROTH 00004
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 00001
+#endif
+#ifndef S_IRUSR
+#define S_IRUSR S_IREAD
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR S_IWRITE
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 000020
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 000002
+#ifndef rlim_t
+typedef int rlim_t;
+#endif
+typedef u_long n_long;
+#endif
+
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#define waitpid(a,b,c) wait4((a) == -1 ? 0 : (a),(union wait *)(b),c,NULL)
+typedef int pid_t;
+#define JMP_BUF jmp_buf
+#define USE_LONGJMP
+#define NO_USE_SIGACTION
+
+#elif defined(LINUX)
+#if LINUX > 1
+#define HAVE_SHMGET
+#define HAVE_SYS_RESOURCE_H
+typedef int rlim_t;
+#endif
+#define USE_FCNTL_SERIALIZED_ACCEPT
+#undef HAVE_GMTOFF
+#undef NO_KILLPG
+#undef NO_SETSID
+#undef NEED_STRDUP
+#define JMP_BUF sigjmp_buf
+#include <sys/time.h>
+
+#elif defined(SCO)
+#undef HAVE_GMTOFF
+#undef NO_KILLPG
+#undef NO_SETSID
+#define NEED_INITGROUPS
+#define NO_WRITEV
+#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. */
+#include <sys/time.h>
+
+#elif defined(SCO5)
+
+#define JMP_BUF sigjmp_buf
+#define SIGURG SIGUSR1
+#define HAVE_SYS_SELECT_H
+#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_MMAP
+#define HAVE_SYS_RESOURCE_H
+#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(AUX)
+/* These are to let -Wall compile more cleanly */
+extern int strcasecmp(const char *, const char *);
+extern int strncasecmp(const char *,const char *,unsigned);
+extern int set42sig(), getopt(), getpeername(), bzero();
+extern int listen(), bind(), socket(), getsockname();
+extern int accept(), gethostname(), connect(), lstat();
+extern int select(), killpg(), shutdown();
+extern int initgroups(), setsockopt();
+extern char *shmat();
+extern int shmctl();
+extern int shmget();
+extern char *sbrk();
+extern char *crypt();
+#include <sys/time.h>
+#undef HAVE_GMTOFF
+#undef NO_KILLPG
+#undef NO_SETSID
+#define NEED_STRDUP
+#define JMP_BUF sigjmp_buf
+/* fcntl() locking is expensive with NFS */
+#define USE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_SHMGET
+/*
+ * NOTE: If when you run Apache under A/UX and you get a warning
+ * that httpd couldn't move break, then the below value for
+ * MOVEBREAK (64megs) is too large for your setup. Try reducing
+ * to 0x2000000 which is still PLENTY of space. I doubt if
+ * even on heavy systems sbrk() would be called at all...
+ */
+#define MOVEBREAK 0x4000000
+#define NO_LINGCLOSE
+#define NO_SLACK
+
+#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
+/* A lot of SVR4 systems need this */
+#define USE_FCNTL_SERIALIZED_ACCEPT
+
+#elif defined(UW)
+#define NO_LINGCLOSE
+#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 HAVE_RESOURCE
+#define HAVE_MMAP
+#define HAVE_SHMGET
+#define HAVE_CRYPT_H
+#define HAVE_SYS_SELECT_H
+#define HAVE_SYS_RESOURCE_H
+#include <sys/time.h>
+#define _POSIX_SOURCE
+
+#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
+/* A lot of SVR4 systems need this */
+#define USE_FCNTL_SERIALIZED_ACCEPT
+
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#define HAVE_SYS_RESOURCE_H
+#define HAVE_GMTOFF
+#undef NO_KILLPG
+#undef NO_SETSID
+#define JMP_BUF sigjmp_buf
+#define DEFAULT_USER "nobody"
+#define DEFAULT_GROUP "nogroup"
+#define HAVE_MMAP
+#define USE_FLOCK_SERIALIZED_ACCEPT
+
+
+#elif defined(UTS21)
+#undef HAVE_GMTOFF
+#undef NO_KILLPG
+#define NO_SETSID
+#define NEED_WAITPID
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#define strftime(buf,bufsize,fmt,tm) ascftime(buf,fmt,tm)
+#include <sys/types.h>
+
+#elif defined(APOLLO)
+#undef HAVE_GMTOFF
+#undef NO_KILLPG
+#undef NO_SETSID
+
+#elif defined(__FreeBSD__) || defined(__bsdi__)
+#if defined(__FreeBSD__)
+#include <osreldate.h>
+#endif
+#define HAVE_SYS_RESOURCE_H
+#define HAVE_GMTOFF
+#undef NO_KILLPG
+#undef NO_SETSID
+#define JMP_BUF sigjmp_buf
+#define HAVE_MMAP
+#define DEFAULT_USER "nobody"
+#define DEFAULT_GROUP "nogroup"
+#if defined(__bsdi__) || \
+(defined(__FreeBSD_version) && (__FreeBSD_version < 220000))
+typedef quad_t rlim_t;
+#endif
+#define USE_FLOCK_SERIALIZED_ACCEPT
+
+#elif defined(QNX)
+#ifndef crypt
+char *crypt(const char *pw, const char *salt);
+#endif
+#ifndef initgroups
+int initgroups (char *, int);
+#endif
+#ifndef strncasecmp
+#define strncasecmp strnicmp
+#endif
+#undef NO_KILLPG
+#undef NO_SETSID
+#define NEED_INITGROUPS
+#define NEED_SELECT_H
+#define NEED_PROCESS_H
+#define HAVE_SYS_SELECT_H
+#include <unix.h>
+#define JMP_BUF sigjmp_buf
+
+#elif defined(LYNXOS)
+#undef NO_KILLPG
+#undef NO_SETSID
+#define NEED_STRCASECMP
+#define NEED_STRNCASECMP
+#define NEED_INITGROUPS
+#define JMP_BUF jmp_buf
+
+#elif defined(UXPDS)
+#undef NEED_STRCASECMP
+#undef NEED_STRNCASECMP
+#undef NEED_STRDUP
+#undef HAVE_GMTOFF
+#define NO_KILLPG
+#undef NO_SETSID
+#define HAVE_RESOURCE 1
+#define bzero(a,b) memset(a,0,b)
+#define JMP_BUF sigjmp_buf
+#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_MMAP
+#define HAVE_CRYPT_H
+
+#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
+#include <sys/time.h>
+#define MAXSOCKETS 4096
+#define HAVE_MMAP
+
+#elif defined(__MACHTEN__)
+typedef int rlim_t;
+#define JMP_BUF sigjmp_buf
+#undef NO_KILLPG
+#define NO_SETSID
+#define HAVE_GMTOFF
+#ifndef __MACHTEN_PPC__
+#ifndef __MACHTEN_68K__
+#define __MACHTEN_68K__
+#endif
+#define USE_FLOCK_SERIALIZED_ACCEPT
+#define NO_USE_SIGACTION
+#define USE_LONGJMP
+#undef NEED_STRDUP
+#else
+#define HAVE_SHMGET
+#define USE_FCNTL_SERIALIZED_ACCEPT
+#endif
+
+/* Convex OS v11 */
+#elif defined(CONVEXOS11)
+#undef HAVE_GMTOFF
+#undef NO_KILLPG
+#undef NO_SETSID
+#undef NEED_STRDUP
+#define HAVE_MMAP
+
+#define NO_TIMEZONE
+#include <stdio.h>
+#include <sys/types.h>
+#define JMP_BUF jmp_buf
+typedef int rlim_t;
+
+#elif defined(ISC)
+#include <net/errno.h>
+#define NO_KILLPG
+#undef NO_SETSID
+#define HAVE_SHMGET
+#define SIGURG SIGUSR1
+#define JMP_BUF sigjmp_buf
+#define USE_FCNTL_SERIALIZED_ACCEPT
+
+/* Unknown system - Edit these to match */
+#else
+#ifdef BSD
+#define HAVE_GMTOFF
+#else
+#undef HAVE_GMTOFF
+#endif
+/* NO_KILLPG is set on systems that don't have killpg */
+#undef NO_KILLPG
+/* NO_SETSID is set on systems that don't have setsid */
+#undef NO_SETSID
+/* NEED_STRDUP is set on stupid systems that don't have strdup. */
+#undef NEED_STRDUP
+#endif
+
+/* Do we have sys/resource.h; assume that BSD does. */
+#ifndef HAVE_SYS_RESOURCE_H
+#ifdef BSD
+#define HAVE_SYS_RESOURCE_H
+#endif
+#endif /* HAVE_SYS_RESOURCE_H */
+
+/*
+ * 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>
+#include <stdarg.h>
+/*
+ * We use snprintf() to avoid overflows, but we include
+ * our own version (ap_snprintf). Allow for people to use their
+ * snprintf() if they want
+ */
+#ifdef HAVE_SNPRINTF
+#define ap_snprintf snprintf
+#define ap_vsnprintf vsnprintf
+#else
+int ap_snprintf(char *buf, size_t len, const char *format,...);
+int ap_vsnprintf(char *buf, size_t len, const char *format, va_list ap);
+#endif
+
+#if !defined(NEXT)
+#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>
+#ifndef MPE
+#include <sys/file.h>
+#endif
+#include <sys/socket.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <ctype.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#ifndef MPE
+#include <arpa/inet.h> /* for inet_ntoa */
+#endif
+#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>
+#if !defined(QNX) && !defined(CONVEXOS11) && !defined(NEXT)
+#include <memory.h>
+#endif
+#ifdef NEED_PROCESS_H
+#include <process.h>
+#endif
+
+#include <regex.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#ifdef SUNOS4
+int getrlimit( int, struct rlimit *);
+int setrlimit( int, struct rlimit *);
+#endif
+#endif
+#ifdef HAVE_MMAP
+#ifndef __EMX__
+/* This file is not needed for OS/2 */
+#include <sys/mman.h>
+#endif
+#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
+#ifndef __EMX__
+/* Don't define this for OS/2 */
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long) -1)
+#endif
+
+/*
+ * Replace signal function with sigaction equivalent
+ */
+#ifndef NO_USE_SIGACTION
+typedef void Sigfunc(int);
+
+#if defined(SIG_IGN) && !defined(SIG_ERR)
+#define SIG_ERR ((Sigfunc *)-1)
+#endif
+
+/*
+ * For some strange reason, QNX defines signal to signal. Eliminate it.
+ */
+#ifdef signal
+#undef signal
+#endif
+#define signal(s,f) ap_signal(s,f)
+Sigfunc *signal(int signo, Sigfunc *func);
+#endif
+
+#include <setjmp.h>
+
+#if defined(USE_LONGJMP)
+#define ap_longjmp(x, y) longjmp((x), (y))
+#define ap_setjmp(x) setjmp(x)
+#else
+#define ap_longjmp(x, y) siglongjmp((x), (y))
+#define ap_setjmp(x) sigsetjmp((x), 1)
+#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);
+#ifndef NO_KILLPG
+int killpg(int, int);
+#endif
+int socket (int, int, int);
+int setsockopt (int, int, int, const char*, int);
+int listen (int, int);
+int bind (int, struct sockaddr *, int);
+int connect (int, struct sockaddr *, int);
+int accept (int, struct sockaddr *, int *);
+int shutdown (int, int);
+
+int getsockname (int s, struct sockaddr *name, int *namelen);
+int getpeername (int s, struct sockaddr *name, int *namelen);
+int gethostname (char *name, int namelen);
+void syslog (int, char *, ...);
+char *mktemp (char *);
+
+long vfprintf (FILE *, char *, va_list);
+
+#endif
diff --git a/usr.sbin/httpd/src/explain.c b/usr.sbin/httpd/src/explain.c
new file mode 100644
index 00000000000..6490c940522
--- /dev/null
+++ b/usr.sbin/httpd/src/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/usr.sbin/httpd/src/explain.h b/usr.sbin/httpd/src/explain.h
new file mode 100644
index 00000000000..5912502585e
--- /dev/null
+++ b/usr.sbin/httpd/src/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/usr.sbin/httpd/src/helpers/CutRule b/usr.sbin/httpd/src/helpers/CutRule
new file mode 100644
index 00000000000..740a3362c1b
--- /dev/null
+++ b/usr.sbin/httpd/src/helpers/CutRule
@@ -0,0 +1,7 @@
+# Helper script for Configure - cut a rule from Configuration.
+# note that there is a tab and a space in the character groups.
+# Map to lowercase to make tests easier
+
+egrep "^[ ]*Rule[ ]+$1[ ]*=" $2 | \
+awk 'BEGIN {FS="="}{print $2}' | \
+sed 's/[ ]//g' | tr "A-Z" "a-z"
diff --git a/usr.sbin/httpd/src/helpers/GuessOS b/usr.sbin/httpd/src/helpers/GuessOS
new file mode 100644
index 00000000000..0029797bbc5
--- /dev/null
+++ b/usr.sbin/httpd/src/helpers/GuessOS
@@ -0,0 +1,234 @@
+#!/bin/sh
+#
+# Simple OS/Platform guesser. Similar to config.guess but
+# much, much smaller. Since it was developed for use with
+# Apache, it follows under Apache's regular licensing
+# with one specific addition: Any changes or additions
+# to this script should be Emailed to the Apache
+# group (apache@apache.org) in general and to
+# Jim Jagielski (jim@jaguNET.com) in specific.
+#
+# Be as similar to the output of config.guess/config.sub
+# as possible.
+
+# First get uname entries that we use below
+
+MACHINE=`(uname -m) 2>/dev/null` || MACHINE="unknown"
+RELEASE=`(uname -r) 2>/dev/null` || RELEASE="unknown"
+SYSTEM=`(uname -s) 2>/dev/null` || SYSTEM="unknown"
+VERSION=`(uname -v) 2>/dev/null` || VERSION="unknown"
+
+
+# Now test for ISC and SCO, since it is has a braindamaged uname.
+#
+# We need to work around FreeBSD 1.1.5.1
+XREL=`uname -X 2>/dev/null | grep "^Release" | awk '{print $3}'`
+if [ "x$XREL" != "x" ]; then
+ if [ -f /etc/kconfig ]; then
+ case "$XREL" in
+ 4.0|4.1)
+ echo "${MACHINE}-whatever-isc4"; exit 0
+ ;;
+ esac
+ else
+ case "$XREL" in
+ 3.2v4.2)
+ echo "whatever-whatever-sco3"; exit 0
+ ;;
+ 3.2v5.0*)
+ echo "whatever-whatever-sco5"; exit 0
+ ;;
+ 4.2MP)
+ if [ "x$VERSION" = "x2.1.1" ]; then
+ echo "${MACHINE}-whatever-unixware211"; exit 0
+ elif [ "x$VERSION" = "x2.1.2" ]; then
+ echo "${MACHINE}-whatever-unixware212"; exit 0
+ else
+ echo "${MACHINE}-whatever-unixware2"; exit 0
+ fi
+ ;;
+ 4.2)
+ echo "whatever-whatever-unixware1"; exit 0
+ ;;
+ esac
+ fi
+fi
+# Now we simply scan though... In most cases, the SYSTEM info is enough
+#
+case "${SYSTEM}:${RELEASE}:${VERSION}:${MACHINE}" in
+ A/UX:*)
+ echo "m68k-apple-aux3"; exit 0
+ ;;
+
+ AIX:*)
+ echo "${MACHINE}-ibm-aix${VERSION}.${RELEASE}"; exit 0
+ ;;
+
+ dgux:*)
+ echo "${MACHINE}-dg-dgux"; exit 0
+ ;;
+
+ HI-UX:*)
+ echo "${MACHINE}-hi-hiux"; exit 0
+ ;;
+
+ HP-UX:*)
+ HPUXVER=`echo ${RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "$HPUXVER" in
+ 10.*)
+ echo "${MACHINE}-hp-hpux10."; exit 0
+ ;;
+ *)
+ echo "${MACHINE}-hp-hpux"; exit 0
+ ;;
+ esac
+ ;;
+
+ IRIX:*)
+ echo "${MACHINE}-sgi-irix"; exit 0
+ ;;
+
+ IRIX64:*)
+ echo "${MACHINE}-sgi-irix64"; exit 0
+ ;;
+
+ Linux:[2-9].*)
+ echo "${MACHINE}-whatever-linux2"; exit 0
+ ;;
+
+ Linux:1.*)
+ echo "${MACHINE}-whatever-linux1"; exit 0
+ ;;
+
+ LynxOS:*)
+ echo "${MACHINE}-lynx-lynxos"; exit 0
+ ;;
+
+ BSD/386:*:*:*486*|BSD/OS:*:*:*:*486*)
+ echo "i486-whatever-bsdi"; exit 0
+ ;;
+
+ BSD/386:*|BSD/OS:*)
+ echo "${MACHINE}-whatever-bsdi"; exit 0
+ ;;
+
+ FreeBSD:*:*:*486*)
+ echo "i486-whatever-freebsd"; exit 0
+ ;;
+
+ FreeBSD:*)
+ echo "${MACHINE}-whatever-freebsd"; exit 0
+ ;;
+
+ NetBSD:*:*:*486*)
+ echo "i486-whatever-netbsd"; exit 0
+ ;;
+
+ NetBSD:*)
+ echo "${MACHINE}-whatever-netbsd"; exit 0
+ ;;
+
+ OpenBSD:*)
+ echo "${MACHINE}-whatever-openbsd"; exit 0
+ ;;
+
+ OSF1:*:*:*alpha*)
+ echo "${MACHINE}-dec-osf"; exit 0
+ ;;
+
+ QNX:*)
+ case "$VERSION" in
+ 423)
+ echo "${MACHINE}-qssl-qnx32"
+ ;;
+ *)
+ echo "${MACHINE}-qssl-qnx"
+ ;;
+ esac
+ exit 0
+ ;;
+
+ Paragon*:*:*:*)
+ echo "i860-intel-osf1"; exit 0
+ ;;
+
+ SunOS:5.*)
+ echo "${MACHINE}-sun-solaris2"; exit 0
+ ;;
+
+ SunOS:*)
+ echo "${MACHINE}-sun-sunos4"; exit 0
+ ;;
+
+ UNIX_System_V:4.*:*)
+ echo "${MACHINE}-whatever-sysv4"; exit 0
+ ;;
+
+ *:4*:R4*:m88k)
+ echo "${MACHINE}-whatever-sysv4"; exit 0
+ ;;
+
+ DYNIX/ptx:4*:*)
+ echo "${MACHINE}-whatever-sysv4"; exit 0
+ ;;
+
+ *:4.0:3.0:3[34]?? | *:4.0:3.0:3[34]??,*)
+ echo "i486-ncr-sysv4"; exit 0
+ ;;
+
+ ULTRIX:*)
+ echo "${MACHINE}-unknown-ultrix"; exit 0
+ ;;
+
+ SINIX-?:* | ReliantUNIX-?:*)
+ echo "${MACHINE}-sni-sysv4"; exit 0
+ ;;
+
+ machten:*)
+ echo "${MACHINE}-tenon-${SYSTEM}"; exit 0;
+ ;;
+
+ library:*)
+ echo "${MACHINE}-ncr-sysv4"; exit 0
+ ;;
+
+ ConvexOS:*:11.*:*)
+ echo "${MACHINE}-v11-${SYSTEM}"; exit 0;
+ ;;
+
+ UNIX_SV:*:*:maxion)
+ echo "${MACHINE}-ccur-sysv4"; exit 0;
+ ;;
+
+ NonStop-UX:4.[02]*:[BC]*:*)
+ echo "${MACHINE}-tandem-sysv4"; exit 0;
+ ;;
+
+esac
+
+#
+# Ugg. These are all we can determine by what we know about
+# the output of uname. Be more creative:
+#
+
+# Do the Apollo stuff first. Here, we just simply assume
+# that the existance of the /usr/apollo directory is proof
+# enough
+if [ -d /usr/apollo ]; then
+ echo "whatever-apollo-whatever"
+ exit 0
+fi
+
+# Now NeXT
+ISNEXT=`hostinfo 2>/dev/null`
+case "$ISNEXT" in
+ *NeXT*)
+ echo "whatever-next-nextstep"; exit 0
+ ;;
+esac
+
+# At this point we gone through all the one's
+# we know of: Punt
+
+echo "${MACHINE}-whatever-${SYSTEM}|${RELEASE}|${VERSION}"
+exit 0
diff --git a/usr.sbin/httpd/src/helpers/PrintPath b/usr.sbin/httpd/src/helpers/PrintPath
new file mode 100644
index 00000000000..9ec0e0c37f0
--- /dev/null
+++ b/usr.sbin/httpd/src/helpers/PrintPath
@@ -0,0 +1,46 @@
+#!/bin/sh
+# Look for $1 somewhere in $PATH
+# will print out the full pathname unless
+# called with the '-s' option
+#
+# We do some funny stuff to check to see
+# if test/[] knows about -x
+#
+testfile="pp.t.$$"
+
+cat > $testfile <<ENDTEST
+#!/bin/sh
+if [ -x / ] || [ -x /bin ] || [ -x /bin/ls ]; then
+ exit 0
+fi
+exit 1
+ENDTEST
+
+if `/bin/sh $testfile 2>/dev/null`; then
+ test_exec_flag="-x"
+else
+ test_exec_flag="-r"
+fi
+rm -f $testfile
+
+if [ "x$1" = "x-s" ]; then
+ shift
+else
+ echo="yes"
+fi
+
+for path in `echo $PATH |
+ sed 's/^:/.:/
+ s/::/:.:/g
+ s/:$/:./
+ s/:/ /g' `
+do
+ if [ $test_exec_flag $path/$1 ] && [ ! -d $path/$1 ]; then
+ if [ "$echo" = "yes" ]; then
+ echo $path/$1
+ fi
+ exit 0
+ fi
+done
+exit 1
+
diff --git a/usr.sbin/httpd/src/htconf.634 b/usr.sbin/httpd/src/htconf.634
new file mode 100644
index 00000000000..e8e7e53fdb7
--- /dev/null
+++ b/usr.sbin/httpd/src/htconf.634
@@ -0,0 +1,25 @@
+EXTRA_CFLAGS=
+EXTRA_LFLAGS=
+EXTRA_LIBS=
+EXTRA_INCLUDES=
+##Rule:STATUS=yes
+##Rule:SOCKS4=no
+##Rule:IRIXNIS=no
+##Rule:WANTHSREGEX=default
+Module env_module mod_env.o
+Module config_log_module mod_log_config.o
+Module mime_module mod_mime.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 asis_module mod_asis.o
+Module imap_module mod_imap.o
+Module action_module mod_actions.o
+Module userdir_module mod_userdir.o
+Module alias_module mod_alias.o
+Module access_module mod_access.o
+Module auth_module mod_auth.o
+Module db_auth_module mod_auth_db.o
+Module dbm_auth_module mod_auth_dbm.o
+Module browser_module mod_browser.o
diff --git a/usr.sbin/httpd/src/http_bprintf.c b/usr.sbin/httpd/src/http_bprintf.c
new file mode 100644
index 00000000000..3035b759ccf
--- /dev/null
+++ b/usr.sbin/httpd/src/http_bprintf.c
@@ -0,0 +1,602 @@
+/*
+ * 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 <assert.h>
+#include <math.h>
+#include "conf.h"
+#include "alloc.h"
+#include "buff.h"
+
+#if !defined(max)
+#define max(a,b) (a > b ? a : b)
+#endif
+
+#ifdef NO_LONG_DOUBLE
+#define LONG_DOUBLE double
+#else
+#define LONG_DOUBLE long double
+#endif
+
+#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
+ */
+#define NULL_STRING "(null)"
+ /*
+ * String displayed if given a NULL pointer.
+ */
+
+/*
+ * 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 (charPtrArg == NULL) {
+ charPtrArg = NULL_STRING;
+ };
+ if(precision == -1)
+ buffReqd = strlen(charPtrArg);
+ else
+ {
+ p = memchr(charPtrArg, '\0', precision);
+ if (p == NULL)
+ buffReqd = precision;
+ else
+ buffReqd = 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 *);
+ if (buffPtr == NULL) {
+ buffPtr = NULL_STRING;
+ };
+ 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/usr.sbin/httpd/src/http_conf_globals.h b/usr.sbin/httpd/src/http_conf_globals.h
new file mode 100644
index 00000000000..3a60e7db77b
--- /dev/null
+++ b/usr.sbin/httpd/src/http_conf_globals.h
@@ -0,0 +1,86 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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;
+#ifdef MULTIPLE_GROUPS
+extern gid_t group_id_list[NGROUPS_MAX];
+#endif
+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 int suexec_enabled;
+
+extern char *pid_fname;
+extern char *scoreboard_fname;
+extern char *lock_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/usr.sbin/httpd/src/http_config.c b/usr.sbin/httpd/src/http_config.c
new file mode 100644
index 00000000000..d39af86994c
--- /dev/null
+++ b/usr.sbin/httpd/src/http_config.c
@@ -0,0 +1,1213 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 Apache... commands which are part of the
+ * server core can now be found next door in "http_core.c". Now contains
+ * general command loop, and functions which do bookkeeping for the new
+ * Apache config stuff (modules and configuration vectors).
+ *
+ * rst
+ *
+ */
+
+#define CORE_PRIVATE
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h" /* for errors in parse_htaccess */
+#include "http_request.h" /* for default_handler (see invoke_handler) */
+#include "http_conf_globals.h" /* Sigh... */
+#include "explain.h"
+
+DEF_Explain
+
+/****************************************************************
+ *
+ * We begin with the functions which deal with the linked list
+ * of modules which control just about all of the server operation.
+ */
+
+/* num_modules is the number of currently active modules. */
+static int num_modules = 0;
+/* total_modules is the number of modules linked in. */
+static int total_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*) * total_modules);
+ return (void *)conf_vector;
+}
+
+void *
+create_default_per_dir_config (pool *p)
+{
+ void **conf_vector = (void **)pcalloc(p, sizeof(void*) * (total_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*) * total_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*) * (total_modules+DYNAMIC_MODULE_LIMIT));
+ module *modp;
+
+ for (modp = top_module; modp; modp = modp->next) {
+ if (modp->create_server_config)
+ conf_vector[modp->module_index]=(*modp->create_server_config)(p,s);
+ }
+
+ return (void*)conf_vector;
+}
+
+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);
+}
+
+#ifdef EXPLAIN
+
+struct
+ {
+ int offset;
+ char *method;
+ } aMethods[]=
+ {
+#define m(meth) { XtOffsetOf(module,meth),#meth }
+ m(translate_handler),
+ m(check_user_id),
+ m(auth_checker),
+ m(type_checker),
+ m(fixer_upper),
+ m(logger),
+ { -1,"?" },
+#undef m
+ };
+
+char *ShowMethod(module *modp,int offset)
+ {
+ int n;
+ static char buf[200];
+
+ for(n=0 ; aMethods[n].offset >= 0 ; ++n)
+ if(aMethods[n].offset == offset)
+ break;
+ ap_snprintf(buf, sizeof(buf), "%s:%s",modp->name,aMethods[n].method);
+ return buf;
+ }
+#else
+#define ShowMethod(modp,offset)
+#endif
+
+/****************************************************************
+ *
+ * Dispatch through the modules to find handlers for various phases
+ * of request handling. These are invoked by http_request.c to actually
+ * do the dirty work of slogging through the module structures.
+ */
+
+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;
+
+ Explain1("Run %s",ShowMethod(modp,offset));
+ result = (*mod_handler)(r);
+
+ Explain2("%s returned %d",ShowMethod(modp,offset),result);
+ 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);
+}
+
+int header_parse (request_rec *r) {
+ return run_method (r, XtOffsetOf (module, header_parser), 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, *p;
+
+ if ((p = strchr(content_type, ';')) != NULL) { /* MIME type arguments */
+ while (p > content_type && p[-1] == ' ') --p; /* strip trailing spaces */
+ content_type = pstrndup(r->pool, content_type, p - content_type);
+ }
+ 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...
+ */
+
+ /* At some point, we may want to offer back-compatibility for
+ * loading modules that are for older versions of Apache. For now,
+ * though, we don't.
+ */
+
+ if (m->version != MODULE_MAGIC_NUMBER) {
+ fprintf(stderr, "httpd: module \"%s\" is not compatible with this "
+ "version of Apache.\n", m->name);
+ fprintf(stderr, "Please contact the author for the correct version.\n");
+ exit(1);
+ }
+
+ if (m->next == NULL) {
+ m->next = top_module;
+ top_module = m;
+ }
+ if (m->module_index == -1) {
+ m->module_index = num_modules++;
+ }
+}
+
+void setup_prelinked_modules ()
+{
+ extern module *prelinked_modules[], *preloaded_modules[];
+ module **m;
+
+ /* First, set all module indices, and init total_modules. */
+ total_modules = 0;
+ for (m = preloaded_modules; *m; ++m, ++total_modules) {
+ (*m)->module_index = total_modules;
+ }
+
+ for (m = prelinked_modules; *m; ++m) {
+ add_module (*m);
+ }
+}
+
+const char *find_module_name (module *m)
+{
+ return m->name;
+}
+
+module *find_linked_module (const char *name)
+{
+ module *modp;
+
+ for (modp = top_module; modp; modp = modp->next) {
+ if (strcmp(modp->name, name) == 0)
+ return modp;
+ }
+ return NULL;
+}
+
+/* Add a named module. Returns 1 if module found, 0 otherwise. */
+int add_named_module (const char *name)
+{
+ extern module *preloaded_modules[];
+ module *modp;
+ int i = 0;
+
+ for (modp = preloaded_modules[i]; modp; modp = preloaded_modules[++i]) {
+ if (strcmp(modp->name, name) == 0) {
+ /* Only add modules that are not already enabled. */
+ if (modp->next == NULL) {
+ add_module(modp);
+ }
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Clear the internal list of modules, in preparation for starting over. */
+void clear_module_list ()
+{
+ module **m = &top_module;
+ module **next_m;
+
+ while (*m) {
+ next_m = &((*m)->next);
+ *m = NULL;
+ m = next_m;
+ }
+
+ num_modules = 0;
+
+ /* This is required; so we add it always. */
+ add_named_module ("http_core.c");
+}
+
+/*****************************************************************
+ *
+ * Resource, access, and .htaccess config files now parsed by a common
+ * command loop.
+ *
+ * Let's begin with the basics; parsing the line and
+ * invoking the function...
+ */
+
+const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms, void *mconfig,
+ const char *args)
+{
+ char *w, *w2, *w3;
+ const char *errmsg;
+
+ if ((parms->override & cmd->req_override) == 0)
+ return pstrcat (parms->pool, cmd->name, " not allowed here", NULL);
+
+ parms->info = cmd->cmd_data;
+ parms->cmd = cmd;
+
+ switch (cmd->args_how) {
+ case RAW_ARGS:
+ return (*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 TAKE12:
+
+ w = getword_conf (parms->pool, &args);
+ w2 = getword_conf (parms->pool, &args);
+
+ if (*w == '\0' || *args != 0)
+ return pstrcat (parms->pool, cmd->name, " takes 1-2 arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return (*cmd->func) (parms, mconfig, w, *w2 ? w2 : NULL);
+
+ case TAKE3:
+
+ w = getword_conf (parms->pool, &args);
+ w2 = getword_conf (parms->pool, &args);
+ w3 = getword_conf (parms->pool, &args);
+
+ if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0)
+ return pstrcat (parms->pool, cmd->name, " takes three arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return (*cmd->func) (parms, mconfig, w, w2, w3);
+
+ case TAKE23:
+
+ w = getword_conf (parms->pool, &args);
+ w2 = getword_conf (parms->pool, &args);
+ w3 = *args ? getword_conf (parms->pool, &args) : NULL;
+
+ if (*w == '\0' || *w2 == '\0' || *args != 0)
+ return pstrcat (parms->pool, cmd->name, " takes two or three arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return (*cmd->func) (parms, mconfig, w, w2, w3);
+
+ case TAKE123:
+
+ w = getword_conf (parms->pool, &args);
+ w2 = *args ? getword_conf (parms->pool, &args) : NULL;
+ w3 = *args ? getword_conf (parms->pool, &args) : NULL;
+
+ if (*w == '\0' || *args != 0)
+ return pstrcat (parms->pool, cmd->name, " takes one, two or three arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return (*cmd->func) (parms, mconfig, w, w2, w3);
+
+ case TAKE13:
+
+ w = getword_conf (parms->pool, &args);
+ w2 = *args ? getword_conf (parms->pool, &args) : NULL;
+ w3 = *args ? getword_conf (parms->pool, &args) : NULL;
+
+ if (*w == '\0' || (*w2 && !w3) || *args != 0)
+ return pstrcat (parms->pool, cmd->name, " takes one or three arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return (*cmd->func) (parms, mconfig, w, w2, w3);
+
+ 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);
+ }
+}
+
+const command_rec *find_command (const char *name, const command_rec *cmds)
+{
+ while (cmds->name)
+ if (!strcasecmp (name, cmds->name))
+ return cmds;
+ else
+ ++cmds;
+
+ return NULL;
+}
+
+const command_rec *find_command_in_modules (const char *cmd_name, module **mod)
+{
+ const command_rec *cmdp;
+ module *modp;
+
+ for (modp = *mod; modp; modp = modp->next)
+ if (modp->cmds && (cmdp = find_command (cmd_name, modp->cmds))) {
+ *mod = modp;
+ return cmdp;
+ }
+
+ return NULL;
+}
+
+const char *handle_command (cmd_parms *parms, void *config, const char *l)
+{
+ const char *args, *cmd_name, *retval;
+ const command_rec *cmd;
+ module *mod = top_module;
+
+ ++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;
+
+ do {
+ 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);
+ }
+
+ retval = invoke_cmd (cmd, parms, mconfig, args);
+ mod = mod->next; /* Next time around, skip this one */
+ }
+ } while (retval && !strcmp(retval, DECLINE_CMD));
+
+ return retval;
+}
+
+const char *srm_command_loop (cmd_parms *parms, void *config)
+{
+ char l[MAX_STRING_LEN];
+
+ while (!(cfg_getline (l, MAX_STRING_LEN, parms->infile))) {
+ const char *errmsg = handle_command (parms, config, l);
+ if (errmsg) return errmsg;
+ }
+
+ return NULL;
+}
+
+/*
+ * Generic command functions...
+ */
+
+const 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;
+}
+
+const char *set_flag_slot (cmd_parms *cmd, char *struct_ptr, int arg)
+{
+ /* This one's pretty generic too... */
+
+ int offset = (int)cmd->info;
+ *(int *)(struct_ptr + offset) = arg ? 1 : 0;
+ 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;
+ const char *errmsg;
+ cmd_parms parms;
+ struct stat finfo;
+
+ fname = server_root_relative (p, fname);
+
+ if (!(strcmp(fname, server_root_relative(p, RESOURCE_CONFIG_FILE))) ||
+ !(strcmp(fname, server_root_relative(p, ACCESS_CONFIG_FILE)))) {
+ if (stat(fname, &finfo) == -1)
+ return;
+ }
+
+ /* 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"))) {
+ perror("fopen");
+ fprintf(stderr,"httpd: could not open document config file %s\n",
+ fname);
+ 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;
+ const 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 {
+ if (errno == ENOENT || errno == ENOTDIR)
+ dc = NULL;
+ else {
+ log_unixerr("pfopen", filename,
+ "unable to check htaccess file, ensure it is readable",
+ r->server);
+ return HTTP_FORBIDDEN;
+ }
+ }
+
+/* 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.
+ */
+
+/*
+ * Parses a host of the form <address>[:port]
+ * paddr is used to create a list in the order of input
+ * **paddr is the ->next pointer of the last entry (or s->addrs)
+ * *paddr is the variable used to keep track of **paddr between calls
+ * port is the default port to assume
+ */
+static void get_addresses (pool *p, char *w, server_addr_rec ***paddr, unsigned port)
+{
+ struct hostent *hep;
+ unsigned long my_addr;
+ server_addr_rec *sar;
+ char *t;
+ int i, is_an_ip_addr;
+
+ if( *w == 0 ) return;
+
+ t = strchr(w, ':');
+ if (t) {
+ if( strcmp(t+1,"*") == 0 ) {
+ port = 0;
+ } else if( (i = atoi(t+1)) ) {
+ port = i;
+ } else {
+ fprintf( stderr, "Port must be numeric\n" );
+ }
+ *t = 0;
+ }
+
+ is_an_ip_addr = 0;
+ if (strcmp(w, "*") == 0) {
+ my_addr = htonl(INADDR_ANY);
+ is_an_ip_addr = 1;
+ } else if( strcasecmp(w, "_default_") == 0
+ || strcmp(w, "255.255.255.255") == 0 ) {
+ my_addr = DEFAULT_VHOST_ADDR;
+ is_an_ip_addr = 1;
+ } else if(
+#ifdef DGUX
+ ( my_addr = inet_network(w) )
+#else
+ ( my_addr = inet_addr(w) )
+#endif
+ != INADDR_NONE ) {
+ is_an_ip_addr = 1;
+ }
+ if( is_an_ip_addr ) {
+ sar = pcalloc( p, sizeof( server_addr_rec ) );
+ **paddr = sar;
+ *paddr = &sar->next;
+ sar->host_addr.s_addr = my_addr;
+ sar->host_port = port;
+ sar->virthost = pstrdup(p, w);
+ if (t != NULL) *t = ':';
+ return;
+ }
+
+ hep = gethostbyname(w);
+
+ if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
+ fprintf (stderr, "Cannot resolve host name %s --- ignoring!\n", w);
+ if (t != NULL) *t = ':';
+ return;
+ }
+
+ for( i = 0; hep->h_addr_list[i]; ++i ) {
+ sar = pcalloc( p, sizeof( server_addr_rec ) );
+ **paddr = sar;
+ *paddr = &sar->next;
+ sar->host_addr = *(struct in_addr *)hep->h_addr_list[i];
+ sar->host_port = port;
+ sar->virthost = pstrdup(p, w);
+ }
+
+ if (t != NULL) *t = ':';
+}
+
+server_rec *init_virtual_host (pool *p, const char *hostname,
+ server_rec *main_server)
+{
+ server_rec *s = (server_rec *)pcalloc (p, sizeof (server_rec));
+ server_addr_rec **addrs;
+
+#ifdef RLIMIT_NOFILE
+ struct rlimit limits;
+
+ getrlimit ( RLIMIT_NOFILE, &limits );
+ if ( limits.rlim_cur < limits.rlim_max ) {
+ limits.rlim_cur += 2;
+ if ( setrlimit ( RLIMIT_NOFILE, &limits ) < 0 ) {
+ perror ("setrlimit(RLIMIT_NOFILE)");
+ fprintf (stderr, "Cannot exceed hard limit for open files");
+ }
+ }
+#endif
+
+ s->server_admin = NULL;
+ s->server_hostname = NULL;
+ s->error_fname = NULL;
+ s->srm_confname = NULL;
+ s->access_confname = NULL;
+ s->timeout = 0;
+ s->keep_alive_timeout = 0;
+ s->keep_alive = -1;
+ s->keep_alive_max = -1;
+ s->error_log = main_server->error_log;
+ /* start the list of addreses */
+ addrs = &s->addrs;
+ while( hostname[0] ) {
+ get_addresses( p, getword_conf( p, &hostname ), &addrs,
+ main_server->port );
+ }
+ /* terminate the list */
+ *addrs = NULL;
+ if( s->addrs ) {
+ if (s->addrs->host_port) {
+ s->port = s->addrs->host_port; /* set them the same, by default */
+ } else {
+ /* otherwise we get a port of 0 on redirects */
+ s->port = main_server->port;
+ }
+ }
+ s->next = NULL;
+
+ s->is_virtual = 1;
+ s->names = NULL;
+
+ s->module_config = create_empty_config (p);
+ s->lookup_defaults = create_per_dir_config (p);
+
+ s->server_uid = user_id;
+ s->server_gid = group_id;
+
+ 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->server_admin == NULL)
+ virt->server_admin = main_server->server_admin;
+
+ if (virt->srm_confname == NULL)
+ virt->srm_confname = main_server->srm_confname;
+
+ if (virt->access_confname == NULL)
+ virt->access_confname = main_server->access_confname;
+
+ if (virt->timeout == 0)
+ virt->timeout = main_server->timeout;
+
+ if (virt->keep_alive_timeout == 0)
+ virt->keep_alive_timeout = main_server->keep_alive_timeout;
+
+ if (virt->keep_alive == -1)
+ virt->keep_alive = main_server->keep_alive;
+
+ if (virt->keep_alive_max == -1)
+ virt->keep_alive_max = main_server->keep_alive_max;
+
+ if (virt->send_buffer_size == 0)
+ virt->send_buffer_size = main_server->send_buffer_size;
+ }
+}
+
+/*****************************************************************
+ *
+ * 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;
+ lock_fname = DEFAULT_LOCKFILE;
+ 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->error_log = stderr;
+ 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_max = DEFAULT_KEEPALIVE;
+ s->keep_alive = 1;
+ s->next = NULL;
+ s->addrs = pcalloc(p, sizeof (server_addr_rec));
+ s->addrs->host_addr.s_addr = htonl (INADDR_ANY); /* NOT virtual host;
+ * don't match any real network
+ * interface.
+ */
+ s->addrs->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);
+
+ 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);
+
+ return s;
+}
+
+
+void init_modules(pool *p, server_rec *s)
+{
+ module *m;
+
+ for (m = top_module; m; m = m->next)
+ if (m->init)
+ (*m->init) (s, p);
+}
+
+
+/********************************************************************
+ * Configuration directives are restricted in terms of where they may
+ * appear in the main configuration files and/or .htaccess files according
+ * to the bitmask req_override in the command_rec structure.
+ * If any of the overrides set in req_override are also allowed in the
+ * context in which the command is read, then the command is allowed.
+ * The context is determined as follows:
+ *
+ * inside *.conf --> override = (RSRC_CONF|OR_ALL)&~(OR_AUTHCFG|OR_LIMIT);
+ * within <Directory> or <Location> --> override = OR_ALL|ACCESS_CONF;
+ * within .htaccess --> override = AllowOverride for current directory;
+ *
+ * the result is, well, a rather confusing set of possibilities for when
+ * a particular directive is allowed to be used. This procedure prints
+ * in English where the given (pc) directive can be used.
+ */
+void show_overrides(command_rec *pc, module *pm)
+{
+ int n = 0;
+
+ printf("\tAllowed in *.conf ");
+ if ((pc->req_override & (OR_OPTIONS|OR_FILEINFO|OR_INDEXES)) ||
+ ((pc->req_override & RSRC_CONF) &&
+ ((pc->req_override & (ACCESS_CONF|OR_AUTHCFG|OR_LIMIT)))))
+ printf("anywhere");
+ else if (pc->req_override & RSRC_CONF)
+ printf("only outside <Directory> or <Location>");
+ else
+ printf("only inside <Directory> or <Location>");
+
+ /* Warn if the directive is allowed inside <Directory> or .htaccess
+ * but module doesn't support per-dir configuration */
+
+ if ((pc->req_override & (OR_ALL|ACCESS_CONF)) && !pm->create_dir_config)
+ printf(" [no per-dir config]");
+
+ if (pc->req_override & OR_ALL) {
+ printf(" and in .htaccess\n\twhen AllowOverride");
+
+ if ((pc->req_override & OR_ALL) == OR_ALL)
+ printf(" isn't None");
+ else {
+ printf(" includes ");
+
+ if (pc->req_override & OR_AUTHCFG) {
+ if (n++) printf(" or ");
+ printf("AuthConfig");
+ }
+ if (pc->req_override & OR_LIMIT) {
+ if (n++) printf(" or ");
+ printf("Limit");
+ }
+ if (pc->req_override & OR_OPTIONS) {
+ if (n++) printf(" or ");
+ printf("Options");
+ }
+ if (pc->req_override & OR_FILEINFO) {
+ if (n++) printf(" or ");
+ printf("FileInfo");
+ }
+ if (pc->req_override & OR_INDEXES) {
+ if (n++) printf(" or ");
+ printf("Indexes");
+ }
+ }
+ }
+ printf("\n");
+}
+
+/* Show the preloaded configuration directives, the help string explaining
+ * the directive arguments, in what module they are handled, and in
+ * what parts of the configuration they are allowed. Used for httpd -h.
+ */
+void show_directives()
+{
+ extern module *preloaded_modules[];
+ command_rec *pc;
+ int n;
+
+ for (n = 0; preloaded_modules[n]; ++n)
+ for (pc = preloaded_modules[n]->cmds; pc && pc->name; ++pc) {
+ printf("%s\n", pc->name);
+ if (pc->errmsg)
+ printf("\t%s\n", pc->errmsg);
+ printf("\t%s\n", preloaded_modules[n]->name);
+ show_overrides(pc, preloaded_modules[n]);
+ }
+}
+
+/* Show the preloaded module names. Used for httpd -l. */
+void show_modules()
+{
+ extern module *preloaded_modules[];
+ int n;
+
+ printf ("Compiled-in modules:\n");
+ for (n = 0; preloaded_modules[n]; ++n)
+ printf (" %s\n", preloaded_modules[n]->name);
+}
+
diff --git a/usr.sbin/httpd/src/http_config.h b/usr.sbin/httpd/src/http_config.h
new file mode 100644
index 00000000000..02d041854d3
--- /dev/null
+++ b/usr.sbin/httpd/src/http_config.h
@@ -0,0 +1,298 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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> */
+ TAKE12, /* one or two arguments */
+ TAKE3, /* three arguments only */
+ TAKE23, /* two or three arguments */
+ TAKE123, /* one, two or three arguments */
+ TAKE13 /* one or three arguments */
+};
+
+typedef struct command_struct {
+ char *name; /* Name of this command */
+ const char *(*func)(); /* Function invoked */
+ void *cmd_data; /* Extra data, for functions which
+ * implement multiple commands...
+ */
+ int req_override; /* What overrides need to be allowed to
+ * enable this command.
+ */
+ enum cmd_how args_how; /* What the command expects as arguments */
+
+ char *errmsg; /* 'usage' message, in case of syntax errors */
+} command_rec;
+
+/* The allowed locations for a configuration directive are the union of
+ * those indicated by each set bit in the req_override mask.
+ *
+ * (req_override & RSRC_CONF) => *.conf outside <Directory> or <Location>
+ * (req_override & ACCESS_CONF) => *.conf inside <Directory> or <Location>
+ * (req_override & OR_AUTHCFG) => *.conf inside <Directory> or <Location>
+ * and .htaccess when AllowOverride AuthConfig
+ * (req_override & OR_LIMIT) => *.conf inside <Directory> or <Location>
+ * and .htaccess when AllowOverride Limit
+ * (req_override & OR_OPTIONS) => *.conf anywhere
+ * and .htaccess when AllowOverride Options
+ * (req_override & OR_FILEINFO) => *.conf anywhere
+ * and .htaccess when AllowOverride FileInfo
+ * (req_override & OR_INDEXES) => *.conf anywhere
+ * and .htaccess when AllowOverride Indexes
+ */
+#define OR_NONE 0
+#define OR_LIMIT 1
+#define OR_OPTIONS 2
+#define OR_FILEINFO 4
+#define OR_AUTHCFG 8
+#define OR_INDEXES 16
+#define OR_UNSET 32
+#define ACCESS_CONF 64
+#define RSRC_CONF 128
+#define OR_ALL (OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG|OR_INDEXES)
+
+/* This can be returned by a function if they don't wish to handle
+ * a command. Make it something not likely someone will actually use
+ * as an error code.
+ */
+
+#define DECLINE_CMD "\a\b"
+
+/*
+ * This structure is passed to a command which is being invoked,
+ * to carry a large variety of miscellaneous data which is all of
+ * use to *somebody*...
+ */
+
+typedef struct {
+ void *info; /* Argument to command from cmd_table */
+ int override; /* Which allow-override bits are set */
+ int limited; /* Which methods are <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.
+ */
+ const command_rec *cmd; /* configuration command */
+} cmd_parms;
+
+/* This structure records the existence of handlers in a module... */
+
+typedef struct {
+ char *content_type;
+ int (*handler) (request_rec *);
+} handler_rec;
+
+/*
+ * Module structures. Just about everything is dispatched through
+ * these, directly or indirectly (through the command and handler
+ * tables).
+ */
+
+typedef struct module_struct {
+ int version; /* API version, *not* module version;
+ * check that module is compatible with this
+ * version of the server.
+ */
+ int module_index; /* Index to this modules structures in
+ * config vectors.
+ */
+
+ const char *name;
+
+ 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 *);
+ int (*header_parser)(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 19970622
+#define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER, -1, __FILE__, 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... */
+
+const char *set_string_slot (cmd_parms *, char *, char *);
+const char *set_flag_slot (cmd_parms *, char *, int);
+
+/* 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);
+int add_named_module (const char *name);
+void clear_module_list ();
+const char *find_module_name (module *m);
+module *find_linked_module (const char *name);
+
+#ifdef CORE_PRIVATE
+
+/* For http_main.c... */
+
+server_rec *read_config (pool *conf_pool, pool *temp_pool, char *config_name);
+void init_modules(pool *p, server_rec *s);
+void setup_prelinked_modules();
+void show_directives();
+void show_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);
+const char *srm_command_loop (cmd_parms *parms, void *config);
+
+server_rec *init_virtual_host (pool *p, const char *hostname, server_rec *main_server);
+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);
+int header_parse (request_rec *);
+
+#endif
diff --git a/usr.sbin/httpd/src/http_core.c b/usr.sbin/httpd/src/http_core.c
new file mode 100644
index 00000000000..ce44d64eea2
--- /dev/null
+++ b/usr.sbin/httpd/src/http_core.c
@@ -0,0 +1,1413 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 if (strncmp(dir,"proxy:",6)==0) conf->d = pstrdup (a, dir);
+ else conf->d = pstrcat (a, dir, "/", NULL);
+ conf->d_is_matchexp = conf->d ? is_matchexp( conf->d ) : 0;
+
+
+ conf->opts = dir ? OPT_UNSET : OPT_ALL;
+ conf->opts_add = conf->opts_remove = OPT_NONE;
+ 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 */
+ conf->satisfy = SATISFY_NOSPEC;
+
+#ifdef RLIMIT_CPU
+ conf->limit_cpu = NULL;
+#endif
+#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM)
+ conf->limit_mem = NULL;
+#endif
+#ifdef RLIMIT_NPROC
+ conf->limit_nproc = NULL;
+#endif
+
+ conf->sec = make_array (a, 2, sizeof(void *));
+
+ 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));
+ if( base->response_code_strings ) {
+ conf->response_code_strings = palloc(a,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES );
+ memcpy( conf->response_code_strings, base->response_code_strings,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES );
+ }
+
+ conf->d = new->d;
+ conf->d_is_matchexp = new->d_is_matchexp;
+ conf->r = new->r;
+
+ if (new->opts != OPT_UNSET) conf->opts = new->opts;
+ if (new->opts_add) conf->opts |= new->opts_add;
+ if (new->opts_remove) conf->opts &= ~(new->opts_remove);
+
+ 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;
+
+ if( new->response_code_strings ) {
+ if( conf->response_code_strings == NULL ) {
+ conf->response_code_strings = palloc(a,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES );
+ memcpy( conf->response_code_strings, new->response_code_strings,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES );
+ } else {
+ for (i = 0; i < RESPONSE_CODES; ++i)
+ if (new->response_code_strings[i] != NULL)
+ conf->response_code_strings[i] = new->response_code_strings[i];
+ }
+ }
+ if (new->hostname_lookups != 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;
+
+#ifdef RLIMIT_CPU
+ if (new->limit_cpu) conf->limit_cpu = new->limit_cpu;
+#endif
+#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM)
+ if (new->limit_mem) conf->limit_mem = new->limit_mem;
+#endif
+#ifdef RLIMIT_NPROC
+ if (new->limit_nproc) conf->limit_nproc = new->limit_nproc;
+#endif
+
+ conf->sec = append_arrays (a, base->sec, new->sec);
+
+ if (new->satisfy != SATISFY_NOSPEC) conf->satisfy = new->satisfy;
+ 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, base->sec, virt->sec);
+ conf->sec_url = append_arrays (p, base->sec_url, virt->sec_url);
+
+ return conf;
+}
+
+/* Add per-directory configuration entry (for <directory> section);
+ * these are part of the core server config.
+ */
+
+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;
+}
+
+void add_file_conf (core_dir_config *conf, void *url_config)
+{
+ void **new_space = (void **) push_array (conf->sec);
+
+ *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;
+}
+
+int satisfies (request_rec *r)
+{
+ core_dir_config *conf =
+ (core_dir_config *)get_module_config(r->per_dir_config, &core_module);
+
+ return conf->satisfy;
+}
+
+/* Should probably just get rid of this... the only code that cares is
+ * part of the core anyway (and in fact, it isn't publicised to other
+ * modules).
+ */
+
+char *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);
+
+ if( conf->response_code_strings == NULL ) {
+ return NULL;
+ }
+ 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 = NULL;
+
+/* If we haven't checked the host name, and we want to */
+ if (dir_config)
+ dir_conf = (core_dir_config *)get_module_config(dir_config, &core_module);
+
+ if ((!dir_conf) || (type != REMOTE_NOLOOKUP && 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.
+ */
+
+const 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;
+}
+
+const 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;
+}
+
+const char *set_error_document (cmd_parms *cmd, core_dir_config *conf,
+ char *line)
+{
+ int error_number, index_number, idx500;
+ char *w;
+
+ /* 1st parameter should be a 3 digit number, which we recognize;
+ * convert it into an array index
+ */
+
+ w = getword_conf_nc (cmd->pool, &line);
+ error_number = atoi(w);
+
+ idx500 = index_of_response(HTTP_INTERNAL_SERVER_ERROR);
+
+ if (error_number == HTTP_INTERNAL_SERVER_ERROR)
+ index_number = idx500;
+ else if ((index_number = index_of_response(error_number)) == idx500)
+ return pstrcat(cmd->pool, "Unsupported HTTP response code ", w, NULL);
+
+ /* Store it... */
+
+ if( conf->response_code_strings == NULL ) {
+ conf->response_code_strings = pcalloc(cmd->pool,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES );
+ }
+ 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.
+ */
+
+const char *set_override (cmd_parms *cmd, core_dir_config *d, const 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;
+}
+
+const char *set_options (cmd_parms *cmd, core_dir_config *d, const char *l)
+{
+ char opt;
+ int first = 1;
+ char action;
+
+ while(l[0]) {
+ char *w = getword_conf(cmd->pool, &l);
+ action = '\0';
+
+ if (*w == '+' || *w == '-')
+ action = *(w++);
+ else if (first) {
+ d->opts = OPT_NONE;
+ first = 0;
+ }
+
+ if(!strcasecmp(w,"Indexes"))
+ opt = OPT_INDEXES;
+ else if(!strcasecmp(w,"Includes"))
+ opt = OPT_INCLUDES;
+ else if(!strcasecmp(w,"IncludesNOEXEC"))
+ opt = (OPT_INCLUDES | OPT_INCNOEXEC);
+ else if(!strcasecmp(w,"FollowSymLinks"))
+ opt = OPT_SYM_LINKS;
+ else if(!strcasecmp(w,"SymLinksIfOwnerMatch"))
+ opt = OPT_SYM_OWNER;
+ else if(!strcasecmp(w,"execCGI"))
+ opt = OPT_EXECCGI;
+ else if (!strcasecmp(w,"MultiViews"))
+ opt = OPT_MULTI;
+ else if (!strcasecmp(w,"RunScripts")) /* AI backcompat. Yuck */
+ opt = OPT_MULTI|OPT_EXECCGI;
+ else if(!strcasecmp(w,"None"))
+ opt = OPT_NONE;
+ else if(!strcasecmp(w,"All"))
+ opt = OPT_ALL;
+ else
+ return pstrcat (cmd->pool, "Illegal option ", w, NULL);
+
+ if (action == '-')
+ d->opts_remove |= opt;
+ else if (action == '+')
+ d->opts_add |= opt;
+ else
+ d->opts |= opt;
+ }
+
+ return NULL;
+}
+
+const char *satisfy (cmd_parms *cmd, core_dir_config *c, char *arg)
+{
+ if(!strcasecmp(arg,"all"))
+ c->satisfy = SATISFY_ALL;
+ else if(!strcasecmp(arg,"any"))
+ c->satisfy = SATISFY_ANY;
+ else
+ return "Satisfy either 'any' or 'all'.";
+ return NULL;
+}
+
+const 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;
+}
+
+const char *limit (cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const 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 if(!strcasecmp(method,"OPTIONS")) limited |= (1 << M_OPTIONS);
+ else return "unknown method in <Limit>";
+ }
+
+ cmd->limited = limited;
+ return NULL;
+}
+
+const char *endlimit (cmd_parms *cmd, void *dummy, void *dummy2)
+{
+ if (cmd->limited == -1) return "</Limit> unexpected";
+
+ cmd->limited = -1;
+ return NULL;
+}
+
+static const char end_dir_magic[] = "</Directory> outside of any <Directory> section";
+
+const char *end_dirsection (cmd_parms *cmd, void *dummy) {
+ return end_dir_magic;
+}
+
+const char *dirsection (cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *errmsg;
+ char *endp = strrchr (arg, '>');
+ int old_overrides = cmd->override;
+ char *old_path = cmd->path;
+ core_dir_config *conf;
+ void *new_dir_conf = create_per_dir_config (cmd->pool);
+ regex_t *r = NULL;
+
+ 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);
+#ifdef __EMX__
+ /* Fix OS/2 HPFS filename case problem. */
+ cmd->path = strlwr(cmd->path);
+#endif
+ cmd->override = OR_ALL|ACCESS_CONF;
+
+ if (!strcmp(cmd->path, "~")) {
+ cmd->path = getword_conf (cmd->pool, &arg);
+ r = pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+ }
+
+ errmsg = srm_command_loop (cmd, new_dir_conf);
+ if (errmsg != end_dir_magic) return errmsg;
+
+ conf = (core_dir_config *)get_module_config(new_dir_conf, &core_module);
+ conf->r = r;
+
+ add_per_dir_conf (cmd->server, new_dir_conf);
+
+ cmd->path = old_path;
+ cmd->override = old_overrides;
+
+ return NULL;
+}
+
+static const char end_url_magic[] = "</Location> outside of any <Location> section";
+
+const char *end_urlsection (cmd_parms *cmd, void *dummy) {
+ return end_url_magic;
+}
+
+const char *urlsection (cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *errmsg;
+ char *endp = strrchr (arg, '>');
+ int old_overrides = cmd->override;
+ char *old_path = cmd->path;
+ core_dir_config *conf;
+ regex_t *r = NULL;
+
+ 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;
+
+ if (!strcmp(cmd->path, "~")) {
+ cmd->path = getword_conf (cmd->pool, &arg);
+ r = pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+ }
+
+ 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 */
+ conf->d_is_matchexp = is_matchexp( conf->d );
+ conf->r = r;
+
+ add_per_url_conf (cmd->server, new_url_conf);
+
+ cmd->path = old_path;
+ cmd->override = old_overrides;
+
+ return NULL;
+}
+
+static char *end_file_magic = "</Files> outside of any <Files> section";
+
+const char *end_filesection (cmd_parms *cmd, void *dummy) {
+ return end_file_magic;
+}
+
+const char *filesection (cmd_parms *cmd, core_dir_config *c, const char *arg)
+{
+ const char *errmsg;
+ char *endp = strrchr (arg, '>');
+ int old_overrides = cmd->override;
+ char *old_path = cmd->path;
+ core_dir_config *conf;
+ regex_t *r = NULL;
+
+ void *new_file_conf = create_per_dir_config (cmd->pool);
+
+ if (endp) *endp = '\0';
+
+ if (cmd->limited != -1) return "Can't have <Files> within <Limit>";
+
+ cmd->path = getword_conf (cmd->pool, &arg);
+ /* Only if not an .htaccess file */
+ if (cmd->path)
+ cmd->override = OR_ALL|ACCESS_CONF;
+
+ if (!strcmp(cmd->path, "~")) {
+ cmd->path = getword_conf (cmd->pool, &arg);
+ if (old_path && cmd->path[0] != '/' && cmd->path[0] != '^')
+ cmd->path = pstrcat(cmd->pool, "^", old_path, cmd->path, NULL);
+ r = pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+ }
+ else if (old_path && cmd->path[0] != '/')
+ cmd->path = pstrcat(cmd->pool, old_path, cmd->path, NULL);
+
+ errmsg = srm_command_loop (cmd, new_file_conf);
+ if (errmsg != end_file_magic) return errmsg;
+
+ conf = (core_dir_config *)get_module_config(new_file_conf, &core_module);
+ conf->d = pstrdup(cmd->pool, cmd->path);
+ conf->d_is_matchexp = is_matchexp( conf->d );
+ conf->r = r;
+
+ add_file_conf (c, new_file_conf);
+
+ cmd->path = old_path;
+ cmd->override = old_overrides;
+
+ return NULL;
+}
+
+const char *end_ifmod (cmd_parms *cmd, void *dummy) {
+ return NULL;
+}
+
+const char *start_ifmod (cmd_parms *cmd, void *dummy, char *arg)
+{
+ char *endp = strrchr (arg, '>');
+ char l[MAX_STRING_LEN];
+ int not = (arg[0] == '!');
+ module *found;
+ int nest = 1;
+
+ if (endp) *endp = '\0';
+ if (not) arg++;
+
+ found = find_linked_module(arg);
+
+ if ((!not && found) || (not && !found))
+ return NULL;
+
+ while (nest && !(cfg_getline (l, MAX_STRING_LEN, cmd->infile))) {
+ if (!strncasecmp(l, "<IfModule", 9))
+ nest++;
+ if (!strcasecmp(l, "</IfModule>"))
+ nest--;
+ }
+
+ return NULL;
+}
+
+/* httpd.conf commands... beginning with the <VirtualHost> business */
+
+const char end_virthost_magic[] = "</Virtualhost> out of place";
+
+const char *end_virtualhost_section (cmd_parms *cmd, void *dummy)
+{
+ return end_virthost_magic;
+}
+
+const char *virtualhost_section (cmd_parms *cmd, void *dummy, char *arg)
+{
+ server_rec *main_server = cmd->server, *s;
+ const char *errmsg;
+ char *endp = strrchr (arg, '>');
+ pool *p = cmd->pool, *ptemp = cmd->temp_pool;
+
+ if (endp) *endp = '\0';
+
+ /* FIXME: There's another feature waiting to happen here -- since you
+ can now put multiple addresses/names on a single <VirtualHost>
+ you might want to use it to group common definitions and then
+ define other "subhosts" with their individual differences. But
+ personally I'd rather just do it with a macro preprocessor. -djg */
+ if (main_server->is_virtual)
+ return "<VirtualHost> doesn't nest!";
+
+ s = init_virtual_host (p, arg, main_server);
+ 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;
+}
+
+const char *add_module_command (cmd_parms *cmd, void *dummy, char *arg)
+{
+ if (add_named_module (arg))
+ return NULL;
+ return "required module not found";
+}
+
+const char *clear_module_list_command (cmd_parms *cmd, void *dummy)
+{
+ clear_module_list ();
+ return NULL;
+}
+
+const 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;
+}
+
+const 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;
+}
+
+const char *server_port (cmd_parms *cmd, void *dummy, char *arg) {
+ cmd->server->port = atoi (arg);
+ return NULL;
+}
+
+const char *set_send_buffer_size (cmd_parms *cmd, void *dummy, char *arg) {
+ int s = atoi (arg);
+ if (s < 512 && s != 0) {
+ return "SendBufferSize must be >= 512 bytes, or 0 for system default.";
+ }
+ cmd->server->send_buffer_size = s;
+ return NULL;
+}
+
+const char *set_user (cmd_parms *cmd, void *dummy, char *arg)
+{
+ if (!cmd->server->is_virtual) {
+ user_name = pstrdup (cmd->pool, arg);
+ cmd->server->server_uid = user_id = uname2id(arg);
+ }
+ else {
+ if (suexec_enabled)
+ cmd->server->server_uid = uname2id(arg);
+ else {
+ cmd->server->server_uid = user_id;
+ fprintf(stderr,
+ "Warning: User directive in <VirtualHost> "
+ "requires SUEXEC wrapper.\n");
+ }
+ }
+#if !defined (BIG_SECURITY_HOLE)
+ if (cmd->server->server_uid == 0) {
+ fprintf (stderr,
+"Error:\tApache has not been designed to serve pages while running\n"
+"\tas root. There are known race conditions that will allow any\n"
+"\tlocal user to read any file on the system. Should you still\n"
+"\tdesire to serve pages as root then add -DBIG_SECURITY_HOLE to\n"
+"\tthe EXTRA_CFLAGS line in your src/Configuration file and rebuild\n"
+"\tthe server. It is strongly suggested that you instead modify the\n"
+"\tUser directive in your httpd.conf file to list a non-root user.\n");
+ exit (1);
+ }
+#endif
+
+ return NULL;
+}
+
+const char *set_group (cmd_parms *cmd, void *dummy, char *arg)
+{
+ if (!cmd->server->is_virtual)
+ cmd->server->server_gid = group_id = gname2id(arg);
+ else {
+ if (suexec_enabled)
+ cmd->server->server_gid = gname2id(arg);
+ else {
+ cmd->server->server_gid = group_id;
+ fprintf(stderr,
+ "Warning: Group directive in <VirtualHost> requires SUEXEC wrapper.\n");
+ }
+ }
+
+ return NULL;
+}
+
+const char *set_server_root (cmd_parms *cmd, void *dummy, char *arg) {
+ if (!is_directory (arg)) return "ServerRoot must be a valid directory";
+ strncpy (server_root, arg, sizeof(server_root)-1);
+ server_root[sizeof(server_root)-1] = '\0';
+ return NULL;
+}
+
+const char *set_timeout (cmd_parms *cmd, void *dummy, char *arg) {
+ cmd->server->timeout = atoi (arg);
+ return NULL;
+}
+
+const char *set_keep_alive_timeout (cmd_parms *cmd, void *dummy, char *arg) {
+ cmd->server->keep_alive_timeout = atoi (arg);
+ return NULL;
+}
+
+const char *set_keep_alive (cmd_parms *cmd, void *dummy, char *arg) {
+ /* We've changed it to On/Off, but used to use numbers
+ * so we accept anything but "Off" or "0" as "On"
+ */
+ if (!strcasecmp(arg, "off") || !strcmp(arg, "0"))
+ cmd->server->keep_alive = 0;
+ else
+ cmd->server->keep_alive = 1;
+ return NULL;
+}
+
+const char *set_keep_alive_max (cmd_parms *cmd, void *dummy, char *arg) {
+ cmd->server->keep_alive_max = atoi (arg);
+ return NULL;
+}
+
+const char *set_pidfile (cmd_parms *cmd, void *dummy, char *arg) {
+ pid_fname = pstrdup (cmd->pool, arg);
+ return NULL;
+}
+
+const char *set_scoreboard (cmd_parms *cmd, void *dummy, char *arg) {
+ scoreboard_fname = pstrdup (cmd->pool, arg);
+ return NULL;
+}
+
+const char *set_lockfile (cmd_parms *cmd, void *dummy, char *arg) {
+ lock_fname = pstrdup (cmd->pool, arg);
+ return NULL;
+}
+
+const char *set_idcheck (cmd_parms *cmd, core_dir_config *d, int arg) {
+ d->do_rfc1413 = arg;
+ return NULL;
+}
+
+const char *set_hostname_lookups (cmd_parms *cmd, core_dir_config *d, int arg)
+{
+ d->hostname_lookups = arg;
+ return NULL;
+}
+
+const char *set_serverpath (cmd_parms *cmd, void *dummy, char *arg) {
+ cmd->server->path = pstrdup (cmd->pool, arg);
+ cmd->server->pathlen = strlen (arg);
+ return NULL;
+}
+
+const char *set_content_md5 (cmd_parms *cmd, core_dir_config *d, int arg) {
+ d->content_md5 = arg;
+ return NULL;
+}
+
+const char *set_daemons_to_start (cmd_parms *cmd, void *dummy, char *arg) {
+ daemons_to_start = atoi (arg);
+ return NULL;
+}
+
+const 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;
+}
+
+const char *set_max_free_servers (cmd_parms *cmd, void *dummy, char *arg) {
+ daemons_max_free = atoi (arg);
+ return NULL;
+}
+
+const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg) {
+ daemons_limit = atoi (arg);
+ if (daemons_limit > HARD_SERVER_LIMIT) {
+ fprintf(stderr, "WARNING: MaxClients of %d exceeds compile time limit "
+ "of %d servers,\n", daemons_limit, HARD_SERVER_LIMIT);
+ fprintf(stderr, " lowering MaxClients to %d. To increase, please "
+ "see the\n", HARD_SERVER_LIMIT);
+ fprintf(stderr, " HARD_SERVER_LIMIT define in src/httpd.h.\n");
+ daemons_limit = HARD_SERVER_LIMIT;
+ } else if (daemons_limit < 1) {
+ fprintf (stderr, "WARNING: Require MaxClients > 0, setting to 1\n");
+ daemons_limit = 1;
+ }
+ return NULL;
+}
+
+const char *set_max_requests (cmd_parms *cmd, void *dummy, char *arg) {
+ max_requests_per_child = atoi (arg);
+ return NULL;
+}
+
+#if defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC)
+static void set_rlimit(cmd_parms *cmd, struct rlimit **plimit, const char *arg,
+ const char * arg2, int type)
+{
+ char *str;
+ struct rlimit *limit;
+ /* If your platform doesn't define rlim_t then typedef it in conf.h */
+ rlim_t cur = 0;
+ rlim_t max = 0;
+
+ *plimit=(struct rlimit *)pcalloc(cmd->pool,sizeof **plimit);
+ limit=*plimit;
+ if ((getrlimit(type, limit)) != 0)
+ {
+ *plimit = NULL;
+ log_unixerr("getrlimit",cmd->cmd->name,"failed",cmd->server);
+ return;
+ }
+
+ if ((str = getword_conf(cmd->pool, &arg)))
+ if (!strcasecmp(str, "max"))
+ cur = limit->rlim_max;
+ else
+ cur = atol(str);
+ else {
+ log_printf(cmd->server, "Invalid parameters for %s", cmd->cmd->name);
+ return;
+ }
+
+ if (arg2 && (str = getword_conf(cmd->pool, &arg2)))
+ max = atol(str);
+
+ /* if we aren't running as root, cannot increase max */
+ if (geteuid()) {
+ limit->rlim_cur = cur;
+ if (max)
+ log_printf(cmd->server, "Must be uid 0 to raise maximum %s",
+ cmd->cmd->name);
+ }
+ else {
+ if (cur)
+ limit->rlim_cur = cur;
+ if (max)
+ limit->rlim_max = max;
+ }
+}
+#endif
+
+#if !defined (RLIMIT_CPU) || !(defined (RLIMIT_DATA) || defined (RLIMIT_VMEM)) || !defined (RLIMIT_NPROC)
+static const char *no_set_limit (cmd_parms *cmd, core_dir_config *conf,
+ char *arg, char *arg2)
+{
+ log_printf(cmd->server, "%s not supported on this platform",
+ cmd->cmd->name);
+ return NULL;
+}
+#endif
+
+#ifdef RLIMIT_CPU
+const char *set_limit_cpu (cmd_parms *cmd, core_dir_config *conf, char *arg, char *arg2)
+{
+ set_rlimit(cmd,&conf->limit_cpu,arg,arg2,RLIMIT_CPU);
+ return NULL;
+}
+#endif
+
+#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM)
+const char *set_limit_mem (cmd_parms *cmd, core_dir_config *conf, char *arg, char * arg2)
+{
+#ifdef RLIMIT_DATA
+ set_rlimit(cmd,&conf->limit_mem,arg,arg2,RLIMIT_DATA);
+#else
+ set_rlimit(cmd,&conf->limit_mem,arg,arg2,RLIMIT_VMEM);
+#endif
+ return NULL;
+}
+#endif
+
+#ifdef RLIMIT_NPROC
+const char *set_limit_nproc (cmd_parms *cmd, core_dir_config *conf, char *arg, char * arg2)
+{
+ set_rlimit(cmd,&conf->limit_nproc,arg,arg2,RLIMIT_NPROC);
+ return NULL;
+}
+#endif
+
+const char *set_bind_address (cmd_parms *cmd, void *dummy, char *arg) {
+ bind_address.s_addr = get_virthost_addr (arg, NULL);
+ return NULL;
+}
+
+const char *set_listener(cmd_parms *cmd, void *dummy, char *ips)
+{
+ listen_rec *new;
+ char *ports;
+ unsigned port;
+
+ 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=pcalloc(cmd->pool, sizeof(listen_rec));
+ new->local_addr.sin_family = AF_INET;
+ if (ports == ips) /* no address */
+ new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ else
+ new->local_addr.sin_addr.s_addr = get_virthost_addr(ips, NULL);
+ port=atoi(ports);
+ if(!port)
+ return "Port must be numeric";
+ new->local_addr.sin_port = htons(port);
+ new->fd = -1;
+ new->used = 0;
+ new->next = 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, "Container for directives affecting resources located in the specified directories" },
+{ "</Directory>", end_dirsection, NULL, ACCESS_CONF, NO_ARGS, NULL },
+{ "<Location", urlsection, NULL, RSRC_CONF, RAW_ARGS, "Container for directives affecting resources accessed through the specified URL paths" },
+{ "</Location>", end_urlsection, NULL, ACCESS_CONF, NO_ARGS, NULL },
+{ "<VirtualHost", virtualhost_section, NULL, RSRC_CONF, RAW_ARGS, "Container to map directives to a particular virtual host" },
+{ "</VirtualHost>", end_virtualhost_section, NULL, RSRC_CONF, NO_ARGS, NULL },
+{ "<Files", filesection, NULL, OR_ALL, RAW_ARGS, "Container for directives affecting files matching specified patterns" },
+{ "</Files>", end_filesection, NULL, OR_ALL, NO_ARGS, NULL },
+{ "<Limit", limit, NULL, OR_ALL, RAW_ARGS, "Container for authentication directives when accessed using specified HTTP methods" },
+{ "</Limit>", endlimit, NULL, OR_ALL, RAW_ARGS, NULL },
+{ "<IfModule", start_ifmod, NULL, OR_ALL, RAW_ARGS, "Container for directives based on existance of specified modules" },
+{ "</IfModule>", end_ifmod, NULL, OR_ALL, NO_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, "The authentication realm (e.g. \"Members Only\")" },
+{ "Require", require, NULL, OR_AUTHCFG, RAW_ARGS, "Selects which authenticated users or groups may access a protected space" },
+{ "Satisfy", satisfy, NULL, OR_AUTHCFG, TAKE1,
+ "access policy if both allow and require used ('all' or 'any')" },
+
+/* Old resource config file commands */
+
+{ "AccessFileName", set_access_name, NULL, RSRC_CONF, TAKE1, "Name of per-directory config files (default: .htaccess)" },
+{ "DocumentRoot", set_document_root, NULL, RSRC_CONF, TAKE1, "Root directory of the document tree" },
+{ "ErrorDocument", set_error_document, NULL, OR_FILEINFO, RAW_ARGS, "Change responses for HTTP errors" },
+{ "AllowOverride", set_override, NULL, ACCESS_CONF, RAW_ARGS, "Controls what groups of directives can be configured by per-directory config files" },
+{ "Options", set_options, NULL, OR_OPTIONS, RAW_ARGS, "Set a number of attributes for a given directory" },
+{ "DefaultType", 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, "\"on\" to enable or \"off\" to disable reverse DNS lookups" },
+{ "User", set_user, NULL, RSRC_CONF, TAKE1, "Effective user id for this server"},
+{ "Group", set_group, NULL, RSRC_CONF, TAKE1, "Effective group id for this server"},
+{ "ServerAdmin", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, server_admin), RSRC_CONF, TAKE1,
+ "The email address of the server administrator" },
+{ "ServerName", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, server_hostname), RSRC_CONF, TAKE1,
+ "The hostname of the server" },
+{ "ServerRoot", set_server_root, NULL, RSRC_CONF, TAKE1, "Common directory of server-related files (logs, confs, etc)" },
+{ "ErrorLog", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, error_fname), RSRC_CONF, TAKE1,
+ "The filename of the error log" },
+{ "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1,
+ "A file for logging the server process ID"},
+{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1,
+ "A file for Apache to maintain runtime process management information"},
+{ "LockFile", set_lockfile, NULL, RSRC_CONF, TAKE1,
+ "The lockfile used when Apache needs to lock the accept() call"},
+{ "AccessConfig", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, access_confname), RSRC_CONF, TAKE1,
+ "The filename of the access config file" },
+{ "ResourceConfig", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, srm_confname), RSRC_CONF, TAKE1,
+ "The filename of the resource config file" },
+{ "ServerAlias", set_server_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)"},
+{ "MaxKeepAliveRequests", set_keep_alive_max, NULL, RSRC_CONF, TAKE1, "Maximum number of Keep-Alive requests per connection, or 0 for infinite" },
+{ "KeepAlive", set_keep_alive, NULL, RSRC_CONF, TAKE1, "Whether persistent connections should be On or Off" },
+{ "IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF, FLAG, "Enable identd (RFC 1413) user lookups - SLOW" },
+{ "ContentDigest", set_content_md5, NULL, RSRC_CONF|ACCESS_CONF|OR_AUTHCFG, FLAG, "whether or not to send a Content-MD5 header with each request" },
+{ "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1, "Number of child processes launched at server startup" },
+{ "MinSpareServers", set_min_free_servers, NULL, RSRC_CONF, TAKE1, "Minimum number of idle children, to handle request spikes" },
+{ "MaxSpareServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, "Maximum number of idle children" },
+{ "MaxServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, "Deprecated equivalent to MaxSpareServers" },
+{ "ServersSafetyLimit", set_server_limit, NULL, RSRC_CONF, TAKE1, "Deprecated equivalent to MaxClients" },
+{ "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1, "Maximum number of children alive at the same time" },
+{ "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1, "Maximum number of requests a particular child serves before dying." },
+{ "RLimitCPU",
+#ifdef RLIMIT_CPU
+ set_limit_cpu, (void*)XtOffsetOf(core_dir_config, limit_cpu),
+#else
+ no_set_limit, NULL,
+#endif
+ OR_ALL, TAKE12, "soft/hard limits for max CPU usage in seconds" },
+{ "RLimitMEM",
+#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM)
+ set_limit_mem, (void*)XtOffsetOf(core_dir_config, limit_mem),
+#else
+ no_set_limit, NULL,
+#endif
+ OR_ALL, TAKE12, "soft/hard limits for max memory usage per process" },
+{ "RLimitNPROC",
+#ifdef RLIMIT_NPROC
+ set_limit_nproc, (void*)XtOffsetOf(core_dir_config, limit_nproc),
+#else
+ no_set_limit, NULL,
+#endif
+ OR_ALL, TAKE12, "soft/hard limits for max number of processes per uid" },
+{ "BindAddress", set_bind_address, NULL, RSRC_CONF, TAKE1,
+ "'*', a numeric IP address, or the name of a host with a unique IP address"},
+{ "Listen", set_listener, NULL, RSRC_CONF, TAKE1,
+ "a port number or a numeric IP address and a port number"},
+{ "SendBufferSize", set_send_buffer_size, NULL, RSRC_CONF, TAKE1, "send buffer size in bytes"},
+{ "AddModule", add_module_command, NULL, RSRC_CONF, ITERATE,
+ "the name of a module" },
+{ "ClearModuleList", clear_module_list_command, NULL, RSRC_CONF, NO_ARGS, NULL },
+{ 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 HTTP_FORBIDDEN;
+ if ((r->uri[0] != '/') && strcmp(r->uri, "*")) {
+ log_printf(r->server, "Invalid URI in request %s", r->the_request);
+ return BAD_REQUEST;
+ }
+
+ if (r->server->path &&
+ !strncmp(r->uri, r->server->path, r->server->pathlen) &&
+ (r->server->path[r->server->pathlen - 1] == '/' ||
+ r->uri[r->server->pathlen] == '/' ||
+ r->uri[r->server->pathlen] == '\0'))
+ r->filename = 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
+ * and OPTIONS at this point... anyone who wants to write a generic
+ * handler for PUT or POST is free to do so, but it seems unwise to provide
+ * any defaults yet... So, for now, we assume that this will always be
+ * the last handler called and return 405 or 501.
+ */
+
+int default_handler (request_rec *r)
+{
+ core_dir_config *d =
+ (core_dir_config *)get_module_config(r->per_dir_config, &core_module);
+ int rangestatus, errstatus;
+ FILE *f;
+
+ /* This handler has no use for a request body (yet), but we still
+ * need to read and discard it if the client sent one.
+ */
+ if ((errstatus = discard_request_body(r)) != OK)
+ return errstatus;
+
+ r->allowed |= (1 << M_GET) | (1 << M_OPTIONS);
+
+ if (r->method_number == M_INVALID) {
+ log_printf(r->server, "Invalid method in request %s", r->the_request);
+ return NOT_IMPLEMENTED;
+ }
+ if (r->method_number == M_OPTIONS) return send_http_options(r);
+ if (r->method_number == M_PUT) return METHOD_NOT_ALLOWED;
+
+ if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) {
+ log_reason("File does not exist",
+ r->path_info ? pstrcat(r->pool, r->filename, r->path_info, NULL)
+ : r->filename, r);
+ return NOT_FOUND;
+ }
+ if (r->method_number != M_GET) return METHOD_NOT_ALLOWED;
+
+#ifdef __EMX__
+ /* Need binary mode for OS/2 */
+ f = pfopen (r->pool, r->filename, "rb");
+#else
+ f = pfopen (r->pool, r->filename, "r");
+#endif
+
+ if (f == NULL) {
+ log_reason("file permissions deny server access", r->filename, r);
+ return FORBIDDEN;
+ }
+
+ if ((errstatus = set_last_modified (r, r->finfo.st_mtime))
+ || (errstatus = set_content_length (r, r->finfo.st_size)))
+ return errstatus;
+
+ if (d->content_md5 & 1) {
+ table_set (r->headers_out, "Content-MD5", md5digest(r->pool, f));
+ }
+
+ rangestatus = set_byterange(r);
+ send_http_header (r);
+
+ if (!r->header_only) {
+ if (!rangestatus)
+ send_fd (f, r);
+ else {
+ long offset, length;
+ while (each_byterange(r, &offset, &length)) {
+ fseek(f, offset, SEEK_SET);
+ send_fd_length(f, r, length);
+ }
+ }
+ }
+
+ pfclose(r->pool, 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/http_core.h b/usr.sbin/httpd/src/http_core.h
new file mode 100644
index 00000000000..8454b81ad11
--- /dev/null
+++ b/usr.sbin/httpd/src/http_core.h
@@ -0,0 +1,205 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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)
+#define REMOTE_NOLOOKUP (2)
+
+#define SATISFY_ALL 0
+#define SATISFY_ANY 1
+#define SATISFY_NOSPEC 2
+
+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 *);
+int satisfies (request_rec *r);
+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;
+ /* since is_matchexp(conf->d) was being called so frequently in
+ * directory_walk() and its relatives, this field was created and
+ * is set to the result of that call.
+ */
+ int d_is_matchexp;
+
+ allow_options_t opts;
+ allow_options_t opts_add;
+ allow_options_t opts_remove;
+ overrides_t override;
+
+ /* MIME typing --- the core doesn't do anything at all with this,
+ * but it does know what to slap on a request for a document which
+ * goes untyped by other mechanisms before it slips out the door...
+ */
+
+ char *default_type;
+
+ /* Authentication stuff. Groan... */
+
+ int satisfy;
+ 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.
+ * if response_code_strings is NULL then there are none in the config,
+ * if it's not null then it's allocated to sizeof(char*)*RESPONSE_CODES.
+ * This lets us do quick merges in merge_core_dir_configs().
+ */
+
+ char **response_code_strings;
+
+ /* Hostname resolution etc */
+ int hostname_lookups;
+ int do_rfc1413; /* See if client is advertising a username? */
+
+ /* System Resource Control */
+#ifdef RLIMIT_CPU
+ struct rlimit *limit_cpu;
+#endif
+#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM)
+ struct rlimit *limit_mem;
+#endif
+#ifdef RLIMIT_NPROC
+ struct rlimit *limit_nproc;
+#endif
+
+ /* Access control */
+ array_header *sec;
+ regex_t *r;
+
+} 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/usr.sbin/httpd/src/http_log.c b/usr.sbin/httpd/src/http_log.c
new file mode 100644
index 00000000000..896c0a8dc59
--- /dev/null
+++ b/usr.sbin/httpd/src/http_log.c
@@ -0,0 +1,197 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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);
+#ifdef __EMX__
+ /* For OS/2 we need to use a '/' */
+ execl (SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+#else
+ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+ exit (1);
+}
+
+void open_error_log(server_rec *s, pool *p)
+{
+ char *fname;
+
+ fname = server_root_relative (p, s->error_fname);
+
+ if (*s->error_fname == '|') {
+ FILE *dummy;
+
+ if (!spawn_child (p, error_log_child, (void *)(s->error_fname+1),
+ kill_after_timeout, &dummy, NULL)) {
+ perror ("spawn_child");
+ fprintf (stderr, "Couldn't fork child for ErrorLog process\n");
+ exit (1);
+ }
+
+ s->error_log = dummy;
+ } else {
+ if(!(s->error_log = pfopen(p, fname, "a"))) {
+ perror("fopen");
+ fprintf(stderr,"httpd: could not open error log file %s.\n", fname);
+ 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(const char *reason, const 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/usr.sbin/httpd/src/http_log.h b/usr.sbin/httpd/src/http_log.h
new file mode 100644
index 00000000000..e30324db61d
--- /dev/null
+++ b/usr.sbin/httpd/src/http_log.h
@@ -0,0 +1,62 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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(const char *reason, const char *fname, request_rec *r);
+
diff --git a/usr.sbin/httpd/src/http_main.c b/usr.sbin/httpd/src/http_main.c
new file mode 100644
index 00000000000..e553a2d3636
--- /dev/null
+++ b/usr.sbin/httpd/src/http_main.c
@@ -0,0 +1,2620 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 Apache.
+ */
+
+#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 <assert.h>
+#include <sys/stat.h>
+#ifdef HAVE_SHMGET
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+#ifdef SecureWare
+#include <sys/security.h>
+#include <sys/audit.h>
+#include <prot.h>
+#endif
+#include <netinet/tcp.h>
+
+#ifdef HAVE_BSTRING_H
+#include <bstring.h> /* for IRIX, FD_SET calls bzero() */
+#endif
+
+#include "explain.h"
+
+#if !defined(max)
+#define max(a,b) (a > b ? a : b)
+#endif
+
+#ifdef __EMX__
+ /* Add MMAP style functionality to OS/2 */
+ #ifdef HAVE_MMAP
+ #define INCL_DOSMEMMGR
+ #include <os2.h>
+ #include <umalloc.h>
+ #include <stdio.h>
+ caddr_t create_shared_heap (const char *, size_t);
+ caddr_t get_shared_heap (const char *);
+ #endif
+#endif
+
+
+DEF_Explain
+
+/*
+ * 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;
+#ifdef MULTIPLE_GROUPS
+gid_t group_id_list[NGROUPS_MAX];
+#endif
+int max_requests_per_child;
+char *pid_fname;
+char *scoreboard_fname;
+char *lock_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;
+int suexec_enabled = 0;
+
+char server_root[MAX_STRING_LEN];
+char server_confname[MAX_STRING_LEN];
+
+/* *Non*-shared http_main globals... */
+
+server_rec *server_conf;
+JMP_BUF jmpbuffer;
+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 --- Apache doesn't keep track of their pids, it just sends
+ * SIGHUP to the process group, ignoring it in the root process.
+ * Continue through and you'll be fine.).
+ */
+
+int one_process = 0;
+
+/* small utility macros to make things easier to read */
+
+#ifdef NO_KILLPG
+#define ap_killpg(x, y) (kill (-(x), (y)))
+#else
+#define ap_killpg(x, y) (killpg ((x), (y)))
+#endif
+
+#if defined(USE_FCNTL_SERIALIZED_ACCEPT) || defined(USE_FLOCK_SERIALIZED_ACCEPT)
+static void expand_lock_fname(pool *p)
+{
+ char buf[20];
+
+ ap_snprintf( buf, sizeof(buf), ".%u", getpid() );
+ lock_fname = pstrcat (p, server_root_relative (p, lock_fname), buf, NULL);
+}
+#endif
+
+#if defined(USE_FCNTL_SERIALIZED_ACCEPT)
+static struct flock lock_it;
+static struct flock unlock_it;
+
+static int lock_fd=-1;
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+void
+accept_mutex_init(pool *p)
+{
+
+ lock_it.l_whence = SEEK_SET; /* from current point */
+ lock_it.l_start = 0; /* -"- */
+ lock_it.l_len = 0; /* until end of file */
+ lock_it.l_type = F_WRLCK; /* set exclusive/write lock */
+ lock_it.l_pid = 0; /* pid not actually interesting */
+ unlock_it.l_whence = SEEK_SET; /* from current point */
+ unlock_it.l_start = 0; /* -"- */
+ unlock_it.l_len = 0; /* until end of file */
+ unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */
+ unlock_it.l_pid = 0; /* pid not actually interesting */
+
+ expand_lock_fname (p);
+ lock_fd = popenf(p, lock_fname, O_CREAT | O_WRONLY | O_EXCL, 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!"
+ "Perhaps you need to use the LockFile directive to place "
+ "your lock file on a local disk!", 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!"
+ "Perhaps you need to use the LockFile directive to place "
+ "your lock file on a local disk!", server_conf);
+ exit(1);
+ }
+}
+#elif defined(USE_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)
+{
+
+ expand_lock_fname (p);
+ lock_fd = popenf(p, lock_fname, O_CREAT | O_WRONLY | O_EXCL, 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 = 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] [-h] [-l]\n",bin);
+ fprintf(stderr,"-d directory : specify an alternate initial ServerRoot\n");
+ fprintf(stderr,"-f file : specify an alternate ServerConfigFile\n");
+ fprintf(stderr,"-v : show version number\n");
+ fprintf(stderr,"-h : list directives\n");
+ fprintf(stderr,"-l : list modules\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;
+
+#ifndef NO_USE_SIGACTION
+/*
+ * Replace standard signal() with the more reliable sigaction equivalent
+ * from W. Richard Stevens' "Advanced Programming in the UNIX Environment"
+ * (the version that does not automatically restart system calls).
+ */
+Sigfunc *signal(int signo, Sigfunc *func)
+{
+ struct sigaction act, oact;
+
+ act.sa_handler = func;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+#ifdef SA_INTERRUPT /* SunOS */
+ act.sa_flags |= SA_INTERRUPT;
+#endif
+ if (sigaction(signo, &act, &oact) < 0)
+ return SIG_ERR;
+ return oact.sa_handler;
+}
+#endif
+
+void timeout(int sig) /* Also called on SIGPIPE */
+{
+ char errstr[MAX_STRING_LEN];
+ void *dirconf;
+
+ signal(SIGPIPE, SIG_IGN); /* Block SIGPIPE */
+ if (alarms_blocked) {
+ alarm_pending = 1;
+ return;
+ }
+
+ if (!current_conn) {
+ ap_longjmp (jmpbuffer, 1);
+ }
+
+ if (timeout_req != NULL) dirconf = timeout_req->per_dir_config;
+ else dirconf = current_conn->server->lookup_defaults;
+ if (sig == SIGPIPE) {
+ ap_snprintf(errstr, sizeof(errstr), "%s lost connection to client %s",
+ timeout_name ? timeout_name : "request",
+ get_remote_host(current_conn, dirconf, REMOTE_NAME));
+ } else {
+ ap_snprintf(errstr, sizeof(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);
+
+ bsetflag(timeout_req->connection->client, B_EOUT, 1);
+ bclose(timeout_req->connection->client);
+
+ if (!standalone) exit(0);
+
+ ap_longjmp (jmpbuffer, 1);
+ }
+ else { /* abort the connection */
+ bsetflag(current_conn->client, B_EOUT, 1);
+ current_conn->aborted = 1;
+ }
+}
+
+/*
+ * 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 keepalive_timeout (char *name, request_rec *r)
+{
+ timeout_req = r;
+ timeout_name = name;
+
+ signal(SIGALRM, timeout);
+ if (r->connection->keptalive)
+ alarm (r->server->keep_alive_timeout);
+ else
+ alarm (r->server->timeout);
+}
+
+void hard_timeout (char *name, request_rec *r)
+{
+ timeout_req = r;
+ timeout_name = name;
+
+ signal(SIGALRM, timeout);
+ alarm (r->server->timeout);
+}
+
+void soft_timeout (char *name, request_rec *r)
+{
+ timeout_name = name;
+
+ signal(SIGALRM, 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);
+ }
+}
+
+/*
+ * More machine-dependent networking gooo... on some systems,
+ * you've got to be *really* sure that all the packets are acknowledged
+ * before closing the connection, since the client will not be able
+ * to see the last response if their TCP buffer is flushed by a RST
+ * packet from us, which is what the server's TCP stack will send
+ * if it receives any request data after closing the connection.
+ *
+ * In an ideal world, this function would be accomplished by simply
+ * setting the socket option SO_LINGER and handling it within the
+ * server's TCP stack while the process continues on to the next request.
+ * Unfortunately, it seems that most (if not all) operating systems
+ * block the server process on close() when SO_LINGER is used.
+ * For those that don't, see USE_SO_LINGER below. For the rest,
+ * we have created a home-brew lingering_close.
+ *
+ * Many operating systems tend to block, puke, or otherwise mishandle
+ * calls to shutdown only half of the connection. You should define
+ * NO_LINGCLOSE in conf.h if such is the case for your system.
+ */
+#ifndef MAX_SECS_TO_LINGER
+#define MAX_SECS_TO_LINGER 30
+#endif
+
+#ifdef USE_SO_LINGER
+#define NO_LINGCLOSE /* The two lingering options are exclusive */
+
+static void sock_enable_linger (int s)
+{
+ struct linger li;
+
+ li.l_onoff = 1;
+ li.l_linger = MAX_SECS_TO_LINGER;
+
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER,
+ (char *)&li, sizeof(struct linger)) < 0) {
+ log_unixerr("setsockopt", "(SO_LINGER)", NULL, server_conf);
+ /* not a fatal error */
+ }
+}
+
+#else
+#define sock_enable_linger(s) /* NOOP */
+#endif /* USE_SO_LINGER */
+
+#ifndef NO_LINGCLOSE
+
+/* Special version of timeout for lingering_close */
+
+static void lingerout(sig)
+int sig;
+{
+ if (alarms_blocked) {
+ alarm_pending = 1;
+ return;
+ }
+
+ if (!current_conn) {
+ ap_longjmp (jmpbuffer, 1);
+ }
+ bsetflag(current_conn->client, B_EOUT, 1);
+ current_conn->aborted = 1;
+}
+
+static void linger_timeout ()
+{
+ timeout_name = "lingering close";
+
+ signal(SIGALRM, lingerout);
+ alarm(MAX_SECS_TO_LINGER);
+}
+
+/* Since many clients will abort a connection instead of closing it,
+ * attempting to log an error message from this routine will only
+ * confuse the webmaster. There doesn't seem to be any portable way to
+ * distinguish between a dropped connection and something that might be
+ * worth logging.
+ */
+static void lingering_close (request_rec *r)
+{
+ int dummybuf[512];
+ struct timeval tv;
+ fd_set lfds, fds_read, fds_err;
+ int select_rv = 0, read_rv = 0;
+ int lsd;
+
+ /* Prevent a slow-drip client from holding us here indefinitely */
+
+ linger_timeout();
+
+ /* Send any leftover data to the client, but never try to again */
+
+ if (bflush(r->connection->client) == -1) {
+ kill_timeout(r);
+ bclose(r->connection->client);
+ return;
+ }
+ bsetflag(r->connection->client, B_EOUT, 1);
+
+ /* Close our half of the connection --- send the client a FIN */
+
+ lsd = r->connection->client->fd;
+
+ if ((shutdown(lsd, 1) != 0) || r->connection->aborted) {
+ kill_timeout(r);
+ bclose(r->connection->client);
+ return;
+ }
+
+ /* Set up to wait for readable data on socket... */
+
+ FD_ZERO(&lfds);
+ FD_SET(lsd, &lfds);
+
+ /* Wait for readable data or error condition on socket;
+ * slurp up any data that arrives... We exit when we go for
+ * an interval of tv length without getting any more data, get an
+ * error from select(), get an exception on lsd, get an error or EOF
+ * on a read, or the timer expires.
+ */
+
+ do {
+ /* We use a 2 second timeout because current (Feb 97) browsers
+ * fail to close a connection after the server closes it. Thus,
+ * to avoid keeping the child busy, we are only lingering long enough
+ * for a client that is actively sending data on a connection.
+ * This should be sufficient unless the connection is massively
+ * losing packets, in which case we might have missed the RST anyway.
+ * These parameters are reset on each pass, since they might be
+ * changed by select.
+ */
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ read_rv = 0;
+ fds_read = lfds;
+ fds_err = lfds;
+
+#ifdef SELECT_NEEDS_CAST
+ select_rv = select(lsd+1, (int*)&fds_read, NULL, (int*)&fds_err, &tv);
+#else
+ select_rv = select(lsd+1, &fds_read, NULL, &fds_err, &tv);
+#endif
+ } while ((select_rv > 0) && /* Something to see on socket */
+ !FD_ISSET(lsd, &fds_err) && /* that isn't an error condition */
+ FD_ISSET(lsd, &fds_read) && /* and is worth trying to read */
+ ((read_rv = read(lsd, dummybuf, sizeof dummybuf)) > 0));
+
+ /* Should now have seen final ack. Safe to finally kill socket */
+
+ bclose(r->connection->client);
+
+ kill_timeout(r);
+}
+#endif /* ndef NO_LINGCLOSE */
+
+/*****************************************************************
+ *
+ * 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 scoreboard *scoreboard_image=NULL;
+
+static void setup_shared_mem(void)
+{
+ caddr_t m;
+
+#ifdef __EMX__
+ char errstr[MAX_STRING_LEN];
+ int rc;
+
+ m = (caddr_t)create_shared_heap("\\SHAREMEM\\SCOREBOARD", HARD_SERVER_LIMIT*sizeof(short_score));
+ if(m == 0) {
+ fprintf(stderr, "httpd: Could not create OS/2 Shared memory pool.\n");
+ exit(1);
+ }
+
+ rc = _uopen((Heap_t)m);
+ if(rc != 0) {
+ fprintf(stderr, "httpd: Could not uopen() newly created OS/2 Shared memory pool.\n");
+ }
+
+#elif defined(MAP_ANON) || defined(MAP_FILE)
+/* BSD style */
+#ifdef CONVEXOS11
+ /*
+ * 9-Aug-97 - Jeff Venters (venters@convex.hp.com)
+ * ConvexOS maps address space as follows:
+ * 0x00000000 - 0x7fffffff : Kernel
+ * 0x80000000 - 0xffffffff : User
+ * Start mmapped area 1GB above start of text.
+ *
+ * Also, the length requires a pointer as the actual length is
+ * returned (rounded up to a page boundary).
+ */
+ {
+ unsigned len = SCOREBOARD_SIZE;
+
+ m = mmap((caddr_t)0xC0000000, &len,
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, NOFD, 0);
+ }
+#else
+ m = mmap((caddr_t)0, SCOREBOARD_SIZE,
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
+#endif
+ if (m == (caddr_t)-1)
+ {
+ perror("mmap");
+ fprintf(stderr, "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, SCOREBOARD_SIZE,
+ 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 = (scoreboard *)m;
+ scoreboard_image->global.exit_generation=0;
+}
+
+#elif defined(HAVE_SHMGET)
+static scoreboard *scoreboard_image=NULL;
+static key_t shmkey = IPC_PRIVATE;
+static int shmid = -1;
+
+static void setup_shared_mem(void)
+{
+ char errstr[MAX_STRING_LEN];
+ struct shmid_ds shmbuf;
+#ifdef MOVEBREAK
+ char *obrk;
+#endif
+
+ if ((shmid = shmget(shmkey, SCOREBOARD_SIZE, IPC_CREAT|SHM_R|SHM_W)) == -1)
+ {
+#ifdef LINUX
+ if (errno == ENOSYS) {
+ fprintf(stderr,
+ "httpd: Your kernel was built without CONFIG_SYSVIPC\n"
+ "httpd: please consult the Apache FAQ for details\n");
+ }
+#endif
+ perror("shmget");
+ fprintf(stderr, "httpd: Could not call shmget\n");
+ exit(1);
+ }
+
+ ap_snprintf(errstr, sizeof(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 ((scoreboard *)(-1))
+ if ((scoreboard_image = (scoreboard *)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);
+ ap_snprintf(errstr, sizeof(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
+ scoreboard_image->global.exit_generation=0;
+}
+
+#else
+#define SCOREBOARD_FILE
+static scoreboard _scoreboard_image;
+static scoreboard *scoreboard_image=&_scoreboard_image;
+static int scoreboard_fd;
+
+/* XXX: things are seriously screwed if we ever have to do a partial
+ * read or write ... we could get a corrupted scoreboard
+ */
+static int force_write (int fd, 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) || (rv == -1 && errno == EINTR));
+
+ 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) || (rv == -1 && errno == EINTR));
+
+ return rv < 0? rv : orig_sz - bufsz;
+}
+#endif
+
+/* Called by parent process */
+void reinit_scoreboard (pool *p)
+{
+ int exit_gen=0;
+ if(scoreboard_image)
+ exit_gen=scoreboard_image->global.exit_generation;
+
+#ifndef SCOREBOARD_FILE
+ if (scoreboard_image == NULL)
+ {
+ setup_shared_mem();
+ }
+ memset(scoreboard_image, 0, SCOREBOARD_SIZE);
+ scoreboard_image->global.exit_generation=exit_gen;
+#else
+ scoreboard_fname = server_root_relative (p, scoreboard_fname);
+
+#ifdef __EMX__
+ /* OS/2 needs binary mode set. */
+ scoreboard_fd = popenf(p, scoreboard_fname, O_CREAT|O_BINARY|O_RDWR, 0644);
+#else
+ scoreboard_fd = popenf(p, scoreboard_fname, O_CREAT|O_RDWR, 0644);
+#endif
+ if (scoreboard_fd == -1)
+ {
+ perror (scoreboard_fname);
+ fprintf (stderr, "Cannot open scoreboard file:\n");
+ exit (1);
+ }
+
+ memset ((char*)scoreboard_image, 0, sizeof(*scoreboard_image));
+ scoreboard_image->global.exit_generation=exit_gen;
+ force_write (scoreboard_fd, (char*)scoreboard_image,
+ sizeof(*scoreboard_image));
+#endif
+}
+
+/* called by child */
+void reopen_scoreboard (pool *p)
+{
+#ifdef SCOREBOARD_FILE
+ if (scoreboard_fd != -1) pclosef (p, scoreboard_fd);
+
+#ifdef __EMX__
+ /* OS/2 needs binary mode set. */
+ scoreboard_fd = popenf(p, scoreboard_fname, O_CREAT|O_BINARY|O_RDWR, 0666);
+#else
+ scoreboard_fd = popenf(p, scoreboard_fname, O_CREAT|O_RDWR, 0666);
+#endif
+ if (scoreboard_fd == -1)
+ {
+ perror (scoreboard_fname);
+ fprintf (stderr, "Cannot open scoreboard file:\n");
+ exit (1);
+ }
+#else
+#ifdef __EMX__
+#ifdef HAVE_MMAP
+ caddr_t m;
+ int rc;
+
+ m = (caddr_t)get_shared_heap("\\SHAREMEM\\SCOREBOARD");
+ if(m == 0) {
+ fprintf(stderr, "httpd: Could not find existing OS/2 Shared memory pool.\n");
+ exit(1);
+ }
+
+ rc = _uopen((Heap_t)m);
+ scoreboard_image = (scoreboard *)m;
+#endif
+#endif
+#endif
+}
+
+void cleanup_scoreboard ()
+{
+#ifdef SCOREBOARD_FILE
+ 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 ()
+{
+#ifdef SCOREBOARD_FILE
+ lseek (scoreboard_fd, 0L, 0);
+ force_read (scoreboard_fd, (char*)scoreboard_image,
+ sizeof(*scoreboard_image));
+#endif
+}
+
+int exists_scoreboard_image ()
+{
+ return (scoreboard_image ? 1 : 0);
+}
+
+int update_child_status (int child_num, int status, request_rec *r)
+{
+ int old_status;
+ short_score new_score_rec;
+
+ if (child_num < 0)
+ return -1;
+
+ sync_scoreboard_image();
+ new_score_rec = scoreboard_image->servers[child_num];
+ 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_READY || status == SERVER_DEAD) {
+ /*
+ * Reset individual counters
+ */
+ if (status == SERVER_DEAD) {
+ 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_NOLOOKUP), 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';
+ slot_size = sizeof(new_score_rec.vhost) - 1;
+ strncpy(new_score_rec.vhost,r->server->server_hostname, slot_size);
+ new_score_rec.vhost[slot_size] = '\0';
+ }
+#endif
+
+#ifndef SCOREBOARD_FILE
+ memcpy(&scoreboard_image->servers[child_num], &new_score_rec, sizeof new_score_rec);
+#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;
+}
+
+void update_scoreboard_global()
+ {
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd,
+ (char *)&scoreboard_image->global-(char *)scoreboard_image,0);
+ force_write(scoreboard_fd,(char *)&scoreboard_image->global,
+ sizeof scoreboard_image->global);
+#endif
+ }
+
+int get_child_status (int child_num)
+{
+ if (child_num<0 || child_num>=HARD_SERVER_LIMIT)
+ return -1;
+ else
+ return scoreboard_image->servers[child_num].status;
+}
+
+int count_busy_servers ()
+{
+ int i;
+ int res = 0;
+
+ for (i = 0; i < HARD_SERVER_LIMIT; ++i)
+ if (scoreboard_image->servers[i].status == SERVER_BUSY_READ ||
+ scoreboard_image->servers[i].status == SERVER_BUSY_WRITE ||
+ scoreboard_image->servers[i].status == SERVER_BUSY_KEEPALIVE ||
+ scoreboard_image->servers[i].status == SERVER_BUSY_LOG ||
+ scoreboard_image->servers[i].status == SERVER_BUSY_DNS)
+ ++res;
+ return res;
+}
+
+int count_live_servers()
+ {
+ int i;
+ int res = 0;
+
+ for (i = 0; i < HARD_SERVER_LIMIT; ++i)
+ if (scoreboard_image->servers[i].status != SERVER_DEAD)
+ ++res;
+ return res;
+ }
+
+short_score get_scoreboard_info(int i)
+{
+ return (scoreboard_image->servers[i]);
+}
+
+#if defined(STATUS)
+static void increment_counts (int child_num, request_rec *r)
+{
+ long int bs=0;
+ short_score new_score_rec;
+
+ sync_scoreboard_image();
+ new_score_rec = scoreboard_image->servers[child_num];
+ if (r->sent_bodyct)
+ bgetopt(r->connection->client, BO_BYTECT, &bs);
+
+ 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);
+
+
+#ifndef SCOREBOARD_FILE
+ memcpy(&scoreboard_image->servers[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->servers[i].status == SERVER_READY)
+ ++res;
+
+ return res;
+}
+
+int find_free_child_num ()
+{
+ int i;
+
+ for (i = 0; i < HARD_SERVER_LIMIT; ++i)
+ if (scoreboard_image->servers[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->servers[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->servers[i].pid;
+
+ if (pid != my_pid && pid != 0) {
+ int waitret = 0,
+ tries = 1;
+
+ while (waitret == 0 && tries <= 4) {
+ long int waittime = 4096; /* in usecs */
+ struct timeval tv;
+
+ /* don't want to hold up progress any more than
+ * necessary, so keep checking to see if the child
+ * has exited with an exponential backoff.
+ * Currently set for a maximum wait of a bit over
+ * four seconds.
+ */
+ while (((waitret = waitpid(pid, &status, WNOHANG)) == 0) &&
+ waittime < 3000000) {
+ tv.tv_sec = waittime / 1000000;
+ tv.tv_usec = waittime % 1000000;
+ waittime = waittime * 2;
+ select(0, NULL, NULL, NULL, &tv);
+ }
+ if (waitret == 0) {
+ switch (tries) {
+ case 1:
+ /* perhaps it missed the SIGHUP, lets try again */
+ log_printf(server_conf, "child process %d did not exit, sending another SIGHUP", pid);
+ kill(pid, SIGHUP);
+ break;
+ case 2:
+ /* ok, now it's being annoying */
+ log_printf(server_conf, "child process %d still did not exit, sending a SIGTERM", pid);
+ kill(pid, SIGTERM);
+ break;
+ case 3:
+ /* die child scum */
+ log_printf(server_conf, "child process %d still did not exit, sending a SIGKILL", pid);
+ kill(pid, SIGKILL);
+ break;
+ case 4:
+ /* gave it our best shot, but alas... If this really
+ * is a child we are trying to kill and it really hasn't
+ * exited, we will likely fail to bind to the port
+ * after the restart.
+ */
+ log_printf(server_conf, "could not make child process %d exit, attempting to continue anyway", pid);
+ break;
+ }
+ }
+ tries++;
+ }
+ }
+ }
+}
+
+#if defined(BROKEN_WAIT) || defined(NEED_WAITPID)
+/*
+Some systems appear to fail to deliver dead children to wait() at times.
+This sorts them out. In fact, this may have been caused by a race condition
+in wait_or_timeout(). But this routine is still useful for systems with no
+waitpid().
+*/
+int reap_children ()
+{
+ int status, n;
+ int ret = 0;
+
+ for (n = 0; n < HARD_SERVER_LIMIT; ++n) {
+ if (scoreboard_image->servers[n].status != SERVER_DEAD
+ && waitpid (scoreboard_image->servers[n].pid, &status, WNOHANG)
+ == -1
+ && errno == ECHILD) {
+ sync_scoreboard_image ();
+ update_child_status (n, SERVER_DEAD, NULL);
+ ret = 1;
+ }
+ }
+ return ret;
+}
+#endif
+
+/* Finally, this routine is used by the caretaker process to wait for
+ * a while...
+ */
+
+static int wait_or_timeout ()
+{
+#ifndef NEED_WAITPID
+ int ret;
+
+ ret = waitpid (-1, NULL, WNOHANG);
+ if (ret == -1 && errno == EINTR) {
+ return -1;
+ }
+ if (ret <= 0) {
+ sleep (1);
+ return -1;
+ }
+ return ret;
+#else
+ if (!reap_children ()) {
+ sleep(1);
+ }
+ return -1;
+#endif
+}
+
+
+void sig_term() {
+ log_error("httpd: caught SIGTERM, shutting down", server_conf);
+ cleanup_scoreboard();
+ ap_killpg (pgrp, SIGKILL);
+ close(sd);
+ exit(1);
+}
+
+void bus_error(void) {
+ char emsg[256];
+
+ ap_snprintf
+ (
+ emsg,
+ sizeof(emsg) - 1,
+ "httpd: caught SIGBUS, attempting to dump core in %s",
+ server_root
+ );
+ log_error(emsg, server_conf);
+ chdir(server_root);
+ abort();
+ exit(1);
+}
+
+void seg_fault() {
+ char emsg[256];
+
+ ap_snprintf
+ (
+ emsg,
+ sizeof(emsg) - 1,
+ "httpd: caught SIGSEGV, attempting to dump core in %s",
+ server_root
+ );
+ log_error(emsg, server_conf);
+ chdir(server_root);
+ abort();
+ exit(1);
+}
+
+void just_die() /* SIGHUP to child process??? */
+{
+ exit (0);
+}
+
+static int deferred_die;
+
+static void deferred_die_handler ()
+{
+ deferred_die = 1;
+}
+
+/* volatile just in case */
+static volatile int restart_pending;
+static volatile int is_graceful;
+static volatile int generation;
+
+static void restart (int sig)
+{
+ is_graceful = (sig == SIGUSR1);
+ restart_pending = 1;
+}
+
+
+void set_signals()
+{
+#ifndef NO_USE_SIGACTION
+ struct sigaction sa;
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ if (!one_process) {
+ sa.sa_handler = (void (*)())seg_fault;
+ if (sigaction (SIGSEGV, &sa, NULL) < 0)
+ log_unixerr ("sigaction(SIGSEGV)", NULL, NULL, server_conf);
+ sa.sa_handler = (void (*)())bus_error;
+ if (sigaction (SIGBUS, &sa, NULL) < 0)
+ log_unixerr ("sigaction(SIGBUS)", NULL, NULL, server_conf);
+ }
+ sa.sa_handler = (void (*)())sig_term;
+ if (sigaction (SIGTERM, &sa, NULL) < 0)
+ log_unixerr ("sigaction(SIGTERM)", NULL, NULL, server_conf);
+
+ /* wait_or_timeout uses sleep() which could deliver a SIGALRM just as we're
+ * trying to process the restart requests. That's not good. restart
+ * cleans out the SIGALRM handler, but this totally avoids the race
+ * condition between when the restart request is made and when the handler
+ * is invoked.
+ *
+ * We also don't want to ignore HUPs and USR1 while we're busy processing
+ * one.
+ */
+ sigaddset (&sa.sa_mask, SIGALRM);
+ sigaddset (&sa.sa_mask, SIGHUP);
+ sigaddset (&sa.sa_mask, SIGUSR1);
+ sa.sa_handler = (void (*)())restart;
+ if (sigaction (SIGHUP, &sa, NULL) < 0)
+ log_unixerr ("sigaction(SIGHUP)", NULL, NULL, server_conf);
+ if (sigaction (SIGUSR1, &sa, NULL) < 0)
+ log_unixerr ("sigaction(SIGUSR1)", NULL, NULL, server_conf);
+#else
+ if(!one_process) {
+ signal (SIGSEGV, (void (*)())seg_fault);
+ signal (SIGBUS, (void (*)())bus_error);
+ }
+
+ signal (SIGTERM, (void (*)())sig_term);
+ signal (SIGHUP, (void (*)())restart);
+ signal (SIGUSR1, (void (*)())restart);
+#endif
+}
+
+
+/*****************************************************************
+ * Here follows a long bunch of generic server bookkeeping stuff...
+ */
+
+void detach()
+{
+ int x;
+
+ chdir("/");
+#ifndef MPE
+/* Don't detach for MPE because child processes can't survive the death of
+ the parent. */
+ if((x = fork()) > 0)
+ exit(0);
+ else if(x == -1) {
+ perror("fork");
+ fprintf(stderr,"httpd: unable to fork new process\n");
+ exit(1);
+ }
+#endif
+#ifndef NO_SETSID
+ if((pgrp=setsid()) == -1) {
+ perror("setsid");
+ fprintf(stderr,"httpd: setsid failed\n");
+ exit(1);
+ }
+#elif defined(NEXT)
+ if(setpgrp(0,getpid()) == -1 || (pgrp = getpgrp(0)) == -1) {
+ perror("setpgrp");
+ fprintf(stderr,"httpd: setpgrp or getpgrp failed\n");
+ exit(1);
+ }
+#elif defined(__EMX__)
+ /* OS/2 doesn't support process group IDs */
+ pgrp=getpid();
+#elif defined(MPE)
+ /* MPE uses negative pid for process group */
+ pgrp=-getpid();
+#else
+ if((pgrp=setpgrp(getpid(),0)) == -1) {
+ perror("setpgrp");
+ fprintf(stderr,"httpd: setpgrp failed\n");
+ exit(1);
+ }
+#endif
+}
+
+/* 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);
+ }
+#ifdef MULTIPLE_GROUPS
+ if (getgroups(NGROUPS_MAX, group_id_list) == -1) {
+ log_unixerr("getgroups", NULL, "unable to get group list", server_conf);
+ exit (1);
+ }
+#endif
+ if (setgid(group_id) == -1) {
+ log_unixerr("setgid", NULL, "unable to set group id", server_conf);
+ exit (1);
+ }
+#endif
+ }
+}
+
+/* check to see if we have the 'suexec' setuid wrapper installed */
+int init_suexec ()
+{
+ struct stat wrapper;
+
+ if ((stat(SUEXEC_BIN, &wrapper)) != 0)
+ return (suexec_enabled);
+
+ if ((wrapper.st_mode & S_ISUID) && wrapper.st_uid == 0) {
+ suexec_enabled = 1;
+ fprintf(stderr, "Configuring Apache for use with suexec wrapper.\n");
+ }
+
+ return (suexec_enabled);
+}
+
+/*****************************************************************
+ * 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 */
+
+static server_rec *find_virtual_server (struct in_addr server_ip,
+ unsigned port, server_rec *server)
+{
+ server_rec *virt;
+ server_addr_rec *sar;
+ server_rec *def;
+
+ def = server;
+ for (virt = server->next; virt; virt = virt->next) {
+ for (sar = virt->addrs; sar; sar = sar->next) {
+ if ((virt->is_virtual == 1) && /* VirtualHost */
+ (sar->host_addr.s_addr == htonl(INADDR_ANY) ||
+ sar->host_addr.s_addr == server_ip.s_addr) &&
+ (sar->host_port == 0 || sar->host_port == port)) {
+ return virt;
+ } else if ( sar->host_addr.s_addr == DEFAULT_VHOST_ADDR
+ && (sar->host_port == 0 || sar->host_port == port)) {
+ /* this is so that you can build a server that is the
+ "default" for any interface which isn't explicitly
+ specified. So that you can implement "deny anything
+ which isn't expressly permitted" -djg */
+ def = virt;
+ }
+ }
+ }
+
+ return def;
+}
+
+void default_server_hostnames(server_rec *s)
+{
+ struct hostent *h;
+ struct in_addr *main_addr;
+ int num_addr;
+ char *def_hostname;
+ int n;
+ server_addr_rec *sar;
+ int has_default_vhost_addr;
+ unsigned mainport = s->port;
+ int from_local=0;
+
+ /* Main host first */
+
+ if (!s->server_hostname) {
+ s->server_hostname = get_local_host(pconf);
+ from_local = 1;
+ }
+
+ def_hostname = s->server_hostname;
+ h = gethostbyname(def_hostname);
+ if( h == NULL ) {
+ fprintf(stderr,"httpd: cannot determine the IP address of ");
+ if (from_local) {
+ fprintf(stderr,"the local host (%s). Use ServerName to set it manually.\n",
+ s->server_hostname ? s->server_hostname : "<NULL>");
+ } else {
+ fprintf(stderr,"the specified ServerName (%s).\n",
+ s->server_hostname ? s->server_hostname : "<NULL>");
+ };
+ exit(1);
+ }
+ /* we need to use gethostbyaddr below... and since it shares a static
+ area with gethostbyname it'd clobber the value we just got. So
+ we need to make a copy. -djg */
+ for (num_addr = 0; h->h_addr_list[num_addr] != NULL; num_addr++) {
+ /* nop */
+ }
+ main_addr = palloc( pconf, sizeof( *main_addr ) * num_addr );
+ for (n = 0; n < num_addr; n++) {
+ main_addr[n] = *(struct in_addr *)h->h_addr_list[n];
+ }
+
+ /* 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 */
+ has_default_vhost_addr = 0;
+ for (n = 0; n < num_addr; n++) {
+ for(sar = s->addrs; sar; sar = sar->next) {
+ if (sar->host_addr.s_addr == main_addr[n].s_addr &&
+ s->port == mainport)
+ s->is_virtual = 2;
+ if( sar->host_addr.s_addr == DEFAULT_VHOST_ADDR ) {
+ has_default_vhost_addr = 1;
+ }
+ }
+ }
+
+ /* FIXME: some of this decision doesn't make a lot of sense in
+ the presence of multiple addresses on the <VirtualHost>
+ directive. It should issue warnings here perhaps. -djg */
+ if (!s->server_hostname) {
+ if (s->is_virtual == 2) {
+ if (s->addrs) {
+ s->server_hostname = s->addrs->virthost;
+ } else {
+ /* what else can we do? at this point this vhost has
+ no configured name, probably because they used
+ DNS in the VirtualHost statement. It's disabled
+ anyhow by the host matching code. -djg */
+ s->server_hostname = "bogus_host_without_forward_dns";
+ }
+ } else if (has_default_vhost_addr) {
+ s->server_hostname = def_hostname;
+ } else {
+ if (s->addrs
+ && (h = gethostbyaddr ((char *)&(s->addrs->host_addr),
+ sizeof (struct in_addr), AF_INET))) {
+ s->server_hostname = pstrdup (pconf, (char *)h->h_name);
+ } else {
+ /* again, what can we do? They didn't specify a
+ ServerName, and their DNS isn't working. -djg */
+ if (s->addrs) {
+ fprintf(stderr, "Failed to resolve server name "
+ "for %s (check DNS)\n",
+ inet_ntoa(s->addrs->host_addr));
+ }
+ s->server_hostname = "bogus_host_without_reverse_dns";
+ }
+ }
+ }
+ }
+}
+
+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));
+
+ /* Got a connection structure, so initialize what fields we can
+ * (the rest are zeroed out by pcalloc).
+ */
+
+ conn->child_num = child_num;
+
+ conn->pool = p;
+ conn->local_addr = *saddr;
+ conn->server = find_virtual_server(saddr->sin_addr, ntohs(saddr->sin_port),
+ server);
+ conn->base_server = conn->server;
+ conn->client = inout;
+
+ conn->remote_addr = *remaddr;
+ conn->remote_ip = pstrdup (conn->pool,
+ inet_ntoa(conn->remote_addr.sin_addr));
+
+ return conn;
+}
+
+#if defined(TCP_NODELAY) && !defined(MPE)
+static void sock_disable_nagle (int s)
+{
+ /* The Nagle algorithm says that we should delay sending partial
+ * packets in hopes of getting more data. We don't want to do
+ * this; we are not telnet. There are bad interactions between
+ * persistent connections and Nagle's algorithm that have very severe
+ * performance penalties. (Failing to disable Nagle is not much of a
+ * problem with simple HTTP.)
+ *
+ * In spite of these problems, failure here is not a shooting offense.
+ */
+ int just_say_no = 1;
+
+ if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&just_say_no,
+ sizeof(int)) < 0) {
+ log_unixerr("setsockopt", "(TCP_NODELAY)", NULL, server_conf);
+ }
+}
+#else
+#define sock_disable_nagle(s) /* NOOP */
+#endif
+
+/*****************************************************************
+ * Child process main loop.
+ * The following vars are static to avoid getting clobbered by longjmp();
+ * they are really private to child_main.
+ */
+
+static int srv;
+static int csd;
+static int dupped_csd;
+static int requests_this_child;
+static int child_num;
+static fd_set main_fds;
+
+void child_main(int child_num_arg)
+{
+#if defined(UW)
+ size_t clen;
+#else
+ int clen;
+#endif
+ 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);
+
+#ifdef MPE
+ /* Only try to switch if we're running as MANAGER.SYS */
+ if (geteuid() == 1 && user_id > 1) {
+ GETPRIVMODE();
+ if (setuid(user_id) == -1) {
+ GETUSERMODE();
+#else
+ /* Only try to switch if we're running as root */
+ if (!geteuid() && setuid(user_id) == -1) {
+#endif
+ log_unixerr("setuid", NULL, "unable to change uid", server_conf);
+ exit (1);
+ }
+#ifdef MPE
+ GETUSERMODE();
+ }
+#endif
+
+ /*
+ * Setup the jump buffers so that we can return here after
+ * a signal or a timeout (yeah, I know, same thing).
+ */
+ ap_setjmp (jmpbuffer);
+#ifndef __EMX__
+#ifdef SIGURG
+ signal(SIGURG, timeout);
+#endif
+#endif
+
+ while (1) {
+ int errsave;
+ BUFF *conn_io;
+ request_rec *r;
+
+ /* Prepare to receive a SIGUSR1 due to graceful restart so that
+ * we can exit cleanly. Since we're between connections right
+ * now it's the right time to exit, but we might be blocked in a
+ * system call when the graceful restart request is made. */
+ signal (SIGUSR1, (void (*)())just_die);
+
+ /*
+ * (Re)initialize this child to a pre-connection state.
+ */
+
+ 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 (scoreboard_image->global.exit_generation >= generation)
+ exit(0);
+
+ if ((count_idle_servers() >= daemons_max_free)
+ || (max_requests_per_child > 0
+ && ++requests_this_child >= max_requests_per_child))
+ {
+ exit(0);
+ }
+
+ (void)update_child_status(child_num, SERVER_READY, (request_rec*)NULL);
+
+ if (listeners == NULL) {
+ FD_ZERO(&listenfds);
+ FD_SET(sd, &listenfds);
+ listenmaxfd = sd;
+ }
+
+ /*
+ * Wait for an acceptable connection to arrive.
+ */
+
+ accept_mutex_on(); /* Lock around "accept", if necessary */
+
+ for (;;) {
+ memcpy(&main_fds, &listenfds, sizeof(fd_set));
+#ifdef SELECT_NEEDS_CAST
+ srv = select(listenmaxfd+1, (int*)&main_fds, NULL, NULL, NULL);
+#else
+ srv = select(listenmaxfd+1, &main_fds, NULL, NULL, NULL);
+#endif
+ errsave = errno;
+
+ sync_scoreboard_image();
+ if (scoreboard_image->global.exit_generation >= generation)
+ exit(0);
+
+ errno = errsave;
+ if (srv < 0 && errno != EINTR) {
+ /* Single Unix documents select as returning errnos
+ * EBADF, EINTR, and EINVAL... and in none of those
+ * cases does it make sense to continue. In fact
+ * on Linux 2.0.x we seem to end up with EFAULT
+ * occasionally, and we'd loop forever due to it.
+ */
+ log_unixerr("select", "(listen)", NULL, server_conf);
+ exit(1);
+ }
+
+ if (srv <= 0)
+ continue;
+
+ if (listeners != NULL) {
+ for (sd = listenmaxfd; sd >= 0; sd--)
+ if (FD_ISSET(sd, &main_fds)) break;
+ if (sd < 0)
+ continue;
+ }
+
+ /* if we accept() something we don't want to die, so we have to
+ * defer the exit
+ */
+ deferred_die = 0;
+ signal (SIGUSR1, (void (*)())deferred_die_handler);
+ for (;;) {
+ clen = sizeof(sa_client);
+ csd = accept(sd, &sa_client, &clen);
+ if (csd >= 0 || errno != EINTR) break;
+ if (deferred_die) {
+ /* we didn't get a socket, and we were told to die */
+ exit (0);
+ }
+ }
+
+ if (csd >= 0)
+ break; /* We have a socket ready for reading */
+ else {
+ /* Our old behaviour here was to continue after accept()
+ * errors. But this leads us into lots of troubles
+ * because most of the errors are quite fatal. For
+ * example, EMFILE can be caused by slow descriptor
+ * leaks (say in a 3rd party module, or libc). It's
+ * foolish for us to continue after an EMFILE. We also
+ * seem to tickle kernel bugs on some platforms which
+ * lead to never-ending loops here. So it seems best
+ * to just exit in most cases.
+ */
+ switch (errno) {
+#ifdef EPROTO
+ /* EPROTO on certain older kernels really means
+ * ECONNABORTED, so we need to ignore it for them.
+ * See discussion in new-httpd archives nh.9701
+ * search for EPROTO.
+ *
+ * Also see nh.9603, search for EPROTO:
+ * There is potentially a bug in Solaris 2.x x<6,
+ * and other boxes that implement tcp sockets in
+ * userland (i.e. on top of STREAMS). On these
+ * systems, EPROTO can actually result in a fatal
+ * loop. See PR#981 for example. It's hard to
+ * handle both uses of EPROTO.
+ */
+ case EPROTO:
+#endif
+#ifdef ECONNABORTED
+ case ECONNABORTED:
+#endif
+ /* Linux generates the rest of these, other tcp
+ * stacks (i.e. bsd) tend to hide them behind
+ * getsockopt() interfaces. They occur when
+ * the net goes sour or the client disconnects
+ * after the three-way handshake has been done
+ * in the kernel but before userland has picked
+ * up the socket.
+ */
+#ifdef ECONNRESET
+ case ECONNRESET:
+#endif
+#ifdef ETIMEDOUT
+ case ETIMEDOUT:
+#endif
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH:
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH:
+#endif
+ break;
+
+ default:
+ log_unixerr("accept", "(client socket)", NULL, server_conf);
+ exit(1);
+ }
+ }
+
+ /* go around again, safe to die */
+ signal (SIGUSR1, (void (*)())just_die);
+ if (deferred_die) {
+ /* ok maybe not, see ya later */
+ exit (0);
+ }
+ }
+
+ accept_mutex_off(); /* unlock after "accept" */
+
+ /* We've got a socket, let's at least process one request off the
+ * socket before we accept a graceful restart request.
+ */
+ signal (SIGUSR1, SIG_IGN);
+
+ note_cleanups_for_fd(ptrans,csd);
+
+ /*
+ * We now have a connection, so set it up with the appropriate
+ * socket options, file descriptors, and read/write buffers.
+ */
+
+ clen = sizeof(sa_server);
+ if (getsockname(csd, &sa_server, &clen) < 0) {
+ log_unixerr("getsockname", NULL, NULL, server_conf);
+ continue;
+ }
+
+ sock_disable_nagle(csd);
+
+ (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... */
+ }
+ note_cleanups_for_fd(ptrans,dupped_csd);
+#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);
+
+ /*
+ * Read and process each request found on our connection
+ * until no requests are left or we decide to close.
+ */
+
+ while ((r = read_request(current_conn)) != NULL) {
+
+ /* ok we've read the request... it's a little too late
+ * to do a graceful restart, so ignore them for now.
+ */
+ signal (SIGUSR1, SIG_IGN);
+
+ (void)update_child_status(child_num, SERVER_BUSY_WRITE, r);
+
+ process_request(r);
+#if defined(STATUS)
+ increment_counts(child_num, r);
+#endif
+ if (!current_conn->keepalive || current_conn->aborted)
+ break;
+
+ destroy_pool(r->pool);
+ (void)update_child_status(child_num, SERVER_BUSY_KEEPALIVE,
+ (request_rec*)NULL);
+
+ sync_scoreboard_image();
+ if (scoreboard_image->global.exit_generation >= generation) {
+ bclose(conn_io);
+ exit(0);
+ }
+
+ /* In case we get a graceful restart while we're blocked
+ * waiting for the request.
+ *
+ * XXX: This isn't perfect, we might actually read the
+ * request and then just die without saying anything to
+ * the client. This can be fixed by using deferred_die
+ * but you have to teach buff.c about it so that it can handle
+ * the EINTR properly.
+ *
+ * In practice though browsers (have to) expect keepalive
+ * connections to close before receiving a response because
+ * of network latencies and server timeouts.
+ */
+ signal (SIGUSR1, (void (*)())just_die);
+ }
+
+ /*
+ * Close the connection, being careful to send out whatever is still
+ * in our buffers. If possible, try to avoid a hard close until the
+ * client has ACKed our FIN and/or has stopped sending us data.
+ */
+
+#ifdef NO_LINGCLOSE
+ bclose(conn_io); /* just close it */
+#else
+ if (r && r->connection
+ && !r->connection->aborted
+ && r->connection->client
+ && (r->connection->client->fd >= 0)) {
+
+ lingering_close(r);
+ }
+ else {
+ bsetflag(conn_io, B_EOUT, 1);
+ bclose(conn_io);
+ }
+#endif
+ }
+}
+
+int 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);
+ }
+
+ Explain1 ("Starting new child in slot %d", child_num);
+ (void)update_child_status (child_num, SERVER_STARTING, (request_rec *)NULL);
+
+ if ((pid = fork()) == -1) {
+ log_unixerr("fork", NULL, "Unable to fork new process", server_conf);
+
+ /* fork didn't succeed. Fix the scoreboard or else
+ * it will say SERVER_STARTING forever and ever
+ */
+ (void)update_child_status (child_num, SERVER_DEAD, (request_rec*)NULL);
+
+ /* In case system resources are maxxed out, we don't want
+ Apache running away with the CPU trying to fork over and
+ over and over again. */
+ sleep(10);
+
+ return -1;
+ }
+
+ if (!pid) {
+ /* Disable the restart signal handlers and enable the just_die stuff.
+ * Note that since restart() just notes that a restart has been
+ * requested there's no race condition here.
+ */
+ signal (SIGHUP, (void (*)())just_die);
+ signal (SIGUSR1, (void (*)())just_die);
+ signal (SIGTERM, (void (*)())just_die);
+ child_main (child_num);
+ }
+
+ /* If the parent proceeds with a restart before the child has written
+ * their pid into the scoreboard we'll end up "forgetting" about the
+ * child. So we write the child pid into the scoreboard now. (This
+ * is safe, because the child is going to be writing the same value
+ * to the same word.)
+ * XXX: this needs to be sync'd to disk in the non shared memory stuff
+ */
+ scoreboard_image->servers[child_num].pid = pid;
+
+ return 0;
+}
+
+static int make_sock(pool *pconf, const struct sockaddr_in *server)
+{
+ int s;
+ int one = 1;
+
+ if ((s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) {
+ log_unixerr("socket", NULL, "Failed to get a socket, exiting child",
+ server_conf);
+ exit(1);
+ }
+
+ /* Solaris (probably versions 2.4, 2.5, and 2.5.1 with various levels
+ * of tcp patches) has some really weird bugs where if you dup the
+ * socket now it breaks things across SIGHUP restarts. It'll either
+ * be unable to bind, or it won't respond.
+ */
+#ifndef SOLARIS2
+ s = ap_slack(s, AP_SLACK_HIGH);
+
+ note_cleanups_for_fd(pconf, s); /* arrange to close on exec or restart */
+#endif
+
+#ifndef MPE
+/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */
+ if (setsockopt(s, SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(int)) < 0) {
+ log_unixerr("setsockopt", "(SO_REUSEADDR)", NULL, server_conf);
+ exit(1);
+ }
+ one = 1;
+ if (setsockopt(s, SOL_SOCKET,SO_KEEPALIVE,(char *)&one,sizeof(int)) < 0) {
+ log_unixerr("setsockopt", "(SO_KEEPALIVE)", NULL, server_conf);
+ exit(1);
+ }
+#endif
+
+ sock_disable_nagle(s);
+ sock_enable_linger(s);
+
+ /*
+ * To send data over high bandwidth-delay connections at full
+ * speed we must force the TCP window to open wide enough to keep the
+ * pipe full. The default window size on many systems
+ * is only 4kB. Cross-country WAN connections of 100ms
+ * at 1Mb/s are not impossible for well connected sites.
+ * If we assume 100ms cross-country latency,
+ * a 4kB buffer limits throughput to 40kB/s.
+ *
+ * To avoid this problem I've added the SendBufferSize directive
+ * to allow the web master to configure send buffer size.
+ *
+ * The trade-off of larger buffers is that more kernel memory
+ * is consumed. YMMV, know your customers and your network!
+ *
+ * -John Heidemann <johnh@isi.edu> 25-Oct-96
+ *
+ * If no size is specified, use the kernel default.
+ */
+ if (server_conf->send_buffer_size) {
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
+ (char *)&server_conf->send_buffer_size, sizeof(int)) < 0) {
+ log_unixerr("setsockopt", "(SO_SNDBUF)",
+ "Failed to set SendBufferSize, using default",
+ server_conf);
+ /* not a fatal error */
+ }
+ }
+
+#ifdef MPE
+/* MPE requires CAP=PM and GETPRIVMODE to bind to ports less than 1024 */
+ if (ntohs(server->sin_port) < 1024) GETPRIVMODE();
+#endif
+ if(bind(s, (struct sockaddr *)server,sizeof(struct sockaddr_in)) == -1)
+ {
+ perror("bind");
+#ifdef MPE
+ if (ntohs(server->sin_port) < 1024) GETUSERMODE();
+#endif
+ 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);
+ }
+#ifdef MPE
+ if (ntohs(server->sin_port) < 1024) GETUSERMODE();
+#endif
+ listen(s, 512);
+
+#ifdef SOLARIS2
+ s = ap_slack(s, AP_SLACK_HIGH);
+
+ note_cleanups_for_fd(pconf, s); /* arrange to close on exec or restart */
+#endif
+ return s;
+}
+
+static listen_rec *old_listeners;
+
+static void copy_listeners(pool *p)
+ {
+ listen_rec *lr;
+
+ assert(old_listeners == NULL);
+ for(lr=listeners ; lr ; lr=lr->next)
+ {
+ listen_rec *nr=malloc(sizeof *nr);
+ if (nr == NULL) {
+ fprintf (stderr, "Ouch! malloc failed in copy_listeners()\n");
+ exit (1);
+ }
+ *nr=*lr;
+ kill_cleanups_for_fd(p,nr->fd);
+ nr->next=old_listeners;
+ assert(!nr->used);
+ old_listeners=nr;
+ }
+ }
+
+static int find_listener(listen_rec *lr)
+ {
+ listen_rec *or;
+
+ for(or=old_listeners ; or ; or=or->next)
+ if(!memcmp(&or->local_addr,&lr->local_addr,sizeof or->local_addr))
+ {
+ or->used=1;
+ return or->fd;
+ }
+ return -1;
+ }
+
+static void close_unused_listeners()
+ {
+ listen_rec *or,*next;
+
+ for(or=old_listeners ; or ; or=next)
+ {
+ next=or->next;
+ if(!or->used)
+ close(or->fd);
+ free(or);
+ }
+ old_listeners=NULL;
+ }
+
+/*****************************************************************
+ * Executive routines.
+ */
+
+void standalone_main(int argc, char **argv)
+{
+ struct sockaddr_in sa_server;
+ int saved_sd;
+ int remaining_children_to_start;
+
+ standalone = 1;
+ sd = listenmaxfd = -1;
+
+ is_graceful = 0;
+ ++generation;
+
+ if (!one_process) detach ();
+
+ do {
+ copy_listeners(pconf);
+ saved_sd = sd;
+ if (!is_graceful) {
+ restart_time = time(NULL);
+ }
+#ifdef SCOREBOARD_FILE
+ else {
+ kill_cleanups_for_fd (pconf, scoreboard_fd);
+ }
+#endif
+ clear_pool (pconf);
+ ptrans = make_sub_pool (pconf);
+
+ server_conf = read_config (pconf, ptrans, server_confname);
+
+ if (listeners == NULL) {
+ if (!is_graceful) {
+ 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 {
+ sd = saved_sd;
+ note_cleanups_for_fd(pconf, sd);
+ }
+ }
+ else {
+ listen_rec *lr;
+ int fd;
+
+ listenmaxfd = -1;
+ FD_ZERO (&listenfds);
+ for (lr = listeners; lr != NULL; lr = lr->next)
+ {
+ fd = find_listener (lr);
+ if (fd < 0) {
+ fd = make_sock (pconf, &lr->local_addr);
+ }
+ FD_SET (fd, &listenfds);
+ if (fd > listenmaxfd) listenmaxfd = fd;
+ lr->fd = fd;
+ }
+ close_unused_listeners ();
+ sd = -1;
+ }
+
+ init_modules (pconf, server_conf);
+ open_logs (server_conf, pconf);
+ set_group_privs ();
+ accept_mutex_init (pconf);
+ if (!is_graceful) {
+ reinit_scoreboard(pconf);
+ }
+#ifdef SCOREBOARD_FILE
+ else {
+ scoreboard_fname = server_root_relative (pconf, scoreboard_fname);
+ note_cleanups_for_fd (pconf, scoreboard_fd);
+ }
+#endif
+
+ default_server_hostnames (server_conf);
+
+ set_signals ();
+ log_pid (pconf, pid_fname);
+
+ if (daemons_max_free < daemons_min_free + 1) /* Don't thrash... */
+ daemons_max_free = daemons_min_free + 1;
+
+ /* If we're doing a graceful_restart then we're going to see a lot
+ * of children exiting immediately when we get into the main loop
+ * below (because we just sent them SIGUSR1). This happens pretty
+ * rapidly... and for each one that exits we'll start a new one until
+ * we reach at least daemons_min_free. But we may be permitted to
+ * start more than that, so we'll just keep track of how many we're
+ * supposed to start up without the 1 second penalty between each fork.
+ */
+ remaining_children_to_start = daemons_to_start;
+ if( remaining_children_to_start > daemons_limit ) {
+ remaining_children_to_start = daemons_limit;
+ }
+ if (!is_graceful) {
+ while (remaining_children_to_start) {
+ --remaining_children_to_start;
+ make_child (server_conf, remaining_children_to_start);
+ }
+ }
+
+ log_error ("Server configured -- resuming normal operations",
+ server_conf);
+ restart_pending = 0;
+
+ while (!restart_pending) {
+ int child_slot;
+ int pid = wait_or_timeout ();
+
+ /* XXX: if it takes longer than 1 second for all our children
+ * to start up and get into IDLE state then we may spawn an
+ * extra child
+ */
+ if (pid >= 0) {
+ /* Child died... note that it's gone in the scoreboard. */
+ sync_scoreboard_image ();
+ child_slot = find_child_by_pid (pid);
+ Explain2 ("Reaping child %d slot %d", pid, child_slot);
+ if (child_slot >= 0) {
+ (void)update_child_status (child_slot, SERVER_DEAD,
+ (request_rec *)NULL);
+ } else if (is_graceful) {
+ /* Great, we've probably just lost a slot in the
+ * scoreboard. Somehow we don't know about this
+ * child.
+ */
+ log_printf (server_conf,
+ "long lost child came home! (pid %d)", pid );
+ }
+ } else if (remaining_children_to_start) {
+ /* we hit a 1 second timeout in which none of the previous
+ * generation of children needed to be reaped... so assume
+ * they're all done, and pick up the slack if any is left.
+ */
+ while (remaining_children_to_start > 0) {
+ child_slot = find_free_child_num ();
+ if (child_slot < 0 || child_slot >= daemons_limit) {
+ remaining_children_to_start = 0;
+ break;
+ }
+ if (make_child (server_conf, child_slot) < 0) {
+ remaining_children_to_start = 0;
+ break;
+ }
+ --remaining_children_to_start;
+ }
+ /* In any event we really shouldn't do the code below because
+ * few of the servers we just started are in the IDLE state
+ * yet, so we'd mistakenly create an extra server.
+ */
+ continue;
+ }
+
+ sync_scoreboard_image ();
+ if ((remaining_children_to_start
+ || (count_idle_servers () < daemons_min_free))
+ && (child_slot = find_free_child_num ()) >= 0
+ && child_slot < daemons_limit) {
+ make_child (server_conf, child_slot);
+ }
+ if (remaining_children_to_start) {
+ --remaining_children_to_start;
+ }
+ }
+
+ /* we've been told to restart */
+ signal (SIGHUP, SIG_IGN);
+ signal (SIGUSR1, SIG_IGN);
+
+ if (one_process) {
+ /* not worth thinking about */
+ exit (0);
+ }
+
+ if (is_graceful) {
+#ifndef SCOREBOARD_FILE
+ int i;
+#endif
+
+ /* USE WITH CAUTION: Graceful restarts are not known to work
+ * in various configurations on the architectures we support. */
+ scoreboard_image->global.exit_generation = generation;
+ update_scoreboard_global ();
+
+ log_error ("SIGUSR1 received. Doing graceful restart",server_conf);
+ kill_cleanups_for_fd (pconf, sd);
+ /* kill off the idle ones */
+ if (ap_killpg(pgrp, SIGUSR1) < 0) {
+ log_unixerr ("killpg SIGUSR1", NULL, NULL, server_conf);
+ }
+#ifndef SCOREBOARD_FILE
+ /* This is mostly for debugging... so that we know what is still
+ * gracefully dealing with existing request. But we can't really
+ * do it if we're in a SCOREBOARD_FILE because it'll cause
+ * corruption too easily.
+ */
+ sync_scoreboard_image();
+ for (i = 0; i < daemons_limit; ++i ) {
+ if (scoreboard_image->servers[i].status != SERVER_DEAD) {
+ scoreboard_image->servers[i].status = SERVER_GRACEFUL;
+ }
+ }
+#endif
+ }
+ else {
+ /* Kill 'em off */
+ if (ap_killpg (pgrp, SIGHUP) < 0) {
+ log_unixerr ("killpg SIGHUP", NULL, NULL, server_conf);
+ }
+ reclaim_child_processes(); /* Not when just starting up */
+ log_error ("SIGHUP received. Attempting to restart", server_conf);
+ }
+ ++generation;
+
+ } while (restart_pending);
+
+} /* 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];
+ strncpy (server_root, HTTPD_ROOT, sizeof(server_root)-1);
+ server_root[sizeof(server_root)-1] = '\0';
+ strncpy (server_confname, SERVER_CONFIG_FILE, sizeof(server_root)-1);
+ server_confname[sizeof(server_confname)-1] = '\0';
+
+ while((c = getopt(argc,argv,"Xd:f:vhl")) != -1) {
+ switch(c) {
+ case 'd':
+ strncpy (server_root, optarg, sizeof(server_root)-1);
+ server_root[sizeof(server_root)-1] = '\0';
+ break;
+ case 'f':
+ strncpy (server_confname, optarg, sizeof(server_confname)-1);
+ server_confname[sizeof(server_confname)-1] = '\0';
+ break;
+ case 'v':
+ printf("Server version %s.\n",SERVER_VERSION);
+ exit(0);
+ case 'h':
+ show_directives();
+ exit(0);
+ case 'l':
+ show_modules();
+ exit(0);
+ 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();
+
+ suexec_enabled = init_suexec();
+ server_conf = read_config (pconf, ptrans, server_confname);
+ init_modules (pconf, server_conf);
+
+ 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);
+
+#ifdef MPE
+ /* Only try to switch if we're running as MANAGER.SYS */
+ if (geteuid() == 1 && user_id > 1) {
+ GETPRIVMODE();
+ if (setuid(user_id) == -1) {
+ GETUSERMODE();
+#else
+ /* Only try to switch if we're running as root */
+ if(!geteuid() && setuid(user_id) == -1) {
+#endif
+ log_unixerr("setuid", NULL, "unable to change uid", server_conf);
+ exit (1);
+ }
+#ifdef MPE
+ GETUSERMODE();
+ }
+#endif
+ if (ap_setjmp (jmpbuffer)) {
+ exit (0);
+ }
+
+ 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);
+#ifdef MPE
+/* HP MPE 5.5 inetd only passes the incoming socket as stdin (fd 0), whereas
+ HPUX inetd passes the incoming socket as stdin (fd 0) and stdout (fd 1).
+ Go figure. SR 5003355016 has been submitted to request that the existing
+ functionality be documented, and then to enhance the functionality to be
+ like HPUX. */
+
+ cio->fd = fileno(stdin);
+#else
+ cio->fd = fileno(stdout);
+#endif
+ 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 && !conn->aborted) {
+ destroy_pool(r->pool);
+ r = read_request (conn);
+ if (r) process_request (r);
+ }
+
+ bclose(cio);
+ }
+ exit (0);
+}
+
+#ifdef __EMX__
+#ifdef HAVE_MMAP
+/* The next two routines are used to access shared memory under OS/2. */
+/* This requires EMX v09c to be installed. */
+
+caddr_t create_shared_heap (const char *name, size_t size)
+{
+ ULONG rc;
+ void *mem;
+ Heap_t h;
+
+ rc = DosAllocSharedMem (&mem, name, size,
+ PAG_COMMIT | PAG_READ | PAG_WRITE);
+ if (rc != 0)
+ return NULL;
+ h = _ucreate (mem, size, !_BLOCK_CLEAN, _HEAP_REGULAR | _HEAP_SHARED,
+ NULL, NULL);
+ if (h == NULL)
+ DosFreeMem (mem);
+ return (caddr_t)h;
+}
+
+caddr_t get_shared_heap (const char *Name)
+{
+
+ PVOID BaseAddress; /* Pointer to the base address of
+ the shared memory object */
+ ULONG AttributeFlags; /* Flags describing characteristics
+ of the shared memory object */
+ APIRET rc; /* Return code */
+
+ /* Request read and write access to */
+ /* the shared memory object */
+ AttributeFlags = PAG_WRITE | PAG_READ;
+
+ rc = DosGetNamedSharedMem(&BaseAddress, Name, AttributeFlags);
+
+ if(rc != 0) {
+ printf("DosGetNamedSharedMem error: return code = %ld", rc);
+ return 0;
+ }
+
+ return BaseAddress;
+}
+#endif
+#endif
+
diff --git a/usr.sbin/httpd/src/http_main.h b/usr.sbin/httpd/src/http_main.h
new file mode 100644
index 00000000000..7d3c2f5a789
--- /dev/null
+++ b/usr.sbin/httpd/src/http_main.h
@@ -0,0 +1,99 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 keepalive_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/usr.sbin/httpd/src/http_protocol.c b/usr.sbin/httpd/src/http_protocol.c
new file mode 100644
index 00000000000..f34097260c1
--- /dev/null
+++ b/usr.sbin/httpd/src/http_protocol.c
@@ -0,0 +1,2047 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 Robert S. Thau
+ * and the Apache Group.
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h" /* For errors detected in basic auth
+ * common support code...
+ */
+#include "util_date.h" /* For parseHTTPdate and BAD_DATE */
+#include <stdarg.h>
+
+#define SET_BYTES_SENT(r) \
+ do { if (r->sent_bodyct) \
+ bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
+ } while (0)
+
+
+static int parse_byterange (char *range, long clength, long *start, long *end)
+{
+ char *dash = strchr(range, '-');
+
+ if (!dash)
+ return 0;
+
+ if ((dash == range)) {
+ /* In the form "-5" */
+ *start = clength - atol(dash + 1);
+ *end = clength - 1;
+ }
+ else {
+ *dash = '\0';
+ dash++;
+ *start = atol(range);
+ if (*dash)
+ *end = atol(dash);
+ else /* "5-" */
+ *end = clength -1;
+ }
+
+ if (*start > *end)
+ return 0;
+
+ if (*end >= clength)
+ *end = clength - 1;
+
+ return 1;
+}
+
+static int internal_byterange(int, long*, request_rec*, char**, long*, long*);
+
+int set_byterange (request_rec *r)
+{
+ char *range, *if_range, *match;
+ char ts[MAX_STRING_LEN];
+ long range_start, range_end;
+
+ if (!r->clength || r->assbackwards) return 0;
+
+ /* Check for Range request-header (HTTP/1.1) or Request-Range for
+ * backwards-compatibility with second-draft Luotonen/Franks
+ * byte-ranges (e.g. Netscape Navigator 2-3).
+ *
+ * We support this form, with Request-Range, and (farther down) we
+ * send multipart/x-byteranges instead of multipart/byteranges for
+ * Request-Range based requests to work around a bug in Netscape
+ * Navigator 2-3 and MSIE 3.
+ */
+
+ if (!(range = table_get(r->headers_in, "Range")))
+ range = table_get(r->headers_in, "Request-Range");
+
+ if (!range || strncasecmp(range, "bytes=", 6)) {
+ table_set (r->headers_out, "Accept-Ranges", "bytes");
+ return 0;
+ }
+
+ /* Check the If-Range header for Etag or Date */
+
+ if ((if_range = table_get(r->headers_in, "If-Range"))) {
+ if (if_range[0] == '"') {
+ if (!(match = table_get(r->headers_out, "Etag")) ||
+ (strcasecmp(if_range, match) != 0))
+ return 0;
+ }
+ else if (!(match = table_get(r->headers_out, "Last-Modified")) ||
+ (strcasecmp(if_range, match) != 0))
+ return 0;
+ }
+
+ if (!strchr(range, ',')) {
+ /* A single range */
+ if (!parse_byterange(pstrdup(r->pool, range + 6), r->clength,
+ &range_start, &range_end))
+ return 0;
+
+ r->byterange = 1;
+
+ ap_snprintf(ts, sizeof(ts), "bytes %ld-%ld/%ld",
+ range_start, range_end, r->clength);
+ table_set(r->headers_out, "Content-Range", ts);
+ ap_snprintf(ts, sizeof(ts), "%ld", range_end - range_start + 1);
+ table_set(r->headers_out, "Content-Length", ts);
+ }
+ else {
+ /* a multiple range */
+ char boundary[33]; /* Long enough */
+ char *r_range = pstrdup(r->pool, range + 6);
+ long tlength = 0;
+
+ r->byterange = 2;
+ ap_snprintf(boundary, sizeof(boundary), "%lx%lx",
+ r->request_time, (long)getpid());
+ r->boundary = pstrdup(r->pool, boundary);
+ while (internal_byterange(0, &tlength, r, &r_range, NULL, NULL));
+ ap_snprintf(ts, sizeof(ts), "%ld", tlength);
+ table_set(r->headers_out, "Content-Length", ts);
+ }
+
+ r->status = PARTIAL_CONTENT;
+ r->range = range + 6;
+
+ return 1;
+}
+
+int each_byterange (request_rec *r, long *offset, long *length)
+{
+ return internal_byterange(1, NULL, r, &r->range, offset, length);
+}
+
+/* If this function is called with realreq=1, it will spit out
+ * the correct headers for a byterange chunk, and set offset and
+ * length to the positions they should be.
+ *
+ * If it is called with realreq=0, it will add to tlength the length
+ * it *would* have used with realreq=1.
+ *
+ * Either case will return 1 if it should be called again, and 0
+ * when done.
+ *
+ */
+
+static int internal_byterange(int realreq, long *tlength, request_rec *r,
+ char **r_range, long *offset, long *length)
+{
+ long range_start, range_end;
+ char *range;
+
+ if (!**r_range) {
+ if (r->byterange > 1) {
+ if (realreq)
+ rvputs(r, "\015\012--", r->boundary, "--\015\012", NULL);
+ else
+ *tlength += 4 + strlen(r->boundary) + 4;
+ }
+ return 0;
+ }
+
+ range = getword_nc(r->pool, r_range, ',');
+ if (!parse_byterange(range, r->clength, &range_start, &range_end))
+ /* Skip this one */
+ return internal_byterange(realreq, tlength, r, r_range, offset,
+ length);
+
+ if (r->byterange > 1) {
+ char *ct = r->content_type ? r->content_type : default_type(r);
+ char ts[MAX_STRING_LEN];
+
+ ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end, r->clength);
+ if (realreq)
+ rvputs(r, "\015\012--", r->boundary, "\015\012Content-type: ",
+ ct, "\015\012Content-range: bytes ", ts, "\015\012\015\012",
+ NULL);
+ else
+ *tlength += 4 + strlen(r->boundary) + 16 + strlen(ct) + 23 +
+ strlen(ts) + 4;
+ }
+
+ if (realreq) {
+ *offset = range_start;
+ *length = range_end - range_start + 1;
+ }
+ else {
+ *tlength += range_end - range_start + 1;
+ }
+ return 1;
+}
+
+int set_content_length (request_rec *r, long clength)
+{
+ char ts[MAX_STRING_LEN];
+
+ r->clength = clength;
+
+ ap_snprintf (ts, sizeof(ts), "%ld", clength);
+ table_set (r->headers_out, "Content-Length", ts);
+
+ return 0;
+}
+
+int set_keepalive(request_rec *r)
+{
+ int ka_sent = 0;
+ int wimpy = find_token(r->pool,
+ table_get(r->headers_out, "Connection"), "close");
+ char *conn = table_get(r->headers_in, "Connection");
+
+ /* The following convoluted conditional determines whether or not
+ * the current connection should remain persistent after this response
+ * (a.k.a. HTTP Keep-Alive) and whether or not the output message
+ * body should use the HTTP/1.1 chunked transfer-coding. In English,
+ *
+ * IF we have not marked this connection as errored;
+ * and the response body has a defined length due to the status code
+ * being 304 or 204, the request method being HEAD, already
+ * having defined Content-Length or Transfer-Encoding: chunked, or
+ * the request version being HTTP/1.1 and thus capable of being set
+ * as chunked [we know the (r->chunked = 1) side-effect is ugly];
+ * and the server configuration enables keep-alive;
+ * and the server configuration has a reasonable inter-request timeout;
+ * and there is no maximum # requests or the max hasn't been reached;
+ * and the response status does not require a close;
+ * and the response generator has not already indicated close;
+ * and the client did not request non-persistence (Connection: close);
+ * and we haven't been configured to ignore the buggy twit
+ * or they're a buggy twit coming through a HTTP/1.1 proxy
+ * and the client is requesting an HTTP/1.0-style keep-alive
+ * or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
+ * THEN we can be persistent, which requires more headers be output.
+ *
+ * Note that the condition evaluation order is extremely important.
+ */
+ if ((r->connection->keepalive != -1) &&
+ ((r->status == HTTP_NOT_MODIFIED) ||
+ (r->status == HTTP_NO_CONTENT) ||
+ r->header_only ||
+ table_get(r->headers_out, "Content-Length") ||
+ find_last_token(r->pool,
+ table_get(r->headers_out, "Transfer-Encoding"),
+ "chunked") ||
+ ((r->proto_num >= 1001) && (r->chunked = 1))) &&
+ r->server->keep_alive &&
+ (r->server->keep_alive_timeout > 0) &&
+ ((r->server->keep_alive_max == 0) ||
+ (r->server->keep_alive_max > r->connection->keepalives)) &&
+ !status_drops_connection(r->status) &&
+ !wimpy &&
+ !find_token(r->pool, conn, "close") &&
+ (!table_get(r->subprocess_env, "nokeepalive") ||
+ table_get(r->headers_in, "Via")) &&
+ ((ka_sent = find_token(r->pool, conn, "keep-alive")) ||
+ (r->proto_num >= 1001))
+ ) {
+ char header[256];
+ int left = r->server->keep_alive_max - r->connection->keepalives;
+
+ r->connection->keepalive = 1;
+ r->connection->keepalives++;
+
+ /* If they sent a Keep-Alive token, send one back */
+ if (ka_sent) {
+ if (r->server->keep_alive_max)
+ ap_snprintf(header, sizeof(header), "timeout=%d, max=%d",
+ r->server->keep_alive_timeout, left);
+ else
+ ap_snprintf(header, sizeof(header), "timeout=%d",
+ r->server->keep_alive_timeout);
+ table_set(r->headers_out, "Keep-Alive", header);
+ table_merge(r->headers_out, "Connection", "Keep-Alive");
+ }
+
+ return 1;
+ }
+
+ /* Otherwise, we need to indicate that we will be closing this
+ * connection immediately after the current response.
+ *
+ * We only really need to send "close" to HTTP/1.1 clients, but we
+ * always send it anyway, because a broken proxy may identify itself
+ * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
+ * to a HTTP/1.1 client. Better safe than sorry.
+ */
+ if (!wimpy)
+ table_merge(r->headers_out, "Connection", "close");
+
+ r->connection->keepalive = 0;
+
+ return 0;
+}
+
+int set_last_modified(request_rec *r, time_t mtime)
+{
+ char *etag, weak_etag[MAX_STRING_LEN];
+ char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
+ time_t now = time(NULL);
+
+ if (now < 0)
+ now = r->request_time;
+
+ table_set(r->headers_out, "Last-Modified",
+ gm_timestr_822(r->pool, (mtime > now) ? now : mtime));
+
+ /* Make an ETag header out of various pieces of information. We use
+ * the last-modified date and, if we have a real file, the
+ * length and inode number - note that this doesn't have to match
+ * the content-length (i.e. includes), it just has to be unique
+ * for the file.
+ *
+ * If the request was made within a second of the last-modified date,
+ * we send a weak tag instead of a strong one, since it could
+ * be modified again later in the second, and the validation
+ * would be incorrect.
+ */
+
+ if (r->finfo.st_mode != 0)
+ ap_snprintf(weak_etag, sizeof(weak_etag), "W/\"%lx-%lx-%lx\"",
+ (unsigned long)r->finfo.st_ino,
+ (unsigned long)r->finfo.st_size, (unsigned long)mtime);
+ else
+ ap_snprintf(weak_etag, sizeof(weak_etag), "W/\"%lx\"",
+ (unsigned long)mtime);
+
+ etag = weak_etag + ((r->request_time - mtime > 1) ? 2 : 0);
+ table_set(r->headers_out, "ETag", etag);
+
+ /* Check for conditional requests --- note that we only want to do
+ * this if we are successful so far and we are not processing a
+ * subrequest or an ErrorDocument.
+ *
+ * The order of the checks is important, since etag checks are supposed
+ * to be more accurate than checks relative to the modification time.
+ */
+
+ if (!is_HTTP_SUCCESS(r->status) || r->no_local_copy)
+ return OK;
+
+ /* If an If-Match request-header field was given and
+ * if our ETag does not match any of the entity tags in that field
+ * and the field value is not "*" (meaning match anything), then
+ * respond with a status of 412 (Precondition Failed).
+ */
+
+ if ((if_match = table_get(r->headers_in, "If-Match")) != NULL) {
+ if ((if_match[0] != '*') && !find_token(r->pool, if_match, etag))
+ return HTTP_PRECONDITION_FAILED;
+ }
+
+ /* Else if a valid If-Unmodified-Since request-header field was given
+ * and the requested resource has been modified since the time
+ * specified in this field, then the server MUST
+ * respond with a status of 412 (Precondition Failed).
+ */
+
+ else if ((if_unmodified = table_get(r->headers_in, "If-Unmodified-Since"))
+ != NULL) {
+ time_t ius = parseHTTPdate(if_unmodified);
+
+ if ((ius != BAD_DATE) && (mtime > ius))
+ return HTTP_PRECONDITION_FAILED;
+ }
+
+ /* If an If-None-Match request-header field was given and
+ * if our ETag matches any of the entity tags in that field or
+ * if the field value is "*" (meaning match anything), then
+ * if the request method was GET or HEAD, the server SHOULD
+ * respond with a 304 (Not Modified) response.
+ * For all other request methods, the server MUST
+ * respond with a status of 412 (Precondition Failed).
+ */
+
+ if ((if_nonematch = table_get(r->headers_in, "If-None-Match")) != NULL) {
+ if ((if_nonematch[0] == '*') || find_token(r->pool,if_nonematch,etag))
+ return (r->method_number == M_GET) ? HTTP_NOT_MODIFIED
+ : HTTP_PRECONDITION_FAILED;
+ }
+
+ /* Else if a valid If-Modified-Since request-header field was given
+ * and it is a GET or HEAD request
+ * and the requested resource has not been modified since the time
+ * specified in this field, then the server MUST
+ * respond with a status of 304 (Not Modified).
+ * A date later than the server's current request time is invalid.
+ */
+
+ else if ((r->method_number == M_GET) && ((if_modified_since =
+ table_get(r->headers_in, "If-Modified-Since")) != NULL)) {
+ time_t ims = parseHTTPdate(if_modified_since);
+
+ if ((ims >= mtime) && (ims <= r->request_time))
+ return HTTP_NOT_MODIFIED;
+ }
+
+ return OK;
+}
+
+/* Get a line of protocol input, including any continuation lines
+ * caused by MIME folding (or broken clients) if fold != 0, and place it
+ * in the buffer s, of size n bytes, without the ending newline.
+ *
+ * Returns -1 on error, or the length of s.
+ *
+ * Note: Because bgets uses 1 char for newline and 1 char for NUL,
+ * the most we can get is (n - 2) actual characters if it
+ * was ended by a newline, or (n - 1) characters if the line
+ * length exceeded (n - 1). So, if the result == (n - 1),
+ * then the actual input line exceeded the buffer length,
+ * and it would be a good idea for the caller to puke 400 or 414.
+ */
+static int getline(char *s, int n, BUFF *in, int fold)
+{
+ char *pos, next;
+ int retval;
+ int total = 0;
+
+ pos = s;
+
+ do {
+ retval = bgets(pos, n, in); /* retval == -1 if error, 0 if EOF */
+
+ if (retval <= 0)
+ return ((retval < 0) && (total == 0)) ? -1 : total;
+
+ /* retval is the number of characters read, not including NUL */
+
+ n -= retval; /* Keep track of how much of s is full */
+ pos += (retval-1); /* and where s ends */
+ total += retval; /* and how long s has become */
+
+ if (*pos == '\n') { /* Did we get a full line of input? */
+ *pos = '\0';
+ --total; ++n;
+ }
+ else return total; /* if not, input line exceeded buffer size */
+
+ /* Continue appending if line folding is desired and
+ * the last line was not empty and we have room in the buffer and
+ * the next line begins with a continuation character.
+ */
+ } while (fold && (retval != 1) && (n > 1) &&
+ (blookc(&next, in) == 1) &&
+ ((next == ' ') || (next == '\t')));
+
+ return total;
+}
+
+void parse_uri (request_rec *r, const char *uri)
+{
+ const char *s;
+
+#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 = pstrdup(r->pool, uri);
+ r->args = NULL;
+ }
+ else if (r->method && !strcmp(r->method, "TRACE")) {
+ r->proxyreq = 0;
+ r->uri = pstrdup(r->pool, 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] = '/';
+ };
+
+ /* Fix OS/2 HPFS filename case problem. */
+ r->uri = strlwr(r->uri);
+#endif
+
+ if (*uri) r->args= pstrdup(r->pool, uri);
+ else r->args = NULL;
+ }
+}
+
+const char *check_fulluri (request_rec *r, const char *uri)
+{
+ char *name, *host;
+ int i;
+ unsigned port;
+ server_addr_rec * sar;
+
+ /* This routine parses full URLs, if they match the server */
+ if (strncasecmp(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_nc(r->pool, &name, ':');
+ if (*name) port = atoi(name);
+ else port = 80;
+
+ /* Make sure ports patch */
+ if (port != r->server->port) {
+ for (sar = r->server->addrs; sar; sar = sar->next) {
+ if( (sar->host_port == 0) || (port == sar->host_port) )
+ break;
+ }
+ if (!sar) 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];
+ const char *ll = l, *uri;
+ conn_rec *conn = r->connection;
+ int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol*/
+ int len;
+
+ /* Read past empty lines until we get a real request line,
+ * a read error, the connection closes (EOF), or we timeout.
+ *
+ * We skip empty lines because browsers have to tack a CRLF on to the end
+ * of POSTs to support old CERN webservers. But note that we may not
+ * have flushed any previous response completely to the client yet.
+ * We delay the flush as long as possible so that we can improve
+ * performance for clients that are pipelining requests. If a request
+ * is pipelined then we won't block during the (implicit) read() below.
+ * If the requests aren't pipelined, then the client is still waiting
+ * for the final buffer flush from us, and we will block in the implicit
+ * read(). B_SAFEREAD ensures that the BUFF layer flushes if it will
+ * have to block during a read.
+ */
+ bsetflag( conn->client, B_SAFEREAD, 1 );
+ while ((len = getline(l, HUGE_STRING_LEN, conn->client, 0)) <= 0) {
+ if ((len < 0) || bgetflag(conn->client, B_EOF)) {
+ bsetflag( conn->client, B_SAFEREAD, 0 );
+ return 0;
+ }
+ }
+ /* we've probably got something to do, ignore graceful restart requests */
+ signal (SIGUSR1, SIG_IGN);
+ bsetflag( conn->client, B_SAFEREAD, 0 );
+ if (len == (HUGE_STRING_LEN - 1)) {
+ log_printf(r->server, "request failed for %s, reason: URI too long",
+ get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME));
+ r->status = HTTP_REQUEST_URI_TOO_LARGE;
+ return 0;
+ }
+
+ r->request_time = time(NULL);
+ r->the_request = pstrdup (r->pool, l);
+ r->method = getword_white(r->pool, &ll);
+ uri = getword_white(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)
+{
+ conn_rec *c = r->connection;
+ int len;
+ char *value;
+ char field[MAX_STRING_LEN];
+
+ /* Read header lines until we get the empty separator line,
+ * a read error, the connection closes (EOF), or we timeout.
+ * Should we also check for overflow (len == MAX_STRING_LEN-1)?
+ */
+ while ((len = getline(field, MAX_STRING_LEN, c->client, 1)) > 0) {
+
+ if (!(value = strchr(field,':'))) /* Find the colon separator */
+ continue; /* or should puke 400 here */
+
+ *value = '\0';
+ ++value;
+ while (isspace(*value)) ++value; /* Skip to start of value */
+
+ table_merge(r->headers_in, field, value);
+ }
+}
+
+#define ADDR_MATCHES(addr1,addr2) \
+ (addr1.s_addr == addr2.s_addr) || (addr1.s_addr == htonl(INADDR_ANY)) \
+ || (addr1.s_addr == DEFAULT_VHOST_ADDR)
+
+static void check_hostalias (request_rec *r)
+{
+ const char *hostname=r->hostname;
+ char *host = getword(r->pool, &hostname, ':'); /* Get rid of port */
+ unsigned port = (*hostname) ? atoi(hostname) : 80;
+ server_rec *s = r->server;
+ server_addr_rec * sar;
+ int l;
+
+/* make sure the client can't spoof the port;
+ * have to check all possiblities to see if the server
+ * should be listening. */
+ if (port != r->server->port) {
+ for (sar = s->addrs; sar; sar = sar->next) {
+ if ( (port == sar->host_port) || (sar->host_port == 0) )
+ break;
+ }
+ if (!sar) return;
+ }
+
+ l = strlen(host)-1;
+ if ((host[l]) == '.') {
+ host[l] = '\0';
+ }
+
+ r->hostname = host;
+
+ for (s = r->server->next; s; s = s->next) {
+ const char *names;
+ server_addr_rec *sar;
+
+ if (s->addrs == NULL) {
+ /* this server has been disabled because of DNS screwups during
+ configuration */
+ continue;
+ }
+/* ok, now there are several possibilities, and we're matching the
+ * hostname, the port, and r->connection->local_addr. The last is
+ * required so as to only respond on an address to which this vhost
+ * should actually be listening.
+ *
+ * Either we can match s->server_name and s->port while matching
+ * against the ip address in a record in the s->addrs list *or* we
+ * can match s->server_name and a complete record in the s->addrs
+ * list *or* we can match the virtual host name and the address and
+ * the port of a record in the s->addrs list.
+ */
+ if (!strcasecmp(host,s->server_hostname)) { /* ServerName matches hostname */
+ if (port == s->port) { /* possibly configured by Port */
+ for (sar = s->addrs; sar; sar = sar->next) {
+ if (ADDR_MATCHES(sar->host_addr,r->connection->local_addr.sin_addr))
+ break; /* SN matches, Port matches, and one IP addr matches */
+ }
+ } else { /* check to see if an addr matches */
+ for (sar = s->addrs; sar; sar = sar->next) {
+ if (((port == sar->host_port) || (sar->host_port == 0))
+ && (ADDR_MATCHES(sar->host_addr,
+ r->connection->local_addr.sin_addr)))
+ break; /* SN matches, and a addr matches IP & port */
+ }
+ }
+ if (sar) { /* we got a match */
+ r->server = r->connection->server = s;
+ if (r->hostlen && !strncasecmp(r->uri, "http://", 7)) {
+ r->uri += r->hostlen;
+ parse_uri(r, r->uri);
+ /* we still might want to do something below (ie. set r->proxyreq) */
+ }
+ }
+ } /* ServerName doesn't match */
+
+ /* now s->addrs list, include the names, from the VirtualHost directive */
+ for (sar = s->addrs; sar; sar = sar->next) {
+ if (((sar->host_port==0) || (port==sar->host_port))
+ && (ADDR_MATCHES(sar->host_addr,r->connection->local_addr.sin_addr))
+ && !strcasecmp(host,sar->virthost)) {
+ /* ok, an element in the addrs list matched all three items */
+ r->server = r->connection->server = s;
+ if (r->hostlen && !strncasecmp(r->uri, "http://", 7)) {
+ r->uri += r->hostlen;
+ r->proxyreq = 0;
+ }
+ }
+ }
+
+ /* search all the aliases from ServerAlias directive
+ * ServerAlias acts like a wildcard, so as to help deal with the
+ * transition when the DNS for a given host changes.
+ */
+ names = s->names;
+ if (names) {
+ 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 && !strncasecmp(r->uri, "http://", 7)) {
+ r->uri += r->hostlen;
+ r->proxyreq = 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+void check_serverpath (request_rec *r)
+{
+ server_rec *s;
+ server_addr_rec * sar = NULL;
+ int port = r->connection->local_addr.sin_port;
+
+ /* This is in conjunction with the ServerPath code in
+ * http_core, so we get the right host attached to a non-
+ * Host-sending request.
+ */
+
+ for (s = r->server->next; s; s = s->next) {
+ /* we should check to make sure that this server should be listening
+ * at all.
+ *
+ * this code is duplicated in check_hostalias, but only one of these
+ * two functions runs for a given request.
+ */
+ if (!s->addrs) continue;
+
+ if( (port == s->port) ) {
+ for(sar = s->addrs; sar; sar = sar->next) {
+ if(ADDR_MATCHES(sar->host_addr, r->connection->local_addr.sin_addr))
+ break;
+ }
+ }
+ else {
+ for(sar = s->addrs; sar; sar = sar->next) {
+ if( ( (port == sar->host_port) || (sar->host_port == 0) )
+ && ( ADDR_MATCHES(sar->host_addr,
+ r->connection->local_addr.sin_addr) ) )
+ break;
+ }
+ }
+
+ if (!sar) continue; /* no match */
+
+ if (s->path && !strncmp(r->uri, s->path, s->pathlen)
+ && (s->path[s->pathlen - 1] == '/'
+ || r->uri[s->pathlen] == '/'
+ || r->uri[s->pathlen] == '\0'))
+ r->server = r->connection->server = s;
+ }
+}
+
+request_rec *read_request (conn_rec *conn)
+{
+ request_rec *r = (request_rec *)pcalloc (conn->pool, sizeof(request_rec));
+
+ r->connection = conn;
+ conn->server = conn->base_server;
+ 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->read_length = 0;
+ r->read_body = REQUEST_NO_BODY;
+
+ r->status = HTTP_REQUEST_TIME_OUT; /* Until we get a request */
+
+ /* Get the request... */
+
+ keepalive_timeout("read request line", r);
+ if (!read_request_line (r)) {
+ kill_timeout(r);
+ return NULL;
+ }
+ if (!r->assbackwards) {
+ hard_timeout("read request headers", r);
+ get_mime_headers (r);
+ }
+ kill_timeout(r);
+
+ r->status = HTTP_OK; /* Until further notice. */
+
+ /* 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);
+
+ /* we may have switched to another server */
+ r->per_dir_config = r->server->lookup_defaults;
+
+ conn->keptalive = 0; /* We now have a request to play with */
+
+ if(!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 if(!strcmp(r->method,"OPTIONS"))
+ r->method_number = M_OPTIONS;
+ else if(!strcmp(r->method,"TRACE"))
+ r->method_number = M_TRACE;
+ 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, const request_rec *r)
+{
+ rnew->the_request = r->the_request; /* Keep original request-line */
+
+ rnew->assbackwards = 1; /* Don't send headers from this. */
+ rnew->no_local_copy = 1; /* Don't try to send USE_LOCAL_COPY for a
+ * fragment.
+ */
+ rnew->method = "GET"; rnew->method_number = M_GET;
+ rnew->protocol = "INCLUDED";
+
+ rnew->status = HTTP_OK;
+
+ rnew->headers_in = r->headers_in;
+ rnew->subprocess_env = 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->read_length = r->read_length;
+ rnew->read_body = REQUEST_NO_BODY;
+
+ rnew->main = (request_rec *)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[256];
+
+ ap_snprintf(nonce, sizeof(nonce), "%lu", r->request_time);
+ 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)
+{
+ const 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 (strcasecmp(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_nc (r->connection->pool, &t, ':');
+ r->connection->auth_type = "Basic";
+
+ *pw = t;
+
+ return OK;
+}
+
+/* New Apache routine to map status codes into array indicies
+ * e.g. 100 -> 0, 101 -> 1, 200 -> 2 ...
+ * The number of status lines must equal the value of RESPONSE_CODES (httpd.h)
+ * and must be listed in order.
+ */
+
+static char *status_lines[] = {
+ "100 Continue",
+ "101 Switching Protocols",
+#define LEVEL_200 2
+ "200 OK",
+ "201 Created",
+ "202 Accepted",
+ "203 Non-Authoritative Information",
+ "204 No Content",
+ "205 Reset Content",
+ "206 Partial Content",
+#define LEVEL_300 9
+ "300 Multiple Choices",
+ "301 Moved Permanently",
+ "302 Moved Temporarily",
+ "303 See Other",
+ "304 Not Modified",
+ "305 Use Proxy",
+#define LEVEL_400 15
+ "400 Bad Request",
+ "401 Authorization Required",
+ "402 Payment Required",
+ "403 Forbidden",
+ "404 File Not Found",
+ "405 Method Not Allowed",
+ "406 Not Acceptable",
+ "407 Proxy Authentication Required",
+ "408 Request Time-out",
+ "409 Conflict",
+ "410 Gone",
+ "411 Length Required",
+ "412 Precondition Failed",
+ "413 Request Entity Too Large",
+ "414 Request-URI Too Large",
+ "415 Unsupported Media Type",
+#define LEVEL_500 31
+ "500 Internal Server Error",
+ "501 Method Not Implemented",
+ "502 Bad Gateway",
+ "503 Service Temporarily Unavailable",
+ "504 Gateway Time-out",
+ "505 HTTP Version Not Supported",
+ "506 Variant Also Varies"
+};
+
+/* The index is found by its offset from the x00 code of each level.
+ * Although this is fast, it will need to be replaced if some nutcase
+ * decides to define a high-numbered code before the lower numbers.
+ * If that sad event occurs, replace the code below with a linear search
+ * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
+ */
+
+int index_of_response(int status)
+{
+ static int shortcut[6] = { 0, LEVEL_200, LEVEL_300, LEVEL_400,
+ LEVEL_500, RESPONSE_CODES };
+ int i, pos;
+
+ if (status < 100) /* Below 100 is illegal for HTTP status */
+ return LEVEL_500;
+
+ for (i = 0; i < 5; i++) {
+ status -= 100;
+ if (status < 100) {
+ pos = (status + shortcut[i]);
+ if (pos < shortcut[i+1])
+ return pos;
+ else
+ return LEVEL_500; /* status unknown (falls in gap) */
+ }
+ }
+ return LEVEL_500; /* 600 or above is also illegal */
+}
+
+/* Send a single HTTP header field to the client. Note that this function
+ * is used in calls to table_do(), so their interfaces are co-dependent.
+ * In other words, don't change this one without checking table_do in alloc.c.
+ * It returns true unless there was a write error of some kind.
+ */
+int send_header_field (request_rec *r, const char *fieldname,
+ const char *fieldval)
+{
+ return (0 < bvputs(r->connection->client,
+ fieldname, ": ", fieldval, "\015\012", NULL));
+}
+
+void basic_http_header (request_rec *r)
+{
+ char *protocol;
+
+ if (r->assbackwards) return;
+
+ if (!r->status_line)
+ r->status_line = status_lines[index_of_response(r->status)];
+
+ /* mod_proxy is only HTTP/1.0, so avoid sending HTTP/1.1 error response;
+ * kluge around broken browsers when indicated by force-response-1.0
+ */
+ if (r->proxyreq
+ || (r->proto_num == 1000
+ && table_get(r->subprocess_env,"force-response-1.0"))) {
+
+ protocol = "HTTP/1.0";
+ r->connection->keepalive = -1;
+ }
+ else
+ protocol = SERVER_PROTOCOL;
+
+ /* Output the HTTP/1.x Status-Line and the Date and Server fields */
+
+ bvputs(r->connection->client,
+ protocol, " ", r->status_line, "\015\012", NULL);
+
+ send_header_field(r, "Date", gm_timestr_822(r->pool, r->request_time));
+ send_header_field(r, "Server", SERVER_VERSION);
+
+ table_unset(r->headers_out, "Date"); /* Avoid bogosity */
+ table_unset(r->headers_out, "Server");
+}
+
+/* Navigator versions 2.x, 3.x and 4.0 betas up to and including 4.0b2
+ * have a header parsing bug. If the terminating \r\n occur starting
+ * at the 256th or 257th byte of output then it will not properly parse
+ * the headers. Curiously it doesn't exhibit this problem at 512, 513.
+ * We are guessing that this is because their initial read of a new request
+ * uses a 256 byte buffer, and subsequent reads use a larger buffer.
+ * So the problem might exist at different offsets as well.
+ *
+ * This should also work on keepalive connections assuming they use the
+ * same small buffer for the first read of each new request.
+ *
+ * At any rate, we check the bytes written so far and, if we are about to
+ * tickle the bug, we instead insert a bogus padding header. Since the bug
+ * manifests as a broken image in Navigator, users blame the server. :(
+ * It is more expensive to check the User-Agent than it is to just add the
+ * bytes, so we haven't used the BrowserMatch feature here.
+ */
+static void terminate_header (BUFF *client)
+{
+ long int bs;
+
+ bgetopt(client, BO_BYTECT, &bs);
+ if (bs >= 255 && bs <= 257)
+ bputs("X-Pad: avoid browser bug\015\012", client);
+
+ bputs("\015\012", client); /* Send the terminating empty line */
+}
+
+/* Build the Allow field-value from the request handler method mask.
+ * Note that we always allow TRACE, since it is handled below.
+ */
+static char *make_allow(request_rec *r)
+{
+ return 2 + pstrcat(r->pool,
+ (r->allowed & (1 << M_GET)) ? ", GET, HEAD" : "",
+ (r->allowed & (1 << M_POST)) ? ", POST" : "",
+ (r->allowed & (1 << M_PUT)) ? ", PUT" : "",
+ (r->allowed & (1 << M_DELETE)) ? ", DELETE" : "",
+ (r->allowed & (1 << M_OPTIONS)) ? ", OPTIONS" : "",
+ ", TRACE",
+ NULL);
+}
+
+int send_http_trace (request_rec *r)
+{
+ int rv;
+
+ /* Get the original request */
+ while (r->prev) r = r->prev;
+
+ if ((rv = setup_client_block(r, REQUEST_NO_BODY)))
+ return rv;
+
+ hard_timeout("send TRACE", r);
+
+ r->content_type = "message/http";
+ send_http_header(r);
+
+ /* Now we recreate the request, and echo it back */
+
+ rvputs( r, r->the_request, "\015\012", NULL );
+
+ table_do((int (*)(void *, const char *, const char *))send_header_field,
+ (void *)r, r->headers_in, NULL);
+ bputs("\015\012", r->connection->client);
+
+ kill_timeout(r);
+ return OK;
+}
+
+int send_http_options(request_rec *r)
+{
+ const long int zero = 0L;
+
+ if (r->assbackwards) return DECLINED;
+
+ hard_timeout("send OPTIONS", r);
+
+ basic_http_header(r);
+
+ table_set(r->headers_out, "Content-Length", "0");
+ table_set(r->headers_out, "Allow", make_allow(r));
+ set_keepalive(r);
+
+ table_do((int (*)(void *, const char *, const char *))send_header_field,
+ (void *)r, r->headers_out, NULL);
+
+ terminate_header(r->connection->client);
+
+ kill_timeout(r);
+ bsetopt(r->connection->client, BO_BYTECT, &zero);
+
+ return OK;
+}
+
+/*
+ * Here we try to be compatible with clients that want multipart/x-byteranges
+ * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
+ * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
+ * that the browser supports an older protocol. We also check User-Agent
+ * for Microsoft Internet Explorer 3, which needs this as well.
+ */
+
+static int use_range_x(request_rec *r) {
+ char *ua;
+ return (table_get(r->headers_in, "Request-Range") ||
+ ((ua = table_get(r->headers_in, "User-Agent"))
+ && strstr(ua, "MSIE 3")));
+}
+
+void send_http_header(request_rec *r)
+{
+ int i;
+ const long int zero = 0L;
+
+ if (r->assbackwards) {
+ if(!r->main)
+ bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1;
+ return;
+ }
+
+ /* Now that we are ready to send a response, we need to combine the two
+ * header field tables into a single table. If we don't do this, our
+ * later attempts to set or unset a given fieldname might be bypassed.
+ */
+ if (!is_empty_table(r->err_headers_out))
+ r->headers_out = overlay_tables(r->pool, r->err_headers_out,
+ r->headers_out);
+
+ hard_timeout("send headers", r);
+
+ basic_http_header(r);
+
+ set_keepalive(r);
+
+ if (r->chunked) {
+ table_merge(r->headers_out, "Transfer-Encoding", "chunked");
+ table_unset(r->headers_out, "Content-Length");
+ }
+
+ if (r->byterange > 1)
+ table_set(r->headers_out, "Content-Type",
+ pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
+ "byteranges; boundary=", r->boundary, NULL));
+ else if (r->content_type)
+ table_set(r->headers_out, "Content-Type", r->content_type);
+ else
+ table_set(r->headers_out, "Content-Type", default_type(r));
+
+ if (r->content_encoding)
+ table_set(r->headers_out, "Content-Encoding", r->content_encoding);
+
+ if (r->content_languages && r->content_languages->nelts) {
+ for (i = 0; i < r->content_languages->nelts; ++i) {
+ table_merge(r->headers_out, "Content-Language",
+ ((char**)(r->content_languages->elts))[i]);
+ }
+ }
+ else if (r->content_language)
+ table_set(r->headers_out, "Content-Language", r->content_language);
+
+ /* Control cachability for non-cachable responses if not already set
+ * by some other part of the server configuration.
+ */
+ if (r->no_cache && !table_get(r->headers_out, "Expires"))
+ table_add(r->headers_out, "Expires",
+ gm_timestr_822(r->pool, r->request_time));
+
+ /* Send the entire table of header fields, terminated by an empty line. */
+
+ table_do((int (*)(void *, const char *, const char *))send_header_field,
+ (void *)r, r->headers_out, NULL);
+
+ terminate_header(r->connection->client);
+
+ kill_timeout(r);
+
+ bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1; /* Whatever follows is real body stuff... */
+
+ /* Set buffer flags for the body */
+ if (r->chunked) bsetflag(r->connection->client, B_CHUNK, 1);
+}
+
+void finalize_request_protocol (request_rec *r)
+{
+ /* Turn off chunked encoding */
+
+ if (r->chunked && !r->connection->aborted) {
+ soft_timeout("send ending chunk", r);
+ bsetflag(r->connection->client, B_CHUNK, 0);
+ bputs("0\015\012", r->connection->client);
+ /* If we had footer "headers", we'd send them now */
+ bputs("\015\012", r->connection->client);
+ kill_timeout(r);
+ }
+}
+
+/* Here we deal with getting the request message body from the client.
+ * Whether or not the request contains a body is signaled by the presence
+ * of a non-zero Content-Length or by a Transfer-Encoding: chunked.
+ *
+ * Note that this is more complicated than it was in Apache 1.1 and prior
+ * versions, because chunked support means that the module does less.
+ *
+ * The proper procedure is this:
+ *
+ * 1. Call setup_client_block() near the beginning of the request
+ * handler. This will set up all the necessary properties, and will
+ * return either OK, or an error code. If the latter, the module should
+ * return that error code. The second parameter selects the policy to
+ * apply if the request message indicates a body, and how a chunked
+ * transfer-coding should be interpreted. Choose one of
+ *
+ * REQUEST_NO_BODY Send 413 error if message has any body
+ * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
+ * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
+ * REQUEST_CHUNKED_PASS Pass the chunks to me without removal.
+ *
+ * In order to use the last two options, the caller MUST provide a buffer
+ * large enough to hold a chunk-size line, including any extensions.
+ *
+ * 2. When you are ready to read a body (if any), call should_client_block().
+ * This will tell the module whether or not to read input. If it is 0,
+ * the module should assume that there is no message body to read.
+ * This step also sends a 100 Continue response to HTTP/1.1 clients,
+ * so should not be called until the module is *definitely* ready to
+ * read content. (otherwise, the point of the 100 response is defeated).
+ * Never call this function more than once.
+ *
+ * 3. Finally, call get_client_block in a loop. Pass it a buffer and its size.
+ * It will put data into the buffer (not necessarily a full buffer), and
+ * return the length of the input block. When it is done reading, it will
+ * return 0 if EOF, or -1 if there was an error.
+ * If an error occurs on input, we force an end to keepalive.
+ */
+
+int setup_client_block (request_rec *r, int read_policy)
+{
+ char *tenc = table_get(r->headers_in, "Transfer-Encoding");
+ char *lenp = table_get(r->headers_in, "Content-Length");
+
+ r->read_body = read_policy;
+ r->read_chunked = 0;
+ r->remaining = 0;
+
+ if (tenc) {
+ if (strcasecmp(tenc, "chunked")) {
+ log_printf(r->server, "Unknown Transfer-Encoding %s", tenc);
+ return HTTP_BAD_REQUEST;
+ }
+ if (r->read_body == REQUEST_CHUNKED_ERROR) {
+ log_reason("chunked Transfer-Encoding forbidden", r->uri, r);
+ return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED;
+ }
+
+ r->read_chunked = 1;
+ }
+ else if (lenp) {
+ char *pos = lenp;
+
+ while (isdigit(*pos) || isspace(*pos)) ++pos;
+ if (*pos != '\0') {
+ log_printf(r->server, "Invalid Content-Length %s", lenp);
+ return HTTP_BAD_REQUEST;
+ }
+
+ r->remaining = atol(lenp);
+ }
+
+ if ((r->read_body == REQUEST_NO_BODY) &&
+ (r->read_chunked || (r->remaining > 0))) {
+ log_printf(r->server, "%s with body is not allowed for %s",
+ r->method, r->uri);
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ return OK;
+}
+
+int should_client_block (request_rec *r)
+{
+ /* First check if we have already read the request body */
+
+ if (r->read_length || (!r->read_chunked && (r->remaining <= 0)))
+ return 0;
+
+ if (!r->read_chunked && (r->remaining <= 0))
+ return 0;
+
+ if (r->proto_num >= 1001) { /* sending 100 Continue interim response */
+ bvputs(r->connection->client,
+ SERVER_PROTOCOL, " ", status_lines[0], "\015\012\015\012", NULL);
+ bflush(r->connection->client);
+ }
+
+ return 1;
+}
+
+static long get_chunk_size (char *b)
+{
+ long chunksize = 0;
+
+ while (isxdigit(*b)) {
+ int xvalue = 0;
+
+ if (*b >= '0' && *b <= '9') xvalue = *b - '0';
+ else if (*b >= 'A' && *b <= 'F') xvalue = *b - 'A' + 0xa;
+ else if (*b >= 'a' && *b <= 'f') xvalue = *b - 'a' + 0xa;
+
+ chunksize = (chunksize << 4) | xvalue;
+ ++b;
+ }
+
+ return chunksize;
+}
+
+/* get_client_block is called in a loop to get the request message body.
+ * This is quite simple if the client includes a content-length
+ * (the normal case), but gets messy if the body is chunked. Note that
+ * r->remaining is used to maintain state across calls and that
+ * r->read_length is the total number of bytes given to the caller
+ * across all invocations. It is messy because we have to be careful not
+ * to read past the data provided by the client, since these reads block.
+ * Returns 0 on End-of-body, -1 on error or premature chunk end.
+ *
+ * Reading the chunked encoding requires a buffer size large enough to
+ * hold a chunk-size line, including any extensions. For now, we'll leave
+ * that to the caller, at least until we can come up with a better solution.
+ */
+long get_client_block (request_rec *r, char *buffer, int bufsiz)
+{
+ int c;
+ long len_read, len_to_read;
+ long chunk_start = 0;
+
+ if (!r->read_chunked) { /* Content-length read */
+ len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
+ len_read = bread(r->connection->client, buffer, len_to_read);
+ if (len_read <= 0) {
+ if (len_read < 0) r->connection->keepalive = -1;
+ return len_read;
+ }
+ r->read_length += len_read;
+ r->remaining -= len_read;
+ return len_read;
+ }
+
+ /* Handle chunked reading
+ * Note: we are careful to shorten the input bufsiz so that there
+ * will always be enough space for us to add a CRLF (if necessary).
+ */
+ if (r->read_body == REQUEST_CHUNKED_PASS)
+ bufsiz -= 2;
+ if (bufsiz <= 0)
+ return -1; /* Cannot read chunked with a small buffer */
+
+ if (r->remaining == 0) { /* Start of new chunk */
+
+ chunk_start = getline(buffer, bufsiz, r->connection->client, 0);
+ if ((chunk_start <= 0) || (chunk_start >= (bufsiz - 1))
+ || !isxdigit(*buffer)) {
+ r->connection->keepalive = -1;
+ return -1;
+ }
+
+ len_to_read = get_chunk_size(buffer);
+
+ if (len_to_read == 0) { /* Last chunk indicated, get footers */
+ if (r->read_body == REQUEST_CHUNKED_DECHUNK) {
+ get_mime_headers(r);
+ ap_snprintf(buffer, bufsiz, "%ld", r->read_length);
+ table_unset(r->headers_in, "Transfer-Encoding");
+ table_set(r->headers_in, "Content-Length", buffer);
+ return 0;
+ }
+ r->remaining = -1; /* Indicate footers in-progress */
+ }
+ else {
+ r->remaining = len_to_read;
+ }
+ if (r->read_body == REQUEST_CHUNKED_PASS) {
+ buffer[chunk_start++] = CR; /* Restore chunk-size line end */
+ buffer[chunk_start++] = LF;
+ buffer += chunk_start; /* and pass line on to caller */
+ bufsiz -= chunk_start;
+ } else {
+ /* REQUEST_CHUNKED_DECHUNK -- do not include the length of
+ * the header in the return value */
+ chunk_start = 0;
+ }
+ }
+ /* When REQUEST_CHUNKED_PASS, we are */
+ if (r->remaining == -1) { /* reading footers until empty line */
+ len_read = chunk_start;
+
+ while ((bufsiz > 1) && ((len_read =
+ getline(buffer, bufsiz, r->connection->client, 1)) > 0)) {
+
+ if (len_read != (bufsiz - 1)) {
+ buffer[len_read++] = CR; /* Restore footer line end */
+ buffer[len_read++] = LF;
+ }
+ chunk_start += len_read;
+ buffer += len_read;
+ bufsiz -= len_read;
+ }
+ if (len_read < 0) {
+ r->connection->keepalive = -1;
+ return -1;
+ }
+
+ if (len_read == 0) { /* Indicates an empty line */
+ buffer[0] = CR;
+ buffer[1] = LF;
+ chunk_start += 2;
+ r->remaining = -2;
+ }
+ r->read_length += chunk_start;
+ return chunk_start;
+ }
+ /* When REQUEST_CHUNKED_PASS, we */
+ if (r->remaining == -2) { /* finished footers when last called */
+ r->remaining = 0; /* so now we must signal EOF */
+ return 0;
+ }
+
+ /* Otherwise, we are in the midst of reading a chunk of data */
+
+ len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
+
+ len_read = bread(r->connection->client, buffer, len_to_read);
+ if (len_read <= 0) {
+ r->connection->keepalive = -1;
+ return -1;
+ }
+
+ r->remaining -= len_read;
+
+ if (r->remaining == 0) { /* End of chunk, get trailing CRLF */
+ if ((c = bgetc(r->connection->client)) == CR) {
+ c = bgetc(r->connection->client);
+ }
+ if (c != LF) {
+ r->connection->keepalive = -1;
+ return -1;
+ }
+ if (r->read_body == REQUEST_CHUNKED_PASS) {
+ buffer[len_read++] = CR;
+ buffer[len_read++] = LF;
+ }
+ }
+ r->read_length += (chunk_start + len_read);
+
+ return (chunk_start + len_read);
+}
+
+/* In HTTP/1.1, any method can have a body. However, most GET handlers
+ * wouldn't know what to do with a request body if they received one.
+ * This helper routine tests for and reads any message body in the request,
+ * simply discarding whatever it receives. We need to do this because
+ * failing to read the request body would cause it to be interpreted
+ * as the next request on a persistent connection.
+ *
+ * Since we return an error status if the request is malformed, this
+ * routine should be called at the beginning of a no-body handler, e.g.,
+ *
+ * if ((retval = discard_request_body(r)) != OK)
+ * return retval;
+ */
+int discard_request_body(request_rec *r)
+{
+ int rv;
+
+ if ((rv = setup_client_block(r, REQUEST_CHUNKED_PASS)))
+ return rv;
+
+ if (should_client_block(r)) {
+ char dumpbuf[HUGE_STRING_LEN];
+
+ hard_timeout("reading request body", r);
+ while ((rv = get_client_block(r, dumpbuf, HUGE_STRING_LEN)) > 0)
+ continue;
+ kill_timeout(r);
+
+ if (rv < 0)
+ return HTTP_BAD_REQUEST;
+ }
+ return OK;
+}
+
+/*
+ * Send the body of a response to the client.
+ */
+long send_fd(FILE *f, request_rec *r) { return send_fd_length(f, r, -1); }
+
+long send_fd_length(FILE *f, request_rec *r, long length)
+{
+ char buf[IOBUFSIZE];
+ long total_bytes_sent = 0;
+ register int n, w, o, len;
+
+ if (length == 0) return 0;
+
+ soft_timeout("send body", r);
+
+ while (!r->connection->aborted) {
+ if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length)
+ len = length - total_bytes_sent;
+ else len = IOBUFSIZE;
+
+ while ((n= fread(buf, sizeof(char), len, f)) < 1
+ && ferror(f) && errno == EINTR && !r->connection->aborted)
+ continue;
+
+ if (n < 1) {
+ break;
+ }
+ o=0;
+
+ while (n && !r->connection->aborted) {
+ w = bwrite(r->connection->client, &buf[o], n);
+ if (w > 0) {
+ reset_timeout(r); /* reset timeout after successful write */
+ total_bytes_sent += w;
+ n-=w;
+ o+=w;
+ }
+ else if (w < 0) {
+ if (r->connection->aborted)
+ break;
+ else if (errno == EAGAIN)
+ continue;
+ else {
+ log_unixerr("send body lost connection to",
+ get_remote_host(r->connection,
+ r->per_dir_config, REMOTE_NAME),
+ NULL, r->server);
+ bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ kill_timeout(r);
+ 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 rwrite(const void *buf, int nbyte, request_rec *r)
+{
+ int n;
+ if (r->connection->aborted) return EOF;
+ n=bwrite(r->connection->client, buf, nbyte);
+ SET_BYTES_SENT(r);
+ return n;
+}
+
+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;
+}
+
+int rflush (request_rec *r) {
+ return bflush(r->connection->client);
+}
+
+/* We should have named this send_canned_response, since it is used for any
+ * response that can be generated by the server from the request record.
+ * This includes all 204 (no content), 3xx (redirect), 4xx (client error),
+ * and 5xx (server error) messages that have not been redirected to another
+ * handler via the ErrorDocument feature.
+ */
+void send_error_response (request_rec *r, int recursive_error)
+{
+ BUFF *fd = r->connection->client;
+ int status = r->status;
+ int idx = index_of_response (status);
+ char *custom_response;
+ char *location = pstrdup(r->pool, table_get(r->headers_out, "Location"));
+
+ /* We need to special-case the handling of 204 and 304 responses,
+ * since they have specific HTTP requirements and do not include a
+ * message body. Note that being assbackwards here is not an option.
+ */
+ if (status == HTTP_NOT_MODIFIED) {
+ if (!is_empty_table(r->err_headers_out))
+ r->headers_out = overlay_tables(r->pool, r->err_headers_out,
+ r->headers_out);
+ hard_timeout("send 304", r);
+
+ basic_http_header(r);
+ set_keepalive(r);
+
+ table_do((int (*)(void *, const char *, const char *))send_header_field,
+ (void *)r, r->headers_out,
+ "Connection",
+ "Keep-Alive",
+ "ETag",
+ "Content-Location",
+ "Expires",
+ "Cache-Control",
+ "Vary",
+ "Warning",
+ "WWW-Authenticate",
+ NULL);
+
+ terminate_header(r->connection->client);
+
+ kill_timeout(r);
+ return;
+ }
+
+ if (status == HTTP_NO_CONTENT) {
+ send_http_header(r);
+ finalize_request_protocol(r);
+ return;
+ }
+
+ if (!r->assbackwards) {
+ table *tmp = r->headers_out;
+
+ /* For all HTTP/1.x responses for which we generate the message,
+ * we need to avoid inheriting the "normal status" header fields
+ * that may have been set by the request handler before the
+ * error or redirect, except for Location on external redirects.
+ */
+ r->headers_out = r->err_headers_out;
+ r->err_headers_out = tmp;
+ clear_table(r->err_headers_out);
+
+ if (location && *location
+ && (is_HTTP_REDIRECT(status) || status == HTTP_CREATED))
+ table_set(r->headers_out, "Location", location);
+
+ r->content_language = NULL;
+ r->content_languages = NULL;
+ r->content_encoding = NULL;
+ r->clength = 0;
+ r->content_type = "text/html";
+
+ if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED))
+ table_set(r->headers_out, "Allow", make_allow(r));
+
+ send_http_header(r);
+
+ if (r->header_only) {
+ finalize_request_protocol(r);
+ return;
+ }
+ }
+
+ hard_timeout("send error body", r);
+
+ if ((custom_response = response_code_string (r, idx))) {
+ /*
+ * We have a custom response output. This should only be
+ * a text-string to write back. But if the ErrorDocument
+ * was a local redirect and the requested resource failed
+ * for any reason, the custom_response will still hold the
+ * redirect URL. We don't really want to output this URL
+ * as a text message, so first check the custom response
+ * string to ensure that it is a text-string (using the
+ * same test used in die(), i.e. does it start with a
+ * "). If it doesn't, we've got a recursive error, so find
+ * the original error and output that as well.
+ */
+ if (custom_response[0] == '\"') {
+ bputs(custom_response+1, fd);
+ kill_timeout(r);
+ finalize_request_protocol(r);
+ return;
+ }
+ /* Redirect failed, so get back the original error
+ */
+ while (r->prev && (r->prev->status != HTTP_OK))
+ r = r->prev;
+ }
+ {
+ char *title = status_lines[idx];
+ /* folks decided they didn't want the error code in the H1 text */
+
+ char *h1 = 4 + status_lines[idx];
+
+ bvputs
+ (
+ fd,
+ "<HTML><HEAD>\n<TITLE>",
+ title,
+ "</TITLE>\n</HEAD><BODY>\n<H1>",
+ h1,
+ "</H1>\n",
+ NULL
+ );
+
+ switch (status) {
+ case REDIRECT:
+ case MOVED:
+ bvputs(fd, "The document has moved <A HREF=\"",
+ escape_html(r->pool, location), "\">here</A>.<P>\n", NULL);
+ break;
+ case HTTP_SEE_OTHER:
+ bvputs(fd, "The answer to your request is located <A HREF=\"",
+ escape_html(r->pool, location), "\">here</A>.<P>\n", NULL);
+ break;
+ case HTTP_USE_PROXY:
+ bvputs(fd, "This resource is only accessible through the proxy\n",
+ escape_html(r->pool, location), "<BR>\nYou will need to ",
+ "configure your client to use that proxy.<P>\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.<P>\n", fd);
+ break;
+ case BAD_REQUEST:
+ bputs("Your browser sent a request that\n", fd);
+ bputs("this server could not understand.<P>\n", fd);
+ break;
+ case FORBIDDEN:
+ bvputs(fd, "You don't have permission to access ",
+ escape_html(r->pool, r->uri), "\non this server.<P>\n",
+ NULL);
+ break;
+ case NOT_FOUND:
+ bvputs(fd, "The requested URL ", escape_html(r->pool, r->uri),
+ " was not found on this server.<P>\n", NULL);
+ break;
+ case METHOD_NOT_ALLOWED:
+ bvputs(fd, "The requested method ", r->method, " is not allowed "
+ "for the URL ", escape_html(r->pool, r->uri),
+ ".<P>\n", NULL);
+ break;
+ case NOT_ACCEPTABLE:
+ bvputs(fd,
+ "An appropriate representation of the requested resource ",
+ escape_html(r->pool, r->uri),
+ " could not be found on this server.<P>\n", NULL);
+ /* fall through */
+ case MULTIPLE_CHOICES:
+ {
+ char *list;
+ if ((list = table_get (r->notes, "variant-list")))
+ bputs(list, fd);
+ }
+ break;
+ case LENGTH_REQUIRED:
+ bvputs(fd, "A request of the requested method ", r->method,
+ " requires a valid Content-length.<P>\n", NULL);
+ break;
+ case PRECONDITION_FAILED:
+ bvputs(fd, "The precondition on the request for the URL ",
+ escape_html(r->pool, r->uri), " evaluated to false.<P>\n",
+ NULL);
+ break;
+ case NOT_IMPLEMENTED:
+ bvputs(fd, escape_html(r->pool, r->method), " to ",
+ escape_html(r->pool, r->uri), " not supported.<P>\n", NULL);
+ break;
+ case BAD_GATEWAY:
+ bputs("The proxy server received an invalid\015\012", fd);
+ bputs("response from an upstream server.<P>\015\012", fd);
+ break;
+ case VARIANT_ALSO_VARIES:
+ bvputs(fd, "A variant for the requested entity ",
+ escape_html(r->pool, r->uri), " is itself a ",
+ "transparently negotiable resource.<P>\n", NULL);
+ break;
+ case HTTP_REQUEST_TIME_OUT:
+ bputs("I'm tired of waiting for your request.\n", fd);
+ break;
+ case HTTP_GONE:
+ bvputs(fd, "The requested resource<BR>",
+ escape_html(r->pool, r->uri),
+ "<BR>\nis no longer available on this server ",
+ "and there is no forwarding address.\n",
+ "Please remove all references to this resource.\n", NULL);
+ break;
+ case HTTP_REQUEST_ENTITY_TOO_LARGE:
+ bvputs(fd, "The requested resource<BR>",
+ escape_html(r->pool, r->uri), "<BR>\n",
+ "does not allow request data with ", r->method,
+ " requests, or the amount of data provided in\n",
+ "the request exceeds the capacity limit.\n", NULL);
+ break;
+ case HTTP_REQUEST_URI_TOO_LARGE:
+ bputs("The requested URL's length exceeds the capacity\n", fd);
+ bputs("limit for this server.\n", fd);
+ break;
+ case HTTP_UNSUPPORTED_MEDIA_TYPE:
+ bputs("The supplied request data is not in a format\n", fd);
+ bputs("acceptable for processing by this resource.\n", fd);
+ break;
+ case HTTP_SERVICE_UNAVAILABLE:
+ bputs("The server is temporarily unable to service your\n", fd);
+ bputs("request due to maintenance downtime or capacity\n", fd);
+ bputs("problems. Please try again later.\n", fd);
+ break;
+ case HTTP_GATEWAY_TIME_OUT:
+ bputs("The proxy server did not receive a timely response\n", fd);
+ bputs("from the upstream server.<P>\n", fd);
+ break;
+ default: /* HTTP_INTERNAL_SERVER_ERROR */
+ bputs("The server encountered an internal error or\n", fd);
+ bputs("misconfiguration and was unable to complete\n", fd);
+ bputs("your request.<P>\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.<P>\n", fd);
+ break;
+ }
+
+ if (recursive_error) {
+ bvputs(fd, "<P>Additionally, a ",
+ status_lines[index_of_response(recursive_error)],
+ "\nerror was encountered while trying to use an "
+ "ErrorDocument to handle the request.\n", NULL);
+ }
+ bputs("</BODY></HTML>\n", fd);
+ }
+ kill_timeout(r);
+ finalize_request_protocol(r);
+}
+
+/* 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/usr.sbin/httpd/src/http_protocol.h b/usr.sbin/httpd/src/http_protocol.h
new file mode 100644
index 00000000000..db608c253e9
--- /dev/null
+++ b/usr.sbin/httpd/src/http_protocol.h
@@ -0,0 +1,191 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 a single HTTP header field */
+
+int send_header_field (request_rec *r, const char *fieldname,
+ const char *fieldval);
+
+/* Send the Status-Line and header fields for HTTP response */
+
+void send_http_header (request_rec *l);
+
+/* Send the response to special method requests */
+
+int send_http_trace (request_rec *r);
+int send_http_options (request_rec *r);
+
+/* Finish up stuff after a request */
+
+void finalize_request_protocol (request_rec *r);
+
+/* Send error back to client... last arg indicates error status in case
+ * we get an error in the process of trying to deal with an ErrorDocument
+ * to handle some other error. In that case, we print the default report
+ * for the first thing that went wrong, and more briefly report on the
+ * problem with the ErrorDocument.
+ */
+
+void 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);
+
+/* 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);
+long send_fd_length(FILE *f, request_rec *r, long length);
+
+/* Hmmm... could macrofy these for now, and maybe forever, though the
+ * definitions of the macros would get a whole lot hairier.
+ */
+
+int rputc (int c, request_rec *r);
+int rputs(const char *str, request_rec *r);
+int rwrite(const void *buf, int nbyte, request_rec *r);
+int rvputs(request_rec *r, ...);
+int rprintf(request_rec *r,const char *fmt,...);
+int rflush(request_rec *r);
+
+/*
+ * Index used in custom_responses array for a specific error code
+ * (only use outside protocol.c is in getting them configured).
+ */
+
+int index_of_response (int status);
+
+/* Reading a block of data from the client connection (e.g., POST arg) */
+
+int setup_client_block (request_rec *r, int read_policy);
+int should_client_block (request_rec *r);
+long get_client_block (request_rec *r, char *buffer, int bufsiz);
+int discard_request_body (request_rec *r);
+
+/* Sending a byterange */
+
+int set_byterange (request_rec *r);
+int each_byterange (request_rec *r, long *offset, long *length);
+
+/* 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, const 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, const char *uri);
diff --git a/usr.sbin/httpd/src/http_request.c b/usr.sbin/httpd/src/http_request.c
new file mode 100644
index 00000000000..0ffa6ee6386
--- /dev/null
+++ b/usr.sbin/httpd/src/http_request.c
@@ -0,0 +1,1152 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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: functions to get and process requests
+ *
+ * Rob McCool 3/21/93
+ *
+ * Thoroughly revamped by rst for Apache. NB this file reads
+ * best from the bottom up.
+ *
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_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.
+ */
+
+
+/*
+ * We don't want people able to serve up pipes, or unix sockets, or other
+ * scary things. Note that symlink tests are performed later.
+ */
+static int check_safe_file(request_rec *r)
+{
+ if (r->finfo.st_mode == 0 /* doesn't exist */
+ || S_ISDIR (r->finfo.st_mode)
+ || S_ISREG (r->finfo.st_mode)
+ || S_ISLNK (r->finfo.st_mode)) {
+ return OK;
+ }
+ log_reason("object is not a file, directory or symlink", r->filename, r);
+ return HTTP_FORBIDDEN;
+}
+
+
+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 HTTP_FORBIDDEN;
+
+ if (stat (d, &fi) < 0) return HTTP_FORBIDDEN;
+
+ return (fi.st_uid == lfi.st_uid) ? OK : HTTP_FORBIDDEN;
+
+#endif
+}
+
+/* Dealing with the file system to get PATH_INFO
+ */
+
+int 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';
+
+ errno = 0;
+ rv = stat(path, &r->finfo);
+
+ if (cp != end) *cp = '/';
+
+ if (!rv) {
+
+ /* Aha! Found something. If it was a directory, we will
+ * search contents of that directory for a multi_match, so
+ * the PATH_INFO argument starts with the component after that.
+ */
+
+ if (S_ISDIR(r->finfo.st_mode) && last_cp) {
+ r->finfo.st_mode = 0; /* No such file... */
+ cp = last_cp;
+ }
+
+ r->path_info = pstrdup (r->pool, cp);
+ *cp = '\0';
+ return OK;
+ }
+#if defined(ENOENT) && defined(ENOTDIR)
+ else if (errno == ENOENT || errno == ENOTDIR) {
+#else
+#error ENOENT || ENOTDIR not defined -- check the comment below this line in the source for details
+ /*
+ * If ENOENT || ENOTDIR is not defined in one of the your OS's
+ * include files, Apache does not know how to check to see why
+ * the stat() of the index file failed; there are cases where
+ * it can fail even though the file exists. This means
+ * that it is possible for someone to get a directory
+ * listing of a directory even though there is an index
+ * (eg. index.html) file in it. If you do not have a
+ * problem with this, delete the above #error line and
+ * start the compile again. If you need to do this, please
+ * submit a bug report from http://www.apache.org/bug_report.html
+ * letting us know that you needed to do this. Please be
+ * sure to include the operating system you are using.
+ */
+
+ else {
+#endif
+ last_cp = cp;
+
+ while (--cp > path && *cp != '/')
+ continue;
+
+ while (cp > path && cp[-1] == '/')
+ --cp;
+ }
+#if defined(ENOENT) && defined(ENOTDIR)
+ else {
+#if defined(EACCES)
+ if (errno != EACCES)
+#endif
+ log_printf(r->server,
+ "access to %s failed for %s, reason: stat: %s (errno = %d)",
+ r->uri, get_remote_host(r->connection, r->per_dir_config,
+ REMOTE_NAME), strerror(errno), errno);
+
+ return HTTP_FORBIDDEN;
+ }
+#endif /* ENOENT && ENOTDIR */
+ }
+ return OK;
+}
+
+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.
+ */
+
+#ifdef __EMX__
+ /* Add OS/2 drive name support */
+ if ((test_filename[0] != '/') && (test_filename[1] != ':'))
+#else
+ if (test_filename[0] != '/')
+#endif
+ {
+/* 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 (entry_core->r) {
+ if (!regexec(entry_core->r, test_filename, 0, NULL, 0))
+ this_conf = entry_config;
+ }
+ else if (entry_core->d_is_matchexp) {
+ 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);
+
+ res = get_path_info (r);
+ if (res != OK) {
+ return res;
+ }
+
+ if ((res = check_safe_file(r))) {
+ return res;
+ }
+
+ if (test_filename[strlen(test_filename)-1] == '/')
+ --num_dirs;
+
+ 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 overrides_here;
+ void *this_conf, *htaccess_conf = NULL;
+ char *this_dir = make_dirstr (r->pool, test_filename, i);
+ int j;
+
+ /* Do symlink checks first, because they are done with the
+ * permissions appropriate to the *parent* directory...
+ */
+
+ if ((res = check_symlinks (this_dir, core_dir->opts)))
+ {
+ log_reason("Symbolic link not allowed", this_dir, r);
+ return res;
+ }
+
+ /* Begin *this* level by looking for matching <Directory> 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;
+
+ this_conf = NULL;
+ if (entry_core->r) {
+ if (!regexec(entry_core->r, this_dir, 0, NULL,
+ (j == num_sec) ? 0 : REG_NOTEOL)) {
+ /* 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 (entry_core->d_is_matchexp &&
+ !strcmp_match(this_dir, entry_dir)) {
+ 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) {
+ char *config_name = make_full_path(r->pool, this_dir,
+ sconf->access_name);
+ 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;
+
+ /* Symlink permissions are determined by the parent. If the request is for
+ * a directory then applying the symlink test here would use the
+ * permissions of the directory as opposed to its parent. Consider a
+ * symlink pointing to a dir with a .htaccess disallowing symlinks. If you
+ * access /symlink (or /symlink/) you would get a 403 without this S_ISDIR
+ * test. But if you accessed /symlink/index.html, for example, you would
+ * *not* get the 403.
+ */
+ if (!S_ISDIR (r->finfo.st_mode)
+ && (res = check_symlinks (r->filename, 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 len, num_url = url_array->nelts;
+ char *test_location = pstrdup (r->pool, r->uri);
+
+ /* Collapse multiple slashes, if it's a path URL (we don't want to
+ * do anything to <Location http://...> or such).
+ */
+ if (test_location[0] == '/')
+ no2slash (test_location);
+
+ /* 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;
+
+ len = strlen(entry_url);
+
+ this_conf = NULL;
+
+ if (entry_core->r) {
+ if (!regexec(entry_core->r, test_location, 0, NULL, 0))
+ this_conf = entry_config;
+ }
+ else if( entry_core->d_is_matchexp ) {
+ if (!strcmp_match(test_location, entry_url))
+ this_conf = entry_config;
+ }
+ else if (!strncmp (test_location, entry_url, len) &&
+ (entry_url[len - 1] == '/' ||
+ test_location[len] == '/' || test_location[len] == '\0'))
+ this_conf = entry_config;
+
+ if (this_conf)
+ per_dir_defaults = merge_per_dir_configs (r->pool,
+ per_dir_defaults, this_conf);
+ }
+
+ r->per_dir_config = per_dir_defaults;
+ }
+
+ return OK;
+}
+
+int file_walk (request_rec *r)
+{
+ core_dir_config *conf = get_module_config(r->per_dir_config, &core_module);
+ array_header *file_array = copy_array (r->pool, conf->sec);
+ void *per_dir_defaults = r->per_dir_config;
+
+ core_dir_config **file = (core_dir_config **)file_array->elts;
+ int len, num_files = file_array->nelts;
+ char *test_file = pstrdup (r->pool, r->filename);
+
+ /* Collapse multiple slashes */
+ no2slash (test_file);
+
+ /* Go through the file entries, and check for matches. */
+
+ if (num_files) {
+ void *this_conf, *entry_config;
+ core_dir_config *entry_core;
+ char *entry_file;
+ int j;
+
+/*
+ * we apply the directive sections in some order; should really try them
+ * with the most general first.
+ */
+ for (j = 0; j < num_files; ++j) {
+
+ entry_config = file[j];
+ if (!entry_config) continue;
+
+ entry_core =(core_dir_config *)
+ get_module_config(entry_config, &core_module);
+ entry_file = entry_core->d;
+
+ len = strlen(entry_file);
+
+ this_conf = NULL;
+
+ if (entry_core->r) {
+ if (!regexec(entry_core->r, test_file, 0, NULL, 0))
+ this_conf = entry_config;
+ }
+ else if ( entry_core->d_is_matchexp ) {
+ if (!strcmp_match(test_file, entry_file))
+ this_conf = entry_config;
+ }
+ else if (!strncmp (test_file, entry_file, len) &&
+ (entry_file[len - 1] == '/' ||
+ test_file[len] == '/' || test_file[len] == '\0'))
+ 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 Apache version didn't destroy the sub_reqs used in directory
+ * indexing. The result, when indexing a directory with 800-odd files in
+ * it, was massively excessive storage allocation).
+ *
+ * Note more manipulation of protocol-specific vars in the request
+ * structure...
+ */
+
+request_rec *make_sub_request (const 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_uri (const char *new_file, const request_rec *r)
+{
+ request_rec *rnew;
+ int res;
+ char *udir;
+
+ rnew = make_sub_request (r);
+ rnew->request_time = r->request_time;
+ rnew->connection = r->connection;
+ rnew->server = r->server;
+ rnew->request_config = create_request_config (rnew->pool);
+ rnew->htaccess = r->htaccess; /* copy htaccess cache */
+ rnew->per_dir_config=r->server->lookup_defaults;
+ 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);
+
+ if ((res = location_walk (rnew))) {
+ rnew->status=res;
+ return rnew;
+ }
+
+ 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.
+ */
+ /* NB: directory_walk() clears the per_dir_config, so we don't inherit from
+ location_walk() above */
+
+ if ((res = directory_walk (rnew))
+ || (res = file_walk (rnew))
+ || (res = location_walk (rnew))
+ || ((satisfies(rnew)==SATISFY_ALL || satisfies(rnew)==SATISFY_NOSPEC)?
+ ((res = check_access (rnew))
+ || (some_auth_required (rnew) &&
+ ((res = check_user_id (rnew)) || (res = check_auth (rnew))))):
+ ((res = check_access (rnew))
+ && (!some_auth_required (rnew) ||
+ ((res = check_user_id (rnew)) || (res = check_auth (rnew)))))
+ )
+ || (res = find_types (rnew))
+ || (res = run_fixups (rnew))
+ )
+ {
+ rnew->status = res;
+ }
+
+ return rnew;
+}
+
+request_rec *sub_req_lookup_file (const char *new_file, const request_rec *r)
+{
+ request_rec *rnew;
+ int res;
+ char *fdir;
+
+ rnew = make_sub_request (r);
+ rnew->request_time = r->request_time;
+ 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);
+ fdir = make_dirstr (rnew->pool, r->filename, count_dirs (r->filename));
+
+ /* Check for a special case... if there are no '/' characters in new_file
+ * at all, then we are looking at a relative lookup in the same directory.
+ * That means we won't have to redo directory_walk, and we may not
+ * even have to redo access checks.
+ */
+
+ if (strchr (new_file, '/') == NULL) {
+ char *udir = make_dirstr(rnew->pool, r->uri, count_dirs(r->uri));
+
+ rnew->uri = make_full_path (rnew->pool, udir, new_file);
+ rnew->filename = make_full_path (rnew->pool, fdir, new_file);
+ if (stat (rnew->filename, &rnew->finfo) < 0) {
+ rnew->finfo.st_mode = 0;
+ }
+
+ if ((res = check_safe_file(rnew))) {
+ rnew->status = res;
+ return rnew;
+ }
+
+ rnew->per_dir_config = r->per_dir_config;
+
+ /* no matter what, if it's a subdirectory, we need to re-run
+ * directory_walk */
+ if (S_ISDIR (rnew->finfo.st_mode)) {
+ res = directory_walk (rnew);
+ if (!res) {
+ res = file_walk (rnew);
+ }
+ } else {
+ if ((res = check_symlinks (rnew->filename, allow_options (rnew)))) {
+ log_reason ("Symbolic link not allowed", rnew->filename, rnew);
+ rnew->status = res;
+ return rnew;
+ }
+ /* do a file_walk, if it doesn't change the per_dir_config then
+ * we know that we don't have to redo all the access checks */
+ if ((res = file_walk (rnew))) {
+ rnew->status = res;
+ return rnew;
+ }
+ if (rnew->per_dir_config == r->per_dir_config) {
+ if ((res = find_types (rnew)) || (res = run_fixups (rnew))) {
+ rnew->status = res;
+ }
+ return rnew;
+ }
+ }
+ } else {
+ /* XXX: this should be set properly like it is in the same-dir case
+ * but it's actually sometimes to impossible to do it... because the
+ * file may not have a uri associated with it -djg */
+ rnew->uri = "INTERNALLY GENERATED file-relative req";
+ rnew->filename = ((new_file[0] == '/') ?
+ pstrdup(rnew->pool,new_file) :
+ make_full_path (rnew->pool, fdir, new_file));
+ rnew->per_dir_config = r->server->lookup_defaults;
+ res = directory_walk (rnew);
+ if (!res) {
+ res = file_walk (rnew);
+ }
+ }
+
+ if (res
+ || ((satisfies(rnew)==SATISFY_ALL || satisfies(rnew)==SATISFY_NOSPEC)?
+ ((res = check_access (rnew))
+ || (some_auth_required (rnew) &&
+ ((res = check_user_id (rnew)) || (res = check_auth (rnew))))):
+ ((res = check_access (rnew))
+ && (!some_auth_required (rnew) ||
+ ((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 != HTTP_OK) {
+ recursive_error = type;
+
+ while (r->prev && (r->prev->status != HTTP_OK))
+ r = r->prev; /* Get back to original error */
+
+ type = r->status;
+ custom_response = NULL; /* Do NOT retry the custom thing! */
+ }
+
+ r->status = type;
+
+ /*
+ * If we want to keep the connection, be sure that the request body
+ * (if any) has been read.
+ */
+ if ((r->status != HTTP_NOT_MODIFIED) && (r->status != HTTP_NO_CONTENT)
+ && !status_drops_connection(r->status)
+ && r->connection && (r->connection->keepalive != -1)) {
+ (void) discard_request_body(r);
+ }
+
+ /* Two types of custom redirects --- plain text, and URLs.
+ * Plain text has a leading '"', so the URL code, here, is triggered
+ * on its absence
+ */
+
+ if (custom_response && custom_response[0] != '"') {
+
+ if (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_local_copy = 1; /* Do NOT send USE_LOCAL_COPY for
+ * error documents!
+ */
+ /* This redirect needs to be a GET no matter what the original
+ * method was.
+ */
+ table_set(r->subprocess_env, "REQUEST_METHOD", r->method);
+ 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);
+}
+
+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)) ||
+ ((r->proto_num == 1001) && !table_get(r->headers_in, "Host"))) {
+ /* 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.
+ * As a special case, HTTP/1.1 mentions twice (S9, S14.23)
+ * that a request MUST contain a Host: header, and the server
+ * MUST respond with 400 if it doesn't.
+ */
+ log_reason ("client sent HTTP/1.1 request without hostname (see RFC2068 sections 9 and 14.23)",
+ r->uri, r);
+ die (BAD_REQUEST, r);
+ return;
+ }
+
+ if (!r->proxyreq)
+ {
+ /* We don't want TRACE to run through the normal handler set,
+ * we handle it specially.
+ */
+ if (r->method_number == M_TRACE) {
+ if ((access_status = send_http_trace(r)))
+ die(access_status, r);
+ else
+ finalize_request_protocol(r);
+ return;
+ }
+
+ access_status = unescape_url(r->uri);
+ if (access_status)
+ {
+ die(access_status, r);
+ return;
+ }
+
+ getparents(r->uri); /* OK --- shrinking transformations... */
+ }
+
+ if ((access_status = location_walk (r))) {
+ die (access_status, r);
+ return;
+ }
+
+ if ((access_status = translate_name (r))) {
+ decl_die (access_status, "translate", r);
+ return;
+ }
+
+ if (r->proto_num > 1000 && table_get (r->subprocess_env, "downgrade-1.0")) {
+ r->proto_num = 1000;
+ }
+
+ /* NB: directory_walk() clears the per_dir_config, so we don't inherit from
+ location_walk() above */
+
+ if ((access_status = directory_walk (r))) {
+ die (access_status, r);
+ return;
+ }
+
+ if ((access_status = file_walk (r))) {
+ die (access_status, r);
+ return;
+ }
+
+ if ((access_status = location_walk (r))) {
+ die (access_status, r);
+ return;
+ }
+
+ if ((access_status = header_parse (r))) {
+ die (access_status, r);
+ return;
+ }
+
+ switch (satisfies(r)) {
+ case SATISFY_ALL: case SATISFY_NOSPEC:
+ 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;
+ }
+ }
+ break;
+ case SATISFY_ANY:
+ if ((access_status = check_access (r)) != 0) {
+ if (!some_auth_required (r)) {
+ decl_die (access_status, "check access", r);
+ return;
+ }
+ 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;
+ }
+ }
+ break;
+ }
+
+ 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);
+ return;
+ }
+
+ /* Take care of little things that need to happen when we're done */
+ finalize_request_protocol (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 (const char *new_uri, request_rec *r)
+{
+ request_rec *new = (request_rec *)pcalloc(r->pool, sizeof(request_rec));
+ char t[256]; /* 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->the_request = r->the_request;
+
+ new->method = r->method;
+ new->method_number = r->method_number;
+ new->allowed = r->allowed;
+
+ new->status = r->status;
+ new->assbackwards = r->assbackwards;
+ new->header_only = r->header_only;
+ new->protocol = r->protocol;
+ new->proto_num = r->proto_num;
+ new->hostname = r->hostname;
+ new->hostlen = r->hostlen;
+ new->request_time = r->request_time;
+ 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!
+ */
+ new->no_local_copy = r->no_local_copy;
+
+ new->read_length = r->read_length; /* We can only read it once */
+
+ ap_snprintf (t, sizeof(t), "%d", r->status);
+ table_set (new->subprocess_env, "REDIRECT_STATUS", pstrdup (r->pool, t));
+
+ return new;
+}
+
+void internal_redirect (const char *new_uri, request_rec *r)
+{
+ request_rec *new = internal_internal_redirect(new_uri, r);
+ process_request_internal (new);
+}
+
+/* This function is designed for things like actions or CGI scripts, when
+ * using AddHandler, and you want to preserve the content type across
+ * an internal redirect.
+ */
+
+void internal_redirect_handler (const char *new_uri, request_rec *r)
+{
+ request_rec *new = internal_internal_redirect(new_uri, r);
+ if (r->handler)
+ new->content_type = r->content_type;
+ process_request_internal (new);
+}
diff --git a/usr.sbin/httpd/src/http_request.h b/usr.sbin/httpd/src/http_request.h
new file mode 100644
index 00000000000..f20d494cad9
--- /dev/null
+++ b/usr.sbin/httpd/src/http_request.h
@@ -0,0 +1,92 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 (const char *new_file, const request_rec *r);
+request_rec *sub_req_lookup_file (const char *new_file, const 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 (const char *new_uri, request_rec *);
+void internal_redirect_handler (const char *new_uri, request_rec *);
+int some_auth_required (request_rec *r);
+
+#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/usr.sbin/httpd/src/httpd.h b/usr.sbin/httpd/src/httpd.h
new file mode 100644
index 00000000000..33bb73cd645
--- /dev/null
+++ b/usr.sbin/httpd/src/httpd.h
@@ -0,0 +1,766 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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.
+ */
+#ifndef HTTPD_ROOT
+#ifdef __EMX__
+/* Set default for OS/2 file system */
+#define HTTPD_ROOT "/os2httpd"
+#else
+#define HTTPD_ROOT "/var/www"
+#endif
+#endif
+
+#ifndef DOCUMENT_LOCATION
+/* Root of server */
+#ifdef __EMX__
+/* Set default for OS/2 file system */
+#define DOCUMENT_LOCATION "/os2httpd/docs"
+#else
+#define DOCUMENT_LOCATION "/var/www/htdocs"
+#endif
+#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 --- */
+
+#ifndef DEFAULT_USER
+#define DEFAULT_USER "#-1"
+#endif
+#ifndef DEFAULT_GROUP
+#define DEFAULT_GROUP "#-1"
+#endif
+
+/* The name of the log files */
+#ifndef DEFAULT_XFERLOG
+#ifdef __EMX__
+/* Set default for OS/2 file system */
+#define DEFAULT_XFERLOG "logs/access.log"
+#else
+#define DEFAULT_XFERLOG "logs/access_log"
+#endif
+#endif /* DEFAULT_XFERLOG */
+#ifndef DEFAULT_ERRORLOG
+#ifdef __EMX__
+/* Set default for OS/2 file system */
+#define DEFAULT_ERRORLOG "logs/error.log"
+#else
+#define DEFAULT_ERRORLOG "logs/error_log"
+#endif
+#endif /* DEFAULT_ERRORLOG */
+#ifndef DEFAULT_PIDLOG
+#define DEFAULT_PIDLOG "logs/httpd.pid"
+#endif
+#ifndef DEFAULT_SCOREBOARD
+#define DEFAULT_SCOREBOARD "logs/apache_runtime_status"
+#endif
+#ifndef DEFAULT_LOCKFILE
+#define DEFAULT_LOCKFILE "logs/accept.lock"
+#endif
+
+/* Define this to be what your HTML directory content files are called */
+#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/plain"
+
+/* 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 */
+#ifndef SERVER_CONFIG_FILE
+#define SERVER_CONFIG_FILE "conf/httpd.conf"
+#endif
+
+#ifndef RESOURCE_CONFIG_FILE
+/* The name of the document config file */
+#define RESOURCE_CONFIG_FILE "conf/srm.conf"
+#endif
+
+#ifndef TYPES_CONFIG_FILE
+/* The name of the MIME types file */
+#define TYPES_CONFIG_FILE "conf/mime.types"
+#endif
+
+#ifndef ACCESS_CONFIG_FILE
+/* The name of the access file */
+#define ACCESS_CONFIG_FILE "conf/access.conf"
+#endif
+
+/* 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 */
+#ifndef DEFAULT_PATH
+#define DEFAULT_PATH "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin"
+#endif
+
+/* The path to the Bourne shell, for parsed docs */
+#ifndef SHELL_PATH
+#ifdef __EMX__
+/* Set default for OS/2 file system */
+#define SHELL_PATH "CMD.EXE"
+#else
+#define SHELL_PATH "/bin/sh"
+#endif
+#endif
+
+/* The path to the suExec wrapper, can be overridden in Configuration */
+#ifndef SUEXEC_BIN
+#define SUEXEC_BIN "/usr/local/etc/httpd/sbin/suexec"
+#endif
+
+/* The default string lengths */
+#define MAX_STRING_LEN HUGE_STRING_LEN
+#define HUGE_STRING_LEN 8192
+
+/* The timeout for waiting for messages */
+#define DEFAULT_TIMEOUT 300
+
+/* 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 100
+
+/* The size of the server's internal read-write buffers */
+#define IOBUFSIZE 8192
+
+/* Number of servers to spawn off by default --- also, if fewer than
+ * this free when the caretaker checks, it will spawn more.
+ */
+#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. Secondly, it keeps the size of the scoreboard file small
+ * enough that we can read the whole thing without worrying too much about
+ * the overhead.
+ */
+#ifndef HARD_SERVER_LIMIT
+#define HARD_SERVER_LIMIT 256
+#endif
+
+/* 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
+ * identifier below, please keep to the HTTP 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, by adding
+ * their product tokens to EXTRA_CFLAGS in the Configuration file like so.
+ *
+ * EXTRA_CFLAGS="-DSERVER_SUBVERSION="MrWidget/0.1-alpha"
+ *
+ * The tokens are listed in order of their significance for identifying the
+ * application.
+ *
+ * "Product tokens should be short and to the point -- use of them for
+ * advertizing or other non-essential information is explicitly forbidden."
+ *
+ * Example: "Apache/1.1.0 MrWidget/0.1-alpha"
+ */
+
+#define SERVER_BASEVERSION "Apache/1.2.6" /* SEE COMMENTS ABOVE */
+#ifdef SERVER_SUBVERSION
+#define SERVER_VERSION SERVER_BASEVERSION " " SERVER_SUBVERSION
+#else
+#define SERVER_VERSION SERVER_BASEVERSION
+#endif
+
+/* Numeric release version identifier: major minor bugfix betaseq
+ * Always increases along the same track as the source branch.
+ */
+#define APACHE_RELEASE 1020600
+
+#define SERVER_PROTOCOL "HTTP/1.1"
+#define SERVER_SUPPORT "http://www.apache.org/"
+
+#define DECLINED -1 /* Module declines to handle */
+#define OK 0 /* Module has handled this stage. */
+
+
+/* ----------------------- HTTP Status Codes ------------------------- */
+
+#define RESPONSE_CODES 38
+
+#define HTTP_CONTINUE 100
+#define HTTP_SWITCHING_PROTOCOLS 101
+#define HTTP_OK 200
+#define HTTP_CREATED 201
+#define HTTP_ACCEPTED 202
+#define HTTP_NON_AUTHORITATIVE 203
+#define HTTP_NO_CONTENT 204
+#define HTTP_RESET_CONTENT 205
+#define HTTP_PARTIAL_CONTENT 206
+#define HTTP_MULTIPLE_CHOICES 300
+#define HTTP_MOVED_PERMANENTLY 301
+#define HTTP_MOVED_TEMPORARILY 302
+#define HTTP_SEE_OTHER 303
+#define HTTP_NOT_MODIFIED 304
+#define HTTP_USE_PROXY 305
+#define HTTP_BAD_REQUEST 400
+#define HTTP_UNAUTHORIZED 401
+#define HTTP_PAYMENT_REQUIRED 402
+#define HTTP_FORBIDDEN 403
+#define HTTP_NOT_FOUND 404
+#define HTTP_METHOD_NOT_ALLOWED 405
+#define HTTP_NOT_ACCEPTABLE 406
+#define HTTP_PROXY_AUTHENTICATION_REQUIRED 407
+#define HTTP_REQUEST_TIME_OUT 408
+#define HTTP_CONFLICT 409
+#define HTTP_GONE 410
+#define HTTP_LENGTH_REQUIRED 411
+#define HTTP_PRECONDITION_FAILED 412
+#define HTTP_REQUEST_ENTITY_TOO_LARGE 413
+#define HTTP_REQUEST_URI_TOO_LARGE 414
+#define HTTP_UNSUPPORTED_MEDIA_TYPE 415
+#define HTTP_INTERNAL_SERVER_ERROR 500
+#define HTTP_NOT_IMPLEMENTED 501
+#define HTTP_BAD_GATEWAY 502
+#define HTTP_SERVICE_UNAVAILABLE 503
+#define HTTP_GATEWAY_TIME_OUT 504
+#define HTTP_VERSION_NOT_SUPPORTED 505
+#define HTTP_VARIANT_ALSO_VARIES 506
+
+#define DOCUMENT_FOLLOWS HTTP_OK
+#define PARTIAL_CONTENT HTTP_PARTIAL_CONTENT
+#define MULTIPLE_CHOICES HTTP_MULTIPLE_CHOICES
+#define MOVED HTTP_MOVED_PERMANENTLY
+#define REDIRECT HTTP_MOVED_TEMPORARILY
+#define USE_LOCAL_COPY HTTP_NOT_MODIFIED
+#define BAD_REQUEST HTTP_BAD_REQUEST
+#define AUTH_REQUIRED HTTP_UNAUTHORIZED
+#define FORBIDDEN HTTP_FORBIDDEN
+#define NOT_FOUND HTTP_NOT_FOUND
+#define METHOD_NOT_ALLOWED HTTP_METHOD_NOT_ALLOWED
+#define NOT_ACCEPTABLE HTTP_NOT_ACCEPTABLE
+#define LENGTH_REQUIRED HTTP_LENGTH_REQUIRED
+#define PRECONDITION_FAILED HTTP_PRECONDITION_FAILED
+#define SERVER_ERROR HTTP_INTERNAL_SERVER_ERROR
+#define NOT_IMPLEMENTED HTTP_NOT_IMPLEMENTED
+#define BAD_GATEWAY HTTP_BAD_GATEWAY
+#define VARIANT_ALSO_VARIES HTTP_VARIANT_ALSO_VARIES
+
+#define is_HTTP_INFO(x) (((x) >= 100)&&((x) < 200))
+#define is_HTTP_SUCCESS(x) (((x) >= 200)&&((x) < 300))
+#define is_HTTP_REDIRECT(x) (((x) >= 300)&&((x) < 400))
+#define is_HTTP_ERROR(x) (((x) >= 400)&&((x) < 600))
+#define is_HTTP_CLIENT_ERROR(x) (((x) >= 400)&&((x) < 500))
+#define is_HTTP_SERVER_ERROR(x) (((x) >= 500)&&((x) < 600))
+
+#define status_drops_connection(x) (((x) == HTTP_BAD_REQUEST) || \
+ ((x) == HTTP_REQUEST_TIME_OUT) || \
+ ((x) == HTTP_LENGTH_REQUIRED) || \
+ ((x) == HTTP_REQUEST_ENTITY_TOO_LARGE) || \
+ ((x) == HTTP_REQUEST_URI_TOO_LARGE) || \
+ ((x) == HTTP_INTERNAL_SERVER_ERROR) || \
+ ((x) == HTTP_SERVICE_UNAVAILABLE))
+
+
+#define METHODS 8
+#define M_GET 0
+#define M_PUT 1
+#define M_POST 2
+#define M_DELETE 3
+#define M_CONNECT 4
+#define M_OPTIONS 5
+#define M_TRACE 6
+#define M_INVALID 7
+
+#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
+
+/* Possible values for request_rec.read_body (set by handling module):
+ * REQUEST_NO_BODY Send 413 error if message has any body
+ * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
+ * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
+ * REQUEST_CHUNKED_PASS Pass the chunks to me without removal.
+ */
+#define REQUEST_NO_BODY 0
+#define REQUEST_CHUNKED_ERROR 1
+#define REQUEST_CHUNKED_DECHUNK 2
+#define REQUEST_CHUNKED_PASS 3
+
+/* Things which may vary per file-lookup WITHIN a request ---
+ * e.g., state of MIME config. Basically, the name of an object, info
+ * about the object, and any other info we may ahve which may need to
+ * change as we go poking around looking for it (e.g., overridden by
+ * .htaccess files).
+ *
+ * Note how the default state of almost all these things is properly
+ * zero, so that allocating it with pcalloc does the right thing without
+ * a whole lot of hairy initialization... so long as we are willing to
+ * make the (fairly) portable assumption that the bit pattern of a NULL
+ * pointer is, in fact, zero.
+ */
+
+/* This represents the result of calling htaccess; these are cached for
+ * each request.
+ */
+struct htaccess_result
+{
+ char *dir; /* the directory to which this applies */
+ int override; /* the overrides allowed for the .htaccess file */
+ void *htaccess; /* the configuration directives */
+/* the next one, or NULL if no more; N.B. never change this */
+ const struct htaccess_result *next;
+};
+
+
+typedef struct conn_rec conn_rec;
+typedef struct server_rec server_rec;
+typedef struct request_rec request_rec;
+typedef struct listen_rec listen_rec;
+
+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 */
+
+ time_t request_time; /* When the request started */
+
+ 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 allowed; /* Allowed methods - for 405, OPTIONS, etc */
+
+ int sent_bodyct; /* byte count in stream is for body */
+ long bytes_sent; /* body byte count, for easy access */
+
+ /* HTTP/1.1 connection-level features */
+
+ int chunked; /* sending chunked transfer-coding */
+ int byterange; /* number of byte ranges */
+ char *boundary; /* multipart/byteranges boundary */
+ char *range; /* The Range: header */
+ long clength; /* The "real" content length */
+
+ long remaining; /* bytes left to read */
+ long read_length; /* bytes that have been read */
+ int read_body; /* how the request body should be read */
+ int read_chunked; /* reading chunked transfer-coding */
+
+ /* 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; /* for back-compat. only -- do not use */
+ array_header *content_languages; /* array of (char*) */
+
+ int no_cache;
+ int no_local_copy;
+
+ /* 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;
+ server_rec *base_server; /* Physical vhost this conn come in on */
+
+ /* 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 rfc1413 lookups.
+ * N.B. Only access this through
+ * get_remote_logname() */
+ char *user; /* If an authentication check was made,
+ * this gets set to the user name. We assume
+ * that there's only one user per connection(!)
+ */
+ char *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... */
+
+/* The address 255.255.255.255, when used as a virtualhost address,
+ * will become the "default" server when the ip doesn't match other vhosts.
+ */
+#define DEFAULT_VHOST_ADDR 0xfffffffful
+
+typedef struct server_addr_rec server_addr_rec;
+struct server_addr_rec {
+ server_addr_rec *next;
+ struct in_addr host_addr; /* The bound address, for this server */
+ unsigned short host_port; /* The bound port, for this server */
+ char *virthost; /* The name given in <VirtualHost> */
+};
+
+
+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;
+ unsigned 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 */
+
+ server_addr_rec *addrs;
+ int timeout; /* Timeout, in seconds, before we give up */
+ int keep_alive_timeout; /* Seconds we'll wait for another request */
+ int keep_alive_max; /* Maximum requests per connection */
+ int keep_alive; /* Use persistent connections? */
+ int send_buffer_size; /* size of TCP send buffer (in bytes) */
+
+ char *path; /* Pathname for ServerPath */
+ int pathlen; /* Length of path */
+
+ char *names; /* Wildcarded names for ServerAlias servers */
+
+ uid_t server_uid; /* effective user id when calling exec wrapper */
+ gid_t server_gid; /* effective group id when calling exec wrapper */
+};
+
+/* These are more like real hosts than virtual hosts */
+struct listen_rec {
+ listen_rec *next;
+ struct sockaddr_in local_addr; /* local IP address and port */
+ int fd;
+ int used; /* Only used during restart */
+/* more stuff here, like which protocol is bound to the port */
+};
+
+/* Prototypes for utilities... util.c.
+ */
+
+/* Time */
+extern const char month_snames[12][4];
+
+struct tm *get_gmtoff(int *tz);
+char *get_time();
+char *ht_time (pool *p, time_t t, const char *fmt, int gmt);
+char *gm_timestr_822(pool *p, time_t t);
+
+/* String handling. The *_nc variants allow you to use non-const char **s as
+arguments (unfortunately C won't automatically convert a char ** to a const
+char **) */
+
+char *getword(pool *p, const char **line, char stop);
+char *getword_nc(pool *p, char **line, char stop);
+char *getword_white(pool *p, const char **line);
+char *getword_white_nc(pool *p, char **line);
+char *getword_nulls (pool *p, const char **line, char stop);
+char *getword_nulls_nc (pool *p, char **line, char stop);
+char *getword_conf (pool *p, const char **line);
+char *getword_conf_nc (pool *p, char **line);
+
+char *get_token (pool *p, char **accept_line, int accept_white);
+int find_token (pool *p, const char *line, const char *tok);
+int find_last_token (pool *p, const char *line, const char *tok);
+
+int is_url(const 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);
+#define escape_uri(ppool,path) os_escape_path(ppool,path,1)
+extern char *escape_html(pool *p, const char *s);
+char *construct_server(pool *p, const char *hostname, unsigned port);
+char *construct_url (pool *p, const char *path, const server_rec *s);
+char *escape_shell_cmd (pool *p, const char *s);
+
+int count_dirs(const char *path);
+char *make_dirstr(pool *a, const char *s, int n);
+char *make_full_path(pool *a, const char *dir, const char *f);
+
+int is_matchexp(const char *str);
+int strcmp_match(const char *str, const char *exp);
+int strcasecmp_match(const char *str, const char *exp);
+char *uudecode (pool *, const char *);
+
+char *pregsub(pool *p, const char *input, const char *source,
+ size_t nmatch, regmatch_t pmatch[]);
+
+void str_tolower (char *);
+int ind (const char *, char); /* Sigh... */
+int rind (const char *, char);
+
+int cfg_getline(char *s, int n, FILE *f);
+
+#ifdef NEED_STRERROR
+char *strerror (int err);
+#endif
+
+/* Misc system hackery */
+
+uid_t uname2id(const char *name);
+gid_t gname2id(const char *name);
+int is_directory(const char *name);
+int can_exec(const struct stat *);
+void chdir_file(const char *file);
+
+char *get_local_host(pool *);
+unsigned long get_virthost_addr (const char *hostname, unsigned short *port);
+
+extern time_t restart_time;
+
+/*
+ * Apache tries to keep all of its long term filehandles (such as log files,
+ * and sockets) above this number. This is to workaround problems in many
+ * third party libraries that are compiled with a small FD_SETSIZE. There
+ * should be no reason to lower this, because it's only advisory. If a file
+ * can't be allocated above this number then it will remain in the "slack"
+ * area.
+ *
+ * Only the low slack line is used by default. If HIGH_SLACK_LINE is defined
+ * then an attempt is also made to keep all non-FILE * files above the high
+ * slack line. This is to work around a Solaris C library limitation, where it
+ * uses an unsigned char to store the file descriptor.
+ */
+#ifndef LOW_SLACK_LINE
+#define LOW_SLACK_LINE 15
+#endif
+/* #define HIGH_SLACK_LINE 255 */
+
+/*
+ * The ap_slack() function takes a fd, and tries to move it above the indicated
+ * line. It returns an fd which may or may not have moved above the line, and
+ * never fails. If the high line was requested and it fails it will also try
+ * the low line.
+ */
+int ap_slack (int fd, int line);
+#define AP_SLACK_LOW 1
+#define AP_SLACK_HIGH 2
diff --git a/usr.sbin/httpd/src/md5.h b/usr.sbin/httpd/src/md5.h
new file mode 100644
index 00000000000..a8ff86c4bd0
--- /dev/null
+++ b/usr.sbin/httpd/src/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,1997 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/usr.sbin/httpd/src/md5c.c b/usr.sbin/httpd/src/md5c.c
new file mode 100644
index 00000000000..fd42bcb456e
--- /dev/null
+++ b/usr.sbin/httpd/src/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,1997 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/usr.sbin/httpd/src/mod_access.c b/usr.sbin/httpd/src/mod_access.c
new file mode 100644
index 00000000000..eb352380123
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_access.c
@@ -0,0 +1,283 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * 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"
+#include "http_request.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;
+}
+
+const 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;
+}
+
+const 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 (strcasecmp(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] == '.');
+}
+
+static int is_ip(const char *host)
+{
+ while ((*host == '.') || isdigit(*host))
+ host++;
+ return (*host == '\0');
+}
+
+int find_allowdeny (request_rec *r, array_header *a, int method)
+{
+ allowdeny *ap = (allowdeny *)a->elts;
+ int mmask = (1 << method);
+ int i;
+ int gothost = 0;
+ const char *remotehost = NULL;
+
+ for (i = 0; i < a->nelts; ++i) {
+ if (!(mmask & ap[i].limited))
+ continue;
+
+ if (!strncmp(ap[i].from,"env=",4) && table_get(r->subprocess_env,ap[i].from+4))
+ return 1;
+
+ if (ap[i].from && !strcmp(ap[i].from, "user-agents")) {
+ char * this_agent = table_get(r->headers_in, "User-Agent");
+ int j;
+
+ if (!this_agent) return 0;
+
+ for (j = i+1; j < a->nelts; ++j) {
+ if (strstr(this_agent, ap[j].from)) return 1;
+ }
+ return 0;
+ }
+
+ if (!strcmp (ap[i].from, "all"))
+ return 1;
+
+ if (!gothost) {
+ remotehost = get_remote_host(r->connection, r->per_dir_config,
+ REMOTE_HOST);
+
+ if ((remotehost == NULL) || is_ip(remotehost))
+ gothost = 1;
+ else
+ gothost = 2;
+ }
+
+ if ((gothost == 2) && 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 && (
+ satisfies(r) != SATISFY_ANY || !some_auth_required(r)
+ )) {
+ 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_actions.c b/usr.sbin/httpd/src/mod_actions.c
new file mode 100644
index 00000000000..570147069ed
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_actions.c
@@ -0,0 +1,219 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * 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 is
+ * requested. 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;
+}
+
+const char *add_action(cmd_parms *cmd, action_dir_config *m, char *type,
+ char *script)
+{
+ table_set (m->action_types, type, script);
+ return NULL;
+}
+
+const 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;
+
+ /* Set allowed stuff */
+ if (conf->get) r->allowed |= (1 << M_GET);
+ if (conf->post) r->allowed |= (1 << M_POST);
+ if (conf->put) r->allowed |= (1 << M_PUT);
+ if (conf->delete) r->allowed |= (1 << M_DELETE);
+
+ /* 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 ((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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_alias.c b/usr.sbin/httpd/src/mod_alias.c
new file mode 100644
index 00000000000..2d96fd713fd
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_alias.c
@@ -0,0 +1,329 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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_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;
+ int redir_status; /* 301, 302, 303, 410, etc */
+} alias_entry;
+
+typedef struct {
+ array_header *aliases;
+ array_header *redirects;
+} alias_server_conf;
+
+typedef struct {
+ array_header *redirects;
+} alias_dir_conf;
+module 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 *create_alias_dir_config (pool *p, char *d)
+{
+ alias_dir_conf *a =
+ (alias_dir_conf *)pcalloc (p, sizeof(alias_dir_conf));
+ a->redirects = make_array (p, 2, 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;
+}
+
+void *merge_alias_dir_config (pool *p, void *basev, void *overridesv)
+{
+ alias_dir_conf *a =
+ (alias_dir_conf *)pcalloc (p, sizeof(alias_dir_conf));
+ alias_dir_conf *base = (alias_dir_conf *)basev,
+ *overrides = (alias_dir_conf *)overridesv;
+ a->redirects = append_arrays (p, overrides->redirects, base->redirects);
+ return a;
+}
+
+const 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;
+}
+
+const char *add_redirect(cmd_parms *cmd, alias_dir_conf *dirconf, char *arg1,
+ char *arg2, char *arg3)
+{
+ alias_entry *new;
+ server_rec *s = cmd->server;
+ alias_server_conf *serverconf =
+ (alias_server_conf *)get_module_config(s->module_config,&alias_module);
+ int status = (int)cmd->info;
+ char *f = arg2;
+ char *url = arg3;
+
+ if (!strcasecmp(arg1, "gone"))
+ status = HTTP_GONE;
+ else if (!strcasecmp(arg1, "permanent"))
+ status = HTTP_MOVED_PERMANENTLY;
+ else if (!strcasecmp(arg1, "temp"))
+ status = HTTP_MOVED_TEMPORARILY;
+ else if (!strcasecmp(arg1, "seeother"))
+ status = HTTP_SEE_OTHER;
+ else if (isdigit(*arg1))
+ status = atoi(arg1);
+ else {
+ f = arg1;
+ url = arg2;
+ }
+
+ if (is_HTTP_REDIRECT(status)) {
+ if (!url) return "URL to redirect to is missing";
+ if (!is_url (url)) return "Redirect to non-URL";
+ }
+ else {
+ if (url) return "Redirect URL not valid for this status";
+ }
+
+ if ( cmd->path )
+ new = push_array (dirconf->redirects);
+ else
+ new = push_array (serverconf->redirects);
+
+ new->fake = f; new->real = url;
+ new->redir_status = status;
+ 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, (void*)HTTP_MOVED_TEMPORARILY,
+ OR_FILEINFO, TAKE23,
+ "an optional status, then document to be redirected and destination URL" },
+{ "RedirectTemp", add_redirect, (void*)HTTP_MOVED_TEMPORARILY,
+ OR_FILEINFO, TAKE2,
+ "a document to be redirected, then the destination URL" },
+{ "RedirectPermanent", add_redirect, (void*)HTTP_MOVED_PERMANENTLY,
+ OR_FILEINFO, TAKE2,
+ "a document to be redirected, then the destination URL" },
+{ NULL }
+};
+
+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, int *status)
+{
+ 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);
+ }
+
+ *status = p->redir_status;
+
+ 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 *serverconf =
+ (alias_server_conf *)get_module_config(sconf, &alias_module);
+ char *ret;
+ int status;
+
+#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 DECLINED;
+
+ if ((ret = try_alias_list (r, serverconf->redirects, 1, &status)) != NULL) {
+ if (is_HTTP_REDIRECT(status)) {
+ /* include QUERY_STRING if any */
+ if (r->args) {
+ ret = pstrcat (r->pool, ret, "?", r->args, NULL);
+ }
+ table_set (r->headers_out, "Location", ret);
+ }
+ return status;
+ }
+
+ if ((ret = try_alias_list (r, serverconf->aliases, 0, &status)) != NULL) {
+ r->filename = ret;
+ return OK;
+ }
+
+ return DECLINED;
+}
+
+int fixup_redir(request_rec *r)
+{
+ void *dconf = r->per_dir_config;
+ alias_dir_conf *dirconf =
+ (alias_dir_conf *)get_module_config(dconf, &alias_module);
+ char *ret;
+ int status;
+
+ /* It may have changed since last time, so try again */
+
+ if ((ret = try_alias_list (r, dirconf->redirects, 1, &status)) != NULL) {
+ if (is_HTTP_REDIRECT(status))
+ table_set (r->headers_out, "Location", ret);
+ return status;
+ }
+
+ return DECLINED;
+}
+
+module alias_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_alias_dir_config, /* dir config creater */
+ merge_alias_dir_config, /* dir merger --- default is to override */
+ create_alias_config, /* server config */
+ merge_alias_config, /* merge server configs */
+ alias_cmds, /* command table */
+ NULL, /* handlers */
+ translate_alias_redir, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ fixup_redir, /* fixups */
+ NULL, /* logger */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_asis.c b/usr.sbin/httpd/src/mod_asis.c
new file mode 100644
index 00000000000..47bf206d137
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_asis.c
@@ -0,0 +1,131 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 "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;
+
+ r->allowed |= (1 << M_GET);
+ 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 = pfopen (r->pool, 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 == HTTP_OK) || is_HTTP_REDIRECT(r->status))) {
+
+ pfclose(r->pool, f);
+
+ /* Internal redirect -- fake-up a pseudo-request */
+ r->status = HTTP_OK;
+
+ /* This redirect needs to be a GET no matter what the original
+ * method was.
+ */
+ r->method = pstrdup(r->pool, "GET");
+ r->method_number = M_GET;
+
+ internal_redirect_handler (location, r);
+ return OK;
+ }
+
+ send_http_header (r);
+ if (!r->header_only) send_fd (f, r);
+
+ pfclose(r->pool, 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_auth.c b/usr.sbin/httpd/src/mod_auth.c
new file mode 100644
index 00000000000..acacaf07b2f
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_auth.c
@@ -0,0 +1,298 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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_auth: authentication
+ *
+ * Rob McCool
+ *
+ * Adapted to Apache by rst.
+ *
+ * dirkx - Added Authoritative control to allow passing on to lower
+ * modules if and only if the user-id is not known to this
+ * module. A known user with a faulty or absent password still
+ * causes an AuthRequired. The default is 'Authoritative', i.e.
+ * no control is passed along.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#if defined(HAVE_CRYPT_H)
+#include <crypt.h>
+#endif
+
+typedef struct auth_config_struct {
+ char *auth_pwfile;
+ char *auth_grpfile;
+ int auth_authoritative;
+} auth_config_rec;
+
+void *create_auth_dir_config (pool *p, char *d)
+{
+ auth_config_rec *sec =
+ (auth_config_rec *) pcalloc (p, sizeof(auth_config_rec));
+ sec->auth_pwfile = NULL; /* just to illustrate the default really */
+ sec->auth_grpfile = NULL; /* unless you have a broken HP cc */
+ sec->auth_authoritative = 1; /* keep the fortress secure by default */
+ return sec;
+}
+
+const char *set_auth_slot (cmd_parms *cmd, void *offset, char *f, char *t)
+{
+ if (t && strcmp(t, "standard"))
+ return pstrcat(cmd->pool, "Invalid auth file type: ", t, NULL);
+
+ return set_string_slot(cmd, offset, f);
+}
+
+command_rec auth_cmds[] = {
+{ "AuthUserFile", set_auth_slot,
+ (void*)XtOffsetOf(auth_config_rec,auth_pwfile), OR_AUTHCFG, TAKE12, NULL },
+{ "AuthGroupFile", set_auth_slot,
+ (void*)XtOffsetOf(auth_config_rec,auth_grpfile), OR_AUTHCFG, TAKE12, NULL },
+{ "AuthAuthoritative", set_flag_slot,
+ (void*)XtOffsetOf(auth_config_rec,auth_authoritative),
+ OR_AUTHCFG, FLAG,
+ "Set to 'no' to allow access control to be passed along to lower modules if the UserID is not known to this module" },
+{ NULL }
+};
+
+module auth_module;
+
+char *get_pw(request_rec *r, char *user, char *auth_pwfile)
+{
+ FILE *f;
+ char l[MAX_STRING_LEN];
+ const 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];
+ const 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))) {
+ if (!(sec->auth_authoritative))
+ return DECLINED;
+ ap_snprintf(errstr, sizeof(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))) {
+ ap_snprintf(errstr, sizeof(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;
+ const 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;
+
+ if (!(sec -> auth_authoritative))
+ return DECLINED;
+
+ 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_auth_anon.c b/usr.sbin/httpd/src/mod_auth_anon.c
new file mode 100644
index 00000000000..2bb90b4d823
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_auth_anon.c
@@ -0,0 +1,299 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_auth: authentication
+ *
+ * Rob McCool & Brian Behlendorf.
+ *
+ * Adapted to Apache by rst.
+ *
+ * Version 0.5 May 1996
+ *
+ * Modified 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 <directory> 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_Authoritative [ 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_authoritative;
+
+} 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_authoritative =0;
+ return sec;
+}
+
+const char *anon_set_passwd_flag (cmd_parms *cmd,
+ anon_auth_config_rec *sec, int arg) {
+ sec->auth_anon_mustemail=arg;
+ return NULL;
+}
+
+const char *anon_set_userid_flag (cmd_parms *cmd,
+ anon_auth_config_rec *sec, int arg) {
+ sec->auth_anon_nouserid=arg;
+ return NULL;
+}
+const char *anon_set_logemail_flag (cmd_parms *cmd,
+ anon_auth_config_rec *sec, int arg) {
+ sec->auth_anon_logemail=arg;
+ return NULL;
+}
+const char *anon_set_verifyemail_flag (cmd_parms *cmd,
+ anon_auth_config_rec *sec, int arg) {
+ sec->auth_anon_verifyemail=arg;
+ return NULL;
+}
+const char *anon_set_authoritative_flag (cmd_parms *cmd,
+ anon_auth_config_rec *sec, int arg) {
+ sec->auth_anon_authoritative=arg;
+ return NULL;
+}
+
+const 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_Authoritative", anon_set_authoritative_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 && r->prev == NULL && r->main == NULL) {
+ ap_snprintf(errstr, sizeof(errstr), "Anonymous: Passwd <%s> Accepted",
+ send_pw ? send_pw : "\'none\'");
+ log_error (errstr, r->server );
+ }
+ return OK;
+ } else {
+ if (sec->auth_anon_authoritative) {
+ ap_snprintf(errstr, sizeof(errstr),
+ "Anonymous: Authoritative, 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_auth_db.c b/usr.sbin/httpd/src/mod_auth_db.c
new file mode 100644
index 00000000000..76d21d7ea6c
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_auth_db.c
@@ -0,0 +1,303 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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://www.apache.org/>.
+ *
+ */
+
+/*
+ * 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.
+ *
+ * dirkx - Added Authoritative control to allow passing on to lower
+ * modules if and only if the user-id is not known to this
+ * module. A known user with a faulty or absent password still
+ * causes an AuthRequired. The default is 'Authoritative', i.e.
+ * no control is passed along.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include <db.h>
+
+typedef struct {
+
+ char *auth_dbpwfile;
+ char *auth_dbgrpfile;
+ int auth_dbauthoritative;
+} db_auth_config_rec;
+
+void *create_db_auth_dir_config (pool *p, char *d)
+{
+ db_auth_config_rec *sec
+ = (db_auth_config_rec *)pcalloc (p, sizeof(db_auth_config_rec));
+ sec->auth_dbpwfile = NULL;
+ sec->auth_dbgrpfile = NULL;
+ sec->auth_dbauthoritative=1; /* fortress is secure by default */
+ return sec;
+}
+
+const char *set_db_slot (cmd_parms *cmd, void *offset, char *f, char *t)
+{
+ if (!t || strcmp(t, "db"))
+ return DECLINE_CMD;
+
+ return set_string_slot(cmd, offset, f);
+}
+
+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 },
+{ "AuthUserFile", set_db_slot,
+ (void*)XtOffsetOf(db_auth_config_rec, auth_dbpwfile),
+ OR_AUTHCFG, TAKE12, NULL },
+{ "AuthGroupFile", set_db_slot,
+ (void*)XtOffsetOf(db_auth_config_rec, auth_dbgrpfile),
+ OR_AUTHCFG, TAKE12, NULL },
+{ "AuthDBAuthoritative", set_flag_slot,
+ (void*)XtOffsetOf(db_auth_config_rec, auth_dbauthoritative),
+ OR_AUTHCFG, FLAG,
+ "Set to 'no' to allow access control to be passed along to lower modules if the userID is not known to this module" },
+{ 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))) {
+ if (!(sec -> auth_dbauthoritative))
+ return DECLINED;
+ ap_snprintf(errstr, sizeof(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))) {
+ ap_snprintf(errstr, sizeof(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;
+ const char *t;
+ char *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) {
+ const char *orig_groups,*groups;
+ char *v;
+
+ if (!(groups = get_db_grp(r, user, sec->auth_dbgrpfile))) {
+ if (!(sec->auth_dbauthoritative))
+ return DECLINED;
+ ap_snprintf(errstr, sizeof(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;
+ }
+ }
+ ap_snprintf(errstr, sizeof(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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_auth_dbm.c b/usr.sbin/httpd/src/mod_auth_dbm.c
new file mode 100644
index 00000000000..d383264e9f6
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_auth_dbm.c
@@ -0,0 +1,291 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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_auth: authentication
+ *
+ * Rob McCool & Brian Behlendorf.
+ *
+ * Adapted to Apache by rst.
+ *
+ * dirkx - Added Authoritative control to allow passing on to lower
+ * modules if and only if the user-id is not known to this
+ * module. A known user with a faulty or absent password still
+ * causes an AuthRequired. The default is 'Authoritative', i.e.
+ * no control is passed along.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include <ndbm.h>
+
+typedef struct {
+
+ char *auth_dbmpwfile;
+ char *auth_dbmgrpfile;
+ int auth_dbmauthoritative;
+
+} dbm_auth_config_rec;
+
+void *create_dbm_auth_dir_config (pool *p, char *d)
+{
+ dbm_auth_config_rec *sec
+ = (dbm_auth_config_rec *)pcalloc (p, sizeof(dbm_auth_config_rec));
+
+ sec->auth_dbmpwfile = NULL;
+ sec->auth_dbmgrpfile = NULL;
+ sec->auth_dbmauthoritative = 1; /* fortress is secure by default */
+
+ return sec;
+}
+
+const char *set_dbm_slot (cmd_parms *cmd, void *offset, char *f, char *t)
+{
+ if (!t || strcmp(t, "dbm"))
+ return DECLINE_CMD;
+
+ return set_string_slot(cmd, offset, f);
+}
+
+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 },
+{ "AuthUserFile", set_dbm_slot,
+ (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmpwfile),
+ OR_AUTHCFG, TAKE12, NULL },
+{ "AuthGroupFile", set_dbm_slot,
+ (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmgrpfile),
+ OR_AUTHCFG, TAKE12, NULL },
+{ "AuthDBMAuthoritative", set_flag_slot,
+ (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmauthoritative),
+ OR_AUTHCFG, FLAG, "Set to 'no' to allow access control to be passed along to lower modules, if the UserID is not known in this module" },
+{ 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))) {
+ if (!(sec->auth_dbmauthoritative))
+ return DECLINED;
+ ap_snprintf(errstr, sizeof(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))) {
+ ap_snprintf(errstr, sizeof(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;
+ const char *t;
+ char *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) {
+ const char *orig_groups,*groups;
+ char *v;
+
+ if (!(groups = get_dbm_grp(r, user, sec->auth_dbmgrpfile))) {
+ if (!(sec->auth_dbmauthoritative))
+ return DECLINED;
+ ap_snprintf(errstr, sizeof(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;
+ }
+ }
+ ap_snprintf(errstr, sizeof(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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_auth_msql.c b/usr.sbin/httpd/src/mod_auth_msql.c
new file mode 100644
index 00000000000..dc8064276e8
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_auth_msql.c
@@ -0,0 +1,996 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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://www.apache.org/>.
+ *
+ */
+
+/*
+ * mod_auth_msql: authentication
+ *
+ * Rob McCool & Brian Behlendorf.
+ *
+ * Adapted to Apache 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.
+ *
+ * <directory /web/docs/private>
+ *
+ * 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_Authoritative 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
+ *
+ * <limit get post head>
+ * 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.
+ * </limit>
+ * </directory>
+ *
+ * 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 Authoritative 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 <on|off>
+ * 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_Authoritative <on|off>
+ * 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 <on|off>
+ * 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,
+ * <stout@lava.et.tudelft.nl> for spotting this one.
+ * 0.8 Authoritative directive added. See above.
+ * 0.9 palloc return code check(s), should be backward compatible with
+ * 1.11 version of Vivek Khera <khera@kciLink.com> 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_LEN claims.
+ * 1.0 removed some error check as they where already done elsehwere
+ * NumFields -> NumRows (Thanks Vitek). More stack memory.
+ * 1.1 no logging of empty password strings.
+ * 1.2 Problem with the Backward vitek which cause it to check
+ * even if msql_auth was not configured; Also more carefull
+ * with the authoritative stuff; caught by thomas@marvin.calvacom.fr.
+ * 1.3 Even more changes to get it right; that BACKWARD thing was a bad
+ * idea.
+ */
+
+
+#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 <khera@kciLink.com> 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 <msql.h>
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#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_authoritative;
+ 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_authoritative = 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;
+}
+
+const char *set_passwd_flag (cmd_parms *cmd, msql_auth_config_rec *sec, int arg) {
+ sec->auth_msql_nopasswd=arg;
+ return NULL;
+}
+
+const char *set_authoritative_flag (cmd_parms *cmd, msql_auth_config_rec *sec, int arg) {
+ sec->auth_msql_authoritative=arg;
+ return NULL;
+}
+
+const char *set_crypted_password_flag (cmd_parms *cmd, msql_auth_config_rec *sec , int arg) {
+ sec->auth_msql_encrypted = arg;
+ return NULL;
+}
+
+const 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_Authoritative", set_authoritative_flag, NULL, OR_AUTHCFG, FLAG,
+ "When 'on' the mSQL database is taken to be authoritative 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)) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN,
+ "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) {
+ ap_snprintf (msql_errstr, MAX_STRING_LEN,
+ "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 ) {
+ ap_snprintf (msql_errstr, MAX_STRING_LEN,
+ "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 ) {
+ ap_snprintf (msql_errstr, MAX_STRING_LEN,
+ "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())) {
+ ap_snprintf (msql_errstr, MAX_STRING_LEN,
+ "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.
+ */
+ ap_snprintf (msql_errstr, MAX_STRING_LEN,
+ "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))) {
+ ap_snprintf (msql_errstr, MAX_STRING_LEN,
+ "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)
+ ) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN,
+ "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))) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN,
+ "mSQL: Could not cope/escape the '%s' user_id value; ",user);
+ return NULL;
+ };
+ ap_snprintf(query, sizeof(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)
+ ) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN,
+ "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))) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN,
+ "mSQL: Could not cope/escape the '%s' user_id value",user);
+
+ return NULL;
+ };
+ if (!(msql_escape(esc_group, group,msql_errstr))) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN,
+ "mSQL: Could not cope/escape the '%s' group_id value",group);
+
+ return NULL;
+ };
+
+ ap_snprintf(query, sizeof(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];
+ /* msql_errstr must be MAX_STRING_LEN in size unless you
+ * change size in ap_snprintf() calls
+ */
+ 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.
+ * We no longer check on the user field name; to avoid problems
+ * with Backward VITEK.
+ */
+ if (!sec->auth_msql_pwd_table) 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_authoritative) {
+ /* insist that the user is in the database
+ */
+ ap_snprintf(msql_errstr, MAX_STRING_LEN,
+ "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 authoritative */
+ }; /* 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))) {
+/*
+ ap_snprintf(msql_errstr, MAX_STRING_LEN,
+ "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))) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN,
+ "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)) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN,
+ "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];
+ /* msql_errstr must be MAX_STRING_LEN in size unless you
+ * change size in ap_snprintf() calls
+ */
+ 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;
+ const char *t, *w;
+ msql_errstr[0]='\0';
+
+ /* If we are not configured, ignore */
+ if (!sec->auth_msql_pwd_table) return DECLINED;
+
+ if (!reqs_arr) {
+ if (sec->auth_msql_authoritative) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN, "user %s denied, no access rules specified (MSQL-Authoritative) ",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_authoritative) && ( user_result != OK)) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN, "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_authoritative) && (group_result != OK) ) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN, "user %s not in right groups (MSQL-Authoritative) ",user);
+ log_reason (msql_errstr, r->uri, r);
+ note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ };
+ };
+
+ if(!strcmp(w,"valid-user")) {
+ user_result= OK;
+ };
+ }
+
+ /* Get serious if we are authoritative, previous
+ * returns are only if msql yielded a correct result.
+ * This really is not needed.
+ */
+ if (((group_result == AUTH_REQUIRED) || (user_result == AUTH_REQUIRED)) && (sec->auth_msql_authoritative) ) {
+ ap_snprintf(msql_errstr, MAX_STRING_LEN, "mSQL-Authoritative: Access denied on %s %s rule(s) ",
+ (group_result == AUTH_REQUIRED) ? "USER" : "",
+ (user_result == AUTH_REQUIRED) ? "GROUP" : ""
+ );
+ log_reason (msql_errstr, r->uri, r);
+ return AUTH_REQUIRED;
+ };
+
+ if ( (user_result == OK) || (group_result == OK))
+ return OK;
+
+ return DECLINED;
+}
+
+
+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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_browser.c b/usr.sbin/httpd/src/mod_browser.c
new file mode 100644
index 00000000000..3779cdec087
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_browser.c
@@ -0,0 +1,189 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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://www.apache.org/>.
+ *
+ */
+
+/*
+ * mod_browser.c
+ * Set environment variables based on browser support.
+ *
+ * Alexei Kosut <akosut@organic.com>
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+
+typedef struct {
+ char *name;
+ regex_t *preg;
+ table *features;
+} browser_entry;
+
+typedef struct {
+ array_header *browsers;
+} browser_server_config_rec;
+
+module browser_module;
+
+void *create_browser_config (pool *p, server_rec *dummy)
+{
+ browser_server_config_rec *new =
+ (browser_server_config_rec *) palloc (p, sizeof(browser_server_config_rec));
+
+ new->browsers = make_array (p, 20, sizeof(browser_entry));
+ return (void *)new;
+}
+
+void *merge_browser_config (pool *p, void *basev, void *overridesv)
+{
+ browser_server_config_rec *a =
+ pcalloc(p, sizeof(browser_server_config_rec));
+ browser_server_config_rec *base = basev, *overrides = overridesv;
+
+ a->browsers = append_arrays(p, base->browsers, overrides->browsers);
+ return a;
+}
+
+const char *add_browser(cmd_parms *cmd, void *dummy, char *name,
+ const char *feature)
+{
+ browser_server_config_rec *sconf =
+ get_module_config (cmd->server->module_config, &browser_module);
+ browser_entry *new, *entries = (browser_entry *)sconf->browsers->elts;
+ char *var;
+ int i, cflags = (int)cmd->info;
+
+ /* First, try to merge into an existing entry */
+
+ for (i = 0; i < sconf->browsers->nelts; ++i) {
+ browser_entry *b = &entries[i];
+ if (!strcmp(b->name, name)) {
+ var = getword(cmd->pool, &feature, '=');
+ if (*feature) table_set(b->features, var, feature);
+ else if (*var == '!') table_set(b->features, var + 1, "!");
+ else table_set(b->features, var, "1");
+ return NULL;
+ }
+ }
+
+ /* If none was found, create a new entry */
+
+ new = push_array(sconf->browsers);
+ new->name = name;
+ new->preg = pregcomp (cmd->pool, name, REG_EXTENDED|REG_NOSUB|cflags);
+ if (new->preg == NULL) {
+ return "Browser regex could not be compiled.";
+ }
+ new->features = make_table(cmd->pool, 5);
+
+ var = getword(cmd->pool, &feature, '=');
+ if (*feature) table_set(new->features, var, feature);
+ else if (*var == '!') table_set(new->features, var + 1, "!");
+ else table_set(new->features, var, "1");
+
+ return NULL;
+}
+
+command_rec browser_module_cmds[] = {
+{ "BrowserMatch", add_browser, (void*)0,
+ RSRC_CONF, ITERATE2, "A browser regex and a list of variables." },
+{ "BrowserMatchNoCase", add_browser, (void*)REG_ICASE,
+ RSRC_CONF, ITERATE2, "a browser regex and a list of variables." },
+{ NULL },
+};
+
+static int browser_match(request_rec *r)
+{
+ server_rec *s = r->server;
+ browser_server_config_rec *sconf = get_module_config (s->module_config,
+ &browser_module);
+ browser_entry *entries = (browser_entry *)sconf->browsers->elts;
+ table_entry *elts;
+ char *ua = table_get(r->headers_in, "User-Agent");
+ int i, j;
+
+ if (!ua) return DECLINED;
+
+ for (i = 0; i < sconf->browsers->nelts; ++i) {
+ browser_entry *b = &entries[i];
+
+ if (!regexec(b->preg, ua, 0, NULL, 0)) {
+ elts = (table_entry *)b->features->elts;
+
+ for (j = 0; j < b->features->nelts; ++j) {
+ if (!strcmp(elts[j].val, "!"))
+ table_unset(r->subprocess_env, elts[j].key);
+ else
+ table_set(r->subprocess_env, elts[j].key, elts[j].val);
+ }
+ }
+ }
+
+ return DECLINED;
+}
+
+module browser_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ create_browser_config, /* server config */
+ merge_browser_config, /* merge server configs */
+ browser_module_cmds, /* command table */
+ NULL, /* handlers */
+ browser_match, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL, /* logger */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_cern_meta.c b/usr.sbin/httpd/src/mod_cern_meta.c
new file mode 100644
index 00000000000..d05677c8c25
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_cern_meta.c
@@ -0,0 +1,325 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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://www.apache.org/>.
+ *
+ */
+
+/*
+ * mod_cern_meta.c
+ * version 0.0.5
+ * status beta
+ *
+ * Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 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 <directory name>
+ *
+ * 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 <meta file suffix>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include "util_script.h"
+#include "http_log.h"
+#include "http_request.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;
+}
+
+const 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;
+}
+
+const 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;
+ FILE *f;
+ cern_meta_config *cmc ;
+ int rv;
+ request_rec *rr;
+
+ 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);
+
+ /* XXX: it sucks to require this subrequest to complete, because this
+ * means people must leave their meta files accessible to the world.
+ * A better solution might be a "safe open" feature of pfopen to avoid
+ * pipes, symlinks, and crap like that.
+ */
+ rr = sub_req_lookup_file (metafilename, r);
+ if (rr->status != HTTP_OK) {
+ destroy_sub_req (rr);
+ return DECLINED;
+ }
+ destroy_sub_req (rr);
+
+ f = pfopen (r->pool, metafilename, "r");
+ if (f == NULL) {
+ if (errno == ENOENT) {
+ return DECLINED;
+ }
+ 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_cgi.c b/usr.sbin/httpd/src/mod_cgi.c
new file mode 100644
index 00000000000..6931d128878
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_cgi.c
@@ -0,0 +1,575 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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_script: keeps all script-related ramblings together.
+ *
+ * Compliant to CGI/1.1 spec
+ *
+ * Adapted by rst from original NCSA code by Rob McCool
+ *
+ * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for
+ * custom error responses, and DOCUMENT_ROOT because we found it useful.
+ * It also adds SERVER_ADMIN - useful for scripts to know who to mail when
+ * they fail.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "util_script.h"
+#include "http_conf_globals.h"
+
+module cgi_module;
+
+/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
+ * in ScriptAliased directories, which means we need to know if this
+ * request came through ScriptAlias or not... so the Alias module
+ * leaves a note for us.
+ */
+
+int is_scriptaliased (request_rec *r)
+{
+ char *t = table_get (r->notes, "alias-forced-type");
+ return t && (!strcasecmp (t, "cgi-script"));
+}
+
+/* Configuration stuff */
+
+#define DEFAULT_LOGBYTES 10385760
+#define DEFAULT_BUFBYTES 1024
+
+typedef struct {
+ char *logname;
+ long logbytes;
+ int bufbytes;
+} cgi_server_conf;
+
+void *create_cgi_config (pool *p, server_rec *s)
+{
+ cgi_server_conf *c =
+ (cgi_server_conf *)pcalloc (p, sizeof(cgi_server_conf));
+
+ c->logname = NULL;
+ c->logbytes = DEFAULT_LOGBYTES;
+ c->bufbytes = DEFAULT_BUFBYTES;
+
+ return c;
+}
+
+void *merge_cgi_config (pool *p, void *basev, void *overridesv)
+{
+ cgi_server_conf *base = (cgi_server_conf *)basev,
+ *overrides = (cgi_server_conf *)overridesv;
+
+ return overrides->logname ? overrides : base;
+}
+
+const char *set_scriptlog (cmd_parms *cmd, void *dummy, char *arg) {
+ server_rec *s = cmd->server;
+ cgi_server_conf *conf =
+ (cgi_server_conf *)get_module_config(s->module_config, &cgi_module);
+
+ conf->logname = arg;
+ return NULL;
+}
+
+const char *set_scriptlog_length (cmd_parms *cmd, void *dummy, char *arg) {
+ server_rec *s = cmd->server;
+ cgi_server_conf *conf =
+ (cgi_server_conf *)get_module_config(s->module_config, &cgi_module);
+
+ conf->logbytes = atol (arg);
+ return NULL;
+}
+
+const char *set_scriptlog_buffer (cmd_parms *cmd, void *dummy, char *arg) {
+ server_rec *s = cmd->server;
+ cgi_server_conf *conf =
+ (cgi_server_conf *)get_module_config(s->module_config, &cgi_module);
+
+ conf->bufbytes = atoi (arg);
+ return NULL;
+}
+
+command_rec cgi_cmds[] = {
+{ "ScriptLog", set_scriptlog, NULL, RSRC_CONF, TAKE1,
+ "the name of a log for script debugging info"},
+{ "ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, TAKE1,
+ "the maximum length (in bytes) of the script debug log"},
+{ "ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, TAKE1,
+ "the maximum size (in bytes) to record of a POST request"},
+{ NULL}
+};
+
+static int log_scripterror(request_rec *r, cgi_server_conf *conf, int ret,
+ char *error)
+{
+ FILE *f;
+
+ log_reason(error, r->filename, r);
+
+ if (!conf->logname ||
+ ((stat(server_root_relative(r->pool, conf->logname), &r->finfo) == 0)
+ && (r->finfo.st_size > conf->logbytes)) ||
+ ((f = pfopen(r->pool, server_root_relative(r->pool, conf->logname),
+ "a")) == NULL)) {
+ return ret;
+ }
+
+ /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
+ fprintf(f, "%%%% [%s] %s %s%s%s %s\n", get_time(), r->method, r->uri,
+ r->args ? "?" : "", r->args ? r->args : "", r->protocol);
+ /* "%% 500 /usr/local/etc/httpd/cgi-bin */
+ fprintf(f, "%%%% %d %s\n", ret, r->filename);
+
+ fprintf(f, "%%error\n%s\n", error);
+
+ pfclose(r->pool, f);
+ return ret;
+}
+
+static int log_script(request_rec *r, cgi_server_conf *conf, int ret,
+ char *dbuf, char *sbuf, FILE *script_in, FILE *script_err)
+{
+ table *hdrs_arr = r->headers_in;
+ table_entry *hdrs = (table_entry *)hdrs_arr->elts;
+ char argsbuffer[HUGE_STRING_LEN];
+ FILE *f;
+ int i;
+
+ if (!conf->logname ||
+ ((stat(server_root_relative(r->pool, conf->logname), &r->finfo) == 0)
+ && (r->finfo.st_size > conf->logbytes)) ||
+ ((f = pfopen(r->pool, server_root_relative(r->pool, conf->logname),
+ "a")) == NULL)) {
+ /* Soak up script output */
+ while (fgets(argsbuffer, MAX_STRING_LEN-1, script_in) != NULL)
+ continue;
+ while (fgets(argsbuffer, MAX_STRING_LEN-1, script_err) != NULL)
+ continue;
+ return ret;
+ }
+
+ /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
+ fprintf(f, "%%%% [%s] %s %s%s%s %s\n", get_time(), r->method, r->uri,
+ r->args ? "?" : "", r->args ? r->args : "", r->protocol);
+ /* "%% 500 /usr/local/etc/httpd/cgi-bin */
+ fprintf(f, "%%%% %d %s\n", ret, r->filename);
+
+ fputs("%request\n", f);
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (!hdrs[i].key) continue;
+ fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
+ }
+ if ((r->method_number == M_POST || r->method_number == M_PUT)
+ && dbuf && *dbuf) {
+ fprintf(f, "\n%s\n", dbuf);
+ }
+
+ fputs("%response\n", f);
+ hdrs_arr = r->err_headers_out;
+ hdrs = (table_entry *)hdrs_arr->elts;
+
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (!hdrs[i].key) continue;
+ fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
+ }
+
+ if (sbuf && *sbuf)
+ fprintf(f, "%s\n", sbuf);
+
+ *argsbuffer = '\0';
+ fgets(argsbuffer, HUGE_STRING_LEN-1, script_in);
+ if (*argsbuffer) {
+ fputs("%stdout\n", f);
+ fputs(argsbuffer, f);
+ while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL)
+ fputs(argsbuffer, f);
+ fputs("\n", f);
+ }
+
+ *argsbuffer = '\0';
+ fgets(argsbuffer, HUGE_STRING_LEN-1, script_err);
+ if (*argsbuffer) {
+ fputs("%stderr\n", f);
+ fputs(argsbuffer, f);
+ while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_err) != NULL)
+ fputs(argsbuffer, f);
+ fputs("\n", f);
+ }
+
+ pfclose(r->main ? r->main->pool : r->pool, script_in);
+ pfclose(r->main ? r->main->pool : r->pool, script_err);
+
+ pfclose(r->pool, f);
+ return ret;
+}
+
+/****************************************************************
+ *
+ * Actual CGI handling...
+ */
+
+
+struct cgi_child_stuff {
+ request_rec *r;
+ int nph;
+ int debug;
+ 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);
+ if (!cld->debug)
+ 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();
+
+ call_exec(r, argv0, env, 0);
+
+ /* 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).
+ */
+
+ ap_snprintf(err_string, sizeof(err_string),
+ "exec of %s failed, reason: %s (errno = %d)\n",
+ r->filename, strerror(errno), errno);
+ write(2, err_string, strlen(err_string));
+ exit(0);
+}
+
+int cgi_handler (request_rec *r)
+{
+ int retval, nph, dbpos = 0;
+ char *argv0, *dbuf = NULL;
+ FILE *script_out, *script_in, *script_err;
+ char argsbuffer[HUGE_STRING_LEN];
+ int is_included = !strcmp (r->protocol, "INCLUDED");
+ void *sconf = r->server->module_config;
+ cgi_server_conf *conf =
+ (cgi_server_conf *)get_module_config(sconf, &cgi_module);
+
+ struct cgi_child_stuff cld;
+ pid_t child_pid;
+
+ if (r->method_number == M_OPTIONS) {
+ /* 99 out of 100 CGI scripts, this is all they support */
+ r->allowed |= (1 << M_GET);
+ r->allowed |= (1 << M_POST);
+ return DECLINED;
+ }
+
+ if((argv0 = strrchr(r->filename,'/')) != NULL)
+ argv0++;
+ else argv0 = r->filename;
+
+ nph = !(strncmp(argv0,"nph-",4));
+
+ if (!(allow_options (r) & OPT_EXECCGI) && !is_scriptaliased (r))
+ return log_scripterror(r, conf, FORBIDDEN,
+ "Options ExecCGI is off in this directory");
+ if (nph && is_included)
+ return log_scripterror(r, conf, FORBIDDEN,
+ "attempt to include NPH CGI script");
+
+ if (S_ISDIR(r->finfo.st_mode))
+ return log_scripterror(r, conf, FORBIDDEN,
+ "attempt to invoke directory as script");
+#ifdef __EMX__
+ /* Allow for cgi files without the .EXE extension on them under OS/2 */
+ if (r->finfo.st_mode == 0) {
+ struct stat statbuf;
+
+ r->filename = pstrcat (r->pool, r->filename, ".EXE", NULL);
+
+ if ((stat(r->filename, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode))) {
+ return log_scripterror(r, conf, NOT_FOUND,
+ "script not found or unable to stat");
+ }
+ }
+#else
+ if (r->finfo.st_mode == 0)
+ return log_scripterror(r, conf, NOT_FOUND,
+ "script not found or unable to stat");
+#endif
+ if (!suexec_enabled) {
+ if (!can_exec(&r->finfo))
+ return log_scripterror(r, conf, FORBIDDEN,
+ "file permissions deny server execution");
+ }
+
+ if ((retval = setup_client_block(r, REQUEST_CHUNKED_ERROR)))
+ return retval;
+
+ add_common_vars (r);
+ cld.argv0 = argv0; cld.r = r; cld.nph = nph;
+ cld.debug = conf->logname ? 1 : 0;
+
+ if (!(child_pid =
+ /*
+ * we spawn out of r->main if it's there so that we can avoid
+ * waiting for free_proc_chain to cleanup in the middle of an
+ * SSI request -djg
+ */
+ spawn_child_err (r->main ? r->main->pool : r->pool, cgi_child,
+ (void *)&cld,
+ nph ? just_wait : kill_after_timeout,
+#ifdef __EMX__
+ &script_out, &script_in, &script_err))) {
+#else
+ &script_out, nph ? NULL : &script_in,
+ &script_err))) {
+#endif
+ log_reason ("couldn't spawn child process", r->filename, r);
+ return SERVER_ERROR;
+ }
+
+ /* 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).
+ */
+
+ if (should_client_block(r)) {
+ void (*handler)();
+ int dbsize, len_read;
+
+ if (conf->logname) {
+ dbuf = pcalloc(r->pool, conf->bufbytes+1);
+ dbpos = 0;
+ }
+
+ hard_timeout ("copy script args", r);
+ handler = signal (SIGPIPE, SIG_IGN);
+
+ while ((len_read =
+ get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0)
+ {
+ if (conf->logname) {
+ if ((dbpos + len_read) > conf->bufbytes) {
+ dbsize = conf->bufbytes - dbpos;
+ }
+ else {
+ dbsize = len_read;
+ }
+ memcpy(dbuf + dbpos, argsbuffer, dbsize);
+ dbpos += dbsize;
+ }
+ reset_timeout(r);
+ if (fwrite(argsbuffer, sizeof(char), len_read, script_out)
+ < (size_t)len_read) {
+ /* silly script stopped reading, soak up remaining message */
+ while (get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0)
+ ; /* dump it */
+ break;
+ }
+ }
+
+ fflush (script_out);
+ signal (SIGPIPE, handler);
+
+ kill_timeout (r);
+ }
+
+ pfclose (r->main ? r->main->pool : r->pool, script_out);
+
+ /* Handle script return... */
+ if (script_in && !nph) {
+ char *location, sbuf[MAX_STRING_LEN];
+ int ret;
+
+ if ((ret = scan_script_header_err(r, script_in, sbuf)))
+ return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err);
+
+ 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 (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_in)
+ > 0)
+ continue;
+ while (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_err)
+ > 0)
+ 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;
+
+ /* We already read the message body (if any), so don't allow
+ * the redirected request to think it has one. We can ignore
+ * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
+ */
+ table_unset(r->headers_in, "Content-Length");
+
+ 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;
+ }
+
+ send_http_header(r);
+ if (!r->header_only)
+ send_fd(script_in, r);
+ pfclose (r->main ? r->main->pool : r->pool, script_in);
+
+ /* Soak up stderr */
+ soft_timeout("soaking script stderr", r);
+ while (!r->connection->aborted &&
+ (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_err) > 0))
+ continue;
+ kill_timeout(r);
+ pfclose (r->main ? r->main->pool : r->pool, script_err);
+ }
+
+ if (nph) {
+#ifdef __EMX__
+ while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL) {
+ bputs(argsbuffer, r->connection->client);
+ }
+#else
+ waitpid(child_pid, (int*)0, 0);
+#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 */
+ create_cgi_config, /* server config */
+ merge_cgi_config, /* merge server config */
+ cgi_cmds, /* command table */
+ cgi_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL, /* logger */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_digest.c b/usr.sbin/httpd/src/mod_digest.c
new file mode 100644
index 00000000000..814f416d3e2
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_digest.c
@@ -0,0 +1,363 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * mod_digest: MD5 digest authentication
+ *
+ * by Alexei Kosut <akosut@nueva.pvt.k12.ca.us>
+ * 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));
+}
+
+const char *set_digest_slot (cmd_parms *cmd, void *offset, char *f, char *t)
+{
+ if (t && strcmp(t, "standard"))
+ return pstrcat(cmd->pool, "Invalid auth file type: ", t, NULL);
+
+ return set_string_slot(cmd, offset, f);
+}
+
+command_rec digest_cmds[] = {
+{ "AuthDigestFile", set_digest_slot,
+ (void*)XtOffsetOf(digest_config_rec,pwfile), OR_AUTHCFG, TAKE12, NULL },
+{ NULL }
+};
+
+module digest_module;
+
+char *get_hash(request_rec *r, char *user, char *auth_pwfile)
+{
+ FILE *f;
+ char l[MAX_STRING_LEN];
+ const char *rpw;
+ char *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) {
+ const 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 (strcasecmp(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))) {
+ ap_snprintf(errstr, sizeof(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))) {
+ ap_snprintf(errstr, sizeof(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;
+ const char *t;
+ char *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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_dir.c b/usr.sbin/httpd/src/mod_dir.c
new file mode 100644
index 00000000000..435b5d3378f
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_dir.c
@@ -0,0 +1,911 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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_dir.c: Handles the on-the-fly html index generation
+ *
+ * Rob McCool
+ * 3/23/93
+ *
+ * Adapted to Apache by rst.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+
+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;
+}
+
+const 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;
+}
+
+const char *add_icon(cmd_parms *cmd, void *d, char *icon, char *to)
+{
+ char *iconbak = pstrdup (cmd->pool, icon);
+
+ if(icon[0] == '(') {
+ char *alt = getword_nc (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;
+}
+
+const 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;
+}
+
+const 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;
+}
+
+const char *add_header(cmd_parms *cmd, void *d, char *name) {
+ if (strchr (name, '/')) {
+ return "HeaderName cannot contain a /";
+ }
+ push_item(((dir_config_rec *)d)->hdr_list, 0, NULL, cmd->path, name);
+ return NULL;
+}
+
+const char *add_readme(cmd_parms *cmd, void *d, char *name) {
+ if (strchr (name, '/')) {
+ return "ReadmeName cannot contain a /";
+ }
+ push_item(((dir_config_rec *)d)->rdme_list, 0, NULL, cmd->path, name);
+ return NULL;
+}
+
+
+const 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;
+}
+
+const char *fancy_indexing (cmd_parms *cmd, void *d, int arg)
+{
+ return add_opts_int (cmd, d, arg? FANCY_INDEXING : 0);
+}
+
+const char *add_opts(cmd_parms *cmd, void *d, const 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;
+ request_rec *rr;
+
+ /* XXX: this is a load of crap, it needs to do a full sub_req_lookup_uri */
+ 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("<HR>\n", r);
+ rputs("<PRE>\n", r);
+ }
+ else if (rule) rputs("<HR>\n", r);
+ /* XXX: when the above is rewritten properly, this necessary security
+ * check will be redundant. -djg */
+ rr = sub_req_lookup_file (fn, r);
+ if (rr->status != HTTP_OK) {
+ destroy_sub_req (rr);
+ return 0;
+ }
+ destroy_sub_req (rr);
+ 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("&lt;", r);
+ else if (ch == '>') rputs("&gt;", r);
+ else if (ch == '&') rputs("&amp;", r);
+ c = i + 1;
+ }
+ }
+ }
+ pfclose(r->pool, f);
+ if(plaintext)
+ rputs("</PRE>\n", r);
+ return 1;
+}
+
+
+char *find_title(request_rec *r) {
+ char titlebuf[MAX_STRING_LEN], *find = "<TITLE>";
+ FILE *thefile = NULL;
+ int x,y,n,p;
+
+ if (r->status != HTTP_OK) {
+ return NULL;
+ }
+ if (r->content_type && !strcasecmp(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 = NULL;
+ char *tmp;
+ int dir_opts = find_opts(dir_conf, r);
+
+ if(!(d=opendir(name))) {
+ log_reason ("Can't open directory for index", r->filename, r);
+ return HTTP_FORBIDDEN;
+ }
+
+ r->content_type = "text/html";
+
+ send_http_header(r);
+
+ if (r->header_only) {
+ closedir (d);
+ return 0;
+ }
+ hard_timeout("send directory", r);
+
+ /* Spew HTML preamble */
+
+ title_endp = title_name + strlen(title_name) - 1;
+
+ while (title_endp > title_name && *title_endp == '/')
+ *title_endp-- = '\0';
+
+ rvputs
+ (
+ r,
+ "<HTML><HEAD>\n<TITLE>Index of ",
+ title_name,
+ "</TITLE>\n</HEAD><BODY>\n",
+ NULL
+ );
+
+ if((!(tmp = find_header(dir_conf,r))) || (!(insert_readme(name,tmp,0,r))))
+ rvputs(r, "<H1>Index of ", title_name, "</H1>\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++;
+ }
+ }
+ if (num_ent > 0) {
+ 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("</UL>", r);
+ }
+
+ rputs ("</BODY></HTML>\n", r);
+
+ kill_timeout(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);
+ const char *names_ptr = d->index_names ? d->index_names : DEFAULT_INDEX;
+ int allow_opts = allow_options (r);
+ int error_notfound = 0;
+
+ 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 HTTP_MOVED_PERMANENTLY;
+ }
+
+ /* KLUDGE --- make the sub_req lookups happen in the right directory.
+ * Fixing this in the sub_req_lookup functions themselves is difficult,
+ * and would probably break virtual includes...
+ */
+
+ 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 == HTTP_OK && 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;
+ }
+
+ /* If the request returned a redirect, propagate it to the client */
+
+ if (is_HTTP_REDIRECT(rr->status) ||
+ (rr->status == HTTP_NOT_ACCEPTABLE && *names_ptr == '\0')) {
+
+ error_notfound = rr->status;
+ r->notes = overlay_tables(r->pool, r->notes, rr->notes);
+ r->headers_out = overlay_tables(r->pool, r->headers_out,
+ rr->headers_out);
+ r->err_headers_out = overlay_tables(r->pool, r->err_headers_out,
+ rr->err_headers_out);
+ destroy_sub_req(rr);
+ return error_notfound;
+ }
+
+ /* If the request returned something other than 404 (or 200),
+ * it means the module encountered some sort of problem. To be
+ * secure, we should return the error, rather than create
+ * along a (possibly unsafe) directory index.
+ *
+ * So we store the error, and if none of the listed files
+ * exist, we return the last error response we got, instead
+ * of a directory listing.
+ */
+ if (rr->status && rr->status != HTTP_NOT_FOUND && rr->status != HTTP_OK)
+ error_notfound = rr->status;
+
+ destroy_sub_req (rr);
+ }
+
+ if (error_notfound)
+ return error_notfound;
+
+ r->allowed |= (1 << M_GET);
+ 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 {
+ log_reason ("Directory index forbidden by rule", r->filename, r);
+ return HTTP_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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_dld.c b/usr.sbin/httpd/src/mod_dld.c
new file mode 100644
index 00000000000..ac6ff33a7ad
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_dld.c
@@ -0,0 +1,190 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * 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 <dld.h>
+
+/*
+ * 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_env.c b/usr.sbin/httpd/src/mod_env.c
new file mode 100644
index 00000000000..8ae697038da
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_env.c
@@ -0,0 +1,261 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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://www.apache.org/>.
+ *
+ */
+
+/*
+ * mod_env.c
+ * version 0.0.5
+ * status beta
+ * Pass environment variables to CGI/SSI scripts.
+ *
+ * Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 06.Dec.95
+ *
+ * Change log:
+ * 08.Dec.95 Now allows PassEnv directive to appear more than once in
+ * conf files.
+ * 10.Dec.95 optimisation. getenv() only called at startup and used
+ * to build a fast-to-access table. table used to build
+ * per-server environment for each request.
+ * robustness. better able to handle errors in configuration
+ * files:
+ * 1) PassEnv directive present, but no environment variable listed
+ * 2) PassEnv FOO present, but $FOO not present in environment
+ * 3) no PassEnv directive present
+ * 23.Dec.95 Now allows SetEnv directive with same semantics as 'sh' setenv:
+ * SetEnv Var sets Var to the empty string
+ * SetEnv Var Val sets Var to the value Val
+ * Values containing whitespace should be quoted, eg:
+ * SetEnv Var "this is some text"
+ * Environment variables take their value from the last instance
+ * of PassEnv / SetEnv to be reached in the configuration file.
+ * For example, the sequence:
+ * PassEnv FOO
+ * SetEnv FOO override
+ * Causes FOO to take the value 'override'.
+ * 23.Feb.96 Added UnsetEnv directive to allow environment variables
+ * to be removed.
+ * Virtual hosts now 'inherit' parent server environment which
+ * they're able to overwrite with their own directives or
+ * selectively ignore with UnsetEnv.
+ * *** IMPORTANT - the way that virtual hosts inherit their ***
+ * *** environment variables from the default server's ***
+ * *** configuration has changed. You should test your ***
+ * *** configuration carefully before accepting this ***
+ * *** version of the module in a live webserver which used ***
+ * *** older versions of the module. ***
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+
+typedef struct {
+ table *vars;
+ char *unsetenv;
+ int vars_present;
+} env_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;
+ const char *uenv, *unset;
+
+ /*
+ * new_table = copy_table( p, base->vars );
+ * foreach $element ( @add->vars ) {
+ * table_set( new_table, $element.key, $element.val );
+ * };
+ * foreach $unsetenv ( @UNSETENV ) {
+ * table_unset( new_table, $unsetenv );
+ * }
+ */
+
+ new_table = 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 );
+ }
+
+ unset = add->unsetenv;
+ uenv = getword_conf( p, &unset );
+ while ( uenv[0] != '\0' ) {
+ table_unset( new_table, uenv );
+ uenv = getword_conf( p, &unset );
+ }
+
+ new->vars = new_table;
+
+ new->vars_present = base->vars_present || add->vars_present;
+
+ return new;
+}
+
+const char *add_env_module_vars_passed (cmd_parms *cmd, char *struct_ptr,
+ const 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;
+}
+
+const char *add_env_module_vars_set (cmd_parms *cmd, char *struct_ptr,
+ const 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;
+}
+
+const 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_expires.c b/usr.sbin/httpd/src/mod_expires.c
new file mode 100644
index 00000000000..226726a0a93
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_expires.c
@@ -0,0 +1,477 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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://www.apache.org/>.
+ *
+ */
+
+/*
+ * mod_expires.c
+ * version 0.0.11
+ * status beta
+ *
+ * Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 26.Jan.96
+ *
+ * This module allows you to control the form of the Expires: header
+ * that Apache issues for each access. Directives can appear in
+ * configuration files or in .htaccess files so expiry semantics can
+ * be defined on a per-directory basis.
+ *
+ * DIRECTIVE SYNTAX
+ *
+ * Valid directives are:
+ *
+ * ExpiresActive on | off
+ * ExpiresDefault <code><seconds>
+ * ExpiresByType type/encoding <code><seconds>
+ *
+ * Valid values for <code> are:
+ *
+ * 'M' expires header shows file modification date + <seconds>
+ * 'A' expires header shows access time + <seconds>
+ *
+ * [I'm not sure which of these is best under different
+ * circumstances, I guess it's for other people to explore.
+ * The effects may be indistinguishable for a number of cases]
+ *
+ * <seconds> should be an integer value [acceptable to atoi()]
+ *
+ * There is NO space between the <code> and <seconds>.
+ *
+ * For example, a directory which contains information which changes
+ * frequently might contain:
+ *
+ * # reports generated by cron every hour. don't let caches
+ * # hold onto stale information
+ * ExpiresDefault M3600
+ *
+ * Another example, our html pages can change all the time, the gifs
+ * tend not to change often:
+ *
+ * # pages are hot (1 week), images are cold (1 month)
+ * ExpiresByType text/html A604800
+ * ExpiresByType image/gif A2592000
+ *
+ * Expires can be turned on for all URLs on the server by placing the
+ * following directive in a conf file:
+ *
+ * ExpiresActive on
+ *
+ * ExpiresActive can also appear in .htaccess files, enabling the
+ * behaviour to be turned on or off for each chosen directory.
+ *
+ * # turn off Expires behaviour in this directory
+ * # and subdirectories
+ * ExpiresActive off
+ *
+ * Directives defined for a directory are valid in subdirectories
+ * unless explicitly overridden by new directives in the subdirectory
+ * .htaccess files.
+ *
+ * ALTERNATIVE DIRECTIVE SYNTAX
+ *
+ * Directives can also be defined in a more readable syntax of the form:
+ *
+ * ExpiresDefault "<base> [plus] {<num> <type>}*"
+ * ExpiresByType type/encoding "<base> [plus] {<num> <type>}*"
+ *
+ * where <base> is one of:
+ * access
+ * now equivalent to 'access'
+ * modification
+ *
+ * where the 'plus' keyword is optional
+ *
+ * where <num> should be an integer value [acceptable to atoi()]
+ *
+ * where <type> is one of:
+ * years
+ * months
+ * weeks
+ * days
+ * hours
+ * minutes
+ * seconds
+ *
+ * For example, any of the following directives can be used to make
+ * documents expire 1 month after being accessed, by default:
+ *
+ * ExpiresDefault "access plus 1 month"
+ * ExpiresDefault "access plus 4 weeks"
+ * ExpiresDefault "access plus 30 days"
+ *
+ * The expiry time can be fine-tuned by adding several '<num> <type>'
+ * clauses:
+ *
+ * ExpiresByType text/html "access plus 1 month 15 days 2 hours"
+ * ExpiresByType image/gif "modification plus 5 hours 3 minutes"
+ *
+ * ---
+ *
+ * Change-log:
+ * 29.Jan.96 Hardened the add_* functions. Server will now bail out
+ * if bad directives are given in the conf files.
+ * 02.Feb.96 Returns DECLINED if not 'ExpiresActive on', giving other
+ * expires-aware modules a chance to play with the same
+ * directives. [Michael Rutman]
+ * 03.Feb.96 Call tzset() before localtime(). Trying to get the module
+ * to work properly in non GMT timezones.
+ * 12.Feb.96 Modified directive syntax to allow more readable commands:
+ * ExpiresDefault "now plus 10 days 20 seconds"
+ * ExpiresDefault "access plus 30 days"
+ * ExpiresDefault "modification plus 1 year 10 months 30 days"
+ * 13.Feb.96 Fix call to table_get() with NULL 2nd parameter [Rob Hartill]
+ * 19.Feb.96 Call gm_timestr_822() to get time formatted correctly, can't
+ * rely on presence of HTTP_TIME_FORMAT in Apache 1.1+.
+ * 21.Feb.96 This version (0.0.9) reverses assumptions made in 0.0.8
+ * about star/star handlers. Reverting to 0.0.7 behaviour.
+ * 08.Jun.96 allows ExpiresDefault to be used with responses that use
+ * the DefaultType by not DECLINING, but instead skipping
+ * the table_get check and then looking for an ExpiresDefault.
+ * [Rob Hartill]
+ * 04.Nov.96 'const' definitions added.
+ *
+ * TODO
+ * add support for Cache-Control: max-age=20 from the HTTP/1.1
+ * proposal (in this case, a ttl of 20 seconds) [ask roy]
+ * add per-file expiry and explicit expiry times - duplicates some
+ * of the mod_cern_meta.c functionality. eg:
+ * ExpiresExplicit index.html "modification plus 30 days"
+ *
+ * BUGS
+ * Hi, welcome to the internet.
+ */
+
+#include <ctype.h>
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+
+typedef struct {
+ int active;
+ char *expiresdefault;
+ table *expiresbytype;
+} expires_dir_config;
+
+/* from mod_dir, why is this alias used?
+ */
+#define DIR_CMD_PERMS OR_INDEXES
+
+#define ACTIVE_ON 1
+#define ACTIVE_OFF 0
+#define ACTIVE_DONTCARE 2
+
+module expires_module;
+
+void *create_dir_expires_config (pool *p, char *dummy)
+{
+ expires_dir_config *new =
+ (expires_dir_config *) pcalloc (p, sizeof(expires_dir_config));
+ new->active = ACTIVE_DONTCARE;
+ new->expiresdefault = "";
+ new->expiresbytype = make_table(p, 4);
+ return (void *)new;
+}
+
+const char *set_expiresactive (cmd_parms *cmd, expires_dir_config *dir_config, int arg)
+{
+ /* if we're here at all it's because someone explicitly
+ * set the active flag
+ */
+ dir_config->active = ACTIVE_ON;
+ if ( arg == 0 ) {
+ dir_config->active = ACTIVE_OFF;
+ };
+ return NULL;
+}
+
+/* check_code() parse 'code' and return NULL or an error response
+ * string. If we return NULL then real_code contains code converted
+ * to the cnnnn format.
+ */
+char *check_code( pool *pool, const char *code, char **real_code )
+{
+ char *word;
+ char base = 'X';
+ int modifier = 0;
+ int num = 0;
+ int factor = 0;
+ char foo[MAX_STRING_LEN];
+
+ /* 0.0.4 compatibility?
+ */
+ if ( (code[0] == 'A') || (code[0] == 'M') ) {
+ *real_code = pstrdup( pool, code );
+ return NULL;
+ };
+
+ /* <base> [plus] {<num> <type>}*
+ */
+
+ /* <base>
+ */
+ word = getword_conf( pool, &code );
+ if ( !strncasecmp( word, "now", 1 ) ||
+ !strncasecmp( word, "access", 1 ) ) {
+ base = 'A';
+ } else if ( !strncasecmp( word, "modification", 1 ) ) {
+ base = 'M';
+ } else {
+ return pstrcat( pool, "bad expires code, unrecognised <base> '",
+ word, "'", NULL);
+ };
+
+ /* [plus]
+ */
+ word = getword_conf( pool, &code );
+ if ( !strncasecmp( word, "plus", 1 ) ) {
+ word = getword_conf( pool, &code );
+ };
+
+ /* {<num> <type>}*
+ */
+ while ( word[0] ) {
+ /* <num>
+ */
+ if (isdigit(word[0])) {
+ num = atoi( word );
+ } else {
+ return pstrcat( pool, "bad expires code, numeric value expected <num> '",
+ word, "'", NULL);
+ };
+
+ /* <type>
+ */
+ word = getword_conf( pool, &code );
+ if ( word[0] ) {
+ /* do nothing */
+ } else {
+ return pstrcat( pool, "bad expires code, missing <type>", NULL);
+ };
+
+ factor = 0;
+ if ( !strncasecmp( word, "years", 1 ) ) {
+ factor = 60*60*24*365;
+ } else if ( !strncasecmp( word, "months", 2 ) ) {
+ factor = 60*60*24*30;
+ } else if ( !strncasecmp( word, "weeks", 1 ) ) {
+ factor = 60*60*24*7;
+ } else if ( !strncasecmp( word, "days", 1 ) ) {
+ factor = 60*60*24;
+ } else if ( !strncasecmp( word, "hours", 1 ) ) {
+ factor = 60*60;
+ } else if ( !strncasecmp( word, "minutes", 2 ) ) {
+ factor = 60;
+ } else if ( !strncasecmp( word, "seconds", 1 ) ) {
+ factor = 1;
+ } else {
+ return pstrcat( pool, "bad expires code, unrecognised <type>",
+ "'", word, "'", NULL);
+ };
+
+ modifier = modifier + factor * num;
+
+ /* next <num>
+ */
+ word = getword_conf( pool, &code );
+ };
+
+ ap_snprintf(foo, sizeof(foo), "%c%d", base, modifier );
+ *real_code = pstrdup( pool, foo );
+
+ return NULL;
+}
+
+const char *set_expiresbytype(cmd_parms *cmd, expires_dir_config *dir_config, char *mime, char *code)
+{
+ char *response, *real_code;
+
+ if ( (response = check_code( cmd->pool, code, &real_code )) == NULL ) {
+ table_set (dir_config->expiresbytype, mime, real_code);
+ return NULL;
+ };
+ return pstrcat( cmd->pool,
+ "'ExpiresByType ", mime, " ", code, "': ", response, NULL );
+}
+
+const char *set_expiresdefault (cmd_parms *cmd, expires_dir_config *dir_config, char *code)
+{
+ char *response, *real_code;
+
+ if ( (response = check_code( cmd->pool, code, &real_code )) == NULL ) {
+ dir_config->expiresdefault = pstrdup( cmd->pool, real_code );
+ return NULL;
+ };
+ return pstrcat( cmd->pool,
+ "'ExpiresDefault ", code, "': ", response, NULL );
+}
+
+command_rec expires_cmds[] = {
+{ "ExpiresActive", set_expiresactive, NULL, DIR_CMD_PERMS, FLAG, NULL},
+{ "ExpiresBytype", set_expiresbytype, NULL, DIR_CMD_PERMS, TAKE2,
+ "a mime type followed by an expiry date code"},
+{ "ExpiresDefault", set_expiresdefault, NULL, DIR_CMD_PERMS, TAKE1,
+ "an expiry date code"},
+{ NULL }
+};
+
+void *merge_expires_dir_configs (pool *p, void *basev, void *addv)
+{
+ expires_dir_config *new= (expires_dir_config*)pcalloc (p, sizeof(expires_dir_config));
+ expires_dir_config *base = (expires_dir_config *)basev;
+ expires_dir_config *add = (expires_dir_config *)addv;
+
+ if ( add->active == ACTIVE_DONTCARE ) {
+ new->active = base->active;
+ } else {
+ new->active = add->active;
+ };
+
+ if ( add->expiresdefault != '\0' ) {
+ new->expiresdefault = add->expiresdefault;
+ };
+
+ new->expiresbytype = overlay_tables (p, add->expiresbytype,
+ base->expiresbytype);
+ return new;
+}
+
+int add_expires(request_rec *r)
+{
+ expires_dir_config *conf =
+ (expires_dir_config *)get_module_config(r->per_dir_config, &expires_module);
+ char *code;
+ time_t base;
+ time_t additional;
+ time_t expires;
+
+ if ( r->finfo.st_mode == 0 )
+ return DECLINED;
+
+ /* COMMA bites my ass...
+ */
+ if ( conf == NULL ) {
+ log_reason ("internal error in expires_module; add_expires(), conf == NULL", r->filename, r);
+ return SERVER_ERROR;
+ };
+
+ if ( conf->active != ACTIVE_ON )
+ return DECLINED;
+
+ /* we perhaps could use the default_type(r) in its place but that
+ * may be 2nd guesing the desired configuration... calling table_get
+ * with a NULL key will SEGV us
+ *
+ * I still don't know *why* r->content_type would ever be NULL, this
+ * is possibly a result of fixups being called in many different
+ * places. Fixups is probably the wrong place to be doing all this
+ * work... Bah.
+ *
+ * Changed as of 08.Jun.96 don't DECLINE, look for an ExpiresDefault.
+ */
+ if ( r->content_type == NULL )
+ code = NULL;
+ else
+ code = (char *) table_get( conf->expiresbytype, r->content_type );
+
+ if ( code == NULL ) {
+ /* no expires defined for that type, is there a default? */
+ code = conf->expiresdefault;
+
+ if ( code[0] == '\0' )
+ return OK;
+ };
+
+ /* we have our code */
+
+ switch (code[0]) {
+ case 'M':
+ base = r->finfo.st_mtime;
+ additional = atoi( &code[1] );
+ break;
+ case 'A':
+ /* there's been some discussion and it's possible that
+ * 'access time' will be stored in request structure
+ */
+ base = time( NULL );
+ additional = atoi( &code[1] );
+ break;
+ default:
+ /* expecting the add_* routines to be case-hardened this
+ * is just a reminder that module is beta
+ */
+ log_reason ("internal error in expires_module; bad expires code", r->filename, r);
+ return SERVER_ERROR;
+ };
+
+ expires = base + additional;
+ tzset(); /* redundant? called implicitly by localtime, at least
+ * under FreeBSD
+ */
+ table_set( r->headers_out, "Expires", gm_timestr_822( r->pool, expires ));
+ return OK;
+}
+
+module expires_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_dir_expires_config, /* dir config creater */
+ merge_expires_dir_configs, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server configs */
+ expires_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ add_expires, /* fixups */
+ NULL, /* logger */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_headers.c b/usr.sbin/httpd/src/mod_headers.c
new file mode 100644
index 00000000000..3976be7ba8a
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_headers.c
@@ -0,0 +1,253 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/>.
+ *
+ */
+
+/*
+ * mod_headers.c: Add/append/remove HTTP response headers
+ * Written by Paul Sutton, paul@ukweb.com, 1 Oct 1996
+ *
+ * New directive, Header, can be used to add/replace/remove HTTP headers.
+ * Valid in both per-server and per-dir configurations.
+ *
+ * Syntax is:
+ *
+ * Header action header value
+ *
+ * Where action is one of:
+ * set - set this header, replacing any old value
+ * add - add this header, possible resulting in two or more
+ * headers with the same name
+ * append - append this text onto any existing header of this same
+ * unset - remove this header
+ *
+ * Where action is unset, the third argument (value) should not be given.
+ * The header name can include the colon, or not.
+ *
+ * The Header directive can only be used where allowed by the FileInfo
+ * override.
+ *
+ * When the request is processed, the header directives are processed in
+ * this order: firstly, the main server, then the virtual server handling
+ * this request (if any), then any <Directory> sections (working downwards
+ * from the root dir), then an <Location> sections (working down from
+ * shortest URL component), the any <File> sections. This order is
+ * important if any 'set' or 'unset' actions are used. For example,
+ * the following two directives have different effect if applied in
+ * the reverse order:
+ *
+ * Header append Author "John P. Doe"
+ * Header unset Author
+ *
+ * Examples:
+ *
+ * To set the "Author" header, use
+ * Header add Author "John P. Doe"
+ *
+ * To remove a header:
+ * Header unset Author
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+
+typedef enum {
+ hdr_add = 'a', /* add header (could mean multiple hdrs) */
+ hdr_set = 's', /* set (replace old value) */
+ hdr_append = 'm', /* append (merge into any old value) */
+ hdr_unset = 'u' /* unset header */
+} hdr_actions;
+
+typedef struct {
+ hdr_actions action;
+ char *header;
+ char *value;
+} header_entry;
+
+/*
+ * headers_conf is our per-module configuration. This is used as both
+ * a per-dir and per-server config
+ */
+typedef struct {
+ array_header *headers;
+} headers_conf;
+
+module headers_module;
+
+void *create_headers_config (pool *p, server_rec *s)
+{
+ headers_conf *a =
+ (headers_conf *)pcalloc (p, sizeof(headers_conf));
+
+ a->headers = make_array (p, 2, sizeof(header_entry));
+ return a;
+}
+
+void *create_headers_dir_config (pool *p, char *d)
+{
+ return (headers_conf*)create_headers_config(p, NULL);
+}
+
+void *merge_headers_config (pool *p, void *basev, void *overridesv)
+{
+ headers_conf *a =
+ (headers_conf *)pcalloc (p, sizeof(headers_conf));
+ headers_conf *base = (headers_conf *)basev,
+ *overrides = (headers_conf *)overridesv;
+
+ a->headers = append_arrays(p, base->headers, overrides->headers);
+
+ return a;
+}
+
+
+const char *header_cmd(cmd_parms *cmd, headers_conf *dirconf, char *action, char *hdr, char *value)
+{
+ header_entry *new;
+ server_rec *s = cmd->server;
+ headers_conf *serverconf =
+ (headers_conf *)get_module_config(s->module_config,&headers_module);
+ char *colon;
+
+ if ( cmd->path )
+ {
+ new = (header_entry*)push_array(dirconf->headers);
+ }
+ else
+ {
+ new = (header_entry*)push_array(serverconf->headers);
+ }
+
+ if (!strcasecmp(action, "set")) new->action = hdr_set;
+ else if (!strcasecmp(action, "add")) new->action = hdr_add;
+ else if (!strcasecmp(action, "append")) new->action = hdr_append;
+ else if (!strcasecmp(action, "unset")) new->action = hdr_unset;
+ else
+ return "first argument must be add, set, append or unset.";
+
+ if (new->action == hdr_unset) {
+ if (value) return "Header unset takes two arguments";
+ }
+ else if (!value)
+ return "Header requires three arguments";
+
+ if ((colon = strchr(hdr, ':')))
+ *colon = '\0';
+
+ new->header = pstrdup(cmd->pool, hdr);
+ new->value = value ? pstrdup(cmd->pool, value) : NULL;
+
+ return NULL;
+}
+
+command_rec headers_cmds[] = {
+{ "Header", header_cmd, NULL, OR_FILEINFO, TAKE23,
+ "an action, header and value"},
+{ NULL }
+};
+
+void do_headers_fixup(request_rec *r, array_header *headers)
+{
+ int i;
+
+ for (i = 0; i < headers->nelts; ++i) {
+ header_entry *hdr = &((header_entry*)(headers->elts))[i];
+ switch (hdr->action) {
+ case hdr_add:
+ table_add(r->headers_out, hdr->header, hdr->value);
+ break;
+ case hdr_append:
+ table_merge(r->headers_out, hdr->header, hdr->value);
+ break;
+ case hdr_set:
+ table_set(r->headers_out, hdr->header, hdr->value);
+ break;
+ case hdr_unset:
+ table_unset(r->headers_out, hdr->header);
+ break;
+ }
+ }
+
+}
+
+int fixup_headers(request_rec *r)
+{
+ void *sconf = r->server->module_config;
+ headers_conf *serverconf =
+ (headers_conf *)get_module_config(sconf, &headers_module);
+ void *dconf = r->per_dir_config;
+ headers_conf *dirconf =
+ (headers_conf *)get_module_config(dconf, &headers_module);
+
+ do_headers_fixup(r, serverconf->headers);
+ do_headers_fixup(r, dirconf->headers);
+
+ return DECLINED;
+}
+
+module headers_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_headers_dir_config, /* dir config creater */
+ merge_headers_config, /* dir merger --- default is to override */
+ create_headers_config, /* server config */
+ merge_headers_config, /* merge server configs */
+ headers_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ fixup_headers, /* fixups */
+ NULL, /* logger */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_imap.c b/usr.sbin/httpd/src/mod_imap.c
new file mode 100644
index 00000000000..3282dfe71f3
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_imap.c
@@ -0,0 +1,877 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * This imagemap module started as a port of the original imagemap.c
+ * written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu).
+ * This version includes the mapping algorithms found in version 1.3
+ * of imagemap.c.
+ *
+ * Contributors to this code include:
+ *
+ * Kevin Hughes, kevinh@pulua.hcc.hawaii.edu
+ *
+ * Eric Haines, erich@eye.com
+ * "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
+ *
+ * Randy Terbush, randy@zyzzyva.com
+ * port to Apache module format, "base_uri" and support for relative URLs
+ *
+ * James H. Cloos, Jr., cloos@jhcloos.com
+ * Added point datatype, using code in NCSA's version 1.8 imagemap.c
+ * program, as distributed with version 1.4.1 of their server.
+ * The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu
+ *
+ * Nathan Kurz, nate@tripod.com
+ * Rewrite/reorganization. New handling of default, base and relative URLs.
+ * New Configuration directives:
+ * ImapMenu {none, formatted, semiformatted, unformatted}
+ * ImapDefault {error, nocontent, referer, menu, URL}
+ * ImapBase {map, referer, URL}
+ * Support for creating non-graphical menu added. (backwards compatible):
+ * Old: directive URL [x,y ...]
+ * New: directive URL "Menu text" [x,y ...]
+ * or: directive URL x,y ... "Menu text"
+ * Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca.
+ *
+ * Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "util_script.h"
+
+#define IMAP_MAGIC_TYPE "application/x-httpd-imap"
+#define MAXVERTS 100
+#define X 0
+#define Y 1
+
+#define IMAP_MENU_DEFAULT "formatted"
+#define IMAP_DEFAULT_DEFAULT "nocontent"
+#define IMAP_BASE_DEFAULT "map"
+
+#ifdef SUNOS4
+double strtod(); /* SunOS needed this */
+#endif
+
+module imap_module;
+
+typedef struct {
+ char *imap_menu;
+ char *imap_default;
+ char *imap_base;
+} imap_conf_rec;
+
+static void *create_imap_dir_config(pool *p, char *dummy)
+{
+ imap_conf_rec *icr =
+ (imap_conf_rec *) palloc(p, sizeof(imap_conf_rec));
+
+ icr->imap_menu = NULL;
+ icr->imap_default = NULL;
+ icr->imap_base = NULL;
+
+ return icr;
+}
+
+static void *merge_imap_dir_configs(pool *p, void *basev, void *addv)
+{
+ imap_conf_rec *new = (imap_conf_rec *) pcalloc(p, sizeof(imap_conf_rec));
+ imap_conf_rec *base = (imap_conf_rec *) basev;
+ imap_conf_rec *add = (imap_conf_rec *) addv;
+
+ new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu;
+ new->imap_default = add->imap_default ? add->imap_default : base->imap_default;
+ new->imap_base = add->imap_base ? add->imap_base : base->imap_base;
+
+ return new;
+}
+
+
+static 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}
+};
+
+static int pointinrect(const double point[2], double coords[MAXVERTS][2])
+{
+ double max[2], min[2];
+ if (coords[0][X] > coords[1][X]) {
+ max[0] = coords[0][X];
+ min[0] = coords[1][X];
+ }
+ else {
+ max[0] = coords[1][X];
+ min[0] = coords[0][X];
+ }
+
+ if (coords[0][Y] > coords[1][Y]) {
+ max[1] = coords[0][Y];
+ min[1] = coords[1][Y];
+ }
+ else {
+ max[1] = coords[1][Y];
+ min[1] = coords[0][Y];
+ }
+
+ return ((point[X] >= min[0] && point[X] <= max[0]) &&
+ (point[Y] >= min[1] && point[Y] <= max[1]));
+}
+
+static int pointincircle(const double point[2], double coords[MAXVERTS][2])
+{
+ double radius1, radius2;
+
+ radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
+ + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
+
+ radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
+ + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
+
+ return (radius2 <= radius1);
+}
+
+static int pointinpoly(const double point[2], double pgon[MAXVERTS][2])
+{
+ int i, numverts, inside_flag, xflag0;
+ int crossings;
+ double *p;
+ const double *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);
+}
+
+
+static int is_closer(const double point[2], double coords[MAXVERTS][2],
+ double *closest)
+{
+ double dist_squared = ((point[X] - coords[0][X]) * (point[X] - coords[0][X]))
+ + ((point[Y] - coords[0][Y]) * (point[Y] - coords[0][Y]));
+
+ if (point[X] < 0 || point[Y] < 0)
+ return (0); /* don't mess around with negative coordinates */
+
+ if (*closest < 0 || dist_squared < *closest) {
+ *closest = dist_squared;
+ return (1); /* if this is the first point or is the closest yet
+ set 'closest' equal to this distance^2 */
+ }
+
+ return (0); /* if it's not the first or closest */
+
+}
+
+static double get_x_coord(const char *args)
+{
+ char *endptr; /* we want it non-null */
+ double x_coord = -1; /* -1 is returned if no coordinate is given */
+
+ if (args == NULL)
+ return (-1); /* in case we aren't passed anything */
+
+ while (*args && !isdigit(*args) && *args != ',')
+ args++; /* jump to the first digit, but not past a comma or end */
+
+ x_coord = strtod(args, &endptr);
+
+ if (endptr > args) /* if a conversion was made */
+ return (x_coord);
+
+ return (-1); /* else if no conversion was made, or if no args was given */
+}
+
+static double get_y_coord(const char *args)
+{
+ char *endptr; /* we want it non-null */
+ char *start_of_y = NULL;
+ double y_coord = -1; /* -1 is returned on error */
+
+ if (args == NULL)
+ return (-1); /* in case we aren't passed anything */
+
+ start_of_y = strchr(args, ','); /* the comma */
+
+ if (start_of_y) {
+
+ start_of_y++; /* start looking at the character after the comma */
+
+ while (*start_of_y && !isdigit(*start_of_y))
+ start_of_y++; /* jump to the first digit, but not past the end */
+
+ y_coord = strtod(start_of_y, &endptr);
+
+ if (endptr > start_of_y)
+ return (y_coord);
+ }
+
+ return (-1); /* if no conversion was made, or no comma was found in args */
+}
+
+
+/* See if string has a "quoted part", and if so set *quoted_part to
+ * the first character of the quoted part, then hammer a \0 onto the
+ * trailing quote, and set *string to point at the first character
+ * past the second quote.
+ *
+ * Otherwise set *quoted_part to NULL, and leave *string alone.
+ */
+static void read_quoted(char **string, char **quoted_part)
+{
+ char *strp = *string;
+
+ /* assume there's no quoted part */
+ *quoted_part = NULL;
+
+ while (isspace(*strp))
+ strp++; /* go along string until non-whitespace */
+
+ if (*strp == '"') { /* if that character is a double quote */
+ strp++; /* step over it */
+ *quoted_part = strp; /* note where the quoted part begins */
+
+ while (*strp && *strp != '"') {
+ ++strp; /* skip the quoted portion */
+ }
+
+ *strp = '\0'; /* end the string with a NUL */
+
+ strp++; /* step over the last double quote */
+ *string = strp;
+ }
+}
+
+/*
+ * returns the mapped URL or NULL.
+ */
+static char *imap_url(request_rec *r, const char *base, const char *value)
+{
+/* translates a value into a URL. */
+ int slen, clen;
+ char *string_pos = NULL;
+ const char *string_pos_const = NULL;
+ char *directory = NULL;
+ char *referer = NULL;
+ char *my_base;
+
+ if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) {
+ return construct_url(r->pool, r->uri, r->server);
+ }
+
+ if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) {
+ return pstrdup(r->pool, value); /* these are handled elsewhere, so just copy them */
+ }
+
+ if (!strcasecmp(value, "referer")) {
+ referer = table_get(r->headers_in, "Referer");
+ if (referer && *referer) {
+ return pstrdup(r->pool, referer);
+ }
+ else {
+ /* XXX: This used to do *value = '\0'; ... which is totally bogus
+ * because it hammers the passed in value, which can be a string constant,
+ * or part of a config, or whatever. Total garbage. This works around
+ * that without changing the rest of this code much
+ */
+ value = ""; /* if 'referer' but no referring page, null the value */
+ }
+ }
+
+ string_pos_const = value;
+ while (isalpha(*string_pos_const))
+ string_pos_const++; /* go along the URL from the map until a non-letter */
+ if (*string_pos_const == ':') {
+ /* if letters and then a colon (like http:) */
+ /* it's an absolute URL, so use it! */
+ return pstrdup(r->pool, value);
+ }
+
+ if (!base || !*base) {
+ if (value && *value) {
+ return pstrdup(r->pool, value); /* no base: use what is given */
+ }
+ /* no base, no value: pick a simple default */
+ return construct_url(r->pool, "/", r->server);
+ }
+
+ /* must be a relative URL to be combined with base */
+ if (strchr(base, '/') == NULL && (!strncmp(value, "../", 3) || !strcmp(value, ".."))) {
+ log_reason("invalid base directive in map file: %s", r->uri, r);
+ return NULL;
+ }
+ my_base = pstrdup(r->pool, base);
+ string_pos = my_base;
+ while (*string_pos) {
+ if (*string_pos == '/' && *(string_pos + 1) == '/') {
+ string_pos += 2; /* if there are two slashes, jump over them */
+ continue;
+ }
+ if (*string_pos == '/') { /* the first single slash */
+ if (value[0] == '/') {
+ *string_pos = '\0';
+ } /* if the URL from the map starts from root, end the
+ base URL string at the first single slash */
+ else {
+ directory = string_pos; /* save the start of the directory portion */
+
+ string_pos = strrchr(string_pos, '/'); /* now reuse string_pos */
+ string_pos++; /* step over that last slash */
+ *string_pos = '\0';
+ } /* but if the map url is relative, leave the
+ slash on the base (if there is one) */
+ break;
+ }
+ string_pos++; /* until we get to the end of my_base without finding
+ a slash by itself */
+ }
+
+ while (!strncmp(value, "../", 3) || !strcmp(value, "..")) {
+
+ if (directory && (slen = strlen(directory))) {
+
+ /* for each '..', knock a directory off the end
+ by ending the string right at the last slash.
+ But only consider the directory portion: don't eat
+ into the server name. And only try if a directory
+ portion was found */
+
+ clen = slen - 1;
+
+ while ((slen - clen) == 1) {
+
+ if ((string_pos = strrchr(directory, '/')))
+ *string_pos = '\0';
+ clen = strlen(directory);
+ if (clen == 0)
+ break;
+ }
+
+ value += 2; /* jump over the '..' that we found in the value */
+ }
+ else if (directory) {
+ log_reason("invalid directory name in map file: %s", r->uri, r);
+ return NULL;
+ }
+
+ if (!strncmp(value, "/../", 4) || !strcmp(value, "/.."))
+ value++; /* step over the '/' if there are more '..' to do.
+ this way, we leave the starting '/' on value after
+ the last '..', but get rid of it otherwise */
+
+ } /* by this point, value does not start with '..' */
+
+ if (value && *value) {
+ return pstrcat(r->pool, my_base, value, NULL);
+ }
+ return my_base;
+}
+
+static int imap_reply(request_rec *r, char *redirect)
+{
+ if (!strcasecmp(redirect, "error")) {
+ return SERVER_ERROR; /* they actually requested an error! */
+ }
+ if (!strcasecmp(redirect, "nocontent")) {
+ return HTTP_NO_CONTENT; /* tell the client to keep the page it has */
+ }
+ if (redirect && *redirect) {
+ table_set(r->headers_out, "Location", redirect);
+ return REDIRECT; /* must be a URL, so redirect to it */
+ }
+ return SERVER_ERROR;
+}
+
+static void menu_header(request_rec *r, char *menu)
+{
+ r->content_type = "text/html";
+ send_http_header(r);
+ hard_timeout("send menu", r); /* killed in menu_footer */
+
+ rvputs(r, "<html><head>\n<title>Menu for ", r->uri,
+ "</title>\n</head><body>\n", NULL);
+
+ if (!strcasecmp(menu, "formatted")) {
+ rvputs(r, "<h1>Menu for ", r->uri, "</h1>\n<hr>\n\n", NULL);
+ }
+
+ return;
+}
+
+static void menu_blank(request_rec *r, char *menu)
+{
+ if (!strcasecmp(menu, "formatted")) {
+ rputs("\n", r);
+ }
+ if (!strcasecmp(menu, "semiformatted")) {
+ rputs("<br>\n", r);
+ }
+ if (!strcasecmp(menu, "unformatted")) {
+ rputs("\n", r);
+ }
+ return;
+}
+
+static 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 */
+}
+
+static void menu_default(request_rec *r, char *menu, char *href, char *text)
+{
+ if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
+ return; /* don't print such lines, these aren'te really href's */
+ }
+ if (!strcasecmp(menu, "formatted")) {
+ rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text, "</a></pre>\n",
+ NULL);
+ }
+ if (!strcasecmp(menu, "semiformatted")) {
+ rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text, "</a></pre>\n",
+ NULL);
+ }
+ if (!strcasecmp(menu, "unformatted")) {
+ rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
+ }
+ return;
+}
+
+static void menu_directive(request_rec *r, char *menu, char *href, char *text)
+{
+ if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
+ return; /* don't print such lines, as this isn't really an href */
+ }
+ if (!strcasecmp(menu, "formatted")) {
+ rvputs(r, "<pre> <a href=\"", href, "\">", text, "</a></pre>\n",
+ NULL);
+ }
+ if (!strcasecmp(menu, "semiformatted")) {
+ rvputs(r, "<pre> <a href=\"", href, "\">", text, "</a></pre>\n",
+ NULL);
+ }
+ if (!strcasecmp(menu, "unformatted")) {
+ rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
+ }
+ return;
+}
+
+static void menu_footer(request_rec *r)
+{
+ rputs("\n\n</body>\n</html>\n", r); /* finish the menu */
+ kill_timeout(r);
+}
+
+static int imap_handler(request_rec *r)
+{
+ char input[MAX_STRING_LEN];
+ char *directive;
+ char *value;
+ char *href_text;
+ char *base;
+ char *redirect;
+ char *mapdflt;
+ char *closest = NULL;
+ double closest_yet = -1;
+
+ double testpoint[2];
+ double pointarray[MAXVERTS + 1][2];
+ int vertex;
+
+ char *string_pos;
+ int showmenu = 0;
+
+ imap_conf_rec *icr = 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;
+
+ if (r->method_number != M_GET)
+ return DECLINED;
+
+ imap = pfopen(r->pool, r->filename, "r");
+
+ if (!imap)
+ return NOT_FOUND;
+
+ base = imap_url(r, NULL, imap_base); /* set base according to default */
+ if (!base)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ mapdflt = imap_url(r, NULL, imap_default); /* and default to global default */
+ if (!mapdflt)
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ testpoint[X] = get_x_coord(r->args);
+ testpoint[Y] = get_y_coord(r->args);
+
+ if ((testpoint[X] == -1 || testpoint[Y] == -1) ||
+ (testpoint[X] == 0 && testpoint[Y] == 0)) {
+ /* if either is -1 or if both are zero (new Lynx) */
+ /* we don't have valid coordinates */
+ testpoint[X] = -1;
+ testpoint[Y] = -1;
+ if (strncasecmp(imap_menu, "none", 2))
+ showmenu = 1; /* show the menu _unless_ ImapMenu is 'none' or 'no' */
+ }
+
+ if (showmenu) { /* send start of imagemap menu if we're going to */
+ menu_header(r, imap_menu);
+ }
+
+ while (!cfg_getline(input, sizeof(input), imap)) {
+ if (!input[0]) {
+ if (showmenu) {
+ menu_blank(r, imap_menu);
+ }
+ continue;
+ }
+
+ if (input[0] == '#') {
+ if (showmenu) {
+ menu_comment(r, imap_menu, input + 1);
+ }
+ continue;
+ } /* blank lines and comments are ignored if we aren't printing a menu */
+
+ /* find the first two space delimited fields, recall that
+ * cfg_getline has removed leading/trailing whitespace and
+ * compressed the other whitespace down to one space a piece
+ *
+ * note that we're tokenizing as we go... if we were to use the
+ * getword() class of functions we would end up allocating extra
+ * memory for every line of the map file
+ */
+ string_pos = input;
+ if (!*string_pos) /* need at least two fields */
+ goto need_2_fields;
+
+ directive = string_pos;
+ while (*string_pos && *string_pos != ' ') /* past directive */
+ ++string_pos;
+ if (!*string_pos) /* need at least two fields */
+ goto need_2_fields;
+ *string_pos++ = '\0';
+
+ if (!*string_pos) /* need at least two fields */
+ goto need_2_fields;
+ value = string_pos;
+ while (*string_pos && *string_pos != ' ') /* past value */
+ ++string_pos;
+ if (*string_pos == ' ') {
+ *string_pos++ = '\0';
+ }
+ else {
+ /* end of input, don't advance past it */
+ *string_pos = '\0';
+ }
+
+ if (!strncasecmp(directive, "base", 4)) { /* base, base_uri */
+ base = imap_url(r, NULL, value);
+ if (!base)
+ goto menu_bail;
+ continue; /* base is never printed to a menu */
+ }
+
+ read_quoted(&string_pos, &href_text);
+
+ if (!strcasecmp(directive, "default")) { /* default */
+ mapdflt = imap_url(r, NULL, value);
+ if (!mapdflt)
+ goto menu_bail;
+ if (showmenu) { /* print the default if there's a menu */
+ redirect = imap_url(r, base, mapdflt);
+ if (!redirect)
+ goto menu_bail;
+ menu_default(r, imap_menu, redirect, href_text ? href_text : mapdflt);
+ }
+ continue;
+ }
+
+ vertex = 0;
+ while (vertex < MAXVERTS &&
+ sscanf(string_pos, "%lf%*[, ]%lf",
+ &pointarray[vertex][X], &pointarray[vertex][Y]) == 2) {
+ /* Now skip what we just read... we can't use ANSIism %n */
+ while (isspace(*string_pos)) /* past whitespace */
+ string_pos++;
+ while (isdigit(*string_pos)) /* and the 1st number */
+ string_pos++;
+ string_pos++; /* skip the ',' */
+ while (isspace(*string_pos)) /* past any more whitespace */
+ string_pos++;
+ 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) {
+ if (!href_text) {
+ read_quoted(&string_pos, &href_text); /* href text could be here instead */
+ }
+ redirect = imap_url(r, base, value);
+ if (!redirect)
+ goto menu_bail;
+ menu_directive(r, imap_menu, redirect, href_text ? href_text : value);
+ continue;
+ }
+ /* note that we don't make it past here if we are making a menu */
+
+ if (testpoint[X] == -1 || pointarray[0][X] == -1)
+ continue; /* don't try the following tests if testpoints
+ are invalid, or if there are no coordinates */
+
+ if (!strcasecmp(directive, "poly")) { /* poly */
+
+ if (pointinpoly(testpoint, pointarray)) {
+ pfclose(r->pool, imap);
+ redirect = imap_url(r, base, value);
+ if (!redirect)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return (imap_reply(r, redirect));
+ }
+ continue;
+ }
+
+ if (!strcasecmp(directive, "circle")) { /* circle */
+
+ if (pointincircle(testpoint, pointarray)) {
+ pfclose(r->pool, imap);
+ redirect = imap_url(r, base, value);
+ if (!redirect)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return (imap_reply(r, redirect));
+ }
+ continue;
+ }
+
+ if (!strcasecmp(directive, "rect")) { /* rect */
+
+ if (pointinrect(testpoint, pointarray)) {
+ pfclose(r->pool, imap);
+ redirect = imap_url(r, base, value);
+ if (!redirect)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return (imap_reply(r, redirect));
+ }
+ continue;
+ }
+
+ if (!strcasecmp(directive, "point")) { /* point */
+
+ if (is_closer(testpoint, pointarray, &closest_yet)) {
+ closest = pstrdup(r->pool, value);
+ }
+
+ 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 */
+ redirect = imap_url(r, base, closest);
+ if (!redirect)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return (imap_reply(r, redirect));
+ }
+
+ if (mapdflt) { /* a default should be defined, even if only 'nocontent' */
+ redirect = imap_url(r, base, mapdflt);
+ if (!redirect)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return (imap_reply(r, redirect));
+ }
+
+ return SERVER_ERROR; /* If we make it this far, we failed. They lose! */
+
+need_2_fields:
+ log_reason("all map file lines require at least two fields", r->uri, r);
+ /* fall through */
+menu_bail:
+ pfclose(r->pool, imap);
+ if (showmenu) {
+ /* There's not much else we can do ... we've already sent the headers
+ * to the client.
+ */
+ rputs("\n\n[an internal server error occured]\n", r);
+ menu_footer(r);
+ return OK;
+ }
+ return HTTP_INTERNAL_SERVER_ERROR;
+}
+
+
+static 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_include.c b/usr.sbin/httpd/src/mod_include.c
new file mode 100644
index 00000000000..2b53f24aaa1
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_include.c
@@ -0,0 +1,2329 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. 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_include.c: Handles the server-parsed HTML documents
+ *
+ * Original by Rob McCool; substantial fixups by David Robinson;
+ * incorporated into the Apache module framework by rst.
+ *
+ */
+/*
+ * sub key may be anything a Perl*Handler can be:
+ * subroutine name, package name (defaults to package::handler),
+ * Class->method call or anoymous sub {}
+ *
+ * Child <!--#perl sub="sub {print $$}" --> accessed
+ * <!--#perl sub="sub {print ++$Access::Cnt }" --> times. <br>
+ *
+ * <!--#perl arg="one" sub="mymod::includer" -->
+ *
+ * -Doug MacEachern
+ */
+
+#ifdef USE_PERL_SSI
+#include "config.h"
+#ifdef USE_SFIO
+#undef USE_SFIO
+#define USE_STDIO
+#endif
+#include "modules/perl/mod_perl.h"
+#else
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+#endif
+
+#define STARTING_SEQUENCE "<!--#"
+#define ENDING_SEQUENCE "-->"
+#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
+#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
+#define SIZEFMT_BYTES 0
+#define SIZEFMT_KMG 1
+
+
+static void safe_copy(char *dest, const char *src, size_t max_len)
+{
+ strncpy(dest, src, max_len - 1);
+ dest[max_len - 1] = '\0';
+}
+
+/* ------------------------ Environment function -------------------------- */
+
+static void add_include_vars(request_rec *r, char *timefmt)
+{
+ struct passwd *pw;
+ table *e = r->subprocess_env;
+ char *t;
+ time_t date = r->request_time;
+
+ 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];
+ ap_snprintf(uid, sizeof(uid), "user#%lu",
+ (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) {
+ char *arg_copy = pstrdup(r->pool, r->args);
+
+ unescape_url(arg_copy);
+ table_set(e, "QUERY_STRING_UNESCAPED",
+ escape_shell_cmd(r->pool, arg_copy));
+ }
+}
+
+
+
+/* --------------------------- Parser functions --------------------------- */
+
+#define OUTBUFSIZE 4096
+/* PUT_CHAR and FLUSH_BUF currently only work within the scope of
+ * find_string(); they are hacks to avoid calling rputc for each and
+ * every character output. A common set of buffering calls for this
+ * type of output SHOULD be implemented.
+ */
+#define PUT_CHAR(c,r) \
+ { \
+ outbuf[outind++] = c; \
+ if (outind == OUTBUFSIZE) { \
+ FLUSH_BUF(r) \
+ }; \
+ }
+
+/* there SHOULD be some error checking on the return value of
+ * rwrite, however it is unclear what the API for rwrite returning
+ * errors is and little can really be done to help the error in
+ * any case.
+ */
+#define FLUSH_BUF(r) \
+ { \
+ rwrite(outbuf, outind, r); \
+ outind = 0; \
+ }
+
+/*
+ * f: file handle being read from
+ * c: character to read into
+ * ret: return value to use if input fails
+ * r: current request_rec
+ *
+ * This macro is redefined after find_string() for historical reasons
+ * to avoid too many code changes. This is one of the many things
+ * that should be fixed.
+ */
+#define GET_CHAR(f,c,ret,r) \
+ { \
+ int i = getc(f); \
+ if (i == EOF) { /* either EOF or error -- needs error handling if latter */ \
+ if (ferror(f)) { \
+ fprintf(stderr, "encountered error in GET_CHAR macro, " \
+ "mod_include.\n"); \
+ } \
+ FLUSH_BUF(r); \
+ pfclose(r->pool, f); \
+ return ret; \
+ } \
+ c = (char)i; \
+ }
+
+static int find_string(FILE *in, const char *str, request_rec *r, int printing)
+{
+ int x, l = strlen(str), p;
+ char outbuf[OUTBUFSIZE];
+ int outind = 0;
+ char c;
+
+ p = 0;
+ while (1) {
+ GET_CHAR(in, c, 1, r);
+ if (c == str[p]) {
+ if ((++p) == l) {
+ FLUSH_BUF(r);
+ return 0;
+ }
+ }
+ else {
+ if (printing) {
+ for (x = 0; x < p; x++) {
+ PUT_CHAR(str[x], r);
+ }
+ PUT_CHAR(c, r);
+ }
+ p = 0;
+ }
+ }
+}
+
+#undef FLUSH_BUF
+#undef PUT_CHAR
+#undef GET_CHAR
+#define GET_CHAR(f,c,r,p) \
+ { \
+ int i = getc(f); \
+ if (i == EOF) { /* either EOF or error -- needs error handling if latter */ \
+ if (ferror(f)) { \
+ fprintf(stderr, "encountered error in GET_CHAR macro, " \
+ "mod_include.\n"); \
+ } \
+ pfclose(p, f); \
+ return r; \
+ } \
+ c = (char)i; \
+ }
+
+/*
+ * decodes a string containing html entities or numeric character references.
+ * 's' is overwritten with the decoded string.
+ * If 's' is syntatically incorrect, then the followed fixups will be made:
+ * unknown entities will be left undecoded;
+ * references to unused numeric characters will be deleted.
+ * In particular, &#00; will not be decoded, but will be deleted.
+ *
+ * drtr
+ */
+
+/* maximum length of any ISO-LATIN-1 HTML entity name. */
+#define MAXENTLEN (6)
+
+/* The following is a shrinking transformation, therefore safe. */
+
+static void decodehtml(char *s)
+{
+ int val, i, j;
+ char *p = s;
+ const char *ents;
+ static const char * const entlist[MAXENTLEN + 1] =
+ {
+ NULL, /* 0 */
+ NULL, /* 1 */
+ "lt\074gt\076", /* 2 */
+ "amp\046ETH\320eth\360", /* 3 */
+ "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\
+iuml\357ouml\366uuml\374yuml\377", /* 4 */
+ "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\
+THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\
+ucirc\373thorn\376", /* 5 */
+ "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\
+Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\
+Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\
+egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\
+otilde\365oslash\370ugrave\371uacute\372yacute\375" /* 6 */
+ };
+
+ for (; *s != '\0'; s++, p++) {
+ if (*s != '&') {
+ *p = *s;
+ continue;
+ }
+ /* find end of entity */
+ for (i = 1; s[i] != ';' && s[i] != '\0'; i++) {
+ continue;
+ }
+
+ if (s[i] == '\0') { /* treat as normal data */
+ *p = *s;
+ continue;
+ }
+
+ /* is it numeric ? */
+ if (s[1] == '#') {
+ for (j = 2, val = 0; j < i && isdigit(s[j]); j++) {
+ val = val * 10 + s[j] - '0';
+ }
+ s += i;
+ if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
+ (val >= 127 && val <= 160) || val >= 256) {
+ p--; /* no data to output */
+ }
+ else {
+ *p = 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;
+
+ /* makes code below a little less cluttered */
+ --tagbuf_len;
+
+ 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 == '>') {
+ safe_copy(tag, "done", tagbuf_len);
+ return tag;
+ }
+ }
+ return NULL; /* failed */
+ }
+
+ /* find end of tag name */
+ while (1) {
+ if (t - tag == tagbuf_len) {
+ *t = '\0';
+ return NULL;
+ }
+ if (c == '=' || 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 != '=') {
+ ungetc(c, in);
+ 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 (t - tag == tagbuf_len) {
+ *t = '\0';
+ return NULL;
+ }
+/* Want to accept \" as a valid character within a string. */
+ if (c == '\\') {
+ *(t++) = c; /* Add backslash */
+ GET_CHAR(in, c, NULL, p);
+ if (c == term) { /* Only if */
+ *(--t) = c; /* Replace backslash ONLY for terminator */
+ }
+ }
+ else if (c == term) {
+ break;
+ }
+ *(t++) = c;
+ }
+ *t = '\0';
+ if (dodecode) {
+ decodehtml(tag_val);
+ }
+ return pstrdup(p, tag_val);
+}
+
+static int get_directive(FILE *in, char *dest, size_t len, pool *p)
+{
+ char *d = dest;
+ char c;
+
+ /* make room for nul terminator */
+ --len;
+
+ /* skip initial whitespace */
+ while (1) {
+ GET_CHAR(in, c, 1, p);
+ if (!isspace(c)) {
+ break;
+ }
+ }
+ /* now get directive */
+ while (1) {
+ if (d - dest == len) {
+ return 1;
+ }
+ *d++ = tolower(c);
+ GET_CHAR(in, c, 1, p);
+ if (isspace(c)) {
+ break;
+ }
+ }
+ *d = '\0';
+ return 0;
+}
+
+/*
+ * Do variable substitution on strings
+ */
+static void parse_string(request_rec *r, const char *in, char *out,
+ size_t length, int leave_name)
+{
+ char ch;
+ char *next = out;
+ char *end_out;
+
+ /* leave room for nul terminator */
+ end_out = out + length - 1;
+
+ while ((ch = *in++) != '\0') {
+ switch (ch) {
+ case '\\':
+ if (next == end_out) {
+ /* truncated */
+ *next = '\0';
+ return;
+ }
+ if (*in == '$') {
+ *next++ = *in++;
+ }
+ else {
+ *next++ = ch;
+ }
+ break;
+ case '$':
+ {
+ char var[MAX_STRING_LEN];
+ const char *start_of_var_name;
+ const char *end_of_var_name; /* end of var name + 1 */
+ const char *expansion;
+ const char *val;
+ size_t l;
+
+ /* guess that the expansion won't happen */
+ expansion = in - 1;
+ if (*in == '{') {
+ ++in;
+ start_of_var_name = in;
+ in = strchr(in, '}');
+ if (in == NULL) {
+ log_printf(r->server,
+ "Missing '}' on variable \"%s\" in %s",
+ expansion, r->filename);
+ *next = '\0';
+ return;
+ }
+ end_of_var_name = in;
+ ++in;
+ }
+ else {
+ start_of_var_name = in;
+ while (isalnum(*in) || *in == '_') {
+ ++in;
+ }
+ end_of_var_name = in;
+ }
+ /* what a pain, too bad there's no table_getn where you can
+ * pass a non-nul terminated string */
+ l = end_of_var_name - start_of_var_name;
+ l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l;
+ memcpy(var, start_of_var_name, l);
+ var[l] = '\0';
+
+ val = table_get(r->subprocess_env, var);
+ if (val) {
+ expansion = val;
+ l = strlen(expansion);
+ }
+ else if (leave_name) {
+ l = in - expansion;
+ }
+ else {
+ break; /* no expansion to be done */
+ }
+ l = (l > end_out - next) ? (end_out - next) : l;
+ memcpy(next, expansion, l);
+ next += l;
+ break;
+ }
+ default:
+ if (next == end_out) {
+ /* truncated */
+ *next = '\0';
+ return;
+ }
+ *next++ = ch;
+ break;
+ }
+ }
+ *next = '\0';
+ return;
+}
+
+/* --------------------------- Action handlers ---------------------------- */
+
+static int include_cgi(char *s, request_rec *r)
+{
+ request_rec *rr = sub_req_lookup_uri(s, r);
+ int rr_status;
+
+ if (rr->status != HTTP_OK) {
+ return -1;
+ }
+
+ /* No hardwired path info or query allowed */
+
+ if ((rr->path_info && rr->path_info[0]) || rr->args) {
+ return -1;
+ }
+ if (rr->finfo.st_mode == 0) {
+ return -1;
+ }
+
+ /* Script gets parameters of the *document*, for back compatibility */
+
+ rr->path_info = r->path_info; /* hard to get right; see mod_cgi.c */
+ rr->args = r->args;
+
+ /* Force sub_req to be treated as a CGI request, even if ordinary
+ * typing rules would have called it something else.
+ */
+
+ rr->content_type = CGI_MAGIC_TYPE;
+
+ /* Run it. */
+
+ rr_status = run_sub_req(rr);
+ if (is_HTTP_REDIRECT(rr_status)) {
+ char *location = table_get(rr->headers_out, "Location");
+ location = escape_html(rr->pool, location);
+ rvputs(r, "<A HREF=\"", location, "\">", location, "</A>", NULL);
+ }
+
+ destroy_sub_req(rr);
+ chdir_file(r->filename);
+
+ return 0;
+}
+
+/* ensure that path is relative, and does not contain ".." elements
+ * ensentially ensure that it does not match the regex:
+ * (^/|(^|/)\.\.(/|$))
+ * XXX: this needs os abstraction... consider c:..\foo in win32
+ */
+static int is_only_below(const char *path)
+{
+ if (path[0] == '/') {
+ return 0;
+ }
+ if (path[0] == '.' && path[1] == '.'
+ && (path[2] == '\0' || path[2] == '/')) {
+ return 0;
+ }
+ while (*path) {
+ if (*path == '/' && path[1] == '.' && path[2] == '.'
+ && (path[3] == '\0' || path[3] == '/')) {
+ return 0;
+ }
+ ++path;
+ }
+ return 1;
+}
+
+static int handle_include(FILE *in, request_rec *r, const char *error, int noexec)
+{
+ char tag[MAX_STRING_LEN];
+ char parsed_string[MAX_STRING_LEN];
+ char *tag_val;
+
+ while (1) {
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
+ return 1;
+ }
+ if (!strcmp(tag, "file") || !strcmp(tag, "virtual")) {
+ request_rec *rr = NULL;
+ char *error_fmt = NULL;
+
+ parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+ if (tag[0] == 'f') {
+ /* be safe; only files in this directory or below allowed */
+ if (!is_only_below(parsed_string)) {
+ error_fmt = "unable to include file \"%s\" "
+ "in parsed file %s";
+ }
+ else {
+ rr = sub_req_lookup_file(parsed_string, r);
+ }
+ }
+ else {
+ rr = sub_req_lookup_uri(parsed_string, r);
+ }
+
+ if (!error_fmt && rr->status != HTTP_OK) {
+ error_fmt = "unable to include \"%s\" in parsed file %s";
+ }
+
+ if (!error_fmt && noexec && rr->content_type
+ && (strncmp(rr->content_type, "text/", 5))) {
+ error_fmt = "unable to include potential exec \"%s\" "
+ "in parsed file %s";
+ }
+ if (error_fmt == NULL) {
+ 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";
+ }
+ chdir_file(r->filename);
+
+ 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;
+
+static 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
+ FILE *dbg = fopen("/dev/tty", "w");
+#endif
+ char err_string[MAX_STRING_LEN];
+
+#ifdef DEBUG_INCLUDE_CMD
+#ifdef __EMX__
+ /* under OS/2 /dev/tty is referenced as con */
+ FILE *dbg = fopen("con", "w");
+#else
+ fprintf(dbg, "Attempting to include command '%s'\n", s);
+#endif
+#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) {
+ char *arg_copy = pstrdup(r->pool, r->args);
+
+ table_set(env, "QUERY_STRING", r->args);
+ unescape_url(arg_copy);
+ table_set(env, "QUERY_STRING_UNESCAPED",
+ escape_shell_cmd(r->pool, arg_copy));
+ }
+
+ error_log2stderr(r->server);
+
+#ifdef DEBUG_INCLUDE_CMD
+ fprintf(dbg, "Attempting to exec '%s'\n", s);
+#endif
+ cleanup_for_exec();
+ /* set shellcmd flag to pass arg to SHELL_PATH */
+ call_exec(r, s, create_environment(r->pool, env), 1);
+ /* Oh, drat. We're still here. The log file descriptors are closed,
+ * so we have to whimper a complaint onto stderr...
+ */
+
+#ifdef DEBUG_INCLUDE_CMD
+ fprintf(dbg, "Exec failed\n");
+#endif
+ ap_snprintf(err_string, sizeof(err_string),
+ "httpd: exec of %s failed, reason: %s (errno = %d)\n",
+ SHELL_PATH, strerror(errno), errno);
+ write(STDERR_FILENO, err_string, strlen(err_string));
+ exit(0);
+}
+
+static int include_cmd(char *s, request_rec *r)
+{
+ include_cmd_arg arg;
+ FILE *f;
+
+ arg.r = r;
+ arg.s = s;
+
+ if (!spawn_child(r->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;
+}
+
+
+static int handle_exec(FILE *in, request_rec *r, const char *error)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ char *file = r->filename;
+ char parsed_string[MAX_STRING_LEN];
+
+ while (1) {
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
+ return 1;
+ }
+ if (!strcmp(tag, "cmd")) {
+ parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1);
+ if (include_cmd(parsed_string, r) == -1) {
+ log_printf(r->server,
+ "execution failure for parameter \"%s\" "
+ "to tag exec in file %s",
+ tag, r->filename);
+ rputs(error, r);
+ }
+ /* just in case some stooge changed directories */
+ chdir_file(r->filename);
+ }
+ else if (!strcmp(tag, "cgi")) {
+ parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+ if (include_cgi(parsed_string, 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);
+ }
+ }
+
+}
+
+static int handle_echo(FILE *in, request_rec *r, const char *error)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+
+ while (1) {
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
+ return 1;
+ }
+ if (!strcmp(tag, "var")) {
+ 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);
+ }
+ }
+}
+
+#ifdef USE_PERL_SSI
+static int handle_perl(FILE *in, request_rec *r, const char *error)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ SV *sub = Nullsv;
+ AV *av = newAV();
+
+ if (!(allow_options(r) & OPT_INCLUDES)) {
+ log_printf(r->server,
+ "httpd: #perl SSI disallowed by IncludesNoExec in %s",
+ r->filename);
+ return DECLINED;
+ }
+ while (1) {
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
+ break;
+ }
+ if (strnEQ(tag, "sub", 3)) {
+ sub = newSVpv(tag_val, 0);
+ }
+ else if (strnEQ(tag, "arg", 3)) {
+ av_push(av, newSVpv(tag_val, 0));
+ }
+ else if (strnEQ(tag, "done", 4)) {
+ break;
+ }
+ }
+ perl_stdout2client(r);
+ perl_call_handler(sub, r, av);
+ return OK;
+}
+#endif
+
+/* error and tf must point to a string with room for at
+ * least MAX_STRING_LEN characters
+ */
+static int handle_config(FILE *in, request_rec *r, char *error, char *tf,
+ int *sizefmt)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ char parsed_string[MAX_STRING_LEN];
+ table *env = r->subprocess_env;
+
+ while (1) {
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0))) {
+ return 1;
+ }
+ if (!strcmp(tag, "errmsg")) {
+ parse_string(r, tag_val, error, MAX_STRING_LEN, 0);
+ }
+ else if (!strcmp(tag, "timefmt")) {
+ time_t date = r->request_time;
+
+ parse_string(r, tag_val, tf, MAX_STRING_LEN, 0);
+ 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")) {
+ parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+ decodehtml(parsed_string);
+ if (!strcmp(parsed_string, "bytes")) {
+ *sizefmt = SIZEFMT_BYTES;
+ }
+ else if (!strcmp(parsed_string, "abbrev")) {
+ *sizefmt = SIZEFMT_KMG;
+ }
+ }
+ else if (!strcmp(tag, "done")) {
+ return 0;
+ }
+ else {
+ log_printf(r->server,
+ "unknown parameter \"%s\" to tag config in %s",
+ tag, r->filename);
+ rputs(error, r);
+ }
+ }
+}
+
+
+static int find_file(request_rec *r, const char *directive, const char *tag,
+ char *tag_val, struct stat *finfo, const char *error)
+{
+ char *to_send;
+
+ if (!strcmp(tag, "file")) {
+ getparents(tag_val); /* get rid of any nasties */
+ to_send = make_full_path(r->pool, "./", 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 == HTTP_OK && 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;
+ }
+}
+
+
+static int handle_fsize(FILE *in, request_rec *r, const char *error, int sizefmt)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ struct stat finfo;
+ char parsed_string[MAX_STRING_LEN];
+
+ while (1) {
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
+ return 1;
+ }
+ else if (!strcmp(tag, "done")) {
+ return 0;
+ }
+ else {
+ parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+ if (!find_file(r, "fsize", tag, parsed_string, &finfo, error)) {
+ if (sizefmt == SIZEFMT_KMG) {
+ send_size(finfo.st_size, r);
+ }
+ else {
+ int l, x;
+#if defined(BSD) && BSD > 199305
+ /* ap_snprintf can't handle %qd */
+ sprintf(tag, "%qd", finfo.st_size);
+#else
+ ap_snprintf(tag, sizeof(tag), "%ld", finfo.st_size);
+#endif
+ l = strlen(tag); /* grrr */
+ for (x = 0; x < l; x++) {
+ if (x && (!((l - x) % 3))) {
+ rputc(',', r);
+ }
+ rputc(tag[x], r);
+ }
+ }
+ }
+ }
+ }
+}
+
+static int handle_flastmod(FILE *in, request_rec *r, const char *error, const char *tf)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ struct stat finfo;
+ char parsed_string[MAX_STRING_LEN];
+
+ while (1) {
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
+ return 1;
+ }
+ else if (!strcmp(tag, "done")) {
+ return 0;
+ }
+ else {
+ parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+ if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) {
+ rputs(ht_time(r->pool, finfo.st_mtime, tf, 0), r);
+ }
+ }
+ }
+}
+
+static int re_check(request_rec *r, char *string, char *rexp)
+{
+ regex_t *compiled;
+ int regex_error;
+
+ compiled = pregcomp(r->pool, rexp, REG_EXTENDED | REG_NOSUB);
+ if (compiled == NULL) {
+ log_printf(r->server, "unable to compile pattern \"%s\"", rexp);
+ return -1;
+ }
+ regex_error = regexec(compiled, string, 0, (regmatch_t *) NULL, 0);
+ pregfree(r->pool, compiled);
+ return (!regex_error);
+}
+
+enum token_type {
+ token_string,
+ token_and, token_or, token_not, token_eq, token_ne,
+ token_rbrace, token_lbrace, token_group,
+ token_ge, token_le, token_gt, token_lt
+};
+struct token {
+ enum token_type type;
+ char value[MAX_STRING_LEN];
+};
+
+/* there is an implicit assumption here that string is at most MAX_STRING_LEN-1
+ * characters long...
+ */
+static const char *get_ptoken(request_rec *r, const char *string, struct token *token)
+{
+ char ch;
+ int next = 0;
+ int qs = 0;
+
+ /* Skip leading white space */
+ if (string == (char *) NULL) {
+ return (char *) NULL;
+ }
+ while ((ch = *string++)) {
+ if (!isspace(ch)) {
+ break;
+ }
+ }
+ if (ch == '\0') {
+ return (char *) NULL;
+ }
+
+ token->type = token_string; /* the default type */
+ switch (ch) {
+ case '(':
+ token->type = token_lbrace;
+ return (string);
+ case ')':
+ token->type = token_rbrace;
+ return (string);
+ case '=':
+ token->type = token_eq;
+ return (string);
+ case '!':
+ if (*string == '=') {
+ token->type = token_ne;
+ return (string + 1);
+ }
+ else {
+ token->type = token_not;
+ return (string);
+ }
+ case '\'':
+ token->type = token_string;
+ qs = 1;
+ break;
+ case '|':
+ if (*string == '|') {
+ token->type = token_or;
+ return (string + 1);
+ }
+ break;
+ case '&':
+ if (*string == '&') {
+ token->type = token_and;
+ return (string + 1);
+ }
+ break;
+ case '>':
+ if (*string == '=') {
+ token->type = token_ge;
+ return (string + 1);
+ }
+ else {
+ token->type = token_gt;
+ return (string);
+ }
+ case '<':
+ if (*string == '=') {
+ token->type = token_le;
+ return (string + 1);
+ }
+ else {
+ token->type = token_lt;
+ return (string);
+ }
+ default:
+ token->type = token_string;
+ break;
+ }
+ /* We should only be here if we are in a string */
+ if (!qs) {
+ token->value[next++] = ch;
+ }
+
+ /*
+ * Yes I know that goto's are BAD. But, c doesn't allow me to
+ * exit a loop from a switch statement. Yes, I could use a flag,
+ * but that is (IMHO) even less readable/maintainable than the goto.
+ */
+ /*
+ * I used the ++string throughout this section so that string
+ * ends up pointing to the next token and I can just return it
+ */
+ for (ch = *string; ch != '\0'; ch = *++string) {
+ if (ch == '\\') {
+ if ((ch = *++string) == '\0') {
+ goto TOKEN_DONE;
+ }
+ token->value[next++] = ch;
+ continue;
+ }
+ if (!qs) {
+ if (isspace(ch)) {
+ goto TOKEN_DONE;
+ }
+ switch (ch) {
+ case '(':
+ goto TOKEN_DONE;
+ case ')':
+ goto TOKEN_DONE;
+ case '=':
+ goto TOKEN_DONE;
+ case '!':
+ goto TOKEN_DONE;
+ case '|':
+ if (*(string + 1) == '|') {
+ goto TOKEN_DONE;
+ }
+ break;
+ case '&':
+ if (*(string + 1) == '&') {
+ goto TOKEN_DONE;
+ }
+ break;
+ case '<':
+ goto TOKEN_DONE;
+ case '>':
+ goto TOKEN_DONE;
+ }
+ token->value[next++] = ch;
+ }
+ else {
+ if (ch == '\'') {
+ qs = 0;
+ ++string;
+ goto TOKEN_DONE;
+ }
+ token->value[next++] = ch;
+ }
+ }
+ TOKEN_DONE:
+ /* If qs is still set, I have an unmatched ' */
+ if (qs) {
+ rputs("\nUnmatched '\n", r);
+ next = 0;
+ }
+ token->value[next] = '\0';
+ return (string);
+}
+
+
+/*
+ * Hey I still know that goto's are BAD. I don't think that I've ever
+ * used two in the same project, let alone the same file before. But,
+ * I absolutely want to make sure that I clean up the memory in all
+ * cases. And, without rewriting this completely, the easiest way
+ * is to just branch to the return code which cleans it up.
+ */
+/* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1
+ * characters long...
+ */
+static int parse_expr(request_rec *r, const char *expr, const char *error)
+{
+ struct parse_node {
+ struct parse_node *left, *right, *parent;
+ struct token token;
+ int value, done;
+ } *root, *current, *new;
+ const char *parse;
+ char buffer[MAX_STRING_LEN];
+ pool *expr_pool;
+ int retval = 0;
+
+ if ((parse = expr) == (char *) NULL) {
+ return (0);
+ }
+ root = current = (struct parse_node *) NULL;
+ expr_pool = make_sub_pool(r->pool);
+
+ /* Create Parse Tree */
+ while (1) {
+ new = (struct parse_node *) palloc(expr_pool,
+ sizeof(struct parse_node));
+ new->parent = new->left = new->right = (struct parse_node *) NULL;
+ new->done = 0;
+ if ((parse = get_ptoken(r, parse, &new->token)) == (char *) NULL) {
+ break;
+ }
+ switch (new->token.type) {
+
+ case token_string:
+#ifdef DEBUG_INCLUDE
+ rvputs(r, " Token: string (", new->token.value, ")\n", NULL);
+#endif
+ if (current == (struct parse_node *) NULL) {
+ root = current = new;
+ break;
+ }
+ switch (current->token.type) {
+ case token_string:
+ if (current->token.value[0] != '\0') {
+ strncat(current->token.value, " ",
+ MAX_STRING_LEN - strlen(current->token.value) - 1);
+ }
+ strncat(current->token.value, new->token.value,
+ MAX_STRING_LEN - strlen(current->token.value) - 1);
+ current->token.value[sizeof(current->token.value) - 1] = '\0';
+ break;
+ case token_eq:
+ case token_ne:
+ case token_and:
+ case token_or:
+ case token_lbrace:
+ case token_not:
+ case token_ge:
+ case token_gt:
+ case token_le:
+ case token_lt:
+ new->parent = current;
+ current = current->right = new;
+ break;
+ default:
+ log_printf(r->server,
+ "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ break;
+
+ case token_and:
+ case token_or:
+#ifdef DEBUG_INCLUDE
+ rputs(" Token: and/or\n", r);
+#endif
+ if (current == (struct parse_node *) NULL) {
+ log_printf(r->server,
+ "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ /* Percolate upwards */
+ while (current != (struct parse_node *) NULL) {
+ switch (current->token.type) {
+ case token_string:
+ case token_group:
+ case token_not:
+ case token_eq:
+ case token_ne:
+ case token_and:
+ case token_or:
+ case token_ge:
+ case token_gt:
+ case token_le:
+ case token_lt:
+ current = current->parent;
+ continue;
+ case token_lbrace:
+ break;
+ default:
+ log_printf(r->server,
+ "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ break;
+ }
+ if (current == (struct parse_node *) NULL) {
+ new->left = root;
+ new->left->parent = new;
+ new->parent = (struct parse_node *) NULL;
+ root = new;
+ }
+ else {
+ new->left = current->right;
+ current->right = new;
+ new->parent = current;
+ }
+ current = new;
+ break;
+
+ case token_not:
+#ifdef DEBUG_INCLUDE
+ rputs(" Token: not\n", r);
+#endif
+ if (current == (struct parse_node *) NULL) {
+ root = current = new;
+ break;
+ }
+ /* Percolate upwards */
+ while (current != (struct parse_node *) NULL) {
+ switch (current->token.type) {
+ case token_not:
+ case token_eq:
+ case token_ne:
+ case token_and:
+ case token_or:
+ case token_lbrace:
+ case token_ge:
+ case token_gt:
+ case token_le:
+ case token_lt:
+ break;
+ default:
+ log_printf(r->server,
+ "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ break;
+ }
+ if (current == (struct parse_node *) NULL) {
+ new->left = root;
+ new->left->parent = new;
+ new->parent = (struct parse_node *) NULL;
+ root = new;
+ }
+ else {
+ new->left = current->right;
+ current->right = new;
+ new->parent = current;
+ }
+ current = new;
+ break;
+
+ case token_eq:
+ case token_ne:
+ case token_ge:
+ case token_gt:
+ case token_le:
+ case token_lt:
+#ifdef DEBUG_INCLUDE
+ rputs(" Token: eq/ne/ge/gt/le/lt\n", r);
+#endif
+ if (current == (struct parse_node *) NULL) {
+ log_printf(r->server,
+ "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ /* Percolate upwards */
+ while (current != (struct parse_node *) NULL) {
+ switch (current->token.type) {
+ case token_string:
+ case token_group:
+ current = current->parent;
+ continue;
+ case token_lbrace:
+ case token_and:
+ case token_or:
+ break;
+ case token_not:
+ case token_eq:
+ case token_ne:
+ case token_ge:
+ case token_gt:
+ case token_le:
+ case token_lt:
+ default:
+ log_printf(r->server,
+ "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ break;
+ }
+ if (current == (struct parse_node *) NULL) {
+ new->left = root;
+ new->left->parent = new;
+ new->parent = (struct parse_node *) NULL;
+ root = new;
+ }
+ else {
+ new->left = current->right;
+ current->right = new;
+ new->parent = current;
+ }
+ current = new;
+ break;
+
+ case token_rbrace:
+#ifdef DEBUG_INCLUDE
+ rputs(" Token: rbrace\n", r);
+#endif
+ while (current != (struct parse_node *) NULL) {
+ if (current->token.type == token_lbrace) {
+ current->token.type = token_group;
+ break;
+ }
+ current = current->parent;
+ }
+ if (current == (struct parse_node *) NULL) {
+ log_printf(r->server, "Unmatched ')' in \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ break;
+
+ case token_lbrace:
+#ifdef DEBUG_INCLUDE
+ rputs(" Token: lbrace\n", r);
+#endif
+ if (current == (struct parse_node *) NULL) {
+ root = current = new;
+ break;
+ }
+ /* Percolate upwards */
+ while (current != (struct parse_node *) NULL) {
+ switch (current->token.type) {
+ case token_not:
+ case token_eq:
+ case token_ne:
+ case token_and:
+ case token_or:
+ case token_lbrace:
+ case token_ge:
+ case token_gt:
+ case token_le:
+ case token_lt:
+ break;
+ case token_string:
+ case token_group:
+ default:
+ log_printf(r->server,
+ "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ break;
+ }
+ if (current == (struct parse_node *) NULL) {
+ new->left = root;
+ new->left->parent = new;
+ new->parent = (struct parse_node *) NULL;
+ root = new;
+ }
+ else {
+ new->left = current->right;
+ current->right = new;
+ new->parent = current;
+ }
+ current = new;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Evaluate Parse Tree */
+ current = root;
+ while (current != (struct parse_node *) NULL) {
+ switch (current->token.type) {
+ case token_string:
+#ifdef DEBUG_INCLUDE
+ rputs(" Evaluate string\n", r);
+#endif
+ parse_string(r, current->token.value, buffer, sizeof(buffer), 0);
+ safe_copy(current->token.value, buffer, sizeof(current->token.value));
+ current->value = (current->token.value[0] != '\0');
+ current->done = 1;
+ current = current->parent;
+ break;
+
+ case token_and:
+ case token_or:
+#ifdef DEBUG_INCLUDE
+ rputs(" Evaluate and/or\n", r);
+#endif
+ if (current->left == (struct parse_node *) NULL ||
+ current->right == (struct parse_node *) NULL) {
+ log_printf(r->server, "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ if (!current->left->done) {
+ switch (current->left->token.type) {
+ case token_string:
+ parse_string(r, current->left->token.value,
+ buffer, sizeof(buffer), 0);
+ safe_copy(current->left->token.value, buffer,
+ sizeof(current->left->token.value));
+ current->left->value = (current->left->token.value[0] != '\0');
+ current->left->done = 1;
+ break;
+ default:
+ current = current->left;
+ continue;
+ }
+ }
+ if (!current->right->done) {
+ switch (current->right->token.type) {
+ case token_string:
+ parse_string(r, current->right->token.value,
+ buffer, sizeof(buffer), 0);
+ safe_copy(current->right->token.value, buffer,
+ sizeof(current->right->token.value));
+ current->right->value = (current->right->token.value[0] != '\0');
+ current->right->done = 1;
+ break;
+ default:
+ current = current->right;
+ continue;
+ }
+ }
+#ifdef DEBUG_INCLUDE
+ rvputs(r, " Left: ", current->left->value ? "1" : "0",
+ "\n", NULL);
+ rvputs(r, " Right: ", current->right->value ? "1" : "0",
+ "\n", NULL);
+#endif
+ if (current->token.type == token_and) {
+ current->value = current->left->value && current->right->value;
+ }
+ else {
+ current->value = current->left->value || current->right->value;
+ }
+#ifdef DEBUG_INCLUDE
+ rvputs(r, " Returning ", current->value ? "1" : "0",
+ "\n", NULL);
+#endif
+ current->done = 1;
+ current = current->parent;
+ break;
+
+ case token_eq:
+ case token_ne:
+#ifdef DEBUG_INCLUDE
+ rputs(" Evaluate eq/ne\n", r);
+#endif
+ if ((current->left == (struct parse_node *) NULL) ||
+ (current->right == (struct parse_node *) NULL) ||
+ (current->left->token.type != token_string) ||
+ (current->right->token.type != token_string)) {
+ log_printf(r->server, "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ parse_string(r, current->left->token.value,
+ buffer, sizeof(buffer), 0);
+ safe_copy(current->left->token.value, buffer,
+ sizeof(current->left->token.value));
+ parse_string(r, current->right->token.value,
+ buffer, sizeof(buffer), 0);
+ safe_copy(current->right->token.value, buffer,
+ sizeof(current->right->token.value));
+ if (current->right->token.value[0] == '/') {
+ int len;
+ len = strlen(current->right->token.value);
+ if (current->right->token.value[len - 1] == '/') {
+ current->right->token.value[len - 1] = '\0';
+ }
+ else {
+ log_printf(r->server, "Invalid rexp \"%s\" in file %s",
+ current->right->token.value, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+#ifdef DEBUG_INCLUDE
+ rvputs(r, " Re Compare (", current->left->token.value,
+ ") with /", &current->right->token.value[1], "/\n", NULL);
+#endif
+ current->value =
+ re_check(r, current->left->token.value,
+ &current->right->token.value[1]);
+ }
+ else {
+#ifdef DEBUG_INCLUDE
+ rvputs(r, " Compare (", current->left->token.value,
+ ") with (", current->right->token.value, ")\n", NULL);
+#endif
+ current->value =
+ (strcmp(current->left->token.value,
+ current->right->token.value) == 0);
+ }
+ if (current->token.type == token_ne) {
+ current->value = !current->value;
+ }
+#ifdef DEBUG_INCLUDE
+ rvputs(r, " Returning ", current->value ? "1" : "0",
+ "\n", NULL);
+#endif
+ current->done = 1;
+ current = current->parent;
+ break;
+ case token_ge:
+ case token_gt:
+ case token_le:
+ case token_lt:
+#ifdef DEBUG_INCLUDE
+ rputs(" Evaluate ge/gt/le/lt\n", r);
+#endif
+ if ((current->left == (struct parse_node *) NULL) ||
+ (current->right == (struct parse_node *) NULL) ||
+ (current->left->token.type != token_string) ||
+ (current->right->token.type != token_string)) {
+ log_printf(r->server, "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+ }
+ parse_string(r, current->left->token.value,
+ buffer, sizeof(buffer), 0);
+ safe_copy(current->left->token.value, buffer,
+ sizeof(current->left->token.value));
+ parse_string(r, current->right->token.value,
+ buffer, sizeof(buffer), 0);
+ safe_copy(current->right->token.value, buffer,
+ sizeof(current->right->token.value));
+#ifdef DEBUG_INCLUDE
+ rvputs(r, " Compare (", current->left->token.value,
+ ") with (", current->right->token.value, ")\n", NULL);
+#endif
+ current->value =
+ strcmp(current->left->token.value,
+ current->right->token.value);
+ if (current->token.type == token_ge) {
+ current->value = current->value >= 0;
+ }
+ else if (current->token.type == token_gt) {
+ current->value = current->value > 0;
+ }
+ else if (current->token.type == token_le) {
+ current->value = current->value <= 0;
+ }
+ else if (current->token.type == token_lt) {
+ current->value = current->value < 0;
+ }
+ else {
+ current->value = 0; /* Don't return -1 if unknown token */
+ }
+#ifdef DEBUG_INCLUDE
+ rvputs(r, " Returning ", current->value ? "1" : "0",
+ "\n", NULL);
+#endif
+ current->done = 1;
+ current = current->parent;
+ break;
+
+ case token_not:
+ if (current->right != (struct parse_node *) NULL) {
+ if (!current->right->done) {
+ current = current->right;
+ continue;
+ }
+ current->value = !current->right->value;
+ }
+ else {
+ current->value = 0;
+ }
+#ifdef DEBUG_INCLUDE
+ rvputs(r, " Evaluate !: ", current->value ? "1" : "0",
+ "\n", NULL);
+#endif
+ current->done = 1;
+ current = current->parent;
+ break;
+
+ case token_group:
+ if (current->right != (struct parse_node *) NULL) {
+ if (!current->right->done) {
+ current = current->right;
+ continue;
+ }
+ current->value = current->right->value;
+ }
+ else {
+ current->value = 1;
+ }
+#ifdef DEBUG_INCLUDE
+ rvputs(r, " Evaluate (): ", current->value ? "1" : "0",
+ "\n", NULL);
+#endif
+ current->done = 1;
+ current = current->parent;
+ break;
+
+ case token_lbrace:
+ log_printf(r->server, "Unmatched '(' in \"%s\" in file %s",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+
+ case token_rbrace:
+ log_printf(r->server, "Unmatched ')' in \"%s\" in file %s\n",
+ expr, r->filename);
+ rputs(error, r);
+ goto RETURN;
+
+ default:
+ log_printf(r->server, "bad token type");
+ rputs(error, r);
+ goto RETURN;
+ }
+ }
+
+ retval = (root == (struct parse_node *) NULL) ? 0 : root->value;
+ RETURN:
+ destroy_pool(expr_pool);
+ return (retval);
+}
+
+static int handle_if(FILE *in, request_rec *r, const char *error,
+ int *conditional_status, int *printing)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ char *expr;
+
+ expr = NULL;
+ while (1) {
+ tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0);
+ if (*tag == '\0') {
+ return 1;
+ }
+ else if (!strcmp(tag, "done")) {
+ if (expr == NULL) {
+ log_printf(r->server, "missing expr in if statement: %s",
+ r->filename);
+ rputs(error, r);
+ return 1;
+ }
+ *printing = *conditional_status = parse_expr(r, expr, error);
+#ifdef DEBUG_INCLUDE
+ rvputs(r, "**** if conditional_status=\"",
+ *conditional_status ? "1" : "0", "\"\n", NULL);
+#endif
+ return 0;
+ }
+ else if (!strcmp(tag, "expr")) {
+ expr = tag_val;
+#ifdef DEBUG_INCLUDE
+ rvputs(r, "**** if expr=\"", expr, "\"\n", NULL);
+#endif
+ }
+ else {
+ log_printf(r->server, "unknown parameter \"%s\" to tag if in %s",
+ tag, r->filename);
+ rputs(error, r);
+ }
+ }
+}
+
+static int handle_elif(FILE *in, request_rec *r, const char *error,
+ int *conditional_status, int *printing)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ char *expr;
+
+ expr = NULL;
+ while (1) {
+ tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0);
+ if (*tag == '\0') {
+ return 1;
+ }
+ else if (!strcmp(tag, "done")) {
+#ifdef DEBUG_INCLUDE
+ rvputs(r, "**** elif conditional_status=\"",
+ *conditional_status ? "1" : "0", "\"\n", NULL);
+#endif
+ if (*conditional_status) {
+ *printing = 0;
+ return (0);
+ }
+ if (expr == NULL) {
+ log_printf(r->server, "missing expr in elif statement: %s",
+ r->filename);
+ rputs(error, r);
+ return 1;
+ }
+ *printing = *conditional_status = parse_expr(r, expr, error);
+#ifdef DEBUG_INCLUDE
+ rvputs(r, "**** elif conditional_status=\"",
+ *conditional_status ? "1" : "0", "\"\n", NULL);
+#endif
+ return 0;
+ }
+ else if (!strcmp(tag, "expr")) {
+ expr = tag_val;
+#ifdef DEBUG_INCLUDE
+ rvputs(r, "**** if expr=\"", expr, "\"\n", NULL);
+#endif
+ }
+ else {
+ log_printf(r->server, "unknown parameter \"%s\" to tag if in %s",
+ tag, r->filename);
+ rputs(error, r);
+ }
+ }
+}
+
+static int handle_else(FILE *in, request_rec *r, const char *error,
+ int *conditional_status, int *printing)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
+ return 1;
+ }
+ else if (!strcmp(tag, "done")) {
+#ifdef DEBUG_INCLUDE
+ rvputs(r, "**** else conditional_status=\"",
+ *conditional_status ? "1" : "0", "\"\n", NULL);
+#endif
+ *printing = !(*conditional_status);
+ *conditional_status = 1;
+ return 0;
+ }
+ else {
+ log_printf(r->server, "else directive does not take tags in %s",
+ r->filename);
+ if (*printing) {
+ rputs(error, r);
+ }
+ return -1;
+ }
+}
+
+static int handle_endif(FILE *in, request_rec *r, const char *error,
+ int *conditional_status, int *printing)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
+ return 1;
+ }
+ else if (!strcmp(tag, "done")) {
+#ifdef DEBUG_INCLUDE
+ rvputs(r, "**** endif conditional_status=\"",
+ *conditional_status ? "1" : "0", "\"\n", NULL);
+#endif
+ *printing = 1;
+ *conditional_status = 1;
+ return 0;
+ }
+ else {
+ log_printf(r->server, "endif directive does not take tags in %s",
+ r->filename);
+ rputs(error, r);
+ return -1;
+ }
+}
+
+static int handle_set(FILE *in, request_rec *r, const char *error)
+{
+ char tag[MAX_STRING_LEN];
+ char parsed_string[MAX_STRING_LEN];
+ char *tag_val;
+ char *var;
+
+ var = (char *) NULL;
+ while (1) {
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
+ return 1;
+ }
+ else if (!strcmp(tag, "done")) {
+ return 0;
+ }
+ else if (!strcmp(tag, "var")) {
+ var = tag_val;
+ }
+ else if (!strcmp(tag, "value")) {
+ if (var == (char *) NULL) {
+ log_printf(r->server,
+ "variable must precede value in set directive in %s",
+ r->filename);
+ rputs(error, r);
+ return -1;
+ }
+ parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+ table_set(r->subprocess_env, var, parsed_string);
+ }
+ else {
+ log_printf(r->server, "Invalid tag for set directive in %s",
+ r->filename);
+ rputs(error, r);
+ return -1;
+ }
+ }
+}
+
+static int handle_printenv(FILE *in, request_rec *r, const char *error)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ table_entry *elts = (table_entry *) r->subprocess_env->elts;
+ int i;
+
+ if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
+ return 1;
+ }
+ else if (!strcmp(tag, "done")) {
+ for (i = 0; i < r->subprocess_env->nelts; ++i) {
+ rvputs(r, elts[i].key, "=", elts[i].val, "\n", NULL);
+ }
+ return 0;
+ }
+ else {
+ log_printf(r->server, "printenv directive does not take tags in %s",
+ r->filename);
+ rputs(error, r);
+ return -1;
+ }
+}
+
+
+
+/* -------------------------- The main function --------------------------- */
+
+/* This is a stub which parses a file descriptor. */
+
+static void send_parsed_content(FILE *f, request_rec *r)
+{
+ char directive[MAX_STRING_LEN], error[MAX_STRING_LEN];
+ char timefmt[MAX_STRING_LEN];
+ int noexec = allow_options(r) & OPT_INCNOEXEC;
+ int ret, sizefmt;
+ int if_nesting;
+ int printing;
+ int conditional_status;
+
+ safe_copy(error, DEFAULT_ERROR_MSG, sizeof(error));
+ safe_copy(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt));
+ sizefmt = SIZEFMT_KMG;
+
+/* Turn printing on */
+ printing = conditional_status = 1;
+ if_nesting = 0;
+
+ chdir_file(r->filename);
+ if (r->args) { /* add QUERY stuff to env cause it ain't yet */
+ char *arg_copy = pstrdup(r->pool, r->args);
+
+ table_set(r->subprocess_env, "QUERY_STRING", r->args);
+ unescape_url(arg_copy);
+ table_set(r->subprocess_env, "QUERY_STRING_UNESCAPED",
+ escape_shell_cmd(r->pool, arg_copy));
+ }
+
+ while (1) {
+ if (!find_string(f, STARTING_SEQUENCE, r, printing)) {
+ if (get_directive(f, directive, sizeof(directive), r->pool)) {
+ log_printf(r->server,
+ "mod_include: error reading directive in %s",
+ r->filename);
+ rputs(error, r);
+ return;
+ }
+ if (!strcmp(directive, "if")) {
+ if (!printing) {
+ if_nesting++;
+ }
+ else {
+ ret = handle_if(f, r, error, &conditional_status,
+ &printing);
+ if_nesting = 0;
+ }
+ continue;
+ }
+ else if (!strcmp(directive, "else")) {
+ if (!if_nesting) {
+ ret = handle_else(f, r, error, &conditional_status,
+ &printing);
+ }
+ continue;
+ }
+ else if (!strcmp(directive, "elif")) {
+ if (!if_nesting) {
+ ret = handle_elif(f, r, error, &conditional_status,
+ &printing);
+ }
+ continue;
+ }
+ else if (!strcmp(directive, "endif")) {
+ if (!if_nesting) {
+ ret = handle_endif(f, r, error, &conditional_status,
+ &printing);
+ }
+ else {
+ if_nesting--;
+ }
+ continue;
+ }
+ if (!printing) {
+ continue;
+ }
+ if (!strcmp(directive, "exec")) {
+ if (noexec) {
+ log_printf(r->server,
+ "httpd: exec used but not allowed in %s",
+ r->filename);
+ if (printing) {
+ rputs(error, r);
+ }
+ ret = find_string(f, ENDING_SEQUENCE, r, 0);
+ }
+ else {
+ ret = handle_exec(f, r, error);
+ }
+ }
+ else if (!strcmp(directive, "config")) {
+ ret = handle_config(f, r, error, timefmt, &sizefmt);
+ }
+ else if (!strcmp(directive, "set")) {
+ ret = handle_set(f, r, error);
+ }
+ else if (!strcmp(directive, "include")) {
+ ret = handle_include(f, r, error, noexec);
+ }
+ else if (!strcmp(directive, "echo")) {
+ ret = handle_echo(f, r, error);
+ }
+ else if (!strcmp(directive, "fsize")) {
+ ret = handle_fsize(f, r, error, sizefmt);
+ }
+ else if (!strcmp(directive, "flastmod")) {
+ ret = handle_flastmod(f, r, error, timefmt);
+ }
+ else if (!strcmp(directive, "printenv")) {
+ ret = handle_printenv(f, r, error);
+ }
+#ifdef USE_PERL_SSI
+ else if (!strcmp(directive, "perl")) {
+ ret = handle_perl(f, r, error);
+ }
+#endif
+ else {
+ log_printf(r->server, "httpd: unknown directive \"%s\" "
+ "in parsed doc %s",
+ directive, r->filename);
+ if (printing) {
+ rputs(error, r);
+ }
+ ret = find_string(f, ENDING_SEQUENCE, r, 0);
+ }
+ 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
+
+static void *create_includes_dir_config(pool *p, char *dummy)
+{
+ enum xbithack *result = (enum xbithack *) palloc(p, sizeof(enum xbithack));
+ *result = DEFAULT_XBITHACK;
+ return result;
+}
+
+static const char *set_xbithack(cmd_parms *cmd, void *xbp, char *arg)
+{
+ enum xbithack *state = (enum xbithack *) xbp;
+
+ if (!strcasecmp(arg, "off")) {
+ *state = xbithack_off;
+ }
+ else if (!strcasecmp(arg, "on")) {
+ *state = xbithack_on;
+ }
+ else if (!strcasecmp(arg, "full")) {
+ *state = xbithack_full;
+ }
+ else {
+ return "XBitHack must be set to Off, On, or Full";
+ }
+
+ return NULL;
+}
+
+static int send_parsed_file(request_rec *r)
+{
+ FILE *f;
+ enum xbithack *state =
+ (enum xbithack *) get_module_config(r->per_dir_config, &includes_module);
+ int errstatus;
+
+ if (!(allow_options(r) & OPT_INCLUDES)) {
+ return DECLINED;
+ }
+ r->allowed |= (1 << M_GET);
+ if (r->method_number != M_GET) {
+ return DECLINED;
+ }
+ if (r->finfo.st_mode == 0) {
+ log_printf(r->server, "File does not exist: %s",
+ (r->path_info
+ ? pstrcat(r->pool, r->filename, r->path_info, NULL)
+ : r->filename));
+ return HTTP_NOT_FOUND;
+ }
+
+ if (!(f = pfopen(r->pool, r->filename, "r"))) {
+ log_printf(r->server,
+ "file permissions deny server access: %s", r->filename);
+ return HTTP_FORBIDDEN;
+ }
+
+ 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);
+ table_unset(r->headers_out, "ETag");
+ if (errstatus) {
+ return errstatus;
+ }
+ }
+
+ send_http_header(r);
+
+ if (r->header_only) {
+ 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 <!--#config timefmt-->
+ */
+ 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);
+ }
+ hard_timeout("send SSI", r);
+
+ send_parsed_content(f, r);
+
+ kill_timeout(r);
+ return OK;
+}
+
+static int send_shtml_file(request_rec *r)
+{
+ r->content_type = "text/html";
+ return send_parsed_file(r);
+}
+
+static int xbithack_handler(request_rec *r)
+{
+#ifdef __EMX__
+ /* OS/2 dosen't currently support the xbithack. This is being worked on. */
+ return DECLINED;
+#else
+ enum xbithack *state;
+
+ if (!(r->finfo.st_mode & S_IXUSR)) {
+ return DECLINED;
+ }
+
+ state = (enum xbithack *) get_module_config(r->per_dir_config,
+ &includes_module);
+
+ if (*state == xbithack_off) {
+ return DECLINED;
+ }
+ return send_parsed_file(r);
+#endif
+}
+
+static command_rec includes_cmds[] =
+{
+ {"XBitHack", set_xbithack, NULL, OR_OPTIONS, TAKE1, "Off, On, or Full"},
+ {NULL}
+};
+
+static 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_info.c b/usr.sbin/httpd/src/mod_info.c
new file mode 100644
index 00000000000..9aedbec2cd5
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_info.c
@@ -0,0 +1,455 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * Info Module. Display configuration information for the server and
+ * all included modules.
+ *
+ * <Location /server-info>
+ * SetHandler server-info
+ * </Location>
+ *
+ * GET /server-info - Returns full configuration page for server and all modules
+ * GET /server-info?server - Returns server configuration only
+ * GET /server-info?module_name - Returns configuration for a single module
+ * GET /server-info?list - Returns quick list of included modules
+ *
+ * Rasmus Lerdorf <rasmus@vex.net>, May 1996
+ *
+ * 05.01.96 Initial Version
+ *
+ */
+
+#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[256]; /* What is the max size of a command? */
+ char *end_ret;
+
+ ret[0]='\0';
+ s = string;
+ t=ret;
+ end_ret = t + sizeof(ret);
+ while((*s) && ((t-ret) < sizeof(ret))) {
+ if(*s=='<') {
+ strncpy(t,"&lt;", end_ret - t);
+ t+=4;
+ } else if(*s=='>') {
+ strncpy(t,"&gt;", end_ret - t);
+ t+=4;
+ } else if(*s=='&') {
+ strncpy(t,"&amp;", end_ret - t);
+ t+=5;
+ } else {
+ *t++=*s;
+ }
+ s++;
+ }
+ *t='\0';
+ return(ret);
+}
+
+mod_info_config_lines *mod_info_load_config(pool *p, char *filename, request_rec *r) {
+ char s[MAX_STRING_LEN];
+ FILE *fp;
+ mod_info_config_lines *new, *ret=NULL, *prev=NULL;
+ char *t,*tt,o, *msg;
+
+ fp = pfopen(p,filename,"r");
+ if(!fp) {
+ msg = pstrcat
+ (
+ r->pool,
+ "mod_info: couldn't open config file ",
+ filename,
+ NULL
+ );
+ log_error (msg, r->server);
+ 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 = (t<tt)?t:tt;
+ else if(tt) t=tt;
+ if(t) {
+ o=*t;
+ *t='\0';
+ new->cmd = 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,"<directory",10) ||
+ !strncasecmp(li->cmd,"<location",9) ||
+ !strncasecmp(li->cmd,"<limit",6) ||
+ !strncasecmp(li->cmd,"<files",6)) {
+ if(nest) li_se=li;
+ else li_st=li;
+ li=li->next;
+ nest++;
+ continue;
+ } else if(nest && (!strncasecmp(li->cmd,"</limit",7) ||
+ !strncasecmp(li->cmd,"</location",10) ||
+ !strncasecmp(li->cmd,"</directory",11) ||
+ !strncasecmp(li->cmd,"</files",7))) {
+ if(block_start) {
+ if((nest==1 && block_start==li_st) || (nest==2 && block_start==li_se)) {
+ rputs("<dd><tt>",r);
+ if(nest==2) rputs("&nbsp;&nbsp;",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("</tt>\n",r);
+ nest--;
+ if(!nest) {
+ block_start=NULL;
+ li_st=NULL;
+ } else {
+ block_start=li_st;
+ }
+ li_se=NULL;
+ } else {
+ nest--;
+ if(!nest) {
+ li_st=NULL;
+ }
+ li_se=NULL;
+ }
+ } else {
+ nest--;
+ if(!nest) {
+ li_st=NULL;
+ }
+ li_se=NULL;
+ }
+ li=li->next;
+ continue;
+ }
+ cmd = cmds;
+ while(cmd) {
+ if(cmd->name) {
+ if(!strcasecmp(cmd->name,li->cmd)) {
+ if(!lab) {
+ rputs("<dt><strong>",r);
+ rputs(label,r);
+ rputs("</strong>\n",r);
+ lab=1;
+ }
+ if(((nest && block_start==NULL) || (nest==2 && block_start==li_st))
+ && (strncasecmp(li->cmd,"<directory",10) &&
+ strncasecmp(li->cmd,"<location",9) && strncasecmp(li->cmd,"<limit",6) &&
+ strncasecmp(li->cmd,"</limit",7) && strncasecmp(li->cmd,"</location",10) &&
+ strncasecmp(li->cmd,"</directory",11) &&
+ strncasecmp(li->cmd,"</files",7))) {
+ rputs("<dd><tt>",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("</tt>\n",r);
+ block_start=li_st;
+ if(li_se) {
+ rputs("<dd><tt>&nbsp;&nbsp;",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("</tt>\n",r);
+ block_start=li_se;
+ }
+ }
+ rputs("<dd><tt>",r);
+ if(nest) rputs("&nbsp;&nbsp;",r);
+ if(nest==2) rputs("&nbsp;&nbsp;",r);
+ rputs(mod_info_html_cmd_string(li->cmd),r);
+ if(li->line) {
+ rputs(" <i>",r);
+ rputs(mod_info_html_cmd_string(li->line),r);
+ rputs("</i></tt>",r);
+ }
+ }
+ } else break;
+ cmd++;
+ }
+ li = li->next;
+ }
+}
+
+int display_info(request_rec *r) {
+ module *modp = NULL;
+ char buf[512], *cfname;
+ 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];
+
+ r->allowed |= (1 << M_GET);
+ if (r->method_number != M_GET)
+ return DECLINED;
+
+ r->content_type = "text/html";
+ send_http_header(r);
+ if(r->header_only) {
+ return 0;
+ }
+ hard_timeout("send server info", r);
+
+ rputs("<html><head><title>Server Information</title></head>\n",r);
+ rputs("<body><h1 align=center>Apache Server Information</h1>\n",r);
+ if(!r->args || strcasecmp(r->args,"list")) {
+ cfname = server_root_relative (r->pool, server_confname);
+ mod_info_cfg_httpd = mod_info_load_config (r->pool, cfname, r);
+ cfname = server_root_relative (r->pool, serv->srm_confname);
+ mod_info_cfg_srm = mod_info_load_config(r->pool, cfname, r);
+ cfname = server_root_relative (r->pool, serv->access_confname);
+ mod_info_cfg_access = mod_info_load_config (r->pool, cfname, r);
+ if(!r->args) {
+ rputs("<tt><a href=\"#server\">Server Settings</a>, ",r);
+ for(modp = top_module; modp; modp = modp->next) {
+ ap_snprintf(buf, sizeof(buf), "<a href=\"#%s\">%s</a>",modp->name,modp->name);
+ rputs(buf, r);
+ if(modp->next) rputs(", ",r);
+ }
+ rputs("</tt><hr>",r);
+
+ }
+ if(!r->args || !strcasecmp(r->args,"server")) {
+ ap_snprintf(buf, sizeof(buf), "<a name=\"server\"><strong>Server Version:</strong> <font size=+1><tt>%s</tt></a></font><br>\n",SERVER_VERSION);
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>API Version:</strong> <tt>%d</tt><br>\n",MODULE_MAGIC_NUMBER);
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>Run Mode:</strong> <tt>%s</tt><br>\n",standalone?"standalone":"inetd");
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>User/Group:</strong> <tt>%s(%d)/%d</tt><br>\n",user_name,(int)user_id,(int)group_id);
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>Hostname/port:</strong> <tt>%s:%u</tt><br>\n",serv->server_hostname,serv->port);
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>Daemons:</strong> <tt>start: %d &nbsp;&nbsp; min idle: %d &nbsp;&nbsp; max idle: %d &nbsp;&nbsp; max: %d</tt><br>\n",daemons_to_start,daemons_min_free,daemons_max_free,daemons_limit);
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>Max Requests:</strong> <tt>per child: %d &nbsp;&nbsp; keep alive: %s &nbsp;&nbsp; max per connection: %d</tt><br>\n",max_requests_per_child,serv->keep_alive ? "on":"off", serv->keep_alive_max);
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>Timeouts:</strong> <tt>connection: %d &nbsp;&nbsp; keep-alive: %d</tt><br>",serv->timeout,serv->keep_alive_timeout);
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>Server Root:</strong> <tt>%s</tt><br>\n",server_root);
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>Config File:</strong> <tt>%s</tt><br>\n",server_confname);
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>PID File:</strong> <tt>%s</tt><br>\n",pid_fname);
+ rputs(buf,r);
+ ap_snprintf(buf, sizeof(buf), "<strong>Scoreboard File:</strong> <tt>%s</tt><br>\n",scoreboard_fname);
+ rputs(buf,r);
+ }
+ rputs("<hr><dl>",r);
+ for(modp = top_module; modp; modp = modp->next) {
+ if(!r->args || !strcasecmp(modp->name,r->args)) {
+ ap_snprintf(buf, sizeof(buf), "<dt><a name=\"%s\"><strong>Module Name:</strong> <font size=+1><tt>%s</tt></a></font>\n",modp->name,modp->name);
+ rputs(buf,r);
+ rputs("<dt><strong>Content-types affected:</strong>",r);
+ hand = modp->handlers;
+ if(hand) {
+ while(hand) {
+ if(hand->content_type) {
+ ap_snprintf(buf, sizeof(buf), " <tt>%s</tt>\n",hand->content_type);
+ rputs(buf,r);
+ } else break;
+ hand++;
+ if(hand && hand->content_type) rputs(",",r);
+ }
+ } else {
+ rputs("<tt> none</tt>",r);
+ }
+ rputs("<dt><strong>Module Groups:</strong> \n",r);
+ if(modp->translate_handler) {
+ rputs("<tt>Translate Handler</tt>\n",r);
+ comma=1;
+ }
+ if(modp->check_user_id) {
+ if(comma) rputs(", ",r);
+ rputs("<tt>User ID Checking</tt>\n",r);
+ comma=1;
+ }
+ if(modp->auth_checker) {
+ if(comma) rputs(", ",r);
+ rputs("<tt>Authentication Checking</tt>\n",r);
+ comma=1;
+ }
+ if(modp->access_checker) {
+ if(comma) rputs(", ",r);
+ rputs("<tt>Access Checking</tt>\n",r);
+ comma=1;
+ }
+ if(modp->type_checker) {
+ if(comma) rputs(", ",r);
+ rputs("<tt>Type Checking</tt>\n",r);
+ comma=1;
+ }
+ if(modp->fixer_upper) {
+ if(comma) rputs(", ",r);
+ rputs("<tt>Header Fixer</tt>\n",r);
+ comma=1;
+ }
+ if(modp->logger) {
+ if(comma) rputs(", ",r);
+ rputs("<tt>Logging</tt>\n",r);
+ comma=1;
+ }
+ if(!comma) rputs("<tt> none</tt>",r);
+ comma=0;
+ rputs("<dt><strong>Module Configuration Commands:</strong> ",r);
+ cmd = modp->cmds;
+ if(cmd) {
+ while(cmd) {
+ if(cmd->name) {
+ ap_snprintf(buf, sizeof(buf), "<dd><tt>%s - <i>",mod_info_html_cmd_string(cmd->name));
+ rputs(buf,r);
+ if(cmd->errmsg) rputs(cmd->errmsg,r);
+ rputs("</i></tt>\n",r);
+ } else break;
+ cmd++;
+ }
+ rputs("<dt><strong>Current Configuration:</strong>\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("<tt> none</tt>\n",r);
+ }
+ rputs("<dt><hr>\n",r);
+ if(r->args) break;
+ }
+ }
+ if(!modp && r->args && strcasecmp(r->args,"server")) rputs("<b>No such module</b>\n",r);
+ } else {
+ for(modp = top_module; modp; modp = modp->next) {
+ rputs(modp->name,r);
+ if(modp->next) rputs("<br>",r);
+ }
+ }
+ rputs("</dl></body></html>\n",r);
+ /* Done, turn off timeout, close file and return */
+ kill_timeout(r);
+ 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_log_agent.c b/usr.sbin/httpd/src/mod_log_agent.c
new file mode 100644
index 00000000000..234de33ab24
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_log_agent.c
@@ -0,0 +1,198 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 "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;
+}
+
+const 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);
+#ifdef __EMX__
+ /* For OS/2 we need to use a '/' */
+ execl (SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+#else
+ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+ perror ("exec");
+ fprintf (stderr, "Exec of shell for logging failed!!!\n");
+ 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;
+
+ if (!spawn_child (p, agent_log_child, (void *)(cls->fname+1),
+ kill_after_timeout, &dummy, NULL)) {
+ perror ("spawn_child");
+ 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) {
+ perror("open");
+ fprintf(stderr,"httpd: could not open agent log file %s.\n", fname);
+ 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)
+ {
+ ap_snprintf(str, sizeof(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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_log_config.c b/usr.sbin/httpd/src/mod_log_config.c
new file mode 100644
index 00000000000..d383cd5f6bc
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_log_config.c
@@ -0,0 +1,787 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * This is module implements the TransferLog directive (same as the
+ * common log module), and additional directives, LogFormat and CustomLog.
+ *
+ *
+ * Syntax:
+ *
+ * TransferLog fn Logs transfers to fn in standard log format, unless
+ * a custom format is set with LogFormat
+ * LogFormat format Set a log format from TransferLog files
+ * CustomLog fn format
+ * Log to file fn with format given by the format
+ * argument
+ *
+ * CookieLog fn For backwards compatability with old Cookie
+ * logging module - now deprecated.
+ *
+ * There can be any number of TransferLog and CustomLog
+ * commands. Each request will be logged to _ALL_ the
+ * named files, in the appropriate format.
+ *
+ * If no TransferLog or CustomLog directive appears in a VirtualHost,
+ * the request will be logged to the log file(s) defined outside
+ * the virtual host section. If a TransferLog or CustomLog directive
+ * appears in the VirtualHost section, the log files defined outside
+ * the VirtualHost will _not_ be used. This makes this module compatable
+ * with the CLF and config log modules, where the use of TransferLog
+ * inside the VirtualHost section overrides its use outside.
+ *
+ * Examples:
+ *
+ * TransferLog logs/access_log
+ * <VirtualHost>
+ * LogFormat "... custom format ..."
+ * TransferLog log/virtual_only
+ * CustomLog log/virtual_useragents "%t %{user-agent}i"
+ * </VirtualHost>
+ *
+ * This will log using CLF to access_log any requests handled by the
+ * main server, while any requests to the virtual host will be logged
+ * with the "... custom format..." to virtual_only _AND_ using
+ * the custom user-agent log to virtual_useragents.
+ *
+ * Note that the NCSA referer and user-agent logs are easily added with
+ * CustomLog:
+ * CustomLog logs/referer "%{referer}i -> %U"
+ * CustomLog logs/agent "%{user-agent}i"
+ *
+ * Except: no RefererIgnore functionality
+ * logs '-' if no Referer or User-Agent instead of nothing
+ *
+ * But using this method allows much easier modification of the
+ * log format, e.g. to log hosts along with UA:
+ * CustomLog logs/referer "%{referer}i %U %h"
+ *
+ * The argument to LogFormat and CustomLog is a string, which can include
+ * literal characters copied into the log files, and '%' directives as
+ * follows:
+ *
+ * %...b: bytes sent, excluding HTTP headers.
+ * %...{FOOBAR}e: The contents of the environment variable FOOBAR
+ * %...f: filename
+ * %...h: remote host
+ * %...{Foobar}i: The contents of Foobar: header line(s) in the request
+ * sent to the client.
+ * %...l: remote logname (from identd, if supplied)
+ * %...{Foobar}n: The contents of note "Foobar" from another module.
+ * %...{Foobar}o: The contents of Foobar: header line(s) in the reply.
+ * %...p: the port the request was served to
+ * %...P: the process ID of the child that serviced the request.
+ * %...r: first line of request
+ * %...s: status. For requests that got internally redirected, this
+ * is status of the *original* request --- %...>s for the last.
+ * %...t: time, in common log format time format
+ * %...{format}t: The time, in the form given by format, which should
+ * be in strftime(3) format.
+ * %...T: the time taken to serve the request, in seconds.
+ * %...u: remote user (from auth; may be bogus if return status (%s) is 401)
+ * %...U: the URL path requested.
+ * %...v: the name of the server (i.e. which virtual host?)
+ *
+ * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
+ * indicate conditions for inclusion of the item (which will cause it
+ * to be replaced with '-' if the condition is not met). Note that
+ * there is no escaping performed on the strings from %r, %...i and
+ * %...o; some with long memories may remember that I thought this was
+ * a bad idea, once upon a time, and I'm still not comfortable with
+ * it, but it is difficult to see how to "do the right thing" with all
+ * of '%..i', unless we URL-escape everything and break with CLF.
+ *
+ * The forms of condition are a list of HTTP status codes, which may
+ * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs
+ * User-agent: on 400 errors and 501 errors (Bad Request, Not
+ * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
+ * requests which did *not* return some sort of normal status.
+ *
+ * The default LogFormat reproduces CLF; see below.
+ *
+ * The way this is supposed to work with virtual hosts is as follows:
+ * a virtual host can have its own LogFormat, or its own TransferLog.
+ * If it doesn't have its own LogFormat, it inherits from the main
+ * server. If it doesn't have its own TransferLog, it writes to the
+ * same descriptor (meaning the same process for "| ...").
+ *
+ * --- rst */
+
+#define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h" /* For REMOTE_NAME */
+
+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
+
+/*
+ * multi_log_state is our per-(virtual)-server configuration. We store
+ * an array of the logs we are going to use, each of type config_log_state.
+ * If a default log format is given by LogFormat, store in default_format
+ * (backward compat. with mod_log_config). We also store a pointer to
+ * the logs specified for the main server for virtual servers, so that
+ * if this vhost has now logs defined, we can use the main server's
+ * logs instead.
+ *
+ * So, for the main server, config_logs contains a list of the log files
+ * and server_config_logs in empty. For a vhost, server_config_logs
+ * points to the same array as config_logs in the main server, and
+ * config_logs points to the array of logs defined inside this vhost,
+ * which might be empty.
+ */
+
+typedef struct {
+ array_header *default_format;
+ array_header *config_logs;
+ array_header *server_config_logs;
+} multi_log_state;
+
+/*
+ * config_log_state holds the status of a single log file. fname cannot
+ * be NULL. format might be NULL, in which case the default_format from
+ * the multi_log_state should be used, or if that is NULL as well, use
+ * the CLF. log_fd is -1 before the log file is opened and set to a valid
+ * fd after it is opened.
+ */
+
+typedef struct {
+ char *fname;
+ 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];
+ ap_snprintf (dummy, sizeof(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) {
+ char *rvalue = r->connection->user;
+
+ if (rvalue == NULL) {
+ rvalue = "-";
+ } else if (strlen (rvalue) == 0) {
+ rvalue = "\"\"";
+ }
+ return rvalue;
+}
+
+char *log_request_line (request_rec *r, char *a)
+{ return r->the_request; }
+
+char *log_request_file (request_rec *r, char *a)
+{ return r->filename; }
+char *log_request_uri (request_rec *r, char *a)
+{ return r->uri; }
+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);
+ ap_snprintf(dummy, sizeof(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 (!strcasecmp(a, "Content-type") && r->content_type)
+ cp = r->content_type;
+ if (cp) return cp;
+ return table_get (r->err_headers_out, a);
+}
+
+char *log_note (request_rec *r, char *a)
+{ return table_get (r->notes, 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)
+{
+ int timz;
+ struct tm *t;
+ char tstr[MAX_STRING_LEN];
+
+ t = get_gmtoff(&timz);
+
+ if (a && *a) /* Custom format */
+ strftime(tstr, MAX_STRING_LEN, a, t);
+ else { /* CLF format */
+ char sign = (timz < 0 ? '-' : '+');
+
+ if(timz < 0) timz = -timz;
+
+ strftime(tstr,MAX_STRING_LEN,"[%d/%b/%Y:%H:%M:%S ",t);
+ ap_snprintf (tstr + strlen(tstr), sizeof(tstr)-strlen(tstr),
+ "%c%.2d%.2d]", sign, timz/60, timz%60);
+ }
+
+ return pstrdup (r->pool, tstr);
+}
+
+char *log_request_duration (request_rec *r, char *a) {
+ char duration[22]; /* Long enough for 2^64 */
+
+ ap_snprintf(duration, sizeof(duration), "%ld", time(NULL) - r->request_time);
+ return pstrdup(r->pool, duration);
+}
+
+char *log_virtual_host (request_rec *r, char *a) {
+ return pstrdup(r->pool, r->server->server_hostname);
+}
+
+char *log_server_port (request_rec *r, char *a) {
+ char portnum[22];
+
+ ap_snprintf(portnum, sizeof(portnum), "%u", r->server->port);
+ return pstrdup(r->pool, portnum);
+}
+
+char *log_child_pid (request_rec *r, char *a) {
+ char pidnum[22];
+ ap_snprintf(pidnum, sizeof(pidnum), "%ld", (long)getpid());
+ return pstrdup(r->pool, pidnum);
+}
+/*****************************************************************
+ *
+ * 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 },
+ { 'T', log_request_duration, 1 },
+ { 'r', log_request_line, 1 },
+ { 'f', log_request_file, 0 },
+ { 'U', log_request_uri, 1 },
+ { 's', log_status, 1 },
+ { 'b', log_bytes_sent, 0 },
+ { 'i', log_header_in, 0 },
+ { 'o', log_header_out, 0 },
+ { 'n', log_note, 0 },
+ { 'e', log_env_var, 0 },
+ { 'v', log_virtual_host, 0 },
+ { 'p', log_server_port, 0 },
+ { 'P', log_child_pid, 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, const char *start, const 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, const char **sa)
+{
+ const 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, const char **sa)
+{
+ const char *s = *sa;
+ if (*s != '%') return parse_log_misc_string (p, it, sa);
+
+ ++s;
+ it->condition_sense = 0;
+ it->conditions = NULL;
+ it->want_orig = -1;
+ it->arg = ""; /* For safety's sake... */
+
+ while (*s) {
+ int i;
+ struct log_item_list *l;
+
+ switch (*s) {
+ case '!':
+ ++s;
+ it->condition_sense = !it->condition_sense;
+ break;
+
+ case '<':
+ ++s;
+ it->want_orig = 1;
+ break;
+
+ case '>':
+ ++s;
+ it->want_orig = 0;
+ break;
+
+ case ',':
+ ++s;
+ break;
+
+ case '{':
+ ++s;
+ it->arg = 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, const char *s, const 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,
+ array_header *default_format) {
+ array_header *strsa;
+ log_format_item *items;
+ char *str, **strs, *s;
+ request_rec *orig;
+ int i;
+ int len = 0;
+ array_header *format;
+
+ format = cls->format ? cls->format : default_format;
+
+ strsa= make_array(r->pool, format->nelts,sizeof(char*));
+ items = (log_format_item *)format->elts;
+
+ orig = r;
+ while (orig->prev) orig = orig->prev;
+ while (r->next) r = r->next;
+
+ for (i = 0; i < format->nelts; ++i)
+ *((char**)push_array (strsa)) = process_item (r, orig, &items[i]);
+
+ strs = (char **)strsa->elts;
+
+ for (i = 0; i < format->nelts; ++i)
+ len += strlen (strs[i]);
+
+ str = palloc (r->pool, len + 1);
+
+ for (i = 0, s = str; i < format->nelts; ++i) {
+ strcpy (s, strs[i]);
+ s += strlen (strs[i]);
+ }
+
+ write(cls->log_fd, str, strlen(str));
+
+ return OK;
+}
+
+int multi_log_transaction(request_rec *r)
+{
+ multi_log_state *mls = get_module_config (r->server->module_config,
+ &config_log_module);
+ config_log_state *clsarray;
+ int i;
+
+ if (mls->config_logs->nelts) {
+ clsarray = (config_log_state *)mls->config_logs->elts;
+ for (i = 0; i < mls->config_logs->nelts; ++i) {
+ config_log_state *cls = &clsarray[i];
+
+ config_log_transaction(r, cls, mls->default_format);
+ }
+ }
+ else if (mls->server_config_logs) {
+ clsarray = (config_log_state *)mls->server_config_logs->elts;
+ for (i = 0; i < mls->server_config_logs->nelts; ++i) {
+ config_log_state *cls = &clsarray[i];
+
+ config_log_transaction(r, cls, mls->default_format);
+ }
+ }
+
+ return OK;
+}
+
+/*****************************************************************
+ *
+ * Module glue...
+ */
+
+void *make_config_log_state (pool *p, server_rec *s)
+{
+ multi_log_state *mls =
+ (multi_log_state *)palloc(p, sizeof (multi_log_state));
+
+ mls->config_logs =
+ make_array(p, 5, sizeof (config_log_state));
+ mls->default_format = NULL;
+ mls->server_config_logs = NULL;
+
+ return mls;
+}
+
+/*
+ * Use the merger to simply add a pointer from the vhost log state
+ * to the log of logs specified for the non-vhost configuration
+ */
+
+void *merge_config_log_state (pool *p, void *basev, void *addv)
+{
+ multi_log_state *base = (multi_log_state *)basev;
+ multi_log_state *add = (multi_log_state *)addv;
+
+ add->server_config_logs = base->config_logs;
+ if (!add->default_format)
+ add->default_format = base->default_format;
+
+ return add;
+}
+
+const char *log_format (cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err_string = NULL;
+ multi_log_state *mls = get_module_config (cmd->server->module_config,
+ &config_log_module);
+
+ mls->default_format = parse_log_string (cmd->pool, arg, &err_string);
+ return err_string;
+}
+
+const char *add_custom_log(cmd_parms *cmd, void *dummy, char *fn, char *fmt)
+{
+ const char *err_string = NULL;
+ multi_log_state *mls = get_module_config (cmd->server->module_config,
+ &config_log_module);
+ config_log_state *cls;
+
+ cls = (config_log_state*)push_array(mls->config_logs);
+ cls->fname = fn;
+ if (!fmt)
+ cls->format = NULL;
+ else
+ cls->format = parse_log_string (cmd->pool, fmt, &err_string);
+ cls->log_fd = -1;
+
+ return err_string;
+}
+
+const char *set_transfer_log(cmd_parms *cmd, void *dummy, char *fn)
+{
+ return add_custom_log(cmd, dummy, fn, NULL);
+}
+
+const char *set_cookie_log(cmd_parms *cmd, void *dummy, char *fn)
+{
+ return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t");
+}
+
+command_rec config_log_cmds[] = {
+{ "CustomLog", add_custom_log, NULL, RSRC_CONF, TAKE2,
+ "a file name and a custom log format string" },
+{ "TransferLog", set_transfer_log, NULL, RSRC_CONF, TAKE1,
+ "the filename of the access log" },
+{ "LogFormat", log_format, NULL, RSRC_CONF, TAKE1,
+ "a log format string (see docs)" },
+{ "CookieLog", set_cookie_log, NULL, RSRC_CONF, TAKE1,
+ "the filename of the cookie log" },
+{ 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);
+#ifdef __EMX__
+ /* For OS/2 we need to use a '/' */
+ execl (SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+#else
+ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+ perror ("exec");
+ 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 *cls,
+ array_header *default_format) {
+ if (cls->log_fd > 0) return cls; /* virtual config shared w/main server */
+
+ if (*cls->fname == '|') {
+ FILE *dummy;
+
+ if (!spawn_child (p, config_log_child, (void *)(cls->fname+1),
+ kill_after_timeout, &dummy, NULL)) {
+ perror ("spawn_child");
+ 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) {
+ perror("open");
+ fprintf (stderr,
+ "httpd: could not open transfer log file %s.\n", fname);
+ exit(1);
+ }
+ }
+
+ return cls;
+}
+
+config_log_state *open_multi_logs (server_rec *s, pool *p)
+{
+ int i;
+ multi_log_state *mls = get_module_config(s->module_config,
+ &config_log_module);
+ config_log_state *clsarray;
+ const char *dummy;
+
+ if (!mls->default_format)
+ mls->default_format = parse_log_string (p, DEFAULT_LOG_FORMAT, &dummy);
+
+ if (mls->config_logs->nelts) {
+ clsarray = (config_log_state *)mls->config_logs->elts;
+ for (i = 0; i < mls->config_logs->nelts; ++i) {
+ config_log_state *cls = &clsarray[i];
+
+ cls = open_config_log(s, p, cls, mls->default_format);
+ }
+ }
+ else if (mls->server_config_logs) {
+ clsarray = (config_log_state *)mls->server_config_logs->elts;
+ for (i = 0; i < mls->server_config_logs->nelts; ++i) {
+ config_log_state *cls = &clsarray[i];
+
+ cls = open_config_log(s, p, cls, mls->default_format);
+ }
+ }
+
+ return NULL;
+}
+
+void init_config_log (server_rec *s, pool *p)
+{
+ /* First, do "physical" server, which gets default log fd and format
+ * for the virtual servers, if they don't override...
+ */
+
+ open_multi_logs (s, p);
+
+ /* Then, virtual servers */
+
+ for (s = s->next; s; s = s->next) open_multi_logs (s, p);
+}
+
+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 */
+ merge_config_log_state, /* merge server config */
+ config_log_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ multi_log_transaction, /* logger */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_log_referer.c b/usr.sbin/httpd/src/mod_log_referer.c
new file mode 100644
index 00000000000..e494298944c
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_log_referer.c
@@ -0,0 +1,235 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 "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;
+}
+
+const 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;
+}
+
+const 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);
+#ifdef __EMX__
+ /* For OS/2 we need to use a '/' */
+ execl (SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+#else
+ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+ perror ("execl");
+ 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;
+
+ if (!spawn_child (p, referer_log_child, (void *)(cls->fname+1),
+ kill_after_timeout, &dummy, NULL)) {
+ perror ("spawn_child");
+ 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) {
+ perror("open");
+ fprintf(stderr,"httpd: could not open referer log file %s.\n", fname);
+ 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_mime.c b/usr.sbin/httpd/src/mod_mime.c
new file mode 100644
index 00000000000..dc84975e1ae
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_mime.c
@@ -0,0 +1,324 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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_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;
+}
+
+const 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;
+}
+
+const 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;
+}
+
+const 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;
+}
+
+const 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...
+ */
+
+const 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"))) {
+ perror("fopen");
+ fprintf(stderr,"httpd: could not open mime types file %s\n",
+ types_confname);
+ exit(1);
+ }
+
+ for(x=0;x<27;x++)
+ hash_buckets[x] = make_table (p, 10);
+
+ while(!(cfg_getline(l,MAX_STRING_LEN,f))) {
+ const 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)
+{
+ const 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))) {
+ char **new;
+
+ r->content_language = type; /* back compat. only */
+ if (!r->content_languages)
+ r->content_languages = make_array (r->pool, 2, sizeof(char*));
+ new = (char **)push_array (r->content_languages);
+ *new = 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_languages = 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_negotiation.c b/usr.sbin/httpd/src/mod_negotiation.c
new file mode 100644
index 00000000000..84e15f0ec4e
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_negotiation.c
@@ -0,0 +1,2052 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * 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"
+#include "util_script.h"
+
+/* define TCN_02 to allow for Holtman I-D transparent negotiation.
+ * This file currently implements the draft-02, except for
+ * anything to do with features and cache-control (max-age etc)
+ *
+ * Since the draft is just that, and we don't yet implement
+ * everything, regard the transparent negotiation stuff as experimental.
+ */
+/*#define TCN_02*/
+
+/* Commands --- configuring document caching on a per (virtual?)
+ * server basis...
+ */
+
+typedef struct {
+ array_header *language_priority;
+} neg_dir_config;
+
+module negotiation_module;
+
+char *merge_string_array (pool *p, array_header *arr, char *sep)
+{
+ int i;
+ char *t = "";
+
+ for (i = 0; i < arr->nelts; i++) {
+ t = pstrcat(p, t, i ? sep : "", ((char**)arr->elts)[i], NULL);
+ }
+ return t;
+}
+
+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;
+}
+
+const 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;
+}
+
+const 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 }
+};
+
+/* 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;
+ char *charset; /* for content-type only */
+} accept_rec;
+
+/* Record of available info on a particular variant
+ *
+ * Note that a few of these fields are updated by the actual negotiation
+ * code. These are:
+ *
+ * level_matched --- initialized to zero. Set to the value of level
+ * if the client actually accepts this media type at that
+ * level (and *not* if it got in on a wildcard). See level_cmp
+ * below.
+ */
+
+typedef struct var_rec {
+ request_rec *sub_req; /* May be NULL (is, for map files) */
+ char *type_name;
+ char *file_name;
+ char *content_encoding;
+ array_header *content_languages; /* list of languages for this variant */
+ char *content_charset;
+ char *description;
+
+ /* The next five items give the quality values for the dimensions
+ * of negotiation for this variant. They are obtained from the
+ * appropriate header lines, except for accept_type_quality, which
+ * is obtained from the variant itself (the 'qs' parameter value
+ * from the variant's mime-type). Apart from type_quality,
+ * these values are set when we find the quality for each variant
+ * (see best_match()). type_quality is set from the 'qs' parameter
+ * of the variant description or mime type: see set_mime_fields().
+ */
+ float lang_quality; /* quality of this variant's language */
+ int encoding_quality; /* ditto encoding (1 or 0 only) */
+ float charset_quality; /* ditto charset */
+ float accept_type_quality; /* ditto media type */
+ float type_quality; /* quality of source for this type */
+
+ /* Now some special values */
+ float level; /* Auxiliary to content-type... */
+ float bytes; /* content length, if known */
+ int lang_index; /* pre HTTP/1.1 language priority stuff */
+ int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */
+
+ /* Above are all written-once properties of the variant. The
+ * three fields below are changed during negotiation:
+ */
+
+ float level_matched;
+ int mime_stars;
+ int definite;
+} var_rec;
+
+/* Something to carry around the state of negotiation (and to keep
+ * all of this thread-safe)...
+ */
+
+typedef struct {
+ pool *pool;
+ request_rec *r;
+ char *dir_name;
+ int accept_q; /* 1 if an Accept item has a q= param */
+ float default_lang_quality; /* fiddle lang q for variants with no lang */
+
+
+ array_header *accepts; /* accept_recs */
+ int have_accept_header; /* 1 if Accept-Header present */
+ array_header *accept_encodings; /* accept_recs */
+ array_header *accept_charsets; /* accept_recs */
+ array_header *accept_langs; /* accept_recs */
+ array_header *avail_vars; /* available variants */
+
+ int ua_can_negotiate; /* 1 if ua can do transparent negotiate */
+ int use_transparent_neg; /* 1 if we are using transparent neg */
+ int short_accept_headers; /* 1 if ua does trans neg & sent short accpt */
+} 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_languages = NULL;
+ mime_info->content_charset = "";
+ mime_info->description = "";
+
+ mime_info->is_pseudo_html = 0;
+ mime_info->level = 0.0;
+ mime_info->level_matched = 0.0;
+ mime_info->bytes = 0;
+ mime_info->lang_index = -1;
+ mime_info->mime_stars = 0;
+ mime_info->definite = 1;
+
+ mime_info->charset_quality = 1.0;
+ mime_info->type_quality = 0.0;
+ mime_info->encoding_quality = 1;
+ mime_info->lang_quality = 1.0;
+ mime_info->accept_type_quality = 1.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->type_quality = mime_info->quality;
+ var->level = mime_info->level;
+ var->content_charset = mime_info->charset;
+
+ 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;
+ result->charset = "";
+
+ /* Note that this handles what I gather is the "old format",
+ *
+ * Accept: text/html text/plain moo/zot
+ *
+ * without any compatibility kludges --- if the token after the
+ * MIME type begins with a semicolon, we know we're looking at parms,
+ * otherwise, we know we aren't. (So why all the pissing and moaning
+ * in the CERN server code? I must be missing something).
+ */
+
+ result->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;
+ char *end;
+
+ ++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;
+ for (end = cp; *end &&
+ *end != '\n' && *end != '\r' && *end != '\"';
+ end++)
+ ;
+ }
+ else {
+ for (end = cp; *end && !isspace(*end); end++)
+ ;
+ }
+ if (*end)
+ *end = '\0'; /* strip ending quote or return */
+ str_tolower(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);
+ else if (!strcmp(parm, "charset"))
+ result->charset = cp;
+ }
+
+ if (*accept_line == ',') ++accept_line;
+
+ return accept_line;
+}
+
+/*****************************************************************
+ *
+ * Dealing with header lines ...
+ *
+ * Accept, Accept-Charset, Accept-Language and Accept-Encoding
+ * are handled by do_header_line() - they all have the same
+ * basic structure of a list of items of the format
+ * name; q=N; charset=TEXT
+ *
+ * where q is only valid in Accept, Accept-Charset and Accept-Languages,
+ * and charset is only valid in Accept.
+ */
+
+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;
+}
+
+/* Given the text of the Content-Languages: line from the var map file,
+ * return an array containing the languages of this variant
+ */
+
+array_header *do_languages_line (pool *p, char **lang_line)
+{
+ array_header *lang_recs = make_array (p, 2, sizeof (char *));
+
+ if (!lang_line) return lang_recs;
+
+ while (**lang_line) {
+ char **new = (char **)push_array (lang_recs);
+ *new = get_token (p, lang_line, 0);
+ str_tolower (*new);
+ if (**lang_line == ',' || **lang_line == ';')
+ ++(*lang_line);
+ }
+
+ return lang_recs;
+}
+
+/*****************************************************************
+ *
+ * Handling header lines from clients...
+ */
+
+negotiation_state *parse_accept_headers (request_rec *r)
+{
+ negotiation_state *new =
+ (negotiation_state *)pcalloc (r->pool, sizeof (negotiation_state));
+ accept_rec *elts;
+ table *hdrs = r->headers_in;
+ int i;
+ char *hdr;
+
+ 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"));
+
+ hdr = table_get (hdrs, "Accept-encoding");
+ if (hdr)
+ new->have_accept_header = 1;
+ new->accept_encodings = do_header_line (r->pool, hdr);
+
+ new->accept_langs =
+ do_header_line (r->pool, table_get (hdrs, "Accept-language"));
+ new->accept_charsets =
+ do_header_line (r->pool, table_get (hdrs, "Accept-charset"));
+ new->avail_vars = make_array (r->pool, 40, sizeof (var_rec));
+
+#ifdef TCN_02
+ if (table_get(r->headers_in, "Negotiate")) {
+ /* Negotiate: header tells us UA does transparent negotiation
+ * We have to decide whether we want to ... for now, yes,
+ * we do */
+
+ new->ua_can_negotiate = 1;
+ if (r->method_number == M_GET)
+ new->use_transparent_neg = 1; /* should be configurable */
+
+ /* Check for 'Short Accept', ie either no Accept: header,
+ * or just "Accept: * / *" */
+ if (new->accepts->nelts == 0 ||
+ (new->accepts->nelts == 1 &&
+ (!strcmp(((accept_rec *)new->accepts->elts)[0].type_name,
+ "*/*")))) {
+ /* Using short accept header */
+ new->short_accept_headers = 1;
+ }
+ }
+#endif
+
+ if (!new->use_transparent_neg) {
+ /* Now we check for q-values. If they're all 1.0, we assume the
+ * client is "broken", and we are allowed to fiddle with the
+ * values later. Otherwise, we leave them alone.
+ */
+
+ elts = (accept_rec *)new->accepts->elts;
+
+ for (i = 0; i < new->accepts->nelts; ++i)
+ if (elts[i].quality < 1.0) new->accept_q = 1;
+ }
+ else new->accept_q = 1;
+
+ 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;
+
+ for ( ; *cp && *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;
+}
+
+static int read_type_map (negotiation_state *neg, request_rec *rr)
+{
+ request_rec *r = neg->r;
+ FILE *map;
+ char buffer[MAX_STRING_LEN];
+ enum header_state hstate;
+ struct var_rec mime_info;
+
+ if (rr->status != HTTP_OK) {
+ return rr->status;
+ }
+ map = pfopen (neg->pool, rr->filename, "r");
+ if (map == NULL) {
+ log_reason("cannot access type map file", rr->filename, 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_languages =
+ do_languages_line(neg->pool, &body);
+ }
+ 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 (!strncmp (buffer, "description:", 12)) {
+ mime_info.description = get_token (neg->pool, &body, 0);
+ }
+ } else {
+ if (mime_info.type_quality > 0 && *mime_info.file_name)
+ {
+ 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... */
+
+ if (strncmp(r->filename, "proxy:", 6) == 0)
+ return DECLINED;
+
+ ++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 != HTTP_OK || !sub_req->content_type) {
+ destroy_sub_req(sub_req);
+ continue;
+ }
+
+ /* If it's a map file, we use that instead of the map
+ * we're building...
+ */
+
+ if (((sub_req->content_type) &&
+ !strcmp (sub_req->content_type, MAP_FILE_MAGIC_TYPE)) ||
+ ((sub_req->handler) &&
+ !strcmp (sub_req->handler, "type-map"))) {
+ closedir(dirp);
+
+ neg->avail_vars->nelts = 0;
+ return read_type_map (neg, sub_req);
+ }
+
+ /* Have reasonable variant --- gather notes.
+ */
+
+ mime_info.sub_req = sub_req;
+ mime_info.file_name = pstrdup(neg->pool, dir_entry->d_name);
+ if (sub_req->content_encoding) {
+ mime_info.content_encoding = sub_req->content_encoding;
+ str_tolower(mime_info.content_encoding);
+ }
+ if (sub_req->content_languages) {
+ int i;
+ mime_info.content_languages = sub_req->content_languages;
+ if (mime_info.content_languages)
+ for (i = 0; i < mime_info.content_languages->nelts; ++i)
+ str_tolower(((char**)
+ (mime_info.content_languages->elts))[i]);
+ }
+
+ 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. The main entry point is set_language_quality()
+ * which is called for each variant. It sets two elements in the
+ * variant record:
+ * language_quality - the 'q' value of the 'best' matching language
+ * from Accept-Language: header (HTTP/1.1)
+ * lang_index - Pre HTTP/1.1 language priority, using
+ * position of language on the Accept-Language:
+ * header, if present, else LanguagePriority
+ * directive order.
+ *
+ * When we do the variant checking for best variant, we use language
+ * quality first, and if a tie, language_index next (this only
+ * applies when _not_ using the network algorithm). If using
+ * network algorithm, lang_index is never used.
+ *
+ * set_language_quality() calls find_lang_index() and find_default_index()
+ * to set lang_index.
+ */
+
+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;
+}
+
+/* set_default_lang_quality() sets the quality we apply to variants
+ * which have no language assigned to them. If none of the variants
+ * have a language, we are not negotiating on language, so all are
+ * acceptable, and we set the default q value to 1.0. However if
+ * some of the variants have languages, we set this default to 0.001.
+ * The value of this default will be applied to all variants with
+ * no explicit language -- which will have the effect of making them
+ * acceptable, but only if no variants with an explicit language
+ * are acceptable. The default q value set here is assigned to variants
+ * with no language type in set_language_quality().
+ *
+ * Note that if using the transparent negotiation network algorythm,
+ * we don't use this fiddle.
+ */
+
+void set_default_lang_quality(negotiation_state *neg)
+{
+ var_rec *avail_recs = (var_rec *)neg->avail_vars->elts;
+ int j;
+
+ if (!neg->use_transparent_neg)
+ for (j = 0; j < neg->avail_vars->nelts; ++j) {
+ var_rec *variant = &avail_recs[j];
+ if (variant->content_languages &&
+ variant->content_languages->nelts) {
+ neg->default_lang_quality = 0.001;
+ return;
+ }
+ }
+
+ neg->default_lang_quality = 1.0;
+}
+
+/* Set the language_quality value in the variant record. Also
+ * assigns lang_index for back-compat.
+ *
+ * To find the language_quality value, we look for the 'q' value
+ * of the 'best' matching language on the Accept-Language:
+ * header. The'best' match is the language on Accept-Language:
+ * header which matches the language of this variant either fully,
+ * or as far as the prefix marker (-). If two or more languages
+ * match, use the longest string from the Accept-Language: header
+ * (see HTTP/1.1 [14.4])
+ *
+ * When a variant has multiple languages, we find the 'best'
+ * match for each variant language tag as above, then select the
+ * one with the highest q value. Because both the accept-header
+ * and variant can have multiple languages, we now have a hairy
+ * loop-within-a-loop here.
+ *
+ * If the variant has no language and we have no Accept-Language
+ * items, leave the quality at 1.0 and return.
+ *
+ * If the variant has no language, we use the default as set by
+ * set_default_lang_quality() (1.0 if we are not negotiating on
+ * language, 0.001 if we are).
+ *
+ * Following the setting of the language quality, we drop through to
+ * set the old 'lang_index'. This is set based on either the order
+ * of the languages on the Accept-Language header, or the
+ * order on the LanguagePriority directive. This is only used
+ * in the negotiation if the language qualities tie.
+ */
+
+void set_language_quality(negotiation_state *neg, var_rec *variant)
+{
+ int i;
+ int naccept = neg->accept_langs->nelts;
+ int index;
+ neg_dir_config *conf = NULL;
+ char *firstlang;
+
+ if (naccept == 0)
+ conf = (neg_dir_config *) get_module_config (neg->r->per_dir_config,
+ &negotiation_module);
+
+ if (naccept == 0 && (!variant->content_languages ||
+ !variant->content_languages->nelts))
+ return; /* no accept-language and no variant lang */
+
+ if (!variant->content_languages || !variant->content_languages->nelts) {
+ /* This variant has no content-language, so use the default
+ * quality factor for variants with no content-language
+ * (previously set by set_default_lang_quality()). */
+ variant->lang_quality = neg->default_lang_quality;
+
+ if (naccept == 0)
+ return; /* no accept-language items */
+
+ }
+ else if (naccept) {
+ /* Variant has one (or more) languages, and we have one (or more)
+ * language ranges on the Accept-Language header. Look for
+ * the best match. We do this by going through each language
+ * on the variant description looking for a match on the
+ * Accept-Language header. The best match is the longest matching
+ * language on the header. The final result is the best q value
+ * from all the languages on the variant description.
+ */
+ int j;
+ float fiddle_q = 0.0;
+ accept_rec *accs = (accept_rec *)neg->accept_langs->elts;
+ accept_rec *best = NULL, *star = NULL;
+ char *p;
+
+ for (j = 0; j < variant->content_languages->nelts; ++j) {
+ char *lang; /* language from variant description */
+ accept_rec *bestthistag = NULL;
+ int prefixlen = 0;
+ int longest_lang_range_len = 0;
+ int len;
+ /* lang is the variant's language-tag, which is the one
+ * we are allowed to use the prefix of in HTTP/1.1
+ */
+ lang = ((char **)(variant->content_languages->elts))[j];
+ p = strchr(lang, '-'); /* find prefix part (if any) */
+ if (p)
+ prefixlen = p - lang;
+
+ /* now find the best (i.e. longest) matching Accept-Language
+ * header language. We put the best match for this tag in
+ * bestthistag. We cannot update the overall best (based on
+ * q value) because the best match for this tag is the longest
+ * language item on the accept header, not necessarily the
+ * highest q.
+ */
+ for (i = 0; i < neg->accept_langs->nelts; ++i) {
+ if (!strcmp(accs[i].type_name, "*")) {
+ if (!star)
+ star = &accs[i];
+ continue;
+ }
+
+ /* Find language. We match if either the variant language
+ * tag exactly matches, or the prefix of the tag up to the
+ * '-' character matches the whole of the language in the
+ * Accept-Language header. We only use this accept-language
+ * item as the best match for the current tag if it
+ * is longer than the previous best match */
+ if ((!strcmp (lang, accs[i].type_name) ||
+ (prefixlen &&
+ !strncmp(lang, accs[i].type_name, prefixlen) &&
+ (accs[i].type_name[prefixlen] == '\0'))) &&
+ ((len = strlen(accs[i].type_name)) >
+ longest_lang_range_len)) {
+ longest_lang_range_len = len;
+ bestthistag = &accs[i];
+ }
+
+ if (! bestthistag) {
+ /* The next bit is a fiddle. Some browsers might be
+ * configured to send more specific language ranges
+ * than desirable. For example, an Accept-Language of
+ * en-US should never match variants with languages en
+ * or en-GB. But US English speakers might pick en-US
+ * as their language choice. So this fiddle checks if
+ * the language range has a prefix, and if so, it
+ * matches variants which match that prefix with a
+ * priority of 0.001. So a request for en-US would
+ * match variants of types en and en-GB, but at much
+ * lower priority than matches of en-US directly, or
+ * of any other language listed on the Accept-Language
+ * header
+ */
+ if ((p = strchr(accs[i].type_name, '-'))) {
+ int plen = p - accs[i].type_name;
+ if (!strncmp(lang, accs[i].type_name, plen))
+ fiddle_q = 0.001;
+ }
+ }
+ }
+ /* Finished looking at Accept-Language headers, the best
+ * (longest) match is in bestthistag, or NULL if no match
+ */
+ if (!best ||
+ (bestthistag && bestthistag->quality > best->quality))
+ best = bestthistag;
+ }
+
+ variant->lang_quality = best ? best->quality :
+ (star ? star->quality : fiddle_q);
+ }
+
+ /* Now set the old lang_index field. Since this is old
+ * stuff anyway, don't both with handling multiple languages
+ * per variant, just use the first one assigned to it
+ */
+ index = 0;
+ if (variant->content_languages && variant->content_languages->nelts)
+ firstlang = ((char**)variant->content_languages->elts)[0];
+ else
+ firstlang = "";
+ if (naccept == 0) /* Client doesn't care */
+ index = find_default_index (conf, firstlang);
+ else /* Client has Accept-Language */
+ index = find_lang_index (neg->accept_langs, firstlang);
+ variant->lang_index = index;
+
+ return;
+}
+
+/* Determining the content length --- if the map didn't tell us,
+ * we have to do a stat() and remember for next time.
+ *
+ * Grump. For Apache, even the first stat here may well be
+ * redundant (for multiviews) with a stat() done by the sub_req
+ * machinery. At some point, that ought to be fixed.
+ */
+
+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;
+}
+
+/* For a given variant, find the best matching Accept: header
+ * and assign the Accept: header's quality value to the
+ * accept_type_quality field of the variant, for later use in
+ * determining the best matching variant.
+ */
+
+void set_accept_quality(negotiation_state *neg, var_rec *variant)
+{
+ int i;
+ accept_rec *accept_recs = (accept_rec *)neg->accepts->elts;
+ float q = 0.0;
+ int q_definite = 1;
+
+ /* if no Accept: header, leave quality alone (will
+ * remain at the default value of 1) */
+ if (!neg->accepts || neg->accepts->nelts == 0)
+ return;
+
+ /*
+ * Go through each of the ranges on the Accept: header,
+ * looking for the 'best' match with this variant's
+ * content-type. We use the best match's quality
+ * value (from the Accept: header) for this variant's
+ * accept_type_quality field.
+ *
+ * The best match is determined like this:
+ * type/type is better than type/ * is better than * / *
+ * if match is type/type, use the level mime param if available
+ */
+ for (i = 0; i < neg->accepts->nelts; ++i) {
+
+ accept_rec *type = &accept_recs[i];
+ int prev_mime_stars;
+
+ prev_mime_stars = variant->mime_stars;
+
+ if (!mime_match(type, variant))
+ continue; /* didn't match the content type at all */
+ else
+ /* did match - see if there were less or more stars than
+ * in previous match
+ */
+ if (prev_mime_stars == variant->mime_stars)
+ continue; /* more stars => not as good a match */
+
+ /* Check maxbytes -- not in HTTP/1.1 or Holtman */
+
+ if (type->max_bytes > 0
+ && (find_content_length(neg, variant)
+ > type->max_bytes))
+ continue;
+
+ /* If we are allowed to mess with the q-values,
+ * make wildcards very low, so we have a low chance
+ * of ending up with them if there's something better.
+ */
+
+ if (!neg->accept_q && variant->mime_stars == 1) q = 0.01;
+ else if (!neg->accept_q && variant->mime_stars == 2) q = 0.02;
+ else q = type->quality;
+
+ q_definite = (variant->mime_stars == 3);
+ }
+ variant->accept_type_quality = q;
+ variant->definite=variant->definite && q_definite;
+
+ /* if the _best_ quality we got for this variant was 0.0,
+ * eliminate it now */
+}
+
+/* For a given variant, find the 'q' value of the charset given
+ * on the Accept-Charset line. If not charsets are listed,
+ * assume value of '1'.
+ */
+
+void set_charset_quality(negotiation_state *neg, var_rec *variant)
+{
+ int i;
+ accept_rec *accept_recs = (accept_rec *)neg->accept_charsets->elts;
+ char *charset = variant->content_charset;
+ accept_rec *star = NULL;
+
+ /* if no Accept-Charset: header, leave quality alone (will
+ * remain at the default value of 1) */
+ if (!neg->accept_charsets || neg->accept_charsets->nelts == 0)
+ return;
+
+ if (charset == NULL || !*charset) charset = "iso-8859-1";
+
+ /*
+ * Go through each of the items on the Accept-Charset: header,
+ * looking for a match with this variant's charset. If none
+ * match, charset is unacceptable, so set quality to 0.
+ */
+ for (i = 0; i < neg->accept_charsets->nelts; ++i) {
+
+ accept_rec *type = &accept_recs[i];
+
+ if (!strcmp(type->type_name, charset)) {
+ variant->charset_quality = type->quality;
+ return;
+ } else
+ if (strcmp(type->type_name, "*") == 0) {
+ star = type;
+ }
+ }
+ /* No explicit match */
+ if (star) {
+ variant->charset_quality = star->quality;
+ return;
+ }
+ /* If this variant is in charset iso-8859-1, the default is 1.0 */
+ if (strcmp(charset, "iso-8859-1") == 0) {
+ variant->charset_quality = 1.0;
+ } else {
+ variant->charset_quality = 0.0;
+ }
+}
+
+/* For a given variant, find the best matching Accept: header
+ * and assign the Accept: header's quality value to the
+ * accept_type_quality field of the variant, for later use in
+ * determining the best matching variant.
+ */
+
+/* is_identity_encoding is included for back-compat, but does anyone
+ * use 7bit, 8bin or binary in their var files??
+ */
+
+int is_identity_encoding (char *enc)
+{
+ return (!enc || !enc[0] || !strcmp (enc, "7bit") || !strcmp (enc, "8bit")
+ || !strcmp (enc, "binary"));
+}
+
+void set_encoding_quality(negotiation_state *neg, var_rec *variant)
+{
+ int i;
+ accept_rec *accept_recs = (accept_rec *)neg->accept_encodings->elts;
+ char *enc = variant->content_encoding;
+
+ if (!enc || is_identity_encoding(enc))
+ return;
+
+
+ /* if no Accept: header, leave quality alone (will
+ * remain at the default value of 1) */
+ if (neg->accept_encodings->nelts == 0) {
+ /* If we had an empty Accept-Encoding header, assume that
+ * no encodings are acceptable, else all encodings are ok */
+ variant->encoding_quality = neg->have_accept_header ? 0 : 1;
+ return;
+ }
+
+ /* Go through each of the encodings on the Accept-Encoding: header,
+ * looking for a match with our encoding
+ */
+ for (i = 0; i < neg->accept_encodings->nelts; ++i) {
+ char *name = accept_recs[i].type_name;
+
+ if (!strcmp(name, enc)) {
+ variant->encoding_quality = 1;
+ return;
+ }
+ }
+
+ /* Encoding not found on Accept-Encoding: header, so it is
+ * _not_ acceptable */
+ variant->encoding_quality = 0;
+}
+
+/* Possible results of the network algorithm */
+enum algorithm_results {
+ na_not_applied = -1, /* net algorithm not used */
+ na_choice = 1, /* choose variant */
+ na_list /* list variants */
+};
+
+/*
+ * This is a heavily-rewritten 'best_match' function. For a start, it
+ * now returns an int, which has one of the three values: na_not_applied,
+ * na_choice or na_list, which give the result of the network algorithm
+ * (if it was not applied, the return value is na_not_applied).
+ * The best variable is returned in *pbest. It also has two possible
+ * algorithms for determining the best match: the network algorithm,
+ * and the standard Apache algorithm. These are split out into
+ * separate functions (is_variant_better_na() and is_variant_better()).
+ *
+ * Previously, best_match iterated first through the content_types
+ * in the Accept: header, then checked each variant, and eliminated
+ * those that didn't match the variant's type. We cannot do this because
+ * we need full information, including language, charset, etc
+ * quality for _every_ variant, for the Alternates: header,
+ * and (possibly) the human-readable choice responses or 406 errors.
+ *
+ * After the 'best' (if any) is determined, the overall result of
+ * the negotiation is obtained. If the network algorithm was not
+ * in use, the result is na_not_applied. Else the result is
+ * na_list if 'short accept header' is in use, else na_list
+ * if _no_ best match was found, or na_choice if a best match
+ * was found.
+ */
+
+/* Firstly, the negotiation 'network algorithm' from Holtman.
+ */
+
+int is_variant_better_na(negotiation_state *neg, var_rec *variant, var_rec *best, float *p_bestq)
+{
+ float bestq = *p_bestq, q;
+
+ /* Note: Encoding is not negotiated in the Holtman
+ * transparent neg draft, so we ignored it here. But
+ * it does mean we could return encodings the UA
+ * or proxy cannot handle. Eek. */
+
+ q = variant->accept_type_quality *
+ variant->type_quality *
+ variant->charset_quality *
+ variant->lang_quality;
+
+#ifdef NEG_DEBUG
+ fprintf(stderr, "Variant: file=%s type=%s lang=%s acceptq=%1.3f langq=%1.3f typeq=%1.3f q=%1.3f definite=%d\n",
+ variant->file_name ? variant->file_name : "",
+ variant->type_name ? variant->type_name : "",
+ variant->content_languages ? merge_string_array(neg->pool, variant->content_languages, ",") : "",
+ variant->accept_type_quality,
+ variant->lang_quality,
+ variant->type_quality,
+ q,
+ variant->definite
+ );
+#endif
+
+ if (q > bestq) {
+ *p_bestq = q;
+ return 1;
+ }
+ if (q == bestq) {
+ /* If the best variant's charset is ISO-8859-1 and this variant has
+ the same charset quality, then we prefer this variant */
+ if (variant->charset_quality == best->charset_quality &&
+ (variant->content_charset != NULL &&
+ *variant->content_charset != '\0' &&
+ strcmp(variant->content_charset, "iso-8859-1") != 0) &&
+ (best->content_charset == NULL ||
+ *best->content_charset == '\0' ||
+ strcmp(best->content_charset, "iso-8859-1") == 0)) {
+ *p_bestq = q;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Negotiation algorithm as used by previous versions of Apache
+ * (just about).
+ */
+
+float is_variant_better(negotiation_state *neg, var_rec *variant, var_rec *best, float *p_bestq)
+{
+ float bestq = *p_bestq, q;
+ int levcmp;
+
+ /*
+ * For non-transparent negotiation, server can choose how
+ * to handle the negotiation. We'll use the following in
+ * order: content-type, language, content-type level, charset,
+ * content length.
+ *
+ * For each check, we have three possible outcomes:
+ * This variant is worse than current best: return 0
+ * This variant is better than the current best:
+ * assign this variant's q to *p_bestq, and return 1
+ * This variant is just as desirable as the current best:
+ * drop through to the next test.
+ *
+ * This code is written in this long-winded way to allow future
+ * customisation, either by the addition of additional
+ * checks, or to allow the order of the checks to be determined
+ * by configuration options (e.g. we might prefer to check
+ * language quality _before_ content type).
+ */
+
+ /* First though, eliminate this variant if it is not
+ * acceptable by type, charset, encoding or language.
+ */
+
+ if (variant->encoding_quality == 0 ||
+ variant->lang_quality == 0 ||
+ variant->type_quality == 0 ||
+ variant->charset_quality == 0 ||
+ variant->accept_type_quality == 0)
+ return 0; /* don't consider unacceptables */
+
+ q = variant->accept_type_quality * variant->type_quality;
+ if (q == 0.0 || q < bestq) return 0;
+ if (q > bestq || !best) {
+ *p_bestq = q;
+ return 1;
+ }
+
+ /* language */
+ if (variant->lang_quality < best->lang_quality)
+ return 0;
+ if (variant->lang_quality > best->lang_quality) {
+ *p_bestq = q;
+ return 1;
+ }
+
+ /* if language qualities were equal, try the LanguagePriority
+ * stuff */
+ if (best->lang_index != -1 && variant->lang_index > best->lang_index)
+ return 0;
+ if (variant->lang_index != -1 &&
+ (variant->lang_index < best->lang_index || best->lang_index == -1)) {
+ *p_bestq = q;
+ return 1;
+ }
+
+ /* content-type level (text/html only?) */
+ levcmp = level_cmp (variant, best);
+ if (levcmp == -1) return 0;
+ if (levcmp == 1) {
+ *p_bestq = q;
+ return 1;
+ }
+
+ /* encoding -- can only be 1 or 0, and if 0 we eliminated this
+ * variant at the start of this function. However we
+ * prefer variants with no encoding over those with encoding */
+ if (!*best->content_encoding && *variant->content_encoding)
+ return 0;
+ if (*best->content_encoding && !*variant->content_encoding) {
+ *p_bestq = q;
+ return 1;
+ }
+
+
+ /* charset */
+ if (variant->charset_quality < best->charset_quality)
+ return 0;
+ /* If the best variant's charset is ISO-8859-1 and this variant has
+ the same charset quality, then we prefer this variant */
+ if (variant->charset_quality > best->charset_quality ||
+ ((variant->content_charset != NULL &&
+ *variant->content_charset != '\0' &&
+ strcmp(variant->content_charset, "iso-8859-1") != 0) &&
+ (best->content_charset == NULL ||
+ *best->content_charset == '\0' ||
+ strcmp(best->content_charset, "iso-8859-1") == 0))) {
+ *p_bestq = q;
+ return 1;
+ }
+
+
+ /* content length if all else equal */
+ if (find_content_length(neg, variant)
+ >=
+ find_content_length(neg, best))
+ return 0;
+
+ /* ok, to get here means every thing turned out equal, except
+ * we have a shorter content length, so use this variant */
+ *p_bestq = q;
+ return 1;
+}
+
+int best_match(negotiation_state *neg, var_rec **pbest)
+{
+ int j;
+ var_rec *best = NULL;
+ float bestq = 0.0;
+ enum algorithm_results algorithm_result = na_not_applied;
+
+ var_rec *avail_recs = (var_rec *)neg->avail_vars->elts;
+
+ set_default_lang_quality(neg);
+
+ /*
+ * Find the 'best' variant
+ */
+
+ for (j = 0; j < neg->avail_vars->nelts; ++j) {
+
+ var_rec *variant = &avail_recs[j];
+
+ /* Find all the relevant 'quality' values from the
+ * Accept... headers, and store in the variant
+ */
+ set_accept_quality(neg, variant);
+ set_language_quality(neg, variant);
+ set_encoding_quality(neg, variant);
+ set_charset_quality(neg, variant);
+
+ /* Now find out if this variant is better than the current
+ * best, either using the network algorithm, or Apache's
+ * internal server-driven algorithm. Presumably other
+ * server-driven algorithms are possible, and could be
+ * implemented here.
+ */
+
+ if (neg->use_transparent_neg) {
+ if (is_variant_better_na(neg, variant, best, &bestq))
+ best = variant;
+ }
+ else {
+ if (is_variant_better(neg, variant, best, &bestq))
+ best = variant;
+ }
+ }
+
+ /* We now either have a best variant, or no best variant
+ */
+ if (neg->use_transparent_neg) {
+ if (neg->short_accept_headers)
+ algorithm_result = na_list;
+ else {
+ /* From Holtman, result is:
+ * If variant & URI are not neigbors, list_ua or list_os
+ * Else
+ * If UA can do trans neg
+ * IF best is definite && best q > 0, choice_ua
+ * ELSE list_ua
+ * ELSE
+ * IF best q > 0, choose_os
+ * ELSE list_os (or forward_os on proxy)
+ */
+
+ /* assume variant and URI are neigbors (since URI in
+ * var map must be in same directory) */
+
+ if(neg->use_transparent_neg)
+ algorithm_result = (best && best->definite) && (bestq>0)
+ ? na_choice : na_list;
+ else
+ algorithm_result = bestq>0 ? na_choice : na_list;
+ }
+ }
+
+ *pbest = best;
+ return algorithm_result;
+}
+
+/*
+ * Sets the Alternates and Vary headers, used if we are going to
+ * return 406 Not Acceptable status, a 300 Multiple Choice status,
+ * or a Choice response.
+ *
+ * 'type' is the result of the network algorithm, if applied.
+ * We do different things if the network algorithm was not applied
+ * (type == na_not_applied): no Alternates header, and Vary:
+ * does not include 'negotiate'.
+ *
+ * We should also add a max-age lifetime for the Alternates header,
+ * but how long we we give it? Presumably this should be
+ * configurable in the map file.
+ */
+
+void set_neg_headers(request_rec *r, negotiation_state *neg, int na_result)
+{
+ int j;
+ var_rec *avail_recs = (var_rec *)neg->avail_vars->elts;
+ char *sample_type = NULL;
+ char *sample_language = NULL;
+ char *sample_encoding = NULL;
+ char *sample_charset = NULL;
+ int vary_by_type = 0;
+ int vary_by_language = 0;
+ int vary_by_charset = 0;
+ int vary_by_encoding = 0;
+ array_header *hdrs;
+
+ /* Put headers into err_headers_out, new send_http_header()
+ * outputs both headers_out and err_headers_out */
+ hdrs = r->err_headers_out;
+
+ for (j = 0; j < neg->avail_vars->nelts; ++j) {
+
+ var_rec *variant = &avail_recs[j];
+ char *rec;
+ char qstr[6];
+ long len;
+ char lenstr[22]; /* enough for 2^64 */
+
+ ap_snprintf(qstr, sizeof(qstr), "%1.3f", variant->type_quality);
+
+ /* Strip trailing zeros (saves those valuable network bytes) */
+ if (qstr[4] == '0') {
+ qstr[4] = '\0';
+ if (qstr[3] == '0') {
+ qstr[3] = '\0';
+ if (qstr[2] == '0') {
+ qstr[1] = '\0';
+ }
+ }
+ }
+
+ rec = pstrcat(r->pool, "{\"", variant->file_name, "\" ", qstr, NULL);
+ if (variant->type_name) {
+ if (*variant->type_name)
+ rec = pstrcat(r->pool, rec, " {type ",
+ variant->type_name, "}", NULL);
+ if (!sample_type) sample_type = variant->type_name;
+ else if (strcmp(sample_type, variant->type_name))
+ vary_by_type = 1;
+ }
+ if (variant->content_languages && variant->content_languages->nelts) {
+ char *langs =
+ merge_string_array (r->pool, variant->content_languages, ",");
+ rec = pstrcat(r->pool, rec, " {language ", langs, "}", NULL);
+ if (!sample_language) sample_language = langs;
+ else if (strcmp(sample_language, langs))
+ vary_by_language = 1;
+ }
+ if (variant->content_encoding) {
+ if (!sample_encoding) sample_encoding = variant->content_encoding;
+ else if (strcmp(sample_encoding, variant->content_encoding))
+ vary_by_encoding = 1;
+ }
+ if (variant->content_charset) {
+ if (*variant->content_charset)
+ rec = pstrcat(r->pool, rec, " {charset ",
+ variant->content_charset, "}", NULL);
+ if (!sample_charset) sample_charset = variant->content_charset;
+ else if (strcmp(sample_charset, variant->content_charset))
+ vary_by_charset = 1;
+ }
+ if ((len = find_content_length(neg, variant)) != 0) {
+ ap_snprintf(lenstr, sizeof(lenstr), "%ld", len);
+ rec = pstrcat(r->pool, rec, " {length ", lenstr, "}", NULL);
+ }
+
+ rec = pstrcat(r->pool, rec, "}", NULL);
+
+ if (na_result != na_not_applied)
+ table_merge(hdrs, "Alternates", rec);
+ }
+
+ if (na_result != na_not_applied)
+ table_merge(hdrs, "Vary", "negotiate");
+ if (vary_by_type)
+ table_merge(hdrs, "Vary", "accept");
+ if (vary_by_language)
+ table_merge(hdrs, "Vary", "accept-language");
+ if (vary_by_charset)
+ table_merge(hdrs, "Vary", "accept-charset");
+ if (vary_by_encoding && na_result == na_not_applied)
+ table_merge(hdrs, "Vary", "accept-encoding");
+}
+
+/**********************************************************************
+ *
+ * Return an HTML list of variants. This is output as part of the
+ * 300 or 406 status body.
+ */
+
+char *make_variant_list (request_rec *r, negotiation_state *neg)
+{
+ int i;
+ char *t;
+
+ t = pstrdup(r->pool, "Available variants:\n<ul>\n");
+ for (i = 0; i < neg->avail_vars->nelts; ++i) {
+ var_rec *variant = &((var_rec *)neg->avail_vars->elts)[i];
+ char *filename = variant->file_name ? variant->file_name : "";
+ array_header *languages = variant->content_languages;
+ char *description = variant->description ? variant->description : "";
+
+ /* The format isn't very neat, and it would be nice to make
+ * the tags human readable (eg replace 'language en' with
+ * 'English'). */
+ t = pstrcat(r->pool, t, "<li><a href=\"", filename, "\">",
+ filename, "</a> ", description, NULL);
+ if (variant->type_name && *variant->type_name)
+ t = pstrcat(r->pool, t, ", type ", variant->type_name, NULL);
+ if (languages && languages->nelts)
+ t = pstrcat(r->pool, t, ", language ",
+ merge_string_array(r->pool, languages, ", "),
+ NULL);
+ if (variant->content_charset && *variant->content_charset)
+ t = pstrcat(r->pool, t, ", charset ", variant->content_charset, NULL);
+ t = pstrcat(r->pool, t, "\n", NULL);
+ }
+ t = pstrcat(r->pool, t, "</ul>\n", NULL);
+
+ return t;
+}
+
+void store_variant_list (request_rec *r, negotiation_state *neg)
+{
+ if (r->main == NULL) {
+ table_set (r->notes, "variant-list", make_variant_list (r, neg));
+ } else {
+ table_set (r->main->notes, "variant-list", make_variant_list (r->main, neg));
+ }
+}
+
+/* Called if we got a "Choice" response from the network algorithm.
+ * It checks the result of the chosen variant to see if it
+ * is itself negotiated (if so, return error VARIANT_ALSO_VARIES).
+ * Otherwise, add the appropriate headers to the current response.
+ */
+
+int setup_choice_response(request_rec *r, negotiation_state *neg, var_rec *variant)
+{
+ request_rec *sub_req;
+ char *sub_vary;
+
+ if (!variant->sub_req) {
+ int status;
+
+ sub_req = sub_req_lookup_file(variant->file_name, r);
+ status = sub_req->status;
+ if (status != HTTP_OK && status != HTTP_MULTIPLE_CHOICES) {
+ destroy_sub_req(sub_req);
+ return status;
+ }
+ variant->sub_req = sub_req;
+ }
+ else
+ sub_req = variant->sub_req;
+
+
+ /* The network algorithm told us to return a "Choice"
+ * response. This is the normal variant response, with
+ * some extra headers. First, ensure that the chosen
+ * variant did not itself return a "List" or "Choice" response.
+ * If not, set the appropriate headers, and fall through to
+ * the normal variant handling
+ */
+
+ if ((sub_req->status == HTTP_MULTIPLE_CHOICES) ||
+ (table_get(sub_req->err_headers_out, "Alternates")) ||
+ (table_get(sub_req->err_headers_out, "Content-Location")))
+ return VARIANT_ALSO_VARIES;
+
+ if ((sub_vary = table_get(sub_req->err_headers_out, "Vary")) != NULL)
+ table_set(r->err_headers_out, "Variant-Vary", sub_vary);
+ table_set(r->err_headers_out, "Content-Location", variant->file_name);
+ set_neg_headers(r, neg, na_choice); /* add Alternates and Vary */
+ /* to do: add Expires */
+
+ return 0;
+}
+
+/****************************************************************
+ *
+ * Executive...
+ */
+
+int handle_map_file (request_rec *r)
+{
+ negotiation_state *neg = parse_accept_headers (r);
+ var_rec *best;
+ int res;
+ int na_result;
+
+ char *udir;
+
+ if ((res = read_type_map (neg, r))) return res;
+
+ maybe_add_default_encodings(neg, 0);
+
+ na_result = best_match(neg, &best);
+
+ /* na_result is one of
+ * na_not_applied: we didn't use the network algorithm
+ * na_choice: return a "Choice" response
+ * na_list: return a "List" response (no variant chosen)
+ */
+
+ if (na_result == na_list) {
+ set_neg_headers(r, neg, na_list);
+ store_variant_list (r, neg);
+ return MULTIPLE_CHOICES;
+ }
+
+ if (!best) {
+ log_reason ("no acceptable variant", r->filename, r);
+
+ set_neg_headers(r, neg, na_result);
+ store_variant_list (r, neg);
+ return NOT_ACCEPTABLE;
+ }
+
+ if (na_result == na_choice)
+ if ((res = setup_choice_response(r, neg, best)) != 0)
+ return res;
+
+ /* Make sure caching works - Vary should handle HTTP/1.1, but for
+ * HTTP/1.0, we can't allow caching at all. NB that we merge the
+ * header in case some other module negotiates on something else.
+ */
+ if (!do_cache_negotiated_docs(r->server) && (r->proto_num < 1001))
+ r->no_cache = 1;
+
+ if (na_result == na_not_applied)
+ set_neg_headers(r, neg, na_not_applied);
+
+ if (r->path_info && *r->path_info) {
+ r->uri[find_path_info(r->uri, r->path_info)] = '\0';
+ }
+ udir = make_dirstr (r->pool, r->uri, count_dirs (r->uri));
+ udir = escape_uri(r->pool, udir);
+ internal_redirect(pstrcat(r->pool, udir, best->file_name, r->path_info,
+ NULL), r);
+ return OK;
+}
+
+int handle_multi (request_rec *r)
+{
+ negotiation_state *neg;
+ var_rec *best, *avail_recs;
+ request_rec *sub_req;
+ int res;
+ int j;
+ int na_result; /* result of network algorithm */
+
+ 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_from_multi:
+ /* free all allocated memory from subrequests */
+ avail_recs = (var_rec *)neg->avail_vars->elts;
+ for (j = 0; j < neg->avail_vars->nelts; ++j) {
+ var_rec *variant = &avail_recs[j];
+ if (variant->sub_req) {
+ destroy_sub_req(variant->sub_req);
+ }
+ }
+ return res;
+ }
+ if (neg->avail_vars->nelts == 0) return DECLINED;
+
+ maybe_add_default_encodings(neg,
+ r->method_number != M_GET
+ || r->args || r->path_info);
+
+ na_result = best_match(neg, &best);
+ if (na_result == na_list) {
+ /*
+ * Network algorithm tols us to output a "List" response.
+ * This is output at a 300 status code, which we will
+ * return. The list of variants will be stored in r->notes
+ * under the name "variants-list".
+ */
+ set_neg_headers(r, neg, na_list); /* set Alternates: and Vary: */
+
+ store_variant_list (r, neg);
+ res = MULTIPLE_CHOICES;
+ goto return_from_multi;
+ }
+
+ if (!best) {
+ log_reason ("no acceptable variant", r->filename, r);
+
+ set_neg_headers (r, neg, na_result);
+ store_variant_list (r, neg);
+ res = NOT_ACCEPTABLE;
+ goto return_from_multi;
+ }
+
+ if (na_result == na_choice)
+ if ((res = setup_choice_response(r, neg, best)) != 0) {
+ goto return_from_multi;
+ }
+
+ if (! (sub_req = best->sub_req)) {
+ /* We got this out of a map file, so we don't actually have
+ * a sub_req structure yet. Get one now.
+ */
+
+ sub_req = sub_req_lookup_file (best->file_name, r);
+ if (sub_req->status != HTTP_OK) {
+ res = sub_req->status;
+ destroy_sub_req(sub_req);
+ goto return_from_multi;
+ }
+ }
+
+ /* BLETCH --- don't multi-resolve non-ordinary files */
+
+ if (!S_ISREG(sub_req->finfo.st_mode)) {
+ res = NOT_FOUND;
+ goto return_from_multi;
+ }
+
+ /* Otherwise, use it. */
+
+ if (!do_cache_negotiated_docs(r->server) && (r->proto_num < 1001))
+ r->no_cache = 1;
+
+ if (na_result == na_not_applied)
+ set_neg_headers(r, neg, na_not_applied);
+
+ r->filename = sub_req->filename;
+ r->handler = sub_req->handler;
+ r->content_type = sub_req->content_type;
+ r->content_encoding = sub_req->content_encoding;
+ r->content_languages = sub_req->content_languages;
+ r->content_language = sub_req->content_language;
+ r->finfo = sub_req->finfo;
+ r->per_dir_config = sub_req->per_dir_config;
+ /* copy output headers from subrequest, but leave negotiation headers */
+ r->notes = overlay_tables(r->pool, sub_req->notes, r->notes);
+ r->headers_out = overlay_tables(r->pool, sub_req->headers_out,
+ r->headers_out);
+ r->err_headers_out = overlay_tables(r->pool, sub_req->err_headers_out,
+ r->err_headers_out);
+ r->subprocess_env = overlay_tables(r->pool, sub_req->subprocess_env,
+ r->subprocess_env);
+ avail_recs = (var_rec *)neg->avail_vars->elts;
+ for (j = 0; j < neg->avail_vars->nelts; ++j) {
+ var_rec *variant = &avail_recs[j];
+ if (variant != best && variant->sub_req) {
+ destroy_sub_req(variant->sub_req);
+ }
+ }
+ 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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_rewrite.c b/usr.sbin/httpd/src/mod_rewrite.c
new file mode 100644
index 00000000000..15fb1d8dffa
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_rewrite.c
@@ -0,0 +1,3335 @@
+
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/>.
+ *
+ */
+
+
+/* _ _ _
+** _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___
+** | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \
+** | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/
+** |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___|
+** |_____|
+**
+** URL Rewriting Module
+**
+** This module uses a rule-based rewriting engine (based on a
+** regular-expression parser) to rewrite requested URLs on the fly.
+**
+** It supports an unlimited number of additional rule conditions (which can
+** operate on a lot of variables, even on HTTP headers) for granular
+** matching and even external database lookups (either via plain text
+** tables, DBM hash files or even external processes) for advanced URL
+** substitution.
+**
+** It operates on the full URLs (including the PATH_INFO part) both in
+** per-server context (httpd.conf) and per-dir context (.htaccess) and even
+** can generate QUERY_STRING parts on result. The rewriting result finally
+** can lead to internal subprocessing, external request redirection or even
+** to internal proxy throughput.
+**
+** This module was originally written in April 1996 and
+** gifted exclusively to the The Apache Group in July 1997 by
+**
+** Ralf S. Engelschall
+** rse@engelschall.com
+** www.engelschall.com
+*/
+
+
+
+
+ /* from the underlaying Unix system ... */
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+
+ /* from the Apache server ... */
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_log.h"
+
+ /* now our own stuff ... */
+#include "mod_rewrite.h"
+
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | static module configuration
+** | |
+** +-------------------------------------------------------+
+*/
+
+
+/*
+**
+** our interface to the Apache server kernel
+**
+** keep in mind:
+**
+** o Runtime logic of a request is as following:
+**
+** while(request or subrequest) {
+** foreach(stage #1...#9) {
+** foreach(module) { (**)
+** try to run hook
+** }
+** }
+** }
+**
+** o the order of modules at (**) is the inverted order as
+** given in the "Configuration" file, i.e. the last module
+** specified is the first one called for each hook!
+** The core module is always the last!
+**
+** o there are two different types of result checking and
+** continue processing:
+** for hook #1,#4,#5,#6,#8:
+** hook run loop stops on first modules which gives
+** back a result != DECLINED, i.e. it usually returns OK
+** which says "OK, module has handled this _stage_" and for #1
+** this have not to mean "Ok, the filename is now valid".
+** for hook #2,#3,#7,#9:
+** all hooks are run, independend of result
+**
+** o at the last stage, the core module always
+** - says "BAD_REQUEST" if r->filename does not begin with "/"
+** - prefix URL with document_root or replaced server_root
+** with document_root and sets r->filename
+** - always return a "OK" independed if the file really exists
+** or not!
+**
+*/
+
+ /* the table of commands we provide */
+static command_rec command_table[] = {
+ { "RewriteEngine", cmd_rewriteengine, NULL, OR_FILEINFO, FLAG,
+ "On or Off to enable or disable (default) the whole rewriting engine" },
+ { "RewriteOptions", cmd_rewriteoptions, NULL, OR_FILEINFO, ITERATE,
+ "List of option strings to set" },
+ { "RewriteBase", cmd_rewritebase, NULL, OR_FILEINFO, TAKE1,
+ "the base URL of the per-directory context" },
+ { "RewriteCond", cmd_rewritecond, NULL, OR_FILEINFO, RAW_ARGS,
+ "a input string and a to be applied regexp-pattern" },
+ { "RewriteRule", cmd_rewriterule, NULL, OR_FILEINFO, RAW_ARGS,
+ "a URL-applied regexp-pattern and a substitution URL" },
+ { "RewriteMap", cmd_rewritemap, NULL, RSRC_CONF, TAKE2,
+ "a mapname and a filename" },
+ { "RewriteLog", cmd_rewritelog, NULL, RSRC_CONF, TAKE1,
+ "the filename of the rewriting logfile" },
+ { "RewriteLogLevel", cmd_rewriteloglevel, NULL, RSRC_CONF, TAKE1,
+ "the level of the rewriting logfile verbosity (0=none, 1=std, .., 9=max)" },
+ { NULL }
+};
+
+ /* the table of content handlers we provide */
+static handler_rec handler_table[] = {
+ { "redirect-handler", handler_redirect },
+ { NULL }
+};
+
+ /* the main config structure */
+module rewrite_module = {
+ STANDARD_MODULE_STUFF,
+
+ init_module, /* module initializer */
+
+ config_perdir_create, /* create per-dir config structures */
+ config_perdir_merge, /* merge per-dir config structures */
+ config_server_create, /* create per-server config structures */
+ config_server_merge, /* merge per-server config structures */
+ command_table, /* table of config file commands */
+
+ handler_table, /* [#8] table of MIME-typed-dispatched request action handlers */
+
+ hook_uri2file, /* [#1] URI to filename translation */
+
+ NULL, /* [#4] check_user_id: get and validate user id from the HTTP request */
+ NULL, /* [#5] check_auth: check if the user is ok _here_ */
+ NULL, /* [#2] check_access: check access by host address, etc. */
+
+ hook_mimetype, /* [#6] determine MIME type */
+
+ hook_fixup, /* [#7] pre-run fixups */
+ NULL, /* [#9] log a transaction */
+ NULL /* [#3] header parser */
+};
+
+ /* the cache */
+static cache *cachep;
+
+ /* whether proxy module is available or not */
+static int proxy_available;
+
+ /* the txt mapfile parsing stuff */
+#define MAPFILE_PATTERN "^([^ \t]+)[ \t]+([^ \t]+).*$"
+#define MAPFILE_OUTPUT "$1,$2"
+static regex_t *lookup_map_txtfile_regexp = NULL;
+static regmatch_t lookup_map_txtfile_regmatch[10];
+
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | configuration directive handling
+** | |
+** +-------------------------------------------------------+
+*/
+
+
+/*
+**
+** per-server configuration structure handling
+**
+*/
+
+static void *config_server_create(pool *p, server_rec *s)
+{
+ rewrite_server_conf *a;
+
+ a = (rewrite_server_conf *)pcalloc(p, sizeof(rewrite_server_conf));
+
+ a->state = ENGINE_DISABLED;
+ a->options = OPTION_NONE;
+ a->rewritelogfile = NULL;
+ a->rewritelogfp = -1;
+ a->rewriteloglevel = 1;
+ a->rewritemaps = make_array(p, 2, sizeof(rewritemap_entry));
+ a->rewriteconds = make_array(p, 2, sizeof(rewritecond_entry));
+ a->rewriterules = make_array(p, 2, sizeof(rewriterule_entry));
+
+ return (void *)a;
+}
+
+static void *config_server_merge(pool *p, void *basev, void *overridesv)
+{
+ rewrite_server_conf *a, *base, *overrides;
+
+ a = (rewrite_server_conf *)pcalloc(p, sizeof(rewrite_server_conf));
+ base = (rewrite_server_conf *)basev;
+ overrides = (rewrite_server_conf *)overridesv;
+
+ a->state = overrides->state;
+ a->options = overrides->options;
+ a->rewritelogfile = base->rewritelogfile != NULL ? base->rewritelogfile : overrides->rewritelogfile;
+ a->rewritelogfp = base->rewritelogfp != -1 ? base->rewritelogfp : overrides->rewritelogfp;
+ a->rewriteloglevel = overrides->rewriteloglevel;
+
+ if (a->options & OPTION_INHERIT) {
+ a->rewritemaps = append_arrays(p, overrides->rewritemaps, base->rewritemaps);
+ a->rewriteconds = append_arrays(p, overrides->rewriteconds, base->rewriteconds);
+ a->rewriterules = append_arrays(p, overrides->rewriterules, base->rewriterules);
+ }
+ else {
+ a->rewritemaps = overrides->rewritemaps;
+ a->rewriteconds = overrides->rewriteconds;
+ a->rewriterules = overrides->rewriterules;
+ }
+
+ return (void *)a;
+}
+
+
+/*
+**
+** per-directory configuration structure handling
+**
+*/
+
+static void *config_perdir_create(pool *p, char *path)
+{
+ rewrite_perdir_conf *a;
+
+ a = (rewrite_perdir_conf *)pcalloc(p, sizeof(rewrite_perdir_conf));
+
+ a->state = ENGINE_DISABLED;
+ a->options = OPTION_NONE;
+ a->baseurl = NULL;
+ a->rewriteconds = make_array(p, 2, sizeof(rewritecond_entry));
+ a->rewriterules = make_array(p, 2, sizeof(rewriterule_entry));
+
+ if (path == NULL)
+ a->directory = NULL;
+ else {
+ /* make sure it has a trailing slash */
+ if (path[strlen(path)-1] == '/')
+ a->directory = pstrdup(p, path);
+ else
+ a->directory = pstrcat(p, path, "/", NULL);
+ }
+
+ return (void *)a;
+}
+
+static void *config_perdir_merge(pool *p, void *basev, void *overridesv)
+{
+ rewrite_perdir_conf *a, *base, *overrides;
+
+ a = (rewrite_perdir_conf *)pcalloc(p, sizeof(rewrite_perdir_conf));
+ base = (rewrite_perdir_conf *)basev;
+ overrides = (rewrite_perdir_conf *)overridesv;
+
+ a->state = overrides->state;
+ a->options = overrides->options;
+ a->directory = overrides->directory;
+ a->baseurl = overrides->baseurl;
+
+ if (a->options & OPTION_INHERIT) {
+ a->rewriteconds = append_arrays(p, overrides->rewriteconds, base->rewriteconds);
+ a->rewriterules = append_arrays(p, overrides->rewriterules, base->rewriterules);
+ }
+ else {
+ a->rewriteconds = overrides->rewriteconds;
+ a->rewriterules = overrides->rewriterules;
+ }
+
+ return (void *)a;
+}
+
+
+/*
+**
+** the configuration commands
+**
+*/
+
+static const char *cmd_rewriteengine(cmd_parms *cmd, rewrite_perdir_conf *dconf, int flag)
+{
+ rewrite_server_conf *sconf;
+
+ sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module);
+ if (cmd->path == NULL) /* is server command */
+ sconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED);
+ else /* is per-directory command */
+ dconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED);
+
+ return NULL;
+}
+
+static const char *cmd_rewriteoptions(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *option)
+{
+ rewrite_server_conf *sconf;
+ const char *err;
+
+ sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module);
+ if (cmd->path == NULL) /* is server command */
+ err = cmd_rewriteoptions_setoption(cmd->pool, &(sconf->options), option);
+ else /* is per-directory command */
+ err = cmd_rewriteoptions_setoption(cmd->pool, &(dconf->options), option);
+
+ return err;
+}
+
+static const char *cmd_rewriteoptions_setoption(pool *p, int *options, char *name)
+{
+ if (strcasecmp(name, "inherit") == 0)
+ *options |= OPTION_INHERIT;
+ else
+ return pstrcat(p, "RewriteOptions: unknown option '", name, "'\n", NULL);
+ return NULL;
+}
+
+static const char *cmd_rewritelog(cmd_parms *cmd, void *dconf, char *a1)
+{
+ rewrite_server_conf *sconf;
+
+ sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module);
+ sconf->rewritelogfile = a1;
+
+ return NULL;
+}
+
+static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, char *a1)
+{
+ rewrite_server_conf *sconf;
+
+ sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module);
+ sconf->rewriteloglevel = atoi(a1);
+
+ return NULL;
+}
+
+static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, char *a1, char *a2)
+{
+ rewrite_server_conf *sconf;
+ rewritemap_entry *new;
+ struct stat st;
+
+ sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module);
+ new = push_array(sconf->rewritemaps);
+
+ new->name = a1;
+ if (strncmp(a2, "txt:", 4) == 0) {
+ new->type = MAPTYPE_TXT;
+ new->datafile = a2+4;
+ new->checkfile = a2+4;
+ }
+ else if (strncmp(a2, "dbm:", 4) == 0) {
+#ifdef HAS_NDBM_LIB
+ new->type = MAPTYPE_DBM;
+ new->datafile = a2+4;
+ new->checkfile = pstrcat(cmd->pool, a2+4, NDBM_FILE_SUFFIX, NULL);
+#else
+ return pstrdup(cmd->pool, "RewriteMap: cannot use NDBM mapfile, because no NDBM support compiled in");
+#endif
+ }
+ else if (strncmp(a2, "prg:", 4) == 0) {
+ new->type = MAPTYPE_PRG;
+ new->datafile = a2+4;
+ new->checkfile = a2+4;
+ }
+ else {
+ new->type = MAPTYPE_TXT;
+ new->datafile = a2;
+ new->checkfile = a2;
+ }
+ new->fpin = 0;
+ new->fpout = 0;
+
+ if (new->checkfile)
+ if (stat(new->checkfile, &st) == -1)
+ return pstrcat(cmd->pool, "RewriteMap: map file or program not found:", new->checkfile, NULL);
+
+ return NULL;
+}
+
+static const char *cmd_rewritebase(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *a1)
+{
+ if (cmd->path == NULL || dconf == NULL)
+ return "RewriteBase: only valid in per-directory config files";
+ if (a1[0] != '/')
+ return "RewriteBase: argument is not a valid URL";
+ if (a1[0] == '\0')
+ return "RewriteBase: empty URL not allowed";
+
+ dconf->baseurl = pstrdup(cmd->pool, a1);
+
+ return NULL;
+}
+
+static const char *cmd_rewritecond(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *str)
+{
+ rewrite_server_conf *sconf;
+ rewritecond_entry *new;
+ regex_t *regexp;
+ char *a1;
+ char *a2;
+ char *a3;
+ char *cp;
+ const char *err;
+ int rc;
+
+ sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module);
+
+ /* make a new entry in the internal temporary rewrite rule list */
+ if (cmd->path == NULL) /* is server command */
+ new = push_array(sconf->rewriteconds);
+ else /* is per-directory command */
+ new = push_array(dconf->rewriteconds);
+
+ /* parse the argument line ourself */
+ if (parseargline(str, &a1, &a2, &a3))
+ return pstrcat(cmd->pool, "RewriteCond: bad argument line '", str, "'\n", NULL);
+
+ /* arg1: the input string */
+ new->input = pstrdup(cmd->pool, a1);
+
+ /* arg3: optional flags field
+ (this have to be first parsed, because we need to
+ know if the regex should be compiled with ICASE!) */
+ new->flags = CONDFLAG_NONE;
+ if (a3 != NULL) {
+ if ((err = cmd_rewritecond_parseflagfield(cmd->pool, new, a3)) != NULL)
+ return err;
+ }
+
+ /* arg2: the pattern
+ try to compile the regexp to test if is ok */
+ cp = a2;
+ if (cp[0] == '!') {
+ new->flags |= CONDFLAG_NOTMATCH;
+ cp++;
+ }
+
+ /* now be careful: Under the POSIX regex library
+ we can compile the pattern for case-insensitive matching,
+ under the old V8 library we have to do it self via a hack */
+ if (new->flags & CONDFLAG_NOCASE)
+ rc = ((regexp = pregcomp(cmd->pool, cp, REG_EXTENDED|REG_ICASE)) == NULL);
+ else
+ rc = ((regexp = pregcomp(cmd->pool, cp, REG_EXTENDED)) == NULL);
+ if (rc)
+ return pstrcat(cmd->pool, "RewriteCond: cannot compile regular expression '", a2, "'\n", NULL);
+ new->pattern = pstrdup(cmd->pool, cp);
+ new->regexp = regexp;
+
+ return NULL;
+}
+
+static const char *cmd_rewritecond_parseflagfield(pool *p, rewritecond_entry *cfg, char *str)
+{
+ char *cp;
+ char *cp1;
+ char *cp2;
+ char *cp3;
+ char *key;
+ char *val;
+ const char *err;
+
+ if (str[0] != '[' || str[strlen(str)-1] != ']')
+ return pstrdup(p, "RewriteCond: bad flag delimiters");
+
+ cp = str+1;
+ str[strlen(str)-1] = ','; /* for simpler parsing */
+ for ( ; *cp != '\0'; ) {
+ /* skip whitespaces */
+ for ( ; (*cp == ' ' || *cp == '\t') && *cp != '\0'; cp++)
+ ;
+ if (*cp == '\0')
+ break;
+ cp1 = cp;
+ if ((cp2 = strchr(cp, ',')) != NULL) {
+ cp = cp2+1;
+ for ( ; (*(cp2-1) == ' ' || *(cp2-1) == '\t'); cp2--)
+ ;
+ *cp2 = '\0';
+ if ((cp3 = strchr(cp1, '=')) != NULL) {
+ *cp3 = '\0';
+ key = cp1;
+ val = cp3+1;
+ }
+ else {
+ key = cp1;
+ val = "";
+ }
+ if ((err = cmd_rewritecond_setflag(p, cfg, key, val)) != NULL)
+ return err;
+ }
+ else
+ break;
+ }
+
+ return NULL;
+}
+
+static const char *cmd_rewritecond_setflag(pool *p, rewritecond_entry *cfg, char *key, char *val)
+{
+ if ( strcasecmp(key, "nocase") == 0
+ || strcasecmp(key, "NC") == 0 ) {
+ cfg->flags |= CONDFLAG_NOCASE;
+ }
+ else if ( strcasecmp(key, "ornext") == 0
+ || strcasecmp(key, "OR") == 0 ) {
+ cfg->flags |= CONDFLAG_ORNEXT;
+ }
+ else {
+ return pstrcat(p, "RewriteCond: unknown flag '", key, "'\n", NULL);
+ }
+ return NULL;
+}
+
+/* NON static */
+const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *str)
+{
+ rewrite_server_conf *sconf;
+ rewriterule_entry *new;
+ regex_t *regexp;
+ char *a1;
+ char *a2;
+ char *a3;
+ char *cp;
+ const char *err;
+
+ sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module);
+
+ /* make a new entry in the internal rewrite rule list */
+ if (cmd->path == NULL) /* is server command */
+ new = push_array(sconf->rewriterules);
+ else /* is per-directory command */
+ new = push_array(dconf->rewriterules);
+
+ /* parse the argument line ourself */
+ if (parseargline(str, &a1, &a2, &a3))
+ return pstrcat(cmd->pool, "RewriteRule: bad argument line '", str, "'\n", NULL);
+
+ /* arg1: the pattern
+ try to compile the regexp to test if is ok */
+ new->flags = RULEFLAG_NONE;
+ cp = a1;
+ if (cp[0] == '!') {
+ new->flags |= RULEFLAG_NOTMATCH;
+ cp++;
+ }
+ if ((regexp = pregcomp(cmd->pool, cp, REG_EXTENDED)) == NULL)
+ return pstrcat(cmd->pool, "RewriteRule: cannot compile regular expression '", a1, "'\n", NULL);
+ new->pattern = pstrdup(cmd->pool, cp);
+ new->regexp = regexp;
+
+ /* arg2: the output string
+ replace the $<N> by \<n> which is needed by the currently
+ used Regular Expression library */
+ new->output = pstrdup(cmd->pool, a2);
+
+ /* arg3: optional flags field */
+ new->forced_mimetype = NULL;
+ new->forced_responsecode = HTTP_MOVED_TEMPORARILY;
+ new->env[0] = NULL;
+ new->skip = 0;
+ if (a3 != NULL) {
+ if ((err = cmd_rewriterule_parseflagfield(cmd->pool, new, a3)) != NULL)
+ return err;
+ }
+
+ /* now, if the server or per-dir config holds an
+ array of RewriteCond entries, we take it for us
+ and clear the array */
+ if (cmd->path == NULL) { /* is server command */
+ new->rewriteconds = sconf->rewriteconds;
+ sconf->rewriteconds = make_array(cmd->pool, 2, sizeof(rewritecond_entry));
+ }
+ else { /* is per-directory command */
+ new->rewriteconds = dconf->rewriteconds;
+ dconf->rewriteconds = make_array(cmd->pool, 2, sizeof(rewritecond_entry));
+ }
+
+ return NULL;
+}
+
+static const char *cmd_rewriterule_parseflagfield(pool *p, rewriterule_entry *cfg, char *str)
+{
+ char *cp;
+ char *cp1;
+ char *cp2;
+ char *cp3;
+ char *key;
+ char *val;
+ const char *err;
+
+ if (str[0] != '[' || str[strlen(str)-1] != ']')
+ return pstrdup(p, "RewriteRule: bad flag delimiters");
+
+ cp = str+1;
+ str[strlen(str)-1] = ','; /* for simpler parsing */
+ for ( ; *cp != '\0'; ) {
+ /* skip whitespaces */
+ for ( ; (*cp == ' ' || *cp == '\t') && *cp != '\0'; cp++)
+ ;
+ if (*cp == '\0')
+ break;
+ cp1 = cp;
+ if ((cp2 = strchr(cp, ',')) != NULL) {
+ cp = cp2+1;
+ for ( ; (*(cp2-1) == ' ' || *(cp2-1) == '\t'); cp2--)
+ ;
+ *cp2 = '\0';
+ if ((cp3 = strchr(cp1, '=')) != NULL) {
+ *cp3 = '\0';
+ key = cp1;
+ val = cp3+1;
+ }
+ else {
+ key = cp1;
+ val = "";
+ }
+ if ((err = cmd_rewriterule_setflag(p, cfg, key, val)) != NULL)
+ return err;
+ }
+ else
+ break;
+ }
+
+ return NULL;
+}
+
+static const char *cmd_rewriterule_setflag(pool *p, rewriterule_entry *cfg, char *key, char *val)
+{
+ int status = 0;
+ int i;
+
+ if ( strcasecmp(key, "redirect") == 0
+ || strcasecmp(key, "R") == 0 ) {
+ cfg->flags |= RULEFLAG_FORCEREDIRECT;
+ if (strlen(val) > 0) {
+ if (strcasecmp(val, "permanent") == 0)
+ status = HTTP_MOVED_PERMANENTLY;
+ else if (strcasecmp(val, "temp") == 0)
+ status = HTTP_MOVED_TEMPORARILY;
+ else if (strcasecmp(val, "seeother") == 0)
+ status = HTTP_SEE_OTHER;
+ else if (isdigit(*val))
+ status = atoi(val);
+ if (!is_HTTP_REDIRECT(status))
+ return pstrdup(p, "RewriteRule: invalid HTTP response code for flag 'R'");
+ cfg->forced_responsecode = status;
+ }
+ }
+ else if ( strcasecmp(key, "last") == 0
+ || strcasecmp(key, "L") == 0 ) {
+ cfg->flags |= RULEFLAG_LASTRULE;
+ }
+ else if ( strcasecmp(key, "next") == 0
+ || strcasecmp(key, "N") == 0 ) {
+ cfg->flags |= RULEFLAG_NEWROUND;
+ }
+ else if ( strcasecmp(key, "chain") == 0
+ || strcasecmp(key, "C") == 0 ) {
+ cfg->flags |= RULEFLAG_CHAIN;
+ }
+ else if ( strcasecmp(key, "type") == 0
+ || strcasecmp(key, "T") == 0 ) {
+ cfg->forced_mimetype = pstrdup(p, val);
+ }
+ else if ( strcasecmp(key, "env") == 0
+ || strcasecmp(key, "E") == 0 ) {
+ for (i = 0; (cfg->env[i] != NULL) && (i < MAX_ENV_FLAGS); i++)
+ ;
+ if (i < MAX_ENV_FLAGS) {
+ cfg->env[i] = pstrdup(p, val);
+ cfg->env[i+1] = NULL;
+ }
+ else
+ return pstrdup(p, "RewriteRule: to much environment flags 'E'");
+ }
+ else if ( strcasecmp(key, "nosubreq") == 0
+ || strcasecmp(key, "NS") == 0 ) {
+ cfg->flags |= RULEFLAG_IGNOREONSUBREQ;
+ }
+ else if ( strcasecmp(key, "proxy") == 0
+ || strcasecmp(key, "P") == 0 ) {
+ cfg->flags |= RULEFLAG_PROXY;
+ }
+ else if ( strcasecmp(key, "passthrough") == 0
+ || strcasecmp(key, "PT") == 0 ) {
+ cfg->flags |= RULEFLAG_PASSTHROUGH;
+ }
+ else if ( strcasecmp(key, "skip") == 0
+ || strcasecmp(key, "S") == 0 ) {
+ cfg->skip = atoi(val);
+ }
+ else if ( strcasecmp(key, "forbidden") == 0
+ || strcasecmp(key, "F") == 0 ) {
+ cfg->flags |= RULEFLAG_FORBIDDEN;
+ }
+ else if ( strcasecmp(key, "gone") == 0
+ || strcasecmp(key, "G") == 0 ) {
+ cfg->flags |= RULEFLAG_GONE;
+ }
+ else if ( strcasecmp(key, "qsappend") == 0
+ || strcasecmp(key, "QSA") == 0 ) {
+ cfg->flags |= RULEFLAG_QSAPPEND;
+ }
+ else {
+ return pstrcat(p, "RewriteRule: unknown flag '", key, "'\n", NULL);
+ }
+ return NULL;
+}
+
+
+/*
+**
+** module initialisation
+** [called from read_config() after all
+** config commands were already called]
+**
+*/
+
+static void init_module(server_rec *s, pool *p)
+{
+ /* step through the servers and
+ - open eachs rewriting logfile
+ - open the RewriteMap prg:xxx programs */
+ for (; s; s = s->next) {
+ open_rewritelog(s, p);
+ run_rewritemap_programs(s, p);
+ }
+
+ /* create the lookup cache */
+ cachep = init_cache(p);
+
+ /* check if proxy module is available */
+ proxy_available = is_proxy_available(s);
+
+ /* precompile a static pattern
+ for the txt mapfile parsing */
+ lookup_map_txtfile_regexp = pregcomp(p, MAPFILE_PATTERN, REG_EXTENDED);
+}
+
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | runtime hooks
+** | |
+** +-------------------------------------------------------+
+*/
+
+
+/*
+**
+** URI-to-filename hook
+**
+** [used for the rewriting engine triggered by
+** the per-server 'RewriteRule' directives]
+**
+*/
+
+static int hook_uri2file(request_rec *r)
+{
+ void *sconf;
+ rewrite_server_conf *conf;
+ char *var;
+ char *thisserver, *thisport, *thisurl;
+ char buf[512];
+ char docroot[512];
+ char *cp, *cp2;
+ struct stat finfo;
+ int n;
+ int l;
+
+ /*
+ * retrieve the config structures
+ */
+ sconf = r->server->module_config;
+ conf = (rewrite_server_conf *)get_module_config(sconf, &rewrite_module);
+
+ /*
+ * only do something under runtime if the engine is really enabled,
+ * else return immediately!
+ */
+ if (conf->state == ENGINE_DISABLED)
+ return DECLINED;
+
+ /*
+ * add the SCRIPT_URL variable to the env. this is a bit complicated
+ * due to the fact that apache uses subrequests and internal redirects
+ */
+
+ if (r->main == NULL) {
+ var = pstrcat(r->pool, "REDIRECT_", ENVVAR_SCRIPT_URL, NULL);
+ var = table_get(r->subprocess_env, var);
+ if (var == NULL)
+ table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, pstrdup(r->pool, r->uri));
+ else
+ table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, pstrdup(r->pool, var));
+ }
+ else {
+ var = table_get(r->main->subprocess_env, ENVVAR_SCRIPT_URL);
+ table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, pstrdup(r->pool, var));
+ }
+
+ /*
+ * create the SCRIPT_URI variable for the env
+ */
+
+ /* add the canonical URI of this URL */
+ thisserver = r->server->server_hostname;
+#ifdef APACHE_SSL
+ if (((!r->connection->client->ssl) && (r->server->port == DEFAULT_PORT)) ||
+ ((r->connection->client->ssl) && (r->server->port == 443)))
+#else
+ if (r->server->port == DEFAULT_PORT)
+#endif
+ thisport = "";
+ else {
+ ap_snprintf(buf, sizeof(buf), ":%u", r->server->port);
+ thisport = pstrdup(r->pool, buf);
+ }
+ thisurl = table_get(r->subprocess_env, ENVVAR_SCRIPT_URL);
+
+ /* set the variable */
+#ifdef APACHE_SSL
+ var = pstrcat(r->pool, http_method(r), "://", thisserver, thisport, thisurl, NULL);
+#else
+ var = pstrcat(r->pool, "http://", thisserver, thisport, thisurl, NULL);
+#endif
+ table_set(r->subprocess_env, ENVVAR_SCRIPT_URI, pstrdup(r->pool, var));
+
+
+ /* if filename was not initially set,
+ we start with the requested URI */
+ if (r->filename == NULL) {
+ r->filename = pstrdup(r->pool, r->uri);
+ rewritelog(r, 2, "init rewrite engine with requested uri %s", r->filename);
+ }
+
+ /*
+ * now apply the rules ...
+ */
+ if (apply_rewrite_list(r, conf->rewriterules, NULL)) {
+
+ if (strlen(r->filename) > 6 &&
+ strncmp(r->filename, "proxy:", 6) == 0) {
+ /* it should be go on as an internal proxy request */
+
+ /* check if the proxy module is enabled, so
+ we can actually use it! */
+ if (!proxy_available) {
+ log_reason("attempt to make remote request from mod_rewrite "
+ "without proxy enabled", r->filename, r);
+ return FORBIDDEN;
+ }
+
+ /* make sure the QUERY_STRING and
+ PATH_INFO parts get incorporated */
+ r->filename = pstrcat(r->pool, r->filename,
+ r->path_info ? r->path_info : "",
+ r->args ? "?" : NULL, r->args,
+ NULL);
+
+ /* now make sure the request gets handled by the
+ proxy handler */
+ r->proxyreq = 1;
+ r->handler = "proxy-server";
+
+ rewritelog(r, 1, "go-ahead with proxy request %s [OK]", r->filename);
+ return OK;
+ }
+ else if ( (strlen(r->filename) > 7 &&
+ strncasecmp(r->filename, "http://", 7) == 0)
+ || (strlen(r->filename) > 8 &&
+ strncasecmp(r->filename, "https://", 8) == 0)
+ || (strlen(r->filename) > 9 &&
+ strncasecmp(r->filename, "gopher://", 9) == 0)
+ || (strlen(r->filename) > 6 &&
+ strncasecmp(r->filename, "ftp://", 6) == 0) ) {
+ /* it was finally rewritten to a remote URL */
+
+ /* skip 'scheme:' */
+ for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++)
+ ;
+ /* skip '://' */
+ cp += 3;
+ /* skip host part */
+ for ( ; *cp != '/' && *cp != '\0'; cp++)
+ ;
+ if (*cp != '\0') {
+ rewritelog(r, 1, "escaping %s for redirect", r->filename);
+ cp2 = escape_uri(r->pool, cp);
+ *cp = '\0';
+ r->filename = pstrcat(r->pool, r->filename, cp2, NULL);
+ }
+
+ /* append the QUERY_STRING part */
+ if (r->args != NULL)
+ r->filename = pstrcat(r->pool, r->filename, "?", r->args, NULL);
+
+ /* determine HTTP redirect response code */
+ if (is_HTTP_REDIRECT(r->status)) {
+ n = r->status;
+ r->status = HTTP_OK; /* make Apache kernel happy */
+ }
+ else
+ n = REDIRECT;
+
+ /* now do the redirection */
+ table_set(r->headers_out, "Location", r->filename);
+ rewritelog(r, 1, "redirect to %s [REDIRECT/%d]", r->filename, n);
+ return n;
+ }
+ else if (strlen(r->filename) > 10 &&
+ strncmp(r->filename, "forbidden:", 10) == 0) {
+ /* This URLs is forced to be forbidden for the requester */
+ return FORBIDDEN;
+ }
+ else if (strlen(r->filename) > 5 &&
+ strncmp(r->filename, "gone:", 5) == 0) {
+ /* This URLs is forced to be gone */
+ return HTTP_GONE;
+ }
+ else if (strlen(r->filename) > 12 &&
+ strncmp(r->filename, "passthrough:", 12) == 0) {
+ /* Hack because of underpowered API: passing the current
+ rewritten filename through to other URL-to-filename handlers
+ just as it were the requested URL. This is to enable
+ post-processing by mod_alias, etc. which always act on
+ r->uri! The difference here is: We do not try to
+ add the document root */
+ r->uri = pstrdup(r->pool, r->filename+12);
+ return DECLINED;
+ }
+ else {
+ /* it was finally rewritten to a local path */
+
+ /* expand "/~user" prefix */
+ r->filename = expand_tildepaths(r, r->filename);
+
+ rewritelog(r, 2, "local path result: %s", r->filename);
+
+ /* the filename has to start with a slash! */
+ if (r->filename[0] != '/')
+ return BAD_REQUEST;
+
+ /* if there is no valid prefix, we have
+ to emulate the translator from the core and
+ prefix the filename with document_root
+
+ NOTICE:
+ We cannot leave out the prefix_stat because
+ - when we always prefix with document_root
+ then no absolute path can be created, e.g. via
+ emulating a ScriptAlias directive, etc.
+ - when we always NOT prefix with document_root
+ then the files under document_root have to
+ be references directly and document_root
+ gets never used and will be a dummy parameter -
+ this is also bad
+
+ BUT:
+ Under real Unix systems this is no problem,
+ because we only do stat() on the first directory
+ and this gets cached by the kernel for along time!
+ */
+ n = prefix_stat(r->filename, &finfo);
+ if (n == 0) {
+ if ((cp = document_root(r)) != NULL) {
+ strncpy(docroot, cp, sizeof(docroot)-1);
+ EOS_PARANOIA(docroot);
+
+ /* always NOT have a trailing slash */
+ l = strlen(docroot);
+ if (docroot[l-1] == '/') {
+ docroot[l-1] = '\0';
+ }
+ if (r->server->path && !strncmp(r->filename, r->server->path, r->server->pathlen))
+ r->filename = pstrcat(r->pool, docroot, (r->filename + r->server->pathlen), NULL);
+ else
+ r->filename = pstrcat(r->pool, docroot, r->filename, NULL);
+ rewritelog(r, 2, "prefixed with document_root to %s", r->filename);
+ }
+ }
+
+ rewritelog(r, 1, "go-ahead with %s [OK]", r->filename);
+ return OK;
+ }
+ }
+ else {
+ rewritelog(r, 1, "pass through %s", r->filename);
+ return DECLINED;
+ }
+}
+
+
+/*
+**
+** MIME-type hook
+**
+** [used to support the forced-MIME-type feature]
+**
+*/
+
+static int hook_mimetype(request_rec *r)
+{
+ char *t;
+
+ /* now check if we have to force a MIME-type */
+ t = table_get(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR);
+ if (t == NULL)
+ return DECLINED;
+ else {
+ rewritelog(r, 1, "force filename %s to have MIME-type '%s'", r->filename, t);
+ r->content_type = t;
+ return OK;
+ }
+}
+
+
+/*
+**
+** Fixup hook
+**
+** [used for the rewriting engine triggered by
+** the per-directory 'RewriteRule' directives]
+**
+*/
+
+static int hook_fixup(request_rec *r)
+{
+ rewrite_perdir_conf *dconf;
+ char *cp;
+ char *cp2;
+ char *prefix;
+ int l;
+ int n;
+ char *ofilename;
+
+ dconf = (rewrite_perdir_conf *)get_module_config(r->per_dir_config, &rewrite_module);
+
+ /* if there is no per-dir config we return immediately */
+ if (dconf == NULL)
+ return DECLINED;
+
+ /* we shouldn't do anything in subrequests */
+ if (r->main != NULL)
+ return DECLINED;
+
+ /* if there are no real (i.e. no RewriteRule directives!)
+ per-dir config of us, we return also immediately */
+ if (dconf->directory == NULL)
+ return DECLINED;
+
+ /*
+ * only do something under runtime if the engine is really enabled,
+ * for this directory, else return immediately!
+ */
+ if (!(allow_options(r) & (OPT_SYM_LINKS | OPT_SYM_OWNER))) {
+ /* FollowSymLinks is mandatory! */
+ log_reason("Options FollowSymLinks or SymLinksIfOwnerMatch is off which implies that RewriteRule directive is forbidden", r->filename, r);
+ return FORBIDDEN;
+ }
+ else {
+ /* FollowSymLinks is given, but the user can
+ still turn off the rewriting engine */
+ if (dconf->state == ENGINE_DISABLED)
+ return DECLINED;
+ }
+
+ /*
+ * remember the current filename before rewriting for later check
+ * to prevent deadlooping because of internal redirects
+ * on final URL/filename which can be equal to the inital one.
+ */
+ ofilename = r->filename;
+
+ /*
+ * now apply the rules ...
+ */
+ if (apply_rewrite_list(r, dconf->rewriterules, dconf->directory)) {
+
+ if (strlen(r->filename) > 6 &&
+ strncmp(r->filename, "proxy:", 6) == 0) {
+ /* it should go on as an internal proxy request */
+
+ /* make sure the QUERY_STRING and
+ PATH_INFO parts get incorporated */
+ r->filename = pstrcat(r->pool, r->filename,
+ /* r->path_info was already
+ appended by the rewriting engine
+ because of the per-dir context! */
+ r->args ? "?" : NULL, r->args,
+ NULL);
+
+ /* now make sure the request gets handled by the
+ proxy handler */
+ r->proxyreq = 1;
+ r->handler = "proxy-server";
+
+ rewritelog(r, 1, "[per-dir %s] go-ahead with proxy request %s [OK]", dconf->directory, r->filename);
+ return OK;
+ }
+ else if ( (strlen(r->filename) > 7 &&
+ strncasecmp(r->filename, "http://", 7) == 0)
+ || (strlen(r->filename) > 8 &&
+ strncasecmp(r->filename, "https://", 8) == 0)
+ || (strlen(r->filename) > 9 &&
+ strncasecmp(r->filename, "gopher://", 9) == 0)
+ || (strlen(r->filename) > 6 &&
+ strncasecmp(r->filename, "ftp://", 6) == 0) ) {
+ /* it was finally rewritten to a remote URL */
+
+ /* because we are in a per-dir context
+ first try to replace the directory with its base-URL
+ if there is a base-URL available */
+ if (dconf->baseurl != NULL) {
+ /* skip 'scheme:' */
+ for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++)
+ ;
+ /* skip '://' */
+ cp += 3;
+ if ((cp = strchr(cp, '/')) != NULL) {
+ rewritelog(r, 2, "[per-dir %s] trying to replace prefix %s with %s", dconf->directory, dconf->directory, dconf->baseurl);
+ cp2 = subst_prefix_path(r, cp, dconf->directory, dconf->baseurl);
+ if (strcmp(cp2, cp) != 0) {
+ *cp = '\0';
+ r->filename = pstrcat(r->pool, r->filename, cp2, NULL);
+ }
+ }
+ }
+
+ /* now prepare the redirect... */
+
+ /* skip 'scheme:' */
+ for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++)
+ ;
+ /* skip '://' */
+ cp += 3;
+ /* skip host part */
+ for ( ; *cp != '/' && *cp != '\0'; cp++)
+ ;
+ if (*cp != '\0') {
+ rewritelog(r, 1, "[per-dir %s] escaping %s for redirect", dconf->directory, r->filename);
+ cp2 = escape_uri(r->pool, cp);
+ *cp = '\0';
+ r->filename = pstrcat(r->pool, r->filename, cp2, NULL);
+ }
+
+ /* append the QUERY_STRING part */
+ if (r->args != NULL)
+ r->filename = pstrcat(r->pool, r->filename, "?", r->args, NULL);
+
+ /* determine HTTP redirect response code */
+ if (is_HTTP_REDIRECT(r->status)) {
+ n = r->status;
+ r->status = HTTP_OK; /* make Apache kernel happy */
+ }
+ else
+ n = REDIRECT;
+
+ /* now do the redirection */
+ table_set(r->headers_out, "Location", r->filename);
+ rewritelog(r, 1, "[per-dir %s] redirect to %s [REDIRECT/%d]", dconf->directory, r->filename, n);
+ return n;
+ }
+ else if (strlen(r->filename) > 10 &&
+ strncmp(r->filename, "forbidden:", 10) == 0) {
+ /* This URLs is forced to be forbidden for the requester */
+ return FORBIDDEN;
+ }
+ else if (strlen(r->filename) > 5 &&
+ strncmp(r->filename, "gone:", 5) == 0) {
+ /* This URLs is forced to be gone */
+ return HTTP_GONE;
+ }
+ else {
+ /* it was finally rewritten to a local path */
+
+ /* if someone used the PASSTHROUGH flag in per-dir
+ context we just ignore it. It is only useful
+ in per-server context */
+ if (strlen(r->filename) > 12 &&
+ strncmp(r->filename, "passthrough:", 12) == 0) {
+ r->filename = pstrdup(r->pool, r->filename+12);
+ }
+
+ /* the filename has to start with a slash! */
+ if (r->filename[0] != '/')
+ return BAD_REQUEST;
+
+ /* Check for deadlooping:
+ * At this point we KNOW that at least one rewriting
+ * rule was applied, but when the resulting URL is
+ * the same as the initial URL, we are not allowed to
+ * use the following internal redirection stuff because
+ * this would lead to a deadloop.
+ */
+ if (strcmp(r->filename, ofilename) == 0) {
+ rewritelog(r, 1, "[per-dir %s] initial URL equal rewritten URL: %s [IGNORING REWRITE]", dconf->directory, r->filename);
+ return OK;
+ }
+
+ /* if there is a valid base-URL then substitute
+ the per-dir prefix with this base-URL if the
+ current filename still is inside this per-dir
+ context. If not then treat the result as a
+ plain URL */
+ if (dconf->baseurl != NULL) {
+ rewritelog(r, 2, "[per-dir %s] trying to replace prefix %s with %s", dconf->directory, dconf->directory, dconf->baseurl);
+ r->filename = subst_prefix_path(r, r->filename, dconf->directory, dconf->baseurl);
+ }
+ else {
+ /* if no explicit base-URL exists we assume
+ that the directory prefix is also a valid URL
+ for this webserver and only try to remove the
+ document_root if it is prefix */
+
+ if ((cp = document_root(r)) != NULL) {
+ prefix = pstrdup(r->pool, cp);
+ /* always NOT have a trailing slash */
+ l = strlen(prefix);
+ if (prefix[l-1] == '/') {
+ prefix[l-1] = '\0';
+ l--;
+ }
+ if (strncmp(r->filename, prefix, l) == 0) {
+ rewritelog(r, 2, "[per-dir %s] strip document_root prefix: %s -> %s", dconf->directory, r->filename, r->filename+l);
+ r->filename = pstrdup(r->pool, r->filename+l);
+ }
+ }
+ }
+
+ /* now initiate the internal redirect */
+ rewritelog(r, 1, "[per-dir %s] internal redirect with %s [INTERNAL REDIRECT]", dconf->directory, r->filename);
+ r->filename = pstrcat(r->pool, "redirect:", r->filename, NULL);
+ r->handler = "redirect-handler";
+ return OK;
+ }
+ }
+ else {
+ rewritelog(r, 1, "[per-dir %s] pass through %s", dconf->directory, r->filename);
+ return DECLINED;
+ }
+}
+
+
+/*
+**
+** Content-Handlers
+**
+** [used for redirect support]
+**
+*/
+
+static int handler_redirect(request_rec *r)
+{
+ /* just make sure that we are really meant! */
+ if (strncmp(r->filename, "redirect:", 9) != 0)
+ return DECLINED;
+
+ /* now do the internal redirect */
+ internal_redirect(pstrcat(r->pool, r->filename+9,
+ r->args ? "?" : NULL, r->args, NULL), r);
+
+ /* and return gracefully */
+ return OK;
+}
+
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | rewriting engine
+** | |
+** +-------------------------------------------------------+
+*/
+
+
+static int apply_rewrite_list(request_rec *r, array_header *rewriterules, char *perdir)
+{
+ rewriterule_entry *entries;
+ rewriterule_entry *p;
+ int i;
+ int changed;
+ int rc;
+ int s;
+
+ entries = (rewriterule_entry *)rewriterules->elts;
+ changed = 0;
+ loop:
+ for (i = 0; i < rewriterules->nelts; i++) {
+ p = &entries[i];
+
+ /* ignore this rule on subrequests if we are explicitly asked to do so
+ or this is a proxy throughput or a forced redirect rule */
+ if (r->main != NULL &&
+ (p->flags & RULEFLAG_IGNOREONSUBREQ ||
+ p->flags & RULEFLAG_PROXY ||
+ p->flags & RULEFLAG_FORCEREDIRECT ))
+ continue;
+
+ /* apply the current rule */
+ rc = apply_rewrite_rule(r, p, perdir);
+ if (rc) {
+ if (rc != 2) /* not a match-only rule */
+ changed = 1;
+ if (p->flags & RULEFLAG_PASSTHROUGH) {
+ rewritelog(r, 2, "forcing '%s' to get passed through to next URI-to-filename handler", r->filename);
+ r->filename = pstrcat(r->pool, "passthrough:", r->filename, NULL);
+ changed = 1;
+ break;
+ }
+ if (p->flags & RULEFLAG_FORBIDDEN) {
+ rewritelog(r, 2, "forcing '%s' to be forbidden", r->filename);
+ r->filename = pstrcat(r->pool, "forbidden:", r->filename, NULL);
+ changed = 1;
+ break;
+ }
+ if (p->flags & RULEFLAG_GONE) {
+ rewritelog(r, 2, "forcing '%s' to be gone", r->filename);
+ r->filename = pstrcat(r->pool, "gone:", r->filename, NULL);
+ changed = 1;
+ break;
+ }
+ if (p->flags & RULEFLAG_PROXY)
+ break;
+ if (p->flags & RULEFLAG_LASTRULE)
+ break;
+ if (p->flags & RULEFLAG_NEWROUND)
+ goto loop;
+
+ /* if we are forced to skip N next rules, do it now */
+ if (p->skip > 0) {
+ s = p->skip;
+ while ( i < rewriterules->nelts
+ && s > 0) {
+ i++;
+ p = &entries[i];
+ s--;
+ }
+ }
+ }
+ else {
+ /* if current rule is chained with next rule(s),
+ skip all this next rule(s) */
+ while ( i < rewriterules->nelts
+ && p->flags & RULEFLAG_CHAIN) {
+ i++;
+ p = &entries[i];
+ }
+ }
+ }
+ return changed;
+}
+
+static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p, char *perdir)
+{
+ char *uri;
+ char *output;
+ int flags;
+ char newuri[MAX_STRING_LEN];
+ char env[MAX_STRING_LEN];
+ char port[32];
+ char env2[MAX_STRING_LEN];
+ regex_t *regexp;
+ regmatch_t regmatch[10];
+ int rc;
+ int prefixstrip;
+ int i;
+ int failed;
+ array_header *rewriteconds;
+ rewritecond_entry *conds;
+ rewritecond_entry *c;
+
+ uri = r->filename;
+ regexp = p->regexp;
+ output = p->output;
+ flags = p->flags;
+
+ if (perdir != NULL && r->path_info != NULL && r->path_info[0] != '\0') {
+ rewritelog(r, 3, "[per-dir %s] add path-info postfix: %s -> %s%s", perdir, uri, uri, r->path_info);
+ uri = pstrcat(r->pool, uri, r->path_info, NULL);
+ }
+
+ prefixstrip = 0;
+ if (perdir != NULL) {
+ /* this is a per-directory match */
+ if ( strlen(uri) >= strlen(perdir)
+ && strncmp(uri, perdir, strlen(perdir)) == 0) {
+ rewritelog(r, 3, "[per-dir %s] strip per-dir prefix: %s -> %s", perdir, uri, uri+strlen(perdir));
+ uri = uri+strlen(perdir);
+ prefixstrip = 1;
+ }
+ }
+
+ if (perdir != NULL)
+ rewritelog(r, 3, "[per-dir %s] applying pattern '%s' to uri '%s'", perdir, p->pattern, uri);
+
+ rc = (regexec(regexp, uri, regexp->re_nsub+1, regmatch, 0) == 0); /* try to match the pattern */
+ if (( rc && !(p->flags & RULEFLAG_NOTMATCH)) ||
+ (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) {
+
+ /* ok, the pattern matched, but we now additionally have to check
+ for any preconditions which have to be also true. We do this
+ at this very late stage to avoid unnessesary checks which
+ slow down the rewriting engine!! */
+ rewriteconds = p->rewriteconds;
+ conds = (rewritecond_entry *)rewriteconds->elts;
+ failed = 0;
+ for (i = 0; i < rewriteconds->nelts; i++) {
+ c = &conds[i];
+ rc = apply_rewrite_cond(r, c, perdir);
+ if (c->flags & CONDFLAG_ORNEXT) {
+ /* there is a "or" flag */
+ if (rc == 0) {
+ /* one cond is false, but another can be true... */
+ continue;
+ }
+ else {
+ /* one true cond is enough, so skip the other conds
+ of the "ornext" chained conds */
+ while ( i < rewriteconds->nelts
+ && c->flags & CONDFLAG_ORNEXT) {
+ i++;
+ c = &conds[i];
+ }
+ continue;
+ }
+ }
+ else {
+ /* no "or" flag, so a single fail means total fail */
+ if (rc == 0) { /* failed */
+ failed = 1;
+ break;
+ }
+ }
+ }
+ if (failed)
+ return 0; /* if any condition fails this complete rule fails */
+
+ /* if this is a pure matching rule we return immediately */
+ if (strcmp(output, "-") == 0) {
+ /* but before we set the env variables... */
+ for (i = 0; p->env[i] != NULL; i++) {
+ strncpy(env2, p->env[i], sizeof(env2)-1);
+ EOS_PARANOIA(env2);
+ strncpy(env, pregsub(r->pool, env2, uri, regexp->re_nsub+1, regmatch), sizeof(env)-1); /* substitute in output */
+ EOS_PARANOIA(env);
+ add_env_variable(r, env);
+ }
+ return 2;
+ }
+
+ /* if this is a forced proxy request ... */
+ if (p->flags & RULEFLAG_PROXY) {
+ if (p->flags & RULEFLAG_NOTMATCH) {
+ output = pstrcat(r->pool, "proxy:", output, NULL);
+ strncpy(newuri, output, sizeof(newuri)-1);
+ EOS_PARANOIA(newuri);
+ expand_variables_inbuffer(r, newuri, sizeof(newuri));/* expand %{...} */
+ expand_map_lookups(r, newuri, sizeof(newuri)); /* expand ${...} */
+ }
+ else {
+ output = pstrcat(r->pool, "proxy:", output, NULL);
+ strncpy(newuri, pregsub(r->pool, output, uri, regexp->re_nsub+1, regmatch), sizeof(newuri)-1); /* substitute in output */
+ EOS_PARANOIA(newuri);
+ for (i = 0; p->env[i] != NULL; i++) {
+ strncpy(env2, p->env[i], sizeof(env2)-1);
+ EOS_PARANOIA(env2);
+ strncpy(env, pregsub(r->pool, env2, uri, regexp->re_nsub+1, regmatch), sizeof(env)-1); /* substitute in output */
+ EOS_PARANOIA(env);
+ add_env_variable(r, env);
+ }
+ expand_variables_inbuffer(r, newuri, sizeof(newuri)); /* expand %{...} */
+ expand_map_lookups(r, newuri, sizeof(newuri)); /* expand ${...} */
+ }
+ if (perdir == NULL)
+ rewritelog(r, 2, "rewrite %s -> %s", r->filename, newuri);
+ else
+ rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, r->filename, newuri);
+ r->filename = pstrdup(r->pool, newuri);
+ return 1;
+ }
+
+ /* if this is an implicit redirect in a per-dir rule */
+ i = strlen(output);
+ if (perdir != NULL
+ && ( (i > 7 && strncasecmp(output, "http://", 7) == 0)
+ || (i > 8 && strncasecmp(output, "https://", 8) == 0)
+ || (i > 9 && strncasecmp(output, "gopher://", 9) == 0)
+ || (i > 6 && strncasecmp(output, "ftp://", 6) == 0) ) ) {
+ if (p->flags & RULEFLAG_NOTMATCH) {
+ strncpy(newuri, output, sizeof(newuri)-1);
+ EOS_PARANOIA(newuri);
+ expand_variables_inbuffer(r, newuri, sizeof(newuri));/* expand %{...} */
+ expand_map_lookups(r, newuri, sizeof(newuri)); /* expand ${...} */
+ }
+ else {
+ strncpy(newuri, pregsub(r->pool, output, uri, regexp->re_nsub+1, regmatch), sizeof(newuri)-1); /* substitute in output */
+ EOS_PARANOIA(newuri);
+ for (i = 0; p->env[i] != NULL; i++) {
+ strncpy(env2, p->env[i], sizeof(env2)-1);
+ EOS_PARANOIA(env2);
+ strncpy(env, pregsub(r->pool, env2, uri, regexp->re_nsub+1, regmatch), sizeof(env)-1); /* substitute in output */
+ EOS_PARANOIA(env);
+ add_env_variable(r, env);
+ }
+ expand_variables_inbuffer(r, newuri, sizeof(newuri));/* expand %{...} */
+ expand_map_lookups(r, newuri, sizeof(newuri)); /* expand ${...} */
+ }
+ rewritelog(r, 2, "[per-dir %s] redirect %s -> %s", perdir, r->filename, newuri);
+ r->filename = pstrdup(r->pool, newuri);
+ splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND);
+ r->status = p->forced_responsecode;
+ return 1;
+ }
+
+ /* add again the previously stripped perdir prefix if the new
+ URI is not a new one (i.e. prefixed by a slash which means
+ that it is not for this per-dir context) */
+ if (prefixstrip && output[0] != '/') {
+ rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s", perdir, output, perdir, output);
+ output = pstrcat(r->pool, perdir, output, NULL);
+ }
+
+ if (p->flags & RULEFLAG_NOTMATCH) {
+ /* just overtake the URI */
+ strncpy(newuri, output, sizeof(newuri)-1);
+ EOS_PARANOIA(newuri);
+ }
+ else {
+ /* substitute in output */
+ strncpy(newuri, pregsub(r->pool, output, uri, regexp->re_nsub+1, regmatch), sizeof(newuri)-1); /* substitute in output */
+ EOS_PARANOIA(newuri);
+ for (i = 0; p->env[i] != NULL; i++) {
+ strncpy(env2, p->env[i], sizeof(env2)-1);
+ EOS_PARANOIA(env2);
+ strncpy(env, pregsub(r->pool, env2, uri, regexp->re_nsub+1, regmatch), sizeof(env)-1); /* substitute in output */
+ EOS_PARANOIA(env);
+ add_env_variable(r, env);
+ }
+ }
+ expand_variables_inbuffer(r, newuri, sizeof(newuri)); /* expand %{...} */
+ expand_map_lookups(r, newuri, sizeof(newuri)); /* expand ${...} */
+
+ if (perdir == NULL)
+ rewritelog(r, 2, "rewrite %s -> %s", uri, newuri);
+ else
+ rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, uri, newuri);
+
+ r->filename = pstrdup(r->pool, newuri);
+
+ /* reduce http[s]://<ourhost>[:<port>] */
+ reduce_uri(r);
+
+ /* split out on-the-fly generated QUERY_STRING '....?xxxxx&xxxx...' */
+ splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND);
+
+ /* if a MIME-type should be later forced for this URL, then remember this */
+ if (p->forced_mimetype != NULL) {
+ table_set(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR, p->forced_mimetype);
+ if (perdir == NULL)
+ rewritelog(r, 2, "remember %s to have MIME-type '%s'", r->filename, p->forced_mimetype);
+ else
+ rewritelog(r, 2, "[per-dir %s] remember %s to have MIME-type '%s'", perdir, r->filename, p->forced_mimetype);
+ }
+
+ /* if we are forced to do a explicit redirect by [R] flag
+ and the current URL still is not a fully qualified one we
+ finally prefix it with http[s]://<ourname> explicitly */
+ if (flags & RULEFLAG_FORCEREDIRECT) {
+ r->status = p->forced_responsecode;
+ if ( !(strlen(r->filename) > 7 &&
+ strncasecmp(r->filename, "http://", 7) == 0)
+ && !(strlen(r->filename) > 8 &&
+ strncasecmp(r->filename, "https://", 8) == 0)
+ && !(strlen(r->filename) > 9 &&
+ strncasecmp(r->filename, "gopher://", 9) == 0)
+ && !(strlen(r->filename) > 6 &&
+ strncasecmp(r->filename, "ftp://", 6) == 0) ) {
+
+#ifdef APACHE_SSL
+ if ((!r->connection->client->ssl && r->server->port == DEFAULT_PORT) ||
+ ( r->connection->client->ssl && r->server->port == 443) )
+#else
+ if (r->server->port == DEFAULT_PORT)
+#endif
+ port[0] = '\0';
+ else
+ ap_snprintf(port, sizeof(port), ":%u", r->server->port);
+ if (r->filename[0] == '/')
+#ifdef APACHE_SSL
+ ap_snprintf(newuri, sizeof(newuri), "%s://%s%s%s", http_method(r), r->server->server_hostname, port, r->filename);
+#else
+ ap_snprintf(newuri, sizeof(newuri), "http://%s%s%s", r->server->server_hostname, port, r->filename);
+#endif
+ else
+#ifdef APACHE_SSL
+ ap_snprintf(newuri, sizeof(newuri), "%s://%s%s/%s", http_method(r), r->server->server_hostname, port, r->filename);
+#else
+ ap_snprintf(newuri, sizeof(newuri), "http://%s%s/%s", r->server->server_hostname, port, r->filename);
+#endif
+ if (perdir == NULL)
+ rewritelog(r, 2, "prepare forced redirect %s -> %s", r->filename, newuri);
+ else
+ rewritelog(r, 2, "[per-dir %s] prepare forced redirect %s -> %s", perdir, r->filename, newuri);
+ r->filename = pstrdup(r->pool, newuri);
+ return 1;
+ }
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
+static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p, char *perdir)
+{
+ char *input;
+ int rc;
+ struct stat sb;
+ request_rec *rsub;
+
+ /* first, we have to expand the input string to match */
+ input = expand_variables(r, p->input);
+
+ rc = 0;
+ if (strcmp(p->pattern, "-f") == 0) {
+ if (stat(input, &sb) == 0)
+ if (S_ISREG(sb.st_mode))
+ rc = 1;
+ }
+ else if (strcmp(p->pattern, "-s") == 0) {
+ if (stat(input, &sb) == 0)
+ if (S_ISREG(sb.st_mode) && sb.st_size > 0)
+ rc = 1;
+ }
+ else if (strcmp(p->pattern, "-l") == 0) {
+#ifndef __EMX__
+/* OS/2 dosen't support links. */
+ if (stat(input, &sb) == 0)
+ if (S_ISLNK(sb.st_mode))
+ rc = 1;
+#endif
+ }
+ else if (strcmp(p->pattern, "-d") == 0) {
+ if (stat(input, &sb) == 0)
+ if (S_ISDIR(sb.st_mode))
+ rc = 1;
+ }
+ else if (strcmp(p->pattern, "-U") == 0) {
+ /* avoid infinite subrequest recursion */
+ if (strlen(input) > 0 /* nonempty path, and */
+ && ( r->main == NULL /* - either not in a subrequest */
+ || ( r->main->uri != NULL /* - or in a subrequest...*/
+ && r->uri != NULL /* ...and then URIs aren't NULL... */
+ /* ...and sub and main URIs differ */
+ && strcmp(r->main->uri, r->uri) != 0) ) ) {
+
+ /* run a URI-based subrequest */
+ rsub = sub_req_lookup_uri(input, r);
+
+ /* URI exists for any result up to 3xx, redirects allowed */
+ if (rsub->status < 400)
+ rc = 1;
+
+ /* log it */
+ rewritelog(r, 5, "RewriteCond URI (-U) check: path=%s -> status=%d", input, rsub->status);
+
+ /* cleanup by destroying the subrequest */
+ destroy_sub_req(rsub);
+ }
+ }
+ else if (strcmp(p->pattern, "-F") == 0) {
+ /* avoid infinite subrequest recursion */
+ if (strlen(input) > 0 /* nonempty path, and */
+ && ( r->main == NULL /* - either not in a subrequest */
+ || ( r->main->uri != NULL /* - or in a subrequest...*/
+ && r->uri != NULL /* ...and then URIs aren't NULL... */
+ /* ...and sub and main URIs differ */
+ && strcmp(r->main->uri, r->uri) != 0) ) ) {
+
+ /* process a file-based subrequest:
+ this differs from -U in that no path translation is done. */
+ rsub = sub_req_lookup_file(input, r);
+
+ /* file exists for any result up to 2xx, no redirects */
+ if (rsub->status < 300 &&
+ /* double-check that file exists since default result is 200 */
+ stat(rsub->filename, &sb) == 0)
+ rc = 1;
+
+ /* log it */
+ rewritelog(r, 5, "RewriteCond file (-F) check: path=%s -> file=%s status=%d", input, rsub->filename, rsub->status);
+
+ /* cleanup by destroying the subrequest */
+ destroy_sub_req(rsub);
+ }
+ }
+ else if (strlen(p->pattern) > 1 && *(p->pattern) == '>') {
+ rc = (compare_lexicography(input, p->pattern+1) == 1 ? 1 : 0);
+ }
+ else if (strlen(p->pattern) > 1 && *(p->pattern) == '<') {
+ rc = (compare_lexicography(input, p->pattern+1) == -1 ? 1 : 0);
+ }
+ else if (strlen(p->pattern) > 1 && *(p->pattern) == '=') {
+ rc = (strcmp(input, p->pattern+1) == 0 ? 1 : 0);
+ }
+ else {
+ /* it is really a regexp pattern, so apply it */
+ rc = (regexec(p->regexp, input, 0, NULL, 0) == 0);
+ }
+
+ /* if this is a non-matching regexp, just negate the result */
+ if (p->flags & CONDFLAG_NOTMATCH)
+ rc = !rc;
+
+ rewritelog(r, 4, "RewriteCond: input='%s' pattern='%s%s' => %s",
+ input, (p->flags & CONDFLAG_NOTMATCH ? "!" : ""),
+ p->pattern, rc ? "matched" : "not-matched");
+
+ /* end just return the result */
+ return rc;
+}
+
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | URL transformation functions
+** | |
+** +-------------------------------------------------------+
+*/
+
+
+/*
+**
+** split out a QUERY_STRING part from
+** the current URI string
+**
+*/
+
+static void splitout_queryargs(request_rec *r, int qsappend)
+{
+ char *q;
+ char *olduri;
+
+ q = strchr(r->filename, '?');
+ if (q != NULL) {
+ olduri = pstrdup(r->pool, r->filename);
+ *q++ = '\0';
+ if (qsappend)
+ r->args = pstrcat(r->pool, q, "&", r->args, NULL);
+ else
+ r->args = pstrdup(r->pool, q);
+ if (strlen(r->args) == 0) {
+ r->args = NULL;
+ rewritelog(r, 3, "split uri=%s -> uri=%s, args=<none>", olduri, r->filename);
+ }
+ else {
+ if (r->args[strlen(r->args)-1] == '&')
+ r->args[strlen(r->args)-1] = '\0';
+ rewritelog(r, 3, "split uri=%s -> uri=%s, args=%s", olduri, r->filename, r->args);
+ }
+ }
+ return;
+}
+
+
+/*
+**
+** strip 'http[s]://ourhost/' from URI
+**
+*/
+
+static void reduce_uri(request_rec *r)
+{
+ char *cp;
+ unsigned short port;
+ char *portp;
+ char *hostp;
+ char *url;
+ char c;
+ char host[LONG_STRING_LEN];
+ char buf[MAX_STRING_LEN];
+ char *olduri;
+
+#ifdef APACHE_SSL
+ if ( (!r->connection->client->ssl &&
+ strncasecmp(r->filename, "http://", 7) == 0)
+ || (r->connection->client->ssl &&
+ strncasecmp(r->filename, "https://", 8) == 0)) {
+#else
+ if (strncasecmp(r->filename, "http://", 7) == 0) {
+#endif
+ /* there was really a rewrite to a remote path */
+
+ olduri = pstrdup(r->pool, r->filename); /* save for logging */
+
+ /* cut the hostname and port out of the URI */
+#ifdef APACHE_SSL
+ strncpy(buf, r->filename+strlen(http_method(r))+3, sizeof(buf)-1);
+#else
+ strncpy(buf, r->filename+7, sizeof(buf)-1);
+#endif
+ EOS_PARANOIA(buf);
+ hostp = buf;
+ for (cp = hostp; *cp != '\0' && *cp != '/' && *cp != ':'; cp++)
+ ;
+ if (*cp == ':') {
+ /* set host */
+ *cp++ = '\0';
+ strncpy(host, hostp, sizeof(host)-1);
+ EOS_PARANOIA(host);
+ /* set port */
+ portp = cp;
+ for (; *cp != '\0' && *cp != '/'; cp++)
+ ;
+ c = *cp;
+ *cp = '\0';
+ port = atoi(portp);
+ *cp = c;
+ /* set remaining url */
+ url = cp;
+ }
+ else if (*cp == '/') {
+ /* set host */
+ *cp = '\0';
+ strncpy(host, hostp, sizeof(host)-1);
+ EOS_PARANOIA(host);
+ *cp = '/';
+ /* set port */
+ port = DEFAULT_PORT;
+ /* set remaining url */
+ url = cp;
+ }
+ else {
+ /* set host */
+ strncpy(host, hostp, sizeof(host)-1);
+ EOS_PARANOIA(host);
+ /* set port */
+ port = DEFAULT_PORT;
+ /* set remaining url */
+ url = "/";
+ }
+
+ /* now check whether we could reduce it to a local path... */
+ if (is_this_our_host(r, host) && port == r->server->port) {
+ /* this is our host, so only the URL remains */
+ r->filename = pstrdup(r->pool, url);
+ rewritelog(r, 3, "reduce %s -> %s", olduri, r->filename);
+ }
+ }
+ return;
+}
+
+
+/*
+**
+** Expand tilde-paths (~user) through
+** Unix /etc/passwd database information
+**
+*/
+
+static char *expand_tildepaths(request_rec *r, char *uri)
+{
+ char user[LONG_STRING_LEN];
+ struct passwd *pw;
+ char *newuri;
+ int i, j;
+
+ newuri = uri;
+ if (uri != NULL && strlen(uri) > 2 && uri[0] == '/' && uri[1] == '~') {
+ /* cut out the username */
+ for (j = 0, i = 2; j < sizeof(user)-1 && uri[i] != '\0' &&
+ ( (uri[i] >= '0' && uri[i] <= '9')
+ || (uri[i] >= 'a' && uri[i] <= 'z')
+ || (uri[i] >= 'A' && uri[i] <= 'Z')); )
+ user[j++] = uri[i++];
+ user[j] = '\0';
+
+ /* lookup username in systems passwd file */
+ if ((pw = getpwnam(user)) != NULL) {
+ /* ok, user was found, so expand the ~user string */
+ if (uri[i] != '\0') {
+ /* ~user/anything... has to be expanded */
+ if (pw->pw_dir[strlen(pw->pw_dir)-1] == '/')
+ pw->pw_dir[strlen(pw->pw_dir)-1] = '\0';
+ newuri = pstrcat(r->pool, pw->pw_dir, uri+i, NULL);
+ }
+ else {
+ /* only ~user has to be expanded */
+ newuri = pstrdup(r->pool, pw->pw_dir);
+ }
+ }
+ }
+ return newuri;
+}
+
+
+/*
+**
+** mapfile expansion support
+** i.e. expansion of MAP lookup directives
+** ${<mapname>:<key>} in RewriteRule rhs
+**
+*/
+
+#define limit_length(n) (n > LONG_STRING_LEN-1 ? LONG_STRING_LEN-1 : n)
+
+static void expand_map_lookups(request_rec *r, char *uri, int uri_len)
+{
+ char newuri[MAX_STRING_LEN];
+ char *cpI;
+ char *cpIE;
+ char *cpO;
+ char *cpT;
+ char *cpT2;
+ char mapname[LONG_STRING_LEN];
+ char mapkey[LONG_STRING_LEN];
+ char defaultvalue[LONG_STRING_LEN];
+ int n;
+
+ cpI = uri;
+ cpIE = cpI+strlen(cpI);
+ cpO = newuri;
+ while (cpI < cpIE) {
+ if (cpI+6 < cpIE && strncmp(cpI, "${", 2) == 0) {
+ /* missing delimiter -> take it as plain text */
+ if ( strchr(cpI+2, ':') == NULL
+ || strchr(cpI+2, '}') == NULL) {
+ memcpy(cpO, cpI, 2);
+ cpO += 2;
+ cpI += 2;
+ continue;
+ }
+ cpI += 2;
+
+ cpT = strchr(cpI, ':');
+ n = cpT-cpI;
+ memcpy(mapname, cpI, limit_length(n));
+ mapname[limit_length(n)] = '\0';
+ cpI += n+1;
+
+ cpT2 = strchr(cpI, '|');
+ cpT = strchr(cpI, '}');
+ if (cpT2 != NULL && cpT2 < cpT) {
+ n = cpT2-cpI;
+ memcpy(mapkey, cpI, limit_length(n));
+ mapkey[limit_length(n)] = '\0';
+ cpI += n+1;
+
+ n = cpT-cpI;
+ memcpy(defaultvalue, cpI, limit_length(n));
+ defaultvalue[limit_length(n)] = '\0';
+ cpI += n+1;
+ }
+ else {
+ n = cpT-cpI;
+ memcpy(mapkey, cpI, limit_length(n));
+ mapkey[limit_length(n)] = '\0';
+ cpI += n+1;
+
+ defaultvalue[0] = '\0';
+ }
+
+ cpT = lookup_map(r, mapname, mapkey);
+ if (cpT != NULL) {
+ n = strlen(cpT);
+ if (cpO + n >= newuri + sizeof(newuri)) {
+ log_printf(r->server, "insufficient space in expand_map_lookups, aborting");
+ return;
+ }
+ memcpy(cpO, cpT, n);
+ cpO += n;
+ }
+ else {
+ n = strlen(defaultvalue);
+ if (cpO + n >= newuri + sizeof(newuri)) {
+ log_printf(r->server, "insufficient space in expand_map_lookups, aborting");
+ return;
+ }
+ memcpy(cpO, defaultvalue, n);
+ cpO += n;
+ }
+ }
+ else {
+ cpT = strstr(cpI, "${");
+ if (cpT == NULL)
+ cpT = cpI+strlen(cpI);
+ n = cpT-cpI;
+ if (cpO + n >= newuri + sizeof(newuri)) {
+ log_printf(r->server, "insufficient space in expand_map_lookups, aborting");
+ return;
+ }
+ memcpy(cpO, cpI, n);
+ cpO += n;
+ cpI += n;
+ }
+ }
+ *cpO = '\0';
+ strncpy(uri, newuri, uri_len-1);
+ uri[uri_len-1] = '\0';
+ return;
+}
+
+#undef limit_length
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | DBM hashfile support
+** | |
+** +-------------------------------------------------------+
+*/
+
+
+static char *lookup_map(request_rec *r, char *name, char *key)
+{
+ void *sconf;
+ rewrite_server_conf *conf;
+ array_header *rewritemaps;
+ rewritemap_entry *entries;
+ rewritemap_entry *s;
+ char *value;
+ struct stat st;
+ int i;
+
+ /* get map configuration */
+ sconf = r->server->module_config;
+ conf = (rewrite_server_conf *)get_module_config(sconf, &rewrite_module);
+ rewritemaps = conf->rewritemaps;
+
+ entries = (rewritemap_entry *)rewritemaps->elts;
+ for (i = 0; i < rewritemaps->nelts; i++) {
+ s = &entries[i];
+ if (strcmp(s->name, name) == 0) {
+ if (s->type == MAPTYPE_TXT) {
+ stat(s->checkfile, &st); /* existence was checked at startup! */
+ value = get_cache_string(cachep, s->name, CACHEMODE_TS, st.st_mtime, key);
+ if (value == NULL) {
+ rewritelog(r, 6, "cache lookup FAILED, forcing new map lookup");
+ if ((value = lookup_map_txtfile(r, s->datafile, key)) != NULL) {
+ rewritelog(r, 5, "map lookup OK: map=%s key=%s[txt] -> val=%s", s->name, key, value);
+ set_cache_string(cachep, s->name, CACHEMODE_TS, st.st_mtime, key, value);
+ return value;
+ }
+ else {
+ rewritelog(r, 5, "map lookup FAILED: map=%s[txt] key=%s", s->name, key);
+ return NULL;
+ }
+ }
+ else {
+ rewritelog(r, 5, "cache lookup OK: map=%s[txt] key=%s -> val=%s", s->name, key, value);
+ return value;
+ }
+ }
+ else if (s->type == MAPTYPE_DBM) {
+#if HAS_NDBM_LIB
+ stat(s->checkfile, &st); /* existence was checked at startup! */
+ value = get_cache_string(cachep, s->name, CACHEMODE_TS, st.st_mtime, key);
+ if (value == NULL) {
+ rewritelog(r, 6, "cache lookup FAILED, forcing new map lookup");
+ if ((value = lookup_map_dbmfile(r, s->datafile, key)) != NULL) {
+ rewritelog(r, 5, "map lookup OK: map=%s[dbm] key=%s -> val=%s", s->name, key, value);
+ set_cache_string(cachep, s->name, CACHEMODE_TS, st.st_mtime, key, value);
+ return value;
+ }
+ else {
+ rewritelog(r, 5, "map lookup FAILED: map=%s[dbm] key=%s", s->name, key);
+ return NULL;
+ }
+ }
+ else {
+ rewritelog(r, 5, "cache lookup OK: map=%s[dbm] key=%s -> val=%s", s->name, key, value);
+ return value;
+ }
+#else
+ return NULL;
+#endif
+ }
+ else if (s->type == MAPTYPE_PRG) {
+ if ((value = lookup_map_program(r, s->fpin, s->fpout, key)) != NULL) {
+ rewritelog(r, 5, "map lookup OK: map=%s key=%s -> val=%s", s->name, key, value);
+ return value;
+ }
+ else {
+ rewritelog(r, 5, "map lookup FAILED: map=%s key=%s", s->name, key);
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+
+static char *lookup_map_txtfile(request_rec *r, char *file, char *key)
+{
+ FILE *fp = NULL;
+ char line[1024];
+ char output[1024];
+ char result[1024];
+ char *value = NULL;
+ char *cpT;
+ char *curkey;
+ char *curval;
+
+ if ((fp = pfopen(r->pool, file, "r")) == NULL)
+ return NULL;
+
+ strncpy(output, MAPFILE_OUTPUT, sizeof(output)-1);
+ EOS_PARANOIA(output);
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = '\0';
+ if (regexec(lookup_map_txtfile_regexp, line, lookup_map_txtfile_regexp->re_nsub+1, lookup_map_txtfile_regmatch, 0) == 0) {
+ strncpy(result, pregsub(r->pool, output, line, lookup_map_txtfile_regexp->re_nsub+1, lookup_map_txtfile_regmatch), sizeof(result)-1); /* substitute in output */
+ EOS_PARANOIA(result);
+ cpT = strchr(result, ',');
+ *cpT = '\0';
+ curkey = result;
+ curval = cpT+1;
+
+ if (strcmp(curkey, key) == 0) {
+ value = pstrdup(r->pool, curval);
+ break;
+ }
+ }
+ }
+ pfclose(r->pool, fp);
+ return value;
+}
+
+#if HAS_NDBM_LIB
+static char *lookup_map_dbmfile(request_rec *r, char *file, char *key)
+{
+ DBM *dbmfp = NULL;
+ datum dbmkey;
+ datum dbmval;
+ char *value = NULL;
+ char buf[MAX_STRING_LEN];
+
+ dbmkey.dptr = key;
+ dbmkey.dsize = (strlen(key) < sizeof(buf) - 1 ? strlen(key) : sizeof(buf)-1);
+ if ((dbmfp = dbm_open(file, O_RDONLY, 0666)) != NULL) {
+ dbmval = dbm_fetch(dbmfp, dbmkey);
+ if (dbmval.dptr != NULL) {
+ memcpy(buf, dbmval.dptr, dbmval.dsize);
+ buf[dbmval.dsize] = '\0';
+ value = pstrdup(r->pool, buf);
+ }
+ dbm_close(dbmfp);
+ }
+ return value;
+}
+#endif
+
+static char *lookup_map_program(request_rec *r, int fpin, int fpout, char *key)
+{
+ char buf[LONG_STRING_LEN];
+ char c;
+ int i;
+
+ /* lock the channel */
+#ifdef USE_PIPE_LOCKING
+ fd_lock(fpin);
+#endif
+
+ /* write out the request key */
+ write(fpin, key, strlen(key));
+ write(fpin, "\n", 1);
+
+ /* read in the response value */
+ i = 0;
+ while (read(fpout, &c, 1) == 1 && (i < LONG_STRING_LEN-1)) {
+ if (c == '\n')
+ break;
+ buf[i++] = c;
+ }
+ buf[i] = '\0';
+
+ /* unlock the channel */
+#ifdef USE_PIPE_LOCKING
+ fd_unlock(fpin);
+#endif
+
+ if (strcasecmp(buf, "NULL") == 0)
+ return NULL;
+ else
+ return pstrdup(r->pool, buf);
+}
+
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | rewriting logfile support
+** | |
+** +-------------------------------------------------------+
+*/
+
+
+static void open_rewritelog(server_rec *s, pool *p)
+{
+ rewrite_server_conf *conf;
+ char *fname;
+ FILE *fp;
+ int rewritelog_flags = ( O_WRONLY|O_APPEND|O_CREAT );
+ mode_t rewritelog_mode = ( S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH );
+
+ conf = get_module_config(s->module_config, &rewrite_module);
+
+ if (conf->rewritelogfile == NULL)
+ return;
+ if (*(conf->rewritelogfile) == '\0')
+ return;
+ if (conf->rewritelogfp > 0)
+ return; /* virtual log shared w/main server */
+
+ fname = server_root_relative(p, conf->rewritelogfile);
+
+ if (*conf->rewritelogfile == '|') {
+ if (!spawn_child(p, rewritelog_child, (void *)(conf->rewritelogfile+1),
+ kill_after_timeout, &fp, NULL)) {
+ perror("spawn_child");
+ fprintf (stderr, "mod_rewrite: could not fork child for RewriteLog process\n");
+ exit (1);
+ }
+ conf->rewritelogfp = fileno(fp);
+ }
+ else if (*conf->rewritelogfile != '\0') {
+ if ((conf->rewritelogfp = popenf(p, fname, rewritelog_flags, rewritelog_mode)) < 0) {
+ perror("open");
+ fprintf(stderr, "mod_rewrite: could not open RewriteLog file %s.\n", fname);
+ exit(1);
+ }
+ }
+ return;
+}
+
+/* Child process code for 'RewriteLog "|..."' */
+static void rewritelog_child(void *cmd)
+{
+ cleanup_for_exec();
+ signal(SIGHUP, SIG_IGN);
+#ifdef __EMX__
+ /* OS/2 needs a '/' */
+ execl(SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+#else
+ execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+ exit(1);
+}
+
+static void rewritelog(request_rec *r, int level, const char *text, ...)
+{
+ rewrite_server_conf *conf;
+ conn_rec *connect;
+ char *str1;
+ char str2[512];
+ char str3[1024];
+ char type[20];
+ char redir[20];
+ va_list ap;
+ int i;
+ request_rec *req;
+ char *ruser;
+ const char *rhost;
+
+ va_start(ap, text);
+ conf = get_module_config(r->server->module_config, &rewrite_module);
+ connect = r->connection;
+
+ if (conf->rewritelogfp <0)
+ return;
+ if (conf->rewritelogfile == NULL)
+ return;
+ if (*(conf->rewritelogfile) == '\0')
+ return;
+
+ if (level > conf->rewriteloglevel)
+ return;
+
+ if (connect->user == NULL) {
+ ruser = "-";
+ }
+ else if (strlen (connect->user) != 0) {
+ ruser = connect->user;
+ }
+ else {
+ ruser = "\"\"";
+ }
+
+ rhost = get_remote_host(connect, r->server->module_config, REMOTE_NOLOOKUP);
+ if (rhost == NULL)
+ rhost = "UNKNOWN-HOST";
+
+ str1 = pstrcat(r->pool, rhost, " ",
+ (connect->remote_logname != NULL ? connect->remote_logname : "-"), " ",
+ ruser, NULL);
+ ap_vsnprintf(str2, sizeof(str2), text, ap);
+
+ if (r->main == NULL)
+ strcpy(type, "initial");
+ else
+ strcpy(type, "subreq");
+
+ for (i = 0, req = r; req->prev != NULL; req = req->prev)
+ i++;
+ if (i == 0)
+ redir[0] = '\0';
+ else
+ ap_snprintf(redir, sizeof(redir), "/redir#%d", i);
+
+ ap_snprintf(str3, sizeof(str3), "%s %s [%s/sid#%x][rid#%x/%s%s] (%d) %s\n", str1, current_logtime(r), r->server->server_hostname, (unsigned int)(r->server), (unsigned int)r, type, redir, level, str2);
+
+ fd_lock(conf->rewritelogfp);
+ write(conf->rewritelogfp, str3, strlen(str3));
+ fd_unlock(conf->rewritelogfp);
+
+ va_end(ap);
+ return;
+}
+
+static char *current_logtime(request_rec *r)
+{
+ int timz;
+ struct tm *t;
+ char tstr[80];
+ char sign;
+
+ t = get_gmtoff(&timz);
+ sign = (timz < 0 ? '-' : '+');
+ if(timz < 0)
+ timz = -timz;
+
+ strftime(tstr, 80, "[%d/%b/%Y:%H:%M:%S ", t);
+ ap_snprintf(tstr + strlen(tstr), 80-strlen(tstr), "%c%.2d%.2d]", sign, timz/60, timz%60);
+ return pstrdup(r->pool, tstr);
+}
+
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | program map support
+** | |
+** +-------------------------------------------------------+
+*/
+
+static void run_rewritemap_programs(server_rec *s, pool *p)
+{
+ rewrite_server_conf *conf;
+ char *fname;
+ FILE *fpin;
+ FILE *fpout;
+ array_header *rewritemaps;
+ rewritemap_entry *entries;
+ rewritemap_entry *map;
+ int i;
+ int rc;
+
+ conf = get_module_config(s->module_config, &rewrite_module);
+
+ rewritemaps = conf->rewritemaps;
+ entries = (rewritemap_entry *)rewritemaps->elts;
+ for (i = 0; i < rewritemaps->nelts; i++) {
+ map = &entries[i];
+ if (map->type != MAPTYPE_PRG)
+ continue;
+ if (map->datafile == NULL ||
+ *(map->datafile) == '\0' ||
+ map->fpin > 0 ||
+ map->fpout > 0 )
+ continue;
+ fname = server_root_relative(p, map->datafile);
+ fpin = NULL;
+ fpout = NULL;
+ rc = spawn_child(p, rewritemap_program_child, (void *)map->datafile, kill_after_timeout, &fpin, &fpout);
+ if (rc == 0 || fpin == NULL || fpout == NULL) {
+ perror("spawn_child");
+ fprintf(stderr, "mod_rewrite: could not fork child for RewriteMap process\n");
+ exit(1);
+ }
+ map->fpin = fileno(fpin);
+ map->fpout = fileno(fpout);
+ }
+ return;
+}
+
+/* child process code */
+static void rewritemap_program_child(void *cmd)
+{
+ cleanup_for_exec();
+ signal(SIGHUP, SIG_IGN);
+#ifdef __EMX__
+ /* OS/2 needs a '/' */
+ execl(SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+#else
+ execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+ exit(1);
+}
+
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | environment variable support
+** | |
+** +-------------------------------------------------------+
+*/
+
+
+static void expand_variables_inbuffer(request_rec *r, char *buf, int buf_len)
+{
+ char *newbuf;
+ newbuf = expand_variables(r, buf);
+ if (strcmp(newbuf, buf) != 0) {
+ strncpy(buf, newbuf, buf_len-1);
+ buf[buf_len-1] = '\0';
+ }
+ return;
+}
+
+static char *expand_variables(request_rec *r, char *str)
+{
+ char output[MAX_STRING_LEN];
+ char input[MAX_STRING_LEN];
+ char *cp;
+ char *cp2;
+ char *cp3;
+ int expanded;
+
+ strncpy(input, str, sizeof(input)-1);
+ EOS_PARANOIA(input);
+ output[0] = '\0';
+ expanded = 0;
+ for (cp = input; cp < input+MAX_STRING_LEN; ) {
+ if ((cp2 = strstr(cp, "%{")) != NULL) {
+ if ((cp3 = strstr(cp2, "}")) != NULL) {
+ *cp2 = '\0';
+ strncpy(&output[strlen(output)], cp, sizeof(output)-strlen(output)-1);
+
+ cp2 += 2;
+ *cp3 = '\0';
+ strncpy(&output[strlen(output)], lookup_variable(r, cp2), sizeof(output)-strlen(output)-1);
+
+ cp = cp3+1;
+ expanded = 1;
+ continue;
+ }
+ }
+ strncpy(&output[strlen(output)], cp, sizeof(output)-strlen(output)-1);
+ EOS_PARANOIA(output);
+ break;
+ }
+ return expanded ? pstrdup(r->pool, output) : str;
+}
+
+static char *lookup_variable(request_rec *r, char *var)
+{
+ char *result;
+ char resultbuf[LONG_STRING_LEN];
+ time_t tc;
+ struct tm *tm;
+ request_rec *rsub;
+ struct passwd *pw;
+ struct group *gr;
+ struct stat finfo;
+
+ result = NULL;
+
+ /* HTTP headers */
+ if (strcasecmp(var, "HTTP_USER_AGENT") == 0) {
+ result = lookup_header(r, "User-Agent");
+ }
+ else if (strcasecmp(var, "HTTP_REFERER") == 0) {
+ result = lookup_header(r, "Referer");
+ }
+ else if (strcasecmp(var, "HTTP_COOKIE") == 0) {
+ result = lookup_header(r, "Cookie");
+ }
+ else if (strcasecmp(var, "HTTP_FORWARDED") == 0) {
+ result = lookup_header(r, "Forwarded");
+ }
+ else if (strcasecmp(var, "HTTP_HOST") == 0) {
+ result = lookup_header(r, "Host");
+ }
+ else if (strcasecmp(var, "HTTP_PROXY_CONNECTION") == 0) {
+ result = lookup_header(r, "Proxy-Connection");
+ }
+ else if (strcasecmp(var, "HTTP_ACCEPT") == 0) {
+ result = lookup_header(r, "Accept");
+ }
+ /* all other headers from which we are still not know about */
+ else if (strlen(var) > 5 && strncasecmp(var, "HTTP:", 5) == 0) {
+ result = lookup_header(r, var+5);
+ }
+
+ /* connection stuff */
+ else if (strcasecmp(var, "REMOTE_ADDR") == 0) {
+ result = r->connection->remote_ip;
+ }
+ else if (strcasecmp(var, "REMOTE_HOST") == 0) {
+ result = (char *)get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME);
+ }
+ else if (strcasecmp(var, "REMOTE_USER") == 0) {
+ result = r->connection->user;
+ }
+ else if (strcasecmp(var, "REMOTE_IDENT") == 0) {
+ result = (char *)get_remote_logname(r);
+ }
+
+ /* request stuff */
+ else if (strcasecmp(var, "THE_REQUEST") == 0) { /* non-standard */
+ result = r->the_request;
+ }
+ else if (strcasecmp(var, "REQUEST_METHOD") == 0) {
+ result = r->method;
+ }
+ else if (strcasecmp(var, "REQUEST_URI") == 0) { /* non-standard */
+ result = r->uri;
+ }
+ else if (strcasecmp(var, "SCRIPT_FILENAME") == 0 ||
+ strcasecmp(var, "REQUEST_FILENAME") == 0 ) {
+ result = r->filename;
+ }
+ else if (strcasecmp(var, "PATH_INFO") == 0) {
+ result = r->path_info;
+ }
+ else if (strcasecmp(var, "QUERY_STRING") == 0) {
+ result = r->args;
+ }
+ else if (strcasecmp(var, "AUTH_TYPE") == 0) {
+ result = r->connection->auth_type;
+ }
+ else if (strcasecmp(var, "IS_SUBREQ") == 0) { /* non-standard */
+ result = (r->main != NULL ? "true" : "false");
+ }
+
+ /* internal server stuff */
+ else if (strcasecmp(var, "DOCUMENT_ROOT") == 0) {
+ result = document_root(r);
+ }
+ else if (strcasecmp(var, "SERVER_ADMIN") == 0) {
+ result = r->server->server_admin;
+ }
+ else if (strcasecmp(var, "SERVER_NAME") == 0) {
+ result = r->server->server_hostname;
+ }
+ else if (strcasecmp(var, "SERVER_PORT") == 0) {
+ ap_snprintf(resultbuf, sizeof(resultbuf), "%u", r->server->port);
+ result = resultbuf;
+ }
+ else if (strcasecmp(var, "SERVER_PROTOCOL") == 0) {
+ result = r->protocol;
+ }
+ else if (strcasecmp(var, "SERVER_SOFTWARE") == 0) {
+ result = pstrdup(r->pool, SERVER_VERSION);
+ }
+ else if (strcasecmp(var, "API_VERSION") == 0) { /* non-standard */
+ ap_snprintf(resultbuf, sizeof(resultbuf), "%d", MODULE_MAGIC_NUMBER);
+ result = resultbuf;
+ }
+
+ /* underlaying Unix system stuff */
+ else if (strcasecmp(var, "TIME_YEAR") == 0) {
+ tc = time(NULL);
+ tm = localtime(&tc);
+ ap_snprintf(resultbuf, sizeof(resultbuf), "%02d%02d", (tm->tm_year / 100) + 19, tm->tm_year % 100);
+ result = resultbuf;
+ }
+#define MKTIMESTR(format, tmfield) \
+ tc = time(NULL); \
+ tm = localtime(&tc); \
+ ap_snprintf(resultbuf, sizeof(resultbuf), format, tm->tmfield); \
+ result = resultbuf;
+ else if (strcasecmp(var, "TIME_MON") == 0) {
+ MKTIMESTR("%02d", tm_mon+1)
+ }
+ else if (strcasecmp(var, "TIME_DAY") == 0) {
+ MKTIMESTR("%02d", tm_mday)
+ }
+ else if (strcasecmp(var, "TIME_HOUR") == 0) {
+ MKTIMESTR("%02d", tm_hour)
+ }
+ else if (strcasecmp(var, "TIME_MIN") == 0) {
+ MKTIMESTR("%02d", tm_min)
+ }
+ else if (strcasecmp(var, "TIME_SEC") == 0) {
+ MKTIMESTR("%02d", tm_sec)
+ }
+ else if (strcasecmp(var, "TIME_WDAY") == 0) {
+ MKTIMESTR("%d", tm_wday)
+ }
+ else if (strcasecmp(var, "TIME") == 0) {
+ tc = time(NULL);
+ tm = localtime(&tc);
+ ap_snprintf(resultbuf, sizeof(resultbuf), "%02d%02d%02d%02d%02d%02d%02d",
+ (tm->tm_year / 100) + 19, (tm->tm_year % 100),
+ tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+ result = resultbuf;
+ rewritelog(r, 1, "RESULT='%s'", result);
+ }
+
+ /* all other env-variables from the parent Apache process */
+ else if (strlen(var) > 4 && strncasecmp(var, "ENV:", 4) == 0) {
+ /* first try the internal Apache notes structure */
+ result = table_get(r->notes, var+4);
+ /* second try the internal Apache env structure */
+ if (result == NULL)
+ result = table_get(r->subprocess_env, var+4);
+ /* third try the external OS env */
+ if (result == NULL)
+ result = getenv(var+4);
+ }
+
+#define LOOKAHEAD(subrecfunc) \
+ if ( \
+ /* filename is safe to use */ \
+ r->filename != NULL \
+ /* - and we're either not in a subrequest */ \
+ && ( r->main == NULL \
+ /* - or in a subrequest where paths are non-NULL... */ \
+ || ( r->main->uri != NULL && r->uri != NULL \
+ /* ...and sub and main paths differ */ \
+ && strcmp(r->main->uri, r->uri) != 0))) { \
+ /* process a file-based subrequest */ \
+ rsub = subrecfunc(r->filename, r); \
+ /* now recursively lookup the variable in the sub_req */ \
+ result = lookup_variable(rsub, var+5); \
+ /* copy it up to our scope before we destroy the sub_req's pool */ \
+ result = pstrdup(r->pool, result); \
+ /* cleanup by destroying the subrequest */ \
+ destroy_sub_req(rsub); \
+ /* log it */ \
+ rewritelog(r, 5, "lookahead: path=%s var=%s -> val=%s", r->filename, var+5, result); \
+ /* return ourself to prevent re-pstrdup */ \
+ return result; \
+ }
+
+ /* look-ahead for parameter through URI-based sub-request */
+ else if (strlen(var) > 5 && strncasecmp(var, "LA-U:", 5) == 0) {
+ LOOKAHEAD(sub_req_lookup_uri)
+ }
+ /* look-ahead for parameter through file-based sub-request */
+ else if (strlen(var) > 5 && strncasecmp(var, "LA-F:", 5) == 0) {
+ LOOKAHEAD(sub_req_lookup_file)
+ }
+
+ /* file stuff */
+ else if (strcasecmp(var, "SCRIPT_USER") == 0) {
+ result = pstrdup(r->pool, "<unknown>");
+ if (r->finfo.st_mode != 0) {
+ if ((pw = getpwuid(r->finfo.st_uid)) != NULL) {
+ result = pstrdup(r->pool, pw->pw_name);
+ }
+ }
+ else {
+ if (stat(r->filename, &finfo) == 0) {
+ if ((pw = getpwuid(finfo.st_uid)) != NULL) {
+ result = pstrdup(r->pool, pw->pw_name);
+ }
+ }
+ }
+ }
+ else if (strcasecmp(var, "SCRIPT_GROUP") == 0) {
+ result = pstrdup(r->pool, "<unknown>");
+ if (r->finfo.st_mode != 0) {
+ if ((gr = getgrgid(r->finfo.st_gid)) != NULL) {
+ result = pstrdup(r->pool, gr->gr_name);
+ }
+ }
+ else {
+ if (stat(r->filename, &finfo) == 0) {
+ if ((gr = getgrgid(finfo.st_gid)) != NULL) {
+ result = pstrdup(r->pool, gr->gr_name);
+ }
+ }
+ }
+ }
+
+ if (result == NULL)
+ return pstrdup(r->pool, "");
+ else
+ return pstrdup(r->pool, result);
+}
+
+static char *lookup_header(request_rec *r, const char *name)
+{
+ array_header *hdrs_arr;
+ table_entry *hdrs;
+ int i;
+
+ hdrs_arr = table_elts(r->headers_in);
+ hdrs = (table_entry *)hdrs_arr->elts;
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (hdrs[i].key == NULL)
+ continue;
+ if (strcasecmp(hdrs[i].key, name) == 0)
+ return hdrs[i].val;
+ }
+ return NULL;
+}
+
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | caching support
+** | |
+** +-------------------------------------------------------+
+*/
+
+
+static cache *init_cache(pool *p)
+{
+ cache *c;
+
+ c = (cache *)palloc(p, sizeof(cache));
+ c->pool = make_sub_pool(p);
+ c->lists = make_array(c->pool, 2, sizeof(cachelist));
+ return c;
+}
+
+static void set_cache_string(cache *c, char *res, int mode, time_t time, char *key, char *value)
+{
+ cacheentry ce;
+
+ ce.time = time;
+ ce.key = key;
+ ce.value = value;
+ store_cache_string(c, res, &ce);
+ return;
+}
+
+static char *get_cache_string(cache *c, char *res, int mode, time_t time, char *key)
+{
+ cacheentry *ce;
+
+ ce = retrieve_cache_string(c, res, key);
+ if (ce == NULL)
+ return NULL;
+ if (mode & CACHEMODE_TS) {
+ if (time != ce->time)
+ return NULL;
+ }
+ else if (mode & CACHEMODE_TTL) {
+ if (time > ce->time)
+ return NULL;
+ }
+ return pstrdup(c->pool, ce->value);
+}
+
+static void store_cache_string(cache *c, char *res, cacheentry *ce)
+{
+ int i;
+ int j;
+ cachelist *l;
+ cacheentry *e;
+ int found_list;
+
+ found_list = 0;
+ /* first try to edit an existing entry */
+ for (i = 0; i < c->lists->nelts; i++) {
+ l = &(((cachelist *)c->lists->elts)[i]);
+ if (strcmp(l->resource, res) == 0) {
+ found_list = 1;
+ for (j = 0; j < l->entries->nelts; j++) {
+ e = &(((cacheentry *)l->entries->elts)[j]);
+ if (strcmp(e->key, ce->key) == 0) {
+ e->time = ce->time;
+ e->value = pstrdup(c->pool, ce->value);
+ return;
+ }
+ }
+ }
+ }
+
+ /* create a needed new list */
+ if (!found_list) {
+ l = push_array(c->lists);
+ l->resource = pstrdup(c->pool, res);
+ l->entries = make_array(c->pool, 2, sizeof(cacheentry));
+ }
+
+ /* create the new entry */
+ for (i = 0; i < c->lists->nelts; i++) {
+ l = &(((cachelist *)c->lists->elts)[i]);
+ if (strcmp(l->resource, res) == 0) {
+ e = push_array(l->entries);
+ e->time = ce->time;
+ e->key = pstrdup(c->pool, ce->key);
+ e->value = pstrdup(c->pool, ce->value);
+ return;
+ }
+ }
+
+ /* not reached, but when it is no problem... */
+ return;
+}
+
+static cacheentry *retrieve_cache_string(cache *c, char *res, char *key)
+{
+ int i;
+ int j;
+ cachelist *l;
+ cacheentry *e;
+
+ for (i = 0; i < c->lists->nelts; i++) {
+ l = &(((cachelist *)c->lists->elts)[i]);
+ if (strcmp(l->resource, res) == 0) {
+ for (j = 0; j < l->entries->nelts; j++) {
+ e = &(((cacheentry *)l->entries->elts)[j]);
+ if (strcmp(e->key, key) == 0) {
+ return e;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+
+
+
+/*
+** +-------------------------------------------------------+
+** | |
+** | misc functions
+** | |
+** +-------------------------------------------------------+
+*/
+
+static char *subst_prefix_path(request_rec *r, char *input, char *match, char *subst)
+{
+ char matchbuf[LONG_STRING_LEN];
+ char substbuf[LONG_STRING_LEN];
+ char *output;
+ int l;
+
+ output = input;
+
+ /* first create a match string which always has a trailing slash */
+ strncpy(matchbuf, match, sizeof(matchbuf)-1);
+ EOS_PARANOIA(matchbuf);
+ l = strlen(matchbuf);
+ if (matchbuf[l-1] != '/') {
+ matchbuf[l] = '/';
+ matchbuf[l+1] = '\0';
+ l++;
+ }
+ /* now compare the prefix */
+ if (strncmp(input, matchbuf, l) == 0) {
+ rewritelog(r, 5, "strip matching prefix: %s -> %s", output, output+l);
+ output = pstrdup(r->pool, output+l);
+
+ /* and now add the base-URL as replacement prefix */
+ strncpy(substbuf, subst, sizeof(substbuf)-1);
+ EOS_PARANOIA(substbuf);
+ l = strlen(substbuf);
+ if (substbuf[l-1] != '/') {
+ substbuf[l] = '/';
+ substbuf[l+1] = '\0';
+ l++;
+ }
+ if (output[0] == '/') {
+ rewritelog(r, 4, "add subst prefix: %s -> %s%s", output, substbuf, output+1);
+ output = pstrcat(r->pool, substbuf, output+1, NULL);
+ }
+ else {
+ rewritelog(r, 4, "add subst prefix: %s -> %s%s", output, substbuf, output);
+ output = pstrcat(r->pool, substbuf, output, NULL);
+ }
+ }
+ return output;
+}
+
+
+/*
+**
+** own command line parser which don't have the '\\' problem
+**
+*/
+
+static int parseargline(char *str, char **a1, char **a2, char **a3)
+{
+ char *cp;
+ int isquoted;
+
+#define SKIP_WHITESPACE(cp) \
+ for ( ; *cp == ' ' || *cp == '\t'; ) \
+ cp++;
+
+#define CHECK_QUOTATION(cp,isquoted) \
+ isquoted = 0; \
+ if (*cp == '"') { \
+ isquoted = 1; \
+ cp++; \
+ }
+
+#define DETERMINE_NEXTSTRING(cp,isquoted) \
+ for ( ; *cp != '\0'; cp++) { \
+ if ( (isquoted && (*cp == ' ' || *cp == '\t')) \
+ || (*cp == '\\' && (*(cp+1) == ' ' || *(cp+1) == '\t'))) { \
+ cp++; \
+ continue; \
+ } \
+ if ( (!isquoted && (*cp == ' ' || *cp == '\t')) \
+ || (isquoted && *cp == '"') ) \
+ break; \
+ }
+
+ cp = str;
+ SKIP_WHITESPACE(cp);
+
+ /* determine first argument */
+ CHECK_QUOTATION(cp, isquoted);
+ *a1 = cp;
+ DETERMINE_NEXTSTRING(cp, isquoted);
+ if (*cp == '\0')
+ return 1;
+ *cp++ = '\0';
+
+ SKIP_WHITESPACE(cp);
+
+ /* determine second argument */
+ CHECK_QUOTATION(cp, isquoted);
+ *a2 = cp;
+ DETERMINE_NEXTSTRING(cp, isquoted);
+ if (*cp == '\0') {
+ *cp++ = '\0';
+ *a3 = NULL;
+ return 0;
+ }
+ *cp++ = '\0';
+
+ SKIP_WHITESPACE(cp);
+
+ /* again check if there are only two arguments */
+ if (*cp == '\0') {
+ *cp++ = '\0';
+ *a3 = NULL;
+ return 0;
+ }
+
+ /* determine second argument */
+ CHECK_QUOTATION(cp, isquoted);
+ *a3 = cp;
+ DETERMINE_NEXTSTRING(cp, isquoted);
+ *cp++ = '\0';
+
+ return 0;
+}
+
+
+static void add_env_variable(request_rec *r, char *s)
+{
+ char var[MAX_STRING_LEN];
+ char val[MAX_STRING_LEN];
+ char *cp;
+ int n;
+
+ if ((cp = strchr(s, ':')) != NULL) {
+ n = ((cp-s) > MAX_STRING_LEN-1 ? MAX_STRING_LEN-1 : (cp-s));
+ memcpy(var, s, n);
+ var[n] = '\0';
+ strncpy(val, cp+1, sizeof(val)-1);
+ EOS_PARANOIA(val);
+ table_set(r->subprocess_env, pstrdup(r->pool, var), pstrdup(r->pool, val));
+ rewritelog(r, 5, "setting env variable '%s' to '%s'", var, val);
+ }
+}
+
+
+
+/*
+**
+** stat() for only the prefix of a path
+**
+*/
+
+static int prefix_stat(const char *path, struct stat *sb)
+{
+ char curpath[LONG_STRING_LEN];
+ char *cp;
+
+ strncpy(curpath, path, sizeof(curpath)-1);
+ EOS_PARANOIA(curpath);
+ if (curpath[0] != '/')
+ return 0;
+ if ((cp = strchr(curpath+1, '/')) != NULL)
+ *cp = '\0';
+ if (stat(curpath, sb) == 0)
+ return 1;
+ else
+ return 0;
+}
+
+
+/*
+**
+** special DNS lookup functions
+**
+*/
+
+static int is_this_our_host(request_rec *r, char *testhost)
+{
+ char **cppHNLour;
+ char **cppHNLtest;
+ char *ourhostname;
+ char *ourhostip;
+ const char *names;
+ char *name;
+ int i, j;
+ server_addr_rec *sar;
+
+ /* we can check:
+ r->
+ char *hostname Host, as set by full URI or Host:
+ int hostlen Length of http://host:port in full URI
+ r->server->
+ int is_virtual 0=main, 1=ip-virtual, 2=non-ip-virtual
+ char *server_hostname used on compare to r->hostname
+ inet_ntoa(r->connection->local_addr.sin_addr)
+ used on compare to r->hostname
+ unsigned short port for redirects
+ char *path name of ServerPath
+ int pathlen len of ServerPath
+ char *names Wildcarded names for ServerAlias servers
+ r->server->addrs->
+ struct in_addr host_addr The bound address, for this server
+ short host_port The bound port, for this server
+ char *virthost The name given in <VirtualHost>
+ */
+
+ ourhostname = r->server->server_hostname;
+ ourhostip = inet_ntoa(r->connection->local_addr.sin_addr);
+
+ /* just a simple common case */
+ if (strcmp(testhost, ourhostname) == 0 ||
+ strcmp(testhost, ourhostip) == 0 )
+ return YES;
+
+ /* now the complicated cases */
+ if (!r->server->is_virtual) {
+ /* main servers */
+
+ /* check for the alternative IP addresses */
+ if ((cppHNLour = resolv_ipaddr_list(r, ourhostname)) == NULL)
+ return NO;
+ if ((cppHNLtest = resolv_ipaddr_list(r, testhost)) == NULL)
+ return NO;
+ for (i = 0; cppHNLtest[i] != NULL; i++) {
+ for (j = 0; cppHNLour[j] != NULL; j++) {
+ if (strcmp(cppHNLtest[i], cppHNLour[j]) == 0) {
+ return YES;
+ }
+ }
+ }
+ }
+ else if (r->server->is_virtual) {
+ /* virtual servers */
+
+ /* check for the names supplied in the VirtualHost directive */
+ for(sar = r->server->addrs; sar != NULL; sar = sar->next) {
+ if(strcasecmp(sar->virthost, testhost) == 0)
+ return YES;
+ }
+
+ /* check for the virtual-server aliases */
+ if (r->server->names != NULL && r->server->names[0] != '\0') {
+ names = r->server->names;
+ while (*names != '\0') {
+ name = getword_conf(r->pool, &names);
+ if ((is_matchexp(name) && !strcasecmp_match(testhost, name)) ||
+ (strcasecmp(testhost, name) == 0) ) {
+ return YES;
+ }
+ }
+ }
+ }
+ return NO;
+}
+
+static int isaddr(char *host)
+{
+ char *cp;
+
+ /* Null pointers and empty strings
+ are not addresses. */
+ if (host == NULL)
+ return NO;
+ if (*host == '\0')
+ return NO;
+ /* Make sure it has only digits and dots. */
+ for (cp = host; *cp; cp++) {
+ if (!isdigit(*cp) && *cp != '.')
+ return NO;
+ }
+ /* If it has a trailing dot,
+ don't treat it as an address. */
+ if (*(cp-1) == '.')
+ return NO;
+ return YES;
+}
+
+static char **resolv_ipaddr_list(request_rec *r, char *name)
+{
+ char **cppHNL;
+ struct hostent *hep;
+ int i;
+
+ if (isaddr(name))
+ hep = gethostbyaddr(name, sizeof(struct in_addr), AF_INET);
+ else
+ hep = gethostbyname(name);
+ if (hep == NULL)
+ return NULL;
+ for (i = 0; hep->h_addr_list[i]; i++)
+ ;
+ cppHNL = (char **)palloc(r->pool, sizeof(char *)*(i+1));
+ for (i = 0; hep->h_addr_list[i]; i++)
+ cppHNL[i] = pstrdup(r->pool, inet_ntoa(*((struct in_addr *)(hep->h_addr_list[i]))) );
+ cppHNL[i] = NULL;
+ return cppHNL;
+}
+
+
+/*
+**
+** check if proxy module is available
+** i.e. if it is compiled in and turned on
+**
+*/
+
+static int is_proxy_available(server_rec *s)
+{
+ return (find_linked_module("mod_proxy.c") != NULL);
+}
+
+
+/*
+**
+** File locking
+**
+*/
+
+#ifdef USE_FCNTL
+static struct flock lock_it;
+static struct flock unlock_it;
+#endif
+
+static void fd_lock(int fd)
+{
+ int rc;
+
+#ifdef USE_FCNTL
+ lock_it.l_whence = SEEK_SET; /* from current point */
+ lock_it.l_start = 0; /* -"- */
+ lock_it.l_len = 0; /* until end of file */
+ lock_it.l_type = F_WRLCK; /* set exclusive/write lock */
+ lock_it.l_pid = 0; /* pid not actually interesting */
+
+ while ( ((rc = fcntl(fd, F_SETLKW, &lock_it)) < 0)
+ && (errno == EINTR) )
+ continue;
+#endif
+#ifdef USE_FLOCK
+ while ( ((rc = flock(fd, LOCK_EX)) < 0)
+ && (errno == EINTR) )
+ continue;
+#endif
+
+ if (rc < 0) {
+#ifdef USE_FLOCK
+ perror("flock");
+#else
+ perror("fcntl");
+#endif
+ fprintf(stderr, "Error getting lock. Exiting!");
+ exit(1);
+ }
+ return;
+}
+
+static void fd_unlock(int fd)
+{
+ int rc;
+
+#ifdef USE_FCNTL
+ unlock_it.l_whence = SEEK_SET; /* from current point */
+ unlock_it.l_start = 0; /* -"- */
+ unlock_it.l_len = 0; /* until end of file */
+ unlock_it.l_type = F_UNLCK; /* unlock */
+ unlock_it.l_pid = 0; /* pid not actually interesting */
+
+ rc = fcntl(fd, F_SETLKW, &unlock_it);
+#endif
+#ifdef USE_FLOCK
+ rc = flock(fd, LOCK_UN);
+#endif
+
+ if (rc < 0) {
+#ifdef USE_FLOCK
+ perror("flock");
+#else
+ perror("fcntl");
+#endif
+ fprintf(stderr, "Error freeing lock. Exiting!");
+ exit(1);
+ }
+}
+
+/*
+**
+** Lexicographic Compare
+**
+*/
+
+static int compare_lexicography(char *cpNum1, char *cpNum2)
+{
+ int i;
+ int n1, n2;
+
+ n1 = strlen(cpNum1);
+ n2 = strlen(cpNum2);
+ if (n1 > n2)
+ return 1;
+ if (n1 < n2)
+ return -1;
+ for (i = 0; i < n1; i++) {
+ if (cpNum1[i] > cpNum2[i])
+ return 1;
+ if (cpNum1[i] < cpNum2[i])
+ return -1;
+ }
+ return 0;
+}
+
+
+/*EOF*/
diff --git a/usr.sbin/httpd/src/mod_rewrite.h b/usr.sbin/httpd/src/mod_rewrite.h
new file mode 100644
index 00000000000..10f91070a85
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_rewrite.h
@@ -0,0 +1,400 @@
+
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/>.
+ *
+ */
+
+
+#ifndef _MOD_REWRITE_H
+#define _MOD_REWRITE_H 1
+
+/*
+** _ _ _
+** _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___
+** | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \
+** | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/
+** |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___|
+** |_____|
+**
+** URL Rewriting Module
+**
+** This module uses a rule-based rewriting engine (based on a
+** regular-expression parser) to rewrite requested URLs on the fly.
+**
+** It supports an unlimited number of additional rule conditions (which can
+** operate on a lot of variables, even on HTTP headers) for granular
+** matching and even external database lookups (either via plain text
+** tables, DBM hash files or even external processes) for advanced URL
+** substitution.
+**
+** It operates on the full URLs (including the PATH_INFO part) both in
+** per-server context (httpd.conf) and per-dir context (.htaccess) and even
+** can generate QUERY_STRING parts on result. The rewriting result finally
+** can lead to internal subprocessing, external request redirection or even
+** to internal proxy throughput.
+**
+** This module was originally written in April 1996 and
+** gifted exclusively to the The Apache Group in July 1997 by
+**
+** Ralf S. Engelschall
+** rse@engelschall.com
+** www.engelschall.com
+*/
+
+
+
+
+ /* The NDBM support:
+ We support only NDBM files.
+ But we have to stat the file for the mtime,
+ so we also need to know the file extension */
+#if HAS_NDBM_LIB
+#include <ndbm.h>
+#if (__FreeBSD__)
+#define NDBM_FILE_SUFFIX ".db"
+#else
+#define NDBM_FILE_SUFFIX ".pag"
+#endif
+#endif
+
+
+ /* The locking support:
+ Try to determine whether we should use fcntl() or flock().
+ Would be better conf.h could provide this... :-( */
+#if defined(USE_FCNTL_SERIALIZED_ACCEPT)
+#define USE_FCNTL 1
+#include <fcntl.h>
+#endif
+#if defined(USE_FLOCK_SERIALIZED_ACCEPT)
+#define USE_FLOCK 1
+#include <sys/file.h>
+#endif
+#if !defined(USE_FCNTL) && !defined(USE_FLOCK)
+#define USE_FLOCK 1
+#ifndef MPE
+#include <sys/file.h>
+#endif
+#ifndef LOCK_UN
+#undef USE_FLOCK
+#define USE_FCNTL 1
+#include <fcntl.h>
+#endif
+#endif
+#ifdef AIX
+#undef USE_FLOCK
+#define USE_FCNTL 1
+#include <fcntl.h>
+#endif
+
+ /* The locking support for the RewriteMap programs:
+ Locking a pipe to the child works fine under most
+ Unix derivates, but braindead SunOS 4.1.x has
+ problems with this approach... */
+#define USE_PIPE_LOCKING 1
+#ifdef SUNOS4
+#undef USE_PIPE_LOCKING
+#endif
+
+
+/*
+**
+** Some defines
+**
+*/
+
+#define ENVVAR_SCRIPT_URL "SCRIPT_URL"
+#define ENVVAR_SCRIPT_URI "SCRIPT_URI"
+
+#ifndef SUPPORT_DBM_REWRITEMAP
+#define SUPPORT_DBM_REWRITEMAP 0
+#endif
+
+#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype"
+
+#define CONDFLAG_NONE 1<<0
+#define CONDFLAG_NOCASE 1<<1
+#define CONDFLAG_NOTMATCH 1<<2
+#define CONDFLAG_ORNEXT 1<<3
+
+#define RULEFLAG_NONE 1<<0
+#define RULEFLAG_FORCEREDIRECT 1<<1
+#define RULEFLAG_LASTRULE 1<<2
+#define RULEFLAG_NEWROUND 1<<3
+#define RULEFLAG_CHAIN 1<<4
+#define RULEFLAG_IGNOREONSUBREQ 1<<5
+#define RULEFLAG_NOTMATCH 1<<6
+#define RULEFLAG_PROXY 1<<7
+#define RULEFLAG_PASSTHROUGH 1<<8
+#define RULEFLAG_FORBIDDEN 1<<9
+#define RULEFLAG_GONE 1<<10
+#define RULEFLAG_QSAPPEND 1<<11
+
+#define MAPTYPE_TXT 1<<0
+#define MAPTYPE_DBM 1<<1
+#define MAPTYPE_PRG 1<<2
+
+#define ENGINE_DISABLED 1<<0
+#define ENGINE_ENABLED 1<<1
+
+#define OPTION_NONE 1<<0
+#define OPTION_INHERIT 1<<1
+
+#define CACHEMODE_TS 1<<0
+#define CACHEMODE_TTL 1<<1
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE !FALSE
+#endif
+
+#ifndef NO
+#define NO FALSE
+#define YES TRUE
+#endif
+
+#ifndef LONG_STRING_LEN
+#define LONG_STRING_LEN 2048
+#endif
+
+#define MAX_ENV_FLAGS 5
+
+#define EOS_PARANOIA(ca) ca[sizeof(ca)-1] = '\0'
+
+
+/*
+**
+** our private data structures we handle with
+**
+*/
+
+ /* the list structures for holding the mapfile information
+ and the rewrite rules */
+
+typedef struct {
+ char *name; /* the name of the map */
+ char *datafile; /* the file which contains the data of the map */
+ char *checkfile; /* the file which stays for existence of the map */
+ int type; /* the type of the map */
+ int fpin; /* in filepointer for program maps */
+ int fpout; /* out filepointer for program maps */
+} rewritemap_entry;
+
+typedef struct {
+ char *input; /* Input string of RewriteCond */
+ char *pattern; /* the RegExp pattern string */
+ regex_t *regexp;
+ int flags; /* Flags which control the match */
+} rewritecond_entry;
+
+typedef struct {
+ array_header *rewriteconds; /* the corresponding RewriteCond entries */
+ char *pattern; /* the RegExp pattern string */
+ regex_t *regexp; /* the RegExp pattern compilation */
+ char *output; /* the Substitution string */
+ int flags; /* Flags which control the substitution */
+ char *forced_mimetype; /* forced MIME type of substitution */
+ int forced_responsecode; /* forced HTTP redirect response status */
+ char *env[MAX_ENV_FLAGS+1];/* added environment variables */
+ int skip; /* number of next rules to skip */
+} rewriterule_entry;
+
+
+ /* the per-server or per-virtual-server configuration
+ statically generated once on startup for every server */
+
+typedef struct {
+ int state; /* the RewriteEngine state */
+ int options; /* the RewriteOption state */
+ char *rewritelogfile; /* the RewriteLog filename */
+ int rewritelogfp; /* the RewriteLog open filepointer */
+ int rewriteloglevel; /* the RewriteLog level of verbosity */
+ array_header *rewritemaps; /* the RewriteMap entries */
+ array_header *rewriteconds; /* the RewriteCond entries (temporary) */
+ array_header *rewriterules; /* the RewriteRule entries */
+} rewrite_server_conf;
+
+
+ /* the per-directory configuration
+ individually generated on-the-fly by Apache server for current request */
+
+typedef struct {
+ int state; /* the RewriteEngine state */
+ int options; /* the RewriteOption state */
+ array_header *rewriteconds; /* the RewriteCond entries (temporary) */
+ array_header *rewriterules; /* the RewriteRule entries */
+ char *directory; /* the directory where it applies */
+ char *baseurl; /* the base-URL where it applies */
+} rewrite_perdir_conf;
+
+
+ /* the cache structures */
+
+typedef struct cacheentry {
+ time_t time;
+ char *key;
+ char *value;
+} cacheentry;
+
+typedef struct cachelist {
+ char *resource;
+ array_header *entries;
+} cachelist;
+
+typedef struct cache {
+ pool *pool;
+ array_header *lists;
+} cache;
+
+
+/*
+**
+** forward declarations
+**
+*/
+
+ /* config structure handling */
+static void *config_server_create(pool *p, server_rec *s);
+static void *config_server_merge (pool *p, void *basev, void *overridesv);
+static void *config_perdir_create(pool *p, char *path);
+static void *config_perdir_merge (pool *p, void *basev, void *overridesv);
+
+ /* config directive handling */
+static const char *cmd_rewriteengine (cmd_parms *cmd, rewrite_perdir_conf *dconf, int flag);
+static const char *cmd_rewriteoptions (cmd_parms *cmd, rewrite_perdir_conf *dconf, char *option);
+static const char *cmd_rewriteoptions_setoption(pool *p, int *options, char *name);
+static const char *cmd_rewritelog (cmd_parms *cmd, void *dconf, char *a1);
+static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, char *a1);
+static const char *cmd_rewritemap (cmd_parms *cmd, void *dconf, char *a1, char *a2);
+
+static const char *cmd_rewritebase(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *a1);
+
+static const char *cmd_rewritecond (cmd_parms *cmd, rewrite_perdir_conf *dconf, char *str);
+static const char *cmd_rewritecond_parseflagfield(pool *p, rewritecond_entry *new, char *str);
+static const char *cmd_rewritecond_setflag (pool *p, rewritecond_entry *cfg, char *key, char *val);
+
+extern const char *cmd_rewriterule (cmd_parms *cmd, rewrite_perdir_conf *dconf, char *str);
+static const char *cmd_rewriterule_parseflagfield(pool *p, rewriterule_entry *new, char *str);
+static const char *cmd_rewriterule_setflag (pool *p, rewriterule_entry *cfg, char *key, char *val);
+
+ /* initialisation */
+static void init_module(server_rec *s, pool *p);
+
+ /* runtime hooks */
+static int hook_uri2file (request_rec *r);
+static int hook_mimetype (request_rec *r);
+static int hook_fixup (request_rec *r);
+static int handler_redirect(request_rec *r);
+
+ /* rewriting engine */
+static int apply_rewrite_list(request_rec *r, array_header *rewriterules, char *perdir);
+static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p, char *perdir);
+static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p, char *perdir);
+
+ /* URI transformation function */
+static void splitout_queryargs(request_rec *r, int qsappend);
+static void reduce_uri(request_rec *r);
+static char *expand_tildepaths(request_rec *r, char *uri);
+static void expand_map_lookups(request_rec *r, char *uri, int uri_len);
+
+ /* DBM hashfile support functions */
+static char *lookup_map(request_rec *r, char *name, char *key);
+static char *lookup_map_txtfile(request_rec *r, char *file, char *key);
+#if HAS_NDBM_LIB
+static char *lookup_map_dbmfile(request_rec *r, char *file, char *key);
+#endif
+static char *lookup_map_program(request_rec *r, int fpin, int fpout, char *key);
+
+ /* rewriting logfile support */
+static void open_rewritelog(server_rec *s, pool *p);
+static void rewritelog_child(void *cmd);
+static void rewritelog(request_rec *r, int level, const char *text, ...);
+static char *current_logtime(request_rec *r);
+
+ /* program map support */
+static void run_rewritemap_programs(server_rec *s, pool *p);
+static void rewritemap_program_child(void *cmd);
+
+ /* env variable support */
+static void expand_variables_inbuffer(request_rec *r, char *buf, int buf_len);
+static char *expand_variables(request_rec *r, char *str);
+static char *lookup_variable(request_rec *r, char *var);
+static char *lookup_header(request_rec *r, const char *name);
+
+ /* caching functions */
+static cache *init_cache(pool *p);
+static char *get_cache_string(cache *c, char *res, int mode, time_t mtime, char *key);
+static void set_cache_string(cache *c, char *res, int mode, time_t mtime, char *key, char *value);
+static cacheentry *retrieve_cache_string(cache *c, char *res, char *key);
+static void store_cache_string(cache *c, char *res, cacheentry *ce);
+
+ /* misc functions */
+static char *subst_prefix_path(request_rec *r, char *input, char *match, char *subst);
+static int parseargline(char *str, char **a1, char **a2, char **a3);
+static int prefix_stat(const char *path, struct stat *sb);
+static void add_env_variable(request_rec *r, char *s);
+
+ /* DNS functions */
+static int is_this_our_host(request_rec *r, char *testhost);
+static int isaddr(char *host);
+static char **resolv_ipaddr_list(request_rec *r, char *name);
+
+ /* Proxy Module check */
+static int is_proxy_available(server_rec *s);
+
+ /* File locking */
+static void fd_lock(int fd);
+static void fd_unlock(int fd);
+
+ /* Lexicographic Comparison */
+static int compare_lexicography(char *cpNum1, char *cpNum2);
+
+#endif /* _MOD_REWRITE_H */
+
+/*EOF*/
diff --git a/usr.sbin/httpd/src/mod_status.c b/usr.sbin/httpd/src/mod_status.c
new file mode 100644
index 00000000000..c00a3a4d5f9
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_status.c
@@ -0,0 +1,643 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/* Status Module. Display lots of internal data about how Apache is
+ * performing and the state of all children processes.
+ *
+ * To enable this, add the following lines into any config file:
+ *
+ * <Location /server-status>
+ * SetHandler server-status
+ * </Location>
+ *
+ * You may want to protect this location by password or domain so no one
+ * else can look at it. Then you can access the statistics with a URL like:
+ *
+ * http://your_server_name/server-status
+ *
+ * /server-status - Returns page using tables
+ * /server-status?notable - Returns page for browsers without table support
+ * /server-status?refresh - Returns page with 1 second refresh
+ * /server-status?refresh=6 - Returns page with refresh every 6 seconds
+ * /server-status?auto - Returns page with data for automatic parsing
+ *
+ * Mark Cox, mark@ukweb.com, November 1995
+ *
+ * 12.11.95 Initial version for www.telescope.org
+ * 13.3.96 Updated to remove rprintf's [Mark]
+ * 18.3.96 Added CPU usage, process information, and tidied [Ben Laurie]
+ * 18.3.96 Make extra Scoreboard variables #definable
+ * 25.3.96 Make short report have full precision [Ben Laurie suggested]
+ * 25.3.96 Show uptime better [Mark/Ben Laurie]
+ * 29.3.96 Better HTML and explanation [Mark/Rob Hartill suggested]
+ * 09.4.96 Added message for non-STATUS compiled version
+ * 18.4.96 Added per child and per slot counters [Jim Jagielski]
+ * 01.5.96 Table format, cleanup, even more spiffy data [Chuck Murcko/Jim J.]
+ * 18.5.96 Adapted to use new rprintf() routine, incidentally fixing a missing
+ * piece in short reports [Ben Laurie]
+ * 21.5.96 Additional Status codes (DNS and LOGGING only enabled if
+ extended STATUS is enabled) [George Burgyan/Jim J.] */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "util_script.h"
+#include <time.h>
+#include "scoreboard.h"
+#include "http_log.h"
+
+#ifdef NEXT
+#include <machine/param.h>
+#endif
+
+#define STATUS_MAXLINE 64
+
+#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 format_kbyte_out(request_rec *r,unsigned long kbytes)
+{
+ if (kbytes < KBYTE)
+ rprintf(r,"%d kB",(int)kbytes);
+ else if (kbytes < MBYTE)
+ rprintf(r,"%.1f MB",(float)kbytes/KBYTE);
+ else
+ rprintf(r,"%.1f GB",(float)kbytes/MBYTE);
+}
+
+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;
+ unsigned long kbcount=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';
+ status[SERVER_GRACEFUL]='G';
+
+ if (!exists_scoreboard_image()) {
+ log_printf(r->server, "Server status unavailable in inetd mode");
+ return HTTP_NOT_IMPLEMENTED;
+ }
+ r->allowed = (1 << M_GET) | (1 << M_TRACE);
+ if (r->method_number != M_GET) return HTTP_METHOD_NOT_ALLOWED;
+
+ 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++;
+ }
+ }
+
+ send_http_header(r);
+
+ if (r->header_only)
+ return 0;
+
+ sync_scoreboard_image();
+ for (i = 0; i<HARD_SERVER_LIMIT; ++i)
+ {
+ score_record = get_scoreboard_info(i);
+ res = score_record.status;
+ stat_buffer[i] = status[res];
+ if (res == SERVER_READY)
+ ready++;
+ else if (res == SERVER_BUSY_READ || res==SERVER_BUSY_WRITE ||
+ res == SERVER_STARTING || res==SERVER_BUSY_KEEPALIVE ||
+ res == SERVER_BUSY_LOG || res==SERVER_BUSY_DNS ||
+ res == SERVER_GRACEFUL)
+ busy++;
+#if defined(STATUS)
+ lres = score_record.access_count;
+ bytes= score_record.bytes_served;
+ if (lres!=0 || (score_record.status != SERVER_READY
+ && score_record.status != SERVER_DEAD))
+ {
+ tu+=score_record.times.tms_utime;
+ ts+=score_record.times.tms_stime;
+ tcu+=score_record.times.tms_cutime;
+ tcs+=score_record.times.tms_cstime;
+ count+=lres;
+ bcount+=bytes;
+ if (bcount>=KBYTE) {
+ kbcount += (bcount >> 10);
+ bcount = bcount & 0x3ff;
+ }
+ }
+#endif /* STATUS */
+ }
+
+ up_time=nowtime-restart_time;
+
+ hard_timeout("send status info", r);
+
+ if (!short_report)
+ {
+ rputs("<HTML><HEAD>\n<TITLE>Apache Status</TITLE>\n</HEAD><BODY>\n",r);
+ rputs("<H1>Apache Server Status for ",r);
+ rvputs(r,server->server_hostname,"</H1>\n\n",NULL);
+ rvputs(r,"Current Time: ",asctime(localtime(&nowtime)),"<br>\n",NULL);
+ rvputs(r,"Restart Time: ",asctime(localtime(&restart_time)),"<br>\n",
+ NULL);
+ rputs("Server uptime: ",r);
+ show_time(r,up_time);
+ rputs("<br>\n",r);
+ }
+
+#if defined(STATUS)
+ if (short_report)
+ {
+ rprintf(r,"Total Accesses: %lu\nTotal kBytes: %lu\n",count,kbcount);
+
+#ifndef __EMX__
+ /* Allow for OS/2 not having CPU stats */
+ if(ts || tu || tcu || tcs)
+ rprintf(r,"CPULoad: %g\n",(tu+ts+tcu+tcs)/tick/up_time*100.);
+#endif
+
+ 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",KBYTE*(float)kbcount/(float)up_time);
+
+ if (count>0)
+ rprintf(r,"BytesPerReq: %g\n",KBYTE*(float)kbcount/(float)count);
+ } else /* !short_report */
+ {
+ rprintf(r,"Total accesses: %lu - Total Traffic: ", count);
+ format_kbyte_out(r,kbcount);
+
+#ifndef __EMX__
+ /* Allow for OS/2 not having CPU stats */
+ rputs("<br>\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.);
+#endif
+
+ rputs("<br>\n",r);
+
+ if (up_time>0)
+ rprintf(r,"%.3g requests/sec - ",
+ (float)count/(float)up_time);
+
+ if (up_time>0)
+ {
+ format_byte_out(r,KBYTE*(float)kbcount/(float)up_time);
+ rputs("/second - ",r);
+ }
+
+ if (count>0)
+ {
+ format_byte_out(r,KBYTE*(float)kbcount/(float)count);
+ rputs("/request",r);
+ }
+
+ rputs("<br>\n",r);
+ } /* short_report */
+#endif /* STATUS */
+
+ if (!short_report)
+ rprintf(r,"\n%d requests currently being processed, %d idle servers\n"
+ ,busy,ready);
+ else
+ rprintf(r,"BusyServers: %d\nIdleServers: %d\n",busy,ready);
+
+ /* send the scoreboard 'table' out */
+
+ if(!short_report)
+ rputs("<PRE>",r);
+ else
+ rputs("Scoreboard: ",r);
+
+ for (i = 0; i<HARD_SERVER_LIMIT; ++i)
+ {
+ rputc(stat_buffer[i], r);
+ if((i%STATUS_MAXLINE == (STATUS_MAXLINE - 1))&&!short_report)
+ rputs("\n",r);
+ }
+
+ if (short_report)
+ rputs("\n",r);
+ else {
+ rputs("</PRE>\n",r);
+ rputs("Scoreboard Key: <br>\n",r);
+ rputs("\"<B><code>_</code></B>\" Waiting for Connection, \n",r);
+ rputs("\"<B><code>S</code></B>\" Starting up, \n",r);
+ rputs("\"<B><code>R</code></B>\" Reading Request,<BR>\n",r);
+ rputs("\"<B><code>W</code></B>\" Sending Reply, \n",r);
+ rputs("\"<B><code>K</code></B>\" Keepalive (read), \n",r);
+ rputs("\"<B><code>D</code></B>\" DNS Lookup,<BR>\n",r);
+ rputs("\"<B><code>L</code></B>\" Logging, \n",r);
+ rputs("\"<B><code>G</code></B>\" Gracefully finishing, \n",r);
+ rputs("\"<B><code>.</code></B>\" Open slot with no current process<P>\n",r);
+ }
+
+#if defined(STATUS)
+ if (!short_report)
+ if(no_table_report)
+ rputs("<p><hr><h2>Server Details</h2>\n\n",r);
+ else
+#ifdef __EMX__
+ /* Allow for OS/2 not having CPU stats */
+ rputs("<p>\n\n<table border=0><tr><th>Srv<th>PID<th>Acc<th>M\n<th>SS<th>Conn<th>Child<th>Slot<th>Host<th>VHost<th>Request</tr>\n\n",r);
+#else
+ rputs("<p>\n\n<table border=0><tr><th>Srv<th>PID<th>Acc<th>M<th>CPU\n<th>SS<th>Conn<th>Child<th>Slot<th>Host<th>VHost<th>Request</tr>\n\n",r);
+#endif
+
+
+ for (i = 0; i<HARD_SERVER_LIMIT; ++i)
+ {
+ score_record=get_scoreboard_info(i);
+ lres = score_record.access_count;
+ my_lres = score_record.my_access_count;
+ conn_lres = score_record.conn_count;
+ bytes= score_record.bytes_served;
+ my_bytes = score_record.my_bytes_served;
+ conn_bytes = score_record.conn_bytes;
+ if (lres!=0 || (score_record.status != SERVER_READY
+ && score_record.status != SERVER_DEAD))
+ {
+ if (!short_report)
+ {
+ if (no_table_report)
+ {
+ rprintf(r,"<b>Server %d</b> (%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("<b>Read</b>",r);
+ break;
+ case SERVER_BUSY_WRITE:
+ rputs("<b>Write</b>",r);
+ break;
+ case SERVER_BUSY_KEEPALIVE:
+ rputs("<b>Keepalive</b>",r);
+ break;
+ case SERVER_BUSY_LOG:
+ rputs("<b>Logging</b>",r);
+ break;
+ case SERVER_BUSY_DNS:
+ rputs("<b>DNS lookup</b>",r);
+ break;
+ case SERVER_DEAD:
+ rputs("Dead",r);
+ break;
+ case SERVER_GRACEFUL:
+ rputs("Graceful",r);
+ break;
+ default:
+ rputs("?STATE?",r);
+ break;
+ }
+#ifdef __EMX__
+ /* Allow for OS/2 not having CPU stats */
+ rprintf(r,"]\n %s (",
+#else
+
+ 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,
+#endif
+ 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," <i>%s {%s}</i><br>\n\n",
+ score_record.client,
+ escape_html(r->pool, score_record.request));
+ }
+ else /* !no_table_report */
+ {
+ rprintf(r,"<tr><td><b>%d</b><td>%d<td>%d/%lu/%lu",
+ i,(int)score_record.pid,(int)conn_lres,my_lres,lres);
+
+ switch (score_record.status)
+ {
+ case SERVER_READY:
+ rputs("<td>_",r);
+ break;
+ case SERVER_STARTING:
+ rputs("<td><b>S</b>",r);
+ break;
+ case SERVER_BUSY_READ:
+ rputs("<td><b>R</b>",r);
+ break;
+ case SERVER_BUSY_WRITE:
+ rputs("<td><b>W</b>",r);
+ break;
+ case SERVER_BUSY_KEEPALIVE:
+ rputs("<td><b>K</b>",r);
+ break;
+ case SERVER_BUSY_LOG:
+ rputs("<td><b>L</b>",r);
+ break;
+ case SERVER_BUSY_DNS:
+ rputs("<td><b>D</b>",r);
+ break;
+ case SERVER_DEAD:
+ rputs("<td>.",r);
+ break;
+ case SERVER_GRACEFUL:
+ rputs("<td>G",r);
+ break;
+ default:
+ rputs("<td>?",r);
+ break;
+ }
+#ifdef __EMX__
+ /* Allow for OS/2 not having CPU stats */
+ rprintf(r,"\n<td>%.0f",
+#else
+ rprintf(r,"\n<td>%.2f<td>%.0f",
+ (score_record.times.tms_utime +
+ score_record.times.tms_stime +
+ score_record.times.tms_cutime +
+ score_record.times.tms_cstime)/tick,
+#endif
+ difftime(nowtime, score_record.last_used));
+ rprintf(r,"<td>%-1.1f<td>%-2.2f<td>%-2.2f\n",
+ (float)conn_bytes/KBYTE, (float)my_bytes/MBYTE,
+ (float)bytes/MBYTE);
+ rprintf(r,"<td>%s<td nowrap>%s<td nowrap>%s</tr>\n\n",
+ score_record.client, score_record.vhost,
+ escape_html(r->pool, score_record.request));
+ } /* no_table_report */
+ } /* !short_report */
+ } /* if (<active child>) */
+ } /* for () */
+
+ if (!(short_report || no_table_report))
+ {
+#ifdef __EMX__
+ rputs("</table>\n \
+<hr> \
+<table>\n \
+<tr><th>Srv<td>Server number\n \
+<tr><th>PID<td>OS process ID\n \
+<tr><th>Acc<td>Number of accesses this connection / this child / this slot\n \
+<tr><th>M<td>Mode of operation\n \
+<tr><th>SS<td>Seconds since beginning of most recent request\n \
+<tr><th>Conn<td>Kilobytes transferred this connection\n \
+<tr><th>Child<td>Megabytes transferred this child\n \
+<tr><th>Slot<td>Total megabytes transferred this slot\n \
+</table>\n",r);
+#else
+ rputs("</table>\n \
+<hr> \
+<table>\n \
+<tr><th>Srv<td>Server number\n \
+<tr><th>PID<td>OS process ID\n \
+<tr><th>Acc<td>Number of accesses this connection / this child / this slot\n \
+<tr><th>M<td>Mode of operation\n \
+<tr><th>CPU<td>CPU usage, number of seconds\n \
+<tr><th>SS<td>Seconds since beginning of most recent request\n \
+<tr><th>Conn<td>Kilobytes transferred this connection\n \
+<tr><th>Child<td>Megabytes transferred this child\n \
+<tr><th>Slot<td>Total megabytes transferred this slot\n \
+</table>\n",r);
+#endif
+ }
+
+#else /* !defined(STATUS) */
+
+ rputs("<hr>To obtain a full report with current status information and",r);
+ rputs(" DNS and LOGGING status codes \n",r);
+ rputs("you need to recompile Apache after adding the line <pre>",r);
+ rputs("Rule STATUS=yes</pre>into the file <code>Configuration</code>\n",r);
+
+#endif /* STATUS */
+
+ if (!short_report)
+ rputs("</BODY></HTML>\n",r);
+
+ kill_timeout(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 */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_userdir.c b/usr.sbin/httpd/src/mod_userdir.c
new file mode 100644
index 00000000000..d251e92b60b
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_userdir.c
@@ -0,0 +1,214 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * 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;
+}
+
+const 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;
+ const char *userdirs = (char *)get_module_config(server_conf,
+ &userdir_module);
+ char *name = r->uri;
+ const char *w, *dname, *redirect;
+ char *x = NULL;
+ struct stat statbuf;
+
+ if (userdirs == NULL || !strcasecmp(userdirs, "disabled") ||
+ (name[0] != '/') || (name[1] != '~')) {
+ return DECLINED;
+ }
+
+ while (*userdirs) {
+ const char *userdir = getword_conf (r->pool, &userdirs);
+ char *filename = NULL;
+
+ dname = name + 2;
+ w = getword(r->pool, &dname, '/');
+
+ /* disallow the empty username, . and .. */
+ if (w[0] == '\0' || (w[1] == '.' && (w[2] == '\0' || (w[2] == '.' && w[3] == '\0'))))
+ 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, '*');
+
+#ifdef __EMX__
+ /* Add support for OS/2 drive letters */
+ if ((userdir[0] == '/') || (userdir[1] == ':') || (userdir[0] == '\0')) {
+#else
+ if ((userdir[0] == '/') || (userdir[0] == '\0')) {
+#endif
+ 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, &statbuf) != -1)) {
+ r->filename = pstrcat(r->pool, filename, dname, NULL);
+ r->finfo = statbuf;
+ return OK;
+ }
+ }
+
+ return DECLINED;
+}
+
+module userdir_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ create_userdir_config, /* server config */
+ NULL, /* merge server config */
+ userdir_cmds, /* command table */
+ NULL, /* handlers */
+ translate_userdir, /*filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL, /* logger */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/mod_usertrack.c b/usr.sbin/httpd/src/mod_usertrack.c
new file mode 100644
index 00000000000..5be43124151
--- /dev/null
+++ b/usr.sbin/httpd/src/mod_usertrack.c
@@ -0,0 +1,328 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/* User Tracking Module (Was mod_cookies.c)
+ *
+ * This Apache module is designed to track users paths through a site.
+ * It uses the client-side state ("Cookie") protocol developed by Netscape.
+ * It is known to work on Netscape browsers, Microsoft Internet
+ * Explorer and others currently being developed.
+ *
+ * Each time a page is requested we look to see if the browser is sending
+ * us a Cookie: header that we previously generated.
+ *
+ * If we don't find one then the user hasn't been to this site since
+ * starting their browser or their browser doesn't support cookies. So
+ * we generate a unique Cookie for the transaction and send it back to
+ * the browser (via a "Set-Cookie" header)
+ * Future requests from the same browser should keep the same Cookie line.
+ *
+ * By matching up all the requests with the same cookie you can
+ * work out exactly what path a user took through your site. To log
+ * the cookie use the " %{Cookie}n " directive in a custom access log;
+ *
+ * Example 1 : If you currently use the standard Log file format (CLF)
+ * and use the command "TransferLog somefilename", add the line
+ * LogFormat "%h %l %u %t \"%r\" %s %b %{Cookie}n"
+ * to your config file.
+ *
+ * Example 2 : If you used to use the old "CookieLog" directive, you
+ * can emulate it by adding the following command to your config file
+ * CustomLog filename "%{Cookie}n \"%r\" %t"
+ *
+ * Notes:
+ * 1. This code now logs the initial transaction (the one that created
+ * the cookie to start with).
+ * 2. This module has been designed to not interfere with other Cookies
+ * your site may be using; just avoid sending out cookies with
+ * the name "Apache=" or things will get confused.
+ * 3. If you want you can modify the Set-Cookie line so that the Cookie
+ * never expires. You would then get the same Cookie each time the
+ * user revisits your site.
+ *
+ * Mark Cox, mark@ukweb.com, 6 July 95
+ *
+ * This file replaces mod_cookies.c
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#ifndef MPE
+#include <sys/time.h>
+#endif
+
+module usertrack_module;
+
+typedef struct {
+ int always;
+ time_t expires;
+} cookie_log_state;
+
+/* Define this to allow post-2000 cookies. Cookies use two-digit dates,
+ * so it might be dicey. (Netscape does it correctly, but others may not)
+ */
+#define MILLENIAL_COOKIES
+
+/* Make Cookie: Now we have to generate something that is going to be
+ * pretty unique. We can base it on the pid, time, hostip */
+
+#define COOKIE_NAME "Apache="
+
+void make_cookie(request_rec *r)
+{
+ cookie_log_state *cls = get_module_config (r->server->module_config,
+ &usertrack_module);
+#ifdef MPE
+ clock_t mpe_times;
+ struct tms mpe_tms;
+#else
+ struct timeval tv;
+ struct timezone tz = { 0 , 0 };
+#endif
+ /* 1024 == hardcoded constants */
+ char *new_cookie = palloc( r->pool, 1024);
+ char *cookiebuf = palloc( r->pool, 1024);
+ char *dot;
+ const char *rname = pstrdup(r->pool,
+ get_remote_host(r->connection, r->per_dir_config,
+ REMOTE_NAME));
+
+ if ((dot = strchr(rname,'.'))) *dot='\0'; /* First bit of hostname */
+
+#ifdef MPE
+/* MPE lacks gettimeofday(), so we must use time() to obtain the epoch
+ seconds, and then times() to obtain CPU clock ticks (milliseconds).
+ Combine this together to obtain a hopefully unique cookie ID. */
+
+ mpe_times=times(&mpe_tms);
+
+ ap_snprintf(cookiebuf, 1024, "%s%d%ld%ld", rname, (int)getpid(),
+ (long)time(NULL), (long)mpe_tms.tms_utime);
+#else
+ gettimeofday(&tv, &tz);
+
+ ap_snprintf(cookiebuf, 1024, "%s%d%ld%d", rname, (int)getpid(),
+ (long)tv.tv_sec, (int)tv.tv_usec/1000);
+#endif
+
+ if (cls->expires) {
+ static const char *const days[7]=
+ {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+ struct tm *tms;
+ time_t when = time(NULL) + cls->expires;
+
+#ifndef MILLENIAL_COOKIES
+ /* Only two-digit date string, so we can't trust "00" or more.
+ * Therefore, we knock it all back to just before midnight on
+ * 1/1/2000 (which is 946684799)
+ */
+
+ if (when > 946684799)
+ when = 946684799;
+#endif
+ tms = gmtime(&when);
+
+ /* Cookie with date; as strftime '%a, %d-%h-%y %H:%M:%S GMT' */
+ ap_snprintf(new_cookie, 1024,
+ "%s%s; path=/; expires=%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT",
+ COOKIE_NAME, cookiebuf, days[tms->tm_wday],
+ tms->tm_mday, month_snames[tms->tm_mon],
+ (tms->tm_year >= 100) ? tms->tm_year - 100 : tms->tm_year,
+ tms->tm_hour, tms->tm_min, tms->tm_sec);
+ }
+ else
+ ap_snprintf(new_cookie, 1024, "%s%s; path=/", COOKIE_NAME, cookiebuf);
+
+ table_set(r->headers_out,"Set-Cookie",new_cookie);
+ table_set(r->notes, "cookie", cookiebuf); /* log first time */
+ return;
+}
+
+int spot_cookie(request_rec *r)
+{
+ int *enable = (int *)get_module_config(r->per_dir_config,
+ &usertrack_module);
+ char *cookie;
+ char *value;
+
+ if (!*enable) return DECLINED;
+
+ if ((cookie = table_get (r->headers_in, "Cookie")))
+ if ((value=strstr(cookie,COOKIE_NAME))) {
+ char *cookiebuf, *cookieend;
+
+ value+=strlen(COOKIE_NAME);
+ cookiebuf=pstrdup( r->pool, value );
+ cookieend=strchr(cookiebuf,';');
+ if (cookieend) *cookieend='\0'; /* Ignore anything after a ; */
+
+ /* Set the cookie in a note, for logging */
+ table_set(r->notes, "cookie", cookiebuf);
+
+ return DECLINED; /* Theres already a cookie, no new one */
+ }
+ make_cookie(r);
+ return OK; /* We set our cookie */
+}
+
+void *make_cookie_log_state (pool *p, server_rec *s)
+{
+ cookie_log_state *cls =
+ (cookie_log_state *)palloc (p, sizeof (cookie_log_state));
+
+ cls->expires = 0;
+
+ return (void *)cls;
+}
+
+void *make_cookie_dir (pool *p, char *d) {
+ return (void *)pcalloc(p, sizeof(int));
+}
+
+const char *set_cookie_enable (cmd_parms *cmd, int *c, int arg)
+{
+ *c = arg;
+ return NULL;
+}
+
+const char *set_cookie_exp (cmd_parms *parms, void *dummy, const char *arg)
+{
+ cookie_log_state *cls = get_module_config (parms->server->module_config,
+ &usertrack_module);
+ time_t factor, modifier = 0;
+ time_t num = 0;
+ char *word;
+
+ /* The simple case first - all numbers (we assume) */
+ if (isdigit(arg[0]) && isdigit(arg[strlen(arg)-1])) {
+ cls->expires = atol(arg);
+ return NULL;
+ }
+
+ /* The harder case - stolen from mod_expires
+ * CookieExpires "[plus] {<num> <type>}*"
+ */
+
+ word = getword_conf( parms->pool, &arg );
+ if ( !strncasecmp( word, "plus", 1 ) ) {
+ word = getword_conf( parms->pool, &arg );
+ };
+
+ /* {<num> <type>}* */
+ while ( word[0] ) {
+ /* <num> */
+ if ( strchr("0123456789", word[0]) != NULL )
+ num = atoi( word );
+ else
+ return "bad expires code, numeric value expected.";
+
+ /* <type> */
+ word = getword_conf( parms->pool, &arg );
+ if (!word[0] )
+ return "bad expires code, missing <type>";
+
+ factor = 0;
+ if ( !strncasecmp( word, "years", 1 ) )
+ factor = 60*60*24*365;
+ else if ( !strncasecmp( word, "months", 2 ) )
+ factor = 60*60*24*30;
+ else if ( !strncasecmp( word, "weeks", 1 ) )
+ factor = 60*60*24*7;
+ else if ( !strncasecmp( word, "days", 1 ) )
+ factor = 60*60*24;
+ else if ( !strncasecmp( word, "hours", 1 ) )
+ factor = 60*60;
+ else if ( !strncasecmp( word, "minutes", 2 ) )
+ factor = 60;
+ else if ( !strncasecmp( word, "seconds", 1 ) )
+ factor = 1;
+ else
+ return "bad expires code, unrecognized type";
+
+ modifier = modifier + factor * num;
+
+ /* next <num> */
+ word = getword_conf( parms->pool, &arg );
+ }
+
+ cls->expires = modifier;
+
+ return NULL;
+}
+
+command_rec cookie_log_cmds[] = {
+{ "CookieExpires", set_cookie_exp, NULL, RSRC_CONF, TAKE1,
+ "an expiry date code" },
+{ "CookieTracking", set_cookie_enable, NULL, OR_FILEINFO, FLAG,
+ "whether or not to enable cookies" },
+{ NULL }
+};
+
+module usertrack_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ make_cookie_dir, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ make_cookie_log_state, /* server config */
+ NULL, /* merge server configs */
+ cookie_log_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ spot_cookie, /* fixups */
+ NULL, /* logger */
+ NULL /* header parser */
+};
diff --git a/usr.sbin/httpd/src/modules/example/Makefile b/usr.sbin/httpd/src/modules/example/Makefile
new file mode 100644
index 00000000000..9bec391a3bf
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/example/Makefile
@@ -0,0 +1,107 @@
+# ====================================================================
+# Copyright (c) 1995-1997 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/>.
+#
+# Makefile for the Apache example module
+#
+
+#
+# This normally lives in modules/example under the Apache source
+# directory. If the depth or relationship changes, the following line
+# may need to be changed.
+#
+INCDIR=../..
+
+#
+# Everything below this point should be invariant.
+#
+SHELL=/bin/sh
+
+#
+# We inherit the definitions of CC, AUX_CFLAGS, and RANLIB from an
+# upline make(1) call.
+#
+CFLAGS=-I$(INCDIR) $(AUX_CFLAGS)
+
+MODULES=mod_example.o
+OBJS= \
+ $(MODULES)
+
+#
+# Now the rules saying how things are built.
+#
+.c.o:
+ $(CC) -c $(CFLAGS) $<
+
+all: $(OBJS)
+
+clean:
+ rm -f $(OBJS)
+
+#
+# Finally, what depnds upon which, so make can figure out what it needs
+# to do.
+#
+
+#
+# Make sure that things get rebuilt if the Makefiles are changed.
+#
+$(OBJS): \
+ Makefile \
+ $(INCDIR)/Makefile
+
+mod_example.o: \
+ $(INCDIR)/httpd.h \
+ $(INCDIR)/http_config.h \
+ $(INCDIR)/http_core.h \
+ $(INCDIR)/http_log.h \
+ $(INCDIR)/http_main.h \
+ $(INCDIR)/http_protocol.h \
+ $(INCDIR)/util_script.h \
+ mod_example.c
diff --git a/usr.sbin/httpd/src/modules/example/README b/usr.sbin/httpd/src/modules/example/README
new file mode 100644
index 00000000000..77abc097c00
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/example/README
@@ -0,0 +1,53 @@
+README for Apache 1.2 Example Module
+[April, 1997]
+
+The files in the src/modules/example directory under the Apache
+distribution directory tree are provided as an example to those that
+wish to write modules that use the Apache API.
+
+The main file is mod_example.c, which illustrates all the different
+callback mechanisms and call syntaces. By no means does an add-on
+module need to include routines for all of the callbacks - quite the
+contrary!
+
+The example module is an actual working module. If you link it into
+your server, enable the "example-handler" handler for a location, and then
+browse to that location, you will see a display of some of the tracing
+the example module did as the various callbacks were made.
+
+To include the example module in your server, follow the steps below:
+
+ 1. Uncomment the "Module example_module" line near the bottom of
+ the src/Configuration file. If there isn't one, add it; it
+ should look like this:
+
+ Module example_module modules/example/mod_example.o
+
+ 2. Run the src/Configure script ("cd src; ./Configure"). This will
+ build the Makefile for the server itself, and update the
+ src/modules/Makefile for any additional modules you have
+ requested from beneath that subdirectory.
+
+ 3. Make the server (run "make" in the src directory).
+
+To add another module of your own:
+
+ A. mkdir src/modules/mymodule
+ B. cp src/modules/example/* src/modules/mymodule
+ C. Modify the files in the new directory
+ D. Follow steps [1] through [3] above, with appropriate changes.
+
+To activate the example module, include a block similar to the
+following in your srm.conf file:
+
+ <Location /example-info>
+ SetHandler example-handler
+ </Location>
+
+As an alternative, you can put the following into a .htaccess file and
+then request the file "test.example" from that location:
+
+ AddHandler example-handler .example
+
+After reloading/restarting your server, you should be able to browse
+to this location and see the brief display mentioned earlier.
diff --git a/usr.sbin/httpd/src/modules/example/mod_example.c b/usr.sbin/httpd/src/modules/example/mod_example.c
new file mode 100644
index 00000000000..1aeda876d01
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/example/mod_example.c
@@ -0,0 +1,1110 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * Apache example module. Provide demonstrations of how modules do things.
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "http_protocol.h"
+#include "util_script.h"
+
+#include <stdio.h>
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* Data declarations. */
+/* */
+/* Here are the static cells and structure declarations private to our */
+/* module. */
+/* */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Sample configuration record. Used for both per-directory and per-server
+ * configuration data.
+ *
+ * It's perfectly reasonable to have two different structures for the two
+ * different environments. The same command handlers will be called for
+ * both, though, so the handlers need to be able to tell them apart. One
+ * possibility is for both structures to start with an int which is zero for
+ * one and 1 for the other.
+ *
+ * Note that while the per-directory and per-server configuration records are
+ * available to most of the module handlers, they should be treated as
+ * READ-ONLY by all except the command and merge handlers. Sometimes handlers
+ * are handed a record that applies to the current location by implication or
+ * inheritance, and modifying it will change the rules for other locations.
+ */
+typedef struct example_config {
+ int cmode; /* Environment to which record applies (directory, */
+ /* server, or combination). */
+#define CONFIG_MODE_SERVER 1
+#define CONFIG_MODE_DIRECTORY 2
+#define CONFIG_MODE_COMBO 3 /* Shouldn't ever happen. */
+ int local; /* Boolean: was "Example" directive declared here? */
+ int congenital; /* Boolean: did we inherit an "Example"? */
+ char *trace; /* Pointer to trace string. */
+ char *loc; /* Location to which this record applies. */
+} example_config;
+
+/*
+ * Let's set up a module-local static cell to point to the accreting callback
+ * trace. As each API callback is made to us, we'll tack on the particulars
+ * to whatever we've already recorded. To avoid massive memory bloat as
+ * directories are walked again and again, we record the routine/environment
+ * the first time (non-request context only), and ignore subsequent calls for
+ * the same routine/environment.
+ */
+static char *trace = NULL;
+static table *static_calls_made = NULL;
+
+/*
+ * To avoid leaking memory from pools other than the per-request one, we
+ * allocate a module-private pool, and then use a sub-pool of that which gets
+ * freed each time we modify the trace. That way previous layers of trace
+ * data don't get lost.
+ */
+static pool *example_pool = NULL;
+static pool *example_subpool = NULL;
+
+/*
+ * Declare ourselves so the configuration routines can find and know us.
+ * We'll fill it in at the end of the module.
+ */
+module example_module;
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* The following pseudo-prototype declarations illustrate the parameters */
+/* passed to command handlers for the different types of directive */
+/* syntax. If an argument was specified in the directive definition */
+/* (look for "command_rec" below), it's available to the command handler */
+/* via the (void *) info field in the cmd_parms argument passed to the */
+/* handler (cmd->info for the examples below). */
+/* */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Command handler for a NO_ARGS directive.
+ *
+ * static const char *handle_NO_ARGS
+ * (cmd_parms *cmd, void *mconfig);
+ */
+
+/*
+ * Command handler for a RAW_ARGS directive. The "args" argument is the text
+ * of the commandline following the directive itself.
+ *
+ * static const char *handle_RAW_ARGS
+ * (cmd_parms *cmd, void *mconfig, const char *args);
+ */
+
+/*
+ * Command handler for a TAKE1 directive. The single parameter is passed in
+ * "word1".
+ *
+ * static const char *handle_TAKE1
+ * (cmd_parms *cmd, void *mconfig, char *word1);
+ */
+
+/*
+ * Command handler for a TAKE2 directive. TAKE2 commands must always have
+ * exactly two arguments.
+ *
+ * static const char *handle_TAKE2
+ * (cmd_parms *cmd, void *mconfig, char *word1, char *word2);
+ */
+
+/*
+ * Command handler for a TAKE3 directive. Like TAKE2, these must have exactly
+ * three arguments, or the parser complains and doesn't bother calling us.
+ *
+ * static const char *handle_TAKE3
+ * (cmd_parms *cmd, void *mconfig, char *word1, char *word2, char *word3);
+ */
+
+/*
+ * Command handler for a TAKE12 directive. These can take either one or two
+ * arguments.
+ * - word2 is a NULL pointer if no second argument was specified.
+ *
+ * static const char *handle_TAKE12
+ * (cmd_parms *cmd, void *mconfig, char *word1, char *word2);
+ */
+
+/*
+ * Command handler for a TAKE123 directive. A TAKE123 directive can be given,
+ * as might be expected, one, two, or three arguments.
+ * - word2 is a NULL pointer if no second argument was specified.
+ * - word3 is a NULL pointer if no third argument was specified.
+ *
+ * static const char *handle_TAKE123
+ * (cmd_parms *cmd, void *mconfig, char *word1, char *word2, char *word3);
+ */
+
+/*
+ * Command handler for a TAKE13 directive. Either one or three arguments are
+ * permitted - no two-parameters-only syntax is allowed.
+ * - word2 and word3 are NULL pointers if only one argument was specified.
+ *
+ * static const char *handle_TAKE13
+ * (cmd_parms *cmd, void *mconfig, char *word1, char *word2, char *word3);
+ */
+
+/*
+ * Command handler for a TAKE23 directive. At least two and as many as three
+ * arguments must be specified.
+ * - word3 is a NULL pointer if no third argument was specified.
+ *
+ * static const char *handle_TAKE23
+ * (cmd_parms *cmd, void *mconfig, char *word1, char *word2, char *word3);
+ */
+
+/*
+ * Command handler for a ITERATE directive.
+ * - Handler is called once for each of n arguments given to the directive.
+ * - word1 points to each argument in turn.
+ *
+ * static const char *handle_ITERATE
+ * (cmd_parms *cmd, void *mconfig, char *word1);
+ */
+
+/*
+ * Command handler for a ITERATE2 directive.
+ * - Handler is called once for each of the second and subsequent arguments
+ * given to the directive.
+ * - word1 is the same for each call for a particular directive instance (the
+ * first argument).
+ * - word2 points to each of the second and subsequent arguments in turn.
+ *
+ * static const char *handle_ITERATE2
+ * (cmd_parms *cmd, void *mconfig, char *word1, char *word2);
+ */
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* These routines are strictly internal to this module, and support its */
+/* operation. They are not referenced by any external portion of the */
+/* server. */
+/* */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Locate our directory configuration record for the current request.
+ */
+static example_config *our_dconfig
+ (request_rec *r) {
+
+ return (example_config *) get_module_config
+ (
+ r->per_dir_config,
+ &example_module
+ );
+}
+
+/*
+ * Locate our server configuration record for the specified server.
+ */
+static example_config *our_sconfig
+ (server_rec *s) {
+
+ return (example_config *) get_module_config
+ (
+ s->module_config,
+ &example_module
+ );
+}
+
+/*
+ * Likewise for our configuration record for the specified request.
+ */
+static example_config *our_rconfig
+ (request_rec *r) {
+
+ return (example_config *) get_module_config
+ (
+ r->request_config,
+ &example_module
+ );
+}
+
+/*
+ * This routine sets up some module-wide cells if they haven't been already.
+ */
+static void setup_module_cells () {
+ /*
+ * If we haven't already allocated our module-private pool, do so now.
+ */
+ if (example_pool == NULL) {
+ example_pool = make_sub_pool (NULL);
+ };
+ /*
+ * Likewise for the table of routine/environment pairs we visit outside of
+ * request context.
+ */
+ if (static_calls_made == NULL) {
+ static_calls_made = make_table (example_pool, 16);
+ };
+}
+
+/*
+ * This routine is used to add a trace of a callback to the list. We're
+ * passed the server record (if available), the request record (if available),
+ * a pointer to our private configuration record (if available) for the
+ * environment to which the callback is supposed to apply, and some text. We
+ * turn this into a textual representation and add it to the tail of the list.
+ * The list can be displayed by the example_handler() routine.
+ *
+ * If the call occurs within a request context (i.e., we're passed a request
+ * record), we put the trace into the request pool and attach it to the
+ * request via the notes mechanism. Otherwise, the trace gets added
+ * to the static (non-request-specific) list.
+ *
+ * Note that the r->notes table is only for storing strings; if you need to
+ * maintain per-request data of any other type, you need to use another
+ * mechanism.
+ */
+
+#define TRACE_NOTE "example-trace"
+
+static void trace_add
+ (server_rec *s, request_rec *r, example_config *mconfig,
+ const char *note) {
+
+ char *sofar;
+ char *addon;
+ char *where;
+ pool *p;
+ char *trace_copy;
+ example_config
+ *rconfig;
+
+ /*
+ * Make sure our pools and tables are set up - we need 'em.
+ */
+ setup_module_cells ();
+ /*
+ * Now, if we're in request-context, we use the request pool.
+ */
+ if (r != NULL) {
+ p = r->pool;
+ if ((trace_copy = table_get (r->notes, TRACE_NOTE)) == NULL) {
+ trace_copy = "";
+ }
+ } else {
+ /*
+ * We're not in request context, so the trace gets attached to our
+ * module-wide pool. We do the create/destroy every time we're called
+ * in non-request context; this avoids leaking memory in some of
+ * the subsequent calls that allocate memory only once (such as the
+ * key formation below).
+ *
+ * Make a new sub-pool and copy any existing trace to it. Point the
+ * trace cell at the copied value.
+ */
+ p = make_sub_pool (example_pool);
+ if (trace != NULL) {
+ trace = pstrdup (p, trace);
+ }
+ /*
+ * Now, if we have a sub-pool from before, nuke it and replace with
+ * the one we just allocated.
+ */
+ if (example_subpool != NULL) {
+ destroy_pool (example_subpool);
+ }
+ example_subpool = p;
+ trace_copy = trace;
+ }
+ /*
+ * If we weren't passed a configuration record, we can't figure out to
+ * what location this call applies. This only happens for co-routines
+ * that don't operate in a particular directory or server context. If we
+ * got a valid record, extract the location (directory or server) to which
+ * it applies.
+ */
+ where = (mconfig != NULL) ? mconfig->loc : "nowhere";
+ where = (where != NULL) ? where : "";
+ /*
+ * Now, if we're not in request context, see if we've been called with
+ * this particular combination before. The table is allocated in the
+ * module's private pool, which doesn't get destroyed.
+ */
+ if (r == NULL) {
+ char *key;
+
+ key = pstrcat (p, note, ":", where, NULL);
+ if (table_get (static_calls_made, key) != NULL) {
+ /*
+ * Been here, done this.
+ */
+ return;
+ } else {
+ /*
+ * First time for this combination of routine and environment -
+ * log it so we don't do it again.
+ */
+ table_set (static_calls_made, key, "been here");
+ }
+ }
+ addon = pstrcat
+ (
+ p,
+ " <LI>\n",
+ " <DL>\n",
+ " <DT><SAMP>",
+ note,
+ "</SAMP>\n",
+ " </DT>\n",
+ " <DD><SAMP>[",
+ where,
+ "]</SAMP>\n",
+ " </DD>\n",
+ " </DL>\n",
+ " </LI>\n",
+ NULL
+ );
+ sofar = (trace_copy == NULL) ? "" : trace_copy;
+ trace_copy = pstrcat (p, sofar, addon, NULL);
+ if (r != NULL) {
+ table_set (r->notes, TRACE_NOTE, trace_copy);
+ } else {
+ trace = trace_copy;
+ }
+ /*
+ * You *could* uncomment the following if you wanted to see the calling
+ * sequence reported in the server's error_log, but beware - almost all of
+ * these co-routines are called for every single request, and the impact
+ * on the size (and readability) of the error_log is considerable.
+ */
+/*
+ if (s != NULL) {
+ log_printf (s, "mod_example: %s", note);
+ }
+ */
+}
+
+/*--------------------------------------------------------------------------*/
+/* We prototyped the various syntax for command handlers (routines that */
+/* are called when the configuration parser detects a directive declared */
+/* by our module) earlier. Now we actually declare a "real" routine that */
+/* will be invoked by the parser when our "real" directive is */
+/* encountered. */
+/* */
+/* If a command handler encounters a problem processing the directive, it */
+/* signals this fact by returning a non-NULL pointer to a string */
+/* describing the problem. */
+/* */
+/* The magic return value DECLINE_CMD is used to deal with directives */
+/* that might be declared by multiple modules. If the command handler */
+/* returns NULL, the directive was processed; if it returns DECLINE_CMD, */
+/* the next module (if any) that declares the directive is given a chance */
+/* at it. If it returns any other value, it's treated as the text of an */
+/* error message. */
+/*--------------------------------------------------------------------------*/
+/*
+ * Command handler for the NO_ARGS "Example" directive. All we do is mark the
+ * call in the trace log, and flag the applicability of the directive to the
+ * current location in that location's configuration record.
+ */
+static const char *cmd_example
+ (cmd_parms *cmd, void *mconfig) {
+
+ example_config
+ *cfg = (example_config *) mconfig;
+
+ /*
+ * "Example Wuz Here"
+ */
+ cfg->local = 1;
+ trace_add (cmd->server, NULL, cfg, "cmd_example()");
+ return NULL;
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* Now we declare our content handlers, which are invoked when the server */
+/* encounters a document which our module is supposed to have a chance to */
+/* see. (See mod_mime's SetHandler and AddHandler directives, and the */
+/* mod_info and mod_status examples, for more details.) */
+/* */
+/* Since content handlers are dumping data directly into the connexion */
+/* (using the r*() routines, such as rputs() and rprintf()) without */
+/* intervention by other parts of the server, they need to make */
+/* sure any accumulated HTTP headers are sent first. This is done by */
+/* calling send_http_header(). Otherwise, no header will be sent at all, */
+/* and the output sent to the client will actually be HTTP-uncompliant. */
+/*--------------------------------------------------------------------------*/
+/*
+ * Sample content handler. All this does is display the call list that has
+ * been built up so far.
+ *
+ * The return value instructs the caller concerning what happened and what to
+ * do next:
+ * OK ("we did our thing")
+ * DECLINED ("this isn't something with which we want to get involved")
+ * HTTP_mumble ("an error status should be reported")
+ */
+static int example_handler
+ (request_rec *r) {
+
+ example_config
+ *dcfg;
+ example_config
+ *rcfg;
+
+ dcfg = our_dconfig (r);
+ trace_add (r->server, r, dcfg, "example_handler()");
+ /*
+ * We're about to start sending content, so we need to force the HTTP
+ * headers to be sent at this point. Otherwise, no headers will be sent
+ * at all. We can set any we like first, of course. **NOTE** Here's
+ * where you set the "Content-type" header, and you do so by putting it in
+ * r->content_type, *not* r->headers_out("Content-type"). If you don't
+ * set it, it will be filled in with the server's default type (typically
+ * "text/plain").
+ *
+ * We also need to start a timer so the server can know if the connexion
+ * is broken.
+ */
+ r->content_type = "text/html";
+ soft_timeout ("send example call trace", r);
+ send_http_header (r);
+ /*
+ * If we're only supposed to send header information (HEAD request), we're
+ * already there.
+ */
+ if (r->header_only) {
+ kill_timeout (r);
+ return OK;
+ }
+
+ /*
+ * Now send our actual output. Since we tagged this as being
+ * "text/html", we need to embed any HTML.
+ */
+ rputs ("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n", r);
+ rputs ("<HTML>\n", r);
+ rputs (" <HEAD>\n", r);
+ rputs (" <TITLE>mod_example Module Content-Handler Output\n", r);
+ rputs (" </TITLE>\n", r);
+ rputs (" </HEAD>\n", r);
+ rputs (" <BODY>\n", r);
+ rputs (" <H1><SAMP>mod_example</SAMP> Module Content-Handler Output\n", r);
+ rputs (" </H1>\n", r);
+ rputs (" <P>\n", r);
+ rputs (" The format for the callback trace is:\n", r);
+ rputs (" </P>\n", r);
+ rputs (" <DL>\n", r);
+ rputs (" <DT><EM>n</EM>.<SAMP>&lt;routine-name&gt;", r);
+ rputs ("(&lt;routine-data&gt;)</SAMP>\n", r);
+ rputs (" </DT>\n", r);
+ rputs (" <DD><SAMP>[&lt;applies-to&gt;]</SAMP>\n", r);
+ rputs (" </DD>\n", r);
+ rputs (" </DL>\n", r);
+ rputs (" <P>\n", r);
+ rputs (" The <SAMP>&lt;routine-data&gt;</SAMP> is supplied by\n", r);
+ rputs (" the routine when it requests the trace,\n", r);
+ rputs (" and the <SAMP>&lt;applies-to&gt;</SAMP> is extracted\n", r);
+ rputs (" from the configuration record at the time of the trace.\n", r);
+ rputs (" <STRONG>SVR()</STRONG> indicates a server environment\n", r);
+ rputs (" (blank means the main or default server, otherwise it's\n", r);
+ rputs (" the name of the VirtualHost); <STRONG>DIR()</STRONG>\n", r);
+ rputs (" indicates a location in the URL or filesystem\n", r);
+ rputs (" namespace.\n", r);
+ rputs (" </P>\n", r);
+ rprintf
+ (
+ r,
+ " <H2>Static callbacks so far:</H2>\n <OL>\n%s </OL>\n",
+ trace
+ );
+ rprintf
+ (
+ r,
+ " <H2>Request-specific callbacks so far:</H2>\n <OL>\n%s </OL>\n",
+ table_get (r->notes, TRACE_NOTE)
+ );
+ rputs (" <H2>Environment for <EM>this</EM> call:</H2>\n", r);
+ rputs (" <UL>\n", r);
+ rprintf (r, " <LI>Applies-to: <SAMP>%s</SAMP>\n </LI>\n", dcfg->loc);
+ rprintf
+ (
+ r,
+ " <LI>\"Example\" directive declared here: %s\n </LI>\n",
+ (dcfg->local ? "YES" : "NO")
+ );
+ rprintf
+ (
+ r,
+ " <LI>\"Example\" inherited: %s\n </LI>\n",
+ (dcfg->congenital ? "YES" : "NO")
+ );
+ rputs (" </UL>\n", r);
+ rputs (" </BODY>\n", r);
+ rputs ("</HTML>\n", r);
+ /*
+ * We're all done, so cancel the timeout we set. Since this is probably
+ * the end of the request we *could* assume this would be done during
+ * post-processing - but it's possible that another handler might be
+ * called and inherit our outstanding timer. Not good; to each its own.
+ */
+ kill_timeout (r);
+ /*
+ * We did what we wanted to do, so tell the rest of the server we
+ * succeeded.
+ */
+ return OK;
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* Now let's declare routines for each of the callback phase in order. */
+/* (That's the order in which they're listed in the callback list, *not */
+/* the order in which the server calls them! See the command_rec */
+/* declaration near the bottom of this file.) Note that these may be */
+/* called for situations that don't relate primarily to our function - in */
+/* other words, the fixup handler shouldn't assume that the request has */
+/* to do with "example" stuff. */
+/* */
+/* With the exception of the content handler, all of our routines will be */
+/* called for each request, unless an earlier handler from another module */
+/* aborted the sequence. */
+/* */
+/* Handlers that are declared as "int" can return the following: */
+/* */
+/* OK Handler accepted the request and did its thing with it. */
+/* DECLINED Handler took no action. */
+/* HTTP_mumble Handler looked at request and found it wanting. */
+/* */
+/* What the server does after calling a module handler depends upon the */
+/* handler's return value. In all cases, if the handler returns */
+/* DECLINED, the server will continue to the next module with an handler */
+/* for the current phase. However, if the handler return a non-OK, */
+/* non-DECLINED status, the server aborts the request right there. If */
+/* the handler returns OK, the server's next action is phase-specific; */
+/* see the individual handler comments below for details. */
+/* */
+/*--------------------------------------------------------------------------*/
+/*
+ * This function is called during server initialisation. Any information
+ * that needs to be recorded must be in static cells, since there's no
+ * configuration record.
+ *
+ * There is no return value.
+ */
+
+/*
+ * All our module-initialiser does is add its trace to the log.
+ */
+static void example_init
+ (server_rec *s, pool *p) {
+
+ char *note;
+ char *sname = s->server_hostname;
+
+ /*
+ * Set up any module cells that ought to be initialised.
+ */
+ setup_module_cells ();
+ /*
+ * The arbitrary text we add to our trace entry indicates for which server
+ * we're being called.
+ */
+ sname = (sname != NULL) ? sname : "";
+ note = pstrcat (p, "example_init(", sname, ")", NULL);
+ trace_add (s, NULL, NULL, note);
+}
+
+/*
+ * This function gets called to create up a per-directory configuration
+ * record. This will be called for the "default" server environment, and for
+ * each directory for which the parser finds any of our directives applicable.
+ * If a directory doesn't have any of our directives involved (i.e., they
+ * aren't in the .htaccess file, or a <Location>, <Directory>, or related
+ * block), this routine will *not* be called - the configuration for the
+ * closest ancestor is used.
+ *
+ * The return value is a pointer to the created module-specific
+ * structure.
+ */
+static void *example_dir_create
+ (pool *p, char *dirspec) {
+
+ example_config
+ *cfg;
+ char *dname = dirspec;
+
+ /*
+ * Allocate the space for our record from the pool supplied.
+ */
+ cfg = (example_config *) pcalloc (p, sizeof(example_config));
+ /*
+ * Now fill in the defaults. If there are any `parent' configuration
+ * records, they'll get merged as part of a separate callback.
+ */
+ cfg->local = 0;
+ cfg->congenital = 0;
+ cfg->cmode = CONFIG_MODE_DIRECTORY;
+ /*
+ * Finally, add our trace to the callback list.
+ */
+ dname = (dname != NULL) ? dname : "";
+ cfg->loc = pstrcat (p, "DIR(", dname, ")", NULL);
+ trace_add (NULL, NULL, cfg, "example_dir_create()");
+ return (void *) cfg;
+}
+
+/*
+ * This function gets called to merge two per-directory configuration
+ * records. This is typically done to cope with things like .htaccess files
+ * or <Location> directives for directories that are beneath one for which a
+ * configuration record was already created. The routine has the
+ * responsibility of creating a new record and merging the contents of the
+ * other two into it appropriately. If the module doesn't declare a merge
+ * routine, the record for the closest ancestor location (that has one) is
+ * used exclusively.
+ *
+ * The routine MUST NOT modify any of its arguments!
+ *
+ * The return value is a pointer to the created module-specific structure
+ * containing the merged values.
+ */
+static void *example_dir_merge
+ (pool *p, void *parent_conf, void *newloc_conf) {
+
+ example_config
+ *merged_config =
+ (example_config *) pcalloc (p, sizeof(example_config));
+ example_config
+ *pconf = (example_config *) parent_conf;
+ example_config
+ *nconf = (example_config *) newloc_conf;
+ char *note;
+
+ /*
+ * Some things get copied directly from the more-specific record, rather
+ * than getting merged.
+ */
+ merged_config->local = nconf->local;
+ merged_config->loc = pstrdup (p, nconf->loc);
+ /*
+ * Others, like the setting of the `congenital' flag, get ORed in. The
+ * setting of that particular flag, for instance, is TRUE if it was ever
+ * true anywhere in the upstream configuration.
+ */
+ merged_config->congenital = (pconf->congenital | pconf->local);
+ /*
+ * If we're merging records for two different types of environment (server
+ * and directory), mark the new record appropriately. Otherwise, inherit
+ * the current value.
+ */
+ merged_config->cmode =
+ (pconf->cmode == nconf->cmode) ? pconf->cmode : CONFIG_MODE_COMBO;
+ /*
+ * Now just record our being called in the trace list. Include the
+ * locations we were asked to merge.
+ */
+ note = pstrcat
+ (
+ p,
+ "example_dir_merge(\"",
+ pconf->loc,
+ "\",\"",
+ nconf->loc,
+ "\")",
+ NULL
+ );
+ trace_add (NULL, NULL, merged_config, note);
+ return (void *) merged_config;
+}
+
+/*
+ * This function gets called to create a per-server configuration
+ * record. It will always be called for the "default" server.
+ *
+ * The return value is a pointer to the created module-specific
+ * structure.
+ */
+static void *example_server_create
+ (pool *p, server_rec *s) {
+
+ example_config
+ *cfg;
+ char *sname = s->server_hostname;
+
+ /*
+ * As with the example_dir_create() reoutine, we allocate and fill in an
+ * empty record.
+ */
+ cfg = (example_config *) pcalloc (p, sizeof(example_config));
+ cfg->local = 0;
+ cfg->congenital = 0;
+ cfg->cmode = CONFIG_MODE_SERVER;
+ /*
+ * Note that we were called in the trace list.
+ */
+ sname = (sname != NULL) ? sname : "";
+ cfg->loc = pstrcat (p, "SVR(", sname, ")", NULL);
+ trace_add (s, NULL, cfg, "example_server_create()");
+ return (void *) cfg;
+}
+
+/*
+ * This function gets called to merge two per-server configuration
+ * records. This is typically done to cope with things like virtual hosts and
+ * the default server configuration The routine has the responsibility of
+ * creating a new record and merging the contents of the other two into it
+ * appropriately. If the module doesn't declare a merge routine, the more
+ * specific existing record is used exclusively.
+ *
+ * The routine MUST NOT modify any of its arguments!
+ *
+ * The return value is a pointer to the created module-specific structure
+ * containing the merged values.
+ */
+static void *example_server_merge
+ (pool *p, void *server1_conf, void *server2_conf) {
+
+ example_config
+ *merged_config =
+ (example_config *) pcalloc (p, sizeof(example_config));
+ example_config
+ *s1conf = (example_config *) server1_conf;
+ example_config
+ *s2conf = (example_config *) server2_conf;
+ char *note;
+
+ /*
+ * Our inheritance rules are our own, and part of our module's semantics.
+ * Basically, just note whence we came.
+ */
+ merged_config->cmode =
+ (s1conf->cmode == s2conf->cmode) ? s1conf->cmode : CONFIG_MODE_COMBO;
+ merged_config->local = s2conf->local;
+ merged_config->congenital = (s1conf->congenital | s1conf->local);
+ merged_config->loc = pstrdup (p, s2conf->loc);
+ /*
+ * Trace our call, including what we were asked to merge.
+ */
+ note = pstrcat
+ (
+ p,
+ "example_server_merge(\"",
+ s1conf->loc,
+ "\",\"",
+ s2conf->loc,
+ "\")",
+ NULL
+ );
+ trace_add (NULL, NULL, merged_config, note);
+ return (void *) merged_config;
+}
+
+/*
+ * This routine gives our module an opportunity to translate the URI into an
+ * actual filename. If we don't do anything special, the server's default
+ * rules (Alias directives and the like) will continue to be followed.
+ *
+ * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no
+ * further modules are called for this phase.
+ */
+static int example_xlate
+ (request_rec *r) {
+
+ example_config
+ *cfg;
+
+ cfg = our_dconfig (r);
+ /*
+ * We don't actually *do* anything here, except note the fact that we were
+ * called.
+ */
+ trace_add (r->server, r, cfg, "example_xlate()");
+ return DECLINED;
+}
+
+/*
+ * This routine is called to check the authentication information sent with
+ * the request (such as looking up the user in a database and verifying that
+ * the [encrypted] password sent matches the one in the database).
+ *
+ * The return value is OK, DECLINED, or some HTTP_mumble error (typically
+ * HTTP_UNAUTHORIZED). If we return OK, no other modules are given a chance
+ * at the request during this phase.
+ */
+static int example_ckuser
+ (request_rec *r) {
+
+ example_config
+ *cfg;
+
+ cfg = our_dconfig (r);
+ /*
+ * Don't do anything except log the call.
+ */
+ trace_add (r->server, r, cfg, "example_ckuser()");
+ return DECLINED;
+}
+
+/*
+ * This routine is called to check to see if the resource being requested
+ * requires authorisation.
+ *
+ * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no
+ * other modules are called during this phase.
+ *
+ * If *all* modules return DECLINED, the request is aborted with a server
+ * error.
+ */
+static int example_ckauth
+ (request_rec *r) {
+
+ example_config
+ *cfg;
+
+ cfg = our_dconfig (r);
+ /*
+ * Log the call and return OK, or access will be denied (even though we
+ * didn't actually do anything).
+ */
+ trace_add (r->server, r, cfg, "example_ckauth()");
+ return OK;
+}
+
+/*
+ * This routine is called to check for any module-specific restrictions placed
+ * upon the requested resource. (See the mod_access module for an example.)
+ *
+ * The return value is OK, DECLINED, or HTTP_mumble. All modules with an
+ * handler for this phase are called regardless of whether their predecessors
+ * return OK or DECLINED. The first one to return any other status, however,
+ * will abort the sequence (and the request) as usual.
+ */
+static int example_ckaccess
+ (request_rec *r) {
+
+ example_config
+ *cfg;
+
+ cfg = our_dconfig (r);
+ trace_add (r->server, r, cfg, "example_ckaccess()");
+ return OK;
+}
+
+/*
+ * This routine is called to determine and/or set the various document type
+ * information bits, like Content-type (via r->content_type), language, et
+ * cetera.
+ *
+ * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no
+ * further modules are given a chance at the request for this phase.
+ */
+static int example_typer
+ (request_rec *r) {
+
+ example_config
+ *cfg;
+
+ cfg = our_dconfig (r);
+ /*
+ * Log the call, but don't do anything else - and report truthfully that
+ * we didn't do anything.
+ */
+ trace_add (r->server, r, cfg, "example_typer()");
+ return DECLINED;
+}
+
+/*
+ * This routine is called to perform any module-specific fixing of header
+ * fields, et cetera. It is invoked just before any content-handler.
+ *
+ * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, the
+ * server will still call any remaining modules with an handler for this
+ * phase.
+ */
+static int example_fixer
+ (request_rec *r) {
+
+ example_config
+ *cfg;
+
+ cfg = our_dconfig (r);
+ /*
+ * Log the call and exit.
+ */
+ trace_add (r->server, r, cfg, "example_fixer()");
+ return OK;
+}
+
+/*
+ * This routine is called to perform any module-specific logging activities
+ * over and above the normal server things.
+ *
+ * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, any
+ * remaining modules with an handler for this phase will still be called.
+ */
+static int example_logger
+ (request_rec *r) {
+
+ example_config
+ *cfg;
+
+ cfg = our_dconfig (r);
+ trace_add (r->server, r, cfg, "example_logger()");
+ return DECLINED;
+}
+
+/*
+ * This routine is called to give the module a chance to look at the request
+ * headers and take any appropriate specific actions early in the processing
+ * sequence.
+ *
+ * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, any
+ * remaining modules with handlers for this phase will still be called.
+ */
+static int example_hparser
+ (request_rec *r) {
+
+ example_config
+ *cfg;
+
+ cfg = our_dconfig (r);
+ trace_add (r->server, r, cfg, "example_hparser()");
+ return DECLINED;
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* All of the routines have been declared now. Here's the list of */
+/* directives specific to our module, and information about where they */
+/* may appear and how the command parser should pass them to us for */
+/* processing. Note that care must be taken to ensure that there are NO */
+/* collisions of directive names between modules. */
+/* */
+/*--------------------------------------------------------------------------*/
+/*
+ * List of directives specific to our module.
+ */
+command_rec example_commands[] = {
+ {
+ "Example", /* directive name */
+ cmd_example, /* action routine for directive */
+ NULL, /* argument to include in call */
+ OR_OPTIONS, /* where available */
+ NO_ARGS, /* arguments */
+ "Example directive - no arguments"
+ /* directive description */
+ },
+ { NULL }
+};
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* Now the list of content handlers available from this module. */
+/* */
+/*--------------------------------------------------------------------------*/
+/*
+ * List of content handlers our module supplies. Each handler is defined by
+ * two parts: a name by which it can be referenced (such as by
+ * {Add,Set}Handler), and the actual routine name. The list is terminated by
+ * a NULL block, since it can be of variable length.
+ *
+ * Note that content-handlers are invoked on a most-specific to least-specific
+ * basis; that is, a handler that is declared for "text/plain" will be
+ * invoked before one that was declared for "text / *". Note also that
+ * if a content-handler returns anything except DECLINED, no other
+ * content-handlers will be called.
+ */
+handler_rec example_handlers[] = {
+ { "example-handler", example_handler },
+ { NULL }
+};
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* Finally, the list of callback routines and data structures that */
+/* provide the hooks into our module from the other parts of the server. */
+/* */
+/*--------------------------------------------------------------------------*/
+/*
+ * Module definition for configuration. If a particular callback is not
+ * needed, replace its routine name below with the word NULL.
+ *
+ * The number in brackets indicates the order in which the routine is called
+ * during request processing. Note that not all routines are necessarily
+ * called (such as if a resource doesn't have access restrictions).
+ */
+module example_module = {
+ STANDARD_MODULE_STUFF,
+ example_init, /* initializer */
+ example_dir_create, /* per-directory config creater */
+ example_dir_merge, /* dir config merger - default is to override */
+ example_server_create, /* server config creator */
+ example_server_merge, /* server config merger */
+ example_commands, /* command table */
+ example_handlers, /* [6] list of handlers */
+ example_xlate, /* [1] filename-to-URI translation */
+ example_ckuser, /* [4] check/validate HTTP user_id */
+ example_ckauth, /* [5] check HTTP user_id is valid *here* */
+ example_ckaccess, /* [3] check access by host address, etc. */
+ example_typer, /* [6] MIME type checker/setter */
+ example_fixer, /* [7] fixups */
+ example_logger, /* [9] logger */
+ example_hparser /* [2] header parser */
+};
diff --git a/usr.sbin/httpd/src/modules/proxy/Makefile b/usr.sbin/httpd/src/modules/proxy/Makefile
new file mode 100644
index 00000000000..102dea2a25f
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/proxy/Makefile
@@ -0,0 +1,89 @@
+# ====================================================================
+# Copyright (c) 1995-1997 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/>.
+#
+# Makefile for the Apache proxy library
+#
+
+SHELL = /bin/sh
+
+INCDIR = ../..
+
+LIB=libproxy.a
+
+# AUX_CFLAGS comes from higher level Makefile
+CFLAGS=-I$(INCDIR) $(AUX_CFLAGS)
+
+# Internal stuff, should not need changing.
+PROXYSRC=mod_proxy.c proxy_cache.c proxy_connect.c proxy_ftp.c proxy_http.c \
+proxy_util.c
+
+OBJS=$(PROXYSRC:.c=.o)
+
+default: force $(LIB)
+
+.c.a:
+ $(MAKE) $(CFLAGS) $<
+
+force:
+ rm -f $(LIB)
+
+$(LIB): $(OBJS)
+ ar crv $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ rm -f *.o libproxy.a
+
+# dependencies
+$(OBJS): mod_proxy.h $(INCDIR)/httpd.h $(INCDIR)/http_config.h $(INCDIR)/http_protocol.h
+proxy_cache.o proxy_connect.o proxy_ftp.o proxy_http.o proxy_util.o: $(INCDIR)/http_main.h
+proxy_cache.o proxy_connect.o proxy_http.o: $(INCDIR)/http_log.h
+proxy_cache.o proxy_http.o: $(INCDIR)/util_date.h
+proxy_cache.o proxy_util.o: $(INCDIR)/md5.h
diff --git a/usr.sbin/httpd/src/modules/proxy/mod_proxy.c b/usr.sbin/httpd/src/modules/proxy/mod_proxy.c
new file mode 100644
index 00000000000..ce8dc88af15
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/proxy/mod_proxy.c
@@ -0,0 +1,554 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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 "mod_proxy.h"
+
+/* Some WWW schemes and their default ports; this is basically /etc/services */
+/* This will become global when the protocol abstraction comes */
+static struct proxy_services defports[]={
+ { "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 */
+};
+
+/*
+ * A Web proxy module. Stages:
+ *
+ * translate_name: set filename to proxy:<URL>
+ * type_checker: set type to PROXY_MAGIC_TYPE if filename begins proxy:
+ * fix_ups: convert the URL stored in the filename to the
+ * canonical form.
+ * handler: handle proxy requests
+ */
+
+/* -------------------------------------------------------------- */
+/* Translate the URL into a 'filename' */
+
+static int
+alias_match(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 proxy_http_canon(r, url+5, "http", DEFAULT_PORT);
+ else if (strncmp(url, "ftp:", 4) == 0)
+ return proxy_ftp_canon(r, url+4);
+ else return OK; /* otherwise; we've done the best we can */
+}
+
+/* -------------------------------------------------------------- */
+/* Invoke handler */
+
+static int
+proxy_handler(request_rec *r)
+{
+ char *url, *scheme, *p;
+ void *sconf = r->server->module_config;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)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;
+
+ if ((rc = setup_client_block(r, REQUEST_CHUNKED_ERROR)))
+ return rc;
+
+ url = r->filename + 6;
+ p = strchr(url, ':');
+ if (p == NULL) return BAD_REQUEST;
+
+ rc = proxy_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 && strcasecmp(scheme, ents[i].scheme) == 0) ||
+ (p != NULL &&
+ strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0))
+ {
+/* we only know how to handle communication to a proxy via http */
+ if (strcasecmp(ents[i].protocol, "http") == 0)
+ rc = proxy_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 proxy_connect_handler(r, cr, url);
+ if (strcasecmp(scheme, "http") == 0)
+ return proxy_http_handler(r, cr, url, NULL, 0);
+ if (strcasecmp(scheme, "ftp") == 0)
+ return proxy_ftp_handler(r, cr, url);
+ else return NOT_IMPLEMENTED;
+}
+
+/* -------------------------------------------------------------- */
+/* Setup configurable data */
+
+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->noproxies = make_array(p, 10, sizeof(struct noproxy_entry));
+ 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 const char *
+add_proxy(cmd_parms *cmd, void *dummy, char *f, char *r)
+{
+ server_rec *s = cmd->server;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)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 (strcasecmp(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 const char *
+add_pass(cmd_parms *cmd, void *dummy, char *f, char *r)
+{
+ server_rec *s = cmd->server;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)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 const char *
+set_proxy_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 noproxy_entry *new;
+ struct noproxy_entry *list=(struct noproxy_entry*)conf->noproxies->elts;
+ struct hostent hp;
+ int found = 0;
+ int i;
+
+ /* Don't duplicate entries */
+ for (i=0; i < conf->noproxies->nelts; i++)
+ {
+ if (strcmp(arg, list[i].name) == 0)
+ found = 1;
+ }
+
+ if (!found)
+ {
+ new = push_array (conf->noproxies);
+ new->name = arg;
+ /* Don't do name lookups on things that aren't dotted */
+ if (strchr(arg, '.') != NULL && proxy_host2addr(new->name, &hp) == NULL)
+ memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
+ else
+ new->addr.s_addr = 0;
+ }
+ return NULL;
+}
+
+static const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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;
+ struct hostent hp;
+ 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;
+ /* Don't do name lookups on things that aren't dotted */
+ if (strchr(arg, '.') != NULL && proxy_host2addr(new->name, &hp) == NULL)
+ memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
+ else
+ new->addr.s_addr= 0;
+ }
+ return NULL;
+}
+
+static handler_rec proxy_handlers[] = {
+{ "proxy-server", proxy_handler },
+{ 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"},
+{ "ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, ITERATE,
+ "A list of names, hosts or domains to which the proxy will not connect" },
+{ "CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1,
+ "The directory to store cache files"},
+{ "CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1,
+ "The maximum disk space used by the cache in Kb"},
+{ "CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1,
+ "The maximum time in hours to cache a document"},
+{ "CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1,
+ "The default time in hours to cache a document"},
+{ "CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1,
+ "The factor used to estimate Expires date from LastModified date"},
+{ "CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1,
+ "The interval between garbage collections, in hours"},
+{ "CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF, TAKE1,
+ "The number of levels of subdirectories in the cache" },
+{ "CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF, TAKE1,
+ "The number of characters in subdirectory names" },
+{ "NoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE,
+ "A list of names, hosts or domains for which caching is *not* provided" },
+{ 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 */
+ NULL /* header parser */
+};
+
diff --git a/usr.sbin/httpd/src/modules/proxy/mod_proxy.h b/usr.sbin/httpd/src/modules/proxy/mod_proxy.h
new file mode 100644
index 00000000000..3287fb2a86a
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/proxy/mod_proxy.h
@@ -0,0 +1,273 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/>.
+ *
+ */
+
+/*
+ * Main include file for the Apache proxy
+ */
+
+/*
+
+Note that the Explain() stuff is not yet complete.
+Also note numerous FIXMEs and CHECKMEs which should be eliminated.
+
+If TESTING is set, then garbage collection doesn't delete ... probably a good
+idea when hacking.
+
+This code is still experimental!
+
+Things to do:
+
+1. Make it garbage collect in the background, not while someone is waiting for
+a response!
+
+2. Check the logic thoroughly.
+
+3. Empty directories are only removed the next time round (but this does avoid
+two passes). Consider doing them the first time round.
+
+Ben Laurie <ben@algroup.co.uk> 30 Mar 96
+
+More things to do:
+
+0. Code cleanup (ongoing)
+
+1. add 230 response output for ftp now that it works
+
+2. Make the ftp proxy transparent, also same with (future) gopher & wais
+
+3. Use protocol handler struct a la Apache module handlers (Dirk van Gulik)
+
+4. Use a cache expiry database for more efficient GC (Jeremy Wohl)
+
+5. Bulletproof GC against SIGALRM
+
+Chuck Murcko <chuck@topsail.org> 15 April 1997
+
+*/
+
+#define TESTING 0
+#undef EXPLAIN
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_protocol.h"
+
+#include "explain.h"
+
+DEF_Explain
+
+
+extern module proxy_module;
+
+
+/* for proxy_canonenc() */
+enum enctype { enc_path, enc_search, enc_user, enc_fpath, enc_parm };
+
+#define HDR_APP (0) /* append header, for proxy_add_header() */
+#define HDR_REP (1) /* replace header, for proxy_add_header() */
+
+/* number of characters in the hash */
+#define HASH_LEN (22*2)
+
+#define SEC_ONE_DAY 86400 /* one day, in seconds */
+#define SEC_ONE_HR 3600 /* one hour, in seconds */
+
+#define DEFAULT_FTP_DATA_PORT 20
+#define DEFAULT_FTP_PORT 21
+#define DEFAULT_GOPHER_PORT 70
+#define DEFAULT_NNTP_PORT 119
+#define DEFAULT_WAIS_PORT 210
+#define DEFAULT_HTTPS_PORT 443
+#define DEFAULT_SNEWS_PORT 563
+#define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */
+
+/* Some WWW schemes and their default ports; this is basically /etc/services */
+struct proxy_services
+{
+ const char *scheme;
+ int port;
+};
+
+/* static information about a remote proxy */
+struct proxy_remote
+{
+ const char *scheme; /* the schemes handled by this proxy, or '*' */
+ const char *protocol; /* the scheme used to talk to this proxy */
+ const char *hostname; /* the hostname of this proxy */
+ int port; /* the port for this proxy */
+};
+
+struct proxy_alias {
+ char *real;
+ char *fake;
+};
+
+struct noproxy_entry {
+ char *name;
+ struct in_addr addr;
+};
+
+struct nocache_entry {
+ char *name;
+ struct in_addr addr;
+};
+
+#define DEFAULT_CACHE_SPACE 5
+#define DEFAULT_CACHE_MAXEXPIRE SEC_ONE_DAY
+#define DEFAULT_CACHE_EXPIRE SEC_ONE_HR
+#define DEFAULT_CACHE_LMFACTOR (0.1)
+
+/* 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 *noproxies;
+ array_header *nocaches;
+ int req; /* true if proxy requests are enabled */
+} proxy_server_conf;
+
+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 */
+};
+
+/* Function prototypes */
+
+/* proxy_cache.c */
+
+void proxy_cache_tidy(struct cache_req *c);
+int proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf,
+ struct cache_req **cr);
+int proxy_cache_update(struct cache_req *c, array_header *resp_hdrs,
+ const int is_HTTP1, int nocache);
+void proxy_garbage_coll(request_rec *r);
+
+/* proxy_connect.c */
+
+int proxy_connect_handler(request_rec *r, struct cache_req *c, char *url);
+
+/* proxy_ftp.c */
+
+int proxy_ftp_canon(request_rec *r, char *url);
+int proxy_ftp_handler(request_rec *r, struct cache_req *c, char *url);
+
+/* proxy_http.c */
+
+int proxy_http_canon(request_rec *r, char *url, const char *scheme,
+ int def_port);
+int proxy_http_handler(request_rec *r, struct cache_req *c, char *url,
+ const char *proxyhost, int proxyport);
+
+/* proxy_util.c */
+
+int proxy_hex2c(const char *x);
+void proxy_c2hex(int ch, char *x);
+char *proxy_canonenc(pool *p, const char *x, int len, enum enctype t,
+ int isenc);
+char *proxy_canon_netloc(pool *pool, char **const urlp, char **userp,
+ char **passwordp, char **hostp, int *port);
+char *proxy_date_canon(pool *p, char *x);
+array_header *proxy_read_headers(pool *pool, char *buffer, int size, BUFF *f);
+long int proxy_send_fb(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c);
+struct hdr_entry *proxy_get_header(array_header *hdrs_arr, const char *name);
+struct hdr_entry *proxy_add_header(array_header *hdrs_arr, char *field,
+ char *value, int rep);
+void proxy_del_header(array_header *hdrs_arr, const char *field);
+void proxy_send_headers(BUFF *fp, const char *respline, array_header *hdrs_arr);
+int proxy_liststr(const char *list, const char *val);
+void proxy_hash(const char *it, char *val,int ndepth,int nlength);
+int proxy_hex2sec(const char *x);
+void proxy_sec2hex(int t, char *y);
+void proxy_log_uerror(const char *routine, const char *file, const char *err,
+ server_rec *s);
+BUFF *proxy_cache_error(struct cache_req *r);
+int proxyerror(request_rec *r, const char *message);
+const char *proxy_host2addr(const char *host, struct hostent *reqhp);
+int proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r);
+
diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_cache.c b/usr.sbin/httpd/src/modules/proxy/proxy_cache.c
new file mode 100644
index 00000000000..56f0a95cd26
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/proxy/proxy_cache.c
@@ -0,0 +1,933 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/>.
+ *
+ */
+
+/* Cache and garbage collection routines for Apache proxy */
+
+#include "md5.h"
+
+#include "mod_proxy.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_date.h"
+#include <utime.h>
+
+#define abs(c) ((c) >= 0 ? (c) : -(c))
+
+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 filenamelen;
+
+static int sub_garbage_coll(request_rec *r,array_header *files,
+ const char *cachedir,const char *cachesubdir);
+
+void proxy_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, timefd;
+ static time_t lastcheck=BAD_DATE; /* static data!!! */
+
+ cachedir = conf->root;
+ cachesize = conf->space;
+ every = conf->gcinterval;
+
+ if (cachedir == NULL || every == -1) return;
+ now = time(NULL);
+ if (now != -1 && lastcheck != BAD_DATE && now < lastcheck + every) return;
+
+ block_alarms(); /* avoid SIGALRM on big cache cleanup */
+
+ filenamelen = strlen(cachedir) + HASH_LEN + 2;
+ filename = palloc(r->pool, filenamelen);
+ ap_snprintf(filename, filenamelen, "%s/.time", cachedir);
+
+ if (stat(filename, &buf) == -1) /* does not exist */
+ {
+ if (errno != ENOENT)
+ {
+ proxy_log_uerror("stat", filename, NULL, r->server);
+ unblock_alarms();
+ return;
+ }
+ if ((timefd = creat(filename, 0666)) == -1)
+ {
+ if (errno != EEXIST)
+ proxy_log_uerror("creat", filename, NULL, r->server);
+ else
+ lastcheck = abs(now); /* someone else got in there */
+ unblock_alarms();
+ return;
+ }
+ close(timefd);
+ } else
+ {
+ lastcheck = buf.st_mtime; /* save the time */
+ if (now < lastcheck + every)
+ {
+ unblock_alarms();
+ return;
+ }
+ if (utime(filename, NULL) == -1)
+ proxy_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)
+ {
+ unblock_alarms();
+ 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];
+ ap_snprintf(filename, filenamelen, "%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)
+ proxy_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;
+
+ ap_snprintf(cachedir, sizeof(cachedir), "%s%s",cachebasedir,cachesubdir);
+ Explain1("GC Examining directory %s",cachedir);
+ dir = opendir(cachedir);
+ if (dir == NULL)
+ {
+ proxy_log_uerror("opendir", cachedir, NULL, r->server);
+ return 0;
+ }
+
+ while ((ent = readdir(dir)) != NULL)
+ {
+ if (ent->d_name[0] == '.') continue;
+ ap_snprintf(filename, filenamelen, "%s%s", cachedir, ent->d_name);
+ Explain1("GC Examining file %s",filename);
+/* is it a temporary file? */
+ if (strncmp(ent->d_name, "tmp", 3) == 0)
+ {
+/* then stat it to see how old it is; delete temporary files > 1 day old */
+ if (stat(filename, &buf) == -1)
+ {
+ if (errno != ENOENT)
+ proxy_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) proxy_log_uerror("open", filename,NULL,
+ r->server);
+ continue;
+ }
+ if (fstat(fd, &buf) == -1)
+ {
+ proxy_log_uerror("fstat", filename, NULL, r->server);
+ close(fd);
+ continue;
+ }
+ if(S_ISDIR(buf.st_mode))
+ {
+ char newcachedir[HUGE_STRING_LEN];
+ close(fd);
+ ap_snprintf(newcachedir, sizeof(newcachedir),
+ "%s%s/",cachesubdir,ent->d_name);
+ if(!sub_garbage_coll(r,files,cachebasedir,newcachedir))
+ {
+ ap_snprintf(newcachedir, sizeof(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)
+ {
+ proxy_log_uerror("read", filename, NULL, r->server);
+ close(fd);
+ continue;
+ }
+ close(fd);
+ line[i] = '\0';
+ expire = proxy_hex2sec(line+18);
+ if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&&") ||
+ expire == BAD_DATE)
+ {
+ /* 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;
+ ap_snprintf(fent->file, sizeof(fent->file), "%s%s", cachesubdir,
+ 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 = proxy_hex2sec(urlbuff);
+ c->lmod = proxy_hex2sec(urlbuff+9);
+ c->expire = proxy_hex2sec(urlbuff+18);
+ c->version = proxy_hex2sec(urlbuff+27);
+ c->len = proxy_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 = proxy_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 = proxy_get_header(c->hdrs, "Content-Length");
+ if (q == NULL)
+ {
+ p = palloc(pool, 15);
+ ap_snprintf(p, 15, "%u", c->len);
+ proxy_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
+ */
+int
+proxy_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 = BAD_DATE;
+ imstr = table_get(r->headers_in, "If-Modified-Since");
+ if (imstr != NULL)
+ {
+/* this may modify the value in the original table */
+ imstr = proxy_date_canon(r->pool, imstr);
+ c->ims = parseHTTPdate(imstr);
+ if (c->ims == BAD_DATE) /* bad or out of range date; remove it */
+ table_set(r->headers_in, "If-Modified-Since", NULL);
+ }
+
+/* find the filename for this cache entry */
+ proxy_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");
+ Explain5("Request for %s, pragma=%s, auth=%s, ims=%ld, imstr=%s",url,
+ pragma,auth,c->ims,imstr);
+ if (c->filename != NULL && r->method_number == M_GET &&
+ strlen(url) < 1024 && !proxy_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)
+ proxy_log_uerror("open", c->filename,
+ "proxy: error opening cache file", r->server);
+#ifdef EXPLAIN
+ else
+ Explain1("File %s not found",c->filename);
+#endif
+ }
+
+ if (cachefp != NULL)
+ {
+ i = rdcache(r->pool, cachefp, c);
+ if (i == -1)
+ proxy_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 != BAD_DATE && now < c->expire)
+ {
+ Explain0("Unexpired data available");
+/* check IMS */
+ if (c->lmod != BAD_DATE && c->ims != BAD_DATE && c->ims >= c->lmod)
+ {
+/* has the cached file changed since this request? */
+ if (c->date == BAD_DATE || 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;
+ if (!r->assbackwards) {
+ soft_timeout("proxy send headers", r);
+ proxy_send_headers(r->connection->client, c->resp_line, c->hdrs);
+ kill_timeout(r);
+ }
+ bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1;
+ if (!r->header_only) proxy_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 != BAD_DATE && !r->header_only)
+ {
+/*
+ * use the later of the one from the request and the last-modified date
+ * from the cache
+ */
+ if (c->ims == BAD_DATE || c->ims < c->lmod)
+ {
+ struct hdr_entry *q;
+
+ q = proxy_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
+ */
+int
+proxy_cache_update(struct cache_req *c, array_header *resp_hdrs,
+ const int is_HTTP1, 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 = proxy_get_header(resp_hdrs, "Expires");
+ if (expire != NULL) expc = parseHTTPdate(expire->value);
+ else expc = BAD_DATE;
+
+/*
+ * read the last-modified date; if the date is bad, then delete it
+ */
+ lmods = proxy_get_header(resp_hdrs, "Last-Modified");
+ if (lmods != NULL)
+ {
+ lmod = parseHTTPdate(lmods->value);
+ if (lmod == BAD_DATE)
+ {
+/* kill last modified date */
+ lmods->value = NULL;
+ lmods = NULL;
+ }
+ } else
+ lmod = BAD_DATE;
+
+/*
+ * 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 == BAD_DATE) ||
+ (r->status == 304 && c->fp == NULL) ||
+ (r->status == 200 && lmods == NULL && is_HTTP1) ||
+ 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 = proxy_get_header(resp_hdrs, "Date");
+ if (dates != NULL) date = parseHTTPdate(dates->value);
+ else date = BAD_DATE;
+
+ now = time(NULL);
+
+ if (date == BAD_DATE) /* No, or bad date */
+ {
+/* no date header! */
+/* add one; N.B. use the time _now_ rather than when we were checking the cache
+ */
+ date = abs(now);
+ p = gm_timestr_822(r->pool, now);
+ dates = proxy_add_header(resp_hdrs, "Date", p, HDR_REP);
+ Explain0("Added date header");
+ }
+
+/* check last-modified date */
+ if (lmod != BAD_DATE && 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 == BAD_DATE && 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 = proxy_get_header(c->hdrs, "Expires");
+ if (expire != NULL) expc = parseHTTPdate(expire->value);
+ }
+/* 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 == BAD_DATE)
+ {
+ if (lmod != BAD_DATE)
+ {
+ double x = (double)(date - lmod)*conf->cache.lmfactor;
+ double maxex=conf->cache.maxexpire;
+ if (x > maxex) x = maxex;
+ expc = abs(now) + (int)x;
+ } else
+ expc = abs(now) + conf->cache.defaultexpire;
+ Explain1("Expiry date calculated %ld",expc);
+ }
+
+/* get the content-length header */
+ clen = proxy_get_header(c->hdrs, "Content-Length");
+ if (clen == NULL) c->len = -1;
+ else c->len = atoi(clen->value);
+
+ proxy_sec2hex(date, buff);
+ buff[8] = ' ';
+ proxy_sec2hex(lmod, buff+9);
+ buff[17] = ' ';
+ proxy_sec2hex(expc, buff+18);
+ buff[26] = ' ';
+ proxy_sec2hex(c->version++, buff+27);
+ buff[35] = ' ';
+ proxy_sec2hex(c->len, buff+36);
+ buff[44] = '\n';
+ buff[45] = '\0';
+
+/* if file not modified */
+ if (r->status == 304)
+ {
+ if (c->ims != BAD_DATE && lmod != BAD_DATE && 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)
+ proxy_log_uerror("lseek", c->filename,
+ "proxy: error seeking on cache file",r->server);
+ else if (write(c->fp->fd, buff, 35) == -1)
+ proxy_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;
+ if (!r->assbackwards) {
+ soft_timeout("proxy send headers", r);
+ proxy_send_headers(r->connection->client, c->resp_line,
+ c->hdrs);
+ kill_timeout(r);
+ }
+ bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1;
+ if (!r->header_only) proxy_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)
+ proxy_log_uerror("lseek", c->filename,
+ "proxy: error seeking on cache file",r->server);
+ else if (write(c->fp->fd, buff, 35) == -1)
+ proxy_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;
+ proxy_sec2hex(0, buff+27);
+ buff[35] = ' ';
+
+/* open temporary file */
+#define TMPFILESTR "/tmpXXXXXX"
+ if (conf->cache.root == NULL)
+ return DECLINED;
+ c->tempfile=palloc(r->pool,strlen(conf->cache.root)+sizeof(TMPFILESTR));
+ strcpy(c->tempfile,conf->cache.root);
+ 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)
+ {
+ proxy_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)
+ {
+ proxy_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;
+}
+
+void
+proxy_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
+ if (c->req->connection->aborted) {
+ 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);
+ proxy_sec2hex(c->len, buff);
+ curpos = lseek(c->fp->fd, 36, SEEK_SET);
+ if (curpos == -1)
+ proxy_log_uerror("lseek", c->tempfile,
+ "proxy: error seeking on cache file", s);
+ else if (write(c->fp->fd, buff, 8) == -1)
+ proxy_log_uerror("write", c->tempfile,
+ "proxy: error updating cache file", s);
+ }
+
+ if (bflush(c->fp) == -1)
+ {
+ proxy_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)
+ {
+ proxy_log_uerror("close", c->tempfile,
+ "proxy: error closing cache file", s);
+ unlink(c->tempfile);
+ return;
+ }
+
+ if (unlink(c->filename) == -1 && errno != ENOENT)
+ {
+ proxy_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)
+ proxy_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)
+ proxy_log_uerror("rename", c->filename,
+ "proxy: error renaming cache file", s);
+}
+#else
+
+ if (link(c->tempfile, c->filename) == -1)
+ proxy_log_uerror("link", c->filename,
+ "proxy: error linking cache file", s);
+ }
+
+ if (unlink(c->tempfile) == -1)
+ proxy_log_uerror("unlink", c->tempfile,
+ "proxy: error deleting temp file",s);
+#endif
+
+}
+
diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_connect.c b/usr.sbin/httpd/src/modules/proxy/proxy_connect.c
new file mode 100644
index 00000000000..ba120271b66
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/proxy/proxy_connect.c
@@ -0,0 +1,228 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/>.
+ *
+ */
+
+/* CONNECT method SSL handling for Apache proxy */
+
+#include "mod_proxy.h"
+#include "http_log.h"
+#include "http_main.h"
+
+#ifdef HAVE_BSTRING_H
+#include <bstring.h> /* for IRIX, FD_SET calls bzero() */
+#endif
+
+/*
+ * 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.
+ */
+
+int
+proxy_connect_handler(request_rec *r, struct cache_req *c, char *url)
+{
+ struct sockaddr_in server;
+ struct in_addr destaddr;
+ struct hostent server_hp;
+ const char *host, *err;
+ char *p;
+ int port, sock;
+ char buffer[HUGE_STRING_LEN];
+ int nbytes, i, j;
+ fd_set fds;
+
+ void *sconf = r->server->module_config;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)get_module_config(sconf, &proxy_module);
+ struct noproxy_entry *npent=(struct noproxy_entry *)conf->noproxies->elts;
+
+ memset(&server, '\0', sizeof(server));
+ server.sin_family=AF_INET;
+
+ /* Break the URL into host:port pairs */
+
+ host = url;
+ p = strchr(url, ':');
+ if (p==NULL)
+ port = DEFAULT_HTTPS_PORT;
+ else
+ {
+ port = atoi(p+1);
+ *p='\0';
+ }
+
+/* check if ProxyBlock directive on this host */
+ destaddr.s_addr = inet_addr(host);
+ for (i=0; i < conf->noproxies->nelts; i++)
+ {
+ if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL)
+ || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*')
+ return proxyerror(r, "Connect to remote machine blocked");
+ }
+
+ switch (port)
+ {
+ case DEFAULT_HTTPS_PORT:
+ case DEFAULT_SNEWS_PORT:
+ break;
+ default:
+ return HTTP_SERVICE_UNAVAILABLE;
+ }
+
+ Explain2("CONNECT to %s on port %d", host, port);
+
+ server.sin_port = htons(port);
+ err = proxy_host2addr(host, &server_hp);
+ if (err != NULL)
+ return proxyerror(r, err); /* give up */
+
+ sock = socket(PF_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);
+
+ j = 0;
+ while (server_hp.h_addr_list[j] != NULL) {
+ memcpy(&server.sin_addr, server_hp.h_addr_list[j],
+ sizeof(struct in_addr));
+ i = proxy_doconnect(sock, &server, r);
+ if (i == 0)
+ break;
+ j++;
+ }
+ 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;
+}
+
diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_ftp.c b/usr.sbin/httpd/src/modules/proxy/proxy_ftp.c
new file mode 100644
index 00000000000..f460adf9a25
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/proxy/proxy_ftp.c
@@ -0,0 +1,1015 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/>.
+ *
+ */
+
+/* FTP routines for Apache proxy */
+
+#include "mod_proxy.h"
+#include "http_main.h"
+
+extern int find_ct(request_rec *r);
+
+/*
+ * 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 = proxy_hex2c(&x[i+1]);
+ i += 2;
+ }
+ x[j] = ch;
+ }
+ x[j] = '\0';
+ return j;
+}
+
+/*
+ * checks an encoded ftp string for bad characters, namely, CR, LF or
+ * non-ascii character
+ */
+static int
+ftp_check_string(const char *x)
+{
+ int i, ch;
+
+ for (i=0; x[i] != '\0'; i++)
+ {
+ ch = x[i];
+ if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2]))
+ {
+ ch = proxy_hex2c(&x[i+1]);
+ i += 2;
+ }
+ if (ch == '\015' || ch == '\012' || (ch & 0x80)) return 0;
+ }
+ return 1;
+}
+
+/*
+ * Canonicalise ftp URLs.
+ */
+int
+proxy_ftp_canon(request_rec *r, char *url)
+{
+ char *user, *password, *host, *path, *parms, *p, sport[7];
+ pool *pool=r->pool;
+ const char *err;
+ int port;
+
+ port = DEFAULT_FTP_PORT;
+ err = proxy_canon_netloc(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 = proxy_canonenc(pool, p, strlen(p), enc_parm, r->proxyreq);
+ if (parms == NULL) return BAD_REQUEST;
+ } else
+ parms = "";
+
+ path = proxy_canonenc(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 = proxy_canonenc(pool, r->args, strlen(r->args), enc_parm, 1);
+ if (p == NULL) return BAD_REQUEST;
+ parms = pstrcat(pool, parms, "?", p, NULL);
+ }
+ else
+ {
+ p = proxy_canonenc(pool, r->args, strlen(r->args), enc_fpath, 1);
+ if (p == NULL) return BAD_REQUEST;
+ path = pstrcat(pool, path, "?", p, NULL);
+ }
+ r->args = NULL;
+ }
+
+/* now, rebuild URL */
+
+ if (port != DEFAULT_FTP_PORT) ap_snprintf(sport, sizeof(sport), ":%d", port);
+ else sport[0] = '\0';
+
+ r->filename = pstrcat(pool, "proxy:ftp://", (user != NULL) ? user : "",
+ (password != NULL) ? ":" : "",
+ (password != NULL) ? password : "",
+ (user != NULL) ? "@" : "", host, sport, "/", path,
+ (parms[0] != '\0') ? ";" : "", parms, NULL);
+
+ return OK;
+}
+
+/*
+ * Returns the ftp status code;
+ * or -1 on I/O error, 0 on data error
+ */
+static int
+ftp_getrc(BUFF *f)
+{
+ int 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] != '-'))
+ status = 0;
+ else
+ status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
+
+ if (linebuff[len-1] != '\n')
+ {
+ i = bskiplf(f);
+ }
+
+/* skip continuation lines */
+ if (linebuff[3] == '-')
+ {
+ memcpy(buff, linebuff, 3);
+ buff[3] = ' ';
+ do
+ {
+ len = bgets(linebuff, 100, f);
+ if (len == -1) return -1;
+ if (linebuff[len-1] != '\n')
+ {
+ i = bskiplf(f);
+ }
+ } while (memcmp(linebuff, buff, 4) != 0);
+ }
+
+ return status;
+}
+
+static char *
+encode_space(request_rec *r, char *path)
+{
+ pool *pool=r->pool;
+ char *newpath;
+ int i, j, len;
+
+ len = strlen(path);
+ newpath = palloc(pool, 3 * len + 1);
+ for (i=0, j=0; i < len; i++, j++) {
+ if (path[i] != ' ')
+ newpath[j] = path[i];
+ else {
+ proxy_c2hex(' ', &newpath[j]);
+ j += 2;
+ }
+ }
+ newpath[j] = '\0';
+ return newpath;
+}
+
+static long int
+send_dir(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c, char *url)
+{
+ char buf[IOBUFSIZE];
+ char buf2[IOBUFSIZE];
+ char *filename;
+ char *tempurl;
+ char *newurlptr;
+ int searchidx = 0;
+ char *searchptr = NULL;
+ int firstfile = 1;
+ char urlptr[HUGE_STRING_LEN];
+ long total_bytes_sent;
+ register int n, o, w;
+ conn_rec *con = r->connection;
+
+ tempurl = pstrdup(r->pool, url);
+ if ((n = strcspn(tempurl, "@")) != strlen(tempurl)) /* hide user/passwd */
+ {
+ memmove(tempurl + (n - 5), tempurl, 6);
+ tempurl += n - 5; /* leave room for ftp:// */
+ }
+
+ n = decodeenc(tempurl);
+ ap_snprintf(buf, sizeof(buf), "<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>Directory %s</H1><HR><PRE>", tempurl, tempurl);
+ bwrite(con->client, buf, strlen(buf));
+ if (f2 != NULL) bwrite(f2, buf, strlen(buf));
+ total_bytes_sent=strlen(buf);
+ while(!con->aborted)
+ {
+ n = bgets(buf, IOBUFSIZE, f);
+ if (n == -1) /* input error */
+ {
+ if (f2 != NULL) f2 = proxy_cache_error(c);
+ break;
+ }
+ if (n == 0) break; /* EOF */
+ if(buf[0]=='l')
+ {
+ char *link;
+
+ link=strstr(buf, " -> ");
+ filename=link;
+ do filename--; while (filename[0]!=' ');
+ *(filename++)=0;
+ *(link++)=0;
+ ap_snprintf(urlptr, sizeof(urlptr), "%s%s%s",url,(url[strlen(url)-1]=='/' ? "" : "/"), filename);
+ ap_snprintf(buf2, sizeof(urlptr), "%s <A HREF=\"%s\">%s %s</A>\015\012", buf, urlptr, filename, link);
+ strncpy(buf, buf2, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ n=strlen(buf);
+ }
+ else if (strrchr(buf, ' ') && (buf[0]=='d' || buf[0]=='-' ||
+ buf[0]=='l' || isdigit(buf[0])) ) {
+ if(isdigit(buf[0])) { /* handle DOS dir */
+ searchptr = strchr(buf, '<');
+ if(searchptr != NULL)
+ *searchptr = '[';
+ searchptr = strchr(buf, '>');
+ if(searchptr != NULL)
+ *searchptr = ']';
+ }
+
+ filename=strrchr(buf, ' ');
+ *(filename++)=0;
+ filename[strlen(filename)-1]=0;
+
+ /* handle filenames with spaces in 'em */
+ if(!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
+ firstfile = 0;
+ searchidx = filename - buf;
+ }
+ else if (searchidx != 0 && buf[searchidx] != 0) {
+ *(--filename) = ' ';
+ buf[searchidx - 1] = 0;
+ filename = &buf[searchidx];
+ }
+
+ /* Special handling for '.' and '..' */
+ if (!strcmp(filename, "."))
+ {
+ ap_snprintf(urlptr, sizeof(urlptr), "%s",url);
+ ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\015\012", buf, urlptr, filename);
+ }
+ else if (!strcmp(filename, ".."))
+ {
+ char temp[200];
+ char newpath[200];
+ char *method, *host, *path, *newfile;
+
+ strncpy(temp, url, sizeof(temp)-1);
+ temp[sizeof(temp)-1] = '\0';
+ method=temp;
+
+ host=strchr(method,':');
+ if (host == NULL) host="";
+ else *(host++)=0;
+ host++; host++;
+
+ path=strchr(host,'/');
+ if (path == NULL) path="";
+ else *(path++)=0;
+
+ strncpy(newpath, path, sizeof(newpath)-1);
+ newpath[sizeof(newpath)-1] = '\0';
+ newfile=strrchr(newpath,'/');
+ if (newfile) *(newfile)=0;
+ else newpath[0]=0;
+
+ ap_snprintf(urlptr, sizeof(urlptr), "%s://%s/%s",method,host,newpath);
+ ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\015\012", buf, urlptr, filename);
+ }
+ else
+ {
+ ap_snprintf(urlptr, sizeof(urlptr), "%s%s%s",url,(url[strlen(url)-1]=='/' ? "" : "/"), filename);
+ newurlptr = encode_space(r, urlptr);
+ ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\015\012", buf, newurlptr, filename);
+ }
+ strncpy(buf, buf2, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ n=strlen(buf);
+ }
+
+ o=0;
+ total_bytes_sent += n;
+
+ if (f2 != NULL)
+ if (bwrite(f2, buf, n) != n) f2 = proxy_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;
+ }
+ }
+ bputs("</PRE><HR></BODY></HTML>\015\012", con->client);
+ if (f2 != NULL) {
+ bputs("</PRE><HR></BODY></HTML>\015\012", f2);
+ }
+ total_bytes_sent+=strlen(buf);
+ bflush(con->client);
+
+ return total_bytes_sent;
+}
+
+/*
+ * Handles direct access of ftp:// URLs
+ * Original (Non-PASV) version from
+ * Troy Morrison <spiffnet@zoom.com>
+ * PASV added by Chuck
+ */
+int
+proxy_ftp_handler(request_rec *r, struct cache_req *c, char *url)
+{
+ char *host, *path, *p, *user, *password, *parms;
+ const char *err;
+ int port, userlen, i, j, len, sock, dsock, rc, nocache;
+ int passlen = 0;
+ int csd = 0;
+ struct sockaddr_in server;
+ struct hostent server_hp;
+ struct hdr_entry *hdr;
+ struct in_addr destaddr;
+ array_header *resp_hdrs;
+ BUFF *f, *cache;
+ BUFF *data = NULL;
+ pool *pool=r->pool;
+ const int one=1;
+ 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 noproxy_entry *npent=(struct noproxy_entry *)conf->noproxies->elts;
+ struct nocache_entry *ncent=(struct nocache_entry *)conf->nocaches->elts;
+
+/* stuff for PASV mode */
+ unsigned int presult, h0, h1, h2, h3, p0, p1;
+ unsigned int paddr;
+ unsigned short pport;
+ struct sockaddr_in data_addr;
+ int pasvmode = 0;
+ char pasv[64];
+ char *pstr;
+
+/* we only support GET and HEAD */
+
+ if (r->method_number != M_GET) return NOT_IMPLEMENTED;
+
+/* We break the URL into host, port, path-search */
+
+ host = pstrdup(pool, url + 6);
+ port = DEFAULT_FTP_PORT;
+ path = strchr(host, '/');
+ if (path == NULL)
+ path = "";
+ else
+ *(path++) = '\0';
+
+ user = password = NULL;
+ nocache = 0;
+ 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 = "apache_proxy@";
+ passlen = strlen(password);
+ }
+
+ p = strchr(host, ':');
+ if (p != NULL)
+ {
+ *(p++) = '\0';
+ if (isdigit(*p))
+ port = atoi(p);
+ }
+
+/* check if ProxyBlock directive on this host */
+ destaddr.s_addr = inet_addr(host);
+ for (i=0; i < conf->noproxies->nelts; i++)
+ {
+ if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL)
+ || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*')
+ return proxyerror(r, "Connect to remote machine blocked");
+ }
+
+ Explain2("FTP: connect to %s:%d",host,port);
+
+ parms = strchr(path, ';');
+ if (parms != NULL) *(parms++) = '\0';
+
+ memset(&server, 0, sizeof(struct sockaddr_in));
+ server.sin_family = AF_INET;
+ server.sin_port = htons(port);
+ err = proxy_host2addr(host, &server_hp);
+ if (err != NULL) return proxyerror(r, err); /* give up */
+
+ sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == -1)
+ {
+ proxy_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)
+ {
+ proxy_log_uerror("setsockopt", NULL,
+ "proxy: error setting reuseaddr option", r->server);
+ pclosef(pool, sock);
+ return SERVER_ERROR;
+ }
+
+ j = 0;
+ while (server_hp.h_addr_list[j] != NULL) {
+ memcpy(&server.sin_addr, server_hp.h_addr_list[j],
+ sizeof(struct in_addr));
+ i = proxy_doconnect(sock, &server, r);
+ if (i == 0)
+ break;
+ j++;
+ }
+ 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);
+ Explain1("FTP: returned status %d", i);
+ if (i == -1) {
+ kill_timeout(r);
+ return proxyerror(r, "Error reading from remote server");
+ }
+ if (i != 220) {
+ kill_timeout(r);
+ return BAD_GATEWAY;
+ }
+
+ Explain0("FTP: connected.");
+
+ bputs("USER ", f);
+ bwrite(f, user, userlen);
+ bputs("\015\012", f);
+ bflush(f); /* capture any errors */
+ Explain1("FTP: USER %s",user);
+
+/* possible results; 230, 331, 332, 421, 500, 501, 530 */
+/* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */
+ i = ftp_getrc(f);
+ Explain1("FTP: returned status %d",i);
+ if (i == -1) {
+ kill_timeout(r);
+ return proxyerror(r, "Error sending to remote server");
+ }
+ if (i == 530) {
+ kill_timeout(r);
+ return proxyerror(r, "Not logged in");
+ }
+ if (i != 230 && i != 331) {
+ kill_timeout(r);
+ 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);
+ Explain1("FTP: PASS %s",password);
+/* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
+ i = ftp_getrc(f);
+ Explain1("FTP: returned status %d",i);
+ if (i == -1) {
+ kill_timeout(r);
+ return proxyerror(r, "Error sending to remote server");
+ }
+ if (i == 332) {
+ kill_timeout(r);
+ return proxyerror(r, "Need account for login");
+ }
+ if (i == 530) {
+ kill_timeout(r);
+ return proxyerror(r, "Not logged in");
+ }
+ if (i != 230 && i != 202) {
+ kill_timeout(r);
+ 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);
+ Explain1("FTP: CWD %s",path);
+/* responses: 250, 421, 500, 501, 502, 530, 550 */
+/* 1,3 error, 2 success, 4,5 failure */
+ i = ftp_getrc(f);
+ Explain1("FTP: returned status %d",i);
+ if (i == -1) {
+ kill_timeout(r);
+ return proxyerror(r, "Error sending to remote server");
+ }
+ if (i == 550) {
+ kill_timeout(r);
+ return NOT_FOUND;
+ }
+ if (i != 250) {
+ kill_timeout(r);
+ 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 = "";
+
+ /* changed to make binary transfers the default */
+
+ if (parms[0] != 'a')
+ {
+ /* set type to image */
+ /* TM - Added \015\012 to the end of TYPE I, otherwise it hangs the
+ connection */
+ bputs("TYPE I\015\012", f);
+ bflush(f);
+ Explain0("FTP: TYPE I");
+/* responses: 200, 421, 500, 501, 504, 530 */
+ i = ftp_getrc(f);
+ Explain1("FTP: returned status %d",i);
+ if (i == -1) {
+ kill_timeout(r);
+ return proxyerror(r, "Error sending to remote server");
+ }
+ if (i != 200 && i != 504) {
+ kill_timeout(r);
+ return BAD_GATEWAY;
+ }
+/* Allow not implemented */
+ if (i == 504)
+ parms[0] = '\0';
+ }
+
+/* try to set up PASV data connection first */
+ dsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (dsock == -1)
+ {
+ proxy_log_uerror("socket", NULL, "proxy: error creating PASV socket",
+ r->server);
+ pclosef(pool, sock);
+ kill_timeout(r);
+ return SERVER_ERROR;
+ }
+ note_cleanups_for_fd(pool, dsock);
+
+ bputs("PASV\015\012", f);
+ bflush(f);
+ Explain0("FTP: PASV command issued");
+/* possible results: 227, 421, 500, 501, 502, 530 */
+ i = bgets(pasv, sizeof(pasv), f);
+
+ if (i == -1)
+ {
+ proxy_log_uerror("command", NULL, "PASV: control connection is toast",
+ r->server);
+ pclosef(pool, dsock);
+ pclosef(pool, sock);
+ kill_timeout(r);
+ return SERVER_ERROR;
+ } else
+ {
+ pasv[i-1] = '\0';
+ pstr = strtok(pasv, " "); /* separate result code */
+ if (pstr != NULL)
+ {
+ presult = atoi(pstr);
+ pstr = strtok(NULL, "("); /* separate address & port params */
+ if (pstr != NULL)
+ pstr = strtok(NULL, ")");
+ }
+ else
+ presult = atoi(pasv);
+
+ Explain1("FTP: returned status %d", presult);
+
+ if (presult == 227 && pstr != NULL && (sscanf(pstr,
+ "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6))
+ {
+ /* pardon the parens, but it makes gcc happy */
+ paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0;
+ pport = (p1 << 8) + p0;
+ Explain5("FTP: contacting host %d.%d.%d.%d:%d",
+ h3, h2, h1, h0, pport);
+ data_addr.sin_family = AF_INET;
+ data_addr.sin_addr.s_addr = htonl(paddr);
+ data_addr.sin_port = htons(pport);
+ i = proxy_doconnect(dsock, &data_addr, r);
+
+ if (i == -1) {
+ kill_timeout(r);
+ return proxyerror(r, "Could not connect to remote machine");
+ }
+ else {
+ data = bcreate(pool, B_RDWR);
+ bpushfd(data, dsock, dsock);
+ pasvmode = 1;
+ }
+ } else
+ pclosef(pool, dsock); /* and try the regular way */
+ }
+
+ if (!pasvmode) /* set up data connection */
+ {
+ len = sizeof(struct sockaddr_in);
+ if (getsockname(sock, (struct sockaddr *)&server, &len) < 0)
+ {
+ proxy_log_uerror("getsockname", NULL,
+ "proxy: error getting socket address", r->server);
+ pclosef(pool, sock);
+ kill_timeout(r);
+ return SERVER_ERROR;
+ }
+
+ dsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (dsock == -1)
+ {
+ proxy_log_uerror("socket", NULL, "proxy: error creating socket",
+ r->server);
+ pclosef(pool, sock);
+ kill_timeout(r);
+ return SERVER_ERROR;
+ }
+ note_cleanups_for_fd(pool, dsock);
+
+ if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one,
+ sizeof(int)) == -1)
+ {
+ proxy_log_uerror("setsockopt", NULL,
+ "proxy: error setting reuseaddr option", r->server);
+ pclosef(pool, dsock);
+ pclosef(pool, sock);
+ kill_timeout(r);
+ return SERVER_ERROR;
+ }
+
+ if (bind(dsock, (struct sockaddr *)&server,
+ sizeof(struct sockaddr_in)) == -1)
+ {
+ char buff[22];
+
+ ap_snprintf(buff, sizeof(buff), "%s:%d", inet_ntoa(server.sin_addr), server.sin_port);
+ proxy_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);
+
+ /* TM - if len == 0 then it must be a directory (you can't RETR nothing) */
+
+ if(len==0)
+ {
+ parms="d";
+ } else
+ {
+ bputs("SIZE ", f);
+ bwrite(f, path, len);
+ bputs("\015\012", f);
+ bflush(f);
+ Explain1("FTP: SIZE %s",path);
+ i = ftp_getrc(f);
+ Explain1("FTP: returned status %d", i);
+ if (i != 500) /* Size command not recognized */
+ {
+ if (i==550) /* Not a regular file */
+ {
+ Explain0("FTP: SIZE shows this is a directory");
+ parms="d";
+ bputs("CWD ", f);
+ bwrite(f, path, len);
+ bputs("\015\012", f);
+ bflush(f);
+ Explain1("FTP: CWD %s",path);
+ i = ftp_getrc(f);
+ Explain1("FTP: returned status %d", i);
+ if (i == -1) {
+ kill_timeout(r);
+ return proxyerror(r, "Error sending to remote server");
+ }
+ if (i == 550) {
+ kill_timeout(r);
+ return NOT_FOUND;
+ }
+ if (i != 250) {
+ kill_timeout(r);
+ return BAD_GATEWAY;
+ }
+ path=""; len=0;
+ }
+ }
+ }
+
+ if (parms[0] == 'd')
+ {
+ if (len != 0) bputs("LIST ", f);
+ else bputs("LIST -lag", f);
+ Explain1("FTP: LIST %s",(len==0 ? "" : path));
+ }
+ else
+ {
+ bputs("RETR ", f);
+ Explain1("FTP: RETR %s",path);
+ }
+ 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);
+ Explain1("FTP: returned status %d",rc);
+ if (rc == -1) {
+ kill_timeout(r);
+ return proxyerror(r, "Error sending to remote server");
+ }
+ if (rc == 550)
+ {
+ Explain0("FTP: RETR failed, trying LIST instead");
+ parms="d";
+ bputs("CWD ", f);
+ bwrite(f, path, len);
+ bputs("\015\012", f);
+ bflush(f);
+ Explain1("FTP: CWD %s", path);
+ rc = ftp_getrc(f);
+ Explain1("FTP: returned status %d", rc);
+ if (rc == -1) {
+ kill_timeout(r);
+ return proxyerror(r, "Error sending to remote server");
+ }
+ if (rc == 550) {
+ kill_timeout(r);
+ return NOT_FOUND;
+ }
+ if (rc != 250) {
+ kill_timeout(r);
+ return BAD_GATEWAY;
+ }
+
+ bputs("LIST -lag\015\012", f);
+ bflush(f);
+ Explain0("FTP: LIST -lag");
+ rc = ftp_getrc(f);
+ Explain1("FTP: returned status %d", rc);
+ if (rc == -1) return proxyerror(r, "Error sending to remote server");
+ }
+ kill_timeout(r);
+ if (rc != 125 && rc != 150 && rc != 226 && rc != 250) return BAD_GATEWAY;
+
+ r->status = 200;
+ r->status_line = "200 OK";
+
+ resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry));
+ if (parms[0] == 'd')
+ proxy_add_header(resp_hdrs, "Content-Type", "text/html", HDR_REP);
+ else
+ {
+ find_ct(r);
+ if(r->content_type != NULL)
+ {
+ proxy_add_header(resp_hdrs, "Content-Type", r->content_type,
+ HDR_REP);
+ Explain1("FTP: Content-Type set to %s",r->content_type);
+ } else
+ {
+ proxy_add_header(resp_hdrs, "Content-Type", "text/plain", HDR_REP);
+ }
+ }
+
+/* check if NoCache directive on this host */
+ for (i=0; i < conf->nocaches->nelts; i++)
+ {
+ if ((ncent[i].name != NULL && strstr(host, ncent[i].name) != NULL)
+ || destaddr.s_addr == ncent[i].addr.s_addr || ncent[i].name[0] == '*')
+ nocache = 1;
+ }
+
+ i = proxy_cache_update(c, resp_hdrs, 0, nocache);
+
+ if (i != DECLINED)
+ {
+ pclosef(pool, dsock);
+ pclosef(pool, sock);
+ return i;
+ }
+ cache = c->fp;
+
+ if (!pasvmode) /* 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);
+ if (csd == -1)
+ {
+ proxy_log_uerror("accept", NULL,
+ "proxy: failed to accept data connection", r->server);
+ pclosef(pool, dsock);
+ pclosef(pool, sock);
+ kill_timeout(r);
+ proxy_cache_error(c);
+ return BAD_GATEWAY;
+ }
+ note_cleanups_for_fd(pool, csd);
+ data = bcreate(pool, B_RDWR);
+ bpushfd(data, csd, -1);
+ kill_timeout(r);
+ }
+
+ hard_timeout ("proxy receive", r);
+/* send response */
+/* write status line */
+ if (!r->assbackwards)
+ rvputs(r, "HTTP/1.0 ", r->status_line, "\015\012", NULL);
+ if (cache != NULL)
+ if (bvputs(cache, "HTTP/1.0 ", r->status_line, "\015\012",
+ NULL) == -1)
+ cache = proxy_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 = proxy_cache_error(c);
+ }
+
+ if (!r->assbackwards) rputs("\015\012", r);
+ if (cache != NULL)
+ if (bputs("\015\012", cache) == -1) cache = proxy_cache_error(c);
+
+ bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1;
+/* send body */
+ if (!r->header_only)
+ {
+ if (parms[0] != 'd') proxy_send_fb(data, r, cache, c);
+ else send_dir(data, r, cache, c, url);
+
+ if (rc == 125 || rc == 150) rc = ftp_getrc(f);
+ if (rc != 226 && rc != 250) proxy_cache_error(c);
+ }
+ else
+ {
+/* abort the transfer */
+ bputs("ABOR\015\012", f);
+ bflush(f);
+ if (!pasvmode)
+ pclosef(pool, csd);
+ Explain0("FTP: ABOR");
+/* responses: 225, 226, 421, 500, 501, 502 */
+ i = ftp_getrc(f);
+ Explain1("FTP: returned status %d",i);
+ }
+
+ kill_timeout(r);
+ proxy_cache_tidy(c);
+
+/* finish */
+ bputs("QUIT\015\012", f);
+ bflush(f);
+ Explain0("FTP: QUIT");
+/* responses: 221, 500 */
+
+ if (!pasvmode)
+ pclosef(pool, csd);
+ pclosef(pool, dsock);
+ pclosef(pool, sock);
+
+ proxy_garbage_coll(r);
+
+ return OK;
+}
+
diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_http.c b/usr.sbin/httpd/src/modules/proxy/proxy_http.c
new file mode 100644
index 00000000000..1144564ee57
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/proxy/proxy_http.c
@@ -0,0 +1,413 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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 routines for Apache proxy */
+
+#include "mod_proxy.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_date.h"
+
+/*
+ * Canonicalise http-like URLs.
+ * scheme is the scheme for the URL
+ * url is the URL starting with the first '/'
+ * def_port is the default port for this scheme.
+ */
+int
+proxy_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 = proxy_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 = proxy_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 = NULL;
+
+ if (port != def_port) ap_snprintf(sport, sizeof(sport), ":%d", port);
+ else sport[0] = '\0';
+
+ r->filename = pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/",
+ path, (search) ? "?" : "", (search) ? search : "", NULL);
+ return OK;
+}
+
+/* Clear all connection-based headers from the incoming headers table */
+static void clear_connection (table *headers)
+{
+ char *name;
+ char *next = table_get(headers, "Connection");
+
+ if (!next) return;
+
+ while (*next) {
+ name = next;
+ while (*next && !isspace(*next) && (*next != ',')) ++next;
+ while (*next && (isspace(*next) || (*next == ','))) {
+ *next = '\0';
+ ++next;
+ }
+ table_unset(headers, name);
+ }
+ table_unset(headers, "Connection");
+}
+
+/*
+ * This handles http:// URLs, and other URLs using a remote proxy over http
+ * If proxyhost is NULL, then contact the server directly, otherwise
+ * go via the proxy.
+ * Note that if a proxy is used, then URLs other than http: can be accessed,
+ * also, if we have trouble which is clearly specific to the proxy, then
+ * we return DECLINED so that we can try another proxy. (Or the direct
+ * route.)
+ */
+int
+proxy_http_handler(request_rec *r, struct cache_req *c, char *url,
+ const char *proxyhost, int proxyport)
+{
+ char *p;
+ const char *err, *desthost;
+ int i, j, sock, len, backasswards;
+ array_header *reqhdrs_arr, *resp_hdrs;
+ table_entry *reqhdrs;
+ struct sockaddr_in server;
+ struct in_addr destaddr;
+ struct hostent server_hp;
+ BUFF *f, *cache;
+ struct hdr_entry *hdr;
+ char buffer[HUGE_STRING_LEN];
+ pool *pool=r->pool;
+ const long int zero=0L;
+ int destport = 0;
+ char *destportstr = NULL;
+ char *urlptr = NULL;
+
+ void *sconf = r->server->module_config;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)get_module_config(sconf, &proxy_module);
+ struct noproxy_entry *npent=(struct noproxy_entry *)conf->noproxies->elts;
+ struct nocache_entry *ncent=(struct nocache_entry *)conf->nocaches->elts;
+ int nocache = 0;
+
+ memset(&server, '\0', sizeof(server));
+ server.sin_family = AF_INET;
+
+/* We break the URL into host, port, path-search */
+
+ urlptr = strstr(url,"://");
+ if (urlptr == NULL) return BAD_REQUEST;
+ urlptr += 3;
+ destport = DEFAULT_PORT;
+ p = strchr(urlptr, '/');
+ if (p == NULL)
+ {
+ desthost = pstrdup(pool, urlptr);
+ urlptr = "/";
+ } else
+ {
+ char *q = palloc(pool, p-urlptr+1);
+ memcpy(q, urlptr, p-urlptr);
+ q[p-urlptr] = '\0';
+ urlptr = p;
+ desthost = q;
+ }
+
+ p = strchr(desthost, ':');
+ if (p != NULL)
+ {
+ *(p++) = '\0';
+ if (isdigit(*p))
+ {
+ destport = atoi(p);
+ destportstr = p;
+ }
+ }
+
+/* check if ProxyBlock directive on this host */
+ destaddr.s_addr = inet_addr(desthost);
+ for (i=0; i < conf->noproxies->nelts; i++)
+ {
+ if ((npent[i].name != NULL && strstr(desthost, npent[i].name) != NULL)
+ || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*')
+ return proxyerror(r, "Connect to remote machine blocked");
+ }
+
+ if (proxyhost != NULL)
+ {
+ server.sin_port = htons(proxyport);
+ err = proxy_host2addr(proxyhost, &server_hp);
+ if (err != NULL) return DECLINED; /* try another */
+ } else
+ {
+ server.sin_port = htons(destport);
+ err = proxy_host2addr(desthost, &server_hp);
+ if (err != NULL) return proxyerror(r, err); /* give up */
+ }
+
+ sock = socket(PF_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);
+
+
+ j = 0;
+ while (server_hp.h_addr_list[j] != NULL) {
+ memcpy(&server.sin_addr, server_hp.h_addr_list[j],
+ sizeof(struct in_addr));
+ i = proxy_doconnect(sock, &server, r);
+ if (i == 0)
+ break;
+ j++;
+ }
+ if (i == -1)
+ {
+ if (proxyhost != NULL) return DECLINED; /* try again another way */
+ else return proxyerror(r, "Could not connect to remote machine");
+ }
+
+ clear_connection(r->headers_in); /* Strip connection-based headers */
+
+ f = bcreate(pool, B_RDWR);
+ bpushfd(f, sock, sock);
+
+ hard_timeout ("proxy send", r);
+ bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.0\015\012",
+ NULL);
+ bvputs(f, "Host: ", desthost, NULL);
+ if (destportstr != NULL && destport != DEFAULT_PORT)
+ bvputs(f, ":", destportstr, "\015\012", NULL);
+ else
+ bputs("\015\012", f);
+
+ 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
+ || !strcasecmp(reqhdrs[i].key, "Host")) /* already sent if there */
+ 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 (should_client_block(r))
+ {
+ while ((i = get_client_block(r, buffer, HUGE_STRING_LEN)) > 0)
+ bwrite(f, buffer, 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);
+ kill_timeout(r);
+ return proxyerror(r, "Error reading from remote server");
+ }
+
+/* Is it an HTTP/1 response? This is buggy if we ever see an HTTP/1.10 */
+ 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);
+ kill_timeout(r);
+ return BAD_GATEWAY;
+ }
+ backasswards = 0;
+ buffer[--len] = '\0';
+
+ 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 = proxy_read_headers(pool, buffer, HUGE_STRING_LEN, f);
+
+ clear_connection((table *)resp_hdrs); /* Strip Connection hdrs */
+ }
+ else
+ {
+/* an http/0.9 response */
+ backasswards = 1;
+ r->status = 200;
+ r->status_line = "200 OK";
+
+/* no headers */
+ resp_hdrs = 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
+ */
+
+ hdr = (struct hdr_entry *)resp_hdrs->elts;
+ for (i=0; i < resp_hdrs->nelts; 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 = proxy_date_canon(pool, hdr[i].value);
+ }
+
+/* check if NoCache directive on this host */
+ for (i=0; i < conf->nocaches->nelts; i++)
+ {
+ if ((ncent[i].name != NULL && strstr(desthost, ncent[i].name) != NULL)
+ || destaddr.s_addr == ncent[i].addr.s_addr || ncent[i].name[0] == '*')
+ nocache = 1;
+ }
+
+ i = proxy_cache_update(c, resp_hdrs, !backasswards, 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, "HTTP/1.0 ", r->status_line, "\015\012", NULL) == -1)
+ cache = proxy_cache_error(c);
+
+/* send headers */
+ for (i=0; i < resp_hdrs->nelts; 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 = proxy_cache_error(c);
+ }
+
+ if (!r->assbackwards) rputs("\015\012", r);
+ if (cache != NULL)
+ if (bputs("\015\012", cache) == -1) cache = proxy_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 (backasswards)
+ {
+ bwrite(r->connection->client, buffer, len);
+ if (cache != NULL)
+ if (bwrite(f, buffer, len) != len) cache = proxy_cache_error(c);
+ }
+ kill_timeout(r);
+
+/* 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) proxy_send_fb(f, r, cache, c);
+
+ proxy_cache_tidy(c);
+
+ pclosef(pool, sock);
+
+ proxy_garbage_coll(r);
+ return OK;
+}
+
diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_util.c b/usr.sbin/httpd/src/modules/proxy/proxy_util.c
new file mode 100644
index 00000000000..524f4e04856
--- /dev/null
+++ b/usr.sbin/httpd/src/modules/proxy/proxy_util.c
@@ -0,0 +1,750 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/>.
+ *
+ */
+
+/* Utility routines for Apache proxy */
+
+#include "mod_proxy.h"
+#include "http_main.h"
+#include "md5.h"
+
+/* already called in the knowledge that the characters are hex digits */
+int
+proxy_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;
+}
+
+void
+proxy_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
+ */
+
+/*
+ * Convert a URL-encoded string to canonical form.
+ * It decodes characters which need not be encoded,
+ * and encodes those which must be encoded, and does not touch
+ * those which must not be touched.
+ */
+char *
+proxy_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 = proxy_hex2c(&x[i+1]);
+ i += 2;
+ if (ch != 0 && ind(reserved, ch) != -1)
+ { /* keep it encoded */
+ proxy_c2hex(ch, &y[j]);
+ j += 2;
+ continue;
+ }
+ }
+/* recode it, if necessary */
+ if (!isalnum(ch) && ind(allowed, ch) == -1)
+ {
+ proxy_c2hex(ch, &y[j]);
+ j += 2;
+ } else y[j] = ch;
+ }
+ y[j] = '\0';
+ return y;
+}
+
+/*
+ * Parses network-location.
+ * urlp on input the URL; on output the path, after the leading /
+ * user NULL if no user/password permitted
+ * password holder for password
+ * host holder for host
+ * port port number; only set if one is supplied.
+ *
+ * Returns an error string.
+ */
+char *
+proxy_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 = proxy_canonenc(pool, p+1, strlen(p+1), enc_user, 1);
+ if (password == NULL)
+ return "Bad %-escape in URL (password)";
+ }
+
+ user = proxy_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;
+}
+
+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.
+ */
+char *
+proxy_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;
+
+ if (strlen(x)+1 < 30) {
+ x = palloc(p, 30);
+ }
+ /* format: "Wed, 17 Dec 1997 00:53:40 GMT" (29 chars data) */
+ ap_snprintf(x, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", wday[wk], mday,
+ months[mon], year, hour, min, sec);
+ return x;
+}
+
+/*
+ * Reads headers from a buffer and returns an array of headers.
+ * Returns NULL on file error
+ */
+array_header *
+proxy_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;
+}
+
+long int
+proxy_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;
+
+ /* Since we are reading from one buffer and writing to another,
+ * it is unsafe to do a soft_timeout here, at least until the proxy
+ * has its own timeout handler which can set both buffers to EOUT.
+ */
+ hard_timeout("proxy send body", r);
+
+ while (!con->aborted && f != NULL) {
+ n = bread(f, buf, IOBUFSIZE);
+ if (n == -1) /* input error */
+ {
+ if (f2 != NULL) f2 = proxy_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 = proxy_cache_error(c);
+
+ while(n && !con->aborted) {
+ w = bwrite(con->client, &buf[o], n);
+ if (w <= 0) {
+ if (f2 != NULL) {
+ pclosef(c->req->pool, c->fp->fd);
+ c->fp = NULL;
+ unlink(c->tempfile);
+ }
+ break;
+ }
+ reset_timeout(r); /* reset timeout after successful write */
+ n-=w;
+ o+=w;
+ }
+ }
+ if (!con->aborted)
+ bflush(con->client);
+
+ kill_timeout(r);
+ return total_bytes_sent;
+}
+
+/*
+ * Read a header from the array, returning the first entry
+ */
+struct hdr_entry *
+proxy_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;
+}
+
+/*
+ * 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
+ */
+struct hdr_entry *
+proxy_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;
+}
+
+void
+proxy_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;
+}
+
+/*
+ * Sends response line and headers
+ * A timeout should be set before calling this routine.
+ */
+void
+proxy_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.
+ */
+int
+proxy_liststr(const char *list, const char *val)
+{
+ int len, i;
+ const char *p;
+
+ len = strlen(val);
+
+ while (list != NULL)
+ {
+ p = strchr(list, ',');
+ if (p != NULL)
+ {
+ i = p - list;
+ do p++; while (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;
+}
+
+void
+proxy_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';
+}
+
+/*
+ * Converts 8 hex digits to a time integer
+ */
+int
+proxy_hex2sec(const char *x)
+{
+ int i, ch;
+ unsigned int j;
+
+ for (i=0, j=0; i < 8; i++)
+ {
+ ch = x[i];
+ j <<= 4;
+ if (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
+ */
+void
+proxy_sec2hex(int t, char *y)
+{
+ int i, ch;
+ unsigned int j=t;
+
+ for (i=7; i >= 0; i--)
+ {
+ ch = j & 0xF;
+ j >>= 4;
+ if (ch >= 10) y[i] = ch + ('A' - 10);
+ else y[i] = ch + '0';
+ }
+ y[8] = '\0';
+}
+
+void
+proxy_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);
+}
+
+BUFF *
+proxy_cache_error(struct cache_req *c)
+{
+ proxy_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;
+}
+
+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);
+ soft_timeout("proxy error", r);
+
+ rvputs(r, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\015\012\
+<html><head><title>Proxy Error</title><head>\015\012<body><h1>Proxy Error\
+</h1>\015\012The proxy server could not handle this request.\
+\015\012<p>\015\012Reason: <b>", message, "</b>\015\012</body><html>\015\012",
+ NULL);
+
+ kill_timeout(r);
+ return OK;
+}
+
+/*
+ * This routine returns its own error message
+ */
+const char *
+proxy_host2addr(const char *host, struct hostent *reqhp)
+{
+ int i;
+ struct hostent *hp;
+ static struct hostent hpbuf;
+ static u_long ipaddr;
+ static char* charpbuf[2];
+
+ for (i=0; host[i] != '\0'; i++)
+ if (!isdigit(host[i]) && host[i] != '.')
+ break;
+
+ if (host[i] != '\0')
+ {
+ hp = gethostbyname(host);
+ if (hp == NULL)
+ return "Host not found";
+ } else
+ {
+ ipaddr = inet_addr(host);
+ hp = gethostbyaddr((char *)&ipaddr, sizeof(u_long), AF_INET);
+ if (hp == NULL) {
+ memset(&hpbuf, 0, sizeof(hpbuf));
+ hpbuf.h_name = 0;
+ hpbuf.h_addrtype = AF_INET;
+ hpbuf.h_length = sizeof(u_long);
+ hpbuf.h_addr_list = charpbuf;
+ hpbuf.h_addr_list[0] = (char*)&ipaddr;
+ hpbuf.h_addr_list[1] = 0;
+ hp = &hpbuf;
+ }
+ }
+ memcpy(reqhp, hp, sizeof(struct hostent));
+ return NULL;
+}
+
+int
+proxy_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) proxy_log_uerror("connect", NULL, NULL, r->server);
+ kill_timeout(r);
+
+ return i;
+}
+
diff --git a/usr.sbin/httpd/src/regex/COPYRIGHT b/usr.sbin/httpd/src/regex/COPYRIGHT
new file mode 100644
index 00000000000..d43362fbfc9
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/COPYRIGHT
@@ -0,0 +1,20 @@
+Copyright 1992, 1993, 1994 Henry Spencer. All rights reserved.
+This software is not subject to any license of the American Telephone
+and Telegraph Company or of the Regents of the University of California.
+
+Permission is granted to anyone to use this software for any purpose on
+any computer system, and to alter it and redistribute it, subject
+to the following restrictions:
+
+1. The author is not responsible for the consequences of use of this
+ software, no matter how awful, even if they arise from flaws in it.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission. Since few users ever read sources,
+ credits must appear in the documentation.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software. Since few users
+ ever read sources, credits must appear in the documentation.
+
+4. This notice may not be removed or altered.
diff --git a/usr.sbin/httpd/src/regex/Makefile b/usr.sbin/httpd/src/regex/Makefile
new file mode 100644
index 00000000000..e4ea0154bea
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/Makefile
@@ -0,0 +1,133 @@
+SHELL = /bin/sh
+
+# You probably want to take -DREDEBUG out of CFLAGS, and put something like
+# -O in, *after* testing (-DREDEBUG strengthens testing by enabling a lot of
+# internal assertion checking and some debugging facilities).
+# Put -Dconst= in for a pre-ANSI compiler.
+# Do not take -DPOSIX_MISTAKE out.
+# REGCFLAGS isn't important to you (it's for my use in some special contexts).
+CFLAGS=-I. -DPOSIX_MISTAKE $(AUX_CFLAGS)
+
+# If you have a pre-ANSI compiler, put -o into MKHFLAGS. If you want
+# the Berkeley __P macro, put -b in.
+MKHFLAGS=
+
+# Flags for linking but not compiling, if any.
+LDFLAGS=
+
+# Extra libraries for linking, if any.
+LIBS=
+
+# Internal stuff, should not need changing.
+OBJPRODN=regcomp.o regexec.o regerror.o regfree.o
+OBJS=$(OBJPRODN) split.o debug.o main.o
+H=cclass.h cname.h regex2.h utils.h
+REGSRC=regcomp.c regerror.c regexec.c regfree.c
+ALLSRC=$(REGSRC) engine.c debug.c main.c split.c
+
+# Stuff that matters only if you're trying to lint the package.
+LINTFLAGS=-I. -Dstatic= -Dconst= -DREDEBUG
+LINTC=regcomp.c regexec.c regerror.c regfree.c debug.c main.c
+JUNKLINT=possible pointer alignment|null effect
+
+# arrangements to build forward-reference header files
+.SUFFIXES: .ih .h
+.c.ih:
+ sh ./mkh $(MKHFLAGS) -p $< >$@
+
+default: r
+
+lib: purge $(OBJPRODN)
+ rm -f libregex.a
+ ar crv libregex.a $(OBJPRODN)
+ $(RANLIB) libregex.a
+
+purge:
+ rm -f *.o
+
+# stuff to build regex.h
+REGEXH=regex.h
+REGEXHSRC=regex2.h $(REGSRC)
+$(REGEXH): $(REGEXHSRC) mkh
+ sh ./mkh $(MKHFLAGS) -i _REGEX_H_ $(REGEXHSRC) >regex.tmp
+ cmp -s regex.tmp regex.h 2>/dev/null || cp regex.tmp regex.h
+ rm -f regex.tmp
+
+# dependencies
+$(OBJPRODN) debug.o: utils.h regex.h regex2.h
+regcomp.o: cclass.h cname.h regcomp.ih
+regexec.o: engine.c engine.ih
+regerror.o: regerror.ih
+debug.o: debug.ih
+main.o: main.ih
+
+# tester
+re: $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
+
+# regression test
+r: re tests
+ ./re <tests
+ ./re -el <tests
+ ./re -er <tests
+
+# 57 variants, and other stuff, for development use -- not useful to you
+ra: ./re tests
+ -./re <tests
+ -./re -el <tests
+ -./re -er <tests
+
+rx: ./re tests
+ ./re -x <tests
+ ./re -x -el <tests
+ ./re -x -er <tests
+
+t: ./re tests
+ -time ./re <tests
+ -time ./re -cs <tests
+ -time ./re -el <tests
+ -time ./re -cs -el <tests
+
+l: $(LINTC)
+ lint $(LINTFLAGS) -h $(LINTC) 2>&1 | egrep -v '$(JUNKLINT)' | tee lint
+
+fullprint:
+ ti README WHATSNEW notes todo | list
+ ti *.h | list
+ list *.c
+ list regex.3 regex.7
+
+print:
+ ti README WHATSNEW notes todo | list
+ ti *.h | list
+ list reg*.c engine.c
+
+
+mf.tmp: Makefile
+ sed '/^REGEXH=/s/=.*/=regex.h/' Makefile | sed '/#DEL$$/d' >$@
+
+DTRH=cclass.h cname.h regex2.h utils.h
+PRE=COPYRIGHT README WHATSNEW
+POST=mkh regex.3 regex.7 tests $(DTRH) $(ALLSRC) fake/*.[ch]
+FILES=$(PRE) Makefile $(POST)
+DTR=$(PRE) Makefile=mf.tmp $(POST)
+dtr: $(FILES) mf.tmp
+ makedtr $(DTR) >$@
+ rm mf.tmp
+
+cio: $(FILES)
+ cio $(FILES)
+
+rdf: $(FILES)
+ rcsdiff -c $(FILES) 2>&1 | p
+
+# various forms of cleanup
+tidy:
+ rm -f junk* core core.* *.core dtr *.tmp lint
+
+clean: tidy
+ rm -f *.o *.s *.ih re libregex.a
+
+# don't do this one unless you know what you're doing
+spotless: clean
+ rm -f mkh regex.h
diff --git a/usr.sbin/httpd/src/regex/README b/usr.sbin/httpd/src/regex/README
new file mode 100644
index 00000000000..cea9b67b666
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/README
@@ -0,0 +1,32 @@
+alpha3.4 release.
+Thu Mar 17 23:17:18 EST 1994
+henry@zoo.toronto.edu
+
+See WHATSNEW for change listing.
+
+installation notes:
+--------
+Read the comments at the beginning of Makefile before running.
+
+Utils.h contains some things that just might have to be modified on
+some systems, as well as a nested include (ugh) of <assert.h>.
+
+The "fake" directory contains quick-and-dirty fakes for some header
+files and routines that old systems may not have. Note also that
+-DUSEBCOPY will make utils.h substitute bcopy() for memmove().
+
+After that, "make r" will build regcomp.o, regexec.o, regfree.o,
+and regerror.o (the actual routines), bundle them together into a test
+program, and run regression tests on them. No output is good output.
+
+"make lib" builds just the .o files for the actual routines (when
+you're happy with testing and have adjusted CFLAGS for production),
+and puts them together into libregex.a. You can pick up either the
+library or *.o ("make lib" makes sure there are no other .o files left
+around to confuse things).
+
+Main.c, debug.c, split.c are used for regression testing but are not part
+of the RE routines themselves.
+
+Regex.h goes in /usr/include. All other .h files are internal only.
+--------
diff --git a/usr.sbin/httpd/src/regex/WHATSNEW b/usr.sbin/httpd/src/regex/WHATSNEW
new file mode 100644
index 00000000000..6e82e1dae0c
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/WHATSNEW
@@ -0,0 +1,92 @@
+New in alpha3.4: The complex bug alluded to below has been fixed (in a
+slightly kludgey temporary way that may hurt efficiency a bit; this is
+another "get it out the door for 4.4" release). The tests at the end of
+the tests file have accordingly been uncommented. The primary sign of
+the bug was that something like a?b matching ab matched b rather than ab.
+(The bug was essentially specific to this exact situation, else it would
+have shown up earlier.)
+
+New in alpha3.3: The definition of word boundaries has been altered
+slightly, to more closely match the usual programming notion that "_"
+is an alphabetic. Stuff used for pre-ANSI systems is now in a subdir,
+and the makefile no longer alludes to it in mysterious ways. The
+makefile has generally been cleaned up some. Fixes have been made
+(again!) so that the regression test will run without -DREDEBUG, at
+the cost of weaker checking. A workaround for a bug in some folks'
+<assert.h> has been added. And some more things have been added to
+tests, including a couple right at the end which are commented out
+because the code currently flunks them (complex bug; fix coming).
+Plus the usual minor cleanup.
+
+New in alpha3.2: Assorted bits of cleanup and portability improvement
+(the development base is now a BSDI system using GCC instead of an ancient
+Sun system, and the newer compiler exposed some glitches). Fix for a
+serious bug that affected REs using many [] (including REG_ICASE REs
+because of the way they are implemented), *sometimes*, depending on
+memory-allocation patterns. The header-file prototypes no longer name
+the parameters, avoiding possible name conflicts. The possibility that
+some clot has defined CHAR_MIN as (say) `-128' instead of `(-128)' is
+now handled gracefully. "uchar" is no longer used as an internal type
+name (too many people have the same idea). Still the same old lousy
+performance, alas.
+
+New in alpha3.1: Basically nothing, this release is just a bookkeeping
+convenience. Stay tuned.
+
+New in alpha3.0: Performance is no better, alas, but some fixes have been
+made and some functionality has been added. (This is basically the "get
+it out the door in time for 4.4" release.) One bug fix: regfree() didn't
+free the main internal structure (how embarrassing). It is now possible
+to put NULs in either the RE or the target string, using (resp.) a new
+REG_PEND flag and the old REG_STARTEND flag. The REG_NOSPEC flag to
+regcomp() makes all characters ordinary, so you can match a literal
+string easily (this will become more useful when performance improves!).
+There are now primitives to match beginnings and ends of words, although
+the syntax is disgusting and so is the implementation. The REG_ATOI
+debugging interface has changed a bit. And there has been considerable
+internal cleanup of various kinds.
+
+New in alpha2.3: Split change list out of README, and moved flags notes
+into Makefile. Macro-ized the name of regex(7) in regex(3), since it has
+to change for 4.4BSD. Cleanup work in engine.c, and some new regression
+tests to catch tricky cases thereof.
+
+New in alpha2.2: Out-of-date manpages updated. Regerror() acquires two
+small extensions -- REG_ITOA and REG_ATOI -- which avoid debugging kludges
+in my own test program and might be useful to others for similar purposes.
+The regression test will now compile (and run) without REDEBUG. The
+BRE \$ bug is fixed. Most uses of "uchar" are gone; it's all chars now.
+Char/uchar parameters are now written int/unsigned, to avoid possible
+portability problems with unpromoted parameters. Some unsigned casts have
+been introduced to minimize portability problems with shifting into sign
+bits.
+
+New in alpha2.1: Lots of little stuff, cleanup and fixes. The one big
+thing is that regex.h is now generated, using mkh, rather than being
+supplied in the distribution; due to circularities in dependencies,
+you have to build regex.h explicitly by "make h". The two known bugs
+have been fixed (and the regression test now checks for them), as has a
+problem with assertions not being suppressed in the absence of REDEBUG.
+No performance work yet.
+
+New in alpha2: Backslash-anything is an ordinary character, not an
+error (except, of course, for the handful of backslashed metacharacters
+in BREs), which should reduce script breakage. The regression test
+checks *where* null strings are supposed to match, and has generally
+been tightened up somewhat. Small bug fixes in parameter passing (not
+harmful, but technically errors) and some other areas. Debugging
+invoked by defining REDEBUG rather than not defining NDEBUG.
+
+New in alpha+3: full prototyping for internal routines, using a little
+helper program, mkh, which extracts prototypes given in stylized comments.
+More minor cleanup. Buglet fix: it's CHAR_BIT, not CHAR_BITS. Simple
+pre-screening of input when a literal string is known to be part of the
+RE; this does wonders for performance.
+
+New in alpha+2: minor bits of cleanup. Notably, the number "32" for the
+word width isn't hardwired into regexec.c any more, the public header
+file prototypes the functions if __STDC__ is defined, and some small typos
+in the manpages have been fixed.
+
+New in alpha+1: improvements to the manual pages, and an important
+extension, the REG_STARTEND option to regexec().
diff --git a/usr.sbin/httpd/src/regex/cclass.h b/usr.sbin/httpd/src/regex/cclass.h
new file mode 100644
index 00000000000..727cbb92552
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/cclass.h
@@ -0,0 +1,31 @@
+/* character-class table */
+static struct cclass {
+ char *name;
+ char *chars;
+ char *multis;
+} cclasses[] = {
+ { "alnum", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789", "" },
+ { "alpha", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
+ "" },
+ { "blank", " \t", "" },
+ { "cntrl", "\007\b\t\n\v\f\r\1\2\3\4\5\6\16\17\20\21\22\23\24\
+\25\26\27\30\31\32\33\34\35\36\37\177", "" },
+ { "digit", "0123456789", "" },
+ { "graph", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+ "" },
+ { "lower", "abcdefghijklmnopqrstuvwxyz",
+ "" },
+ { "print", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ",
+ "" },
+ { "punct", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+ "" },
+ { "space", "\t\n\v\f\r ", "" },
+ { "upper", "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "" },
+ { "xdigit", "0123456789ABCDEFabcdef",
+ "" },
+ { NULL, 0, "" }
+};
diff --git a/usr.sbin/httpd/src/regex/cname.h b/usr.sbin/httpd/src/regex/cname.h
new file mode 100644
index 00000000000..ff116e55e23
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/cname.h
@@ -0,0 +1,102 @@
+/* character-name table */
+static struct cname {
+ char *name;
+ char code;
+} cnames[] = {
+ { "NUL", '\0' },
+ { "SOH", '\001' },
+ { "STX", '\002' },
+ { "ETX", '\003' },
+ { "EOT", '\004' },
+ { "ENQ", '\005' },
+ { "ACK", '\006' },
+ { "BEL", '\007' },
+ { "alert", '\007' },
+ { "BS", '\010' },
+ { "backspace", '\b' },
+ { "HT", '\011' },
+ { "tab", '\t' },
+ { "LF", '\012' },
+ { "newline", '\n' },
+ { "VT", '\013' },
+ { "vertical-tab", '\v' },
+ { "FF", '\014' },
+ { "form-feed", '\f' },
+ { "CR", '\015' },
+ { "carriage-return", '\r' },
+ { "SO", '\016' },
+ { "SI", '\017' },
+ { "DLE", '\020' },
+ { "DC1", '\021' },
+ { "DC2", '\022' },
+ { "DC3", '\023' },
+ { "DC4", '\024' },
+ { "NAK", '\025' },
+ { "SYN", '\026' },
+ { "ETB", '\027' },
+ { "CAN", '\030' },
+ { "EM", '\031' },
+ { "SUB", '\032' },
+ { "ESC", '\033' },
+ { "IS4", '\034' },
+ { "FS", '\034' },
+ { "IS3", '\035' },
+ { "GS", '\035' },
+ { "IS2", '\036' },
+ { "RS", '\036' },
+ { "IS1", '\037' },
+ { "US", '\037' },
+ { "space", ' ' },
+ { "exclamation-mark", '!' },
+ { "quotation-mark", '"' },
+ { "number-sign", '#' },
+ { "dollar-sign", '$' },
+ { "percent-sign", '%' },
+ { "ampersand", '&' },
+ { "apostrophe", '\'' },
+ { "left-parenthesis", '(' },
+ { "right-parenthesis", ')' },
+ { "asterisk", '*' },
+ { "plus-sign", '+' },
+ { "comma", ',' },
+ { "hyphen", '-' },
+ { "hyphen-minus", '-' },
+ { "period", '.' },
+ { "full-stop", '.' },
+ { "slash", '/' },
+ { "solidus", '/' },
+ { "zero", '0' },
+ { "one", '1' },
+ { "two", '2' },
+ { "three", '3' },
+ { "four", '4' },
+ { "five", '5' },
+ { "six", '6' },
+ { "seven", '7' },
+ { "eight", '8' },
+ { "nine", '9' },
+ { "colon", ':' },
+ { "semicolon", ';' },
+ { "less-than-sign", '<' },
+ { "equals-sign", '=' },
+ { "greater-than-sign", '>' },
+ { "question-mark", '?' },
+ { "commercial-at", '@' },
+ { "left-square-bracket", '[' },
+ { "backslash", '\\' },
+ { "reverse-solidus", '\\' },
+ { "right-square-bracket", ']' },
+ { "circumflex", '^' },
+ { "circumflex-accent", '^' },
+ { "underscore", '_' },
+ { "low-line", '_' },
+ { "grave-accent", '`' },
+ { "left-brace", '{' },
+ { "left-curly-bracket", '{' },
+ { "vertical-line", '|' },
+ { "right-brace", '}' },
+ { "right-curly-bracket", '}' },
+ { "tilde", '~' },
+ { "DEL", '\177' },
+ { NULL, 0 }
+};
diff --git a/usr.sbin/httpd/src/regex/debug.c b/usr.sbin/httpd/src/regex/debug.c
new file mode 100644
index 00000000000..c0feaeb1694
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/debug.c
@@ -0,0 +1,242 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regex2.h"
+#include "debug.ih"
+
+/*
+ - regprint - print a regexp for debugging
+ == void regprint(regex_t *r, FILE *d);
+ */
+void
+regprint(r, d)
+regex_t *r;
+FILE *d;
+{
+ register struct re_guts *g = r->re_g;
+ register int i;
+ register int c;
+ register int last;
+ int nincat[NC];
+
+ fprintf(d, "%ld states, %d categories", (long)g->nstates,
+ g->ncategories);
+ fprintf(d, ", first %ld last %ld", (long)g->firststate,
+ (long)g->laststate);
+ if (g->iflags&USEBOL)
+ fprintf(d, ", USEBOL");
+ if (g->iflags&USEEOL)
+ fprintf(d, ", USEEOL");
+ if (g->iflags&BAD)
+ fprintf(d, ", BAD");
+ if (g->nsub > 0)
+ fprintf(d, ", nsub=%ld", (long)g->nsub);
+ if (g->must != NULL)
+ fprintf(d, ", must(%ld) `%*s'", (long)g->mlen, (int)g->mlen,
+ g->must);
+ if (g->backrefs)
+ fprintf(d, ", backrefs");
+ if (g->nplus > 0)
+ fprintf(d, ", nplus %ld", (long)g->nplus);
+ fprintf(d, "\n");
+ s_print(g, d);
+ for (i = 0; i < g->ncategories; i++) {
+ nincat[i] = 0;
+ for (c = CHAR_MIN; c <= CHAR_MAX; c++)
+ if (g->categories[c] == i)
+ nincat[i]++;
+ }
+ fprintf(d, "cc0#%d", nincat[0]);
+ for (i = 1; i < g->ncategories; i++)
+ if (nincat[i] == 1) {
+ for (c = CHAR_MIN; c <= CHAR_MAX; c++)
+ if (g->categories[c] == i)
+ break;
+ fprintf(d, ", %d=%s", i, regchar(c));
+ }
+ fprintf(d, "\n");
+ for (i = 1; i < g->ncategories; i++)
+ if (nincat[i] != 1) {
+ fprintf(d, "cc%d\t", i);
+ last = -1;
+ for (c = CHAR_MIN; c <= CHAR_MAX+1; c++) /* +1 does flush */
+ if (c <= CHAR_MAX && g->categories[c] == i) {
+ if (last < 0) {
+ fprintf(d, "%s", regchar(c));
+ last = c;
+ }
+ } else {
+ if (last >= 0) {
+ if (last != c-1)
+ fprintf(d, "-%s",
+ regchar(c-1));
+ last = -1;
+ }
+ }
+ fprintf(d, "\n");
+ }
+}
+
+/*
+ - s_print - print the strip for debugging
+ == static void s_print(register struct re_guts *g, FILE *d);
+ */
+static void
+s_print(g, d)
+register struct re_guts *g;
+FILE *d;
+{
+ register sop *s;
+ register cset *cs;
+ register int i;
+ register int done = 0;
+ register sop opnd;
+ register int col = 0;
+ register int last;
+ register sopno offset = 2;
+# define GAP() { if (offset % 5 == 0) { \
+ if (col > 40) { \
+ fprintf(d, "\n\t"); \
+ col = 0; \
+ } else { \
+ fprintf(d, " "); \
+ col++; \
+ } \
+ } else \
+ col++; \
+ offset++; \
+ }
+
+ if (OP(g->strip[0]) != OEND)
+ fprintf(d, "missing initial OEND!\n");
+ for (s = &g->strip[1]; !done; s++) {
+ opnd = OPND(*s);
+ switch (OP(*s)) {
+ case OEND:
+ fprintf(d, "\n");
+ done = 1;
+ break;
+ case OCHAR:
+ if (strchr("\\|()^$.[+*?{}!<> ", (char)opnd) != NULL)
+ fprintf(d, "\\%c", (char)opnd);
+ else
+ fprintf(d, "%s", regchar((char)opnd));
+ break;
+ case OBOL:
+ fprintf(d, "^");
+ break;
+ case OEOL:
+ fprintf(d, "$");
+ break;
+ case OBOW:
+ fprintf(d, "\\{");
+ break;
+ case OEOW:
+ fprintf(d, "\\}");
+ break;
+ case OANY:
+ fprintf(d, ".");
+ break;
+ case OANYOF:
+ fprintf(d, "[(%ld)", (long)opnd);
+ cs = &g->sets[opnd];
+ last = -1;
+ for (i = 0; i < g->csetsize+1; i++) /* +1 flushes */
+ if (CHIN(cs, i) && i < g->csetsize) {
+ if (last < 0) {
+ fprintf(d, "%s", regchar(i));
+ last = i;
+ }
+ } else {
+ if (last >= 0) {
+ if (last != i-1)
+ fprintf(d, "-%s",
+ regchar(i-1));
+ last = -1;
+ }
+ }
+ fprintf(d, "]");
+ break;
+ case OBACK_:
+ fprintf(d, "(\\<%ld>", (long)opnd);
+ break;
+ case O_BACK:
+ fprintf(d, "<%ld>\\)", (long)opnd);
+ break;
+ case OPLUS_:
+ fprintf(d, "(+");
+ if (OP(*(s+opnd)) != O_PLUS)
+ fprintf(d, "<%ld>", (long)opnd);
+ break;
+ case O_PLUS:
+ if (OP(*(s-opnd)) != OPLUS_)
+ fprintf(d, "<%ld>", (long)opnd);
+ fprintf(d, "+)");
+ break;
+ case OQUEST_:
+ fprintf(d, "(?");
+ if (OP(*(s+opnd)) != O_QUEST)
+ fprintf(d, "<%ld>", (long)opnd);
+ break;
+ case O_QUEST:
+ if (OP(*(s-opnd)) != OQUEST_)
+ fprintf(d, "<%ld>", (long)opnd);
+ fprintf(d, "?)");
+ break;
+ case OLPAREN:
+ fprintf(d, "((<%ld>", (long)opnd);
+ break;
+ case ORPAREN:
+ fprintf(d, "<%ld>))", (long)opnd);
+ break;
+ case OCH_:
+ fprintf(d, "<");
+ if (OP(*(s+opnd)) != OOR2)
+ fprintf(d, "<%ld>", (long)opnd);
+ break;
+ case OOR1:
+ if (OP(*(s-opnd)) != OOR1 && OP(*(s-opnd)) != OCH_)
+ fprintf(d, "<%ld>", (long)opnd);
+ fprintf(d, "|");
+ break;
+ case OOR2:
+ fprintf(d, "|");
+ if (OP(*(s+opnd)) != OOR2 && OP(*(s+opnd)) != O_CH)
+ fprintf(d, "<%ld>", (long)opnd);
+ break;
+ case O_CH:
+ if (OP(*(s-opnd)) != OOR1)
+ fprintf(d, "<%ld>", (long)opnd);
+ fprintf(d, ">");
+ break;
+ default:
+ fprintf(d, "!%ld(%ld)!", OP(*s), opnd);
+ break;
+ }
+ if (!done)
+ GAP();
+ }
+}
+
+/*
+ - regchar - make a character printable
+ == static char *regchar(int ch);
+ */
+static char * /* -> representation */
+regchar(ch)
+int ch;
+{
+ static char buf[10];
+
+ if (isprint(ch) || ch == ' ')
+ sprintf(buf, "%c", ch);
+ else
+ sprintf(buf, "\\%o", ch);
+ return(buf);
+}
diff --git a/usr.sbin/httpd/src/regex/engine.c b/usr.sbin/httpd/src/regex/engine.c
new file mode 100644
index 00000000000..f3b36b3e01b
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/engine.c
@@ -0,0 +1,1019 @@
+/*
+ * The matching engine and friends. This file is #included by regexec.c
+ * after suitable #defines of a variety of macros used herein, so that
+ * different state representations can be used without duplicating masses
+ * of code.
+ */
+
+#ifdef SNAMES
+#define matcher smatcher
+#define fast sfast
+#define slow sslow
+#define dissect sdissect
+#define backref sbackref
+#define step sstep
+#define print sprint
+#define at sat
+#define match smat
+#endif
+#ifdef LNAMES
+#define matcher lmatcher
+#define fast lfast
+#define slow lslow
+#define dissect ldissect
+#define backref lbackref
+#define step lstep
+#define print lprint
+#define at lat
+#define match lmat
+#endif
+
+/* another structure passed up and down to avoid zillions of parameters */
+struct match {
+ struct re_guts *g;
+ int eflags;
+ regmatch_t *pmatch; /* [nsub+1] (0 element unused) */
+ char *offp; /* offsets work from here */
+ char *beginp; /* start of string -- virtual NUL precedes */
+ char *endp; /* end of string -- virtual NUL here */
+ char *coldp; /* can be no match starting before here */
+ char **lastpos; /* [nplus+1] */
+ STATEVARS;
+ states st; /* current states */
+ states fresh; /* states for a fresh start */
+ states tmp; /* temporary */
+ states empty; /* empty set of states */
+};
+
+#include "engine.ih"
+
+#ifdef REDEBUG
+#define SP(t, s, c) print(m, t, s, c, stdout)
+#define AT(t, p1, p2, s1, s2) at(m, t, p1, p2, s1, s2)
+#define NOTE(str) { if (m->eflags&REG_TRACE) printf("=%s\n", (str)); }
+#else
+#define SP(t, s, c) /* nothing */
+#define AT(t, p1, p2, s1, s2) /* nothing */
+#define NOTE(s) /* nothing */
+#endif
+
+/*
+ - matcher - the actual matching engine
+ == static int matcher(register struct re_guts *g, char *string, \
+ == size_t nmatch, regmatch_t pmatch[], int eflags);
+ */
+static int /* 0 success, REG_NOMATCH failure */
+matcher(g, string, nmatch, pmatch, eflags)
+register struct re_guts *g;
+char *string;
+size_t nmatch;
+regmatch_t pmatch[];
+int eflags;
+{
+ register char *endp;
+ register int i;
+ struct match mv;
+ register struct match *m = &mv;
+ register char *dp;
+ register const sopno gf = g->firststate+1; /* +1 for OEND */
+ register const sopno gl = g->laststate;
+ char *start;
+ char *stop;
+
+ /* simplify the situation where possible */
+ if (g->cflags&REG_NOSUB)
+ nmatch = 0;
+ if (eflags&REG_STARTEND) {
+ start = string + pmatch[0].rm_so;
+ stop = string + pmatch[0].rm_eo;
+ } else {
+ start = string;
+ stop = start + strlen(start);
+ }
+ if (stop < start)
+ return(REG_INVARG);
+
+ /* prescreening; this does wonders for this rather slow code */
+ if (g->must != NULL) {
+ for (dp = start; dp < stop; dp++)
+ if (*dp == g->must[0] && stop - dp >= g->mlen &&
+ memcmp(dp, g->must, (size_t)g->mlen) == 0)
+ break;
+ if (dp == stop) /* we didn't find g->must */
+ return(REG_NOMATCH);
+ }
+
+ /* match struct setup */
+ m->g = g;
+ m->eflags = eflags;
+ m->pmatch = NULL;
+ m->lastpos = NULL;
+ m->offp = string;
+ m->beginp = start;
+ m->endp = stop;
+ STATESETUP(m, 4);
+ SETUP(m->st);
+ SETUP(m->fresh);
+ SETUP(m->tmp);
+ SETUP(m->empty);
+ CLEAR(m->empty);
+
+ /* this loop does only one repetition except for backrefs */
+ for (;;) {
+ endp = fast(m, start, stop, gf, gl);
+ if (endp == NULL) { /* a miss */
+ STATETEARDOWN(m);
+ return(REG_NOMATCH);
+ }
+ if (nmatch == 0 && !g->backrefs)
+ break; /* no further info needed */
+
+ /* where? */
+ assert(m->coldp != NULL);
+ for (;;) {
+ NOTE("finding start");
+ endp = slow(m, m->coldp, stop, gf, gl);
+ if (endp != NULL)
+ break;
+ assert(m->coldp < m->endp);
+ m->coldp++;
+ }
+ if (nmatch == 1 && !g->backrefs)
+ break; /* no further info needed */
+
+ /* oh my, he wants the subexpressions... */
+ if (m->pmatch == NULL)
+ m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) *
+ sizeof(regmatch_t));
+ if (m->pmatch == NULL) {
+ STATETEARDOWN(m);
+ return(REG_ESPACE);
+ }
+ for (i = 1; i <= m->g->nsub; i++)
+ m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1;
+ if (!g->backrefs && !(m->eflags&REG_BACKR)) {
+ NOTE("dissecting");
+ dp = dissect(m, m->coldp, endp, gf, gl);
+ } else {
+ if (g->nplus > 0 && m->lastpos == NULL)
+ m->lastpos = (char **)malloc((g->nplus+1) *
+ sizeof(char *));
+ if (g->nplus > 0 && m->lastpos == NULL) {
+ free(m->pmatch);
+ STATETEARDOWN(m);
+ return(REG_ESPACE);
+ }
+ NOTE("backref dissect");
+ dp = backref(m, m->coldp, endp, gf, gl, (sopno)0);
+ }
+ if (dp != NULL)
+ break;
+
+ /* uh-oh... we couldn't find a subexpression-level match */
+ assert(g->backrefs); /* must be back references doing it */
+ assert(g->nplus == 0 || m->lastpos != NULL);
+ for (;;) {
+ if (dp != NULL || endp <= m->coldp)
+ break; /* defeat */
+ NOTE("backoff");
+ endp = slow(m, m->coldp, endp-1, gf, gl);
+ if (endp == NULL)
+ break; /* defeat */
+ /* try it on a shorter possibility */
+#ifndef NDEBUG
+ for (i = 1; i <= m->g->nsub; i++) {
+ assert(m->pmatch[i].rm_so == -1);
+ assert(m->pmatch[i].rm_eo == -1);
+ }
+#endif
+ NOTE("backoff dissect");
+ dp = backref(m, m->coldp, endp, gf, gl, (sopno)0);
+ }
+ assert(dp == NULL || dp == endp);
+ if (dp != NULL) /* found a shorter one */
+ break;
+
+ /* despite initial appearances, there is no match here */
+ NOTE("false alarm");
+ start = m->coldp + 1; /* recycle starting later */
+ assert(start <= stop);
+ }
+
+ /* fill in the details if requested */
+ if (nmatch > 0) {
+ pmatch[0].rm_so = m->coldp - m->offp;
+ pmatch[0].rm_eo = endp - m->offp;
+ }
+ if (nmatch > 1) {
+ assert(m->pmatch != NULL);
+ for (i = 1; i < nmatch; i++)
+ if (i <= m->g->nsub)
+ pmatch[i] = m->pmatch[i];
+ else {
+ pmatch[i].rm_so = -1;
+ pmatch[i].rm_eo = -1;
+ }
+ }
+
+ if (m->pmatch != NULL)
+ free((char *)m->pmatch);
+ if (m->lastpos != NULL)
+ free((char *)m->lastpos);
+ STATETEARDOWN(m);
+ return(0);
+}
+
+/*
+ - dissect - figure out what matched what, no back references
+ == static char *dissect(register struct match *m, char *start, \
+ == char *stop, sopno startst, sopno stopst);
+ */
+static char * /* == stop (success) always */
+dissect(m, start, stop, startst, stopst)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+ register int i;
+ register sopno ss; /* start sop of current subRE */
+ register sopno es; /* end sop of current subRE */
+ register char *sp; /* start of string matched by it */
+ register char *stp; /* string matched by it cannot pass here */
+ register char *rest; /* start of rest of string */
+ register char *tail; /* string unmatched by rest of RE */
+ register sopno ssub; /* start sop of subsubRE */
+ register sopno esub; /* end sop of subsubRE */
+ register char *ssp; /* start of string matched by subsubRE */
+ register char *sep; /* end of string matched by subsubRE */
+ register char *oldssp; /* previous ssp */
+ register char *dp;
+
+ AT("diss", start, stop, startst, stopst);
+ sp = start;
+ for (ss = startst; ss < stopst; ss = es) {
+ /* identify end of subRE */
+ es = ss;
+ switch (OP(m->g->strip[es])) {
+ case OPLUS_:
+ case OQUEST_:
+ es += OPND(m->g->strip[es]);
+ break;
+ case OCH_:
+ while (OP(m->g->strip[es]) != O_CH)
+ es += OPND(m->g->strip[es]);
+ break;
+ }
+ es++;
+
+ /* figure out what it matched */
+ switch (OP(m->g->strip[ss])) {
+ case OEND:
+ assert(nope);
+ break;
+ case OCHAR:
+ sp++;
+ break;
+ case OBOL:
+ case OEOL:
+ case OBOW:
+ case OEOW:
+ break;
+ case OANY:
+ case OANYOF:
+ sp++;
+ break;
+ case OBACK_:
+ case O_BACK:
+ assert(nope);
+ break;
+ /* cases where length of match is hard to find */
+ case OQUEST_:
+ stp = stop;
+ for (;;) {
+ /* how long could this one be? */
+ rest = slow(m, sp, stp, ss, es);
+ assert(rest != NULL); /* it did match */
+ /* could the rest match the rest? */
+ tail = slow(m, rest, stop, es, stopst);
+ if (tail == stop)
+ break; /* yes! */
+ /* no -- try a shorter match for this one */
+ stp = rest - 1;
+ assert(stp >= sp); /* it did work */
+ }
+ ssub = ss + 1;
+ esub = es - 1;
+ /* did innards match? */
+ if (slow(m, sp, rest, ssub, esub) != NULL) {
+ dp = dissect(m, sp, rest, ssub, esub);
+ assert(dp == rest);
+ } else /* no */
+ assert(sp == rest);
+ sp = rest;
+ break;
+ case OPLUS_:
+ stp = stop;
+ for (;;) {
+ /* how long could this one be? */
+ rest = slow(m, sp, stp, ss, es);
+ assert(rest != NULL); /* it did match */
+ /* could the rest match the rest? */
+ tail = slow(m, rest, stop, es, stopst);
+ if (tail == stop)
+ break; /* yes! */
+ /* no -- try a shorter match for this one */
+ stp = rest - 1;
+ assert(stp >= sp); /* it did work */
+ }
+ ssub = ss + 1;
+ esub = es - 1;
+ ssp = sp;
+ oldssp = ssp;
+ for (;;) { /* find last match of innards */
+ sep = slow(m, ssp, rest, ssub, esub);
+ if (sep == NULL || sep == ssp)
+ break; /* failed or matched null */
+ oldssp = ssp; /* on to next try */
+ ssp = sep;
+ }
+ if (sep == NULL) {
+ /* last successful match */
+ sep = ssp;
+ ssp = oldssp;
+ }
+ assert(sep == rest); /* must exhaust substring */
+ assert(slow(m, ssp, sep, ssub, esub) == rest);
+ dp = dissect(m, ssp, sep, ssub, esub);
+ assert(dp == sep);
+ sp = rest;
+ break;
+ case OCH_:
+ stp = stop;
+ for (;;) {
+ /* how long could this one be? */
+ rest = slow(m, sp, stp, ss, es);
+ assert(rest != NULL); /* it did match */
+ /* could the rest match the rest? */
+ tail = slow(m, rest, stop, es, stopst);
+ if (tail == stop)
+ break; /* yes! */
+ /* no -- try a shorter match for this one */
+ stp = rest - 1;
+ assert(stp >= sp); /* it did work */
+ }
+ ssub = ss + 1;
+ esub = ss + OPND(m->g->strip[ss]) - 1;
+ assert(OP(m->g->strip[esub]) == OOR1);
+ for (;;) { /* find first matching branch */
+ if (slow(m, sp, rest, ssub, esub) == rest)
+ break; /* it matched all of it */
+ /* that one missed, try next one */
+ assert(OP(m->g->strip[esub]) == OOR1);
+ esub++;
+ assert(OP(m->g->strip[esub]) == OOR2);
+ ssub = esub + 1;
+ esub += OPND(m->g->strip[esub]);
+ if (OP(m->g->strip[esub]) == OOR2)
+ esub--;
+ else
+ assert(OP(m->g->strip[esub]) == O_CH);
+ }
+ dp = dissect(m, sp, rest, ssub, esub);
+ assert(dp == rest);
+ sp = rest;
+ break;
+ case O_PLUS:
+ case O_QUEST:
+ case OOR1:
+ case OOR2:
+ case O_CH:
+ assert(nope);
+ break;
+ case OLPAREN:
+ i = OPND(m->g->strip[ss]);
+ assert(0 < i && i <= m->g->nsub);
+ m->pmatch[i].rm_so = sp - m->offp;
+ break;
+ case ORPAREN:
+ i = OPND(m->g->strip[ss]);
+ assert(0 < i && i <= m->g->nsub);
+ m->pmatch[i].rm_eo = sp - m->offp;
+ break;
+ default: /* uh oh */
+ assert(nope);
+ break;
+ }
+ }
+
+ assert(sp == stop);
+ return(sp);
+}
+
+/*
+ - backref - figure out what matched what, figuring in back references
+ == static char *backref(register struct match *m, char *start, \
+ == char *stop, sopno startst, sopno stopst, sopno lev);
+ */
+static char * /* == stop (success) or NULL (failure) */
+backref(m, start, stop, startst, stopst, lev)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+sopno lev; /* PLUS nesting level */
+{
+ register int i;
+ register sopno ss; /* start sop of current subRE */
+ register char *sp; /* start of string matched by it */
+ register sopno ssub; /* start sop of subsubRE */
+ register sopno esub; /* end sop of subsubRE */
+ register char *ssp; /* start of string matched by subsubRE */
+ register char *dp;
+ register size_t len;
+ register int hard;
+ register sop s;
+ register regoff_t offsave;
+ register cset *cs;
+
+ AT("back", start, stop, startst, stopst);
+ sp = start;
+
+ /* get as far as we can with easy stuff */
+ hard = 0;
+ for (ss = startst; !hard && ss < stopst; ss++)
+ switch (OP(s = m->g->strip[ss])) {
+ case OCHAR:
+ if (sp == stop || *sp++ != (char)OPND(s))
+ return(NULL);
+ break;
+ case OANY:
+ if (sp == stop)
+ return(NULL);
+ sp++;
+ break;
+ case OANYOF:
+ cs = &m->g->sets[OPND(s)];
+ if (sp == stop || !CHIN(cs, *sp++))
+ return(NULL);
+ break;
+ case OBOL:
+ if ( (sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
+ (sp < m->endp && *(sp-1) == '\n' &&
+ (m->g->cflags&REG_NEWLINE)) )
+ { /* yes */ }
+ else
+ return(NULL);
+ break;
+ case OEOL:
+ if ( (sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
+ (sp < m->endp && *sp == '\n' &&
+ (m->g->cflags&REG_NEWLINE)) )
+ { /* yes */ }
+ else
+ return(NULL);
+ break;
+ case OBOW:
+ if (( (sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
+ (sp < m->endp && *(sp-1) == '\n' &&
+ (m->g->cflags&REG_NEWLINE)) ||
+ (sp > m->beginp &&
+ !ISWORD(*(sp-1))) ) &&
+ (sp < m->endp && ISWORD(*sp)) )
+ { /* yes */ }
+ else
+ return(NULL);
+ break;
+ case OEOW:
+ if (( (sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
+ (sp < m->endp && *sp == '\n' &&
+ (m->g->cflags&REG_NEWLINE)) ||
+ (sp < m->endp && !ISWORD(*sp)) ) &&
+ (sp > m->beginp && ISWORD(*(sp-1))) )
+ { /* yes */ }
+ else
+ return(NULL);
+ break;
+ case O_QUEST:
+ break;
+ case OOR1: /* matches null but needs to skip */
+ ss++;
+ s = m->g->strip[ss];
+ do {
+ assert(OP(s) == OOR2);
+ ss += OPND(s);
+ } while (OP(s = m->g->strip[ss]) != O_CH);
+ /* note that the ss++ gets us past the O_CH */
+ break;
+ default: /* have to make a choice */
+ hard = 1;
+ break;
+ }
+ if (!hard) { /* that was it! */
+ if (sp != stop)
+ return(NULL);
+ return(sp);
+ }
+ ss--; /* adjust for the for's final increment */
+
+ /* the hard stuff */
+ AT("hard", sp, stop, ss, stopst);
+ s = m->g->strip[ss];
+ switch (OP(s)) {
+ case OBACK_: /* the vilest depths */
+ i = OPND(s);
+ assert(0 < i && i <= m->g->nsub);
+ if (m->pmatch[i].rm_eo == -1)
+ return(NULL);
+ assert(m->pmatch[i].rm_so != -1);
+ len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so;
+ assert(stop - m->beginp >= len);
+ if (sp > stop - len)
+ return(NULL); /* not enough left to match */
+ ssp = m->offp + m->pmatch[i].rm_so;
+ if (memcmp(sp, ssp, len) != 0)
+ return(NULL);
+ while (m->g->strip[ss] != SOP(O_BACK, i))
+ ss++;
+ return(backref(m, sp+len, stop, ss+1, stopst, lev));
+ break;
+ case OQUEST_: /* to null or not */
+ dp = backref(m, sp, stop, ss+1, stopst, lev);
+ if (dp != NULL)
+ return(dp); /* not */
+ return(backref(m, sp, stop, ss+OPND(s)+1, stopst, lev));
+ break;
+ case OPLUS_:
+ assert(m->lastpos != NULL);
+ assert(lev+1 <= m->g->nplus);
+ m->lastpos[lev+1] = sp;
+ return(backref(m, sp, stop, ss+1, stopst, lev+1));
+ break;
+ case O_PLUS:
+ if (sp == m->lastpos[lev]) /* last pass matched null */
+ return(backref(m, sp, stop, ss+1, stopst, lev-1));
+ /* try another pass */
+ m->lastpos[lev] = sp;
+ dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev);
+ if (dp == NULL)
+ return(backref(m, sp, stop, ss+1, stopst, lev-1));
+ else
+ return(dp);
+ break;
+ case OCH_: /* find the right one, if any */
+ ssub = ss + 1;
+ esub = ss + OPND(s) - 1;
+ assert(OP(m->g->strip[esub]) == OOR1);
+ for (;;) { /* find first matching branch */
+ dp = backref(m, sp, stop, ssub, esub, lev);
+ if (dp != NULL)
+ return(dp);
+ /* that one missed, try next one */
+ if (OP(m->g->strip[esub]) == O_CH)
+ return(NULL); /* there is none */
+ esub++;
+ assert(OP(m->g->strip[esub]) == OOR2);
+ ssub = esub + 1;
+ esub += OPND(m->g->strip[esub]);
+ if (OP(m->g->strip[esub]) == OOR2)
+ esub--;
+ else
+ assert(OP(m->g->strip[esub]) == O_CH);
+ }
+ break;
+ case OLPAREN: /* must undo assignment if rest fails */
+ i = OPND(s);
+ assert(0 < i && i <= m->g->nsub);
+ offsave = m->pmatch[i].rm_so;
+ m->pmatch[i].rm_so = sp - m->offp;
+ dp = backref(m, sp, stop, ss+1, stopst, lev);
+ if (dp != NULL)
+ return(dp);
+ m->pmatch[i].rm_so = offsave;
+ return(NULL);
+ break;
+ case ORPAREN: /* must undo assignment if rest fails */
+ i = OPND(s);
+ assert(0 < i && i <= m->g->nsub);
+ offsave = m->pmatch[i].rm_eo;
+ m->pmatch[i].rm_eo = sp - m->offp;
+ dp = backref(m, sp, stop, ss+1, stopst, lev);
+ if (dp != NULL)
+ return(dp);
+ m->pmatch[i].rm_eo = offsave;
+ return(NULL);
+ break;
+ default: /* uh oh */
+ assert(nope);
+ break;
+ }
+
+ /* "can't happen" */
+ assert(nope);
+ /* NOTREACHED */
+ return( NULL );
+}
+
+/*
+ - fast - step through the string at top speed
+ == static char *fast(register struct match *m, char *start, \
+ == char *stop, sopno startst, sopno stopst);
+ */
+static char * /* where tentative match ended, or NULL */
+fast(m, start, stop, startst, stopst)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+ register states st = m->st;
+ register states fresh = m->fresh;
+ register states tmp = m->tmp;
+ register char *p = start;
+ register int c = (start == m->beginp) ? OUT : *(start-1);
+ register int lastc; /* previous c */
+ register int flagch;
+ register int i;
+ register char *coldp; /* last p after which no match was underway */
+
+ CLEAR(st);
+ SET1(st, startst);
+ st = step(m->g, startst, stopst, st, NOTHING, st);
+ ASSIGN(fresh, st);
+ SP("start", st, *p);
+ coldp = NULL;
+ for (;;) {
+ /* next character */
+ lastc = c;
+ c = (p == m->endp) ? OUT : *p;
+ if (EQ(st, fresh))
+ coldp = p;
+
+ /* is there an EOL and/or BOL between lastc and c? */
+ flagch = '\0';
+ i = 0;
+ if ( (lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
+ (lastc == OUT && !(m->eflags&REG_NOTBOL)) ) {
+ flagch = BOL;
+ i = m->g->nbol;
+ }
+ if ( (c == '\n' && m->g->cflags&REG_NEWLINE) ||
+ (c == OUT && !(m->eflags&REG_NOTEOL)) ) {
+ flagch = (flagch == BOL) ? BOLEOL : EOL;
+ i += m->g->neol;
+ }
+ if (i != 0) {
+ for (; i > 0; i--)
+ st = step(m->g, startst, stopst, st, flagch, st);
+ SP("boleol", st, c);
+ }
+
+ /* how about a word boundary? */
+ if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) &&
+ (c != OUT && ISWORD(c)) ) {
+ flagch = BOW;
+ }
+ if ( (lastc != OUT && ISWORD(lastc)) &&
+ (flagch == EOL || (c != OUT && !ISWORD(c))) ) {
+ flagch = EOW;
+ }
+ if (flagch == BOW || flagch == EOW) {
+ st = step(m->g, startst, stopst, st, flagch, st);
+ SP("boweow", st, c);
+ }
+
+ /* are we done? */
+ if (ISSET(st, stopst) || p == stop)
+ break; /* NOTE BREAK OUT */
+
+ /* no, we must deal with this character */
+ ASSIGN(tmp, st);
+ ASSIGN(st, fresh);
+ assert(c != OUT);
+ st = step(m->g, startst, stopst, tmp, c, st);
+ SP("aft", st, c);
+ assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st));
+ p++;
+ }
+
+ assert(coldp != NULL);
+ m->coldp = coldp;
+ if (ISSET(st, stopst))
+ return(p+1);
+ else
+ return(NULL);
+}
+
+/*
+ - slow - step through the string more deliberately
+ == static char *slow(register struct match *m, char *start, \
+ == char *stop, sopno startst, sopno stopst);
+ */
+static char * /* where it ended */
+slow(m, start, stop, startst, stopst)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+ register states st = m->st;
+ register states empty = m->empty;
+ register states tmp = m->tmp;
+ register char *p = start;
+ register int c = (start == m->beginp) ? OUT : *(start-1);
+ register int lastc; /* previous c */
+ register int flagch;
+ register int i;
+ register char *matchp; /* last p at which a match ended */
+
+ AT("slow", start, stop, startst, stopst);
+ CLEAR(st);
+ SET1(st, startst);
+ SP("sstart", st, *p);
+ st = step(m->g, startst, stopst, st, NOTHING, st);
+ matchp = NULL;
+ for (;;) {
+ /* next character */
+ lastc = c;
+ c = (p == m->endp) ? OUT : *p;
+
+ /* is there an EOL and/or BOL between lastc and c? */
+ flagch = '\0';
+ i = 0;
+ if ( (lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
+ (lastc == OUT && !(m->eflags&REG_NOTBOL)) ) {
+ flagch = BOL;
+ i = m->g->nbol;
+ }
+ if ( (c == '\n' && m->g->cflags&REG_NEWLINE) ||
+ (c == OUT && !(m->eflags&REG_NOTEOL)) ) {
+ flagch = (flagch == BOL) ? BOLEOL : EOL;
+ i += m->g->neol;
+ }
+ if (i != 0) {
+ for (; i > 0; i--)
+ st = step(m->g, startst, stopst, st, flagch, st);
+ SP("sboleol", st, c);
+ }
+
+ /* how about a word boundary? */
+ if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) &&
+ (c != OUT && ISWORD(c)) ) {
+ flagch = BOW;
+ }
+ if ( (lastc != OUT && ISWORD(lastc)) &&
+ (flagch == EOL || (c != OUT && !ISWORD(c))) ) {
+ flagch = EOW;
+ }
+ if (flagch == BOW || flagch == EOW) {
+ st = step(m->g, startst, stopst, st, flagch, st);
+ SP("sboweow", st, c);
+ }
+
+ /* are we done? */
+ if (ISSET(st, stopst))
+ matchp = p;
+ if (EQ(st, empty) || p == stop)
+ break; /* NOTE BREAK OUT */
+
+ /* no, we must deal with this character */
+ ASSIGN(tmp, st);
+ ASSIGN(st, empty);
+ assert(c != OUT);
+ st = step(m->g, startst, stopst, tmp, c, st);
+ SP("saft", st, c);
+ assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st));
+ p++;
+ }
+
+ return(matchp);
+}
+
+
+/*
+ - step - map set of states reachable before char to set reachable after
+ == static states step(register struct re_guts *g, sopno start, sopno stop, \
+ == register states bef, int ch, register states aft);
+ == #define BOL (OUT+1)
+ == #define EOL (BOL+1)
+ == #define BOLEOL (BOL+2)
+ == #define NOTHING (BOL+3)
+ == #define BOW (BOL+4)
+ == #define EOW (BOL+5)
+ == #define CODEMAX (BOL+5) // highest code used
+ == #define NONCHAR(c) ((c) > CHAR_MAX)
+ == #define NNONCHAR (CODEMAX-CHAR_MAX)
+ */
+static states
+step(g, start, stop, bef, ch, aft)
+register struct re_guts *g;
+sopno start; /* start state within strip */
+sopno stop; /* state after stop state within strip */
+register states bef; /* states reachable before */
+int ch; /* character or NONCHAR code */
+register states aft; /* states already known reachable after */
+{
+ register cset *cs;
+ register sop s;
+ register sopno pc;
+ register onestate here; /* note, macros know this name */
+ register sopno look;
+ register int i;
+
+ for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) {
+ s = g->strip[pc];
+ switch (OP(s)) {
+ case OEND:
+ assert(pc == stop-1);
+ break;
+ case OCHAR:
+ /* only characters can match */
+ assert(!NONCHAR(ch) || ch != (char)OPND(s));
+ if (ch == (char)OPND(s))
+ FWD(aft, bef, 1);
+ break;
+ case OBOL:
+ if (ch == BOL || ch == BOLEOL)
+ FWD(aft, bef, 1);
+ break;
+ case OEOL:
+ if (ch == EOL || ch == BOLEOL)
+ FWD(aft, bef, 1);
+ break;
+ case OBOW:
+ if (ch == BOW)
+ FWD(aft, bef, 1);
+ break;
+ case OEOW:
+ if (ch == EOW)
+ FWD(aft, bef, 1);
+ break;
+ case OANY:
+ if (!NONCHAR(ch))
+ FWD(aft, bef, 1);
+ break;
+ case OANYOF:
+ cs = &g->sets[OPND(s)];
+ if (!NONCHAR(ch) && CHIN(cs, ch))
+ FWD(aft, bef, 1);
+ break;
+ case OBACK_: /* ignored here */
+ case O_BACK:
+ FWD(aft, aft, 1);
+ break;
+ case OPLUS_: /* forward, this is just an empty */
+ FWD(aft, aft, 1);
+ break;
+ case O_PLUS: /* both forward and back */
+ FWD(aft, aft, 1);
+ i = ISSETBACK(aft, OPND(s));
+ BACK(aft, aft, OPND(s));
+ if (!i && ISSETBACK(aft, OPND(s))) {
+ /* oho, must reconsider loop body */
+ pc -= OPND(s) + 1;
+ INIT(here, pc);
+ }
+ break;
+ case OQUEST_: /* two branches, both forward */
+ FWD(aft, aft, 1);
+ FWD(aft, aft, OPND(s));
+ break;
+ case O_QUEST: /* just an empty */
+ FWD(aft, aft, 1);
+ break;
+ case OLPAREN: /* not significant here */
+ case ORPAREN:
+ FWD(aft, aft, 1);
+ break;
+ case OCH_: /* mark the first two branches */
+ FWD(aft, aft, 1);
+ assert(OP(g->strip[pc+OPND(s)]) == OOR2);
+ FWD(aft, aft, OPND(s));
+ break;
+ case OOR1: /* done a branch, find the O_CH */
+ if (ISSTATEIN(aft, here)) {
+ for (look = 1;
+ OP(s = g->strip[pc+look]) != O_CH;
+ look += OPND(s))
+ assert(OP(s) == OOR2);
+ FWD(aft, aft, look);
+ }
+ break;
+ case OOR2: /* propagate OCH_'s marking */
+ FWD(aft, aft, 1);
+ if (OP(g->strip[pc+OPND(s)]) != O_CH) {
+ assert(OP(g->strip[pc+OPND(s)]) == OOR2);
+ FWD(aft, aft, OPND(s));
+ }
+ break;
+ case O_CH: /* just empty */
+ FWD(aft, aft, 1);
+ break;
+ default: /* ooooops... */
+ assert(nope);
+ break;
+ }
+ }
+
+ return(aft);
+}
+
+#ifdef REDEBUG
+/*
+ - print - print a set of states
+ == #ifdef REDEBUG
+ == static void print(struct match *m, char *caption, states st, \
+ == int ch, FILE *d);
+ == #endif
+ */
+static void
+print(m, caption, st, ch, d)
+struct match *m;
+char *caption;
+states st;
+int ch;
+FILE *d;
+{
+ register struct re_guts *g = m->g;
+ register int i;
+ register int first = 1;
+
+ if (!(m->eflags&REG_TRACE))
+ return;
+
+ fprintf(d, "%s", caption);
+ if (ch != '\0')
+ fprintf(d, " %s", pchar(ch));
+ for (i = 0; i < g->nstates; i++)
+ if (ISSET(st, i)) {
+ fprintf(d, "%s%d", (first) ? "\t" : ", ", i);
+ first = 0;
+ }
+ fprintf(d, "\n");
+}
+
+/*
+ - at - print current situation
+ == #ifdef REDEBUG
+ == static void at(struct match *m, char *title, char *start, char *stop, \
+ == sopno startst, sopno stopst);
+ == #endif
+ */
+static void
+at(m, title, start, stop, startst, stopst)
+struct match *m;
+char *title;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+ if (!(m->eflags&REG_TRACE))
+ return;
+
+ printf("%s %s-", title, pchar(*start));
+ printf("%s ", pchar(*stop));
+ printf("%ld-%ld\n", (long)startst, (long)stopst);
+}
+
+#ifndef PCHARDONE
+#define PCHARDONE /* never again */
+/*
+ - pchar - make a character printable
+ == #ifdef REDEBUG
+ == static char *pchar(int ch);
+ == #endif
+ *
+ * Is this identical to regchar() over in debug.c? Well, yes. But a
+ * duplicate here avoids having a debugging-capable regexec.o tied to
+ * a matching debug.o, and this is convenient. It all disappears in
+ * the non-debug compilation anyway, so it doesn't matter much.
+ */
+static char * /* -> representation */
+pchar(ch)
+int ch;
+{
+ static char pbuf[10];
+
+ if (isprint(ch) || ch == ' ')
+ sprintf(pbuf, "%c", ch);
+ else
+ sprintf(pbuf, "\\%o", ch);
+ return(pbuf);
+}
+#endif
+#endif
+
+#undef matcher
+#undef fast
+#undef slow
+#undef dissect
+#undef backref
+#undef step
+#undef print
+#undef at
+#undef match
diff --git a/usr.sbin/httpd/src/regex/main.c b/usr.sbin/httpd/src/regex/main.c
new file mode 100644
index 00000000000..657338a2c19
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/main.c
@@ -0,0 +1,510 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "main.ih"
+
+char *progname;
+int debug = 0;
+int line = 0;
+int status = 0;
+
+int copts = REG_EXTENDED;
+int eopts = 0;
+regoff_t startoff = 0;
+regoff_t endoff = 0;
+
+
+extern int split();
+extern void regprint();
+
+/*
+ - main - do the simple case, hand off to regress() for regression
+ */
+int main(argc, argv)
+int argc;
+char *argv[];
+{
+ regex_t re;
+# define NS 10
+ regmatch_t subs[NS];
+ char erbuf[100];
+ int err;
+ size_t len;
+ int c;
+ int errflg = 0;
+ register int i;
+ extern int optind;
+ extern char *optarg;
+
+ progname = argv[0];
+
+ while ((c = getopt(argc, argv, "c:e:S:E:x")) != EOF)
+ switch (c) {
+ case 'c': /* compile options */
+ copts = options('c', optarg);
+ break;
+ case 'e': /* execute options */
+ eopts = options('e', optarg);
+ break;
+ case 'S': /* start offset */
+ startoff = (regoff_t)atoi(optarg);
+ break;
+ case 'E': /* end offset */
+ endoff = (regoff_t)atoi(optarg);
+ break;
+ case 'x': /* Debugging. */
+ debug++;
+ break;
+ case '?':
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ fprintf(stderr, "usage: %s ", progname);
+ fprintf(stderr, "[-c copt][-C][-d] [re]\n");
+ exit(2);
+ }
+
+ if (optind >= argc) {
+ regress(stdin);
+ exit(status);
+ }
+
+ err = regcomp(&re, argv[optind++], copts);
+ if (err) {
+ len = regerror(err, &re, erbuf, sizeof(erbuf));
+ fprintf(stderr, "error %s, %d/%d `%s'\n",
+ eprint(err), len, sizeof(erbuf), erbuf);
+ exit(status);
+ }
+ regprint(&re, stdout);
+
+ if (optind >= argc) {
+ regfree(&re);
+ exit(status);
+ }
+
+ if (eopts&REG_STARTEND) {
+ subs[0].rm_so = startoff;
+ subs[0].rm_eo = strlen(argv[optind]) - endoff;
+ }
+ err = regexec(&re, argv[optind], (size_t)NS, subs, eopts);
+ if (err) {
+ len = regerror(err, &re, erbuf, sizeof(erbuf));
+ fprintf(stderr, "error %s, %d/%d `%s'\n",
+ eprint(err), len, sizeof(erbuf), erbuf);
+ exit(status);
+ }
+ if (!(copts&REG_NOSUB)) {
+ len = (int)(subs[0].rm_eo - subs[0].rm_so);
+ if (subs[0].rm_so != -1) {
+ if (len != 0)
+ printf("match `%.*s'\n", (int)len,
+ argv[optind] + subs[0].rm_so);
+ else
+ printf("match `'@%.1s\n",
+ argv[optind] + subs[0].rm_so);
+ }
+ for (i = 1; i < NS; i++)
+ if (subs[i].rm_so != -1)
+ printf("(%d) `%.*s'\n", i,
+ (int)(subs[i].rm_eo - subs[i].rm_so),
+ argv[optind] + subs[i].rm_so);
+ }
+ exit(status);
+}
+
+/*
+ - regress - main loop of regression test
+ == void regress(FILE *in);
+ */
+void
+regress(in)
+FILE *in;
+{
+ char inbuf[1000];
+# define MAXF 10
+ char *f[MAXF];
+ int nf;
+ int i;
+ char erbuf[100];
+ size_t ne;
+ char *badpat = "invalid regular expression";
+# define SHORT 10
+ char *bpname = "REG_BADPAT";
+ regex_t re;
+
+ while (fgets(inbuf, sizeof(inbuf), in) != NULL) {
+ line++;
+ if (inbuf[0] == '#' || inbuf[0] == '\n')
+ continue; /* NOTE CONTINUE */
+ inbuf[strlen(inbuf)-1] = '\0'; /* get rid of stupid \n */
+ if (debug)
+ fprintf(stdout, "%d:\n", line);
+ nf = split(inbuf, f, MAXF, "\t\t");
+ if (nf < 3) {
+ fprintf(stderr, "bad input, line %d\n", line);
+ exit(1);
+ }
+ for (i = 0; i < nf; i++)
+ if (strcmp(f[i], "\"\"") == 0)
+ f[i] = "";
+ if (nf <= 3)
+ f[3] = NULL;
+ if (nf <= 4)
+ f[4] = NULL;
+ try(f[0], f[1], f[2], f[3], f[4], options('c', f[1]));
+ if (opt('&', f[1])) /* try with either type of RE */
+ try(f[0], f[1], f[2], f[3], f[4],
+ options('c', f[1]) &~ REG_EXTENDED);
+ }
+
+ ne = regerror(REG_BADPAT, (regex_t *)NULL, erbuf, sizeof(erbuf));
+ if (strcmp(erbuf, badpat) != 0 || ne != strlen(badpat)+1) {
+ fprintf(stderr, "end: regerror() test gave `%s' not `%s'\n",
+ erbuf, badpat);
+ status = 1;
+ }
+ ne = regerror(REG_BADPAT, (regex_t *)NULL, erbuf, (size_t)SHORT);
+ if (strncmp(erbuf, badpat, SHORT-1) != 0 || erbuf[SHORT-1] != '\0' ||
+ ne != strlen(badpat)+1) {
+ fprintf(stderr, "end: regerror() short test gave `%s' not `%.*s'\n",
+ erbuf, SHORT-1, badpat);
+ status = 1;
+ }
+ ne = regerror(REG_ITOA|REG_BADPAT, (regex_t *)NULL, erbuf, sizeof(erbuf));
+ if (strcmp(erbuf, bpname) != 0 || ne != strlen(bpname)+1) {
+ fprintf(stderr, "end: regerror() ITOA test gave `%s' not `%s'\n",
+ erbuf, bpname);
+ status = 1;
+ }
+ re.re_endp = bpname;
+ ne = regerror(REG_ATOI, &re, erbuf, sizeof(erbuf));
+ if (atoi(erbuf) != (int)REG_BADPAT) {
+ fprintf(stderr, "end: regerror() ATOI test gave `%s' not `%ld'\n",
+ erbuf, (long)REG_BADPAT);
+ status = 1;
+ } else if (ne != strlen(erbuf)+1) {
+ fprintf(stderr, "end: regerror() ATOI test len(`%s') = %ld\n",
+ erbuf, (long)REG_BADPAT);
+ status = 1;
+ }
+}
+
+/*
+ - try - try it, and report on problems
+ == void try(char *f0, char *f1, char *f2, char *f3, char *f4, int opts);
+ */
+void
+try(f0, f1, f2, f3, f4, opts)
+char *f0;
+char *f1;
+char *f2;
+char *f3;
+char *f4;
+int opts; /* may not match f1 */
+{
+ regex_t re;
+# define NSUBS 10
+ regmatch_t subs[NSUBS];
+# define NSHOULD 15
+ char *should[NSHOULD];
+ int nshould;
+ char erbuf[100];
+ int err;
+ int len;
+ char *type = (opts & REG_EXTENDED) ? "ERE" : "BRE";
+ register int i;
+ char *grump;
+ char f0copy[1000];
+ char f2copy[1000];
+
+ strcpy(f0copy, f0);
+ re.re_endp = (opts&REG_PEND) ? f0copy + strlen(f0copy) : NULL;
+ fixstr(f0copy);
+ err = regcomp(&re, f0copy, opts);
+ if (err != 0 && (!opt('C', f1) || err != efind(f2))) {
+ /* unexpected error or wrong error */
+ len = regerror(err, &re, erbuf, sizeof(erbuf));
+ fprintf(stderr, "%d: %s error %s, %d/%d `%s'\n",
+ line, type, eprint(err), len,
+ sizeof(erbuf), erbuf);
+ status = 1;
+ } else if (err == 0 && opt('C', f1)) {
+ /* unexpected success */
+ fprintf(stderr, "%d: %s should have given REG_%s\n",
+ line, type, f2);
+ status = 1;
+ err = 1; /* so we won't try regexec */
+ }
+
+ if (err != 0) {
+ regfree(&re);
+ return;
+ }
+
+ strcpy(f2copy, f2);
+ fixstr(f2copy);
+
+ if (options('e', f1)&REG_STARTEND) {
+ if (strchr(f2, '(') == NULL || strchr(f2, ')') == NULL)
+ fprintf(stderr, "%d: bad STARTEND syntax\n", line);
+ subs[0].rm_so = strchr(f2, '(') - f2 + 1;
+ subs[0].rm_eo = strchr(f2, ')') - f2;
+ }
+ err = regexec(&re, f2copy, NSUBS, subs, options('e', f1));
+
+ if (err != 0 && (f3 != NULL || err != REG_NOMATCH)) {
+ /* unexpected error or wrong error */
+ len = regerror(err, &re, erbuf, sizeof(erbuf));
+ fprintf(stderr, "%d: %s exec error %s, %d/%d `%s'\n",
+ line, type, eprint(err), len,
+ sizeof(erbuf), erbuf);
+ status = 1;
+ } else if (err != 0) {
+ /* nothing more to check */
+ } else if (f3 == NULL) {
+ /* unexpected success */
+ fprintf(stderr, "%d: %s exec should have failed\n",
+ line, type);
+ status = 1;
+ err = 1; /* just on principle */
+ } else if (opts&REG_NOSUB) {
+ /* nothing more to check */
+ } else if ((grump = check(f2, subs[0], f3)) != NULL) {
+ fprintf(stderr, "%d: %s %s\n", line, type, grump);
+ status = 1;
+ err = 1;
+ }
+
+ if (err != 0 || f4 == NULL) {
+ regfree(&re);
+ return;
+ }
+
+ for (i = 1; i < NSHOULD; i++)
+ should[i] = NULL;
+ nshould = split(f4, should+1, NSHOULD-1, ",");
+ if (nshould == 0) {
+ nshould = 1;
+ should[1] = "";
+ }
+ for (i = 1; i < NSUBS; i++) {
+ grump = check(f2, subs[i], should[i]);
+ if (grump != NULL) {
+ fprintf(stderr, "%d: %s $%d %s\n", line,
+ type, i, grump);
+ status = 1;
+ err = 1;
+ }
+ }
+
+ regfree(&re);
+}
+
+/*
+ - options - pick options out of a regression-test string
+ == int options(int type, char *s);
+ */
+int
+options(type, s)
+int type; /* 'c' compile, 'e' exec */
+char *s;
+{
+ register char *p;
+ register int o = (type == 'c') ? copts : eopts;
+ register char *legal = (type == 'c') ? "bisnmp" : "^$#tl";
+
+ for (p = s; *p != '\0'; p++)
+ if (strchr(legal, *p) != NULL)
+ switch (*p) {
+ case 'b':
+ o &= ~REG_EXTENDED;
+ break;
+ case 'i':
+ o |= REG_ICASE;
+ break;
+ case 's':
+ o |= REG_NOSUB;
+ break;
+ case 'n':
+ o |= REG_NEWLINE;
+ break;
+ case 'm':
+ o &= ~REG_EXTENDED;
+ o |= REG_NOSPEC;
+ break;
+ case 'p':
+ o |= REG_PEND;
+ break;
+ case '^':
+ o |= REG_NOTBOL;
+ break;
+ case '$':
+ o |= REG_NOTEOL;
+ break;
+ case '#':
+ o |= REG_STARTEND;
+ break;
+ case 't': /* trace */
+ o |= REG_TRACE;
+ break;
+ case 'l': /* force long representation */
+ o |= REG_LARGE;
+ break;
+ case 'r': /* force backref use */
+ o |= REG_BACKR;
+ break;
+ }
+ return(o);
+}
+
+/*
+ - opt - is a particular option in a regression string?
+ == int opt(int c, char *s);
+ */
+int /* predicate */
+opt(c, s)
+int c;
+char *s;
+{
+ return(strchr(s, c) != NULL);
+}
+
+/*
+ - fixstr - transform magic characters in strings
+ == void fixstr(register char *p);
+ */
+void
+fixstr(p)
+register char *p;
+{
+ if (p == NULL)
+ return;
+
+ for (; *p != '\0'; p++)
+ if (*p == 'N')
+ *p = '\n';
+ else if (*p == 'T')
+ *p = '\t';
+ else if (*p == 'S')
+ *p = ' ';
+ else if (*p == 'Z')
+ *p = '\0';
+}
+
+/*
+ - check - check a substring match
+ == char *check(char *str, regmatch_t sub, char *should);
+ */
+char * /* NULL or complaint */
+check(str, sub, should)
+char *str;
+regmatch_t sub;
+char *should;
+{
+ register int len;
+ register int shlen;
+ register char *p;
+ static char grump[500];
+ register char *at = NULL;
+
+ if (should != NULL && strcmp(should, "-") == 0)
+ should = NULL;
+ if (should != NULL && should[0] == '@') {
+ at = should + 1;
+ should = "";
+ }
+
+ /* check rm_so and rm_eo for consistency */
+ if (sub.rm_so > sub.rm_eo || (sub.rm_so == -1 && sub.rm_eo != -1) ||
+ (sub.rm_so != -1 && sub.rm_eo == -1) ||
+ (sub.rm_so != -1 && sub.rm_so < 0) ||
+ (sub.rm_eo != -1 && sub.rm_eo < 0) ) {
+ sprintf(grump, "start %ld end %ld", (long)sub.rm_so,
+ (long)sub.rm_eo);
+ return(grump);
+ }
+
+ /* check for no match */
+ if (sub.rm_so == -1 && should == NULL)
+ return(NULL);
+ if (sub.rm_so == -1)
+ return("did not match");
+
+ /* check for in range */
+ if (sub.rm_eo > strlen(str)) {
+ sprintf(grump, "start %ld end %ld, past end of string",
+ (long)sub.rm_so, (long)sub.rm_eo);
+ return(grump);
+ }
+
+ len = (int)(sub.rm_eo - sub.rm_so);
+ shlen = (int)strlen(should);
+ p = str + sub.rm_so;
+
+ /* check for not supposed to match */
+ if (should == NULL) {
+ sprintf(grump, "matched `%.*s'", len, p);
+ return(grump);
+ }
+
+ /* check for wrong match */
+ if (len != shlen || strncmp(p, should, (size_t)shlen) != 0) {
+ sprintf(grump, "matched `%.*s' instead", len, p);
+ return(grump);
+ }
+ if (shlen > 0)
+ return(NULL);
+
+ /* check null match in right place */
+ if (at == NULL)
+ return(NULL);
+ shlen = strlen(at);
+ if (shlen == 0)
+ shlen = 1; /* force check for end-of-string */
+ if (strncmp(p, at, shlen) != 0) {
+ sprintf(grump, "matched null at `%.20s'", p);
+ return(grump);
+ }
+ return(NULL);
+}
+
+/*
+ - eprint - convert error number to name
+ == static char *eprint(int err);
+ */
+static char *
+eprint(err)
+int err;
+{
+ static char epbuf[100];
+ size_t len;
+
+ len = regerror(REG_ITOA|err, (regex_t *)NULL, epbuf, sizeof(epbuf));
+ assert(len <= sizeof(epbuf));
+ return(epbuf);
+}
+
+/*
+ - efind - convert error name to number
+ == static int efind(char *name);
+ */
+static int
+efind(name)
+char *name;
+{
+ static char efbuf[100];
+ regex_t re;
+
+ sprintf(efbuf, "REG_%s", name);
+ assert(strlen(efbuf) < sizeof(efbuf));
+ re.re_endp = efbuf;
+ (void) regerror(REG_ATOI, &re, efbuf, sizeof(efbuf));
+ return(atoi(efbuf));
+}
diff --git a/usr.sbin/httpd/src/regex/mkh b/usr.sbin/httpd/src/regex/mkh
new file mode 100644
index 00000000000..252b246c7bd
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/mkh
@@ -0,0 +1,76 @@
+#! /bin/sh
+# mkh - pull headers out of C source
+PATH=/bin:/usr/bin ; export PATH
+
+# egrep pattern to pick out marked lines
+egrep='^ =([ ]|$)'
+
+# Sed program to process marked lines into lines for the header file.
+# The markers have already been removed. Two things are done here: removal
+# of backslashed newlines, and some fudging of comments. The first is done
+# because -o needs to have prototypes on one line to strip them down.
+# Getting comments into the output is tricky; we turn C++-style // comments
+# into /* */ comments, after altering any existing */'s to avoid trouble.
+peel=' /\\$/N
+ /\\\n[ ]*/s///g
+ /\/\//s;\*/;* /;g
+ /\/\//s;//\(.*\);/*\1 */;'
+
+for a
+do
+ case "$a" in
+ -o) # old (pre-function-prototype) compiler
+ # add code to comment out argument lists
+ peel="$peel
+ "'/^\([^#\/][^\/]*[a-zA-Z0-9_)]\)(\(.*\))/s;;\1(/*\2*/);'
+ shift
+ ;;
+ -b) # funny Berkeley __P macro
+ peel="$peel
+ "'/^\([^#\/][^\/]*[a-zA-Z0-9_)]\)(\(.*\))/s;;\1 __P((\2));'
+ shift
+ ;;
+ -s) # compiler doesn't like `static foo();'
+ # add code to get rid of the `static'
+ peel="$peel
+ "'/^static[ ][^\/]*[a-zA-Z0-9_)](.*)/s;static.;;'
+ shift
+ ;;
+ -p) # private declarations
+ egrep='^ ==([ ]|$)'
+ shift
+ ;;
+ -i) # wrap in #ifndef, argument is name
+ ifndef="$2"
+ shift ; shift
+ ;;
+ *) break
+ ;;
+ esac
+done
+
+if test " $ifndef" != " "
+then
+ echo "#ifndef $ifndef"
+ echo "#define $ifndef /* never again */"
+fi
+echo "/* ========= begin header generated by $0 ========= */"
+echo '#ifdef __cplusplus'
+echo 'extern "C" {'
+echo '#endif'
+for f
+do
+ echo
+ echo "/* === $f === */"
+ egrep "$egrep" $f | sed 's/^ ==*[ ]//;s/^ ==*$//' | sed "$peel"
+ echo
+done
+echo '#ifdef __cplusplus'
+echo '}'
+echo '#endif'
+echo "/* ========= end header generated by $0 ========= */"
+if test " $ifndef" != " "
+then
+ echo "#endif"
+fi
+exit 0
diff --git a/usr.sbin/httpd/src/regex/regcomp.c b/usr.sbin/httpd/src/regex/regcomp.c
new file mode 100644
index 00000000000..c3a7b1ae1fe
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/regcomp.c
@@ -0,0 +1,1546 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regex2.h"
+
+#include "cclass.h"
+#include "cname.h"
+
+/*
+ * parse structure, passed up and down to avoid global variables and
+ * other clumsinesses
+ */
+struct parse {
+ char *next; /* next character in RE */
+ char *end; /* end of string (-> NUL normally) */
+ int error; /* has an error been seen? */
+ sop *strip; /* malloced strip */
+ sopno ssize; /* malloced strip size (allocated) */
+ sopno slen; /* malloced strip length (used) */
+ int ncsalloc; /* number of csets allocated */
+ struct re_guts *g;
+# define NPAREN 10 /* we need to remember () 1-9 for back refs */
+ sopno pbegin[NPAREN]; /* -> ( ([0] unused) */
+ sopno pend[NPAREN]; /* -> ) ([0] unused) */
+};
+
+#include "regcomp.ih"
+
+static char nuls[10]; /* place to point scanner in event of error */
+
+/*
+ * macros for use with parse structure
+ * BEWARE: these know that the parse structure is named `p' !!!
+ */
+#define PEEK() (*p->next)
+#define PEEK2() (*(p->next+1))
+#define MORE() (p->next < p->end)
+#define MORE2() (p->next+1 < p->end)
+#define SEE(c) (MORE() && PEEK() == (c))
+#define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b))
+#define EAT(c) ((SEE(c)) ? (NEXT1(), 1) : 0)
+#define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0)
+#define NEXT1() (p->next++)
+#define NEXT2() (p->next += 2)
+#define NEXTn(n) (p->next += (n))
+#define GETNEXT() (*p->next++)
+#define SETERROR(e) seterr(p, (e))
+#define REQUIRE(co, e) ((void)((co) || SETERROR(e)))
+#define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e))
+#define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e))
+#define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e))
+#define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd))
+#define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos)
+#define AHEAD(pos) dofwd(p, pos, HERE()-(pos))
+#define ASTERN(sop, pos) EMIT(sop, HERE()-pos)
+#define HERE() (p->slen)
+#define THERE() (p->slen - 1)
+#define THERETHERE() (p->slen - 2)
+#define DROP(n) (p->slen -= (n))
+
+#ifndef NDEBUG
+static int never = 0; /* for use in asserts; shuts lint up */
+#else
+#define never 0 /* some <assert.h>s have bugs too */
+#endif
+
+/*
+ - regcomp - interface for parser and compilation
+ = extern int regcomp(regex_t *, const char *, int);
+ = #define REG_BASIC 0000
+ = #define REG_EXTENDED 0001
+ = #define REG_ICASE 0002
+ = #define REG_NOSUB 0004
+ = #define REG_NEWLINE 0010
+ = #define REG_NOSPEC 0020
+ = #define REG_PEND 0040
+ = #define REG_DUMP 0200
+ */
+int /* 0 success, otherwise REG_something */
+regcomp(preg, pattern, cflags)
+regex_t *preg;
+const char *pattern;
+int cflags;
+{
+ struct parse pa;
+ register struct re_guts *g;
+ register struct parse *p = &pa;
+ register int i;
+ register size_t len;
+#ifdef REDEBUG
+# define GOODFLAGS(f) (f)
+#else
+# define GOODFLAGS(f) ((f)&~REG_DUMP)
+#endif
+
+ cflags = GOODFLAGS(cflags);
+ if ((cflags&REG_EXTENDED) && (cflags&REG_NOSPEC))
+ return(REG_INVARG);
+
+ if (cflags&REG_PEND) {
+ if (preg->re_endp < pattern)
+ return(REG_INVARG);
+ len = preg->re_endp - pattern;
+ } else
+ len = strlen((char *)pattern);
+
+ /* do the mallocs early so failure handling is easy */
+ g = (struct re_guts *)malloc(sizeof(struct re_guts) +
+ (NC-1)*sizeof(cat_t));
+ if (g == NULL)
+ return(REG_ESPACE);
+ p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */
+ p->strip = (sop *)malloc(p->ssize * sizeof(sop));
+ p->slen = 0;
+ if (p->strip == NULL) {
+ free((char *)g);
+ return(REG_ESPACE);
+ }
+
+ /* set things up */
+ p->g = g;
+ p->next = (char *)pattern; /* convenience; we do not modify it */
+ p->end = p->next + len;
+ p->error = 0;
+ p->ncsalloc = 0;
+ for (i = 0; i < NPAREN; i++) {
+ p->pbegin[i] = 0;
+ p->pend[i] = 0;
+ }
+ g->csetsize = NC;
+ g->sets = NULL;
+ g->setbits = NULL;
+ g->ncsets = 0;
+ g->cflags = cflags;
+ g->iflags = 0;
+ g->nbol = 0;
+ g->neol = 0;
+ g->must = NULL;
+ g->mlen = 0;
+ g->nsub = 0;
+ g->ncategories = 1; /* category 0 is "everything else" */
+ g->categories = &g->catspace[-(CHAR_MIN)];
+ (void) memset((char *)g->catspace, 0, NC*sizeof(cat_t));
+ g->backrefs = 0;
+
+ /* do it */
+ EMIT(OEND, 0);
+ g->firststate = THERE();
+ if (cflags&REG_EXTENDED)
+ p_ere(p, OUT);
+ else if (cflags&REG_NOSPEC)
+ p_str(p);
+ else
+ p_bre(p, OUT, OUT);
+ EMIT(OEND, 0);
+ g->laststate = THERE();
+
+ /* tidy up loose ends and fill things in */
+ categorize(p, g);
+ stripsnug(p, g);
+ findmust(p, g);
+ g->nplus = pluscount(p, g);
+ g->magic = MAGIC2;
+ preg->re_nsub = g->nsub;
+ preg->re_g = g;
+ preg->re_magic = MAGIC1;
+#ifndef REDEBUG
+ /* not debugging, so can't rely on the assert() in regexec() */
+ if (g->iflags&BAD)
+ SETERROR(REG_ASSERT);
+#endif
+
+ /* win or lose, we're done */
+ if (p->error != 0) /* lose */
+ regfree(preg);
+ return(p->error);
+}
+
+/*
+ - p_ere - ERE parser top level, concatenation and alternation
+ == static void p_ere(register struct parse *p, int stop);
+ */
+static void
+p_ere(p, stop)
+register struct parse *p;
+int stop; /* character this ERE should end at */
+{
+ register char c;
+ register sopno prevback = 0;
+ register sopno prevfwd = 0;
+ register sopno conc;
+ register int first = 1; /* is this the first alternative? */
+
+ for (;;) {
+ /* do a bunch of concatenated expressions */
+ conc = HERE();
+ while (MORE() && (c = PEEK()) != '|' && c != stop)
+ p_ere_exp(p);
+ REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */
+
+ if (!EAT('|'))
+ break; /* NOTE BREAK OUT */
+
+ if (first) {
+ INSERT(OCH_, conc); /* offset is wrong */
+ prevfwd = conc;
+ prevback = conc;
+ first = 0;
+ }
+ ASTERN(OOR1, prevback);
+ prevback = THERE();
+ AHEAD(prevfwd); /* fix previous offset */
+ prevfwd = HERE();
+ EMIT(OOR2, 0); /* offset is very wrong */
+ }
+
+ if (!first) { /* tail-end fixups */
+ AHEAD(prevfwd);
+ ASTERN(O_CH, prevback);
+ }
+
+ assert(!MORE() || SEE(stop));
+}
+
+/*
+ - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op
+ == static void p_ere_exp(register struct parse *p);
+ */
+static void
+p_ere_exp(p)
+register struct parse *p;
+{
+ register char c;
+ register sopno pos;
+ register int count;
+ register int count2;
+ register sopno subno;
+ int wascaret = 0;
+
+ assert(MORE()); /* caller should have ensured this */
+ c = GETNEXT();
+
+ pos = HERE();
+ switch (c) {
+ case '(':
+ REQUIRE(MORE(), REG_EPAREN);
+ p->g->nsub++;
+ subno = p->g->nsub;
+ if (subno < NPAREN)
+ p->pbegin[subno] = HERE();
+ EMIT(OLPAREN, subno);
+ if (!SEE(')'))
+ p_ere(p, ')');
+ if (subno < NPAREN) {
+ p->pend[subno] = HERE();
+ assert(p->pend[subno] != 0);
+ }
+ EMIT(ORPAREN, subno);
+ MUSTEAT(')', REG_EPAREN);
+ break;
+#ifndef POSIX_MISTAKE
+ case ')': /* happens only if no current unmatched ( */
+ /*
+ * You may ask, why the ifndef? Because I didn't notice
+ * this until slightly too late for 1003.2, and none of the
+ * other 1003.2 regular-expression reviewers noticed it at
+ * all. So an unmatched ) is legal POSIX, at least until
+ * we can get it fixed.
+ */
+ SETERROR(REG_EPAREN);
+ break;
+#endif
+ case '^':
+ EMIT(OBOL, 0);
+ p->g->iflags |= USEBOL;
+ p->g->nbol++;
+ wascaret = 1;
+ break;
+ case '$':
+ EMIT(OEOL, 0);
+ p->g->iflags |= USEEOL;
+ p->g->neol++;
+ break;
+ case '|':
+ SETERROR(REG_EMPTY);
+ break;
+ case '*':
+ case '+':
+ case '?':
+ SETERROR(REG_BADRPT);
+ break;
+ case '.':
+ if (p->g->cflags&REG_NEWLINE)
+ nonnewline(p);
+ else
+ EMIT(OANY, 0);
+ break;
+ case '[':
+ p_bracket(p);
+ break;
+ case '\\':
+ REQUIRE(MORE(), REG_EESCAPE);
+ c = GETNEXT();
+ ordinary(p, c);
+ break;
+ case '{': /* okay as ordinary except if digit follows */
+ REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT);
+ /* FALLTHROUGH */
+ default:
+ ordinary(p, c);
+ break;
+ }
+
+ if (!MORE())
+ return;
+ c = PEEK();
+ /* we call { a repetition if followed by a digit */
+ if (!( c == '*' || c == '+' || c == '?' ||
+ (c == '{' && MORE2() && isdigit(PEEK2())) ))
+ return; /* no repetition, we're done */
+ NEXT1();
+
+ REQUIRE(!wascaret, REG_BADRPT);
+ switch (c) {
+ case '*': /* implemented as +? */
+ /* this case does not require the (y|) trick, noKLUDGE */
+ INSERT(OPLUS_, pos);
+ ASTERN(O_PLUS, pos);
+ INSERT(OQUEST_, pos);
+ ASTERN(O_QUEST, pos);
+ break;
+ case '+':
+ INSERT(OPLUS_, pos);
+ ASTERN(O_PLUS, pos);
+ break;
+ case '?':
+ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+ INSERT(OCH_, pos); /* offset slightly wrong */
+ ASTERN(OOR1, pos); /* this one's right */
+ AHEAD(pos); /* fix the OCH_ */
+ EMIT(OOR2, 0); /* offset very wrong... */
+ AHEAD(THERE()); /* ...so fix it */
+ ASTERN(O_CH, THERETHERE());
+ break;
+ case '{':
+ count = p_count(p);
+ if (EAT(',')) {
+ if (isdigit(PEEK())) {
+ count2 = p_count(p);
+ REQUIRE(count <= count2, REG_BADBR);
+ } else /* single number with comma */
+ count2 = INFINITY;
+ } else /* just a single number */
+ count2 = count;
+ repeat(p, pos, count, count2);
+ if (!EAT('}')) { /* error heuristics */
+ while (MORE() && PEEK() != '}')
+ NEXT1();
+ REQUIRE(MORE(), REG_EBRACE);
+ SETERROR(REG_BADBR);
+ }
+ break;
+ }
+
+ if (!MORE())
+ return;
+ c = PEEK();
+ if (!( c == '*' || c == '+' || c == '?' ||
+ (c == '{' && MORE2() && isdigit(PEEK2())) ) )
+ return;
+ SETERROR(REG_BADRPT);
+}
+
+/*
+ - p_str - string (no metacharacters) "parser"
+ == static void p_str(register struct parse *p);
+ */
+static void
+p_str(p)
+register struct parse *p;
+{
+ REQUIRE(MORE(), REG_EMPTY);
+ while (MORE())
+ ordinary(p, GETNEXT());
+}
+
+/*
+ - p_bre - BRE parser top level, anchoring and concatenation
+ == static void p_bre(register struct parse *p, register int end1, \
+ == register int end2);
+ * Giving end1 as OUT essentially eliminates the end1/end2 check.
+ *
+ * This implementation is a bit of a kludge, in that a trailing $ is first
+ * taken as an ordinary character and then revised to be an anchor. The
+ * only undesirable side effect is that '$' gets included as a character
+ * category in such cases. This is fairly harmless; not worth fixing.
+ * The amount of lookahead needed to avoid this kludge is excessive.
+ */
+static void
+p_bre(p, end1, end2)
+register struct parse *p;
+register int end1; /* first terminating character */
+register int end2; /* second terminating character */
+{
+ register sopno start = HERE();
+ register int first = 1; /* first subexpression? */
+ register int wasdollar = 0;
+
+ if (EAT('^')) {
+ EMIT(OBOL, 0);
+ p->g->iflags |= USEBOL;
+ p->g->nbol++;
+ }
+ while (MORE() && !SEETWO(end1, end2)) {
+ wasdollar = p_simp_re(p, first);
+ first = 0;
+ }
+ if (wasdollar) { /* oops, that was a trailing anchor */
+ DROP(1);
+ EMIT(OEOL, 0);
+ p->g->iflags |= USEEOL;
+ p->g->neol++;
+ }
+
+ REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */
+}
+
+/*
+ - p_simp_re - parse a simple RE, an atom possibly followed by a repetition
+ == static int p_simp_re(register struct parse *p, int starordinary);
+ */
+static int /* was the simple RE an unbackslashed $? */
+p_simp_re(p, starordinary)
+register struct parse *p;
+int starordinary; /* is a leading * an ordinary character? */
+{
+ register int c;
+ register int count;
+ register int count2;
+ register sopno pos;
+ register int i;
+ register sopno subno;
+# define BACKSL (1<<CHAR_BIT)
+
+ pos = HERE(); /* repetion op, if any, covers from here */
+
+ assert(MORE()); /* caller should have ensured this */
+ c = GETNEXT();
+ if (c == '\\') {
+ REQUIRE(MORE(), REG_EESCAPE);
+ c = BACKSL | (unsigned char)GETNEXT();
+ }
+ switch (c) {
+ case '.':
+ if (p->g->cflags&REG_NEWLINE)
+ nonnewline(p);
+ else
+ EMIT(OANY, 0);
+ break;
+ case '[':
+ p_bracket(p);
+ break;
+ case BACKSL|'{':
+ SETERROR(REG_BADRPT);
+ break;
+ case BACKSL|'(':
+ p->g->nsub++;
+ subno = p->g->nsub;
+ if (subno < NPAREN)
+ p->pbegin[subno] = HERE();
+ EMIT(OLPAREN, subno);
+ /* the MORE here is an error heuristic */
+ if (MORE() && !SEETWO('\\', ')'))
+ p_bre(p, '\\', ')');
+ if (subno < NPAREN) {
+ p->pend[subno] = HERE();
+ assert(p->pend[subno] != 0);
+ }
+ EMIT(ORPAREN, subno);
+ REQUIRE(EATTWO('\\', ')'), REG_EPAREN);
+ break;
+ case BACKSL|')': /* should not get here -- must be user */
+ case BACKSL|'}':
+ SETERROR(REG_EPAREN);
+ break;
+ case BACKSL|'1':
+ case BACKSL|'2':
+ case BACKSL|'3':
+ case BACKSL|'4':
+ case BACKSL|'5':
+ case BACKSL|'6':
+ case BACKSL|'7':
+ case BACKSL|'8':
+ case BACKSL|'9':
+ i = (c&~BACKSL) - '0';
+ assert(i < NPAREN);
+ if (p->pend[i] != 0) {
+ assert(i <= p->g->nsub);
+ EMIT(OBACK_, i);
+ assert(p->pbegin[i] != 0);
+ assert(OP(p->strip[p->pbegin[i]]) == OLPAREN);
+ assert(OP(p->strip[p->pend[i]]) == ORPAREN);
+ (void) dupl(p, p->pbegin[i]+1, p->pend[i]);
+ EMIT(O_BACK, i);
+ } else
+ SETERROR(REG_ESUBREG);
+ p->g->backrefs = 1;
+ break;
+ case '*':
+ REQUIRE(starordinary, REG_BADRPT);
+ /* FALLTHROUGH */
+ default:
+ ordinary(p, c &~ BACKSL);
+ break;
+ }
+
+ if (EAT('*')) { /* implemented as +? */
+ /* this case does not require the (y|) trick, noKLUDGE */
+ INSERT(OPLUS_, pos);
+ ASTERN(O_PLUS, pos);
+ INSERT(OQUEST_, pos);
+ ASTERN(O_QUEST, pos);
+ } else if (EATTWO('\\', '{')) {
+ count = p_count(p);
+ if (EAT(',')) {
+ if (MORE() && isdigit(PEEK())) {
+ count2 = p_count(p);
+ REQUIRE(count <= count2, REG_BADBR);
+ } else /* single number with comma */
+ count2 = INFINITY;
+ } else /* just a single number */
+ count2 = count;
+ repeat(p, pos, count, count2);
+ if (!EATTWO('\\', '}')) { /* error heuristics */
+ while (MORE() && !SEETWO('\\', '}'))
+ NEXT1();
+ REQUIRE(MORE(), REG_EBRACE);
+ SETERROR(REG_BADBR);
+ }
+ } else if (c == (unsigned char)'$') /* $ (but not \$) ends it */
+ return(1);
+
+ return(0);
+}
+
+/*
+ - p_count - parse a repetition count
+ == static int p_count(register struct parse *p);
+ */
+static int /* the value */
+p_count(p)
+register struct parse *p;
+{
+ register int count = 0;
+ register int ndigits = 0;
+
+ while (MORE() && isdigit(PEEK()) && count <= DUPMAX) {
+ count = count*10 + (GETNEXT() - '0');
+ ndigits++;
+ }
+
+ REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR);
+ return(count);
+}
+
+/*
+ - p_bracket - parse a bracketed character list
+ == static void p_bracket(register struct parse *p);
+ *
+ * Note a significant property of this code: if the allocset() did SETERROR,
+ * no set operations are done.
+ */
+static void
+p_bracket(p)
+register struct parse *p;
+{
+ register cset *cs = allocset(p);
+ register int invert = 0;
+
+ /* Dept of Truly Sickening Special-Case Kludges */
+ if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) {
+ EMIT(OBOW, 0);
+ NEXTn(6);
+ return;
+ }
+ if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) {
+ EMIT(OEOW, 0);
+ NEXTn(6);
+ return;
+ }
+
+ if (EAT('^'))
+ invert++; /* make note to invert set at end */
+ if (EAT(']'))
+ CHadd(cs, ']');
+ else if (EAT('-'))
+ CHadd(cs, '-');
+ while (MORE() && PEEK() != ']' && !SEETWO('-', ']'))
+ p_b_term(p, cs);
+ if (EAT('-'))
+ CHadd(cs, '-');
+ MUSTEAT(']', REG_EBRACK);
+
+ if (p->error != 0) /* don't mess things up further */
+ return;
+
+ if (p->g->cflags&REG_ICASE) {
+ register int i;
+ register int ci;
+
+ for (i = p->g->csetsize - 1; i >= 0; i--)
+ if (CHIN(cs, i) && isalpha(i)) {
+ ci = othercase(i);
+ if (ci != i)
+ CHadd(cs, ci);
+ }
+ if (cs->multis != NULL)
+ mccase(p, cs);
+ }
+ if (invert) {
+ register int i;
+
+ for (i = p->g->csetsize - 1; i >= 0; i--)
+ if (CHIN(cs, i))
+ CHsub(cs, i);
+ else
+ CHadd(cs, i);
+ if (p->g->cflags&REG_NEWLINE)
+ CHsub(cs, '\n');
+ if (cs->multis != NULL)
+ mcinvert(p, cs);
+ }
+
+ assert(cs->multis == NULL); /* xxx */
+
+ if (nch(p, cs) == 1) { /* optimize singleton sets */
+ ordinary(p, firstch(p, cs));
+ freeset(p, cs);
+ } else
+ EMIT(OANYOF, freezeset(p, cs));
+}
+
+/*
+ - p_b_term - parse one term of a bracketed character list
+ == static void p_b_term(register struct parse *p, register cset *cs);
+ */
+static void
+p_b_term(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+ register char c;
+ register char start, finish;
+ register int i;
+
+ /* classify what we've got */
+ switch ((MORE()) ? PEEK() : '\0') {
+ case '[':
+ c = (MORE2()) ? PEEK2() : '\0';
+ break;
+ case '-':
+ SETERROR(REG_ERANGE);
+ return; /* NOTE RETURN */
+ break;
+ default:
+ c = '\0';
+ break;
+ }
+
+ switch (c) {
+ case ':': /* character class */
+ NEXT2();
+ REQUIRE(MORE(), REG_EBRACK);
+ c = PEEK();
+ REQUIRE(c != '-' && c != ']', REG_ECTYPE);
+ p_b_cclass(p, cs);
+ REQUIRE(MORE(), REG_EBRACK);
+ REQUIRE(EATTWO(':', ']'), REG_ECTYPE);
+ break;
+ case '=': /* equivalence class */
+ NEXT2();
+ REQUIRE(MORE(), REG_EBRACK);
+ c = PEEK();
+ REQUIRE(c != '-' && c != ']', REG_ECOLLATE);
+ p_b_eclass(p, cs);
+ REQUIRE(MORE(), REG_EBRACK);
+ REQUIRE(EATTWO('=', ']'), REG_ECOLLATE);
+ break;
+ default: /* symbol, ordinary character, or range */
+/* xxx revision needed for multichar stuff */
+ start = p_b_symbol(p);
+ if (SEE('-') && MORE2() && PEEK2() != ']') {
+ /* range */
+ NEXT1();
+ if (EAT('-'))
+ finish = '-';
+ else
+ finish = p_b_symbol(p);
+ } else
+ finish = start;
+/* xxx what about signed chars here... */
+ REQUIRE(start <= finish, REG_ERANGE);
+ for (i = start; i <= finish; i++)
+ CHadd(cs, i);
+ break;
+ }
+}
+
+/*
+ - p_b_cclass - parse a character-class name and deal with it
+ == static void p_b_cclass(register struct parse *p, register cset *cs);
+ */
+static void
+p_b_cclass(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+ register char *sp = p->next;
+ register struct cclass *cp;
+ register size_t len;
+ register char *u;
+ register char c;
+
+ while (MORE() && isalpha(PEEK()))
+ NEXT1();
+ len = p->next - sp;
+ for (cp = cclasses; cp->name != NULL; cp++)
+ if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0')
+ break;
+ if (cp->name == NULL) {
+ /* oops, didn't find it */
+ SETERROR(REG_ECTYPE);
+ return;
+ }
+
+ u = cp->chars;
+ while ((c = *u++) != '\0')
+ CHadd(cs, c);
+ for (u = cp->multis; *u != '\0'; u += strlen(u) + 1)
+ MCadd(p, cs, u);
+}
+
+/*
+ - p_b_eclass - parse an equivalence-class name and deal with it
+ == static void p_b_eclass(register struct parse *p, register cset *cs);
+ *
+ * This implementation is incomplete. xxx
+ */
+static void
+p_b_eclass(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+ register char c;
+
+ c = p_b_coll_elem(p, '=');
+ CHadd(cs, c);
+}
+
+/*
+ - p_b_symbol - parse a character or [..]ed multicharacter collating symbol
+ == static char p_b_symbol(register struct parse *p);
+ */
+static char /* value of symbol */
+p_b_symbol(p)
+register struct parse *p;
+{
+ register char value;
+
+ REQUIRE(MORE(), REG_EBRACK);
+ if (!EATTWO('[', '.'))
+ return(GETNEXT());
+
+ /* collating symbol */
+ value = p_b_coll_elem(p, '.');
+ REQUIRE(EATTWO('.', ']'), REG_ECOLLATE);
+ return(value);
+}
+
+/*
+ - p_b_coll_elem - parse a collating-element name and look it up
+ == static char p_b_coll_elem(register struct parse *p, int endc);
+ */
+static char /* value of collating element */
+p_b_coll_elem(p, endc)
+register struct parse *p;
+int endc; /* name ended by endc,']' */
+{
+ register char *sp = p->next;
+ register struct cname *cp;
+ register int len;
+
+ while (MORE() && !SEETWO(endc, ']'))
+ NEXT1();
+ if (!MORE()) {
+ SETERROR(REG_EBRACK);
+ return(0);
+ }
+ len = p->next - sp;
+ for (cp = cnames; cp->name != NULL; cp++)
+ if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0')
+ return(cp->code); /* known name */
+ if (len == 1)
+ return(*sp); /* single character */
+ SETERROR(REG_ECOLLATE); /* neither */
+ return(0);
+}
+
+/*
+ - othercase - return the case counterpart of an alphabetic
+ == static char othercase(int ch);
+ */
+static char /* if no counterpart, return ch */
+othercase(ch)
+int ch;
+{
+ assert(isalpha(ch));
+ if (isupper(ch))
+ return(tolower(ch));
+ else if (islower(ch))
+ return(toupper(ch));
+ else /* peculiar, but could happen */
+ return(ch);
+}
+
+/*
+ - bothcases - emit a dualcase version of a two-case character
+ == static void bothcases(register struct parse *p, int ch);
+ *
+ * Boy, is this implementation ever a kludge...
+ */
+static void
+bothcases(p, ch)
+register struct parse *p;
+int ch;
+{
+ register char *oldnext = p->next;
+ register char *oldend = p->end;
+ char bracket[3];
+
+ assert(othercase(ch) != ch); /* p_bracket() would recurse */
+ p->next = bracket;
+ p->end = bracket+2;
+ bracket[0] = ch;
+ bracket[1] = ']';
+ bracket[2] = '\0';
+ p_bracket(p);
+ assert(p->next == bracket+2);
+ p->next = oldnext;
+ p->end = oldend;
+}
+
+/*
+ - ordinary - emit an ordinary character
+ == static void ordinary(register struct parse *p, register int ch);
+ */
+static void
+ordinary(p, ch)
+register struct parse *p;
+register int ch;
+{
+ register cat_t *cap = p->g->categories;
+
+ if ((p->g->cflags&REG_ICASE) && isalpha(ch) && othercase(ch) != ch)
+ bothcases(p, ch);
+ else {
+ EMIT(OCHAR, (unsigned char)ch);
+ if (cap[ch] == 0)
+ cap[ch] = p->g->ncategories++;
+ }
+}
+
+/*
+ - nonnewline - emit REG_NEWLINE version of OANY
+ == static void nonnewline(register struct parse *p);
+ *
+ * Boy, is this implementation ever a kludge...
+ */
+static void
+nonnewline(p)
+register struct parse *p;
+{
+ register char *oldnext = p->next;
+ register char *oldend = p->end;
+ char bracket[4];
+
+ p->next = bracket;
+ p->end = bracket+3;
+ bracket[0] = '^';
+ bracket[1] = '\n';
+ bracket[2] = ']';
+ bracket[3] = '\0';
+ p_bracket(p);
+ assert(p->next == bracket+3);
+ p->next = oldnext;
+ p->end = oldend;
+}
+
+/*
+ - repeat - generate code for a bounded repetition, recursively if needed
+ == static void repeat(register struct parse *p, sopno start, int from, int to);
+ */
+static void
+repeat(p, start, from, to)
+register struct parse *p;
+sopno start; /* operand from here to end of strip */
+int from; /* repeated from this number */
+int to; /* to this number of times (maybe INFINITY) */
+{
+ register sopno finish = HERE();
+# define N 2
+# define INF 3
+# define REP(f, t) ((f)*8 + (t))
+# define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N)
+ register sopno copy;
+
+ if (p->error != 0) /* head off possible runaway recursion */
+ return;
+
+ assert(from <= to);
+
+ switch (REP(MAP(from), MAP(to))) {
+ case REP(0, 0): /* must be user doing this */
+ DROP(finish-start); /* drop the operand */
+ break;
+ case REP(0, 1): /* as x{1,1}? */
+ case REP(0, N): /* as x{1,n}? */
+ case REP(0, INF): /* as x{1,}? */
+ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+ INSERT(OCH_, start); /* offset is wrong... */
+ repeat(p, start+1, 1, to);
+ ASTERN(OOR1, start);
+ AHEAD(start); /* ... fix it */
+ EMIT(OOR2, 0);
+ AHEAD(THERE());
+ ASTERN(O_CH, THERETHERE());
+ break;
+ case REP(1, 1): /* trivial case */
+ /* done */
+ break;
+ case REP(1, N): /* as x?x{1,n-1} */
+ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+ INSERT(OCH_, start);
+ ASTERN(OOR1, start);
+ AHEAD(start);
+ EMIT(OOR2, 0); /* offset very wrong... */
+ AHEAD(THERE()); /* ...so fix it */
+ ASTERN(O_CH, THERETHERE());
+ copy = dupl(p, start+1, finish+1);
+ assert(copy == finish+4);
+ repeat(p, copy, 1, to-1);
+ break;
+ case REP(1, INF): /* as x+ */
+ INSERT(OPLUS_, start);
+ ASTERN(O_PLUS, start);
+ break;
+ case REP(N, N): /* as xx{m-1,n-1} */
+ copy = dupl(p, start, finish);
+ repeat(p, copy, from-1, to-1);
+ break;
+ case REP(N, INF): /* as xx{n-1,INF} */
+ copy = dupl(p, start, finish);
+ repeat(p, copy, from-1, to);
+ break;
+ default: /* "can't happen" */
+ SETERROR(REG_ASSERT); /* just in case */
+ break;
+ }
+}
+
+/*
+ - seterr - set an error condition
+ == static int seterr(register struct parse *p, int e);
+ */
+static int /* useless but makes type checking happy */
+seterr(p, e)
+register struct parse *p;
+int e;
+{
+ if (p->error == 0) /* keep earliest error condition */
+ p->error = e;
+ p->next = nuls; /* try to bring things to a halt */
+ p->end = nuls;
+ return(0); /* make the return value well-defined */
+}
+
+/*
+ - allocset - allocate a set of characters for []
+ == static cset *allocset(register struct parse *p);
+ */
+static cset *
+allocset(p)
+register struct parse *p;
+{
+ register int no = p->g->ncsets++;
+ register size_t nc;
+ register size_t nbytes;
+ register cset *cs;
+ register size_t css = (size_t)p->g->csetsize;
+ register int i;
+
+ if (no >= p->ncsalloc) { /* need another column of space */
+ p->ncsalloc += CHAR_BIT;
+ nc = p->ncsalloc;
+ assert(nc % CHAR_BIT == 0);
+ nbytes = nc / CHAR_BIT * css;
+ if (p->g->sets == NULL)
+ p->g->sets = (cset *)malloc(nc * sizeof(cset));
+ else
+ p->g->sets = (cset *)realloc((char *)p->g->sets,
+ nc * sizeof(cset));
+ if (p->g->setbits == NULL)
+ p->g->setbits = (uch *)malloc(nbytes);
+ else {
+ p->g->setbits = (uch *)realloc((char *)p->g->setbits,
+ nbytes);
+ /* xxx this isn't right if setbits is now NULL */
+ for (i = 0; i < no; i++)
+ p->g->sets[i].ptr = p->g->setbits + css*(i/CHAR_BIT);
+ }
+ if (p->g->sets != NULL && p->g->setbits != NULL)
+ (void) memset((char *)p->g->setbits + (nbytes - css),
+ 0, css);
+ else {
+ no = 0;
+ SETERROR(REG_ESPACE);
+ /* caller's responsibility not to do set ops */
+ }
+ }
+
+ assert(p->g->sets != NULL); /* xxx */
+ cs = &p->g->sets[no];
+ cs->ptr = p->g->setbits + css*((no)/CHAR_BIT);
+ cs->mask = 1 << ((no) % CHAR_BIT);
+ cs->hash = 0;
+ cs->smultis = 0;
+ cs->multis = NULL;
+
+ return(cs);
+}
+
+/*
+ - freeset - free a now-unused set
+ == static void freeset(register struct parse *p, register cset *cs);
+ */
+static void
+freeset(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+ register int i;
+ register cset *top = &p->g->sets[p->g->ncsets];
+ register size_t css = (size_t)p->g->csetsize;
+
+ for (i = 0; i < css; i++)
+ CHsub(cs, i);
+ if (cs == top-1) /* recover only the easy case */
+ p->g->ncsets--;
+}
+
+/*
+ - freezeset - final processing on a set of characters
+ == static int freezeset(register struct parse *p, register cset *cs);
+ *
+ * The main task here is merging identical sets. This is usually a waste
+ * of time (although the hash code minimizes the overhead), but can win
+ * big if REG_ICASE is being used. REG_ICASE, by the way, is why the hash
+ * is done using addition rather than xor -- all ASCII [aA] sets xor to
+ * the same value!
+ */
+static int /* set number */
+freezeset(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+ register uch h = cs->hash;
+ register int i;
+ register cset *top = &p->g->sets[p->g->ncsets];
+ register cset *cs2;
+ register size_t css = (size_t)p->g->csetsize;
+
+ /* look for an earlier one which is the same */
+ for (cs2 = &p->g->sets[0]; cs2 < top; cs2++)
+ if (cs2->hash == h && cs2 != cs) {
+ /* maybe */
+ for (i = 0; i < css; i++)
+ if (!!CHIN(cs2, i) != !!CHIN(cs, i))
+ break; /* no */
+ if (i == css)
+ break; /* yes */
+ }
+
+ if (cs2 < top) { /* found one */
+ freeset(p, cs);
+ cs = cs2;
+ }
+
+ return((int)(cs - p->g->sets));
+}
+
+/*
+ - firstch - return first character in a set (which must have at least one)
+ == static int firstch(register struct parse *p, register cset *cs);
+ */
+static int /* character; there is no "none" value */
+firstch(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+ register int i;
+ register size_t css = (size_t)p->g->csetsize;
+
+ for (i = 0; i < css; i++)
+ if (CHIN(cs, i))
+ return((char)i);
+ assert(never);
+ return(0); /* arbitrary */
+}
+
+/*
+ - nch - number of characters in a set
+ == static int nch(register struct parse *p, register cset *cs);
+ */
+static int
+nch(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+ register int i;
+ register size_t css = (size_t)p->g->csetsize;
+ register int n = 0;
+
+ for (i = 0; i < css; i++)
+ if (CHIN(cs, i))
+ n++;
+ return(n);
+}
+
+/*
+ - mcadd - add a collating element to a cset
+ == static void mcadd(register struct parse *p, register cset *cs, \
+ == register char *cp);
+ */
+static void
+mcadd(p, cs, cp)
+register struct parse *p;
+register cset *cs;
+register char *cp;
+{
+ register size_t oldend = cs->smultis;
+
+ cs->smultis += strlen(cp) + 1;
+ if (cs->multis == NULL)
+ cs->multis = malloc(cs->smultis);
+ else
+ cs->multis = realloc(cs->multis, cs->smultis);
+ if (cs->multis == NULL) {
+ SETERROR(REG_ESPACE);
+ return;
+ }
+
+ (void) strcpy(cs->multis + oldend - 1, cp);
+ cs->multis[cs->smultis - 1] = '\0';
+}
+
+
+/*
+ - mcinvert - invert the list of collating elements in a cset
+ == static void mcinvert(register struct parse *p, register cset *cs);
+ *
+ * This would have to know the set of possibilities. Implementation
+ * is deferred.
+ */
+static void
+mcinvert(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+ assert(cs->multis == NULL); /* xxx */
+}
+
+/*
+ - mccase - add case counterparts of the list of collating elements in a cset
+ == static void mccase(register struct parse *p, register cset *cs);
+ *
+ * This would have to know the set of possibilities. Implementation
+ * is deferred.
+ */
+static void
+mccase(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+ assert(cs->multis == NULL); /* xxx */
+}
+
+/*
+ - isinsets - is this character in any sets?
+ == static int isinsets(register struct re_guts *g, int c);
+ */
+static int /* predicate */
+isinsets(g, c)
+register struct re_guts *g;
+int c;
+{
+ register uch *col;
+ register int i;
+ register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT;
+ register unsigned uc = (unsigned char)c;
+
+ for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize)
+ if (col[uc] != 0)
+ return(1);
+ return(0);
+}
+
+/*
+ - samesets - are these two characters in exactly the same sets?
+ == static int samesets(register struct re_guts *g, int c1, int c2);
+ */
+static int /* predicate */
+samesets(g, c1, c2)
+register struct re_guts *g;
+int c1;
+int c2;
+{
+ register uch *col;
+ register int i;
+ register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT;
+ register unsigned uc1 = (unsigned char)c1;
+ register unsigned uc2 = (unsigned char)c2;
+
+ for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize)
+ if (col[uc1] != col[uc2])
+ return(0);
+ return(1);
+}
+
+/*
+ - categorize - sort out character categories
+ == static void categorize(struct parse *p, register struct re_guts *g);
+ */
+static void
+categorize(p, g)
+struct parse *p;
+register struct re_guts *g;
+{
+ register cat_t *cats = g->categories;
+ register int c;
+ register int c2;
+ register cat_t cat;
+
+ /* avoid making error situations worse */
+ if (p->error != 0)
+ return;
+
+ for (c = CHAR_MIN; c <= CHAR_MAX; c++)
+ if (cats[c] == 0 && isinsets(g, c)) {
+ cat = g->ncategories++;
+ cats[c] = cat;
+ for (c2 = c+1; c2 <= CHAR_MAX; c2++)
+ if (cats[c2] == 0 && samesets(g, c, c2))
+ cats[c2] = cat;
+ }
+}
+
+/*
+ - dupl - emit a duplicate of a bunch of sops
+ == static sopno dupl(register struct parse *p, sopno start, sopno finish);
+ */
+static sopno /* start of duplicate */
+dupl(p, start, finish)
+register struct parse *p;
+sopno start; /* from here */
+sopno finish; /* to this less one */
+{
+ register sopno ret = HERE();
+ register sopno len = finish - start;
+
+ assert(finish >= start);
+ if (len == 0)
+ return(ret);
+ enlarge(p, p->ssize + len); /* this many unexpected additions */
+ assert(p->ssize >= p->slen + len);
+ (void) memcpy((char *)(p->strip + p->slen),
+ (char *)(p->strip + start), (size_t)len*sizeof(sop));
+ p->slen += len;
+ return(ret);
+}
+
+/*
+ - doemit - emit a strip operator
+ == static void doemit(register struct parse *p, sop op, size_t opnd);
+ *
+ * It might seem better to implement this as a macro with a function as
+ * hard-case backup, but it's just too big and messy unless there are
+ * some changes to the data structures. Maybe later.
+ */
+static void
+doemit(p, op, opnd)
+register struct parse *p;
+sop op;
+size_t opnd;
+{
+ /* avoid making error situations worse */
+ if (p->error != 0)
+ return;
+
+ /* deal with oversize operands ("can't happen", more or less) */
+ assert(opnd < 1<<OPSHIFT);
+
+ /* deal with undersized strip */
+ if (p->slen >= p->ssize)
+ enlarge(p, (p->ssize+1) / 2 * 3); /* +50% */
+ assert(p->slen < p->ssize);
+
+ /* finally, it's all reduced to the easy case */
+ p->strip[p->slen++] = SOP(op, opnd);
+}
+
+/*
+ - doinsert - insert a sop into the strip
+ == static void doinsert(register struct parse *p, sop op, size_t opnd, sopno pos);
+ */
+static void
+doinsert(p, op, opnd, pos)
+register struct parse *p;
+sop op;
+size_t opnd;
+sopno pos;
+{
+ register sopno sn;
+ register sop s;
+ register int i;
+
+ /* avoid making error situations worse */
+ if (p->error != 0)
+ return;
+
+ sn = HERE();
+ EMIT(op, opnd); /* do checks, ensure space */
+ assert(HERE() == sn+1);
+ s = p->strip[sn];
+
+ /* adjust paren pointers */
+ assert(pos > 0);
+ for (i = 1; i < NPAREN; i++) {
+ if (p->pbegin[i] >= pos) {
+ p->pbegin[i]++;
+ }
+ if (p->pend[i] >= pos) {
+ p->pend[i]++;
+ }
+ }
+
+ memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos],
+ (HERE()-pos-1)*sizeof(sop));
+ p->strip[pos] = s;
+}
+
+/*
+ - dofwd - complete a forward reference
+ == static void dofwd(register struct parse *p, sopno pos, sop value);
+ */
+static void
+dofwd(p, pos, value)
+register struct parse *p;
+register sopno pos;
+sop value;
+{
+ /* avoid making error situations worse */
+ if (p->error != 0)
+ return;
+
+ assert(value < 1<<OPSHIFT);
+ p->strip[pos] = OP(p->strip[pos]) | value;
+}
+
+/*
+ - enlarge - enlarge the strip
+ == static void enlarge(register struct parse *p, sopno size);
+ */
+static void
+enlarge(p, size)
+register struct parse *p;
+register sopno size;
+{
+ register sop *sp;
+
+ if (p->ssize >= size)
+ return;
+
+ sp = (sop *)realloc(p->strip, size*sizeof(sop));
+ if (sp == NULL) {
+ SETERROR(REG_ESPACE);
+ return;
+ }
+ p->strip = sp;
+ p->ssize = size;
+}
+
+/*
+ - stripsnug - compact the strip
+ == static void stripsnug(register struct parse *p, register struct re_guts *g);
+ */
+static void
+stripsnug(p, g)
+register struct parse *p;
+register struct re_guts *g;
+{
+ g->nstates = p->slen;
+ g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop));
+ if (g->strip == NULL) {
+ SETERROR(REG_ESPACE);
+ g->strip = p->strip;
+ }
+}
+
+/*
+ - findmust - fill in must and mlen with longest mandatory literal string
+ == static void findmust(register struct parse *p, register struct re_guts *g);
+ *
+ * This algorithm could do fancy things like analyzing the operands of |
+ * for common subsequences. Someday. This code is simple and finds most
+ * of the interesting cases.
+ *
+ * Note that must and mlen got initialized during setup.
+ */
+static void
+findmust(p, g)
+struct parse *p;
+register struct re_guts *g;
+{
+ register sop *scan;
+ sop *start = NULL;
+ register sop *newstart = NULL;
+ register sopno newlen;
+ register sop s;
+ register char *cp;
+ register sopno i;
+
+ /* avoid making error situations worse */
+ if (p->error != 0)
+ return;
+
+ /* find the longest OCHAR sequence in strip */
+ newlen = 0;
+ scan = g->strip + 1;
+ do {
+ s = *scan++;
+ switch (OP(s)) {
+ case OCHAR: /* sequence member */
+ if (newlen == 0) /* new sequence */
+ newstart = scan - 1;
+ newlen++;
+ break;
+ case OPLUS_: /* things that don't break one */
+ case OLPAREN:
+ case ORPAREN:
+ break;
+ case OQUEST_: /* things that must be skipped */
+ case OCH_:
+ scan--;
+ do {
+ scan += OPND(s);
+ s = *scan;
+ /* assert() interferes w debug printouts */
+ if (OP(s) != O_QUEST && OP(s) != O_CH &&
+ OP(s) != OOR2) {
+ g->iflags |= BAD;
+ return;
+ }
+ } while (OP(s) != O_QUEST && OP(s) != O_CH);
+ /* fallthrough */
+ default: /* things that break a sequence */
+ if (newlen > g->mlen) { /* ends one */
+ start = newstart;
+ g->mlen = newlen;
+ }
+ newlen = 0;
+ break;
+ }
+ } while (OP(s) != OEND);
+
+ if (g->mlen == 0) /* there isn't one */
+ return;
+
+ /* turn it into a character string */
+ g->must = malloc((size_t)g->mlen + 1);
+ if (g->must == NULL) { /* argh; just forget it */
+ g->mlen = 0;
+ return;
+ }
+ cp = g->must;
+ scan = start;
+ for (i = g->mlen; i > 0; i--) {
+ while (OP(s = *scan++) != OCHAR)
+ continue;
+ assert(cp < g->must + g->mlen);
+ *cp++ = (char)OPND(s);
+ }
+ assert(cp == g->must + g->mlen);
+ *cp++ = '\0'; /* just on general principles */
+}
+
+/*
+ - pluscount - count + nesting
+ == static sopno pluscount(register struct parse *p, register struct re_guts *g);
+ */
+static sopno /* nesting depth */
+pluscount(p, g)
+struct parse *p;
+register struct re_guts *g;
+{
+ register sop *scan;
+ register sop s;
+ register sopno plusnest = 0;
+ register sopno maxnest = 0;
+
+ if (p->error != 0)
+ return(0); /* there may not be an OEND */
+
+ scan = g->strip + 1;
+ do {
+ s = *scan++;
+ switch (OP(s)) {
+ case OPLUS_:
+ plusnest++;
+ break;
+ case O_PLUS:
+ if (plusnest > maxnest)
+ maxnest = plusnest;
+ plusnest--;
+ break;
+ }
+ } while (OP(s) != OEND);
+ if (plusnest != 0)
+ g->iflags |= BAD;
+ return(maxnest);
+}
diff --git a/usr.sbin/httpd/src/regex/regerror.c b/usr.sbin/httpd/src/regex/regerror.c
new file mode 100644
index 00000000000..850b0e61b57
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/regerror.c
@@ -0,0 +1,124 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regerror.ih"
+
+/*
+ = #define REG_NOMATCH 1
+ = #define REG_BADPAT 2
+ = #define REG_ECOLLATE 3
+ = #define REG_ECTYPE 4
+ = #define REG_EESCAPE 5
+ = #define REG_ESUBREG 6
+ = #define REG_EBRACK 7
+ = #define REG_EPAREN 8
+ = #define REG_EBRACE 9
+ = #define REG_BADBR 10
+ = #define REG_ERANGE 11
+ = #define REG_ESPACE 12
+ = #define REG_BADRPT 13
+ = #define REG_EMPTY 14
+ = #define REG_ASSERT 15
+ = #define REG_INVARG 16
+ = #define REG_ATOI 255 // convert name to number (!)
+ = #define REG_ITOA 0400 // convert number to name (!)
+ */
+static struct rerr {
+ int code;
+ char *name;
+ char *explain;
+} rerrs[] = {
+ { REG_NOMATCH, "REG_NOMATCH", "regexec() failed to match" },
+ { REG_BADPAT, "REG_BADPAT", "invalid regular expression" },
+ { REG_ECOLLATE, "REG_ECOLLATE", "invalid collating element" },
+ { REG_ECTYPE, "REG_ECTYPE", "invalid character class" },
+ { REG_EESCAPE, "REG_EESCAPE", "trailing backslash (\\)" },
+ { REG_ESUBREG, "REG_ESUBREG", "invalid backreference number" },
+ { REG_EBRACK, "REG_EBRACK", "brackets ([ ]) not balanced" },
+ { REG_EPAREN, "REG_EPAREN", "parentheses not balanced" },
+ { REG_EBRACE, "REG_EBRACE", "braces not balanced" },
+ { REG_BADBR, "REG_BADBR", "invalid repetition count(s)" },
+ { REG_ERANGE, "REG_ERANGE", "invalid character range" },
+ { REG_ESPACE, "REG_ESPACE", "out of memory" },
+ { REG_BADRPT, "REG_BADRPT", "repetition-operator operand invalid" },
+ { REG_EMPTY, "REG_EMPTY", "empty (sub)expression" },
+ { REG_ASSERT, "REG_ASSERT", "\"can't happen\" -- you found a bug" },
+ { REG_INVARG, "REG_INVARG", "invalid argument to regex routine" },
+ { 0, "", "*** unknown regexp error code ***" }
+};
+
+/*
+ - regerror - the interface to error numbers
+ = extern size_t regerror(int, const regex_t *, char *, size_t);
+ */
+/* ARGSUSED */
+size_t
+regerror(errcode, preg, errbuf, errbuf_size)
+int errcode;
+const regex_t *preg;
+char *errbuf;
+size_t errbuf_size;
+{
+ register struct rerr *r;
+ register size_t len;
+ register int target = errcode &~ REG_ITOA;
+ register char *s;
+ char convbuf[50];
+
+ if (errcode == REG_ATOI)
+ s = regatoi(preg, convbuf);
+ else {
+ for (r = rerrs; r->code != 0; r++)
+ if (r->code == target)
+ break;
+
+ if (errcode&REG_ITOA) {
+ if (r->code != 0)
+ (void) strcpy(convbuf, r->name);
+ else
+ sprintf(convbuf, "REG_0x%x", target);
+ assert(strlen(convbuf) < sizeof(convbuf));
+ s = convbuf;
+ } else
+ s = r->explain;
+ }
+
+ len = strlen(s) + 1;
+ if (errbuf_size > 0) {
+ if (errbuf_size > len)
+ (void) strcpy(errbuf, s);
+ else {
+ (void) strncpy(errbuf, s, errbuf_size-1);
+ errbuf[errbuf_size-1] = '\0';
+ }
+ }
+
+ return(len);
+}
+
+/*
+ - regatoi - internal routine to implement REG_ATOI
+ == static char *regatoi(const regex_t *preg, char *localbuf);
+ */
+static char *
+regatoi(preg, localbuf)
+const regex_t *preg;
+char *localbuf;
+{
+ register struct rerr *r;
+
+ for (r = rerrs; r->code != 0; r++)
+ if (strcmp(r->name, preg->re_endp) == 0)
+ break;
+ if (r->code == 0)
+ return("0");
+
+ sprintf(localbuf, "%d", r->code);
+ return(localbuf);
+}
diff --git a/usr.sbin/httpd/src/regex/regex.3 b/usr.sbin/httpd/src/regex/regex.3
new file mode 100644
index 00000000000..100c8a7f71c
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/regex.3
@@ -0,0 +1,502 @@
+.TH REGEX 3 "17 May 1993"
+.BY "Henry Spencer"
+.de ZR
+.\" one other place knows this name: the SEE ALSO section
+.IR regex (7) \\$1
+..
+.SH NAME
+regcomp, regexec, regerror, regfree \- regular-expression library
+.SH SYNOPSIS
+.ft B
+.\".na
+#include <sys/types.h>
+.br
+#include <regex.h>
+.HP 10
+int regcomp(regex_t\ *preg, const\ char\ *pattern, int\ cflags);
+.HP
+int\ regexec(const\ regex_t\ *preg, const\ char\ *string,
+size_t\ nmatch, regmatch_t\ pmatch[], int\ eflags);
+.HP
+size_t\ regerror(int\ errcode, const\ regex_t\ *preg,
+char\ *errbuf, size_t\ errbuf_size);
+.HP
+void\ regfree(regex_t\ *preg);
+.\".ad
+.ft
+.SH DESCRIPTION
+These routines implement POSIX 1003.2 regular expressions (``RE''s);
+see
+.ZR .
+.I Regcomp
+compiles an RE written as a string into an internal form,
+.I regexec
+matches that internal form against a string and reports results,
+.I regerror
+transforms error codes from either into human-readable messages,
+and
+.I regfree
+frees any dynamically-allocated storage used by the internal form
+of an RE.
+.PP
+The header
+.I <regex.h>
+declares two structure types,
+.I regex_t
+and
+.IR regmatch_t ,
+the former for compiled internal forms and the latter for match reporting.
+It also declares the four functions,
+a type
+.IR regoff_t ,
+and a number of constants with names starting with ``REG_''.
+.PP
+.I Regcomp
+compiles the regular expression contained in the
+.I pattern
+string,
+subject to the flags in
+.IR cflags ,
+and places the results in the
+.I regex_t
+structure pointed to by
+.IR preg .
+.I Cflags
+is the bitwise OR of zero or more of the following flags:
+.IP REG_EXTENDED \w'REG_EXTENDED'u+2n
+Compile modern (``extended'') REs,
+rather than the obsolete (``basic'') REs that
+are the default.
+.IP REG_BASIC
+This is a synonym for 0,
+provided as a counterpart to REG_EXTENDED to improve readability.
+.IP REG_NOSPEC
+Compile with recognition of all special characters turned off.
+All characters are thus considered ordinary,
+so the ``RE'' is a literal string.
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+REG_EXTENDED and REG_NOSPEC may not be used
+in the same call to
+.IR regcomp .
+.IP REG_ICASE
+Compile for matching that ignores upper/lower case distinctions.
+See
+.ZR .
+.IP REG_NOSUB
+Compile for matching that need only report success or failure,
+not what was matched.
+.IP REG_NEWLINE
+Compile for newline-sensitive matching.
+By default, newline is a completely ordinary character with no special
+meaning in either REs or strings.
+With this flag,
+`[^' bracket expressions and `.' never match newline,
+a `^' anchor matches the null string after any newline in the string
+in addition to its normal function,
+and the `$' anchor matches the null string before any newline in the
+string in addition to its normal function.
+.IP REG_PEND
+The regular expression ends,
+not at the first NUL,
+but just before the character pointed to by the
+.I re_endp
+member of the structure pointed to by
+.IR preg .
+The
+.I re_endp
+member is of type
+.IR const\ char\ * .
+This flag permits inclusion of NULs in the RE;
+they are considered ordinary characters.
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+.PP
+When successful,
+.I regcomp
+returns 0 and fills in the structure pointed to by
+.IR preg .
+One member of that structure
+(other than
+.IR re_endp )
+is publicized:
+.IR re_nsub ,
+of type
+.IR size_t ,
+contains the number of parenthesized subexpressions within the RE
+(except that the value of this member is undefined if the
+REG_NOSUB flag was used).
+If
+.I regcomp
+fails, it returns a non-zero error code;
+see DIAGNOSTICS.
+.PP
+.I Regexec
+matches the compiled RE pointed to by
+.I preg
+against the
+.IR string ,
+subject to the flags in
+.IR eflags ,
+and reports results using
+.IR nmatch ,
+.IR pmatch ,
+and the returned value.
+The RE must have been compiled by a previous invocation of
+.IR regcomp .
+The compiled form is not altered during execution of
+.IR regexec ,
+so a single compiled RE can be used simultaneously by multiple threads.
+.PP
+By default,
+the NUL-terminated string pointed to by
+.I string
+is considered to be the text of an entire line, minus any terminating
+newline.
+The
+.I eflags
+argument is the bitwise OR of zero or more of the following flags:
+.IP REG_NOTBOL \w'REG_STARTEND'u+2n
+The first character of
+the string
+is not the beginning of a line, so the `^' anchor should not match before it.
+This does not affect the behavior of newlines under REG_NEWLINE.
+.IP REG_NOTEOL
+The NUL terminating
+the string
+does not end a line, so the `$' anchor should not match before it.
+This does not affect the behavior of newlines under REG_NEWLINE.
+.IP REG_STARTEND
+The string is considered to start at
+\fIstring\fR\ + \fIpmatch\fR[0].\fIrm_so\fR
+and to have a terminating NUL located at
+\fIstring\fR\ + \fIpmatch\fR[0].\fIrm_eo\fR
+(there need not actually be a NUL at that location),
+regardless of the value of
+.IR nmatch .
+See below for the definition of
+.IR pmatch
+and
+.IR nmatch .
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+Note that a non-zero \fIrm_so\fR does not imply REG_NOTBOL;
+REG_STARTEND affects only the location of the string,
+not how it is matched.
+.PP
+See
+.ZR
+for a discussion of what is matched in situations where an RE or a
+portion thereof could match any of several substrings of
+.IR string .
+.PP
+Normally,
+.I regexec
+returns 0 for success and the non-zero code REG_NOMATCH for failure.
+Other non-zero error codes may be returned in exceptional situations;
+see DIAGNOSTICS.
+.PP
+If REG_NOSUB was specified in the compilation of the RE,
+or if
+.I nmatch
+is 0,
+.I regexec
+ignores the
+.I pmatch
+argument (but see below for the case where REG_STARTEND is specified).
+Otherwise,
+.I pmatch
+points to an array of
+.I nmatch
+structures of type
+.IR regmatch_t .
+Such a structure has at least the members
+.I rm_so
+and
+.IR rm_eo ,
+both of type
+.I regoff_t
+(a signed arithmetic type at least as large as an
+.I off_t
+and a
+.IR ssize_t ),
+containing respectively the offset of the first character of a substring
+and the offset of the first character after the end of the substring.
+Offsets are measured from the beginning of the
+.I string
+argument given to
+.IR regexec .
+An empty substring is denoted by equal offsets,
+both indicating the character following the empty substring.
+.PP
+The 0th member of the
+.I pmatch
+array is filled in to indicate what substring of
+.I string
+was matched by the entire RE.
+Remaining members report what substring was matched by parenthesized
+subexpressions within the RE;
+member
+.I i
+reports subexpression
+.IR i ,
+with subexpressions counted (starting at 1) by the order of their opening
+parentheses in the RE, left to right.
+Unused entries in the array\(emcorresponding either to subexpressions that
+did not participate in the match at all, or to subexpressions that do not
+exist in the RE (that is, \fIi\fR\ > \fIpreg\fR\->\fIre_nsub\fR)\(emhave both
+.I rm_so
+and
+.I rm_eo
+set to \-1.
+If a subexpression participated in the match several times,
+the reported substring is the last one it matched.
+(Note, as an example in particular, that when the RE `(b*)+' matches `bbb',
+the parenthesized subexpression matches each of the three `b's and then
+an infinite number of empty strings following the last `b',
+so the reported substring is one of the empties.)
+.PP
+If REG_STARTEND is specified,
+.I pmatch
+must point to at least one
+.I regmatch_t
+(even if
+.I nmatch
+is 0 or REG_NOSUB was specified),
+to hold the input offsets for REG_STARTEND.
+Use for output is still entirely controlled by
+.IR nmatch ;
+if
+.I nmatch
+is 0 or REG_NOSUB was specified,
+the value of
+.IR pmatch [0]
+will not be changed by a successful
+.IR regexec .
+.PP
+.I Regerror
+maps a non-zero
+.I errcode
+from either
+.I regcomp
+or
+.I regexec
+to a human-readable, printable message.
+If
+.I preg
+is non-NULL,
+the error code should have arisen from use of
+the
+.I regex_t
+pointed to by
+.IR preg ,
+and if the error code came from
+.IR regcomp ,
+it should have been the result from the most recent
+.I regcomp
+using that
+.IR regex_t .
+.RI ( Regerror
+may be able to supply a more detailed message using information
+from the
+.IR regex_t .)
+.I Regerror
+places the NUL-terminated message into the buffer pointed to by
+.IR errbuf ,
+limiting the length (including the NUL) to at most
+.I errbuf_size
+bytes.
+If the whole message won't fit,
+as much of it as will fit before the terminating NUL is supplied.
+In any case,
+the returned value is the size of buffer needed to hold the whole
+message (including terminating NUL).
+If
+.I errbuf_size
+is 0,
+.I errbuf
+is ignored but the return value is still correct.
+.PP
+If the
+.I errcode
+given to
+.I regerror
+is first ORed with REG_ITOA,
+the ``message'' that results is the printable name of the error code,
+e.g. ``REG_NOMATCH'',
+rather than an explanation thereof.
+If
+.I errcode
+is REG_ATOI,
+then
+.I preg
+shall be non-NULL and the
+.I re_endp
+member of the structure it points to
+must point to the printable name of an error code;
+in this case, the result in
+.I errbuf
+is the decimal digits of
+the numeric value of the error code
+(0 if the name is not recognized).
+REG_ITOA and REG_ATOI are intended primarily as debugging facilities;
+they are extensions,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+Be warned also that they are considered experimental and changes are possible.
+.PP
+.I Regfree
+frees any dynamically-allocated storage associated with the compiled RE
+pointed to by
+.IR preg .
+The remaining
+.I regex_t
+is no longer a valid compiled RE
+and the effect of supplying it to
+.I regexec
+or
+.I regerror
+is undefined.
+.PP
+None of these functions references global variables except for tables
+of constants;
+all are safe for use from multiple threads if the arguments are safe.
+.SH IMPLEMENTATION CHOICES
+There are a number of decisions that 1003.2 leaves up to the implementor,
+either by explicitly saying ``undefined'' or by virtue of them being
+forbidden by the RE grammar.
+This implementation treats them as follows.
+.PP
+See
+.ZR
+for a discussion of the definition of case-independent matching.
+.PP
+There is no particular limit on the length of REs,
+except insofar as memory is limited.
+Memory usage is approximately linear in RE size, and largely insensitive
+to RE complexity, except for bounded repetitions.
+See BUGS for one short RE using them
+that will run almost any system out of memory.
+.PP
+A backslashed character other than one specifically given a magic meaning
+by 1003.2 (such magic meanings occur only in obsolete [``basic''] REs)
+is taken as an ordinary character.
+.PP
+Any unmatched [ is a REG_EBRACK error.
+.PP
+Equivalence classes cannot begin or end bracket-expression ranges.
+The endpoint of one range cannot begin another.
+.PP
+RE_DUP_MAX, the limit on repetition counts in bounded repetitions, is 255.
+.PP
+A repetition operator (?, *, +, or bounds) cannot follow another
+repetition operator.
+A repetition operator cannot begin an expression or subexpression
+or follow `^' or `|'.
+.PP
+`|' cannot appear first or last in a (sub)expression or after another `|',
+i.e. an operand of `|' cannot be an empty subexpression.
+An empty parenthesized subexpression, `()', is legal and matches an
+empty (sub)string.
+An empty string is not a legal RE.
+.PP
+A `{' followed by a digit is considered the beginning of bounds for a
+bounded repetition, which must then follow the syntax for bounds.
+A `{' \fInot\fR followed by a digit is considered an ordinary character.
+.PP
+`^' and `$' beginning and ending subexpressions in obsolete (``basic'')
+REs are anchors, not ordinary characters.
+.SH SEE ALSO
+grep(1), regex(7)
+.PP
+POSIX 1003.2, sections 2.8 (Regular Expression Notation)
+and
+B.5 (C Binding for Regular Expression Matching).
+.SH DIAGNOSTICS
+Non-zero error codes from
+.I regcomp
+and
+.I regexec
+include the following:
+.PP
+.nf
+.ta \w'REG_ECOLLATE'u+3n
+REG_NOMATCH regexec() failed to match
+REG_BADPAT invalid regular expression
+REG_ECOLLATE invalid collating element
+REG_ECTYPE invalid character class
+REG_EESCAPE \e applied to unescapable character
+REG_ESUBREG invalid backreference number
+REG_EBRACK brackets [ ] not balanced
+REG_EPAREN parentheses ( ) not balanced
+REG_EBRACE braces { } not balanced
+REG_BADBR invalid repetition count(s) in { }
+REG_ERANGE invalid character range in [ ]
+REG_ESPACE ran out of memory
+REG_BADRPT ?, *, or + operand invalid
+REG_EMPTY empty (sub)expression
+REG_ASSERT ``can't happen''\(emyou found a bug
+REG_INVARG invalid argument, e.g. negative-length string
+.fi
+.SH HISTORY
+Written by Henry Spencer at University of Toronto,
+henry@zoo.toronto.edu.
+.SH BUGS
+This is an alpha release with known defects.
+Please report problems.
+.PP
+There is one known functionality bug.
+The implementation of internationalization is incomplete:
+the locale is always assumed to be the default one of 1003.2,
+and only the collating elements etc. of that locale are available.
+.PP
+The back-reference code is subtle and doubts linger about its correctness
+in complex cases.
+.PP
+.I Regexec
+performance is poor.
+This will improve with later releases.
+.I Nmatch
+exceeding 0 is expensive;
+.I nmatch
+exceeding 1 is worse.
+.I Regexec
+is largely insensitive to RE complexity \fIexcept\fR that back
+references are massively expensive.
+RE length does matter; in particular, there is a strong speed bonus
+for keeping RE length under about 30 characters,
+with most special characters counting roughly double.
+.PP
+.I Regcomp
+implements bounded repetitions by macro expansion,
+which is costly in time and space if counts are large
+or bounded repetitions are nested.
+An RE like, say,
+`((((a{1,100}){1,100}){1,100}){1,100}){1,100}'
+will (eventually) run almost any existing machine out of swap space.
+.PP
+There are suspected problems with response to obscure error conditions.
+Notably,
+certain kinds of internal overflow,
+produced only by truly enormous REs or by multiply nested bounded repetitions,
+are probably not handled well.
+.PP
+Due to a mistake in 1003.2, things like `a)b' are legal REs because `)' is
+a special character only in the presence of a previous unmatched `('.
+This can't be fixed until the spec is fixed.
+.PP
+The standard's definition of back references is vague.
+For example, does
+`a\e(\e(b\e)*\e2\e)*d' match `abbbd'?
+Until the standard is clarified,
+behavior in such cases should not be relied on.
+.PP
+The implementation of word-boundary matching is a bit of a kludge,
+and bugs may lurk in combinations of word-boundary matching and anchoring.
diff --git a/usr.sbin/httpd/src/regex/regex.7 b/usr.sbin/httpd/src/regex/regex.7
new file mode 100644
index 00000000000..d89012bda1d
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/regex.7
@@ -0,0 +1,233 @@
+.TH REGEX 7 "7 Feb 1994"
+.BY "Henry Spencer"
+.SH NAME
+regex \- POSIX 1003.2 regular expressions
+.SH DESCRIPTION
+Regular expressions (``RE''s),
+as defined in POSIX 1003.2, come in two forms:
+modern REs (roughly those of
+.IR egrep ;
+1003.2 calls these ``extended'' REs)
+and obsolete REs (roughly those of
+.IR ed ;
+1003.2 ``basic'' REs).
+Obsolete REs mostly exist for backward compatibility in some old programs;
+they will be discussed at the end.
+1003.2 leaves some aspects of RE syntax and semantics open;
+`\(dg' marks decisions on these aspects that
+may not be fully portable to other 1003.2 implementations.
+.PP
+A (modern) RE is one\(dg or more non-empty\(dg \fIbranches\fR,
+separated by `|'.
+It matches anything that matches one of the branches.
+.PP
+A branch is one\(dg or more \fIpieces\fR, concatenated.
+It matches a match for the first, followed by a match for the second, etc.
+.PP
+A piece is an \fIatom\fR possibly followed
+by a single\(dg `*', `+', `?', or \fIbound\fR.
+An atom followed by `*' matches a sequence of 0 or more matches of the atom.
+An atom followed by `+' matches a sequence of 1 or more matches of the atom.
+An atom followed by `?' matches a sequence of 0 or 1 matches of the atom.
+.PP
+A \fIbound\fR is `{' followed by an unsigned decimal integer,
+possibly followed by `,'
+possibly followed by another unsigned decimal integer,
+always followed by `}'.
+The integers must lie between 0 and RE_DUP_MAX (255\(dg) inclusive,
+and if there are two of them, the first may not exceed the second.
+An atom followed by a bound containing one integer \fIi\fR
+and no comma matches
+a sequence of exactly \fIi\fR matches of the atom.
+An atom followed by a bound
+containing one integer \fIi\fR and a comma matches
+a sequence of \fIi\fR or more matches of the atom.
+An atom followed by a bound
+containing two integers \fIi\fR and \fIj\fR matches
+a sequence of \fIi\fR through \fIj\fR (inclusive) matches of the atom.
+.PP
+An atom is a regular expression enclosed in `()' (matching a match for the
+regular expression),
+an empty set of `()' (matching the null string)\(dg,
+a \fIbracket expression\fR (see below), `.'
+(matching any single character), `^' (matching the null string at the
+beginning of a line), `$' (matching the null string at the
+end of a line), a `\e' followed by one of the characters
+`^.[$()|*+?{\e'
+(matching that character taken as an ordinary character),
+a `\e' followed by any other character\(dg
+(matching that character taken as an ordinary character,
+as if the `\e' had not been present\(dg),
+or a single character with no other significance (matching that character).
+A `{' followed by a character other than a digit is an ordinary
+character, not the beginning of a bound\(dg.
+It is illegal to end an RE with `\e'.
+.PP
+A \fIbracket expression\fR is a list of characters enclosed in `[]'.
+It normally matches any single character from the list (but see below).
+If the list begins with `^',
+it matches any single character
+(but see below) \fInot\fR from the rest of the list.
+If two characters in the list are separated by `\-', this is shorthand
+for the full \fIrange\fR of characters between those two (inclusive) in the
+collating sequence,
+e.g. `[0-9]' in ASCII matches any decimal digit.
+It is illegal\(dg for two ranges to share an
+endpoint, e.g. `a-c-e'.
+Ranges are very collating-sequence-dependent,
+and portable programs should avoid relying on them.
+.PP
+To include a literal `]' in the list, make it the first character
+(following a possible `^').
+To include a literal `\-', make it the first or last character,
+or the second endpoint of a range.
+To use a literal `\-' as the first endpoint of a range,
+enclose it in `[.' and `.]' to make it a collating element (see below).
+With the exception of these and some combinations using `[' (see next
+paragraphs), all other special characters, including `\e', lose their
+special significance within a bracket expression.
+.PP
+Within a bracket expression, a collating element (a character,
+a multi-character sequence that collates as if it were a single character,
+or a collating-sequence name for either)
+enclosed in `[.' and `.]' stands for the
+sequence of characters of that collating element.
+The sequence is a single element of the bracket expression's list.
+A bracket expression containing a multi-character collating element
+can thus match more than one character,
+e.g. if the collating sequence includes a `ch' collating element,
+then the RE `[[.ch.]]*c' matches the first five characters
+of `chchcc'.
+.PP
+Within a bracket expression, a collating element enclosed in `[=' and
+`=]' is an equivalence class, standing for the sequences of characters
+of all collating elements equivalent to that one, including itself.
+(If there are no other equivalent collating elements,
+the treatment is as if the enclosing delimiters were `[.' and `.]'.)
+For example, if o and \o'o^' are the members of an equivalence class,
+then `[[=o=]]', `[[=\o'o^'=]]', and `[o\o'o^']' are all synonymous.
+An equivalence class may not\(dg be an endpoint
+of a range.
+.PP
+Within a bracket expression, the name of a \fIcharacter class\fR enclosed
+in `[:' and `:]' stands for the list of all characters belonging to that
+class.
+Standard character class names are:
+.PP
+.RS
+.nf
+.ta 3c 6c 9c
+alnum digit punct
+alpha graph space
+blank lower upper
+cntrl print xdigit
+.fi
+.RE
+.PP
+These stand for the character classes defined in
+.IR ctype (3).
+A locale may provide others.
+A character class may not be used as an endpoint of a range.
+.PP
+There are two special cases\(dg of bracket expressions:
+the bracket expressions `[[:<:]]' and `[[:>:]]' match the null string at
+the beginning and end of a word respectively.
+A word is defined as a sequence of
+word characters
+which is neither preceded nor followed by
+word characters.
+A word character is an
+.I alnum
+character (as defined by
+.IR ctype (3))
+or an underscore.
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+.PP
+In the event that an RE could match more than one substring of a given
+string,
+the RE matches the one starting earliest in the string.
+If the RE could match more than one substring starting at that point,
+it matches the longest.
+Subexpressions also match the longest possible substrings, subject to
+the constraint that the whole match be as long as possible,
+with subexpressions starting earlier in the RE taking priority over
+ones starting later.
+Note that higher-level subexpressions thus take priority over
+their lower-level component subexpressions.
+.PP
+Match lengths are measured in characters, not collating elements.
+A null string is considered longer than no match at all.
+For example,
+`bb*' matches the three middle characters of `abbbc',
+`(wee|week)(knights|nights)' matches all ten characters of `weeknights',
+when `(.*).*' is matched against `abc' the parenthesized subexpression
+matches all three characters, and
+when `(a*)*' is matched against `bc' both the whole RE and the parenthesized
+subexpression match the null string.
+.PP
+If case-independent matching is specified,
+the effect is much as if all case distinctions had vanished from the
+alphabet.
+When an alphabetic that exists in multiple cases appears as an
+ordinary character outside a bracket expression, it is effectively
+transformed into a bracket expression containing both cases,
+e.g. `x' becomes `[xX]'.
+When it appears inside a bracket expression, all case counterparts
+of it are added to the bracket expression, so that (e.g.) `[x]'
+becomes `[xX]' and `[^x]' becomes `[^xX]'.
+.PP
+No particular limit is imposed on the length of REs\(dg.
+Programs intended to be portable should not employ REs longer
+than 256 bytes,
+as an implementation can refuse to accept such REs and remain
+POSIX-compliant.
+.PP
+Obsolete (``basic'') regular expressions differ in several respects.
+`|', `+', and `?' are ordinary characters and there is no equivalent
+for their functionality.
+The delimiters for bounds are `\e{' and `\e}',
+with `{' and `}' by themselves ordinary characters.
+The parentheses for nested subexpressions are `\e(' and `\e)',
+with `(' and `)' by themselves ordinary characters.
+`^' is an ordinary character except at the beginning of the
+RE or\(dg the beginning of a parenthesized subexpression,
+`$' is an ordinary character except at the end of the
+RE or\(dg the end of a parenthesized subexpression,
+and `*' is an ordinary character if it appears at the beginning of the
+RE or the beginning of a parenthesized subexpression
+(after a possible leading `^').
+Finally, there is one new type of atom, a \fIback reference\fR:
+`\e' followed by a non-zero decimal digit \fId\fR
+matches the same sequence of characters
+matched by the \fId\fRth parenthesized subexpression
+(numbering subexpressions by the positions of their opening parentheses,
+left to right),
+so that (e.g.) `\e([bc]\e)\e1' matches `bb' or `cc' but not `bc'.
+.SH SEE ALSO
+regex(3)
+.PP
+POSIX 1003.2, section 2.8 (Regular Expression Notation).
+.SH BUGS
+Having two kinds of REs is a botch.
+.PP
+The current 1003.2 spec says that `)' is an ordinary character in
+the absence of an unmatched `(';
+this was an unintentional result of a wording error,
+and change is likely.
+Avoid relying on it.
+.PP
+Back references are a dreadful botch,
+posing major problems for efficient implementations.
+They are also somewhat vaguely defined
+(does
+`a\e(\e(b\e)*\e2\e)*d' match `abbbd'?).
+Avoid using them.
+.PP
+1003.2's specification of case-independent matching is vague.
+The ``one case implies all cases'' definition given above
+is current consensus among implementors as to the right interpretation.
+.PP
+The syntax for word boundaries is incredibly ugly.
diff --git a/usr.sbin/httpd/src/regex/regex.h b/usr.sbin/httpd/src/regex/regex.h
new file mode 100644
index 00000000000..dde954d8332
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/regex.h
@@ -0,0 +1,73 @@
+#ifndef _REGEX_H_
+#define _REGEX_H_ /* never again */
+/* ========= begin header generated by ./mkh ========= */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* === regex2.h === */
+typedef off_t regoff_t;
+typedef struct {
+ int re_magic;
+ size_t re_nsub; /* number of parenthesized subexpressions */
+ const char *re_endp; /* end pointer for REG_PEND */
+ struct re_guts *re_g; /* none of your business :-) */
+} regex_t;
+typedef struct {
+ regoff_t rm_so; /* start of match */
+ regoff_t rm_eo; /* end of match */
+} regmatch_t;
+
+
+/* === regcomp.c === */
+extern int regcomp(regex_t *, const char *, int);
+#define REG_BASIC 0000
+#define REG_EXTENDED 0001
+#define REG_ICASE 0002
+#define REG_NOSUB 0004
+#define REG_NEWLINE 0010
+#define REG_NOSPEC 0020
+#define REG_PEND 0040
+#define REG_DUMP 0200
+
+
+/* === regerror.c === */
+#define REG_NOMATCH 1
+#define REG_BADPAT 2
+#define REG_ECOLLATE 3
+#define REG_ECTYPE 4
+#define REG_EESCAPE 5
+#define REG_ESUBREG 6
+#define REG_EBRACK 7
+#define REG_EPAREN 8
+#define REG_EBRACE 9
+#define REG_BADBR 10
+#define REG_ERANGE 11
+#define REG_ESPACE 12
+#define REG_BADRPT 13
+#define REG_EMPTY 14
+#define REG_ASSERT 15
+#define REG_INVARG 16
+#define REG_ATOI 255 /* convert name to number (!) */
+#define REG_ITOA 0400 /* convert number to name (!) */
+extern size_t regerror(int, const regex_t *, char *, size_t);
+
+
+/* === regexec.c === */
+extern int regexec(const regex_t *, const char *, size_t, regmatch_t [], int);
+#define REG_NOTBOL 00001
+#define REG_NOTEOL 00002
+#define REG_STARTEND 00004
+#define REG_TRACE 00400 /* tracing of execution */
+#define REG_LARGE 01000 /* force large representation */
+#define REG_BACKR 02000 /* force use of backref code */
+
+
+/* === regfree.c === */
+extern void regfree(regex_t *);
+
+#ifdef __cplusplus
+}
+#endif
+/* ========= end header generated by ./mkh ========= */
+#endif
diff --git a/usr.sbin/httpd/src/regex/regex2.h b/usr.sbin/httpd/src/regex/regex2.h
new file mode 100644
index 00000000000..6ec57b2ed4e
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/regex2.h
@@ -0,0 +1,132 @@
+/*
+ * First, the stuff that ends up in the outside-world include file
+ = typedef off_t regoff_t;
+ = typedef struct {
+ = int re_magic;
+ = size_t re_nsub; // number of parenthesized subexpressions
+ = const char *re_endp; // end pointer for REG_PEND
+ = struct re_guts *re_g; // none of your business :-)
+ = } regex_t;
+ = typedef struct {
+ = regoff_t rm_so; // start of match
+ = regoff_t rm_eo; // end of match
+ = } regmatch_t;
+ */
+/*
+ * internals of regex_t
+ */
+#define MAGIC1 ((('r'^0200)<<8) | 'e')
+
+/*
+ * The internal representation is a *strip*, a sequence of
+ * operators ending with an endmarker. (Some terminology etc. is a
+ * historical relic of earlier versions which used multiple strips.)
+ * Certain oddities in the representation are there to permit running
+ * the machinery backwards; in particular, any deviation from sequential
+ * flow must be marked at both its source and its destination. Some
+ * fine points:
+ *
+ * - OPLUS_ and O_PLUS are *inside* the loop they create.
+ * - OQUEST_ and O_QUEST are *outside* the bypass they create.
+ * - OCH_ and O_CH are *outside* the multi-way branch they create, while
+ * OOR1 and OOR2 are respectively the end and the beginning of one of
+ * the branches. Note that there is an implicit OOR2 following OCH_
+ * and an implicit OOR1 preceding O_CH.
+ *
+ * In state representations, an operator's bit is on to signify a state
+ * immediately *preceding* "execution" of that operator.
+ */
+typedef unsigned long sop; /* strip operator */
+typedef long sopno;
+#define OPRMASK 0xf8000000
+#define OPDMASK 0x07ffffff
+#define OPSHIFT ((unsigned)27)
+#define OP(n) ((n)&OPRMASK)
+#define OPND(n) ((n)&OPDMASK)
+#define SOP(op, opnd) ((op)|(opnd))
+/* operators meaning operand */
+/* (back, fwd are offsets) */
+#define OEND (1<<OPSHIFT) /* endmarker - */
+#define OCHAR (2<<OPSHIFT) /* character unsigned char */
+#define OBOL (3<<OPSHIFT) /* left anchor - */
+#define OEOL (4<<OPSHIFT) /* right anchor - */
+#define OANY (5<<OPSHIFT) /* . - */
+#define OANYOF (6<<OPSHIFT) /* [...] set number */
+#define OBACK_ (7<<OPSHIFT) /* begin \d paren number */
+#define O_BACK (8<<OPSHIFT) /* end \d paren number */
+#define OPLUS_ (9<<OPSHIFT) /* + prefix fwd to suffix */
+#define O_PLUS (10<<OPSHIFT) /* + suffix back to prefix */
+#define OQUEST_ (11<<OPSHIFT) /* ? prefix fwd to suffix */
+#define O_QUEST (12<<OPSHIFT) /* ? suffix back to prefix */
+#define OLPAREN (13<<OPSHIFT) /* ( fwd to ) */
+#define ORPAREN (14<<OPSHIFT) /* ) back to ( */
+#define OCH_ (15<<OPSHIFT) /* begin choice fwd to OOR2 */
+#define OOR1 (16u<<OPSHIFT) /* | pt. 1 back to OOR1 or OCH_ */
+#define OOR2 (17u<<OPSHIFT) /* | pt. 2 fwd to OOR2 or O_CH */
+#define O_CH (18u<<OPSHIFT) /* end choice back to OOR1 */
+#define OBOW (19u<<OPSHIFT) /* begin word - */
+#define OEOW (20u<<OPSHIFT) /* end word - */
+
+/*
+ * Structure for [] character-set representation. Character sets are
+ * done as bit vectors, grouped 8 to a byte vector for compactness.
+ * The individual set therefore has both a pointer to the byte vector
+ * and a mask to pick out the relevant bit of each byte. A hash code
+ * simplifies testing whether two sets could be identical.
+ *
+ * This will get trickier for multicharacter collating elements. As
+ * preliminary hooks for dealing with such things, we also carry along
+ * a string of multi-character elements, and decide the size of the
+ * vectors at run time.
+ */
+typedef struct {
+ uch *ptr; /* -> uch [csetsize] */
+ uch mask; /* bit within array */
+ uch hash; /* hash code */
+ size_t smultis;
+ char *multis; /* -> char[smulti] ab\0cd\0ef\0\0 */
+} cset;
+/* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */
+#define CHadd(cs, c) ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c))
+#define CHsub(cs, c) ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c))
+#define CHIN(cs, c) ((cs)->ptr[(uch)(c)] & (cs)->mask)
+#define MCadd(p, cs, cp) mcadd(p, cs, cp) /* regcomp() internal fns */
+
+/* stuff for character categories */
+typedef unsigned char cat_t;
+
+/*
+ * main compiled-expression structure
+ */
+struct re_guts {
+ int magic;
+# define MAGIC2 ((('R'^0200)<<8)|'E')
+ sop *strip; /* malloced area for strip */
+ int csetsize; /* number of bits in a cset vector */
+ int ncsets; /* number of csets in use */
+ cset *sets; /* -> cset [ncsets] */
+ uch *setbits; /* -> uch[csetsize][ncsets/CHAR_BIT] */
+ int cflags; /* copy of regcomp() cflags argument */
+ sopno nstates; /* = number of sops */
+ sopno firststate; /* the initial OEND (normally 0) */
+ sopno laststate; /* the final OEND */
+ int iflags; /* internal flags */
+# define USEBOL 01 /* used ^ */
+# define USEEOL 02 /* used $ */
+# define BAD 04 /* something wrong */
+ int nbol; /* number of ^ used */
+ int neol; /* number of $ used */
+ int ncategories; /* how many character categories */
+ cat_t *categories; /* ->catspace[-CHAR_MIN] */
+ char *must; /* match must contain this string */
+ int mlen; /* length of must */
+ size_t nsub; /* copy of re_nsub */
+ int backrefs; /* does it use back references? */
+ sopno nplus; /* how deep does it nest +s? */
+ /* catspace must be last */
+ cat_t catspace[1]; /* actually [NC] */
+};
+
+/* misc utilities */
+#define OUT (CHAR_MAX+1) /* a non-character value */
+#define ISWORD(c) (isalnum(c) || (c) == '_')
diff --git a/usr.sbin/httpd/src/regex/regexec.c b/usr.sbin/httpd/src/regex/regexec.c
new file mode 100644
index 00000000000..63bb1c37130
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/regexec.c
@@ -0,0 +1,140 @@
+/*
+ * the outer shell of regexec()
+ *
+ * This file includes engine.c *twice*, after muchos fiddling with the
+ * macros that code uses. This lets the same code operate on two different
+ * representations for state sets.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regex2.h"
+
+#ifndef NDEBUG
+static int nope = 0; /* for use in asserts; shuts lint up */
+#endif
+
+/* macros for manipulating states, small version */
+#define states long
+#define states1 states /* for later use in regexec() decision */
+#define CLEAR(v) ((v) = 0)
+#define SET0(v, n) ((v) &= ~(1 << (n)))
+#define SET1(v, n) ((v) |= 1 << (n))
+#define ISSET(v, n) ((v) & (1 << (n)))
+#define ASSIGN(d, s) ((d) = (s))
+#define EQ(a, b) ((a) == (b))
+#define STATEVARS int dummy /* dummy version */
+#define STATESETUP(m, n) /* nothing */
+#define STATETEARDOWN(m) /* nothing */
+#define SETUP(v) ((v) = 0)
+#define onestate int
+#define INIT(o, n) ((o) = (unsigned)1 << (n))
+#define INC(o) ((o) <<= 1)
+#define ISSTATEIN(v, o) ((v) & (o))
+/* some abbreviations; note that some of these know variable names! */
+/* do "if I'm here, I can also be there" etc without branches */
+#define FWD(dst, src, n) ((dst) |= ((unsigned)(src)&(here)) << (n))
+#define BACK(dst, src, n) ((dst) |= ((unsigned)(src)&(here)) >> (n))
+#define ISSETBACK(v, n) ((v) & ((unsigned)here >> (n)))
+/* function names */
+#define SNAMES /* engine.c looks after details */
+
+#include "engine.c"
+
+/* now undo things */
+#undef states
+#undef CLEAR
+#undef SET0
+#undef SET1
+#undef ISSET
+#undef ASSIGN
+#undef EQ
+#undef STATEVARS
+#undef STATESETUP
+#undef STATETEARDOWN
+#undef SETUP
+#undef onestate
+#undef INIT
+#undef INC
+#undef ISSTATEIN
+#undef FWD
+#undef BACK
+#undef ISSETBACK
+#undef SNAMES
+
+/* macros for manipulating states, large version */
+#define states char *
+#define CLEAR(v) memset(v, 0, m->g->nstates)
+#define SET0(v, n) ((v)[n] = 0)
+#define SET1(v, n) ((v)[n] = 1)
+#define ISSET(v, n) ((v)[n])
+#define ASSIGN(d, s) memcpy(d, s, m->g->nstates)
+#define EQ(a, b) (memcmp(a, b, m->g->nstates) == 0)
+#define STATEVARS int vn; char *space
+#define STATESETUP(m, nv) { (m)->space = malloc((nv)*(m)->g->nstates); \
+ if ((m)->space == NULL) return(REG_ESPACE); \
+ (m)->vn = 0; }
+#define STATETEARDOWN(m) { free((m)->space); }
+#define SETUP(v) ((v) = &m->space[m->vn++ * m->g->nstates])
+#define onestate int
+#define INIT(o, n) ((o) = (n))
+#define INC(o) ((o)++)
+#define ISSTATEIN(v, o) ((v)[o])
+/* some abbreviations; note that some of these know variable names! */
+/* do "if I'm here, I can also be there" etc without branches */
+#define FWD(dst, src, n) ((dst)[here+(n)] |= (src)[here])
+#define BACK(dst, src, n) ((dst)[here-(n)] |= (src)[here])
+#define ISSETBACK(v, n) ((v)[here - (n)])
+/* function names */
+#define LNAMES /* flag */
+
+#include "engine.c"
+
+/*
+ - regexec - interface for matching
+ = extern int regexec(const regex_t *, const char *, size_t, \
+ = regmatch_t [], int);
+ = #define REG_NOTBOL 00001
+ = #define REG_NOTEOL 00002
+ = #define REG_STARTEND 00004
+ = #define REG_TRACE 00400 // tracing of execution
+ = #define REG_LARGE 01000 // force large representation
+ = #define REG_BACKR 02000 // force use of backref code
+ *
+ * We put this here so we can exploit knowledge of the state representation
+ * when choosing which matcher to call. Also, by this point the matchers
+ * have been prototyped.
+ */
+int /* 0 success, REG_NOMATCH failure */
+regexec(preg, string, nmatch, pmatch, eflags)
+const regex_t *preg;
+const char *string;
+size_t nmatch;
+regmatch_t pmatch[];
+int eflags;
+{
+ register struct re_guts *g = preg->re_g;
+#ifdef REDEBUG
+# define GOODFLAGS(f) (f)
+#else
+# define GOODFLAGS(f) ((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND))
+#endif
+
+ if (preg->re_magic != MAGIC1 || g->magic != MAGIC2)
+ return(REG_BADPAT);
+ assert(!(g->iflags&BAD));
+ if (g->iflags&BAD) /* backstop for no-debug case */
+ return(REG_BADPAT);
+ eflags = GOODFLAGS(eflags);
+
+ if (g->nstates <= CHAR_BIT*sizeof(states1) && !(eflags&REG_LARGE))
+ return(smatcher(g, (char *)string, nmatch, pmatch, eflags));
+ else
+ return(lmatcher(g, (char *)string, nmatch, pmatch, eflags));
+}
diff --git a/usr.sbin/httpd/src/regex/regfree.c b/usr.sbin/httpd/src/regex/regfree.c
new file mode 100644
index 00000000000..9a6acf1733a
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/regfree.c
@@ -0,0 +1,37 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regex2.h"
+
+/*
+ - regfree - free everything
+ = extern void regfree(regex_t *);
+ */
+void
+regfree(preg)
+regex_t *preg;
+{
+ register struct re_guts *g;
+
+ if (preg->re_magic != MAGIC1) /* oops */
+ return; /* nice to complain, but hard */
+
+ g = preg->re_g;
+ if (g == NULL || g->magic != MAGIC2) /* oops again */
+ return;
+ preg->re_magic = 0; /* mark it invalid */
+ g->magic = 0; /* mark it invalid */
+
+ if (g->strip != NULL)
+ free((char *)g->strip);
+ if (g->sets != NULL)
+ free((char *)g->sets);
+ if (g->setbits != NULL)
+ free((char *)g->setbits);
+ if (g->must != NULL)
+ free(g->must);
+ free((char *)g);
+}
diff --git a/usr.sbin/httpd/src/regex/split.c b/usr.sbin/httpd/src/regex/split.c
new file mode 100644
index 00000000000..188bdb775b9
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/split.c
@@ -0,0 +1,316 @@
+#include <stdio.h>
+#include <string.h>
+
+/*
+ - split - divide a string into fields, like awk split()
+ = int split(char *string, char *fields[], int nfields, char *sep);
+ */
+int /* number of fields, including overflow */
+split(string, fields, nfields, sep)
+char *string;
+char *fields[]; /* list is not NULL-terminated */
+int nfields; /* number of entries available in fields[] */
+char *sep; /* "" white, "c" single char, "ab" [ab]+ */
+{
+ register char *p = string;
+ register char c; /* latest character */
+ register char sepc = sep[0];
+ register char sepc2;
+ register int fn;
+ register char **fp = fields;
+ register char *sepp;
+ register int trimtrail;
+
+ /* white space */
+ if (sepc == '\0') {
+ while ((c = *p++) == ' ' || c == '\t')
+ continue;
+ p--;
+ trimtrail = 1;
+ sep = " \t"; /* note, code below knows this is 2 long */
+ sepc = ' ';
+ } else
+ trimtrail = 0;
+ sepc2 = sep[1]; /* now we can safely pick this up */
+
+ /* catch empties */
+ if (*p == '\0')
+ return(0);
+
+ /* single separator */
+ if (sepc2 == '\0') {
+ fn = nfields;
+ for (;;) {
+ *fp++ = p;
+ fn--;
+ if (fn == 0)
+ break;
+ while ((c = *p++) != sepc)
+ if (c == '\0')
+ return(nfields - fn);
+ *(p-1) = '\0';
+ }
+ /* we have overflowed the fields vector -- just count them */
+ fn = nfields;
+ for (;;) {
+ while ((c = *p++) != sepc)
+ if (c == '\0')
+ return(fn);
+ fn++;
+ }
+ /* not reached */
+ }
+
+ /* two separators */
+ if (sep[2] == '\0') {
+ fn = nfields;
+ for (;;) {
+ *fp++ = p;
+ fn--;
+ while ((c = *p++) != sepc && c != sepc2)
+ if (c == '\0') {
+ if (trimtrail && **(fp-1) == '\0')
+ fn++;
+ return(nfields - fn);
+ }
+ if (fn == 0)
+ break;
+ *(p-1) = '\0';
+ while ((c = *p++) == sepc || c == sepc2)
+ continue;
+ p--;
+ }
+ /* we have overflowed the fields vector -- just count them */
+ fn = nfields;
+ while (c != '\0') {
+ while ((c = *p++) == sepc || c == sepc2)
+ continue;
+ p--;
+ fn++;
+ while ((c = *p++) != '\0' && c != sepc && c != sepc2)
+ continue;
+ }
+ /* might have to trim trailing white space */
+ if (trimtrail) {
+ p--;
+ while ((c = *--p) == sepc || c == sepc2)
+ continue;
+ p++;
+ if (*p != '\0') {
+ if (fn == nfields+1)
+ *p = '\0';
+ fn--;
+ }
+ }
+ return(fn);
+ }
+
+ /* n separators */
+ fn = 0;
+ for (;;) {
+ if (fn < nfields)
+ *fp++ = p;
+ fn++;
+ for (;;) {
+ c = *p++;
+ if (c == '\0')
+ return(fn);
+ sepp = sep;
+ while ((sepc = *sepp++) != '\0' && sepc != c)
+ continue;
+ if (sepc != '\0') /* it was a separator */
+ break;
+ }
+ if (fn < nfields)
+ *(p-1) = '\0';
+ for (;;) {
+ c = *p++;
+ sepp = sep;
+ while ((sepc = *sepp++) != '\0' && sepc != c)
+ continue;
+ if (sepc == '\0') /* it wasn't a separator */
+ break;
+ }
+ p--;
+ }
+
+ /* not reached */
+}
+
+#ifdef TEST_SPLIT
+
+
+/*
+ * test program
+ * pgm runs regression
+ * pgm sep splits stdin lines by sep
+ * pgm str sep splits str by sep
+ * pgm str sep n splits str by sep n times
+ */
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ char buf[512];
+ register int n;
+# define MNF 10
+ char *fields[MNF];
+
+ if (argc > 4)
+ for (n = atoi(argv[3]); n > 0; n--) {
+ (void) strcpy(buf, argv[1]);
+ }
+ else if (argc > 3)
+ for (n = atoi(argv[3]); n > 0; n--) {
+ (void) strcpy(buf, argv[1]);
+ (void) split(buf, fields, MNF, argv[2]);
+ }
+ else if (argc > 2)
+ dosplit(argv[1], argv[2]);
+ else if (argc > 1)
+ while (fgets(buf, sizeof(buf), stdin) != NULL) {
+ buf[strlen(buf)-1] = '\0'; /* stomp newline */
+ dosplit(buf, argv[1]);
+ }
+ else
+ regress();
+
+ exit(0);
+}
+
+dosplit(string, seps)
+char *string;
+char *seps;
+{
+# define NF 5
+ char *fields[NF];
+ register int nf;
+
+ nf = split(string, fields, NF, seps);
+ print(nf, NF, fields);
+}
+
+print(nf, nfp, fields)
+int nf;
+int nfp;
+char *fields[];
+{
+ register int fn;
+ register int bound;
+
+ bound = (nf > nfp) ? nfp : nf;
+ printf("%d:\t", nf);
+ for (fn = 0; fn < bound; fn++)
+ printf("\"%s\"%s", fields[fn], (fn+1 < nf) ? ", " : "\n");
+}
+
+#define RNF 5 /* some table entries know this */
+struct {
+ char *str;
+ char *seps;
+ int nf;
+ char *fi[RNF];
+} tests[] = {
+ "", " ", 0, { "" },
+ " ", " ", 2, { "", "" },
+ "x", " ", 1, { "x" },
+ "xy", " ", 1, { "xy" },
+ "x y", " ", 2, { "x", "y" },
+ "abc def g ", " ", 5, { "abc", "def", "", "g", "" },
+ " a bcd", " ", 4, { "", "", "a", "bcd" },
+ "a b c d e f", " ", 6, { "a", "b", "c", "d", "e f" },
+ " a b c d ", " ", 6, { "", "a", "b", "c", "d " },
+
+ "", " _", 0, { "" },
+ " ", " _", 2, { "", "" },
+ "x", " _", 1, { "x" },
+ "x y", " _", 2, { "x", "y" },
+ "ab _ cd", " _", 2, { "ab", "cd" },
+ " a_b c ", " _", 5, { "", "a", "b", "c", "" },
+ "a b c_d e f", " _", 6, { "a", "b", "c", "d", "e f" },
+ " a b c d ", " _", 6, { "", "a", "b", "c", "d " },
+
+ "", " _~", 0, { "" },
+ " ", " _~", 2, { "", "" },
+ "x", " _~", 1, { "x" },
+ "x y", " _~", 2, { "x", "y" },
+ "ab _~ cd", " _~", 2, { "ab", "cd" },
+ " a_b c~", " _~", 5, { "", "a", "b", "c", "" },
+ "a b_c d~e f", " _~", 6, { "a", "b", "c", "d", "e f" },
+ "~a b c d ", " _~", 6, { "", "a", "b", "c", "d " },
+
+ "", " _~-", 0, { "" },
+ " ", " _~-", 2, { "", "" },
+ "x", " _~-", 1, { "x" },
+ "x y", " _~-", 2, { "x", "y" },
+ "ab _~- cd", " _~-", 2, { "ab", "cd" },
+ " a_b c~", " _~-", 5, { "", "a", "b", "c", "" },
+ "a b_c-d~e f", " _~-", 6, { "a", "b", "c", "d", "e f" },
+ "~a-b c d ", " _~-", 6, { "", "a", "b", "c", "d " },
+
+ "", " ", 0, { "" },
+ " ", " ", 2, { "", "" },
+ "x", " ", 1, { "x" },
+ "xy", " ", 1, { "xy" },
+ "x y", " ", 2, { "x", "y" },
+ "abc def g ", " ", 4, { "abc", "def", "g", "" },
+ " a bcd", " ", 3, { "", "a", "bcd" },
+ "a b c d e f", " ", 6, { "a", "b", "c", "d", "e f" },
+ " a b c d ", " ", 6, { "", "a", "b", "c", "d " },
+
+ "", "", 0, { "" },
+ " ", "", 0, { "" },
+ "x", "", 1, { "x" },
+ "xy", "", 1, { "xy" },
+ "x y", "", 2, { "x", "y" },
+ "abc def g ", "", 3, { "abc", "def", "g" },
+ "\t a bcd", "", 2, { "a", "bcd" },
+ " a \tb\t c ", "", 3, { "a", "b", "c" },
+ "a b c d e ", "", 5, { "a", "b", "c", "d", "e" },
+ "a b\tc d e f", "", 6, { "a", "b", "c", "d", "e f" },
+ " a b c d e f ", "", 6, { "a", "b", "c", "d", "e f " },
+
+ NULL, NULL, 0, { NULL },
+};
+
+regress()
+{
+ char buf[512];
+ register int n;
+ char *fields[RNF+1];
+ register int nf;
+ register int i;
+ register int printit;
+ register char *f;
+
+ for (n = 0; tests[n].str != NULL; n++) {
+ (void) strcpy(buf, tests[n].str);
+ fields[RNF] = NULL;
+ nf = split(buf, fields, RNF, tests[n].seps);
+ printit = 0;
+ if (nf != tests[n].nf) {
+ printf("split `%s' by `%s' gave %d fields, not %d\n",
+ tests[n].str, tests[n].seps, nf, tests[n].nf);
+ printit = 1;
+ } else if (fields[RNF] != NULL) {
+ printf("split() went beyond array end\n");
+ printit = 1;
+ } else {
+ for (i = 0; i < nf && i < RNF; i++) {
+ f = fields[i];
+ if (f == NULL)
+ f = "(NULL)";
+ if (strcmp(f, tests[n].fi[i]) != 0) {
+ printf("split `%s' by `%s', field %d is `%s', not `%s'\n",
+ tests[n].str, tests[n].seps,
+ i, fields[i], tests[n].fi[i]);
+ printit = 1;
+ }
+ }
+ }
+ if (printit)
+ print(nf, RNF, fields);
+ }
+}
+#endif
diff --git a/usr.sbin/httpd/src/regex/tests b/usr.sbin/httpd/src/regex/tests
new file mode 100644
index 00000000000..c05846177f5
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/tests
@@ -0,0 +1,475 @@
+# regular expression test set
+# Lines are at least three fields, separated by one or more tabs. "" stands
+# for an empty field. First field is an RE. Second field is flags. If
+# C flag given, regcomp() is expected to fail, and the third field is the
+# error name (minus the leading REG_).
+#
+# Otherwise it is expected to succeed, and the third field is the string to
+# try matching it against. If there is no fourth field, the match is
+# expected to fail. If there is a fourth field, it is the substring that
+# the RE is expected to match. If there is a fifth field, it is a comma-
+# separated list of what the subexpressions should match, with - indicating
+# no match for that one. In both the fourth and fifth fields, a (sub)field
+# starting with @ indicates that the (sub)expression is expected to match
+# a null string followed by the stuff after the @; this provides a way to
+# test where null strings match. The character `N' in REs and strings
+# is newline, `S' is space, `T' is tab, `Z' is NUL.
+#
+# The full list of flags:
+# - placeholder, does nothing
+# b RE is a BRE, not an ERE
+# & try it as both an ERE and a BRE
+# C regcomp() error expected, third field is error name
+# i REG_ICASE
+# m ("mundane") REG_NOSPEC
+# s REG_NOSUB (not really testable)
+# n REG_NEWLINE
+# ^ REG_NOTBOL
+# $ REG_NOTEOL
+# # REG_STARTEND (see below)
+# p REG_PEND
+#
+# For REG_STARTEND, the start/end offsets are those of the substring
+# enclosed in ().
+
+# basics
+a & a a
+abc & abc abc
+abc|de - abc abc
+a|b|c - abc a
+
+# parentheses and perversions thereof
+a(b)c - abc abc
+a\(b\)c b abc abc
+a( C EPAREN
+a( b a( a(
+a\( - a( a(
+a\( bC EPAREN
+a\(b bC EPAREN
+a(b C EPAREN
+a(b b a(b a(b
+# gag me with a right parenthesis -- 1003.2 goofed here (my fault, partly)
+a) - a) a)
+) - ) )
+# end gagging (in a just world, those *should* give EPAREN)
+a) b a) a)
+a\) bC EPAREN
+\) bC EPAREN
+a()b - ab ab
+a\(\)b b ab ab
+
+# anchoring and REG_NEWLINE
+^abc$ & abc abc
+a^b - a^b
+a^b b a^b a^b
+a$b - a$b
+a$b b a$b a$b
+^ & abc @abc
+$ & abc @
+^$ & "" @
+$^ - "" @
+\($\)\(^\) b "" @
+# stop retching, those are legitimate (although disgusting)
+^^ - "" @
+$$ - "" @
+b$ & abNc
+b$ &n abNc b
+^b$ & aNbNc
+^b$ &n aNbNc b
+^$ &n aNNb @Nb
+^$ n abc
+^$ n abcN @
+$^ n aNNb @Nb
+\($\)\(^\) bn aNNb @Nb
+^^ n^ aNNb @Nb
+$$ n aNNb @NN
+^a ^ a
+a$ $ a
+^a ^n aNb
+^b ^n aNb b
+a$ $n bNa
+b$ $n bNa b
+a*(^b$)c* - b b
+a*\(^b$\)c* b b b
+
+# certain syntax errors and non-errors
+| C EMPTY
+| b | |
+* C BADRPT
+* b * *
++ C BADRPT
+? C BADRPT
+"" &C EMPTY
+() - abc @abc
+\(\) b abc @abc
+a||b C EMPTY
+|ab C EMPTY
+ab| C EMPTY
+(|a)b C EMPTY
+(a|)b C EMPTY
+(*a) C BADRPT
+(+a) C BADRPT
+(?a) C BADRPT
+({1}a) C BADRPT
+\(\{1\}a\) bC BADRPT
+(a|*b) C BADRPT
+(a|+b) C BADRPT
+(a|?b) C BADRPT
+(a|{1}b) C BADRPT
+^* C BADRPT
+^* b * *
+^+ C BADRPT
+^? C BADRPT
+^{1} C BADRPT
+^\{1\} bC BADRPT
+
+# metacharacters, backslashes
+a.c & abc abc
+a[bc]d & abd abd
+a\*c & a*c a*c
+a\\b & a\b a\b
+a\\\*b & a\*b a\*b
+a\bc & abc abc
+a\ &C EESCAPE
+a\\bc & a\bc a\bc
+\{ bC BADRPT
+a\[b & a[b a[b
+a[b &C EBRACK
+# trailing $ is a peculiar special case for the BRE code
+a$ & a a
+a$ & a$
+a\$ & a
+a\$ & a$ a$
+a\\$ & a
+a\\$ & a$
+a\\$ & a\$
+a\\$ & a\ a\
+
+# back references, ugh
+a\(b\)\2c bC ESUBREG
+a\(b\1\)c bC ESUBREG
+a\(b*\)c\1d b abbcbbd abbcbbd bb
+a\(b*\)c\1d b abbcbd
+a\(b*\)c\1d b abbcbbbd
+^\(.\)\1 b abc
+a\([bc]\)\1d b abcdabbd abbd b
+a\(\([bc]\)\2\)*d b abbccd abbccd
+a\(\([bc]\)\2\)*d b abbcbd
+# actually, this next one probably ought to fail, but the spec is unclear
+a\(\(b\)*\2\)*d b abbbd abbbd
+# here is a case that no NFA implementation does right
+\(ab*\)[ab]*\1 b ababaaa ababaaa a
+# check out normal matching in the presence of back refs
+\(a\)\1bcd b aabcd aabcd
+\(a\)\1bc*d b aabcd aabcd
+\(a\)\1bc*d b aabd aabd
+\(a\)\1bc*d b aabcccd aabcccd
+\(a\)\1bc*[ce]d b aabcccd aabcccd
+^\(a\)\1b\(c\)*cd$ b aabcccd aabcccd
+
+# ordinary repetitions
+ab*c & abc abc
+ab+c - abc abc
+ab?c - abc abc
+a\(*\)b b a*b a*b
+a\(**\)b b ab ab
+a\(***\)b bC BADRPT
+*a b *a *a
+**a b a a
+***a bC BADRPT
+
+# the dreaded bounded repetitions
+{ & { {
+{abc & {abc {abc
+{1 C BADRPT
+{1} C BADRPT
+a{b & a{b a{b
+a{1}b - ab ab
+a\{1\}b b ab ab
+a{1,}b - ab ab
+a\{1,\}b b ab ab
+a{1,2}b - aab aab
+a\{1,2\}b b aab aab
+a{1 C EBRACE
+a\{1 bC EBRACE
+a{1a C EBRACE
+a\{1a bC EBRACE
+a{1a} C BADBR
+a\{1a\} bC BADBR
+a{,2} - a{,2} a{,2}
+a\{,2\} bC BADBR
+a{,} - a{,} a{,}
+a\{,\} bC BADBR
+a{1,x} C BADBR
+a\{1,x\} bC BADBR
+a{1,x C EBRACE
+a\{1,x bC EBRACE
+a{300} C BADBR
+a\{300\} bC BADBR
+a{1,0} C BADBR
+a\{1,0\} bC BADBR
+ab{0,0}c - abcac ac
+ab\{0,0\}c b abcac ac
+ab{0,1}c - abcac abc
+ab\{0,1\}c b abcac abc
+ab{0,3}c - abbcac abbc
+ab\{0,3\}c b abbcac abbc
+ab{1,1}c - acabc abc
+ab\{1,1\}c b acabc abc
+ab{1,3}c - acabc abc
+ab\{1,3\}c b acabc abc
+ab{2,2}c - abcabbc abbc
+ab\{2,2\}c b abcabbc abbc
+ab{2,4}c - abcabbc abbc
+ab\{2,4\}c b abcabbc abbc
+((a{1,10}){1,10}){1,10} - a a a,a
+
+# multiple repetitions
+a** &C BADRPT
+a++ C BADRPT
+a?? C BADRPT
+a*+ C BADRPT
+a*? C BADRPT
+a+* C BADRPT
+a+? C BADRPT
+a?* C BADRPT
+a?+ C BADRPT
+a{1}{1} C BADRPT
+a*{1} C BADRPT
+a+{1} C BADRPT
+a?{1} C BADRPT
+a{1}* C BADRPT
+a{1}+ C BADRPT
+a{1}? C BADRPT
+a*{b} - a{b} a{b}
+a\{1\}\{1\} bC BADRPT
+a*\{1\} bC BADRPT
+a\{1\}* bC BADRPT
+
+# brackets, and numerous perversions thereof
+a[b]c & abc abc
+a[ab]c & abc abc
+a[^ab]c & adc adc
+a[]b]c & a]c a]c
+a[[b]c & a[c a[c
+a[-b]c & a-c a-c
+a[^]b]c & adc adc
+a[^-b]c & adc adc
+a[b-]c & a-c a-c
+a[b &C EBRACK
+a[] &C EBRACK
+a[1-3]c & a2c a2c
+a[3-1]c &C ERANGE
+a[1-3-5]c &C ERANGE
+a[[.-.]--]c & a-c a-c
+a[1- &C ERANGE
+a[[. &C EBRACK
+a[[.x &C EBRACK
+a[[.x. &C EBRACK
+a[[.x.] &C EBRACK
+a[[.x.]] & ax ax
+a[[.x,.]] &C ECOLLATE
+a[[.one.]]b & a1b a1b
+a[[.notdef.]]b &C ECOLLATE
+a[[.].]]b & a]b a]b
+a[[:alpha:]]c & abc abc
+a[[:notdef:]]c &C ECTYPE
+a[[: &C EBRACK
+a[[:alpha &C EBRACK
+a[[:alpha:] &C EBRACK
+a[[:alpha,:] &C ECTYPE
+a[[:]:]]b &C ECTYPE
+a[[:-:]]b &C ECTYPE
+a[[:alph:]] &C ECTYPE
+a[[:alphabet:]] &C ECTYPE
+[[:alnum:]]+ - -%@a0X- a0X
+[[:alpha:]]+ - -%@aX0- aX
+[[:blank:]]+ - aSSTb SST
+[[:cntrl:]]+ - aNTb NT
+[[:digit:]]+ - a019b 019
+[[:graph:]]+ - Sa%bS a%b
+[[:lower:]]+ - AabC ab
+[[:print:]]+ - NaSbN aSb
+[[:punct:]]+ - S%-&T %-&
+[[:space:]]+ - aSNTb SNT
+[[:upper:]]+ - aBCd BC
+[[:xdigit:]]+ - p0f3Cq 0f3C
+a[[=b=]]c & abc abc
+a[[= &C EBRACK
+a[[=b &C EBRACK
+a[[=b= &C EBRACK
+a[[=b=] &C EBRACK
+a[[=b,=]] &C ECOLLATE
+a[[=one=]]b & a1b a1b
+
+# complexities
+a(((b)))c - abc abc
+a(b|(c))d - abd abd
+a(b*|c)d - abbd abbd
+# just gotta have one DFA-buster, of course
+a[ab]{20} - aaaaabaaaabaaaabaaaab aaaaabaaaabaaaabaaaab
+# and an inline expansion in case somebody gets tricky
+a[ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab] - aaaaabaaaabaaaabaaaab aaaaabaaaabaaaabaaaab
+# and in case somebody just slips in an NFA...
+a[ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab](wee|week)(knights|night) - aaaaabaaaabaaaabaaaabweeknights aaaaabaaaabaaaabaaaabweeknights
+# fish for anomalies as the number of states passes 32
+12345678901234567890123456789 - a12345678901234567890123456789b 12345678901234567890123456789
+123456789012345678901234567890 - a123456789012345678901234567890b 123456789012345678901234567890
+1234567890123456789012345678901 - a1234567890123456789012345678901b 1234567890123456789012345678901
+12345678901234567890123456789012 - a12345678901234567890123456789012b 12345678901234567890123456789012
+123456789012345678901234567890123 - a123456789012345678901234567890123b 123456789012345678901234567890123
+# and one really big one, beyond any plausible word width
+1234567890123456789012345678901234567890123456789012345678901234567890 - a1234567890123456789012345678901234567890123456789012345678901234567890b 1234567890123456789012345678901234567890123456789012345678901234567890
+# fish for problems as brackets go past 8
+[ab][cd][ef][gh][ij][kl][mn] - xacegikmoq acegikm
+[ab][cd][ef][gh][ij][kl][mn][op] - xacegikmoq acegikmo
+[ab][cd][ef][gh][ij][kl][mn][op][qr] - xacegikmoqy acegikmoq
+[ab][cd][ef][gh][ij][kl][mn][op][q] - xacegikmoqy acegikmoq
+
+# subtleties of matching
+abc & xabcy abc
+a\(b\)?c\1d b acd
+aBc i Abc Abc
+a[Bc]*d i abBCcd abBCcd
+0[[:upper:]]1 &i 0a1 0a1
+0[[:lower:]]1 &i 0A1 0A1
+a[^b]c &i abc
+a[^b]c &i aBc
+a[^b]c &i adc adc
+[a]b[c] - abc abc
+[a]b[a] - aba aba
+[abc]b[abc] - abc abc
+[abc]b[abd] - abd abd
+a(b?c)+d - accd accd
+(wee|week)(knights|night) - weeknights weeknights
+(we|wee|week|frob)(knights|night|day) - weeknights weeknights
+a[bc]d - xyzaaabcaababdacd abd
+a[ab]c - aaabc abc
+abc s abc abc
+a* & b @b
+
+# Let's have some fun -- try to match a C comment.
+# first the obvious, which looks okay at first glance...
+/\*.*\*/ - /*x*/ /*x*/
+# but...
+/\*.*\*/ - /*x*/y/*z*/ /*x*/y/*z*/
+# okay, we must not match */ inside; try to do that...
+/\*([^*]|\*[^/])*\*/ - /*x*/ /*x*/
+/\*([^*]|\*[^/])*\*/ - /*x*/y/*z*/ /*x*/
+# but...
+/\*([^*]|\*[^/])*\*/ - /*x**/y/*z*/ /*x**/y/*z*/
+# and a still fancier version, which does it right (I think)...
+/\*([^*]|\*+[^*/])*\*+/ - /*x*/ /*x*/
+/\*([^*]|\*+[^*/])*\*+/ - /*x*/y/*z*/ /*x*/
+/\*([^*]|\*+[^*/])*\*+/ - /*x**/y/*z*/ /*x**/
+/\*([^*]|\*+[^*/])*\*+/ - /*x****/y/*z*/ /*x****/
+/\*([^*]|\*+[^*/])*\*+/ - /*x**x*/y/*z*/ /*x**x*/
+/\*([^*]|\*+[^*/])*\*+/ - /*x***x/y/*z*/ /*x***x/y/*z*/
+
+# subexpressions
+a(b)(c)d - abcd abcd b,c
+a(((b)))c - abc abc b,b,b
+a(b|(c))d - abd abd b,-
+a(b*|c|e)d - abbd abbd bb
+a(b*|c|e)d - acd acd c
+a(b*|c|e)d - ad ad @d
+a(b?)c - abc abc b
+a(b?)c - ac ac @c
+a(b+)c - abc abc b
+a(b+)c - abbbc abbbc bbb
+a(b*)c - ac ac @c
+(a|ab)(bc([de]+)f|cde) - abcdef abcdef a,bcdef,de
+# the regression tester only asks for 9 subexpressions
+a(b)(c)(d)(e)(f)(g)(h)(i)(j)k - abcdefghijk abcdefghijk b,c,d,e,f,g,h,i,j
+a(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)l - abcdefghijkl abcdefghijkl b,c,d,e,f,g,h,i,j,k
+a([bc]?)c - abc abc b
+a([bc]?)c - ac ac @c
+a([bc]+)c - abc abc b
+a([bc]+)c - abcc abcc bc
+a([bc]+)bc - abcbc abcbc bc
+a(bb+|b)b - abb abb b
+a(bbb+|bb+|b)b - abb abb b
+a(bbb+|bb+|b)b - abbb abbb bb
+a(bbb+|bb+|b)bb - abbb abbb b
+(.*).* - abcdef abcdef abcdef
+(a*)* - bc @b @b
+
+# do we get the right subexpression when it is used more than once?
+a(b|c)*d - ad ad -
+a(b|c)*d - abcd abcd c
+a(b|c)+d - abd abd b
+a(b|c)+d - abcd abcd c
+a(b|c?)+d - ad ad @d
+a(b|c?)+d - abcd abcd @d
+a(b|c){0,0}d - ad ad -
+a(b|c){0,1}d - ad ad -
+a(b|c){0,1}d - abd abd b
+a(b|c){0,2}d - ad ad -
+a(b|c){0,2}d - abcd abcd c
+a(b|c){0,}d - ad ad -
+a(b|c){0,}d - abcd abcd c
+a(b|c){1,1}d - abd abd b
+a(b|c){1,1}d - acd acd c
+a(b|c){1,2}d - abd abd b
+a(b|c){1,2}d - abcd abcd c
+a(b|c){1,}d - abd abd b
+a(b|c){1,}d - abcd abcd c
+a(b|c){2,2}d - acbd acbd b
+a(b|c){2,2}d - abcd abcd c
+a(b|c){2,4}d - abcd abcd c
+a(b|c){2,4}d - abcbd abcbd b
+a(b|c){2,4}d - abcbcd abcbcd c
+a(b|c){2,}d - abcd abcd c
+a(b|c){2,}d - abcbd abcbd b
+a(b+|((c)*))+d - abd abd @d,@d,-
+a(b+|((c)*))+d - abcd abcd @d,@d,-
+
+# check out the STARTEND option
+[abc] &# a(b)c b
+[abc] &# a(d)c
+[abc] &# a(bc)d b
+[abc] &# a(dc)d c
+. &# a()c
+b.*c &# b(bc)c bc
+b.* &# b(bc)c bc
+.*c &# b(bc)c bc
+
+# plain strings, with the NOSPEC flag
+abc m abc abc
+abc m xabcy abc
+abc m xyz
+a*b m aba*b a*b
+a*b m ab
+"" mC EMPTY
+
+# cases involving NULs
+aZb & a a
+aZb &p a
+aZb &p# (aZb) aZb
+aZ*b &p# (ab) ab
+a.b &# (aZb) aZb
+a.* &# (aZb)c aZb
+
+# word boundaries (ick)
+[[:<:]]a & a a
+[[:<:]]a & ba
+[[:<:]]a & -a a
+a[[:>:]] & a a
+a[[:>:]] & ab
+a[[:>:]] & a- a
+[[:<:]]a.c[[:>:]] & axcd-dayc-dazce-abc abc
+[[:<:]]a.c[[:>:]] & axcd-dayc-dazce-abc-q abc
+[[:<:]]a.c[[:>:]] & axc-dayc-dazce-abc axc
+[[:<:]]b.c[[:>:]] & a_bxc-byc_d-bzc-q bzc
+[[:<:]].x..[[:>:]] & y_xa_-_xb_y-_xc_-axdc _xc_
+[[:<:]]a_b[[:>:]] & x_a_b
+
+# past problems, and suspected problems
+(A[1])|(A[2])|(A[3])|(A[4])|(A[5])|(A[6])|(A[7])|(A[8])|(A[9])|(A[A]) - A1 A1
+abcdefghijklmnop i abcdefghijklmnop abcdefghijklmnop
+abcdefghijklmnopqrstuv i abcdefghijklmnopqrstuv abcdefghijklmnopqrstuv
+(ALAK)|(ALT[AB])|(CC[123]1)|(CM[123]1)|(GAMC)|(LC[23][EO ])|(SEM[1234])|(SL[ES][12])|(SLWW)|(SLF )|(SLDT)|(VWH[12])|(WH[34][EW])|(WP1[ESN]) - CC11 CC11
+CC[13]1|a{21}[23][EO][123][Es][12]a{15}aa[34][EW]aaaaaaa[X]a - CC11 CC11
+Char \([a-z0-9_]*\)\[.* b Char xyz[k Char xyz[k xyz
+a?b - ab ab
+-\{0,1\}[0-9]*$ b -5 -5
diff --git a/usr.sbin/httpd/src/regex/utils.h b/usr.sbin/httpd/src/regex/utils.h
new file mode 100644
index 00000000000..f271f759b11
--- /dev/null
+++ b/usr.sbin/httpd/src/regex/utils.h
@@ -0,0 +1,22 @@
+/* utility definitions */
+#ifndef _POSIX2_RE_DUP_MAX
+#define _POSIX2_RE_DUP_MAX 255
+#endif
+
+#define DUPMAX _POSIX2_RE_DUP_MAX /* xxx is this right? */
+#define INFINITY (DUPMAX + 1)
+#define NC (CHAR_MAX - CHAR_MIN + 1)
+typedef unsigned char uch;
+
+/* switch off assertions (if not already off) if no REDEBUG */
+#ifndef REDEBUG
+#ifndef NDEBUG
+#define NDEBUG /* no assertions please */
+#endif
+#endif
+#include <assert.h>
+
+/* for old systems with bcopy() but no memmove() */
+#ifdef USEBCOPY
+#define memmove(d, s, c) bcopy(s, d, c)
+#endif
diff --git a/usr.sbin/httpd/src/rfc1413.c b/usr.sbin/httpd/src/rfc1413.c
new file mode 100644
index 00000000000..858a1b0b241
--- /dev/null
+++ b/usr.sbin/httpd/src/rfc1413.c
@@ -0,0 +1,226 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * rfc1413() speaks a common subset of the RFC 1413, AUTH, TAP and IDENT
+ * protocols. The code queries an RFC 1413 etc. compatible daemon on a remote
+ * host to look up the owner of a connection. The information should not be
+ * used for authentication purposes. This routine intercepts alarm signals.
+ *
+ * Diagnostics are reported through syslog(3).
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology,
+ * The Netherlands.
+ */
+
+/* Some small additions for Apache --- ditch the "sccsid" var if
+ * compiling with gcc (it *has* changed), include 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, ap_longjmp, etc. */
+#include "http_log.h" /* for log_unixerr */
+#include "rfc1413.h"
+
+#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
+
+#ifndef RFC1413_TIMEOUT
+#define RFC1413_TIMEOUT 30
+#endif
+#define ANY_PORT 0 /* Any old port will do */
+#define FROM_UNKNOWN "unknown"
+
+int rfc1413_timeout = RFC1413_TIMEOUT; /* Global so it can be changed */
+
+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 */
+ ap_snprintf(buffer, sizeof(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;
+}
+
+/* ident_timeout - handle timeouts */
+static void
+ident_timeout(int sig)
+{
+ ap_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 (ap_setjmp(timebuf) == 0)
+ {
+ signal(SIGALRM, ident_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/usr.sbin/httpd/src/rfc1413.h b/usr.sbin/httpd/src/rfc1413.h
new file mode 100644
index 00000000000..91bf42e4aef
--- /dev/null
+++ b/usr.sbin/httpd/src/rfc1413.h
@@ -0,0 +1,53 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/usr.sbin/httpd/src/scoreboard.h b/usr.sbin/httpd/src/scoreboard.h
new file mode 100644
index 00000000000..5aa808cb4d6
--- /dev/null
+++ b/usr.sbin/httpd/src/scoreboard.h
@@ -0,0 +1,110 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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 */
+#define SERVER_GRACEFUL 8 /* server is gracefully finishing request */
+
+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... */
+ char vhost[32]; /* What virtual host is being accessed? */
+#endif
+} short_score;
+
+typedef struct
+ {
+ int exit_generation; /* Set by the main process if a graceful
+ restart is required */
+ } global_score;
+
+typedef struct
+ {
+ short_score servers[HARD_SERVER_LIMIT];
+ global_score global;
+ } scoreboard;
+
+#define SCOREBOARD_SIZE sizeof(scoreboard)
+
+extern void sync_scoreboard_image(void);
+short_score get_scoreboard_info(int x);
+int exists_scoreboard_image ();
diff --git a/usr.sbin/httpd/src/util.c b/usr.sbin/httpd/src/util.c
new file mode 100644
index 00000000000..12096af0932
--- /dev/null
+++ b/usr.sbin/httpd/src/util.c
@@ -0,0 +1,1369 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/*
+ * util.c: string utility things
+ *
+ * 3/21/93 Rob McCool
+ * 1995-96 Many changes by the Apache Group
+ *
+ */
+
+#include "httpd.h"
+#include "http_conf_globals.h" /* for user_id & group_id */
+
+const char month_snames[12][4] = {
+ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+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, const 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);
+ ts[MAX_STRING_LEN - 1] = '\0';
+ 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' */
+ ap_snprintf(ts, sizeof(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. */
+#if defined(HAVE_GMTOFF)
+struct tm *get_gmtoff(int *tz) {
+ time_t tt = time(NULL);
+ struct tm *t;
+
+ t = localtime(&tt);
+ *tz = (int) (t->tm_gmtoff / 60);
+ return t;
+}
+#else
+struct tm *get_gmtoff(int *tz) {
+ time_t tt = time(NULL);
+ struct tm gmt;
+ struct tm *t;
+ int days, hours, minutes;
+
+ /* Assume we are never more than 24 hours away. */
+ gmt = *gmtime(&tt); /* remember gmtime/localtime return ptr to static */
+ t = localtime(&tt); /* buffer... so be careful */
+ days = t->tm_yday - gmt.tm_yday;
+ hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24)
+ + t->tm_hour - gmt.tm_hour);
+ minutes = hours * 60 + t->tm_min - gmt.tm_min;
+ *tz = minutes;
+ return t;
+}
+#endif
+
+
+/* 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(const char *str, const char *exp) {
+ int x,y;
+
+ for(x=0,y=0;exp[y];++y,++x) {
+ if((!str[x]) && (exp[y] != '*'))
+ return -1;
+ if(exp[y] == '*') {
+ while(exp[++y] == '*');
+ if(!exp[y])
+ return 0;
+ while(str[x]) {
+ int ret;
+ if((ret = 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(const char *str, const char *exp) {
+ int x,y;
+
+ for(x=0,y=0;exp[y];++y,++x) {
+ if((!str[x]) && (exp[y] != '*'))
+ return -1;
+ if(exp[y] == '*') {
+ while(exp[++y] == '*');
+ if(!exp[y])
+ return 0;
+ while(str[x]) {
+ int ret;
+ if((ret = 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(const char *str) {
+ register int x;
+
+ for(x=0;str[x];x++)
+ if((str[x] == '*') || (str[x] == '?'))
+ return 1;
+ return 0;
+}
+
+/* This function substitutes for $0-$9, filling in regular expression
+ * submatches. Pass it the same nmatch and pmatch arguments that you
+ * passed regexec(). pmatch should not be greater than the maximum number
+ * of subexpressions - i.e. one more than the re_nsub member of regex_t.
+ *
+ * input should be the string with the $-expressions, source should be the
+ * string that was matched against.
+ *
+ * It returns the substituted string, or NULL on error.
+ *
+ * Parts of this code are based on Henry Spencer's regsub(), from his
+ * AT&T V8 regexp package.
+ */
+
+char *pregsub(pool *p, const char *input, const char *source,
+ size_t nmatch, regmatch_t pmatch[]) {
+ const char *src = input;
+ char *dest, *dst;
+ char c;
+ int no, len;
+
+ if (!source) return NULL;
+ if (!nmatch) return pstrdup(p, src);
+
+ /* First pass, find the size */
+
+ len = 0;
+
+ while ((c = *src++) != '\0') {
+ if (c == '&')
+ no = 0;
+ else if (c == '$' && isdigit(*src))
+ no = *src++ - '0';
+ else
+ no = -1;
+
+ if (no < 0) { /* Ordinary character. */
+ if (c == '\\' && (*src == '$' || *src == '&'))
+ c = *src++;
+ len++;
+ } else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+ len += pmatch[no].rm_eo - pmatch[no].rm_so;
+ }
+
+ }
+
+ dest = dst = pcalloc(p, len + 1);
+
+ /* Now actually fill in the string */
+
+ src = input;
+
+ while ((c = *src++) != '\0') {
+ if (c == '&')
+ no = 0;
+ else if (c == '$' && isdigit(*src))
+ no = *src++ - '0';
+ else
+ no = -1;
+
+ if (no < 0) { /* Ordinary character. */
+ if (c == '\\' && (*src == '$' || *src == '&'))
+ c = *src++;
+ *dst++ = c;
+ } else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+ len = pmatch[no].rm_eo - pmatch[no].rm_so;
+ strncpy(dst, source + pmatch[no].rm_so, len);
+ dst += len;
+ if (*(dst-1) == '\0') /* strncpy hit NULL. */
+ return NULL;
+ }
+
+ }
+ *dst = '\0';
+
+ return dest;
+}
+
+/*
+ * 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)
+{
+ char *d, *s;
+
+ s = d = name;
+ while (*s) {
+ if ((*d++ = *s) == '/') {
+ do {
+ ++s;
+ } while (*s == '/');
+ }
+ else {
+ ++s;
+ }
+ }
+ *d = '\0';
+}
+
+char *make_dirstr(pool *p, const char *s, int n) {
+ register int x,f;
+ char *res;
+
+ for(x=0,f=0;s[x];x++) {
+ if(s[x] == '/')
+ if((++f) == n) {
+ res = 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(const char *path) {
+ register int x,n;
+
+ for(x=0,n=0;path[x];x++)
+ if(path[x] == '/') n++;
+ return n;
+}
+
+
+void chdir_file(const char *file) {
+ int i;
+
+ if((i = rind(file,'/')) == -1)
+ return;
+ ((char *)file)[i] = '\0';
+ chdir(file);
+ ((char *)file)[i] = '/';
+}
+
+char *getword_nc(pool* atrans, char **line, char stop)
+ {
+ return getword(atrans,(const char **)line,stop);
+ }
+
+char *getword(pool* atrans, const 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_white_nc(pool* atrans, char **line)
+{
+ return getword_white(atrans,(const char **)line);
+}
+
+char *getword_white(pool* atrans, const char **line) {
+ int pos = -1, x;
+ char *res;
+
+ for(x=0;(*line)[x];x++) {
+ if (isspace((*line)[x])) {
+ pos=x;
+ break;
+ }
+ }
+
+ 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 (isspace((*line)[pos])) ++pos;
+
+ *line += pos;
+
+ return res;
+}
+
+char *getword_nulls_nc(pool* atrans, char **line, char stop)
+{
+ return getword_nulls(atrans,(const char **)line,stop);
+}
+
+char *getword_nulls(pool* atrans, const 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
+ */
+
+static char *substring_conf (pool *p, const char *start, int len, char quote)
+{
+ char *result = palloc (p, len + 2);
+ char *resp = result;
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ if (start[i] == '\\' && (start[i+1] == '/'
+ || (quote && start[i+1] == quote)))
+ *resp++ = start[++i];
+ else
+ *resp++ = start[i];
+ }
+
+ *resp++ = '\0';
+ return result;
+}
+
+char *getword_conf_nc(pool* p, char **line) {
+ return getword_conf(p,(const char **)line);
+}
+
+char *getword_conf(pool* p, const char **line) {
+ const char *str = *line, *strend;
+ char *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[1] == quote)
+ strend += 2;
+ else ++strend;
+ }
+ res = substring_conf (p, str + 1, strend - str - 1, quote);
+
+ if (*strend == quote) ++strend;
+ } else {
+ strend = str;
+ while (*strend && !isspace (*strend))
+ ++strend;
+
+ res = substring_conf (p, str, strend - str, 0);
+ }
+
+ while (*strend && isspace(*strend)) ++ strend;
+ *line = strend;
+ return res;
+}
+
+#ifdef UNDEF
+/* this function is dangerous, and superceded by getword_white, so don't use it
+ */
+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);
+}
+#endif
+
+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 == ' ');
+
+ if(c == EOF)
+ return 1;
+
+ if(n < 2) {
+ /* too small, assume caller is crazy */
+ return 1;
+ }
+
+ 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-2)) {
+ /* blast trailing whitespace */
+ while(i && (s[i-1] == ' ')) --i;
+ s[i] = '\0';
+ return 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;
+}
+
+static char* tspecials = " \t()<>@,;:\\/[]?={}";
+
+/* Next HTTP token from a header line. Warning --- destructive!
+ * Use only with a copy!
+ */
+
+static char *next_token (char **toks) {
+ char *cp = *toks;
+ char *ret;
+
+ while (*cp && (iscntrl (*cp) || strchr (tspecials, *cp))) {
+ if (*cp == '"')
+ while (*cp && (*cp != '"')) ++cp;
+ else
+ ++cp;
+ }
+
+ if (!*cp) ret = NULL;
+ else {
+ ret = cp;
+
+ while (*cp && !iscntrl(*cp) && !strchr (tspecials, *cp))
+ ++cp;
+
+ if (*cp) {
+ *toks = cp + 1;
+ *cp = '\0';
+ }
+ else *toks = cp;
+ }
+
+ return ret;
+}
+
+int find_token (pool *p, const char *line, const char *tok) {
+ char *ltok;
+ char *lcopy;
+
+ if (!line) return 0;
+
+ lcopy = pstrdup (p, line);
+ while ((ltok = next_token (&lcopy)))
+ if (!strcasecmp (ltok, tok))
+ return 1;
+
+ return 0;
+}
+
+int find_last_token (pool *p, const char *line, const char *tok)
+{
+ int llen, tlen, lidx;
+
+ if (!line) return 0;
+
+ llen = strlen(line);
+ tlen = strlen(tok);
+ lidx = llen - tlen;
+
+ if ((lidx < 0) ||
+ ((lidx > 0) && !(isspace(line[lidx-1]) || line[lidx-1] == ',')))
+ return 0;
+
+ return (strncasecmp(&line[lidx], tok, tlen) == 0);
+}
+
+char *escape_shell_cmd(pool *p, const 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] = '+';
+}
+
+static char x2c(const 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_server(pool *p, const char *hostname, unsigned port) {
+ char portnum[22];
+ /* Long enough, even if port > 16 bits for some reason */
+
+ if (port == DEFAULT_PORT)
+ return (char *)hostname;
+ else {
+ ap_snprintf (portnum, sizeof(portnum), "%u", port);
+ return pstrcat (p, hostname, ":", portnum, NULL);
+ }
+}
+
+char *construct_url(pool *p, const char *uri, const server_rec *s) {
+ return pstrcat (p, "http://",
+ construct_server(p, s->server_hostname, s->port),
+ uri, NULL);
+}
+
+#define c2x(what,where) sprintf(where,"%%%02x",(unsigned char)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,&copy[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;
+}
+
+/* escape_uri is now a macro for os_escape_path */
+
+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], "&lt;", 4);
+ j += 3;
+ } else if (s[i] == '>')
+ {
+ memcpy(&x[j], "&gt;", 4);
+ j += 3;
+ } else if (s[i] == '&')
+ {
+ memcpy(&x[j], "&amp;", 5);
+ j += 4;
+ } else
+ x[j] = s[i];
+
+ x[j] = '\0';
+ return x;
+}
+
+int is_directory(const char *path) {
+ struct stat finfo;
+
+ if(stat(path,&finfo) == -1)
+ return 0; /* in error condition, just return no */
+
+ return(S_ISDIR(finfo.st_mode));
+}
+
+char *make_full_path(pool *a, const char *src1, const 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);
+}
+
+/*
+ * Check for an absoluteURI syntax (see section 3.2 in RFC2068).
+ */
+int is_url(const char *u) {
+ register int x;
+
+ for (x = 0; u[x] != ':'; x++) {
+ if ((! u[x]) ||
+ ((! isalpha(u[x])) && (! isdigit(u[x])) &&
+ (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) {
+ return 0;
+ }
+ }
+
+ return (x ? 1 : 0); /* If the first character is ':', it's broken, too */
+}
+
+int can_exec(const struct stat *finfo) {
+#ifdef MULTIPLE_GROUPS
+ int cnt;
+#endif
+#ifdef __EMX__
+ /* OS/2 dosen't have Users and Groups */
+ return 1;
+#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;
+#ifdef MULTIPLE_GROUPS
+ for(cnt=0; cnt < NGROUPS_MAX; cnt++) {
+ if(group_id_list[cnt] == finfo->st_gid)
+ if(finfo->st_mode & S_IXGRP)
+ return 1;
+ }
+#endif
+ return (finfo->st_mode & S_IXOTH);
+#endif
+}
+
+#ifdef NEED_STRDUP
+char *strdup (const char *str)
+{
+ char *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)
+{
+#if defined(QNX) || defined(MPE)
+/* QNX and MPE do not appear to support supplementary groups. */
+ return 0;
+#else /* ndef QNX */
+ gid_t groups[NGROUPS_MAX];
+ struct group *g;
+ int index = 0;
+
+ setgrent();
+
+ groups[index++] = basegid;
+
+ while (index < NGROUPS_MAX && ((g = getgrent()) != NULL))
+ if (g->gr_gid != basegid)
+ {
+ char **names;
+
+ for (names = g->gr_mem; *names != NULL; ++names)
+ if (!strcmp(*names, name))
+ groups[index++] = g->gr_gid;
+ }
+
+ endgrent();
+
+ return setgroups(index, groups);
+#endif /* def QNX */
+}
+#endif /* def NEED_INITGROUPS */
+
+#ifdef NEED_WAITPID
+/* From ikluft@amdahl.com */
+/* this is not ideal but it works for SVR3 variants */
+/* 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(const 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);
+ }
+ return(ent->pw_uid);
+}
+
+gid_t gname2id(const 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);
+ }
+ 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) {
+ perror ("getsockname");
+ fprintf (stderr, "Can't get local host address!\n");
+ exit(1);
+ }
+
+ return ((struct sockaddr_in *)&addr)->sin_addr;
+}
+#endif
+
+/*
+ * Parses a host of the form <address>[:port]
+ * :port is permitted if 'port' is not NULL
+ */
+unsigned long get_virthost_addr (const char *w, unsigned short *ports) {
+ struct hostent *hep;
+ unsigned long my_addr;
+ char *p;
+
+ p = strchr(w, ':');
+ if (ports != NULL)
+ {
+ *ports = 0;
+ if (p != NULL && strcmp(p+1, "*") != 0) *ports = atoi(p+1);
+ }
+
+ if (p != NULL) *p = '\0';
+ if (strcmp(w, "*") == 0)
+ {
+ if (p != NULL) *p = ':';
+ return htonl(INADDR_ANY);
+ }
+
+#ifdef DGUX
+ my_addr = inet_network(w);
+#else
+ my_addr = inet_addr(w);
+#endif
+ if (my_addr != INADDR_NONE)
+ {
+ if (p != NULL) *p = ':';
+ return my_addr;
+ }
+
+ hep = gethostbyname(w);
+
+ if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
+ fprintf (stderr, "Cannot resolve host name %s --- exiting!\n", w);
+ exit(1);
+ }
+
+ if (hep->h_addr_list[1]) {
+ fprintf(stderr, "Host %s has multiple addresses ---\n", w);
+ fprintf(stderr, "you must choose one explicitly for use as\n");
+ fprintf(stderr, "a virtual host. Exiting!!!\n");
+ exit(1);
+ }
+
+ if (p != NULL) *p = ':';
+
+ return ((struct in_addr *)(hep->h_addr))->s_addr;
+}
+
+
+static char *find_fqdn(pool *a, struct hostent *p) {
+ int x;
+
+ if(ind(p->h_name,'.') == -1) {
+ for(x=0;p->h_aliases[x];++x) {
+ if((ind(p->h_aliases[x],'.') != -1) &&
+ (!strncasecmp(p->h_aliases[x],p->h_name,strlen(p->h_name))))
+ return pstrdup(a, p->h_aliases[x]);
+ }
+ return NULL;
+ }
+ return pstrdup(a, (void *)p->h_name);
+}
+
+char *get_local_host(pool *a)
+{
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+ char str[MAXHOSTNAMELEN+1];
+ char *server_hostname;
+ struct hostent *p;
+
+ if( gethostname( str, sizeof( str ) - 1 ) != 0 ) {
+ perror( "Unable to gethostname" );
+ exit(1);
+ }
+ str[MAXHOSTNAMELEN] = '\0';
+ 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, const 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 < sizeof(newpath)-1; loop++) {
+ if (path[loop] == '/') {
+ newpath[offset] = '\\';
+ /*
+ offset = offset + 1;
+ newpath[offset] = '\\';
+ */
+ } else
+ newpath[offset] = path[loop];
+ offset = offset + 1;
+ };
+ /* Debugging code */
+ /* fprintf(stderr, "%s \n", newpath); */
+
+ strcpy(path, newpath);
+};
+#endif
+
+
+#ifdef NEED_STRERROR
+char *
+strerror (int err) {
+
+ char *p;
+ extern char *const sys_errlist[];
+
+ p = sys_errlist[err];
+ return (p);
+}
+#endif
+
+
+int ap_slack (int fd, int line)
+{
+#if !defined(F_DUPFD) || defined(NO_SLACK)
+ return fd;
+#else
+ int new_fd;
+
+#ifdef HIGH_SLACK_LINE
+ if (line == AP_SLACK_HIGH) {
+ new_fd = fcntl (fd, F_DUPFD, HIGH_SLACK_LINE);
+ if (new_fd != -1) {
+ close (fd);
+ return new_fd;
+ }
+ }
+#endif
+ /* otherwise just assume line == AP_SLACK_LOW */
+ new_fd = fcntl (fd, F_DUPFD, LOW_SLACK_LINE);
+ if (new_fd == -1) {
+ return fd;
+ }
+ close (fd);
+ return new_fd;
+#endif
+}
diff --git a/usr.sbin/httpd/src/util_date.c b/usr.sbin/httpd/src/util_date.c
new file mode 100644
index 00000000000..e544a0fe1cc
--- /dev/null
+++ b/usr.sbin/httpd/src/util_date.c
@@ -0,0 +1,296 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/>.
+ *
+ */
+
+/*
+ * util_date.c: date parsing utility routines
+ * These routines are (hopefully) platform-independent.
+ *
+ * 27 Oct 1996 Roy Fielding
+ * Extracted (with many modifications) from mod_proxy.c and
+ * tested with over 50,000 randomly chosen valid date strings
+ * and several hundred variations of invalid date strings.
+ *
+ */
+
+#include "util_date.h"
+#include <ctype.h>
+#include <string.h>
+
+/*
+ * Compare a string to a mask
+ * Mask characters (arbitrary maximum is 256 characters, just in case):
+ * @ - uppercase letter
+ * $ - lowercase letter
+ * & - hex digit
+ * # - digit
+ * ~ - digit or space
+ * * - swallow remaining characters
+ * <x> - exact match for any other character
+ */
+int checkmask(const char *data, const char *mask)
+{
+ int i;
+ char d;
+
+ for (i = 0; i < 256; i++) {
+ d = data[i];
+ switch (mask[i]) {
+ case '\0': return (d == '\0');
+
+ case '*': return 1;
+
+ case '@': if (!isupper(d)) return 0;
+ break;
+ case '$': if (!islower(d)) return 0;
+ break;
+ case '#': if (!isdigit(d)) return 0;
+ break;
+ case '&': if (!isxdigit(d)) return 0;
+ break;
+ case '~': if ((d != ' ') && !isdigit(d)) return 0;
+ break;
+ default: if (mask[i] != d) return 0;
+ break;
+ }
+ }
+ return 0; /* We only get here if mask is corrupted (exceeds 256) */
+}
+
+/*
+ * tm2sec converts a GMT tm structure into the number of seconds since
+ * 1st January 1970 UT. Note that we ignore tm_wday, tm_yday, and tm_dst.
+ *
+ * The return value is always a valid time_t value -- (time_t)0 is returned
+ * if the input date is outside that capable of being represented by time(),
+ * i.e., before Thu, 01 Jan 1970 00:00:00 for all systems and
+ * beyond 2038 for 32bit systems.
+ *
+ * This routine is intended to be very fast, much faster than mktime().
+ */
+time_t tm2sec(const struct tm *t)
+{
+ int year;
+ time_t days;
+ const int dayoffset[12] =
+ {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275};
+
+ year = t->tm_year;
+
+ if (year < 70 || ((sizeof(time_t) <= 4) && (year >= 138)))
+ return BAD_DATE;
+
+ /* shift new year to 1st March in order to make leap year calc easy */
+
+ if (t->tm_mon < 2) year--;
+
+ /* Find number of days since 1st March 1900 (in the Gregorian calendar). */
+
+ days = year * 365 + year/4 - year/100 + (year/100 + 3)/4;
+ days += dayoffset[t->tm_mon] + t->tm_mday - 1;
+ days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
+
+ days = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec;
+
+ if (days < 0)
+ return BAD_DATE; /* must have overflowed */
+ else
+ return days; /* must be a valid time */
+}
+
+/*
+ * Parses an HTTP date in one of three standard forms:
+ *
+ * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ *
+ * and returns the time_t number of seconds since 1 Jan 1970 GMT, or
+ * 0 if this would be out of range or if the date is invalid.
+ *
+ * The restricted HTTP syntax is
+ *
+ * HTTP-date = rfc1123-date | rfc850-date | asctime-date
+ *
+ * rfc1123-date = wkday "," SP date1 SP time SP "GMT"
+ * rfc850-date = weekday "," SP date2 SP time SP "GMT"
+ * asctime-date = wkday SP date3 SP time SP 4DIGIT
+ *
+ * date1 = 2DIGIT SP month SP 4DIGIT
+ * ; day month year (e.g., 02 Jun 1982)
+ * date2 = 2DIGIT "-" month "-" 2DIGIT
+ * ; day-month-year (e.g., 02-Jun-82)
+ * date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
+ * ; month day (e.g., Jun 2)
+ *
+ * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ * ; 00:00:00 - 23:59:59
+ *
+ * wkday = "Mon" | "Tue" | "Wed"
+ * | "Thu" | "Fri" | "Sat" | "Sun"
+ *
+ * weekday = "Monday" | "Tuesday" | "Wednesday"
+ * | "Thursday" | "Friday" | "Saturday" | "Sunday"
+ *
+ * month = "Jan" | "Feb" | "Mar" | "Apr"
+ * | "May" | "Jun" | "Jul" | "Aug"
+ * | "Sep" | "Oct" | "Nov" | "Dec"
+ *
+ * However, for the sake of robustness (and Netscapeness), we ignore the
+ * weekday and anything after the time field (including the timezone).
+ *
+ * This routine is intended to be very fast; 10x faster than using sscanf.
+ *
+ * Originally from Andrew Daviel <andrew@vancouver-webpages.com>, 29 Jul 96
+ * but many changes since then.
+ *
+ */
+time_t parseHTTPdate(const char *date)
+{
+ struct tm ds;
+ int mint, mon;
+ const char *monstr, *timstr;
+ const int months[12] = {
+ ('J' << 16) | ( 'a' << 8) | 'n', ('F' << 16) | ( 'e' << 8) | 'b',
+ ('M' << 16) | ( 'a' << 8) | 'r', ('A' << 16) | ( 'p' << 8) | 'r',
+ ('M' << 16) | ( 'a' << 8) | 'y', ('J' << 16) | ( 'u' << 8) | 'n',
+ ('J' << 16) | ( 'u' << 8) | 'l', ('A' << 16) | ( 'u' << 8) | 'g',
+ ('S' << 16) | ( 'e' << 8) | 'p', ('O' << 16) | ( 'c' << 8) | 't',
+ ('N' << 16) | ( 'o' << 8) | 'v', ('D' << 16) | ( 'e' << 8) | 'c'};
+
+ if (!date)
+ return BAD_DATE;
+
+ while (*date && isspace(*date)) /* Find first non-whitespace char */
+ ++date;
+
+ if (*date == '\0')
+ return BAD_DATE;
+
+ if ((date = strchr(date,' ')) == NULL) /* Find space after weekday */
+ return BAD_DATE;
+
+ ++date; /* Now pointing to first char after space, which should be */
+ /* start of the actual date information for all 3 formats. */
+
+ if (checkmask(date, "## @$$ #### ##:##:## *")) { /* RFC 1123 format */
+ ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100;
+ if (ds.tm_year < 0)
+ return BAD_DATE;
+
+ ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0');
+
+ ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
+
+ monstr = date + 3;
+ timstr = date + 12;
+ }
+ else if (checkmask(date, "##-@$$-## ##:##:## *")) { /* RFC 850 format */
+ ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
+ if (ds.tm_year < 70)
+ ds.tm_year += 100;
+
+ ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
+
+ monstr = date + 3;
+ timstr = date + 10;
+ }
+ else if (checkmask(date, "@$$ ~# ##:##:## ####*")) { /* asctime format */
+ ds.tm_year = ((date[16] - '0') * 10 + (date[17] - '0') - 19) * 100;
+ if (ds.tm_year < 0)
+ return BAD_DATE;
+
+ ds.tm_year += ((date[18] - '0') * 10) + (date[19] - '0');
+
+ if (date[4] == ' ')
+ ds.tm_mday = 0;
+ else
+ ds.tm_mday = (date[4] - '0') * 10;
+
+ ds.tm_mday += (date[5] - '0');
+
+ monstr = date;
+ timstr = date + 7;
+ }
+ else return BAD_DATE;
+
+ if (ds.tm_mday <= 0 || ds.tm_mday > 31)
+ return BAD_DATE;
+
+ ds.tm_hour = ((timstr[0] - '0') * 10) + (timstr[1] - '0');
+ ds.tm_min = ((timstr[3] - '0') * 10) + (timstr[4] - '0');
+ ds.tm_sec = ((timstr[6] - '0') * 10) + (timstr[7] - '0');
+
+ if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61))
+ return BAD_DATE;
+
+ mint = (monstr[0] << 16) | (monstr[1] << 8) | monstr[2];
+ for (mon=0; mon < 12; mon++)
+ if (mint == months[mon])
+ break;
+ if (mon == 12)
+ return BAD_DATE;
+
+ if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10))
+ return BAD_DATE;
+
+ /* February gets special check for leapyear */
+
+ if ((mon == 1) && ((ds.tm_mday > 29) ||
+ ((ds.tm_mday == 29) && ((ds.tm_year & 3) ||
+ (((ds.tm_year % 100) == 0) && (((ds.tm_year % 400) != 100)))))))
+ return BAD_DATE;
+
+ ds.tm_mon = mon;
+
+ return tm2sec(&ds);
+}
+
diff --git a/usr.sbin/httpd/src/util_date.h b/usr.sbin/httpd/src/util_date.h
new file mode 100644
index 00000000000..edfd34ad431
--- /dev/null
+++ b/usr.sbin/httpd/src/util_date.h
@@ -0,0 +1,63 @@
+/* ====================================================================
+ * Copyright (c) 1996,1997 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/>.
+ *
+ */
+
+/*
+ * util_date.h: prototypes for date parsing utility routines
+ */
+
+#include <time.h>
+
+#define BAD_DATE (time_t)0
+
+int checkmask (const char *data, const char *mask);
+time_t tm2sec (const struct tm *t);
+time_t parseHTTPdate (const char *date);
diff --git a/usr.sbin/httpd/src/util_md5.c b/usr.sbin/httpd/src/util_md5.c
new file mode 100644
index 00000000000..bf5565e21f0
--- /dev/null
+++ b/usr.sbin/httpd/src/util_md5.c
@@ -0,0 +1,192 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+/************************************************************************
+ * 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/usr.sbin/httpd/src/util_md5.h b/usr.sbin/httpd/src/util_md5.h
new file mode 100644
index 00000000000..63e2c37cd2b
--- /dev/null
+++ b/usr.sbin/httpd/src/util_md5.h
@@ -0,0 +1,58 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/usr.sbin/httpd/src/util_script.c b/usr.sbin/httpd/src/util_script.c
new file mode 100644
index 00000000000..3fcd3a66cd2
--- /dev/null
+++ b/usr.sbin/httpd/src/util_script.c
@@ -0,0 +1,641 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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_conf_globals.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_core.h" /* For document_root. Sigh... */
+#include "http_request.h" /* for sub_req_lookup_uri() */
+#include "util_script.h"
+#include <assert.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
+
+/* If a request includes query info in the URL (stuff after "?"), and
+ * the query info does not contain "=" (indicative of a FORM submission),
+ * then this routine is called to create the argument list to be passed
+ * to the CGI script. When suexec is enabled, the suexec path, user, and
+ * group are the first three arguments to be passed; if not, all three
+ * must be NULL. The query info is split into separate arguments, where
+ * "+" is the separator between keyword arguments.
+ */
+static char **create_argv(pool *p, char *path, char *user, char *group,
+ char *av0, const char *args)
+{
+ int x, numwords;
+ char **av;
+ char *w;
+ int idx = 0;
+
+ /* count the number of keywords */
+
+ for (x = 0, numwords = 1; args[x]; x++)
+ if (args[x] == '+') ++numwords;
+
+ if (numwords > APACHE_ARG_MAX - 5) {
+ numwords = APACHE_ARG_MAX - 5; /* Truncate args to prevent overrun */
+ }
+ av = (char **)palloc(p, (numwords + 5) * sizeof(char *));
+
+ if (path)
+ av[idx++] = path;
+ if (user)
+ av[idx++] = user;
+ if (group)
+ av[idx++] = group;
+
+ av[idx++] = av0;
+
+ for (x = 1; x <= numwords; x++) {
+ w = getword_nulls(p, &args, '+');
+ unescape_url(w);
+ av[idx++] = escape_shell_cmd(p, w);
+ }
+ av[idx] = NULL;
+ return av;
+}
+
+
+static char *http2env(pool *a, char *w)
+{
+ char *res = pstrcat (a, "HTTP_", w, NULL);
+ char *cp = res;
+
+ while (*++cp)
+ if (*cp == '-') *cp = '_';
+ else *cp = toupper(*cp);
+
+ return res;
+}
+
+char **create_environment(pool *p, table *t)
+{
+ array_header *env_arr = table_elts (t);
+ table_entry *elts = (table_entry *)env_arr->elts;
+ 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);
+ }
+
+ ap_snprintf(port, sizeof(port), "%u", 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); /* Apache */
+
+ ap_snprintf(port, sizeof(port), "%d", ntohs(c->remote_addr.sin_port));
+ table_set (e, "REMOTE_PORT", port); /* Apache */
+
+ 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);
+ }
+}
+
+/* This "cute" little function comes about because the path info on
+ * filenames and URLs aren't always the same. So we take the two,
+ * and find as much of the two that match as possible.
+ */
+
+int find_path_info (char *uri, char *path_info)
+{
+ int lu = strlen(uri);
+ int lp = strlen(path_info);
+
+ while (lu-- && lp-- && uri[lu] == path_info[lp]);
+
+ if (lu == -1)
+ lu=0;
+
+ while (uri[lu] != '\0' && uri[lu] != '/')
+ lu++;
+
+ return lu;
+}
+
+/* Obtain the Request-URI from the original request-line, returning
+ * a new string from the request pool containing the URI or "".
+ */
+static char *original_uri(request_rec *r)
+{
+ char *first, *last;
+
+ if (r->the_request == NULL)
+ return (char *)pcalloc(r->pool, 1);
+
+ first = r->the_request; /* use the request-line */
+
+ while (*first && !isspace(*first)) ++first; /* skip over the method */
+ while (isspace(*first)) ++first; /* and the space(s) */
+
+ last = first;
+ while (*last && !isspace(*last)) ++last; /* end at next whitespace */
+
+ return pstrndup(r->pool, first, last - first);
+}
+
+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 : "");
+ table_set (e, "REQUEST_URI", original_uri(r));
+
+ /* Note that the code below special-cases scripts run from includes,
+ * because it "knows" that the sub_request has been hacked to have the
+ * args and path_info of the original request, and not any that may have
+ * come with the script URI in the include command. Ugh.
+ */
+
+ if (!strcmp (r->protocol, "INCLUDED")) {
+ table_set (e, "SCRIPT_NAME", r->uri);
+ if (r->path_info && *r->path_info)
+ table_set (e, "PATH_INFO", r->path_info);
+ } else if (!r->path_info || !*r->path_info) {
+ table_set (e, "SCRIPT_NAME", r->uri);
+ } else {
+ int path_info_start = find_path_info (r->uri, r->path_info);
+
+ table_set (e, "SCRIPT_NAME", pstrndup(r->pool, r->uri,
+ path_info_start));
+
+ table_set (e, "PATH_INFO", r->path_info);
+ }
+
+ if (r->path_info && r->path_info[0]) {
+ /*
+ * To get PATH_TRANSLATED, treat PATH_INFO as a URI path.
+ * Need to re-escape it for this, since the entire URI was
+ * un-escaped before we determined where the PATH_INFO began.
+ */
+ request_rec *pa_req = sub_req_lookup_uri(
+ escape_uri(r->pool, r->path_info), r);
+
+ /* 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_err(request_rec *r, FILE *f, char *buffer)
+{
+ char x[MAX_STRING_LEN];
+ char *w, *l;
+ int p;
+
+ if (buffer) *buffer = '\0';
+ w = buffer ? buffer : x;
+
+ hard_timeout ("read script header", r);
+
+ while(1) {
+
+ if (fgets(w, MAX_STRING_LEN-1, f) == NULL) {
+ kill_timeout (r);
+ 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);
+
+ if (!buffer)
+ /* 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);
+ }
+ else if(!strcasecmp(w,"Content-Length")) {
+ table_set (r->headers_out, w, l);
+ }
+ else if(!strcasecmp(w,"Transfer-Encoding")) {
+ 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)
+ ap_snprintf(ss, sizeof(ss), "%4dk", (size + 512) / 1024);
+ else if(size < 103809024)
+ ap_snprintf(ss, sizeof(ss), "%4.1fM", size / 1048576.0);
+ else
+ ap_snprintf(ss, sizeof(ss), "%4dM", (size + 524288) / 1048576);
+ rputs(ss, r);
+}
+
+#ifdef __EMX__
+static char **create_argv_cmd(pool *p, char *av0, const char *args, char *path)
+{
+ register int x,n;
+ char **av;
+ char *w;
+
+ for(x=0,n=2;args[x];x++)
+ if(args[x] == '+') ++n;
+
+ /* Add extra strings to array. */
+ n = n + 2;
+
+ av = (char **)palloc(p, (n+1)*sizeof(char *));
+ av[0] = av0;
+
+ /* Now insert the extra strings we made room for above. */
+ av[1] = strdup("/C");
+ av[2] = strdup(path);
+
+ for(x=(1+2);x<n;x++) {
+ w = getword(p, &args, '+');
+ unescape_url(w);
+ av[x] = escape_shell_cmd(p, w);
+ }
+ av[n] = NULL;
+ return av;
+}
+#endif
+
+
+void call_exec (request_rec *r, char *argv0, char **env, int shellcmd)
+{
+#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \
+ defined(RLIMIT_DATA) || defined(RLIMIT_VMEM)
+
+ core_dir_config *conf =
+ (core_dir_config *)get_module_config(r->per_dir_config, &core_module);
+
+#endif
+
+ /* the fd on r->server->error_log is closed, but we need somewhere to
+ * put the error messages from the log_* functions. So, we use stderr,
+ * since that is better than allowing errors to go unnoticed.
+ */
+ r->server->error_log = stderr;
+
+#ifdef RLIMIT_CPU
+ if (conf->limit_cpu != NULL)
+ if ((setrlimit (RLIMIT_CPU, conf->limit_cpu)) != 0)
+ log_unixerr("setrlimit", NULL, "failed to set CPU usage limit",
+ r->server);
+#endif
+#ifdef RLIMIT_NPROC
+ if (conf->limit_nproc != NULL)
+ if ((setrlimit (RLIMIT_NPROC, conf->limit_nproc)) != 0)
+ log_unixerr("setrlimit", NULL, "failed to set process limit",
+ r->server);
+#endif
+#ifdef RLIMIT_DATA
+ if (conf->limit_mem != NULL)
+ if ((setrlimit (RLIMIT_DATA, conf->limit_mem)) != 0)
+ log_unixerr("setrlimit", NULL, "failed to set memory usage limit",
+ r->server);
+#endif
+#ifdef RLIMIT_VMEM
+ if (conf->limit_mem != NULL)
+ if ((setrlimit (RLIMIT_VMEM, conf->limit_mem)) != 0)
+ log_unixerr("setrlimit", NULL, "failed to set memory usage limit",
+ r->server);
+#endif
+
+#ifdef __EMX__
+ {
+ /* Additions by Alec Kloss, to allow exec'ing of scripts under OS/2 */
+ int is_script;
+ char interpreter[2048]; /* hope this is large enough for the interpreter path */
+ FILE * program;
+ program = fopen (r->filename, "r");
+ if (!program) {
+ char err_string[HUGE_STRING_LEN];
+ ap_snprintf(err_string, sizeof(err_string),
+ "open of %s failed, reason: fopen: %s (errno = %d)\n",
+ r->filename, strerror(errno), errno);
+
+ /* write(2, err_string, strlen(err_string)); */
+ /* exit(0); */
+ log_unixerr("fopen", NULL, err_string, r->server);
+ return;
+ }
+ fgets (interpreter, 2048, program);
+ fclose (program);
+ if (!strncmp (interpreter, "#!", 2)) {
+ is_script = 1;
+ interpreter[strlen(interpreter)-1] = '\0';
+ } else {
+ is_script = 0;
+ }
+
+ 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);
+
+ /* Additions by Alec Kloss, to allow exec'ing of scripts under OS/2 */
+ if (is_script) {
+ /* here's the stuff to run the interpreter */
+ execl (interpreter+2, interpreter+2, r->filename, NULL);
+ } else
+
+ if (strstr(strupr(r->filename), ".CMD") > 0) {
+ /* Special case to allow use of REXX commands as scripts. */
+ os2pathname(r->filename);
+ 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
+ environment so that they 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, NULL, NULL, NULL, argv0, r->args));
+ }
+ }
+#else
+ if ( suexec_enabled &&
+ ((r->server->server_uid != user_id) ||
+ (r->server->server_gid != group_id) ||
+ (!strncmp("/~",r->uri,2))) ) {
+
+ char *execuser, *grpname;
+ struct passwd *pw;
+ struct group *gr;
+
+ if (!strncmp("/~",r->uri,2)) {
+ gid_t user_gid;
+ char *username = pstrdup(r->pool, r->uri + 2);
+ int pos = ind(username, '/');
+
+ if (pos >= 0) username[pos] = '\0';
+
+ if ((pw = getpwnam(username)) == NULL) {
+ log_unixerr("getpwnam",username,"invalid username",r->server);
+ return;
+ }
+ execuser = pstrcat(r->pool, "~", pw->pw_name, NULL);
+ user_gid = pw->pw_gid;
+
+ if ((gr = getgrgid(user_gid)) == NULL) {
+ if ((grpname = palloc (r->pool, 16)) == NULL)
+ return;
+ else
+ ap_snprintf(grpname, 16, "%d", user_gid);
+ }
+ else
+ grpname = gr->gr_name;
+ }
+ else {
+ if ((pw = getpwuid (r->server->server_uid)) == NULL) {
+ log_unixerr("getpwuid", NULL, "invalid userid", r->server);
+ return;
+ }
+ execuser = pstrdup(r->pool, pw->pw_name);
+
+ if ((gr = getgrgid (r->server->server_gid)) == NULL) {
+ log_unixerr("getgrgid", NULL, "invalid groupid", r->server);
+ return;
+ }
+ grpname = gr->gr_name;
+ }
+
+ if (shellcmd)
+ execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, NULL, env);
+
+ else if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0))
+ execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, NULL, env);
+
+ else {
+ execve(SUEXEC_BIN,
+ create_argv(r->pool, SUEXEC_BIN, execuser, grpname,
+ argv0, r->args),
+ env);
+ }
+ }
+ else {
+ if (shellcmd)
+ execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
+
+ else if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0))
+ execle(r->filename, argv0, NULL, env);
+
+ else
+ execve(r->filename,
+ create_argv(r->pool, NULL, NULL, NULL, argv0, r->args),
+ env);
+ }
+#endif
+}
diff --git a/usr.sbin/httpd/src/util_script.h b/usr.sbin/httpd/src/util_script.h
new file mode 100644
index 00000000000..28258767781
--- /dev/null
+++ b/usr.sbin/httpd/src/util_script.h
@@ -0,0 +1,69 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ */
+
+#ifndef APACHE_ARG_MAX
+#ifdef _POSIX_ARG_MAX
+#define APACHE_ARG_MAX _POSIX_ARG_MAX
+#else
+#define APACHE_ARG_MAX 512
+#endif
+#endif
+
+char **create_environment(pool *p, table *t);
+int find_path_info(char *uri, char *path_info);
+void add_cgi_vars(request_rec *r);
+void add_common_vars(request_rec *r);
+#define scan_script_header(a1,a2) scan_script_header_err(a1,a2,NULL)
+int scan_script_header_err(request_rec *r, FILE *f, char *buffer);
+void send_size(size_t size, request_rec *r);
+void call_exec (request_rec *r, char *argv0, char **env, int shellcmd);
+
diff --git a/usr.sbin/httpd/src/util_snprintf.c b/usr.sbin/httpd/src/util_snprintf.c
new file mode 100644
index 00000000000..d37c634f753
--- /dev/null
+++ b/usr.sbin/httpd/src/util_snprintf.c
@@ -0,0 +1,949 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 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/>.
+ *
+ * This code is based on, and used with the permission of, the
+ * SIO stdio-replacement strx_* functions by Panos Tsirigotis
+ * <panos@alumni.cs.colorado.edu> for xinetd.
+ */
+
+#include "conf.h"
+
+#ifndef HAVE_SNPRINTF
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#ifdef HAVE_CVT
+
+# define ap_ecvt ecvt
+# define ap_fcvt fcvt
+# define ap_gcvt gcvt
+
+#else
+
+/*
+ * cvt.c - IEEE floating point formatting routines for FreeBSD
+ * from GNU libc-4.6.27
+ */
+
+/*
+ * ap_ecvt converts to decimal
+ * the number of digits is specified by ndigit
+ * decpt is set to the position of the decimal point
+ * sign is set to 0 for positive, 1 for negative
+ */
+
+#define NDIG 80
+
+static char *
+ ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag)
+{
+ register int r2;
+ double fi, fj;
+ register char *p, *p1;
+ static char buf[NDIG];
+
+ if (ndigits >= NDIG - 1)
+ ndigits = NDIG - 2;
+ r2 = 0;
+ *sign = 0;
+ p = &buf[0];
+ if (arg < 0) {
+ *sign = 1;
+ arg = -arg;
+ }
+ arg = modf(arg, &fi);
+ p1 = &buf[NDIG];
+ /*
+ * Do integer part
+ */
+ if (fi != 0) {
+ p1 = &buf[NDIG];
+ while (fi != 0) {
+ fj = modf(fi / 10, &fi);
+ *--p1 = (int) ((fj + .03) * 10) + '0';
+ r2++;
+ }
+ while (p1 < &buf[NDIG])
+ *p++ = *p1++;
+ }
+ else if (arg > 0) {
+ while ((fj = arg * 10) < 1) {
+ arg = fj;
+ r2--;
+ }
+ }
+ p1 = &buf[ndigits];
+ if (eflag == 0)
+ p1 += r2;
+ *decpt = r2;
+ if (p1 < &buf[0]) {
+ buf[0] = '\0';
+ return (buf);
+ }
+ while (p <= p1 && p < &buf[NDIG]) {
+ arg *= 10;
+ arg = modf(arg, &fj);
+ *p++ = (int) fj + '0';
+ }
+ if (p1 >= &buf[NDIG]) {
+ buf[NDIG - 1] = '\0';
+ return (buf);
+ }
+ p = p1;
+ *p1 += 5;
+ while (*p1 > '9') {
+ *p1 = '0';
+ if (p1 > buf)
+ ++ * --p1;
+ else {
+ *p1 = '1';
+ (*decpt)++;
+ if (eflag == 0) {
+ if (p > buf)
+ *p = '0';
+ p++;
+ }
+ }
+ }
+ *p = '\0';
+ return (buf);
+}
+
+static char *
+ ap_ecvt(double arg, int ndigits, int *decpt, int *sign)
+{
+ return (ap_cvt(arg, ndigits, decpt, sign, 1));
+}
+
+static char *
+ ap_fcvt(double arg, int ndigits, int *decpt, int *sign)
+{
+ return (ap_cvt(arg, ndigits, decpt, sign, 0));
+}
+
+/*
+ * ap_gcvt - Floating output conversion to
+ * minimal length string
+ */
+
+static char *
+ ap_gcvt(double number, int ndigit, char *buf)
+{
+ int sign, decpt;
+ register char *p1, *p2;
+ register i;
+
+ p1 = ap_ecvt(number, ndigit, &decpt, &sign);
+ p2 = buf;
+ if (sign)
+ *p2++ = '-';
+ for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--)
+ ndigit--;
+ if ((decpt >= 0 && decpt - ndigit > 4)
+ || (decpt < 0 && decpt < -3)) { /* use E-style */
+ decpt--;
+ *p2++ = *p1++;
+ *p2++ = '.';
+ for (i = 1; i < ndigit; i++)
+ *p2++ = *p1++;
+ *p2++ = 'e';
+ if (decpt < 0) {
+ decpt = -decpt;
+ *p2++ = '-';
+ }
+ else
+ *p2++ = '+';
+ if (decpt / 100 > 0)
+ *p2++ = decpt / 100 + '0';
+ if (decpt / 10 > 0)
+ *p2++ = (decpt % 100) / 10 + '0';
+ *p2++ = decpt % 10 + '0';
+ }
+ else {
+ if (decpt <= 0) {
+ if (*p1 != '0')
+ *p2++ = '.';
+ while (decpt < 0) {
+ decpt++;
+ *p2++ = '0';
+ }
+ }
+ for (i = 1; i <= ndigit; i++) {
+ *p2++ = *p1++;
+ if (i == decpt)
+ *p2++ = '.';
+ }
+ if (ndigit < decpt) {
+ while (ndigit++ < decpt)
+ *p2++ = '0';
+ *p2++ = '.';
+ }
+ }
+ if (p2[-1] == '.')
+ p2--;
+ *p2 = '\0';
+ return (buf);
+}
+
+#endif /* HAVE_CVT */
+
+typedef enum {
+ NO = 0, YES = 1
+} boolean_e;
+
+#define FALSE 0
+#define TRUE 1
+#define NUL '\0'
+#define INT_NULL ((int *)0)
+#define WIDE_INT long
+
+typedef WIDE_INT wide_int;
+typedef unsigned WIDE_INT u_wide_int;
+typedef int bool_int;
+
+#define S_NULL "(null)"
+#define S_NULL_LEN 6
+
+#define FLOAT_DIGITS 6
+#define EXPONENT_LENGTH 10
+
+/*
+ * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
+ *
+ * XXX: this is a magic number; do not decrease it
+ */
+#define NUM_BUF_SIZE 512
+
+
+/*
+ * Descriptor for buffer area
+ */
+struct buf_area {
+ char *buf_end;
+ char *nextb; /* pointer to next byte to read/write */
+};
+
+typedef struct buf_area buffy;
+
+/*
+ * The INS_CHAR macro inserts a character in the buffer and writes
+ * the buffer back to disk if necessary
+ * It uses the char pointers sp and bep:
+ * sp points to the next available character in the buffer
+ * bep points to the end-of-buffer+1
+ * While using this macro, note that the nextb pointer is NOT updated.
+ *
+ * NOTE: Evaluation of the c argument should not have any side-effects
+ */
+#define INS_CHAR( c, sp, bep, cc ) \
+ { \
+ if ( sp < bep ) \
+ { \
+ *sp++ = c ; \
+ cc++ ; \
+ } \
+ }
+
+#define NUM( c ) ( c - '0' )
+
+#define STR_TO_DEC( str, num ) \
+ num = NUM( *str++ ) ; \
+ while ( isdigit( *str ) ) \
+ { \
+ num *= 10 ; \
+ num += NUM( *str++ ) ; \
+ }
+
+/*
+ * This macro does zero padding so that the precision
+ * requirement is satisfied. The padding is done by
+ * adding '0's to the left of the string that is going
+ * to be printed.
+ */
+#define FIX_PRECISION( adjust, precision, s, s_len ) \
+ if ( adjust ) \
+ while ( s_len < precision ) \
+ { \
+ *--s = '0' ; \
+ s_len++ ; \
+ }
+
+/*
+ * Macro that does padding. The padding is done by printing
+ * the character ch.
+ */
+#define PAD( width, len, ch ) do \
+ { \
+ INS_CHAR( ch, sp, bep, cc ) ; \
+ width-- ; \
+ } \
+ while ( width > len )
+
+/*
+ * Prefix the character ch to the string str
+ * Increase length
+ * Set the has_prefix flag
+ */
+#define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES
+
+
+/*
+ * Convert num to its decimal format.
+ * Return value:
+ * - a pointer to a string containing the number (no sign)
+ * - len contains the length of the string
+ * - is_negative is set to TRUE or FALSE depending on the sign
+ * of the number (always set to FALSE if is_unsigned is TRUE)
+ *
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ */
+static char *
+ conv_10(register wide_int num, register bool_int is_unsigned,
+ register bool_int * is_negative, char *buf_end, register int *len)
+{
+ register char *p = buf_end;
+ register u_wide_int magnitude;
+
+ if (is_unsigned) {
+ magnitude = (u_wide_int) num;
+ *is_negative = FALSE;
+ }
+ else {
+ *is_negative = (num < 0);
+
+ /*
+ * On a 2's complement machine, negating the most negative integer
+ * results in a number that cannot be represented as a signed integer.
+ * Here is what we do to obtain the number's magnitude:
+ * a. add 1 to the number
+ * b. negate it (becomes positive)
+ * c. convert it to unsigned
+ * d. add 1
+ */
+ if (*is_negative) {
+ wide_int t = num + 1;
+
+ magnitude = ((u_wide_int) - t) + 1;
+ }
+ else
+ magnitude = (u_wide_int) num;
+ }
+
+ /*
+ * We use a do-while loop so that we write at least 1 digit
+ */
+ do {
+ register u_wide_int new_magnitude = magnitude / 10;
+
+ *--p = magnitude - new_magnitude * 10 + '0';
+ magnitude = new_magnitude;
+ }
+ while (magnitude);
+
+ *len = buf_end - p;
+ return (p);
+}
+
+
+
+/*
+ * Convert a floating point number to a string formats 'f', 'e' or 'E'.
+ * The result is placed in buf, and len denotes the length of the string
+ * The sign is returned in the is_negative argument (and is not placed
+ * in buf).
+ */
+static char *
+ conv_fp(register char format, register double num,
+boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len)
+{
+ register char *s = buf;
+ register char *p;
+ int decimal_point;
+
+ if (format == 'f')
+ p = ap_fcvt(num, precision, &decimal_point, is_negative);
+ else /* either e or E format */
+ p = ap_ecvt(num, precision + 1, &decimal_point, is_negative);
+
+ /*
+ * Check for Infinity and NaN
+ */
+ if (isalpha(*p)) {
+ *len = strlen(strcpy(buf, p));
+ *is_negative = FALSE;
+ return (buf);
+ }
+
+ if (format == 'f')
+ if (decimal_point <= 0) {
+ *s++ = '0';
+ if (precision > 0) {
+ *s++ = '.';
+ while (decimal_point++ < 0)
+ *s++ = '0';
+ }
+ else if (add_dp)
+ *s++ = '.';
+ }
+ else {
+ while (decimal_point-- > 0)
+ *s++ = *p++;
+ if (precision > 0 || add_dp)
+ *s++ = '.';
+ }
+ else {
+ *s++ = *p++;
+ if (precision > 0 || add_dp)
+ *s++ = '.';
+ }
+
+ /*
+ * copy the rest of p, the NUL is NOT copied
+ */
+ while (*p)
+ *s++ = *p++;
+
+ if (format != 'f') {
+ char temp[EXPONENT_LENGTH]; /* for exponent conversion */
+ int t_len;
+ bool_int exponent_is_negative;
+
+ *s++ = format; /* either e or E */
+ decimal_point--;
+ if (decimal_point != 0) {
+ p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative,
+ &temp[EXPONENT_LENGTH], &t_len);
+ *s++ = exponent_is_negative ? '-' : '+';
+
+ /*
+ * Make sure the exponent has at least 2 digits
+ */
+ if (t_len == 1)
+ *s++ = '0';
+ while (t_len--)
+ *s++ = *p++;
+ }
+ else {
+ *s++ = '+';
+ *s++ = '0';
+ *s++ = '0';
+ }
+ }
+
+ *len = s - buf;
+ return (buf);
+}
+
+
+/*
+ * Convert num to a base X number where X is a power of 2. nbits determines X.
+ * For example, if nbits is 3, we do base 8 conversion
+ * Return value:
+ * a pointer to a string containing the number
+ *
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ */
+static char *
+ conv_p2(register u_wide_int num, register int nbits,
+ char format, char *buf_end, register int *len)
+{
+ register int mask = (1 << nbits) - 1;
+ register char *p = buf_end;
+ static char low_digits[] = "0123456789abcdef";
+ static char upper_digits[] = "0123456789ABCDEF";
+ register char *digits = (format == 'X') ? upper_digits : low_digits;
+
+ do {
+ *--p = digits[num & mask];
+ num >>= nbits;
+ }
+ while (num);
+
+ *len = buf_end - p;
+ return (p);
+}
+
+
+/*
+ * Do format conversion placing the output in buffer
+ */
+static int format_converter(register buffy * odp, const char *fmt,
+ va_list ap)
+{
+ register char *sp;
+ register char *bep;
+ register int cc = 0;
+ register int i;
+
+ register char *s = NULL;
+ char *q;
+ int s_len;
+
+ register int min_width = 0;
+ int precision = 0;
+ enum {
+ LEFT, RIGHT
+ } adjust;
+ char pad_char;
+ char prefix_char;
+
+ double fp_num;
+ wide_int i_num = (wide_int) 0;
+ u_wide_int ui_num;
+
+ char num_buf[NUM_BUF_SIZE];
+ char char_buf[2]; /* for printing %% and %<unknown> */
+
+ /*
+ * Flag variables
+ */
+ boolean_e is_long;
+ boolean_e alternate_form;
+ boolean_e print_sign;
+ boolean_e print_blank;
+ boolean_e adjust_precision;
+ boolean_e adjust_width;
+ bool_int is_negative;
+
+ sp = odp->nextb;
+ bep = odp->buf_end;
+
+ while (*fmt) {
+ if (*fmt != '%') {
+ INS_CHAR(*fmt, sp, bep, cc);
+ }
+ else {
+ /*
+ * Default variable settings
+ */
+ adjust = RIGHT;
+ alternate_form = print_sign = print_blank = NO;
+ pad_char = ' ';
+ prefix_char = NUL;
+
+ fmt++;
+
+ /*
+ * Try to avoid checking for flags, width or precision
+ */
+ if (isascii(*fmt) && !islower(*fmt)) {
+ /*
+ * Recognize flags: -, #, BLANK, +
+ */
+ for (;; fmt++) {
+ if (*fmt == '-')
+ adjust = LEFT;
+ else if (*fmt == '+')
+ print_sign = YES;
+ else if (*fmt == '#')
+ alternate_form = YES;
+ else if (*fmt == ' ')
+ print_blank = YES;
+ else if (*fmt == '0')
+ pad_char = '0';
+ else
+ break;
+ }
+
+ /*
+ * Check if a width was specified
+ */
+ if (isdigit(*fmt)) {
+ STR_TO_DEC(fmt, min_width);
+ adjust_width = YES;
+ }
+ else if (*fmt == '*') {
+ min_width = va_arg(ap, int);
+ fmt++;
+ adjust_width = YES;
+ if (min_width < 0) {
+ adjust = LEFT;
+ min_width = -min_width;
+ }
+ }
+ else
+ adjust_width = NO;
+
+ /*
+ * Check if a precision was specified
+ *
+ * XXX: an unreasonable amount of precision may be specified
+ * resulting in overflow of num_buf. Currently we
+ * ignore this possibility.
+ */
+ if (*fmt == '.') {
+ adjust_precision = YES;
+ fmt++;
+ if (isdigit(*fmt)) {
+ STR_TO_DEC(fmt, precision);
+ }
+ else if (*fmt == '*') {
+ precision = va_arg(ap, int);
+ fmt++;
+ if (precision < 0)
+ precision = 0;
+ }
+ else
+ precision = 0;
+ }
+ else
+ adjust_precision = NO;
+ }
+ else
+ adjust_precision = adjust_width = NO;
+
+ /*
+ * Modifier check
+ */
+ if (*fmt == 'l') {
+ is_long = YES;
+ fmt++;
+ }
+ else
+ is_long = NO;
+
+ /*
+ * Argument extraction and printing.
+ * First we determine the argument type.
+ * Then, we convert the argument to a string.
+ * On exit from the switch, s points to the string that
+ * must be printed, s_len has the length of the string
+ * The precision requirements, if any, are reflected in s_len.
+ *
+ * NOTE: pad_char may be set to '0' because of the 0 flag.
+ * It is reset to ' ' by non-numeric formats
+ */
+ switch (*fmt) {
+ case 'u':
+ if (is_long)
+ i_num = va_arg(ap, u_wide_int);
+ else
+ i_num = (wide_int) va_arg(ap, unsigned int);
+ /*
+ * The rest also applies to other integer formats, so fall
+ * into that case.
+ */
+ case 'd':
+ case 'i':
+ /*
+ * Get the arg if we haven't already.
+ */
+ if ((*fmt) != 'u') {
+ if (is_long)
+ i_num = va_arg(ap, wide_int);
+ else
+ i_num = (wide_int) va_arg(ap, int);
+ };
+ s = conv_10(i_num, (*fmt) == 'u', &is_negative,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+
+ if (*fmt != 'u') {
+ if (is_negative)
+ prefix_char = '-';
+ else if (print_sign)
+ prefix_char = '+';
+ else if (print_blank)
+ prefix_char = ' ';
+ }
+ break;
+
+
+ case 'o':
+ if (is_long)
+ ui_num = va_arg(ap, u_wide_int);
+ else
+ ui_num = (u_wide_int) va_arg(ap, unsigned int);
+ s = conv_p2(ui_num, 3, *fmt,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+ if (alternate_form && *s != '0') {
+ *--s = '0';
+ s_len++;
+ }
+ break;
+
+
+ case 'x':
+ case 'X':
+ if (is_long)
+ ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+ else
+ ui_num = (u_wide_int) va_arg(ap, unsigned int);
+ s = conv_p2(ui_num, 4, *fmt,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+ if (alternate_form && i_num != 0) {
+ *--s = *fmt; /* 'x' or 'X' */
+ *--s = '0';
+ s_len += 2;
+ }
+ break;
+
+
+ case 's':
+ s = va_arg(ap, char *);
+ if (s != NULL) {
+ s_len = strlen(s);
+ if (adjust_precision && precision < s_len)
+ s_len = precision;
+ }
+ else {
+ s = S_NULL;
+ s_len = S_NULL_LEN;
+ }
+ pad_char = ' ';
+ break;
+
+
+ case 'f':
+ case 'e':
+ case 'E':
+ fp_num = va_arg(ap, double);
+
+ s = conv_fp(*fmt, fp_num, alternate_form,
+ (adjust_precision == NO) ? FLOAT_DIGITS : precision,
+ &is_negative, &num_buf[1], &s_len);
+ if (is_negative)
+ prefix_char = '-';
+ else if (print_sign)
+ prefix_char = '+';
+ else if (print_blank)
+ prefix_char = ' ';
+ break;
+
+
+ case 'g':
+ case 'G':
+ if (adjust_precision == NO)
+ precision = FLOAT_DIGITS;
+ else if (precision == 0)
+ precision = 1;
+ /*
+ * * We use &num_buf[ 1 ], so that we have room for the sign
+ */
+ s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1]);
+ if (*s == '-')
+ prefix_char = *s++;
+ else if (print_sign)
+ prefix_char = '+';
+ else if (print_blank)
+ prefix_char = ' ';
+
+ s_len = strlen(s);
+
+ if (alternate_form && (q = strchr(s, '.')) == NULL)
+ s[s_len++] = '.';
+ if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
+ *q = 'E';
+ break;
+
+
+ case 'c':
+ char_buf[0] = (char) (va_arg(ap, int));
+ s = &char_buf[0];
+ s_len = 1;
+ pad_char = ' ';
+ break;
+
+
+ case '%':
+ char_buf[0] = '%';
+ s = &char_buf[0];
+ s_len = 1;
+ pad_char = ' ';
+ break;
+
+
+ case 'n':
+ *(va_arg(ap, int *)) = cc;
+ break;
+
+ /*
+ * Always extract the argument as a "char *" pointer. We
+ * should be using "void *" but there are still machines
+ * that don't understand it.
+ * If the pointer size is equal to the size of an unsigned
+ * integer we convert the pointer to a hex number, otherwise
+ * we print "%p" to indicate that we don't handle "%p".
+ */
+ case 'p':
+ ui_num = (u_wide_int) va_arg(ap, char *);
+
+ if (sizeof(char *) <= sizeof(u_wide_int))
+ s = conv_p2(ui_num, 4, 'x',
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ else {
+ s = "%p";
+ s_len = 2;
+ }
+ pad_char = ' ';
+ break;
+
+
+ case NUL:
+ /*
+ * The last character of the format string was %.
+ * We ignore it.
+ */
+ continue;
+
+
+ /*
+ * The default case is for unrecognized %'s.
+ * We print %<char> to help the user identify what
+ * option is not understood.
+ * This is also useful in case the user wants to pass
+ * the output of format_converter to another function
+ * that understands some other %<char> (like syslog).
+ * Note that we can't point s inside fmt because the
+ * unknown <char> could be preceded by width etc.
+ */
+ default:
+ char_buf[0] = '%';
+ char_buf[1] = *fmt;
+ s = char_buf;
+ s_len = 2;
+ pad_char = ' ';
+ break;
+ }
+
+ if (prefix_char != NUL) {
+ *--s = prefix_char;
+ s_len++;
+ }
+
+ if (adjust_width && adjust == RIGHT && min_width > s_len) {
+ if (pad_char == '0' && prefix_char != NUL) {
+ INS_CHAR(*s, sp, bep, cc)
+ s++;
+ s_len--;
+ min_width--;
+ }
+ PAD(min_width, s_len, pad_char);
+ }
+
+ /*
+ * Print the string s.
+ */
+ for (i = s_len; i != 0; i--) {
+ INS_CHAR(*s, sp, bep, cc);
+ s++;
+ }
+
+ if (adjust_width && adjust == LEFT && min_width > s_len)
+ PAD(min_width, s_len, pad_char);
+ }
+ fmt++;
+ }
+ odp->nextb = sp;
+ return (cc);
+}
+
+
+/*
+ * This is the general purpose conversion function.
+ */
+static void strx_printv(int *ccp, char *buf, size_t len, const char *format,
+ va_list ap)
+{
+ buffy od;
+ int cc;
+
+ /*
+ * First initialize the descriptor
+ * Notice that if no length is given, we initialize buf_end to the
+ * highest possible address.
+ */
+ od.buf_end = len ? &buf[len] : (char *) ~0;
+ od.nextb = buf;
+
+ /*
+ * Do the conversion
+ */
+ cc = format_converter(&od, format, ap);
+ if (len == 0 || od.nextb <= od.buf_end)
+ *(od.nextb) = '\0';
+ if (ccp)
+ *ccp = cc;
+}
+
+
+int ap_snprintf(char *buf, size_t len, const char *format,...)
+{
+ int cc;
+ va_list ap;
+
+ va_start(ap, format);
+ strx_printv(&cc, buf, (len - 1), format, ap);
+ va_end(ap);
+ return (cc);
+}
+
+
+int ap_vsnprintf(char *buf, size_t len, const char *format, va_list ap)
+{
+ int cc;
+
+ strx_printv(&cc, buf, (len - 1), format, ap);
+ return (cc);
+}
+
+#endif /* HAVE_SNPRINTF */