home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 8 / CDACTUAL8.iso / share / os2 / varios / apache / http_pro.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-23  |  25.9 KB  |  961 lines

  1.  
  2. /* ====================================================================
  3.  * Copyright (c) 1995 The Apache Group.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer. 
  11.  *
  12.  * 2. Redistributions in binary form must reproduce the above copyright
  13.  *    notice, this list of conditions and the following disclaimer in
  14.  *    the documentation and/or other materials provided with the
  15.  *    distribution.
  16.  *
  17.  * 3. All advertising materials mentioning features or use of this
  18.  *    software must display the following acknowledgment:
  19.  *    "This product includes software developed by the Apache Group
  20.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  21.  *
  22.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  23.  *    endorse or promote products derived from this software without
  24.  *    prior written permission.
  25.  *
  26.  * 5. Redistributions of any form whatsoever must retain the following
  27.  *    acknowledgment:
  28.  *    "This product includes software developed by the Apache Group
  29.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  30.  *
  31.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  32.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  33.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  34.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  35.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  39.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  42.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  43.  * ====================================================================
  44.  *
  45.  * This software consists of voluntary contributions made by many
  46.  * individuals on behalf of the Apache Group and was originally based
  47.  * on public domain software written at the National Center for
  48.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  49.  * For more information on the Apache Group and the Apache HTTP server
  50.  * project, please see <http://www.apache.org/>.
  51.  *
  52.  */
  53.  
  54.  
  55. /*
  56.  * http_protocol.c --- routines which directly communicate with the
  57.  * client.
  58.  *
  59.  * Code originally by Rob McCool; much redone by rst.
  60.  */
  61.  
  62. #define CORE_PRIVATE
  63. #include "httpd.h"
  64. #include "http_config.h"
  65. #include "http_core.h"
  66. #include "http_protocol.h"
  67. #include "http_main.h"
  68. #include "http_log.h"        /* For errors detected in basic auth
  69.                  * common support code...
  70.                  */
  71.  
  72. #include <stdarg.h>
  73.  
  74. #define SET_BYTES_SENT(r) \
  75.   do { if (r->sent_bodyct) \
  76.       bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
  77.   } while (0)
  78.  
  79. /* Handling of conditional gets (if-modified-since); Roy owes Rob beer. 
  80.  * This would be considerably easier if strptime or timegm were portable...
  81.  */
  82.  
  83. const char month_snames[12][4] = {
  84.     "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
  85. };
  86.  
  87. int find_month(char *mon) {
  88.     register int x;
  89.  
  90.     for(x=0;x<12;x++)
  91.         if(!strcmp(month_snames[x],mon))
  92.             return x;
  93.     return -1;
  94. }
  95.  
  96. int later_than(struct tm *lms, char *ims) {
  97.     char *ip;
  98.     char mname[MAX_STRING_LEN];
  99.     int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0, x;
  100.  
  101.     /* Whatever format we're looking at, it will start with weekday. */
  102.     /* Skip to first space. */
  103.     if(!(ip = strchr(ims,' ')))
  104.         return 0;
  105.     else
  106.         while(isspace(*ip))
  107.             ++ip;
  108.  
  109.     if(isalpha(*ip)) {
  110.         /* ctime */
  111.         sscanf(ip,"%s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year);
  112.     }
  113.     else if(ip[2] == '-') {
  114.         /* RFC 850 (normal HTTP) */
  115.         char t[MAX_STRING_LEN];
  116.         sscanf(ip,"%s %d:%d:%d",t,&hour,&min,&sec);
  117.         t[2] = '\0';
  118.         day = atoi(t);
  119.         t[6] = '\0';
  120.         strcpy(mname,&t[3]);
  121.         x = atoi(&t[7]);
  122.         /* Prevent wraparound from ambiguity */
  123.         if(x < 70)
  124.             x += 100;
  125.         year = 1900 + x;
  126.     }
  127.     else {
  128.         /* RFC 822 */
  129.         sscanf(ip,"%d %s %d %d:%d:%d",&day,mname,&year,&hour,&min,&sec);
  130.     }
  131.     month = find_month(mname);
  132.  
  133.     if((x = (1900+lms->tm_year) - year))
  134.         return x < 0;
  135.     if((x = lms->tm_mon - month))
  136.         return x < 0;
  137.     if((x = lms->tm_mday - day))
  138.         return x < 0;
  139.     if((x = lms->tm_hour - hour))
  140.         return x < 0;
  141.     if((x = lms->tm_min - min))
  142.         return x < 0;
  143.     if((x = lms->tm_sec - sec))
  144.         return x < 0;
  145.  
  146.     return 1;
  147. }
  148.  
  149.  
  150. int set_content_length (request_rec *r, long clength)
  151. {
  152.     char ts[MAX_STRING_LEN];
  153.     
  154.     sprintf (ts, "%ld", (long)r->finfo.st_size);
  155.     table_set (r->headers_out, "Content-length", pstrdup (r->pool, ts));
  156.     return 0;
  157. }
  158.  
  159. int set_keepalive(request_rec *r)
  160. {
  161.   char *conn = table_get (r->headers_in, "Connection");
  162.   char *length = table_get (r->headers_out, "Content-length");
  163.  
  164.   if (conn && length && !strncasecmp(conn, "Keep-Alive", 10) &&
  165.       r->server->keep_alive_timeout &&
  166.       (r->server->keep_alive > r->connection->keepalives)) {
  167.     char header[26];
  168.     int left = r->server->keep_alive - r->connection->keepalives;
  169.  
  170.     r->connection->keepalive = 1;
  171.     r->connection->keepalives++;
  172.     sprintf(header, "timeout=%d, max=%d", r->server->keep_alive_timeout,
  173.         left);
  174.     table_set (r->headers_out, "Connection", "Keep-Alive");
  175.     table_set (r->headers_out, "Keep-Alive", pstrdup(r->pool, header));
  176.  
  177.     return 1;
  178.   }
  179.  
  180.       return 0;
  181. }
  182.  
  183. int set_last_modified(request_rec *r, time_t mtime)
  184. {
  185.     char *ts;
  186.     char *if_modified_since = table_get (r->headers_in, "If-modified-since");
  187.  
  188.     /* Cacheing proxies use the absence of a Last-modified header
  189.      * to indicate that a document is dynamic and shouldn't be cached.
  190.      * For the moment, we enforce that here, though it would probably
  191.      * work just as well to generate an Expires: header in send_http_header.
  192.      *
  193.      * However, even in that case, if no_cache is set, we would *not*
  194.      * want to send USE_LOCAL_COPY, since the client isn't *supposed*
  195.      * to have it cached.
  196.      */
  197.     
  198.     if (r->no_cache) return OK;
  199.     
  200.     ts = gm_timestr_822(r->pool, mtime);
  201.     table_set (r->headers_out, "Last-modified", ts);
  202.  
  203.     /* Check for conditional GETs --- note that we only want this check
  204.      * to succeed if the GET was successful; ErrorDocuments *always* get sent.
  205.      */
  206.     
  207.     if (r->status == 200 &&
  208.     if_modified_since && later_than(gmtime(&mtime), if_modified_since))
  209.       
  210.         return USE_LOCAL_COPY;
  211.     else
  212.         return OK;
  213. }
  214.  
  215. /*
  216.  * Finally, real protocol stuff.
  217.  */
  218.  
  219. static char *
  220. getline(char *s, int n, BUFF *in)
  221. {
  222.     int retval = bgets (s, n, in);
  223.  
  224.     if (retval == -1) return NULL;
  225.  
  226.     if (retval > 0 && s[retval-1] == '\n') s[retval-1] = '\0';
  227.  
  228.     return s;
  229. }
  230.  
  231. void parse_uri (request_rec *r, char *uri)
  232. {
  233.     const char *s;
  234.     /* If we ever want to do byte-ranges a la Netscape & Franks,
  235.      * this is the place to parse them; with proper support in
  236.      * rprintf and rputc, and the sub-request setup and finalizers
  237.      * here, it'll all just work, even for vile cases like
  238.      * inclusion of byte-ranges of the output of CGI scripts, with
  239.      * the client requesting only a byte-range of *that*!
  240.      *
  241.      * But for now...
  242.      */
  243.  
  244. #ifdef __EMX__
  245.     /* Variable for OS/2 fix below. */
  246.     int loop;
  247. #endif
  248.  
  249. /* A proxy request contains a ':' early on, but not as first character */
  250.     for (s=uri; s != '\0'; s++)
  251.     if (!isalnum(*s) && *s != '+' && *s != '-' && *s != '.') break;
  252.  
  253.     if (*s == ':' && s != uri)
  254.     {
  255.     r->proxyreq = 1;
  256.     r->uri = uri;
  257.     r->args = NULL;
  258.     } else
  259.     {
  260.     r->proxyreq = 0;
  261.     r->uri = getword (r->pool, &uri, '?');
  262.  
  263. #ifdef __EMX__
  264.     /* Handle path translations for OS/2 and plug security hole. */
  265.     /* This will prevent "http://www.wherever.com/..\..\/" from
  266.        returning a directory for the root drive. */
  267.     for (loop = 0; loop <= strlen(r->uri); ++loop) {
  268.         if (r->uri[loop] == '\\')
  269.             r->uri[loop] = '/';
  270. };
  271. #endif
  272.  
  273.     if (*uri) r->args= uri;
  274.     else r->args = NULL;
  275.     }
  276. }
  277.  
  278. char *check_fulluri (request_rec *r, char *uri) {
  279.   char *name, *host;
  280.   int i, port;
  281.  
  282.   /* This routine parses full URLs, if they match the server */
  283.   if (strncmp(uri, "http://", 7)) return uri;
  284.   name = pstrdup(r->pool, uri + 7);
  285.   
  286.   /* Find the hostname, assuming a valid request */
  287.   i = ind(name, '/');
  288.   name[i] = '\0';
  289.  
  290.   /* Find the port */
  291.   host = getword(r->pool, &name, ':');
  292.   if (*name) port = atoi(name);
  293.   else port = 80;
  294.  
  295.   /* Make sure ports patch */
  296.   if (port != r->server->port) return uri;
  297.  
  298.   /* Save it for later use */
  299.   r->hostname = pstrdup(r->pool, host);
  300.   r->hostlen = 7 + i;
  301.  
  302.   /* The easy cases first */
  303.   if (!strcasecmp(host, r->server->server_hostname)) {
  304.     return (uri + r->hostlen);
  305.   }
  306.   else if (!strcmp(host, inet_ntoa(r->connection->local_addr.sin_addr))) {
  307.     return (uri + r->hostlen);
  308.   }
  309.  
  310.   /* Now things get a bit trickier - check the IP address(es) of the host */
  311.   /* they gave, see if it matches ours.                                   */
  312.   else {
  313.     struct hostent *hp;
  314.     int n;
  315.  
  316.     if ((hp = gethostbyname(host))) {
  317.       for (n = 0; hp->h_addr_list[n] != NULL; n++) {
  318.     if (r->connection->local_addr.sin_addr.s_addr ==
  319.         (((struct in_addr *)(hp->h_addr_list[n]))->s_addr)) {
  320.       return (uri + r->hostlen);
  321.     }
  322.       }
  323.     }
  324.   }
  325.   
  326.   return uri;
  327. }
  328.  
  329. int read_request_line (request_rec *r)
  330. {
  331.     char l[HUGE_STRING_LEN];
  332.     char *ll = l, *uri;
  333.     conn_rec *conn = r->connection;
  334.     int major = 1, minor = 0;    /* Assume HTTP/1.0 if non-"HTTP" protocol*/
  335.     
  336.     l[0] = '\0';
  337.     if(!getline(l, HUGE_STRING_LEN, conn->client))
  338.         return 0;
  339.     if(!l[0]) 
  340.         return 0;
  341.  
  342.     r->the_request = pstrdup (r->pool, l);
  343.     r->method = getword(r->pool, &ll,' ');
  344.     uri = getword(r->pool, &ll,' ');
  345.     uri = check_fulluri(r, uri);
  346.     parse_uri (r, uri);
  347.     
  348.     r->assbackwards = (ll[0] == '\0');
  349.     r->protocol = pstrdup (r->pool, ll[0] ? ll : "HTTP/0.9");
  350.     sscanf(r->protocol, "HTTP/%d.%d", &major, &minor);
  351.     r->proto_num = 1000*major + minor;
  352.  
  353.     return 1;
  354. }
  355.  
  356. void get_mime_headers(request_rec *r)
  357. {
  358.     char w[MAX_STRING_LEN];
  359.     char *t;
  360.     conn_rec *c = r->connection;
  361.  
  362.     while(getline(w, MAX_STRING_LEN-1, c->client)) {
  363.         if(!w[0]) 
  364.             return;
  365.         if(!(t = strchr(w,':')))
  366.             continue;
  367.         *t++ = '\0';
  368.         while(isspace(*t)) ++t;
  369.  
  370.     table_merge (r->headers_in, w, t);
  371.     }
  372. }
  373.  
  374. void check_hostalias (request_rec *r) {
  375.   char *host = getword(r->pool, &r->hostname, ':');    /* Get rid of port */
  376.   int port = (*r->hostname) ? atoi(r->hostname) : 0;
  377.   server_rec *s;
  378.  
  379.   if (port && (port != r->server->port))
  380.     return;
  381.  
  382.   if ((host[strlen(host)-1]) == '.') {
  383.     host[strlen(host)-1] = '\0';
  384.   }
  385.  
  386.   r->hostname = host;
  387.  
  388.   for (s = r->server->next; s; s = s->next) {
  389.     char *names = s->names;
  390.     
  391.     if ((!strcasecmp(host, s->server_hostname)) &&
  392.     (!port || (port == s->port))) {
  393.       r->server = r->connection->server = s;
  394.       if (r->hostlen && !strncmp(r->uri, "http://", 7)) {
  395.     r->uri += r->hostlen;
  396.     parse_uri(r, r->uri);
  397.       }
  398.     }
  399.  
  400.     if (!names) continue;
  401.  
  402.     while (*names) {
  403.       char *name = getword_conf (r->pool, &names);
  404.  
  405.       if ((is_matchexp(name) && !strcasecmp_match(host, name)) ||
  406.       (!strcasecmp(host, name))) {
  407.     r->server = r->connection->server = s;
  408.     if (r->hostlen && !strncmp(r->uri, "http://", 7)) {
  409.       r->uri += r->hostlen;
  410.       r->proxyreq = 0;
  411.     }
  412.       }
  413.     }
  414.   }
  415. }
  416.  
  417. void check_serverpath (request_rec *r) {
  418.   server_rec *s;
  419.  
  420.   /* This is in conjunction with the ServerPath code in
  421.    * http_core, so we get the right host attached to a non-
  422.    * Host-sending request.
  423.    */
  424.  
  425.   for (s = r->server->next; s; s = s->next) {
  426.     if (s->path && !strncmp(r->uri, s->path, s->pathlen))
  427.       r->server = r->connection->server = s;
  428.   }
  429. }
  430.  
  431. request_rec *read_request (conn_rec *conn)
  432. {
  433.     request_rec *r = (request_rec *)pcalloc (conn->pool, sizeof(request_rec));
  434.   
  435.     r->connection = conn;
  436.     r->server = conn->server;
  437.     r->pool = make_sub_pool(conn->pool);
  438.  
  439.     conn->keptalive = conn->keepalive;
  440.     conn->keepalive = 0;
  441.  
  442.     conn->user = NULL;
  443.     conn->auth_type = NULL;
  444.  
  445.     r->headers_in = make_table (r->pool, 50);
  446.     r->subprocess_env = make_table (r->pool, 50);
  447.     r->headers_out = make_table (r->pool, 5);
  448.     r->err_headers_out = make_table (r->pool, 5);
  449.     r->notes = make_table (r->pool, 5);
  450.  
  451.     r->request_config = create_request_config (r->pool);
  452.     r->per_dir_config = r->server->lookup_defaults; /* For now. */
  453.  
  454.     r->sent_bodyct = 0; /* bytect isn't for body */
  455.     
  456.     r->status = 200;        /* Until further notice.
  457.                  * Only changed by die(), or (bletch!)
  458.                  * scan_script_header...
  459.                  */
  460.  
  461.     /* Get the request... */
  462.     
  463.     hard_timeout ("read", r);
  464.     if (!read_request_line (r)) return NULL;
  465.     if (!r->assbackwards) get_mime_headers(r);
  466.  
  467. /* handle Host header here, to get virtual server */
  468.  
  469.     if (r->hostname || (r->hostname = table_get(r->headers_in, "Host")))
  470.       check_hostalias(r);
  471.     else
  472.       check_serverpath(r);
  473.  
  474.     kill_timeout (r);
  475.     conn->keptalive = 0;   /* We now have a request - so no more short timeouts */
  476.     
  477.     if(!strcmp(r->method, "HEAD")) {
  478.         r->header_only=1;
  479.     r->method_number = M_GET;
  480.     }
  481.     else if(!strcmp(r->method, "GET")) 
  482.     r->method_number = M_GET;
  483.     else if(!strcmp(r->method,"POST")) 
  484.         r->method_number = M_POST;
  485.     else if(!strcmp(r->method,"PUT")) 
  486.         r->method_number = M_PUT;
  487.     else if(!strcmp(r->method,"DELETE")) 
  488.         r->method_number = M_DELETE;
  489.     else if(!strcmp(r->method,"CONNECT"))
  490.         r->method_number = M_CONNECT;
  491.     else 
  492.         r->method_number = M_INVALID; /* Will eventually croak. */
  493.  
  494.     return r;
  495. }
  496.  
  497. /*
  498.  * A couple of other functions which initialize some of the fields of
  499.  * a request structure, as appropriate for adjuncts of one kind or another
  500.  * to a request in progress.  Best here, rather than elsewhere, since
  501.  * *someone* has to set the protocol-specific fields...
  502.  */
  503.  
  504. void set_sub_req_protocol (request_rec *rnew, request_rec *r)
  505. {
  506.     rnew->assbackwards = 1;    /* Don't send headers from this. */
  507.     rnew->no_cache = 1;        /* Don't try to send USE_LOCAL_COPY for a
  508.                  * fragment.
  509.                  */
  510.     rnew->method = "GET"; rnew->method_number = M_GET;
  511.     rnew->protocol = "INCLUDED";
  512.  
  513.     rnew->status = 200;
  514.  
  515.     rnew->headers_in = r->headers_in;
  516.     rnew->subprocess_env = copy_table (rnew->pool, r->subprocess_env);
  517.     rnew->headers_out = make_table (rnew->pool, 5);
  518.     rnew->err_headers_out = make_table (rnew->pool, 5);
  519.     rnew->notes = make_table (rnew->pool, 5);
  520.     
  521.     rnew->main = r;
  522. }
  523.  
  524. void finalize_sub_req_protocol (request_rec *sub)
  525. {
  526.     SET_BYTES_SENT (sub->main);
  527.  
  528. /* Support for the Basic authentication protocol, and a bit for Digest.
  529.  */
  530.  
  531. void note_auth_failure(request_rec *r)
  532. {
  533.     if (!strcasecmp(auth_type(r), "Basic"))
  534.       note_basic_auth_failure(r);
  535.     else if(!strcasecmp(auth_type(r), "Digest"))
  536.       note_digest_auth_failure(r);
  537. }
  538.  
  539. void note_basic_auth_failure(request_rec *r)
  540. {
  541.     if (strcasecmp(auth_type(r), "Basic"))
  542.       note_auth_failure(r);
  543.     else
  544.       table_set (r->err_headers_out, "WWW-Authenticate",
  545.          pstrcat(r->pool, "Basic realm=\"", auth_name(r), "\"", NULL));
  546. }
  547.  
  548. void note_digest_auth_failure(request_rec *r)
  549. {
  550.     char nonce[10];
  551.  
  552.     sprintf(nonce, "%lu", time(NULL));
  553.     table_set (r->err_headers_out, "WWW-Authenticate",
  554.                pstrcat(r->pool, "Digest realm=\"", auth_name(r),
  555.                        "\", nonce=\"", nonce, "\"", NULL));
  556. }
  557.  
  558. int get_basic_auth_pw (request_rec *r, char **pw)
  559. {
  560.     char *auth_line = table_get (r->headers_in, "Authorization");
  561.     char *t;
  562.     
  563.     if(!(t = auth_type(r)) || strcasecmp(t, "Basic"))
  564.         return DECLINED;
  565.  
  566.     if (!auth_name (r)) {
  567.         log_reason ("need AuthName", r->uri, r);
  568.     return SERVER_ERROR;
  569.     }
  570.     
  571.     if(!auth_line) {
  572.         note_basic_auth_failure (r);
  573.     return AUTH_REQUIRED;
  574.     }
  575.  
  576.     if (strcmp(getword (r->pool, &auth_line, ' '), "Basic")) {
  577.         /* Client tried to authenticate using wrong auth scheme */
  578.         log_reason ("client used wrong authentication scheme", r->uri, r);
  579.         note_basic_auth_failure (r);
  580.     return AUTH_REQUIRED;
  581.     }
  582.  
  583.     t = uudecode (r->pool, auth_line);
  584.     r->connection->user = getword_nulls (r->pool, &t, ':');
  585.     r->connection->auth_type = "Basic";
  586.  
  587.     *pw = t;
  588.  
  589.     return OK;
  590. }
  591.  
  592. #define RESPONSE_CODE_LIST " 200 302 304 400 401 403 404 500 503 501 502 "
  593.  
  594. /* New Apache routine to map error responses into array indicies 
  595.  *  e.g.  400 -> 0,  500 -> 1,  502 -> 2 ...                     
  596.  * the indicies have no significance
  597.  */
  598.  
  599. char *status_lines[] = {
  600.    "200 OK",
  601.    "302 Found",
  602.    "304 Not Modified",
  603.    "400 Bad Request",
  604.    "401 Unauthorized",
  605.    "403 Forbidden",
  606.    "404 Not found",
  607.    "500 Server error",
  608.    "503 Out of resources",
  609.    "501 Not Implemented",
  610.    "502 Bad Gateway"
  611. }; 
  612.  
  613. char *response_titles[] = {
  614.    "200 OK",            /* Never actually sent, barring die(200,...) */
  615.    "Document moved",        /* 302 Redirect */
  616.    "304 Not Modified",        /* Never sent... 304 MUST be header only */
  617.    "Bad Request",
  618.    "Authorization Required",
  619.    "Forbidden",
  620.    "File Not found",
  621.    "Server Error",
  622.    "Out of resources",
  623.    "Method not implemented",
  624.    "Bad Gateway"
  625. };
  626.  
  627. int index_of_response(int err_no) {
  628.    char *cptr, err_string[10];
  629.    static char *response_codes = RESPONSE_CODE_LIST;
  630.    int index_number;
  631.    
  632.    sprintf(err_string,"%3d",err_no);
  633.    
  634.    cptr = response_codes;
  635.    cptr++;
  636.    index_number = 0;
  637.    while (*cptr && strncmp(cptr, err_string, 3)) { 
  638.       cptr += 4;
  639.       index_number++;
  640.    }
  641.    if (!*cptr) return -1;
  642.    return index_number;
  643. }
  644.  
  645.  
  646. void basic_http_header (request_rec *r)
  647. {
  648.     BUFF *fd = r->connection->client;
  649.     
  650.     if (r->assbackwards) return;
  651.     
  652.     if (!r->status_line)
  653.         r->status_line = status_lines[index_of_response(r->status)];
  654.     
  655.     bvputs(fd, SERVER_PROTOCOL, " ", r->status_line, "\015\012", NULL);
  656.     bvputs(fd,"Date: ",gm_timestr_822 (r->pool, time(NULL)), "\015\012", NULL);
  657.     bvputs(fd,"Server: ", SERVER_VERSION, "\015\012", NULL);
  658. }
  659.  
  660. char *nuke_mime_parms (pool *p, char *content_type)
  661. {
  662.     /* How marvelous.  Arena doesn't *accept* "text/html; level=3"
  663.      * as a MIME type, so we have to strip off the parms.
  664.      */
  665.  
  666. #ifndef ARENA_BUG_WORKAROUND
  667.     return content_type;
  668. #else
  669.  
  670.     char *cp = strchr(content_type, ';');
  671.  
  672.     if (cp) {
  673.         content_type = pstrdup (p, content_type);
  674.     cp = strchr (content_type, ';');
  675.     
  676.         while (cp > content_type && isspace (cp[-1]))
  677.         --cp;
  678.     *cp = '\0';
  679.     }
  680.  
  681.     return content_type;
  682. #endif
  683. }
  684.  
  685. void send_http_header(request_rec *r)
  686. {
  687.     conn_rec *c = r->connection;
  688.     BUFF *fd = c->client;
  689.     const long int zero=0L;
  690.     array_header *hdrs_arr;
  691.     table_entry *hdrs;
  692.     int i;
  693.     
  694.     core_dir_config *dir_conf =
  695.       (core_dir_config *)get_module_config(r->per_dir_config, &core_module);
  696.     char *default_type = dir_conf->default_type;
  697.   
  698.     if (r->assbackwards) {
  699.     bsetopt(fd, BO_BYTECT, &zero);
  700.     r->sent_bodyct = 1;
  701.     return;
  702.     }
  703.     
  704.     basic_http_header (r);
  705.  
  706.     set_keepalive (r);
  707.     
  708.     if (r->content_type)
  709.         bvputs(fd, "Content-type: ", 
  710.          nuke_mime_parms (r->pool, r->content_type), "\015\012", NULL);
  711.     else if(default_type)
  712.         bvputs(fd, "Content-type: ", default_type, "\015\012", NULL);
  713.     
  714.     if (r->content_encoding)
  715.         bvputs(fd,"Content-encoding: ", r->content_encoding, "\015\012", NULL);
  716.     
  717.     if (r->content_language)
  718.         bvputs(fd,"Content-language: ", r->content_language, "\015\012", NULL);
  719.     
  720.     hdrs_arr = table_elts(r->headers_out);
  721.     hdrs = (table_entry *)hdrs_arr->elts;
  722.     for (i = 0; i < hdrs_arr->nelts; ++i) {
  723.         if (!hdrs[i].key) continue;
  724.     bvputs(fd, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL);
  725.     }
  726.  
  727.     hdrs_arr = table_elts(r->err_headers_out);
  728.     hdrs = (table_entry *)hdrs_arr->elts;
  729.     for (i = 0; i < hdrs_arr->nelts; ++i) {
  730.         if (!hdrs[i].key) continue;
  731.     bvputs(fd, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL);
  732.     }
  733.  
  734.     bputs("\015\012",fd);
  735.  
  736.     if (c->keepalive)
  737.     bflush(r->connection->client);  /* For bugs in Netscape, perhaps */
  738.  
  739.     bsetopt(fd, BO_BYTECT, &zero);
  740.     r->sent_bodyct = 1;        /* Whatever follows is real body stuff... */
  741. }
  742.  
  743. long read_client_block (request_rec *r, char *buffer, int bufsiz)
  744. {
  745.     return bread(r->connection->client, buffer, bufsiz);
  746. }
  747.  
  748. long send_fd(FILE *f, request_rec *r)
  749. {
  750.     char buf[IOBUFSIZE];
  751.     long total_bytes_sent;
  752.     register int n,o,w;
  753.     conn_rec *c = r->connection;
  754.     
  755.     total_bytes_sent = 0;
  756.     while (!r->connection->aborted) {
  757.         while ((n= fread(buf, sizeof(char), IOBUFSIZE, f)) < 1
  758.            && ferror(f) && errno == EINTR)
  759.         continue;
  760.     
  761.     if (n < 1) {
  762.             break;
  763.         }
  764.         o=0;
  765.     total_bytes_sent += n;
  766.     
  767.         while(n && !r->connection->aborted) {
  768.             w=bwrite(c->client, &buf[o], n);
  769.         if(w <= 0)
  770.         break;
  771.         reset_timeout(r); /* reset timeout after successfule write */
  772.             n-=w;
  773.             o+=w;
  774.         }
  775.     }
  776.     bflush(c->client);
  777.     
  778.     SET_BYTES_SENT(r);
  779.     return total_bytes_sent;
  780. }
  781.  
  782. int rputc (int c, request_rec *r)
  783. {
  784.     if (r->connection->aborted) return EOF;
  785.     bputc(c, r->connection->client);
  786.     SET_BYTES_SENT(r);
  787.     return c;
  788. }
  789.  
  790. int
  791. rputs(const char *str, request_rec *r)
  792. {
  793.     if (r->connection->aborted) return EOF;
  794.     SET_BYTES_SENT(r);
  795.     return bputs(str, r->connection->client);
  796. }
  797.  
  798. int rprintf(request_rec *r,const char *fmt,...)
  799.     {
  800.     va_list vlist;
  801.     int n;
  802.  
  803.     if(r->connection->aborted) return EOF;
  804.     va_start(vlist,fmt);
  805.     n=vbprintf(r->connection->client,fmt,vlist);
  806.     va_end(vlist);
  807.     SET_BYTES_SENT(r);
  808.     return n;
  809.     }
  810.  
  811. int
  812. rvputs(request_rec *r, ...)
  813. {
  814.     va_list args;
  815.     int i, j, k;
  816.     const char *x;
  817.     BUFF *fb=r->connection->client;
  818.     
  819.     if (r->connection->aborted) return EOF;
  820.     
  821.     va_start (args, r);
  822.     for (k=0;;)
  823.     {
  824.     x = va_arg(args, const char *);
  825.     if (x == NULL) break;
  826.     j = strlen(x);
  827.     i = bwrite(fb, x, j);
  828.     if (i != j)
  829.     {
  830.         va_end(args);
  831.         return -1;
  832.     }
  833.     k += i;
  834.     }
  835.     va_end(args);
  836.  
  837.     SET_BYTES_SENT(r);
  838.     return k;
  839. }
  840.  
  841. void send_error_response (request_rec *r, int recursive_error)
  842. {
  843.     conn_rec *c = r->connection;
  844.     char *custom_response;
  845.     int status = r->status;
  846.     int idx = index_of_response (status);
  847.     char *location = table_get (r->headers_out, "Location");
  848.  
  849.     if (!r->assbackwards) {
  850.     int i;
  851.     table *err_hdrs_arr = r->err_headers_out;
  852.     table_entry *err_hdrs = (table_entry *)err_hdrs_arr->elts;
  853.   
  854.         basic_http_header (r);
  855.     
  856.     /* For conditional get's which didn't send anything, *don't*
  857.      * send a bogus content-type, or any body --- but must still
  858.      * terminate header.
  859.      */
  860.     
  861.     if (status == USE_LOCAL_COPY) {
  862.         if (set_keepalive(r))
  863.         bputs("Connection: Keep-Alive\015\012", c->client);
  864.         bputs("\015\012", c->client);
  865.         return;
  866.     }
  867.     
  868.     if (status == REDIRECT)
  869.         bvputs(c->client, "Location: ", location, "\015\012", NULL);
  870.     
  871.     for (i = 0; i < err_hdrs_arr->nelts; ++i) {
  872.         if (!err_hdrs[i].key) continue;
  873.         bvputs(c->client, err_hdrs[i].key, ": ", err_hdrs[i].val,
  874.            "\015\012", NULL);
  875.     }
  876.  
  877.     bputs("Content-type: text/html\015\012\015\012", c->client);
  878.     }
  879.  
  880.     if (r->header_only) return;
  881.     
  882.     if ((custom_response = response_code_string (r, idx)))
  883.         bputs(custom_response, c->client);
  884.     else {
  885.     char *title = response_titles[idx];
  886.     BUFF *fd = c->client;
  887.     
  888.         bvputs(fd,"<HEAD><TITLE>", title, "</TITLE></HEAD>\n<BODY><H1>", title,
  889.            "</H1>\n", NULL);
  890.     
  891.         switch (r->status) {
  892.     case REDIRECT:
  893.         bvputs(fd, "The document has moved <A HREF=\"",
  894.             escape_html(r->pool, location), "\">here</A>.<P>\n", NULL);
  895.         break;
  896.     case AUTH_REQUIRED:
  897.         bputs("This server could not verify that you\n", fd);
  898.         bputs("are authorized to access the document you\n", fd);
  899.         bputs("requested.  Either you supplied the wrong\n", fd);
  900.         bputs("credentials (e.g., bad password), or your\n", fd);
  901.         bputs("browser doesn't understand how to supply\n", fd);
  902.         bputs("the credentials required.<P>\n", fd);
  903.         break;
  904.     case BAD_REQUEST:
  905.         bputs("Your browser sent a query that\n", fd);
  906.         bputs("this server could not understand.<P>\n", fd);
  907.         break;
  908.     case FORBIDDEN:
  909.         bvputs(fd, "You don't have permission to access ",
  910.              escape_html(r->pool, r->uri), "\non this server.<P>\n",
  911.            NULL);
  912.         break;
  913.     case NOT_FOUND:
  914.          bvputs(fd, "The requested URL ", escape_html(r->pool, r->uri),
  915.             " was not found on this server.<P>\n", NULL);
  916.         break;
  917.     case SERVER_ERROR:
  918.         bputs("The server encountered an internal error or\n", fd);
  919.         bputs("misconfiguration and was unable to complete\n", fd);
  920.         bputs("your request.<P>\n", fd);
  921.         bputs("Please contact the server administrator,\n ", fd);
  922.         bputs(escape_html(r->pool, r->server->server_admin), fd);
  923.         bputs(" and inform them of the time the error occurred,\n", fd);
  924.         bputs("and anything you might have done that may have\n", fd);
  925.         bputs("caused the error.<P>\n", fd);
  926.         break;
  927.     case NOT_IMPLEMENTED:
  928.         bvputs(fd, escape_html(r->pool, r->method), " to ",
  929.            escape_html(r->pool, r->uri), " not supported.<P>\n", NULL);
  930.         break;
  931.     case BAD_GATEWAY:
  932.         bputs("The proxy server received an invalid\015\012", fd);
  933.         bputs("response from an upstream server.<P>\015\012", fd);
  934.         break;
  935.     }
  936.  
  937.         if (recursive_error) {
  938.         char x[80];
  939.         sprintf (x, "Additionally, an error of type %d was encountered\n",
  940.              recursive_error);
  941.         bputs(x, fd);
  942.         bputs("while trying to use an ErrorDocument to\n", fd);
  943.         bputs("handle the request.\n", fd);
  944.     }
  945.     bputs("</BODY>\n", fd);
  946.     }
  947.         
  948. }
  949.  
  950. /* Finally, this... it's here to support nph- scripts
  951.  * Now what ever are we going to do about them when HTTP-NG packetization
  952.  * comes along?
  953.  */
  954.  
  955. void client_to_stdout (conn_rec *c)
  956. {
  957.     bflush(c->client);
  958.     dup2(c->client->fd, STDOUT_FILENO);
  959. }
  960.