-ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
+ScriptAlias /cgi-bin/ /home/httpd/cgi-bin/
+diff -bruN boa-0.94.8.2/request.c boa-0.94.8.2.pld/request.c
+--- boa-0.94.8.2/request.c Thu Jan 1 01:00:00 1970
++++ boa-0.94.8.2.pld/request.c Sun Sep 10 04:58:44 2000
+@@ -0,0 +1,660 @@
++/*
++ * Boa, an http server
++ * Copyright (C) 1995 Paul Phillips <psp@well.com>
++ * Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@jlab.org>
++ * Some changes Copyright (C) 1996-99 Jon Nelson <jnelson@boa.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 1, or (at your option)
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++/* $Id$*/
++
++#include "boa.h"
++#include <netinet/tcp.h>
++#include <arpa/inet.h> /* inet_ntoa */
++#include <stddef.h>
++
++int sockbufsize = SOCKETBUF_SIZE;
++int total_connections;
++
++/*
++ * Name: new_request
++ * Description: Obtains a request struct off the free list, or if the
++ * free list is empty, allocates memory
++ *
++ * Return value: pointer to initialized request
++ */
++
++request *new_request(void)
++{
++ request *req;
++
++ if (request_free) {
++ req = request_free; /* first on free list */
++ dequeue(&request_free, request_free); /* dequeue the head */
++ } else {
++ req = (request *) malloc(sizeof (request));
++ if (!req) {
++ log_error_mesg(__FILE__, __LINE__,
++ "out of memory allocating for new request structure");
++ exit(errno);
++ }
++ }
++
++ memset(req, 0, offsetof(request, buffer) + 1);
++
++ return req;
++}
++
++
++/*
++ * Name: get_request
++ *
++ * Description: Polls the server socket for a request. If one exists,
++ * does some basic initialization and adds it to the ready queue;.
++ */
++
++void get_request(int server_s)
++{
++ int fd; /* socket */
++ struct SOCKADDR remote_addr; /* address */
++ int remote_addrlen = sizeof (struct sockaddr_in);
++ request *conn; /* connection */
++ int len;
++ static int system_bufsize = 0; /* Default size of SNDBUF given by system */
++
++ remote_addr.S_FAMILY = 0xdead;
++ fd = accept(server_s, (struct sockaddr *) &remote_addr,
++ &remote_addrlen);
++
++ if (fd == -1) {
++ if (errno != EAGAIN && errno != EWOULDBLOCK) /* no requests */
++ log_error_mesg(__FILE__, __LINE__, "accept");
++ return;
++ }
++#ifdef DEBUGNONINET
++ /* This shows up due to race conditions in some Linux kernels
++ when the client closes the socket sometime between
++ the select() and accept() syscalls.
++ Code and description by Larry Doolittle <ldoolitt@boa.org>
++ */
++#define HEX(x) (((x)>9)?(('a'-10)+(x)):('0'+(x)))
++ if (remote_addr.sin_family != AF_INET) {
++ struct sockaddr *bogus = (struct sockaddr *) &remote_addr;
++ char *ap, ablock[44];
++ int i;
++ close(fd);
++ log_error_time();
++ for (ap = ablock, i = 0; i < remote_addrlen && i < 14; i++) {
++ *ap++ = ' ';
++ *ap++ = HEX((bogus->sa_data[i] >> 4) & 0x0f);
++ *ap++ = HEX(bogus->sa_data[i] & 0x0f);
++ }
++ *ap = '\0';
++ fprintf(stderr, "non-INET connection attempt: socket %d, "
++ "sa_family = %hu, sa_data[%d] = %s\n",
++ fd, bogus->sa_family, remote_addrlen, ablock);
++ return;
++ }
++#endif
++
++/* XXX Either delete this, or document why it's needed */
++/* Pointed out 3-Oct-1999 by Paul Saab <paul@mu.org> */
++#ifdef REUSE_EACH_CLIENT_CONNECTION_SOCKET
++ if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &sock_opt,
++ sizeof (sock_opt))) == -1) {
++ log_error_mesg(__FILE__, __LINE__,
++ "setsockopt: unable to set SO_REUSEADDR");
++ exit(errno);
++ }
++#endif
++
++ conn = new_request();
++ conn->fd = fd;
++ conn->status = READ_HEADER;
++ conn->header_line = conn->client_stream;
++ conn->time_last = current_time;
++ conn->kacount = ka_max;
++
++ /* fill in local_ip_addr if relevant */
++ if (virtualhost) {
++ struct SOCKADDR salocal;
++ int dummy = sizeof (salocal);
++ if (getsockname(conn->fd, (struct sockaddr *) &salocal, &dummy) ==
++ -1) {
++ log_error_mesg(__FILE__, __LINE__, "getsockname");
++ exit(errno);
++ }
++ ascii_sockaddr(&salocal, conn->local_ip_addr, NI_MAXHOST);
++ }
++
++ /* nonblocking socket */
++ if (fcntl(conn->fd, F_SETFL, NOBLOCK) == -1)
++ log_error_mesg(__FILE__, __LINE__,
++ "fcntl: unable to set new socket to non-block");
++
++ /* set close on exec to true */
++ if (fcntl(conn->fd, F_SETFD, 1) == -1)
++ log_error_mesg(__FILE__, __LINE__,
++ "fctnl: unable to set close-on-exec for new socket");
++
++ /* Increase buffer size if we have to.
++ * Only ask the system the buffer size on the first request,
++ * and assume all subsequent sockets have the same size.
++ */
++ if (system_bufsize == 0) {
++ len = sizeof (system_bufsize);
++ if (getsockopt
++ (conn->fd, SOL_SOCKET, SO_SNDBUF, &system_bufsize, &len) == 0
++ && len == sizeof (system_bufsize)) {
++ /*
++ fprintf(stderr, "%sgetsockopt reports SNDBUF %d\n",
++ get_commonlog_time(), system_bufsize);
++ */
++ ;
++ } else {
++ log_error_mesg(__FILE__, __LINE__, "getsockopt(SNDBUF)");
++ system_bufsize = 1;
++ }
++ }
++ if (system_bufsize < sockbufsize) {
++ if (setsockopt
++ (conn->fd, SOL_SOCKET, SO_SNDBUF, (void *) &sockbufsize,
++ sizeof (sockbufsize)) == -1) {
++ log_error_mesg(__FILE__, __LINE__,
++ "setsockopt: unable to set socket buffer size");
++#ifdef DIE_ON_ERROR_TUNING_SNDBUF
++ exit(errno);
++#endif
++ }
++ }
++
++ /* for log file and possible use by CGI programs */
++ ascii_sockaddr(&remote_addr, conn->remote_ip_addr, NI_MAXHOST);
++
++ /* for possible use by CGI programs */
++ /* the structure doesn't contain port
++ conn->remote_port = ntohs(remote_addr.sin_port);
++ */
++ conn->remote_port = 0;
++
++ status.requests++;
++
++#ifdef USE_TCPNODELAY
++ /* Thanks to Jef Poskanzer <jef@acme.com> for this tweak */
++ {
++ int one = 1;
++ if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY,
++ (void *) &one, sizeof (one)) == -1) {
++ log_error_mesg(__FILE__, __LINE__,
++ "setsockopt: unable to set TCP_NODELAY");
++ exit(errno);
++ }
++
++ }
++#endif
++
++ total_connections++;
++ enqueue(&request_ready, conn);
++}
++
++
++/*
++ * Name: free_request
++ *
++ * Description: Deallocates memory for a finished request and closes
++ * down socket.
++ */
++
++static void free_request(request ** list_head_addr, request * req)
++{
++ /* free_request should *never* get called by anything but
++ process_requests */
++
++ if (req->buffer_end && req->status != DEAD) {
++ req->status = DONE;
++ return;
++ }
++ /* put request on the free list */
++ dequeue(list_head_addr, req); /* dequeue from ready or block list */
++
++ FD_CLR(req->fd, &block_read_fdset);
++ FD_CLR(req->fd, &block_write_fdset);
++ FD_CLR(req->data_fd, &block_read_fdset);
++ FD_CLR(req->post_data_fd, &block_write_fdset);
++
++ if (req->logline) /* access log */
++ log_access(req);
++
++ if (req->mmap_entry_var)
++ release_mmap(req->mmap_entry_var);
++ else if (req->data_mem)
++ munmap(req->data_mem, req->filesize);
++
++ if (req->data_fd)
++ close(req->data_fd);
++
++ if (req->response_status >= 400)
++ status.errors++;
++
++ if (req->cgi_env) {
++ int i;
++ for (i = COMMON_CGI_COUNT; i < req->cgi_env_index; ++i)
++ if (req->cgi_env[i])
++ free(req->cgi_env[i]);
++ free(req->cgi_env);
++ }
++ if (req->pathname)
++ free(req->pathname);
++ if (req->path_info)
++ free(req->path_info);
++ if (req->path_translated)
++ free(req->path_translated);
++ if (req->script_name)
++ free(req->script_name);
++ if (req->query_string)
++ free(req->query_string);
++
++ if ((req->keepalive == KA_ACTIVE) &&
++ (req->response_status < 500) && req->kacount > 0) {
++
++ request *conn = new_request();
++ conn->fd = req->fd;
++ conn->status = READ_HEADER;
++ conn->header_line = conn->client_stream;
++ conn->kacount = req->kacount - 1;
++
++ /* close enough and we avoid a call to time(NULL) */
++ conn->time_last = req->time_last;
++
++ /* for log file and possible use by CGI programs */
++ memcpy(conn->remote_ip_addr, req->remote_ip_addr, NI_MAXHOST);
++
++ /* for possible use by CGI programs */
++ conn->remote_port = req->remote_port;
++
++ status.requests++;
++
++ {
++ int bytes_to_move;
++
++ /* we haven't parsed beyond req->parse_pos, so... */
++ bytes_to_move = req->client_stream_pos - req->parse_pos;
++
++ if (bytes_to_move) {
++ memcpy(conn->client_stream,
++ req->client_stream + req->parse_pos, bytes_to_move);
++ conn->client_stream_pos = bytes_to_move;
++ FD_CLR(conn->fd, &block_read_fdset);
++ enqueue(&request_ready, conn);
++ } else {
++ FD_SET(conn->fd, &block_read_fdset);
++ enqueue(&request_block, conn);
++ }
++ }
++ } else {
++ close(req->fd);
++ total_connections--;
++ }
++
++ enqueue(&request_free, req);
++
++ return;
++}
++
++/*
++ * Name: process_requests
++ *
++ * Description: Iterates through the ready queue, passing each request
++ * to the appropriate handler for processing. It monitors the
++ * return value from handler functions, all of which return -1
++ * to indicate a block, 0 on completion and 1 to remain on the
++ * ready list for more procesing.
++ */
++
++void process_requests(void)
++{
++ int retval = 0;
++ request *current, *trailer;
++
++ current = request_ready;
++
++ while (current) {
++ time(¤t_time);
++ if (current->buffer_end && /* there is data in the buffer */
++ current->status != DEAD && current->status != DONE) {
++ retval = req_flush(current);
++ /*
++ * retval can be -2=error, -1=blocked, or bytes left
++ */
++ if (retval == -2) { /* error */
++ current->status = DEAD;
++ retval = 0;
++ } else if (retval >= 0) {
++ /* notice the >= which is different from below?
++ Here, we may just be flushing headers.
++ We don't want to return 0 because we are not DONE
++ or DEAD */
++
++ retval = 1;
++ }
++ } else {
++ switch (current->status) {
++ case READ_HEADER:
++ case ONE_CR:
++ case ONE_LF:
++ case TWO_CR:
++ retval = read_header(current);
++ break;
++ case BODY_READ:
++ retval = read_body(current);
++ break;
++ case BODY_WRITE:
++ retval = write_body(current);
++ break;
++ case WRITE:
++ retval = process_get(current);
++ break;
++ case PIPE_READ:
++ retval = read_from_pipe(current);
++ break;
++ case PIPE_WRITE:
++ retval = write_from_pipe(current);
++ break;
++ case DONE:
++ /* a non-status that will terminate the request */
++ retval = req_flush(current);
++ /*
++ * retval can be -2=error, -1=blocked, or bytes left
++ */
++ if (retval == -2) { /* error */
++ current->status = DEAD;
++ retval = 0;
++ } else if (retval > 0) {
++ retval = 1;
++ }
++ break;
++ case DEAD:
++ retval = 0;
++ current->buffer_end = 0;
++ SQUASH_KA(current);
++ break;
++ default:
++ retval = 0;
++ fprintf(stderr, "Unknown status (%d), "
++ "closing!\n", current->status);
++ current->status = DEAD;
++ break;
++ }
++
++ }
++
++ if (lame_duck_mode)
++ SQUASH_KA(current);
++
++ switch (retval) {
++ case -1: /* request blocked */
++ trailer = current;
++ current = current->next;
++ block_request(trailer);
++ break;
++ case 0: /* request complete */
++ current->time_last = current_time;
++ trailer = current;
++ current = current->next;
++ free_request(&request_ready, trailer);
++ break;
++ case 1: /* more to do */
++ current->time_last = current_time;
++ current = current->next;
++ break;
++ default:
++ log_error_time();
++ fprintf(stderr, "Unknown retval in process.c - "
++ "Status: %d, retval: %d\n", current->status, retval);
++ current = current->next;
++ break;
++ }
++ }
++}
++
++/*
++ * Name: process_logline
++ *
++ * Description: This is called with the first req->header_line received
++ * by a request, called "logline" because it is logged to a file.
++ * It is parsed to determine request type and method, then passed to
++ * translate_uri for further parsing. Also sets up CGI environment if
++ * needed.
++ */
++
++int process_logline(request * req)
++{
++ char *stop, *stop2;
++ static char *SIMPLE_HTTP_VERSION = "HTTP/0.9";
++
++ req->logline = req->client_stream;
++ if (!memcmp(req->logline, "GET ", 4))
++ req->method = M_GET;
++ else if (!memcmp(req->logline, "HEAD ", 5))
++ /* head is just get w/no body */
++ req->method = M_HEAD;
++ else if (!memcmp(req->logline, "POST ", 5))
++ req->method = M_POST;
++ else {
++ log_error_time();
++ fprintf(stderr, "malformed request: \"%s\"\n", req->logline);
++ send_r_bad_request(req);
++ return 0;
++ }
++
++ /* Guaranteed to find ' ' since we matched a method above */
++ stop = req->logline + 3;
++ if (*stop != ' ')
++ ++stop;
++
++ /* scan to start of non-whitespace */
++ while (*(++stop) == ' ');
++
++ stop2 = stop;
++
++ /* scan to end of non-whitespace */
++ while (*stop2 != '\0' && *stop2 != ' ')
++ ++stop2;
++
++ if (stop2 - stop > MAX_HEADER_LENGTH) {
++ log_error_time();
++ fprintf(stderr, "URI too long %d: \"%s\"\n", MAX_HEADER_LENGTH,
++ req->logline);
++ send_r_bad_request(req);
++ return 0;
++ }
++ memcpy(req->request_uri, stop, stop2 - stop);
++ req->request_uri[stop2 - stop] = '\0';
++
++ if (*stop2 == ' ') {
++ /* if found, we should get an HTTP/x.x */
++ int p1, p2;
++
++ if (sscanf(++stop2, "HTTP/%d.%d", &p1, &p2) == 2 && p1 >= 1) {
++ req->http_version = stop2;
++ req->simple = 0;
++ } else {
++ log_error_time();
++ fprintf(stderr, "bogus HTTP version: \"%s\"\n", stop2);
++ send_r_bad_request(req);
++ return 0;
++ }
++
++ } else {
++ req->http_version = SIMPLE_HTTP_VERSION;
++ req->simple = 1;
++ }
++
++ if (req->method == M_HEAD && req->simple) {
++ send_r_bad_request(req);
++ return 0;
++ }
++ if (translate_uri(req) == 0) { /* unescape, parse uri */
++ SQUASH_KA(req);
++ return 0; /* failure, close down */
++ }
++ if (req->is_cgi)
++ create_env(req);
++ return 1;
++}
++
++/*
++ * Name: process_header_end
++ *
++ * Description: takes a request and performs some final checking before
++ * init_cgi or init_get
++ * Returns 0 for error or NPH, or 1 for success
++ */
++
++int process_header_end(request * req)
++{
++ if (!req->logline) {
++ send_r_error(req);
++ return 0;
++ }
++ if (req->method == M_POST) {
++ char *tmpfilep = tmpnam(NULL);
++
++ if (!tmpfilep) {
++ boa_perror(req, "tmpnam");
++ return 0;
++ }
++ /* open temp file for post data */
++ if ((req->post_data_fd = open(tmpfilep, O_RDWR | O_CREAT /* read-write, create */
++ | O_EXCL, S_IRUSR /* don't overwrite, u+r */
++ & ~S_IWUSR & ~S_IEXEC /* u-wx */
++ & ~S_IRWXG /* g-rwx */
++ & ~S_IRWXO)) == -1) { /* o-rwx */
++ boa_perror(req, "tmpfile open");
++ return 0;
++ }
++#ifndef REALLY_FASCIST_LOGGING
++ unlink(tmpfilep);
++#endif
++ return 1;
++ }
++ if (req->is_cgi) {
++ return init_cgi(req);
++ }
++ req->status = WRITE;
++ return init_get(req); /* get and head */
++}
++
++/*
++ * Name: process_option_line
++ *
++ * Description: Parses the contents of req->header_line and takes
++ * appropriate action.
++ */
++
++void process_option_line(request * req)
++{
++ char c, *value, *line = req->header_line;
++
++ /* Start by aggressively hacking the in-place copy of the header line */
++
++#ifdef FASCIST_LOGGING
++ log_error_time();
++ fprintf(stderr, "%s:%d - Parsing \"%s\"\n", __FILE__, __LINE__, line);
++#endif
++
++ value = strchr(line, ':');
++ if (value == NULL)
++ return;
++ *value++ = '\0'; /* overwrite the : */
++ to_upper(line); /* header types are case-insensitive */
++ while ((c = *value) && (c == ' ' || c == '\t'))
++ value++;
++
++ if (!memcmp(line, "IF_MODIFIED_SINCE", 18) && !req->if_modified_since)
++ req->if_modified_since = value;
++
++ else if (!memcmp(line, "CONTENT_TYPE", 13) && !req->content_type)
++ req->content_type = value;
++
++ else if (!memcmp(line, "CONTENT_LENGTH", 15) && !req->content_length)
++ req->content_length = value;
++
++ else if (!memcmp(line, "CONNECTION", 11) &&
++ ka_max && req->keepalive != KA_STOPPED) {
++ req->keepalive = (!strncasecmp(value, "Keep-Alive", 10) ?
++ KA_ACTIVE : KA_STOPPED);
++ }
++ /* #ifdef ACCEPT_ON */
++ else if (!memcmp(line, "ACCEPT", 7))
++ add_accept_header(req, value);
++ /* #endif */
++
++ /* Need agent and referer for logs */
++ else if (!memcmp(line, "REFERER", 8))
++ req->header_referer = value;
++ else if (!memcmp(line, "USER_AGENT", 11))
++ req->header_user_agent = value;
++
++ /* Silently ignore unknown header lines unless is_cgi */
++ else if (req->is_cgi)
++ add_cgi_env(req, line, value);
++ return;
++}
++
++/*
++ * Name: add_accept_header
++ * Description: Adds a mime_type to a requests accept char buffer
++ * silently ignore any that don't fit -
++ * shouldn't happen because of relative buffer sizes
++ */
++
++void add_accept_header(request * req, char *mime_type)
++{
++#ifdef ACCEPT_ON
++ int l = strlen(req->accept);
++ int l2 = strlen(mime_type);
++
++ if ((l + l2 + 2) >= MAX_HEADER_LENGTH)
++ return;
++
++ if (req->accept[0] == '\0')
++ strcpy(req->accept, mime_type);
++ else {
++ req->accept[l] = ',';
++ req->accept[l + 1] = ' ';
++ memcpy(req->accept + l + 2, mime_type, l2 + 1);
++ /* the +1 is for the '\0' */
++ /*
++ sprintf(req->accept + l, ", %s", mime_type);
++ */
++ }
++#endif
++}
++
++void free_requests(void)
++{
++ request *ptr, *next;
++
++ ptr = request_free;
++ while (ptr != NULL) {
++ next = ptr->next;
++ free(ptr);
++ ptr = next;
++ }
++ request_free = NULL;
++}
diff -bruN boa-0.94.8.2/src/Makefile.in boa-0.94.8.2.pld/src/Makefile.in
--- boa-0.94.8.2/src/Makefile.in Sat Feb 12 22:52:45 2000
+++ boa-0.94.8.2.pld/src/Makefile.in Sun Sep 10 04:37:16 2000