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