home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 9 / CDACTUAL9.iso / share / Os2 / varios / APACHE / MOD_INCL.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-17  |  25.8 KB  |  887 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_include.c: Handles the server-parsed HTML documents
  57.  * 
  58.  * Original by Rob McCool; substantial fixups by David Robinson;
  59.  * incorporated into the Shambhala module framework by rst.
  60.  * 
  61.  */
  62.  
  63. #include "httpd.h"
  64. #include "http_config.h"
  65. #include "http_request.h"
  66. #include "http_core.h"
  67. #include "http_protocol.h"
  68. #include "http_log.h"
  69. #include "http_main.h"
  70. #include "util_script.h"
  71.  
  72. #define STARTING_SEQUENCE "<!--#"
  73. #define ENDING_SEQUENCE "-->"
  74. #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
  75. #define DEFAULT_TIME_FORMAT "%A, %d-%b-%y %T %Z"
  76. #define SIZEFMT_BYTES 0
  77. #define SIZEFMT_KMG 1
  78.  
  79. static void decodehtml(char *s);
  80. static char *get_tag(pool *p, FILE *in, char *tag, int tag_len, int dodecode);
  81. static int get_directive(FILE *in, char *d);
  82.  
  83. /* ------------------------ Environment function -------------------------- */
  84.  
  85. void add_include_vars(request_rec *r, char *timefmt)
  86. {
  87.     table *e = r->subprocess_env;
  88.     char *t;
  89.     time_t date = time(NULL);
  90.  
  91.     table_set(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0));
  92.     table_set(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1));
  93.     table_set(e, "LAST_MODIFIED",ht_time(r->pool,r->finfo.st_mtime,timefmt,0));
  94.     table_set(e, "DOCUMENT_URI", r->uri);
  95.     table_set(e, "DOCUMENT_PATH_INFO", r->path_info);
  96.     if((t = strrchr(r->filename, '/')))
  97.         table_set (e, "DOCUMENT_NAME", ++t);
  98.     else
  99.         table_set (e, "DOCUMENT_NAME", r->uri);
  100. }
  101.  
  102. #define GET_CHAR(f,c,r) \
  103.  { \
  104.    int i = getc(f); \
  105.    if(feof(f) || ferror(f) || (i == -1)) { \
  106.         fclose(f); \
  107.         return r; \
  108.    } \
  109.    c = (char)i; \
  110.  }
  111.  
  112. /* --------------------------- Parser functions --------------------------- */
  113.  
  114. /* Grrrr... rputc makes this slow as all-get-out.  Elsewhere, it doesn't
  115.  * matter much, but this is an inner loop...
  116.  */
  117.  
  118. int find_string(FILE *in,char *str, request_rec *r) {
  119.     int x,l=strlen(str),p;
  120.     char c;
  121.  
  122.     p=0;
  123.     while(1) {
  124.         GET_CHAR(in,c,1);
  125.         if(c == str[p]) {
  126.             if((++p) == l)
  127.                 return 0;
  128.         }
  129.         else {
  130.             if(r) {
  131.                 if(p) {
  132.                     for(x=0;x<p;x++) {
  133.                         rputc(str[x],r);
  134.                     }
  135.                 }
  136.                 rputc(c,r);
  137.             }
  138.             p=0;
  139.         }
  140.     }
  141. }
  142.  
  143. /*
  144.  * decodes a string containing html entities or numeric character references.
  145.  * 's' is overwritten with the decoded string.
  146.  * If 's' is syntatically incorrect, then the followed fixups will be made:
  147.  *   unknown entities will be left undecoded;
  148.  *   references to unused numeric characters will be deleted.
  149.  *   In particular, � will not be decoded, but will be deleted.
  150.  *
  151.  * drtr
  152.  */
  153.  
  154. /* maximum length of any ISO-LATIN-1 HTML entity name. */
  155. #define MAXENTLEN (6)
  156.  
  157. /* The following is a shrinking transformation, therefore safe. */
  158.  
  159. static void
  160. decodehtml(char *s)
  161. {
  162.     int val, i, j;
  163.     char *p=s;
  164.     char *ents;
  165.     static char *entlist[MAXENTLEN+1]={
  166.     NULL,  /* 0 */
  167.     NULL,  /* 1 */
  168.     "lt\074gt\076", /* 2 */
  169.     "amp\046ETH\320eth\360", /* 3 */
  170.     "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\
  171. iuml\357ouml\366uuml\374yuml\377", /* 4 */
  172.     "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\
  173. THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\
  174. ucirc\373thorn\376", /* 5 */
  175.     "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\
  176. Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\
  177. Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\
  178. egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\
  179. otilde\365oslash\370ugrave\371uacute\372yacute\375" /* 6 */
  180.     };
  181.  
  182.     for (; *s != '\0'; s++, p++) {
  183.     if (*s != '&') {
  184.         *p = *s;
  185.         continue;
  186.     }
  187.     /* find end of entity */
  188.     for (i=1; s[i] != ';' && s[i] != '\0'; i++)
  189.         continue;
  190.  
  191.     if (s[i] == '\0') {    /* treat as normal data */
  192.         *p = *s;
  193.         continue;
  194.     }
  195.  
  196.     /* is it numeric ? */
  197.     if (s[1] == '#') {
  198.         for (j=2, val=0; j < i && isdigit(s[j]); j++)
  199.         val = val * 10 + s[j] - '0';
  200.         s += i;
  201.         if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
  202.         (val >= 127 && val <= 160) || val >= 256)
  203.         p--;  /* no data to output */
  204.         else
  205.         *p = val;
  206.     } else{
  207.         j = i-1;
  208.         if (i-1 > MAXENTLEN || entlist[i-1] == NULL) { /* wrong length */
  209.         *p = '&';
  210.         continue;  /* skip it */
  211.         }
  212.         for (ents=entlist[i-1]; *ents != '\0'; ents += i)
  213.         if (strncmp(s+1, ents, i-1) == 0) break;
  214.  
  215.         if (*ents == '\0')
  216.         *p = '&';  /* unknown */
  217.         else {
  218.         *p = ((const unsigned char *)ents)[i-1];
  219.         s += i;
  220.         }
  221.     }
  222.     }
  223.  
  224.     *p = '\0';
  225. }
  226.  
  227. /*
  228.  * extract the next tag name and value.
  229.  * if there are no more tags, set the tag name to 'done'
  230.  * the tag value is html decoded if dodecode is non-zero
  231.  */
  232.  
  233. static char *
  234. get_tag(pool *p, FILE *in, char *tag, int tagbuf_len, int dodecode) {
  235.     char *t = tag, *tag_val, c, term;
  236.     int n;
  237.  
  238.     n = 0;
  239.  
  240.     do { /* skip whitespace */
  241.     GET_CHAR(in,c,NULL);
  242.     } while (isspace(c));
  243.  
  244.     /* tags can't start with - */
  245.     if(c == '-') {
  246.         GET_CHAR(in,c,NULL);
  247.         if(c == '-') {
  248.             do {
  249.         GET_CHAR(in,c,NULL);
  250.         } while (isspace(c));
  251.             if(c == '>') {
  252.                 strcpy(tag,"done");
  253.                 return tag;
  254.             }
  255.         }
  256.     return NULL; /* failed */
  257.     }
  258.  
  259.     /* find end of tag name */
  260.     while(1) {
  261.         if(++n == tagbuf_len) {
  262.             t[tagbuf_len - 1] = '\0';
  263.             return NULL;
  264.         }
  265.     if(c == '=' || isspace(c)) break;
  266.     *(t++) = tolower(c);
  267.         GET_CHAR(in,c,NULL);
  268.     }
  269.  
  270.     *t++ = '\0';
  271.     tag_val = t;
  272.  
  273.     while (isspace(c)) GET_CHAR(in, c, NULL); /* space before = */
  274.     if (c != '=') return NULL;
  275.  
  276.     do {
  277.     GET_CHAR(in,c,NULL);  /* space after = */
  278.     } while (isspace(c));
  279.  
  280.     /* we should allow a 'name' as a value */
  281.     
  282.     if (c != '"' && c != '\'') return NULL;
  283.     term = c;
  284.     while(1) {
  285.     GET_CHAR(in,c,NULL);
  286.     if(++n == tagbuf_len) {
  287.         t[tagbuf_len - 1] = '\0';
  288.         return NULL;
  289.     }
  290.     if (c == term) break;
  291.     *(t++) = c;
  292.     }
  293.     *t = '\0';
  294.     if (dodecode) decodehtml(tag_val);
  295.     return pstrdup (p, tag_val);
  296. }
  297.  
  298. static int
  299. get_directive(FILE *in, char *d) {
  300.     char c;
  301.  
  302.     /* skip initial whitespace */
  303.     while(1) {
  304.         GET_CHAR(in,c,1);
  305.         if(!isspace(c))
  306.             break;
  307.     }
  308.     /* now get directive */
  309.     while(1) {
  310.         *d++ = tolower(c);
  311.         GET_CHAR(in,c,1);
  312.         if(isspace(c))
  313.             break;
  314.     }
  315.     *d = '\0';
  316.     return 0;
  317. }
  318.  
  319. /* --------------------------- Action handlers ---------------------------- */
  320.  
  321. int include_cgi(char *s, request_rec *r)
  322. {
  323.     request_rec *rr = sub_req_lookup_uri (s, r);
  324.     
  325.     if (rr->status != 200) return -1;
  326.     
  327.     /* No hardwired path info or query allowed */
  328.     
  329.     if ((rr->path_info && rr->path_info[0]) || rr->args) return -1;
  330.     if (rr->finfo.st_mode == 0) return -1;
  331.  
  332.     /* Script gets parameters of the *document*, for back compatibility */
  333.     
  334.     rr->path_info = r->path_info; /* painful to get right; see mod_cgi.c */
  335.     rr->args = r->args;
  336.     
  337.     /* Force sub_req to be treated as a CGI request, even if ordinary
  338.      * typing rules would have called it something else.
  339.      */
  340.  
  341.     rr->content_type = CGI_MAGIC_TYPE;
  342.  
  343.     /* Run it. */
  344.     
  345.     if (run_sub_req (rr) == REDIRECT) {
  346.         char *location = table_get (rr->headers_out, "Location");
  347.     location = escape_html(rr->pool, location);
  348.     rprintf(r,"<A HREF=\"%s\">%s</A>",location,location);
  349.     }
  350.     
  351.     destroy_sub_req (rr);
  352.     
  353.     return 0;
  354. }
  355.  
  356. int handle_include(FILE *in, request_rec *r, char *error, int noexec) {
  357.     char tag[MAX_STRING_LEN];
  358.     char *tag_val;
  359.  
  360.     while(1) {
  361.         if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1)))
  362.             return 1;
  363.         if(!strcmp(tag,"file") || !strcmp (tag, "virtual")) {
  364.         request_rec *rr=NULL;
  365.         char *error_fmt = NULL;
  366.  
  367.         if (tag[0] == 'f')
  368.         { /* be safe; only files in this directory or below allowed */
  369.         char tmp[MAX_STRING_LEN+2];
  370.         sprintf(tmp, "/%s/", tag_val);
  371.         if (tag_val[0] == '/' || strstr(tmp, "/../") != NULL)
  372.             error_fmt = "unable to include file %s in parsed file %s";
  373.         else
  374.             rr = sub_req_lookup_file (tag_val, r);
  375.         } else
  376.         rr = sub_req_lookup_uri (tag_val, r);
  377.         
  378.         if (!error_fmt && rr->status != 200)
  379.             error_fmt = "unable to include %s in parsed file %s";
  380.  
  381.         if (!error_fmt && noexec && rr->content_type
  382.         && (strncmp (rr->content_type, "text/", 5)))
  383.             error_fmt =
  384.           "unable to include potential exec %s in parsed file %s";
  385.  
  386.         if (error_fmt == NULL)
  387.         {
  388.         request_rec *p;
  389.  
  390.         for (p=r; p != NULL; p=p->main)
  391.             if (strcmp(p->filename, rr->filename) == 0) break;
  392.         if (p != NULL)
  393.             error_fmt = "Recursive include of %s in parsed file %s";
  394.         }
  395.         
  396.         if (!error_fmt && run_sub_req (rr))
  397.             error_fmt = "unable to include %s in parsed file %s";
  398.             
  399.             if (error_fmt) {
  400.                 log_printf(r->server, error_fmt, tag_val, r->filename);
  401.                 rprintf(r,"%s",error);
  402.             }            
  403.  
  404.         if (rr != NULL) destroy_sub_req (rr);
  405.         } 
  406.         else if(!strcmp(tag,"done"))
  407.             return 0;
  408.         else {
  409.             log_printf(r->server, "unknown parameter %s to tag include in %s",
  410.                tag, r->filename);
  411.             rprintf(r,"%s",error);
  412.         }
  413.     }
  414. }
  415.  
  416. typedef struct {
  417.     request_rec *r;
  418.     char *s;
  419. } include_cmd_arg;
  420.  
  421. void include_cmd_child (void *arg)
  422. {
  423.     request_rec *r =  ((include_cmd_arg *)arg)->r;
  424.     char *s = ((include_cmd_arg *)arg)->s;
  425.     table *env = r->subprocess_env;
  426. #ifdef DEBUG_INCLUDE_CMD    
  427. #ifdef __EMX__
  428.     /* under OS/2 /dev/tty is referenced as con */
  429.     FILE *dbg = fopen ("con", "w");
  430. #else    
  431.     FILE *dbg = fopen ("/dev/tty", "w");
  432. #endif    
  433. #endif    
  434.     char err_string [MAX_STRING_LEN];
  435.  
  436. #ifdef DEBUG_INCLUDE_CMD    
  437.     fprintf (dbg, "Attempting to include command '%s'\n", s);
  438. #endif    
  439.  
  440.     if (r->path_info && r->path_info[0] != '\0')
  441.     {
  442.     request_rec *pa_req;
  443.  
  444.     table_set (env, "PATH_INFO", escape_shell_cmd (r->pool, r->path_info));
  445.     
  446.     pa_req = sub_req_lookup_uri(escape_uri(r->pool, r->path_info), r);
  447.     if (pa_req->filename)
  448.         table_set(env, "PATH_TRANSLATED",
  449.               pstrcat(r->pool, pa_req->filename, pa_req->path_info,
  450.                   NULL));
  451.     }
  452.  
  453.     if (r->args) {
  454.         table_set (env, "QUERY_STRING", r->args);
  455.     unescape_url (r->args);
  456.     table_set (env, "QUERY_STRING_UNESCAPED",
  457.            escape_shell_cmd (r->pool, r->args));
  458.     }
  459.     
  460.     error_log2stderr (r->server);
  461.     
  462. #ifdef DEBUG_INCLUDE_CMD    
  463.     fprintf (dbg, "Attempting to exec '%s'\n", s);
  464. #endif    
  465.     cleanup_for_exec();
  466.     execle(SHELL_PATH, SHELL_PATH, "-c", s, NULL,
  467.        create_environment (r->pool, env));
  468.     
  469.     /* Oh, drat.  We're still here.  The log file descriptors are closed,
  470.      * so we have to whimper a complaint onto stderr...
  471.      */
  472.     
  473. #ifdef DEBUG_INCLUDE_CMD    
  474.     fprintf (dbg, "Exec failed\n");
  475. #endif    
  476.     sprintf(err_string, "httpd: exec of %s failed, errno is %d\n",
  477.         SHELL_PATH,errno);
  478.     write (2, err_string, strlen(err_string));
  479.     exit(0);
  480. }
  481.  
  482. int include_cmd(char *s, request_rec *r) {
  483.     include_cmd_arg arg;
  484.     FILE *f;
  485.  
  486.     arg.r = r; arg.s = s;
  487.  
  488.     if (!spawn_child (r->connection->pool, include_cmd_child, &arg,
  489.               kill_after_timeout, NULL, &f))
  490.         return -1;
  491.     
  492.     send_fd(f,r);
  493.     pfclose(r->pool, f);    /* will wait for zombie when
  494.                  * r->pool is cleared
  495.                  */
  496.     return 0;
  497. }
  498.  
  499.  
  500. int handle_exec(FILE *in, request_rec *r, char *error)
  501. {
  502.     char tag[MAX_STRING_LEN];
  503.     char *tag_val;
  504.     char *file = r->filename;
  505.  
  506.     while(1) {
  507.         if(!(tag_val = get_tag (r->pool, in, tag, MAX_STRING_LEN, 1)))
  508.             return 1;
  509.         if(!strcmp(tag,"cmd")) {
  510.             if(include_cmd(tag_val, r) == -1) {
  511.                 log_printf(r->server, "failed command exec %s in %s",
  512.                tag_val, file);
  513.                 rprintf(r,"%s",error);
  514.             }
  515.             /* just in case some stooge changed directories */
  516.             chdir_file(r->filename);
  517.         } 
  518.         else if(!strcmp(tag,"cgi")) {
  519.             if(include_cgi(tag_val, r) == -1) {
  520.                 log_printf(r->server, "invalid CGI ref %s in %s",tag_val,file);
  521.                 rprintf(r,"%s",error);
  522.             }
  523.             /* grumble groan */
  524.             chdir_file(r->filename);
  525.         }
  526.         else if(!strcmp(tag,"done"))
  527.             return 0;
  528.         else {
  529.             log_printf(r->server, "unknown parameter %s to tag exec in %s",
  530.                tag, file);
  531.             rprintf(r, "%s",error);
  532.         }
  533.     }
  534.  
  535. }
  536.  
  537. int handle_echo (FILE *in, request_rec *r, char *error) {
  538.     char tag[MAX_STRING_LEN];
  539.     char *tag_val;
  540.  
  541.     while(1) {
  542.         if(!(tag_val = get_tag (r->pool, in, tag, MAX_STRING_LEN, 1)))
  543.             return 1;
  544.         if(!strcmp(tag,"var")) {
  545.         char *val = table_get (r->subprocess_env, tag_val);
  546.  
  547.         if (val) rprintf (r, "%s", val);
  548.         else rprintf (r, "(none)");
  549.         } else if(!strcmp(tag,"done"))
  550.             return 0;
  551.         else {
  552.             log_printf(r->server, "unknown parameter %s to tag echo in %s",
  553.             tag, r->filename);
  554.             rprintf (r, "%s", error);
  555.         }
  556.     }
  557. }
  558.  
  559. int handle_config(FILE *in, request_rec *r, char *error, char *tf,
  560.                   int *sizefmt) {
  561.     char tag[MAX_STRING_LEN];
  562.     char *tag_val;
  563.     table *env = r->subprocess_env;
  564.  
  565.     while(1) {
  566.         if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 0)))
  567.             return 1;
  568.         if(!strcmp(tag,"errmsg"))
  569.             strcpy(error,tag_val);
  570.         else if(!strcmp(tag,"timefmt")) {
  571.         time_t date = time(NULL);
  572.             strcpy(tf,tag_val);
  573.             table_set (env, "DATE_LOCAL", ht_time(r->pool,date,tf,0));
  574.             table_set (env, "DATE_GMT", ht_time(r->pool,date,tf,1));
  575.             table_set (env, "LAST_MODIFIED", ht_time(r->pool,r->finfo.st_mtime,tf,0));
  576.         }
  577.         else if(!strcmp(tag,"sizefmt")) {
  578.         decodehtml(tag_val);
  579.             if(!strcmp(tag_val,"bytes"))
  580.                 *sizefmt = SIZEFMT_BYTES;
  581.             else if(!strcmp(tag_val,"abbrev"))
  582.                 *sizefmt = SIZEFMT_KMG;
  583.         } 
  584.         else if(!strcmp(tag,"done"))
  585.             return 0;
  586.         else {
  587.             log_printf(r->server, "unknown parameter %s to tag config in %s",
  588.                     tag, r->filename);
  589.             rprintf (r,"%s",error);
  590.         }
  591.     }
  592. }
  593.  
  594.  
  595.  
  596. int find_file(request_rec *r, char *directive, char *tag, 
  597.               char *tag_val, struct stat *finfo, char *error)
  598. {
  599.     char dir[MAX_STRING_LEN];
  600.     char *to_send;
  601.  
  602.     if(!strcmp(tag,"file")) {
  603.         getparents(tag_val); /* get rid of any nasties */
  604.         getwd(dir);
  605.         to_send = make_full_path (r->pool, dir, tag_val);
  606.         if(stat(to_send,finfo) == -1) {
  607.             log_printf(r->server,
  608.                     "unable to get information about %s in parsed file %s",
  609.                     to_send, r->filename);
  610.             rprintf (r,"%s",error);
  611.             return -1;
  612.         }
  613.         return 0;
  614.     }
  615.     else if(!strcmp(tag,"virtual")) {
  616.     request_rec *rr = sub_req_lookup_uri (tag_val, r);
  617.     
  618.     if (rr->status == 200 && rr->finfo.st_mode != 0) {
  619.         memcpy ((char*)finfo, (const char *)&rr->finfo, sizeof (struct stat));
  620.         destroy_sub_req (rr);
  621.         return 0;
  622.         } else {
  623.             log_printf(r->server,
  624.                     "unable to get information about %s in parsed file %s",
  625.                     tag_val, r->filename);
  626.             rprintf(r,"%s",error);
  627.         destroy_sub_req (rr);
  628.             return -1;
  629.         }
  630.     }
  631.     else {
  632.         log_printf(r->server, "unknown parameter %s to tag %s in %s",
  633.                 tag, directive, r->filename);
  634.         rprintf(r,"%s",error);
  635.         return -1;
  636.     }
  637. }
  638.  
  639.  
  640. int handle_fsize(FILE *in, request_rec *r, char *error, int sizefmt) 
  641. {
  642.     char tag[MAX_STRING_LEN];
  643.     char *tag_val;
  644.     struct stat finfo;
  645.  
  646.     while(1) {
  647.         if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1)))
  648.             return 1;
  649.         else if(!strcmp(tag,"done"))
  650.             return 0;
  651.         else if(!find_file(r,"fsize",tag,tag_val,&finfo,error)) {
  652.             if(sizefmt == SIZEFMT_KMG) {
  653.                 send_size(finfo.st_size, r);
  654.             }
  655.             else {
  656.                 int l,x;
  657. #if defined(BSD) && BSD > 199305
  658.                 sprintf(tag,"%qd",finfo.st_size);
  659. #else
  660.                 sprintf(tag,"%ld",finfo.st_size);
  661. #endif
  662.                 l = strlen(tag); /* grrr */
  663.                 for(x=0;x<l;x++) {
  664.                     if(x && (!((l-x) % 3))) {
  665.                         rputc(',', r);
  666.                     }
  667.                     rputc (tag[x],r);
  668.                 }
  669.             }
  670.         }
  671.     }
  672. }
  673.  
  674. int handle_flastmod(FILE *in, request_rec *r, char *error, char *tf) 
  675. {
  676.     char tag[MAX_STRING_LEN];
  677.     char *tag_val;
  678.     struct stat finfo;
  679.  
  680.     while(1) {
  681.         if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1)))
  682.             return 1;
  683.         else if(!strcmp(tag,"done"))
  684.             return 0;
  685.         else if(!find_file(r,"flastmod",tag,tag_val,&finfo,error))
  686.             rprintf (r, "%s", ht_time(r->pool, finfo.st_mtime, tf, 0));
  687.     }
  688. }    
  689.  
  690.  
  691.  
  692. /* -------------------------- The main function --------------------------- */
  693.  
  694. /* This is a stub which parses a file descriptor. */
  695.  
  696. void send_parsed_content(FILE *f, request_rec *r)
  697. {
  698.     char directive[MAX_STRING_LEN], error[MAX_STRING_LEN];
  699.     char timefmt[MAX_STRING_LEN];
  700.     int noexec = allow_options (r) & OPT_INCNOEXEC;
  701.     int ret, sizefmt;
  702.  
  703.     strcpy(error,DEFAULT_ERROR_MSG);
  704.     strcpy(timefmt,DEFAULT_TIME_FORMAT);
  705.     sizefmt = SIZEFMT_KMG;
  706.  
  707.     chdir_file (r->filename);
  708.  
  709.     while(1) {
  710.         if(!find_string(f,STARTING_SEQUENCE,r)) {
  711.             if(get_directive(f,directive))
  712.                 return;
  713.             if(!strcmp(directive,"exec")) {
  714.                 if(noexec) {
  715.                     log_printf(r->server,
  716.                    "httpd: exec used but not allowed in %s",
  717.                    r->filename);
  718.                     rprintf(r,"%s",error);
  719.                     ret = find_string(f,ENDING_SEQUENCE,NULL);
  720.                 } else 
  721.                     ret=handle_exec(f, r, error);
  722.             } 
  723.             else if(!strcmp(directive,"config"))
  724.                 ret=handle_config(f, r, error, timefmt, &sizefmt);
  725.             else if(!strcmp(directive,"include"))
  726.                 ret=handle_include(f, r, error, noexec);
  727.             else if(!strcmp(directive,"echo"))
  728.                 ret=handle_echo(f, r, error);
  729.             else if(!strcmp(directive,"fsize"))
  730.                 ret=handle_fsize(f, r, error, sizefmt);
  731.             else if(!strcmp(directive,"flastmod"))
  732.                 ret=handle_flastmod(f, r, error, timefmt);
  733.             else {
  734.                 log_printf(r->server, 
  735.                "httpd: unknown directive %s in parsed doc %s",
  736.                directive, r->filename);
  737.                 rprintf (r,"%s",error);
  738.                 ret=find_string(f,ENDING_SEQUENCE,NULL);
  739.             }
  740.             if(ret) {
  741.                 log_printf(r->server, "httpd: premature EOF in parsed file %s",
  742.                r->filename);
  743.                 return;
  744.             }
  745.         } else 
  746.             return;
  747.     }
  748. }
  749.  
  750. /*****************************************************************
  751.  *
  752.  * XBITHACK.  Sigh...  NB it's configurable per-directory; the compile-time
  753.  * option only changes the default.
  754.  */
  755.  
  756. module includes_module;
  757. enum xbithack { xbithack_off, xbithack_on, xbithack_full };
  758.  
  759. #ifdef XBITHACK    
  760. #define DEFAULT_XBITHACK xbithack_full
  761. #else
  762. #define DEFAULT_XBITHACK xbithack_off
  763. #endif
  764.  
  765. void *create_includes_dir_config (pool *p, char *dummy)
  766. {
  767.     enum xbithack *result = (enum xbithack*)palloc(p, sizeof (enum xbithack));
  768.     *result = DEFAULT_XBITHACK;
  769.     return result;
  770. }
  771.  
  772. char *set_xbithack (cmd_parms *cmd, void *xbp, char *arg)
  773. {
  774.    enum xbithack *state = (enum xbithack *)xbp;
  775.  
  776.    if (!strcasecmp (arg, "off")) *state = xbithack_off;
  777.    else if (!strcasecmp (arg, "on")) *state = xbithack_on;
  778.    else if (!strcasecmp (arg, "full")) *state = xbithack_full;
  779.    else return "XBitHack must be set to Off, On, or Full";
  780.  
  781.    return NULL;
  782. }
  783.  
  784. int send_parsed_file(request_rec *r)
  785. {
  786.     FILE *f;
  787.     enum xbithack *state =
  788.     (enum xbithack *)get_module_config(r->per_dir_config,&includes_module);
  789.     int errstatus;
  790.  
  791.     if (!(allow_options (r) & OPT_INCLUDES)) return DECLINED;
  792.     if (r->method_number != M_GET) return DECLINED;
  793.     if (r->finfo.st_mode == 0) return NOT_FOUND;
  794.     
  795.     if (*state == xbithack_full
  796. #ifndef __EMX__    
  797.     /*  OS/2 dosen't support Groups. */
  798.     && (r->finfo.st_mode & S_IXGRP)
  799. #endif
  800.     && (errstatus = set_last_modified (r, r->finfo.st_mtime)))
  801.         return errstatus;
  802.     
  803.     if(!(f=pfopen(r->pool, r->filename, "r"))) {
  804.         log_reason("file permissions deny server access", r->filename, r);
  805.     return FORBIDDEN;
  806.     }
  807.     
  808.     r->content_type = "text/html";
  809.     
  810.     hard_timeout ("send", r);
  811.     send_http_header(r);
  812.  
  813.     if (r->header_only) {
  814.         kill_timeout (r);
  815.     pfclose (r->pool, f);
  816.     return OK;
  817.     }
  818.    
  819.     if (r->main) {
  820.     /* Kludge --- for nested includes, we want to keep the
  821.      * subprocess environment of the base document (for compatibility);
  822.      * that means torquing our own last_modified date as well so that
  823.      * the LAST_MODIFIED variable gets reset to the proper value if
  824.      * the nested document resets <!--#config timefmt-->
  825.      */
  826.     r->subprocess_env = r->main->subprocess_env;
  827.     r->finfo.st_mtime= r->main->finfo.st_mtime;
  828.     } else { 
  829.     add_common_vars (r);
  830.     add_include_vars (r, DEFAULT_TIME_FORMAT);
  831.     }
  832.     
  833.     send_parsed_content (f, r);
  834.     
  835.     kill_timeout (r);
  836.     return OK;
  837. }
  838.  
  839. int xbithack_handler (request_rec *r)
  840. {
  841.     enum xbithack *state;
  842.  
  843. #ifdef __EMX__
  844.     /* OS/2 dosen't currently support the xbithack. This is being worked on. */
  845.     return DECLINED;
  846. #else
  847.     
  848.     if (!(r->finfo.st_mode & S_IXUSR)) return DECLINED;
  849.  
  850.     state = (enum xbithack *)get_module_config(r->per_dir_config,
  851.                            &includes_module);
  852.     
  853.     if (*state == xbithack_off) return DECLINED;
  854.     return send_parsed_file (r);
  855. #endif
  856. }
  857.  
  858. command_rec includes_cmds[] = {
  859. { "XBitHack", set_xbithack, NULL, OR_OPTIONS, TAKE1, "Off, On, or Full" },
  860. { NULL }    
  861. };
  862.  
  863. handler_rec includes_handlers[] = {
  864. { INCLUDES_MAGIC_TYPE, send_parsed_file },
  865. { INCLUDES_MAGIC_TYPE3, send_parsed_file },
  866. { "*/*", xbithack_handler },
  867. { NULL }
  868. };
  869.  
  870. module includes_module = {
  871.    STANDARD_MODULE_STUFF,
  872.    NULL,            /* initializer */
  873.    create_includes_dir_config,    /* dir config creater */
  874.    NULL,            /* dir merger --- default is to override */
  875.    NULL,            /* server config */
  876.    NULL,            /* merge server config */
  877.    includes_cmds,        /* command table */
  878.    includes_handlers,        /* handlers */
  879.    NULL,            /* filename translation */
  880.    NULL,            /* check_user_id */
  881.    NULL,            /* check auth */
  882.    NULL,            /* check access */
  883.    NULL,            /* type_checker */
  884.    NULL,            /* fixups */
  885.    NULL                /* logger */
  886. };
  887.