home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / modules / standard / mod_include.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-01  |  74.6 KB  |  2,470 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. /*
  59.  * http_include.c: Handles the server-parsed HTML documents
  60.  * 
  61.  * Original by Rob McCool; substantial fixups by David Robinson;
  62.  * incorporated into the Apache module framework by rst.
  63.  * 
  64.  */
  65. /* 
  66.  * sub key may be anything a Perl*Handler can be:
  67.  * subroutine name, package name (defaults to package::handler),
  68.  * Class->method call or anoymous sub {}
  69.  *
  70.  * Child <!--#perl sub="sub {print $$}" --> accessed
  71.  * <!--#perl sub="sub {print ++$Access::Cnt }" --> times. <br>
  72.  *
  73.  * <!--#perl arg="one" sub="mymod::includer" -->
  74.  *
  75.  * -Doug MacEachern
  76.  */
  77.  
  78. #ifdef USE_PERL_SSI
  79. #include "config.h"
  80. #undef VOIDUSED
  81. #ifdef USE_SFIO
  82. #undef USE_SFIO
  83. #define USE_STDIO
  84. #endif
  85. #include "modules/perl/mod_perl.h"
  86. #else
  87. #include "httpd.h"
  88. #include "http_config.h"
  89. #include "http_request.h"
  90. #include "http_core.h"
  91. #include "http_protocol.h"
  92. #include "http_log.h"
  93. #include "http_main.h"
  94. #include "util_script.h"
  95. #endif
  96.  
  97. #define STARTING_SEQUENCE "<!--#"
  98. #define ENDING_SEQUENCE "-->"
  99. #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
  100. #define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
  101. #define SIZEFMT_BYTES 0
  102. #define SIZEFMT_KMG 1
  103. #ifdef CHARSET_EBCDIC
  104. #define RAW_ASCII_CHAR(ch)  os_toebcdic[(unsigned char)ch]
  105. #else /*CHARSET_EBCDIC*/
  106. #define RAW_ASCII_CHAR(ch)  (ch)
  107. #endif /*CHARSET_EBCDIC*/
  108.  
  109. module MODULE_VAR_EXPORT includes_module;
  110.  
  111. /* just need some arbitrary non-NULL pointer which can't also be a request_rec */
  112. #define NESTED_INCLUDE_MAGIC    (&includes_module)
  113.  
  114. /* ------------------------ Environment function -------------------------- */
  115.  
  116. /* XXX: could use ap_table_overlap here */
  117. static void add_include_vars(request_rec *r, char *timefmt)
  118. {
  119. #ifndef WIN32
  120.     struct passwd *pw;
  121. #endif /* ndef WIN32 */
  122.     table *e = r->subprocess_env;
  123.     char *t;
  124.     time_t date = r->request_time;
  125.  
  126.     ap_table_setn(e, "DATE_LOCAL", ap_ht_time(r->pool, date, timefmt, 0));
  127.     ap_table_setn(e, "DATE_GMT", ap_ht_time(r->pool, date, timefmt, 1));
  128.     ap_table_setn(e, "LAST_MODIFIED",
  129.               ap_ht_time(r->pool, r->finfo.st_mtime, timefmt, 0));
  130.     ap_table_setn(e, "DOCUMENT_URI", r->uri);
  131.     ap_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
  132. #ifndef WIN32
  133.     pw = getpwuid(r->finfo.st_uid);
  134.     if (pw) {
  135.         ap_table_setn(e, "USER_NAME", ap_pstrdup(r->pool, pw->pw_name));
  136.     }
  137.     else {
  138.         ap_table_setn(e, "USER_NAME", ap_psprintf(r->pool, "user#%lu",
  139.                     (unsigned long) r->finfo.st_uid));
  140.     }
  141. #endif /* ndef WIN32 */
  142.  
  143.     if ((t = strrchr(r->filename, '/'))) {
  144.         ap_table_setn(e, "DOCUMENT_NAME", ++t);
  145.     }
  146.     else {
  147.         ap_table_setn(e, "DOCUMENT_NAME", r->uri);
  148.     }
  149.     if (r->args) {
  150.         char *arg_copy = ap_pstrdup(r->pool, r->args);
  151.  
  152.         ap_unescape_url(arg_copy);
  153.         ap_table_setn(e, "QUERY_STRING_UNESCAPED",
  154.                   ap_escape_shell_cmd(r->pool, arg_copy));
  155.     }
  156. }
  157.  
  158.  
  159.  
  160. /* --------------------------- Parser functions --------------------------- */
  161.  
  162. #define OUTBUFSIZE 4096
  163. /* PUT_CHAR and FLUSH_BUF currently only work within the scope of 
  164.  * find_string(); they are hacks to avoid calling rputc for each and
  165.  * every character output.  A common set of buffering calls for this 
  166.  * type of output SHOULD be implemented.
  167.  */
  168. #define PUT_CHAR(c,r) \
  169.  { \
  170.     outbuf[outind++] = c; \
  171.     if (outind == OUTBUFSIZE) { \
  172.         FLUSH_BUF(r) \
  173.     }; \
  174.  }
  175.  
  176. /* there SHOULD be some error checking on the return value of
  177.  * rwrite, however it is unclear what the API for rwrite returning
  178.  * errors is and little can really be done to help the error in 
  179.  * any case.
  180.  */
  181. #define FLUSH_BUF(r) \
  182.  { \
  183.    ap_rwrite(outbuf, outind, r); \
  184.    outind = 0; \
  185.  }
  186.  
  187. /*
  188.  * f: file handle being read from
  189.  * c: character to read into
  190.  * ret: return value to use if input fails
  191.  * r: current request_rec
  192.  *
  193.  * This macro is redefined after find_string() for historical reasons
  194.  * to avoid too many code changes.  This is one of the many things
  195.  * that should be fixed.
  196.  */
  197. #define GET_CHAR(f,c,ret,r) \
  198.  { \
  199.    int i = getc(f); \
  200.    if (i == EOF) { /* either EOF or error -- needs error handling if latter */ \
  201.        if (ferror(f)) { \
  202.            fprintf(stderr, "encountered error in GET_CHAR macro, " \
  203.                    "mod_include.\n"); \
  204.        } \
  205.        FLUSH_BUF(r); \
  206.        ap_pfclose(r->pool, f); \
  207.        return ret; \
  208.    } \
  209.    c = (char)i; \
  210.  }
  211.  
  212. static int find_string(FILE *in, const char *str, request_rec *r, int printing)
  213. {
  214.     int x, l = strlen(str), p;
  215.     char outbuf[OUTBUFSIZE];
  216.     int outind = 0;
  217.     char c;
  218.  
  219.     p = 0;
  220.     while (1) {
  221.         GET_CHAR(in, c, 1, r);
  222.         if (c == str[p]) {
  223.             if ((++p) == l) {
  224.                 FLUSH_BUF(r);
  225.                 return 0;
  226.             }
  227.         }
  228.         else {
  229.             if (printing) {
  230.                 for (x = 0; x < p; x++) {
  231.                     PUT_CHAR(str[x], r);
  232.                 }
  233.                 PUT_CHAR(c, r);
  234.             }
  235.             p = 0;
  236.         }
  237.     }
  238. }
  239.  
  240. #undef FLUSH_BUF
  241. #undef PUT_CHAR
  242. #undef GET_CHAR
  243. #define GET_CHAR(f,c,r,p) \
  244.  { \
  245.    int i = getc(f); \
  246.    if (i == EOF) { /* either EOF or error -- needs error handling if latter */ \
  247.        if (ferror(f)) { \
  248.            fprintf(stderr, "encountered error in GET_CHAR macro, " \
  249.                    "mod_include.\n"); \
  250.        } \
  251.        ap_pfclose(p, f); \
  252.        return r; \
  253.    } \
  254.    c = (char)i; \
  255.  }
  256.  
  257. /*
  258.  * decodes a string containing html entities or numeric character references.
  259.  * 's' is overwritten with the decoded string.
  260.  * If 's' is syntatically incorrect, then the followed fixups will be made:
  261.  *   unknown entities will be left undecoded;
  262.  *   references to unused numeric characters will be deleted.
  263.  *   In particular, � will not be decoded, but will be deleted.
  264.  *
  265.  * drtr
  266.  */
  267.  
  268. /* maximum length of any ISO-LATIN-1 HTML entity name. */
  269. #define MAXENTLEN (6)
  270.  
  271. /* The following is a shrinking transformation, therefore safe. */
  272.  
  273. static void decodehtml(char *s)
  274. {
  275.     int val, i, j;
  276.     char *p = s;
  277.     const char *ents;
  278.     static const char * const entlist[MAXENTLEN + 1] =
  279.     {
  280.         NULL,                   /* 0 */
  281.         NULL,                   /* 1 */
  282.         "lt\074gt\076",         /* 2 */
  283.         "amp\046ETH\320eth\360",        /* 3 */
  284.         "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\
  285. iuml\357ouml\366uuml\374yuml\377",      /* 4 */
  286.         "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\
  287. THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\
  288. ucirc\373thorn\376",            /* 5 */
  289.         "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\
  290. Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\
  291. Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\
  292. egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\
  293. otilde\365oslash\370ugrave\371uacute\372yacute\375"     /* 6 */
  294.     };
  295.  
  296.     for (; *s != '\0'; s++, p++) {
  297.         if (*s != '&') {
  298.             *p = *s;
  299.             continue;
  300.         }
  301.         /* find end of entity */
  302.         for (i = 1; s[i] != ';' && s[i] != '\0'; i++) {
  303.             continue;
  304.         }
  305.  
  306.         if (s[i] == '\0') {     /* treat as normal data */
  307.             *p = *s;
  308.             continue;
  309.         }
  310.  
  311.         /* is it numeric ? */
  312.         if (s[1] == '#') {
  313.             for (j = 2, val = 0; j < i && ap_isdigit(s[j]); j++) {
  314.                 val = val * 10 + s[j] - '0';
  315.             }
  316.             s += i;
  317.             if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
  318.                 (val >= 127 && val <= 160) || val >= 256) {
  319.                 p--;            /* no data to output */
  320.             }
  321.             else {
  322.                 *p = RAW_ASCII_CHAR(val);
  323.             }
  324.         }
  325.         else {
  326.             j = i - 1;
  327.             if (j > MAXENTLEN || entlist[j] == NULL) {
  328.                 /* wrong length */
  329.                 *p = '&';
  330.                 continue;       /* skip it */
  331.             }
  332.             for (ents = entlist[j]; *ents != '\0'; ents += i) {
  333.                 if (strncmp(s + 1, ents, j) == 0) {
  334.                     break;
  335.                 }
  336.             }
  337.  
  338.             if (*ents == '\0') {
  339.                 *p = '&';       /* unknown */
  340.             }
  341.             else {
  342.                 *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]);
  343.                 s += i;
  344.             }
  345.         }
  346.     }
  347.  
  348.     *p = '\0';
  349. }
  350.  
  351. /*
  352.  * extract the next tag name and value.
  353.  * if there are no more tags, set the tag name to 'done'
  354.  * the tag value is html decoded if dodecode is non-zero
  355.  */
  356.  
  357. static char *get_tag(pool *p, FILE *in, char *tag, int tagbuf_len, int dodecode)
  358. {
  359.     char *t = tag, *tag_val, c, term;
  360.  
  361.     /* makes code below a little less cluttered */
  362.     --tagbuf_len;
  363.  
  364.     do {                        /* skip whitespace */
  365.         GET_CHAR(in, c, NULL, p);
  366.     } while (ap_isspace(c));
  367.  
  368.     /* tags can't start with - */
  369.     if (c == '-') {
  370.         GET_CHAR(in, c, NULL, p);
  371.         if (c == '-') {
  372.             do {
  373.                 GET_CHAR(in, c, NULL, p);
  374.             } while (ap_isspace(c));
  375.             if (c == '>') {
  376.                 ap_cpystrn(tag, "done", tagbuf_len);
  377.                 return tag;
  378.             }
  379.         }
  380.         return NULL;            /* failed */
  381.     }
  382.  
  383.     /* find end of tag name */
  384.     while (1) {
  385.         if (t - tag == tagbuf_len) {
  386.             *t = '\0';
  387.             return NULL;
  388.         }
  389.         if (c == '=' || ap_isspace(c)) {
  390.             break;
  391.         }
  392.         *(t++) = ap_tolower(c);
  393.         GET_CHAR(in, c, NULL, p);
  394.     }
  395.  
  396.     *t++ = '\0';
  397.     tag_val = t;
  398.  
  399.     while (ap_isspace(c)) {
  400.         GET_CHAR(in, c, NULL, p);       /* space before = */
  401.     }
  402.     if (c != '=') {
  403.         ungetc(c, in);
  404.         return NULL;
  405.     }
  406.  
  407.     do {
  408.         GET_CHAR(in, c, NULL, p);       /* space after = */
  409.     } while (ap_isspace(c));
  410.  
  411.     /* we should allow a 'name' as a value */
  412.  
  413.     if (c != '"' && c != '\'') {
  414.         return NULL;
  415.     }
  416.     term = c;
  417.     while (1) {
  418.         GET_CHAR(in, c, NULL, p);
  419.         if (t - tag == tagbuf_len) {
  420.             *t = '\0';
  421.             return NULL;
  422.         }
  423. /* Want to accept \" as a valid character within a string. */
  424.         if (c == '\\') {
  425.             *(t++) = c;         /* Add backslash */
  426.             GET_CHAR(in, c, NULL, p);
  427.             if (c == term) {    /* Only if */
  428.                 *(--t) = c;     /* Replace backslash ONLY for terminator */
  429.             }
  430.         }
  431.         else if (c == term) {
  432.             break;
  433.         }
  434.         *(t++) = c;
  435.     }
  436.     *t = '\0';
  437.     if (dodecode) {
  438.         decodehtml(tag_val);
  439.     }
  440.     return ap_pstrdup(p, tag_val);
  441. }
  442.  
  443. static int get_directive(FILE *in, char *dest, size_t len, pool *p)
  444. {
  445.     char *d = dest;
  446.     char c;
  447.  
  448.     /* make room for nul terminator */
  449.     --len;
  450.  
  451.     /* skip initial whitespace */
  452.     while (1) {
  453.         GET_CHAR(in, c, 1, p);
  454.         if (!ap_isspace(c)) {
  455.             break;
  456.         }
  457.     }
  458.     /* now get directive */
  459.     while (1) {
  460.     if (d - dest == len) {
  461.         return 1;
  462.     }
  463.         *d++ = ap_tolower(c);
  464.         GET_CHAR(in, c, 1, p);
  465.         if (ap_isspace(c)) {
  466.             break;
  467.         }
  468.     }
  469.     *d = '\0';
  470.     return 0;
  471. }
  472.  
  473. /*
  474.  * Do variable substitution on strings
  475.  */
  476. static void parse_string(request_rec *r, const char *in, char *out,
  477.             size_t length, int leave_name)
  478. {
  479.     char ch;
  480.     char *next = out;
  481.     char *end_out;
  482.  
  483.     /* leave room for nul terminator */
  484.     end_out = out + length - 1;
  485.  
  486.     while ((ch = *in++) != '\0') {
  487.         switch (ch) {
  488.         case '\\':
  489.         if (next == end_out) {
  490.         /* truncated */
  491.         *next = '\0';
  492.         return;
  493.         }
  494.             if (*in == '$') {
  495.                 *next++ = *in++;
  496.             }
  497.             else {
  498.                 *next++ = ch;
  499.             }
  500.             break;
  501.         case '$':
  502.             {
  503.         char var[MAX_STRING_LEN];
  504.         const char *start_of_var_name;
  505.         const char *end_of_var_name;    /* end of var name + 1 */
  506.         const char *expansion;
  507.         const char *val;
  508.         size_t l;
  509.  
  510.         /* guess that the expansion won't happen */
  511.         expansion = in - 1;
  512.         if (*in == '{') {
  513.             ++in;
  514.             start_of_var_name = in;
  515.             in = strchr(in, '}');
  516.             if (in == NULL) {
  517.                         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
  518.                     r, "Missing '}' on variable \"%s\"",
  519.                     expansion);
  520.                         *next = '\0';
  521.                         return;
  522.                     }
  523.             end_of_var_name = in;
  524.             ++in;
  525.         }
  526.         else {
  527.             start_of_var_name = in;
  528.             while (ap_isalnum(*in) || *in == '_') {
  529.             ++in;
  530.             }
  531.             end_of_var_name = in;
  532.         }
  533.         /* what a pain, too bad there's no table_getn where you can
  534.          * pass a non-nul terminated string */
  535.         l = end_of_var_name - start_of_var_name;
  536.         if (l != 0) {
  537.             l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l;
  538.             memcpy(var, start_of_var_name, l);
  539.             var[l] = '\0';
  540.  
  541.             val = ap_table_get(r->subprocess_env, var);
  542.             if (val) {
  543.             expansion = val;
  544.             l = strlen(expansion);
  545.             }
  546.             else if (leave_name) {
  547.             l = in - expansion;
  548.             }
  549.             else {
  550.             break;    /* no expansion to be done */
  551.             }
  552.         }
  553.         else {
  554.             /* zero-length variable name causes just the $ to be copied */
  555.             l = 1;
  556.         }
  557.         l = (l > end_out - next) ? (end_out - next) : l;
  558.         memcpy(next, expansion, l);
  559.         next += l;
  560.                 break;
  561.             }
  562.         default:
  563.         if (next == end_out) {
  564.         /* truncated */
  565.         *next = '\0';
  566.         return;
  567.         }
  568.             *next++ = ch;
  569.             break;
  570.         }
  571.     }
  572.     *next = '\0';
  573.     return;
  574. }
  575.  
  576. /* --------------------------- Action handlers ---------------------------- */
  577.  
  578. static int include_cgi(char *s, request_rec *r)
  579. {
  580.     request_rec *rr = ap_sub_req_lookup_uri(s, r);
  581.     int rr_status;
  582.  
  583.     if (rr->status != HTTP_OK) {
  584.         return -1;
  585.     }
  586.  
  587.     /* No hardwired path info or query allowed */
  588.  
  589.     if ((rr->path_info && rr->path_info[0]) || rr->args) {
  590.         return -1;
  591.     }
  592.     if (rr->finfo.st_mode == 0) {
  593.         return -1;
  594.     }
  595.  
  596.     /* Script gets parameters of the *document*, for back compatibility */
  597.  
  598.     rr->path_info = r->path_info;       /* hard to get right; see mod_cgi.c */
  599.     rr->args = r->args;
  600.  
  601.     /* Force sub_req to be treated as a CGI request, even if ordinary
  602.      * typing rules would have called it something else.
  603.      */
  604.  
  605.     rr->content_type = CGI_MAGIC_TYPE;
  606.  
  607.     /* Run it. */
  608.  
  609.     rr_status = ap_run_sub_req(rr);
  610.     if (ap_is_HTTP_REDIRECT(rr_status)) {
  611.         const char *location = ap_table_get(rr->headers_out, "Location");
  612.         location = ap_escape_html(rr->pool, location);
  613.         ap_rvputs(r, "<A HREF=\"", location, "\">", location, "</A>", NULL);
  614.     }
  615.  
  616.     ap_destroy_sub_req(rr);
  617. #ifndef WIN32
  618.     ap_chdir_file(r->filename);
  619. #endif
  620.  
  621.     return 0;
  622. }
  623.  
  624. /* ensure that path is relative, and does not contain ".." elements
  625.  * ensentially ensure that it does not match the regex:
  626.  * (^/|(^|/)\.\.(/|$))
  627.  * XXX: this needs os abstraction... consider c:..\foo in win32
  628.  */
  629. static int is_only_below(const char *path)
  630. {
  631. #if WIN32
  632.     if (path[1] == ':')
  633.     return 0;
  634. #endif
  635.     if (path[0] == '/') {
  636.     return 0;
  637.     }
  638.     if (path[0] == '.' && path[1] == '.'
  639.     && (path[2] == '\0' || path[2] == '/')) {
  640.     return 0;
  641.     }
  642.     while (*path) {
  643.     if (*path == '/' && path[1] == '.' && path[2] == '.'
  644.         && (path[3] == '\0' || path[3] == '/')) {
  645.         return 0;
  646.     }
  647.     ++path;
  648.     }
  649.     return 1;
  650. }
  651.  
  652. static int handle_include(FILE *in, request_rec *r, const char *error, int noexec)
  653. {
  654.     char tag[MAX_STRING_LEN];
  655.     char parsed_string[MAX_STRING_LEN];
  656.     char *tag_val;
  657.  
  658.     while (1) {
  659.         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
  660.             return 1;
  661.         }
  662.         if (!strcmp(tag, "file") || !strcmp(tag, "virtual")) {
  663.             request_rec *rr = NULL;
  664.             char *error_fmt = NULL;
  665.  
  666.             parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
  667.             if (tag[0] == 'f') {
  668.                 /* be safe; only files in this directory or below allowed */
  669.         if (!is_only_below(parsed_string)) {
  670.                     error_fmt = "unable to include file \"%s\" "
  671.                         "in parsed file %s";
  672.                 }
  673.                 else {
  674.                     rr = ap_sub_req_lookup_file(parsed_string, r);
  675.                 }
  676.             }
  677.             else {
  678.                 rr = ap_sub_req_lookup_uri(parsed_string, r);
  679.             }
  680.  
  681.             if (!error_fmt && rr->status != HTTP_OK) {
  682.                 error_fmt = "unable to include \"%s\" in parsed file %s";
  683.             }
  684.  
  685.             if (!error_fmt && noexec && rr->content_type
  686.                 && (strncmp(rr->content_type, "text/", 5))) {
  687.                 error_fmt = "unable to include potential exec \"%s\" "
  688.                     "in parsed file %s";
  689.             }
  690.             if (error_fmt == NULL) {
  691.         /* try to avoid recursive includes.  We do this by walking
  692.          * up the r->main list of subrequests, and at each level
  693.          * walking back through any internal redirects.  At each
  694.          * step, we compare the filenames and the URIs.  
  695.          *
  696.          * The filename comparison catches a recursive include
  697.          * with an ever-changing URL, eg.
  698.          * <!--#include virtual=
  699.          *      "$REQUEST_URI/$QUERY_STRING?$QUERY_STRING/x"-->
  700.          * which, although they would eventually be caught because
  701.          * we have a limit on the length of files, etc., can 
  702.          * recurse for a while.
  703.          *
  704.          * The URI comparison catches the case where the filename
  705.          * is changed while processing the request, so the 
  706.          * current name is never the same as any previous one.
  707.          * This can happen with "DocumentRoot /foo" when you
  708.          * request "/" on the server and it includes "/".
  709.          * This only applies to modules such as mod_dir that 
  710.          * (somewhat improperly) mess with r->filename outside 
  711.          * of a filename translation phase.
  712.          */
  713.         int founddupe = 0;
  714.                 request_rec *p;
  715.                 for (p = r; p != NULL && !founddupe; p = p->main) {
  716.             request_rec *q;
  717.             for (q = p; q != NULL; q = q->prev) {
  718.             if ( (strcmp(q->filename, rr->filename) == 0) ||
  719.                  (strcmp(q->uri, rr->uri) == 0) ){
  720.                 founddupe = 1;
  721.                 break;
  722.             }
  723.             }
  724.         }
  725.  
  726.                 if (p != NULL) {
  727.                     error_fmt = "Recursive include of \"%s\" "
  728.                         "in parsed file %s";
  729.                 }
  730.             }
  731.  
  732.         /* see the Kludge in send_parsed_file for why */
  733.         if (rr) 
  734.         ap_set_module_config(rr->request_config, &includes_module, r);
  735.  
  736.             if (!error_fmt && ap_run_sub_req(rr)) {
  737.                 error_fmt = "unable to include \"%s\" in parsed file %s";
  738.             }
  739. #ifndef WIN32
  740.             ap_chdir_file(r->filename);
  741. #endif
  742.             if (error_fmt) {
  743.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
  744.                 r, error_fmt, tag_val, r->filename);
  745.                 ap_rputs(error, r);
  746.             }
  747.  
  748.         /* destroy the sub request if it's not a nested include */
  749.             if (rr != NULL
  750.         && ap_get_module_config(rr->request_config, &includes_module)
  751.             != NESTED_INCLUDE_MAGIC) {
  752.         ap_destroy_sub_req(rr);
  753.             }
  754.         }
  755.         else if (!strcmp(tag, "done")) {
  756.             return 0;
  757.         }
  758.         else {
  759.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  760.                         "unknown parameter \"%s\" to tag include in %s",
  761.                         tag, r->filename);
  762.             ap_rputs(error, r);
  763.         }
  764.     }
  765. }
  766.  
  767. typedef struct {
  768.     request_rec *r;
  769.     char *s;
  770. } include_cmd_arg;
  771.  
  772. static int include_cmd_child(void *arg, child_info *pinfo)
  773. {
  774.     request_rec *r = ((include_cmd_arg *) arg)->r;
  775.     char *s = ((include_cmd_arg *) arg)->s;
  776.     table *env = r->subprocess_env;
  777.     int child_pid = 0;
  778. #ifdef DEBUG_INCLUDE_CMD
  779. #ifdef OS2
  780.     /* under OS/2 /dev/tty is referenced as con */
  781.     FILE *dbg = fopen("con", "w");
  782. #else
  783.     FILE *dbg = fopen("/dev/tty", "w");
  784. #endif
  785. #endif
  786. #ifndef WIN32
  787.     char err_string[MAX_STRING_LEN];
  788. #endif
  789.  
  790. #ifdef DEBUG_INCLUDE_CMD
  791.     fprintf(dbg, "Attempting to include command '%s'\n", s);
  792. #endif
  793.  
  794.     if (r->path_info && r->path_info[0] != '\0') {
  795.         request_rec *pa_req;
  796.  
  797.         ap_table_setn(env, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info));
  798.  
  799.         pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r);
  800.         if (pa_req->filename) {
  801.             ap_table_setn(env, "PATH_TRANSLATED",
  802.                       ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info,
  803.                               NULL));
  804.         }
  805.     }
  806.  
  807.     if (r->args) {
  808.         char *arg_copy = ap_pstrdup(r->pool, r->args);
  809.  
  810.         ap_table_setn(env, "QUERY_STRING", r->args);
  811.         ap_unescape_url(arg_copy);
  812.         ap_table_setn(env, "QUERY_STRING_UNESCAPED",
  813.                   ap_escape_shell_cmd(r->pool, arg_copy));
  814.     }
  815.  
  816.     ap_error_log2stderr(r->server);
  817.  
  818. #ifdef DEBUG_INCLUDE_CMD
  819.     fprintf(dbg, "Attempting to exec '%s'\n", s);
  820. #endif
  821.     ap_cleanup_for_exec();
  822.     /* set shellcmd flag to pass arg to SHELL_PATH */
  823.     child_pid = ap_call_exec(r, pinfo, s, ap_create_environment(r->pool, env),
  824.                  1);
  825. #ifdef WIN32
  826.     return (child_pid);
  827. #else
  828.     /* Oh, drat.  We're still here.  The log file descriptors are closed,
  829.      * so we have to whimper a complaint onto stderr...
  830.      */
  831.  
  832. #ifdef DEBUG_INCLUDE_CMD
  833.     fprintf(dbg, "Exec failed\n");
  834. #endif
  835.     ap_snprintf(err_string, sizeof(err_string),
  836.                 "exec of %s failed, reason: %s (errno = %d)\n",
  837.                 SHELL_PATH, strerror(errno), errno);
  838.     write(STDERR_FILENO, err_string, strlen(err_string));
  839.     exit(0);
  840.     /* NOT REACHED */
  841.     return (child_pid);
  842. #endif /* WIN32 */
  843. }
  844.  
  845. static int include_cmd(char *s, request_rec *r)
  846. {
  847.     include_cmd_arg arg;
  848.     BUFF *script_in;
  849.  
  850.     arg.r = r;
  851.     arg.s = s;
  852.  
  853.     if (!ap_bspawn_child(r->pool, include_cmd_child, &arg,
  854.              kill_after_timeout, NULL, &script_in, NULL)) {
  855.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  856.              "couldn't spawn include command");
  857.         return -1;
  858.     }
  859.  
  860.     ap_send_fb(script_in, r);
  861.     ap_bclose(script_in);
  862.     return 0;
  863. }
  864.  
  865. static int handle_exec(FILE *in, request_rec *r, const char *error)
  866. {
  867.     char tag[MAX_STRING_LEN];
  868.     char *tag_val;
  869.     char *file = r->filename;
  870.     char parsed_string[MAX_STRING_LEN];
  871.  
  872.     while (1) {
  873.         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
  874.             return 1;
  875.         }
  876.         if (!strcmp(tag, "cmd")) {
  877.             parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1);
  878.             if (include_cmd(parsed_string, r) == -1) {
  879.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  880.                             "execution failure for parameter \"%s\" "
  881.                             "to tag exec in file %s",
  882.                             tag, r->filename);
  883.                 ap_rputs(error, r);
  884.             }
  885.             /* just in case some stooge changed directories */
  886. #ifndef WIN32
  887.             ap_chdir_file(r->filename);
  888. #endif
  889.         }
  890.         else if (!strcmp(tag, "cgi")) {
  891.             parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
  892.             if (include_cgi(parsed_string, r) == -1) {
  893.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  894.                             "invalid CGI ref \"%s\" in %s", tag_val, file);
  895.                 ap_rputs(error, r);
  896.             }
  897.             /* grumble groan */
  898. #ifndef WIN32
  899.             ap_chdir_file(r->filename);
  900. #endif
  901.         }
  902.         else if (!strcmp(tag, "done")) {
  903.             return 0;
  904.         }
  905.         else {
  906.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  907.                         "unknown parameter \"%s\" to tag exec in %s",
  908.                         tag, file);
  909.             ap_rputs(error, r);
  910.         }
  911.     }
  912.  
  913. }
  914.  
  915. static int handle_echo(FILE *in, request_rec *r, const char *error)
  916. {
  917.     char tag[MAX_STRING_LEN];
  918.     char *tag_val;
  919.  
  920.     while (1) {
  921.         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
  922.             return 1;
  923.         }
  924.         if (!strcmp(tag, "var")) {
  925.             const char *val = ap_table_get(r->subprocess_env, tag_val);
  926.  
  927.             if (val) {
  928.                 ap_rputs(val, r);
  929.             }
  930.             else {
  931.                 ap_rputs("(none)", r);
  932.             }
  933.         }
  934.         else if (!strcmp(tag, "done")) {
  935.             return 0;
  936.         }
  937.         else {
  938.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  939.                         "unknown parameter \"%s\" to tag echo in %s",
  940.                         tag, r->filename);
  941.             ap_rputs(error, r);
  942.         }
  943.     }
  944. }
  945.  
  946. #ifdef USE_PERL_SSI
  947. static int handle_perl(FILE *in, request_rec *r, const char *error)
  948. {
  949.     char tag[MAX_STRING_LEN];
  950.     char parsed_string[MAX_STRING_LEN];
  951.     char *tag_val;
  952.     SV *sub = Nullsv;
  953.     AV *av = newAV();
  954.  
  955.     if (ap_allow_options(r) & OPT_INCNOEXEC) {
  956.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  957.               "#perl SSI disallowed by IncludesNoExec in %s",
  958.               r->filename);
  959.         return DECLINED;
  960.     }
  961.     while (1) {
  962.         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
  963.             break;
  964.         }
  965.         if (strnEQ(tag, "sub", 3)) {
  966.             sub = newSVpv(tag_val, 0);
  967.         }
  968.         else if (strnEQ(tag, "arg", 3)) {
  969.             parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
  970.             av_push(av, newSVpv(parsed_string, 0));
  971.         }
  972.         else if (strnEQ(tag, "done", 4)) {
  973.             break;
  974.         }
  975.     }
  976.     perl_stdout2client(r);
  977.     perl_setup_env(r);
  978.     perl_call_handler(sub, r, av);
  979.     return OK;
  980. }
  981. #endif
  982.  
  983. /* error and tf must point to a string with room for at 
  984.  * least MAX_STRING_LEN characters 
  985.  */
  986. static int handle_config(FILE *in, request_rec *r, char *error, char *tf,
  987.                          int *sizefmt)
  988. {
  989.     char tag[MAX_STRING_LEN];
  990.     char *tag_val;
  991.     char parsed_string[MAX_STRING_LEN];
  992.     table *env = r->subprocess_env;
  993.  
  994.     while (1) {
  995.         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0))) {
  996.             return 1;
  997.         }
  998.         if (!strcmp(tag, "errmsg")) {
  999.             parse_string(r, tag_val, error, MAX_STRING_LEN, 0);
  1000.         }
  1001.         else if (!strcmp(tag, "timefmt")) {
  1002.             time_t date = r->request_time;
  1003.  
  1004.             parse_string(r, tag_val, tf, MAX_STRING_LEN, 0);
  1005.             ap_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, tf, 0));
  1006.             ap_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, tf, 1));
  1007.             ap_table_setn(env, "LAST_MODIFIED",
  1008.                       ap_ht_time(r->pool, r->finfo.st_mtime, tf, 0));
  1009.         }
  1010.         else if (!strcmp(tag, "sizefmt")) {
  1011.             parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
  1012.             decodehtml(parsed_string);
  1013.             if (!strcmp(parsed_string, "bytes")) {
  1014.                 *sizefmt = SIZEFMT_BYTES;
  1015.             }
  1016.             else if (!strcmp(parsed_string, "abbrev")) {
  1017.                 *sizefmt = SIZEFMT_KMG;
  1018.             }
  1019.         }
  1020.         else if (!strcmp(tag, "done")) {
  1021.             return 0;
  1022.         }
  1023.         else {
  1024.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1025.                         "unknown parameter \"%s\" to tag config in %s",
  1026.                         tag, r->filename);
  1027.             ap_rputs(error, r);
  1028.         }
  1029.     }
  1030. }
  1031.  
  1032.  
  1033. static int find_file(request_rec *r, const char *directive, const char *tag,
  1034.                      char *tag_val, struct stat *finfo, const char *error)
  1035. {
  1036.     char *to_send;
  1037.     request_rec *rr;
  1038.     int ret=0;
  1039.  
  1040.     if (!strcmp(tag, "file")) {
  1041.         ap_getparents(tag_val);    /* get rid of any nasties */
  1042.         
  1043.         rr = ap_sub_req_lookup_file(tag_val, r);
  1044.  
  1045.         if (rr->status == HTTP_OK && rr->finfo.st_mode != 0) {
  1046.             to_send = rr->filename;
  1047.             if ((ret = stat(to_send, finfo)) == -1) {
  1048.                 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  1049.                             "unable to get information about \"%s\" "
  1050.                             "in parsed file %s",
  1051.                             to_send, r->filename);
  1052.                 ap_rputs(error, r);
  1053.             }
  1054.         }
  1055.         else {
  1056.             ret = -1;
  1057.             ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  1058.                         "unable to lookup information about \"%s\" "
  1059.                         "in parsed file %s",
  1060.                         tag_val, r->filename);
  1061.             ap_rputs(error, r);
  1062.         }
  1063.         
  1064.         ap_destroy_sub_req(rr);
  1065.         
  1066.         return ret;
  1067.     }
  1068.     else if (!strcmp(tag, "virtual")) {
  1069.         rr = ap_sub_req_lookup_uri(tag_val, r);
  1070.  
  1071.         if (rr->status == HTTP_OK && rr->finfo.st_mode != 0) {
  1072.             memcpy((char *) finfo, (const char *) &rr->finfo,
  1073.                    sizeof(struct stat));
  1074.             ap_destroy_sub_req(rr);
  1075.             return 0;
  1076.         }
  1077.         else {
  1078.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1079.                         "unable to get information about \"%s\" "
  1080.                         "in parsed file %s",
  1081.                         tag_val, r->filename);
  1082.             ap_rputs(error, r);
  1083.             ap_destroy_sub_req(rr);
  1084.             return -1;
  1085.         }
  1086.     }
  1087.     else {
  1088.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1089.                     "unknown parameter \"%s\" to tag %s in %s",
  1090.                     tag, directive, r->filename);
  1091.         ap_rputs(error, r);
  1092.         return -1;
  1093.     }
  1094. }
  1095.  
  1096.  
  1097. static int handle_fsize(FILE *in, request_rec *r, const char *error, int sizefmt)
  1098. {
  1099.     char tag[MAX_STRING_LEN];
  1100.     char *tag_val;
  1101.     struct stat finfo;
  1102.     char parsed_string[MAX_STRING_LEN];
  1103.  
  1104.     while (1) {
  1105.         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
  1106.             return 1;
  1107.         }
  1108.         else if (!strcmp(tag, "done")) {
  1109.             return 0;
  1110.         }
  1111.         else {
  1112.             parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
  1113.             if (!find_file(r, "fsize", tag, parsed_string, &finfo, error)) {
  1114.                 if (sizefmt == SIZEFMT_KMG) {
  1115.                     ap_send_size(finfo.st_size, r);
  1116.                 }
  1117.                 else {
  1118.                     int l, x;
  1119. #if defined(BSD) && BSD > 199305
  1120.                     /* ap_snprintf can't handle %qd */
  1121.                     sprintf(tag, "%qd", finfo.st_size);
  1122. #else
  1123.                     ap_snprintf(tag, sizeof(tag), "%ld", finfo.st_size);
  1124. #endif
  1125.                     l = strlen(tag);    /* grrr */
  1126.                     for (x = 0; x < l; x++) {
  1127.                         if (x && (!((l - x) % 3))) {
  1128.                             ap_rputc(',', r);
  1129.                         }
  1130.                         ap_rputc(tag[x], r);
  1131.                     }
  1132.                 }
  1133.             }
  1134.         }
  1135.     }
  1136. }
  1137.  
  1138. static int handle_flastmod(FILE *in, request_rec *r, const char *error, const char *tf)
  1139. {
  1140.     char tag[MAX_STRING_LEN];
  1141.     char *tag_val;
  1142.     struct stat finfo;
  1143.     char parsed_string[MAX_STRING_LEN];
  1144.  
  1145.     while (1) {
  1146.         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
  1147.             return 1;
  1148.         }
  1149.         else if (!strcmp(tag, "done")) {
  1150.             return 0;
  1151.         }
  1152.         else {
  1153.             parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
  1154.             if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) {
  1155.                 ap_rputs(ap_ht_time(r->pool, finfo.st_mtime, tf, 0), r);
  1156.             }
  1157.         }
  1158.     }
  1159. }
  1160.  
  1161. static int re_check(request_rec *r, char *string, char *rexp)
  1162. {
  1163.     regex_t *compiled;
  1164.     int regex_error;
  1165.  
  1166.     compiled = ap_pregcomp(r->pool, rexp, REG_EXTENDED | REG_NOSUB);
  1167.     if (compiled == NULL) {
  1168.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1169.                     "unable to compile pattern \"%s\"", rexp);
  1170.         return -1;
  1171.     }
  1172.     regex_error = regexec(compiled, string, 0, (regmatch_t *) NULL, 0);
  1173.     ap_pregfree(r->pool, compiled);
  1174.     return (!regex_error);
  1175. }
  1176.  
  1177. enum token_type {
  1178.     token_string,
  1179.     token_and, token_or, token_not, token_eq, token_ne,
  1180.     token_rbrace, token_lbrace, token_group,
  1181.     token_ge, token_le, token_gt, token_lt
  1182. };
  1183. struct token {
  1184.     enum token_type type;
  1185.     char value[MAX_STRING_LEN];
  1186. };
  1187.  
  1188. /* there is an implicit assumption here that string is at most MAX_STRING_LEN-1
  1189.  * characters long...
  1190.  */
  1191. static const char *get_ptoken(request_rec *r, const char *string, struct token *token)
  1192. {
  1193.     char ch;
  1194.     int next = 0;
  1195.     int qs = 0;
  1196.  
  1197.     /* Skip leading white space */
  1198.     if (string == (char *) NULL) {
  1199.         return (char *) NULL;
  1200.     }
  1201.     while ((ch = *string++)) {
  1202.         if (!ap_isspace(ch)) {
  1203.             break;
  1204.         }
  1205.     }
  1206.     if (ch == '\0') {
  1207.         return (char *) NULL;
  1208.     }
  1209.  
  1210.     token->type = token_string; /* the default type */
  1211.     switch (ch) {
  1212.     case '(':
  1213.         token->type = token_lbrace;
  1214.         return (string);
  1215.     case ')':
  1216.         token->type = token_rbrace;
  1217.         return (string);
  1218.     case '=':
  1219.         token->type = token_eq;
  1220.         return (string);
  1221.     case '!':
  1222.         if (*string == '=') {
  1223.             token->type = token_ne;
  1224.             return (string + 1);
  1225.         }
  1226.         else {
  1227.             token->type = token_not;
  1228.             return (string);
  1229.         }
  1230.     case '\'':
  1231.         token->type = token_string;
  1232.         qs = 1;
  1233.         break;
  1234.     case '|':
  1235.         if (*string == '|') {
  1236.             token->type = token_or;
  1237.             return (string + 1);
  1238.         }
  1239.         break;
  1240.     case '&':
  1241.         if (*string == '&') {
  1242.             token->type = token_and;
  1243.             return (string + 1);
  1244.         }
  1245.         break;
  1246.     case '>':
  1247.         if (*string == '=') {
  1248.             token->type = token_ge;
  1249.             return (string + 1);
  1250.         }
  1251.         else {
  1252.             token->type = token_gt;
  1253.             return (string);
  1254.         }
  1255.     case '<':
  1256.         if (*string == '=') {
  1257.             token->type = token_le;
  1258.             return (string + 1);
  1259.         }
  1260.         else {
  1261.             token->type = token_lt;
  1262.             return (string);
  1263.         }
  1264.     default:
  1265.         token->type = token_string;
  1266.         break;
  1267.     }
  1268.     /* We should only be here if we are in a string */
  1269.     if (!qs) {
  1270.         token->value[next++] = ch;
  1271.     }
  1272.  
  1273.     /* 
  1274.      * Yes I know that goto's are BAD.  But, c doesn't allow me to
  1275.      * exit a loop from a switch statement.  Yes, I could use a flag,
  1276.      * but that is (IMHO) even less readable/maintainable than the goto.
  1277.      */
  1278.     /* 
  1279.      * I used the ++string throughout this section so that string
  1280.      * ends up pointing to the next token and I can just return it
  1281.      */
  1282.     for (ch = *string; ch != '\0'; ch = *++string) {
  1283.         if (ch == '\\') {
  1284.             if ((ch = *++string) == '\0') {
  1285.                 goto TOKEN_DONE;
  1286.             }
  1287.             token->value[next++] = ch;
  1288.             continue;
  1289.         }
  1290.         if (!qs) {
  1291.             if (ap_isspace(ch)) {
  1292.                 goto TOKEN_DONE;
  1293.             }
  1294.             switch (ch) {
  1295.             case '(':
  1296.                 goto TOKEN_DONE;
  1297.             case ')':
  1298.                 goto TOKEN_DONE;
  1299.             case '=':
  1300.                 goto TOKEN_DONE;
  1301.             case '!':
  1302.                 goto TOKEN_DONE;
  1303.             case '|':
  1304.                 if (*(string + 1) == '|') {
  1305.                     goto TOKEN_DONE;
  1306.                 }
  1307.                 break;
  1308.             case '&':
  1309.                 if (*(string + 1) == '&') {
  1310.                     goto TOKEN_DONE;
  1311.                 }
  1312.                 break;
  1313.             case '<':
  1314.                 goto TOKEN_DONE;
  1315.             case '>':
  1316.                 goto TOKEN_DONE;
  1317.             }
  1318.             token->value[next++] = ch;
  1319.         }
  1320.         else {
  1321.             if (ch == '\'') {
  1322.                 qs = 0;
  1323.                 ++string;
  1324.                 goto TOKEN_DONE;
  1325.             }
  1326.             token->value[next++] = ch;
  1327.         }
  1328.     }
  1329.   TOKEN_DONE:
  1330.     /* If qs is still set, I have an unmatched ' */
  1331.     if (qs) {
  1332.         ap_rputs("\nUnmatched '\n", r);
  1333.         next = 0;
  1334.     }
  1335.     token->value[next] = '\0';
  1336.     return (string);
  1337. }
  1338.  
  1339.  
  1340. /*
  1341.  * Hey I still know that goto's are BAD.  I don't think that I've ever
  1342.  * used two in the same project, let alone the same file before.  But,
  1343.  * I absolutely want to make sure that I clean up the memory in all
  1344.  * cases.  And, without rewriting this completely, the easiest way
  1345.  * is to just branch to the return code which cleans it up.
  1346.  */
  1347. /* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1
  1348.  * characters long...
  1349.  */
  1350. static int parse_expr(request_rec *r, const char *expr, const char *error)
  1351. {
  1352.     struct parse_node {
  1353.         struct parse_node *left, *right, *parent;
  1354.         struct token token;
  1355.         int value, done;
  1356.     }         *root, *current, *new;
  1357.     const char *parse;
  1358.     char buffer[MAX_STRING_LEN];
  1359.     pool *expr_pool;
  1360.     int retval = 0;
  1361.  
  1362.     if ((parse = expr) == (char *) NULL) {
  1363.         return (0);
  1364.     }
  1365.     root = current = (struct parse_node *) NULL;
  1366.     expr_pool = ap_make_sub_pool(r->pool);
  1367.  
  1368.     /* Create Parse Tree */
  1369.     while (1) {
  1370.         new = (struct parse_node *) ap_palloc(expr_pool,
  1371.                                            sizeof(struct parse_node));
  1372.         new->parent = new->left = new->right = (struct parse_node *) NULL;
  1373.         new->done = 0;
  1374.         if ((parse = get_ptoken(r, parse, &new->token)) == (char *) NULL) {
  1375.             break;
  1376.         }
  1377.         switch (new->token.type) {
  1378.  
  1379.         case token_string:
  1380. #ifdef DEBUG_INCLUDE
  1381.             ap_rvputs(r, "     Token: string (", new->token.value, ")\n", NULL);
  1382. #endif
  1383.             if (current == (struct parse_node *) NULL) {
  1384.                 root = current = new;
  1385.                 break;
  1386.             }
  1387.             switch (current->token.type) {
  1388.             case token_string:
  1389.                 if (current->token.value[0] != '\0') {
  1390.                     strncat(current->token.value, " ",
  1391.                          sizeof(current->token.value)
  1392.                 - strlen(current->token.value) - 1);
  1393.                 }
  1394.                 strncat(current->token.value, new->token.value,
  1395.                          sizeof(current->token.value)
  1396.                 - strlen(current->token.value) - 1);
  1397.         current->token.value[sizeof(current->token.value) - 1] = '\0';
  1398.                 break;
  1399.             case token_eq:
  1400.             case token_ne:
  1401.             case token_and:
  1402.             case token_or:
  1403.             case token_lbrace:
  1404.             case token_not:
  1405.             case token_ge:
  1406.             case token_gt:
  1407.             case token_le:
  1408.             case token_lt:
  1409.                 new->parent = current;
  1410.                 current = current->right = new;
  1411.                 break;
  1412.             default:
  1413.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1414.                             "Invalid expression \"%s\" in file %s",
  1415.                             expr, r->filename);
  1416.                 ap_rputs(error, r);
  1417.                 goto RETURN;
  1418.             }
  1419.             break;
  1420.  
  1421.         case token_and:
  1422.         case token_or:
  1423. #ifdef DEBUG_INCLUDE
  1424.             ap_rputs("     Token: and/or\n", r);
  1425. #endif
  1426.             if (current == (struct parse_node *) NULL) {
  1427.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1428.                             "Invalid expression \"%s\" in file %s",
  1429.                             expr, r->filename);
  1430.                 ap_rputs(error, r);
  1431.                 goto RETURN;
  1432.             }
  1433.             /* Percolate upwards */
  1434.             while (current != (struct parse_node *) NULL) {
  1435.                 switch (current->token.type) {
  1436.                 case token_string:
  1437.                 case token_group:
  1438.                 case token_not:
  1439.                 case token_eq:
  1440.                 case token_ne:
  1441.                 case token_and:
  1442.                 case token_or:
  1443.                 case token_ge:
  1444.                 case token_gt:
  1445.                 case token_le:
  1446.                 case token_lt:
  1447.                     current = current->parent;
  1448.                     continue;
  1449.                 case token_lbrace:
  1450.                     break;
  1451.                 default:
  1452.                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1453.                                 "Invalid expression \"%s\" in file %s",
  1454.                                 expr, r->filename);
  1455.                     ap_rputs(error, r);
  1456.                     goto RETURN;
  1457.                 }
  1458.                 break;
  1459.             }
  1460.             if (current == (struct parse_node *) NULL) {
  1461.                 new->left = root;
  1462.                 new->left->parent = new;
  1463.                 new->parent = (struct parse_node *) NULL;
  1464.                 root = new;
  1465.             }
  1466.             else {
  1467.                 new->left = current->right;
  1468.                 current->right = new;
  1469.                 new->parent = current;
  1470.             }
  1471.             current = new;
  1472.             break;
  1473.  
  1474.         case token_not:
  1475. #ifdef DEBUG_INCLUDE
  1476.             ap_rputs("     Token: not\n", r);
  1477. #endif
  1478.             if (current == (struct parse_node *) NULL) {
  1479.                 root = current = new;
  1480.                 break;
  1481.             }
  1482.             /* Percolate upwards */
  1483.             while (current != (struct parse_node *) NULL) {
  1484.                 switch (current->token.type) {
  1485.                 case token_not:
  1486.                 case token_eq:
  1487.                 case token_ne:
  1488.                 case token_and:
  1489.                 case token_or:
  1490.                 case token_lbrace:
  1491.                 case token_ge:
  1492.                 case token_gt:
  1493.                 case token_le:
  1494.                 case token_lt:
  1495.                     break;
  1496.                 default:
  1497.                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1498.                                 "Invalid expression \"%s\" in file %s",
  1499.                                 expr, r->filename);
  1500.                     ap_rputs(error, r);
  1501.                     goto RETURN;
  1502.                 }
  1503.                 break;
  1504.             }
  1505.             if (current == (struct parse_node *) NULL) {
  1506.                 new->left = root;
  1507.                 new->left->parent = new;
  1508.                 new->parent = (struct parse_node *) NULL;
  1509.                 root = new;
  1510.             }
  1511.             else {
  1512.                 new->left = current->right;
  1513.                 current->right = new;
  1514.                 new->parent = current;
  1515.             }
  1516.             current = new;
  1517.             break;
  1518.  
  1519.         case token_eq:
  1520.         case token_ne:
  1521.         case token_ge:
  1522.         case token_gt:
  1523.         case token_le:
  1524.         case token_lt:
  1525. #ifdef DEBUG_INCLUDE
  1526.             ap_rputs("     Token: eq/ne/ge/gt/le/lt\n", r);
  1527. #endif
  1528.             if (current == (struct parse_node *) NULL) {
  1529.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1530.                             "Invalid expression \"%s\" in file %s",
  1531.                             expr, r->filename);
  1532.                 ap_rputs(error, r);
  1533.                 goto RETURN;
  1534.             }
  1535.             /* Percolate upwards */
  1536.             while (current != (struct parse_node *) NULL) {
  1537.                 switch (current->token.type) {
  1538.                 case token_string:
  1539.                 case token_group:
  1540.                     current = current->parent;
  1541.                     continue;
  1542.                 case token_lbrace:
  1543.                 case token_and:
  1544.                 case token_or:
  1545.                     break;
  1546.                 case token_not:
  1547.                 case token_eq:
  1548.                 case token_ne:
  1549.                 case token_ge:
  1550.                 case token_gt:
  1551.                 case token_le:
  1552.                 case token_lt:
  1553.                 default:
  1554.                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1555.                                 "Invalid expression \"%s\" in file %s",
  1556.                                 expr, r->filename);
  1557.                     ap_rputs(error, r);
  1558.                     goto RETURN;
  1559.                 }
  1560.                 break;
  1561.             }
  1562.             if (current == (struct parse_node *) NULL) {
  1563.                 new->left = root;
  1564.                 new->left->parent = new;
  1565.                 new->parent = (struct parse_node *) NULL;
  1566.                 root = new;
  1567.             }
  1568.             else {
  1569.                 new->left = current->right;
  1570.                 current->right = new;
  1571.                 new->parent = current;
  1572.             }
  1573.             current = new;
  1574.             break;
  1575.  
  1576.         case token_rbrace:
  1577. #ifdef DEBUG_INCLUDE
  1578.             ap_rputs("     Token: rbrace\n", r);
  1579. #endif
  1580.             while (current != (struct parse_node *) NULL) {
  1581.                 if (current->token.type == token_lbrace) {
  1582.                     current->token.type = token_group;
  1583.                     break;
  1584.                 }
  1585.                 current = current->parent;
  1586.             }
  1587.             if (current == (struct parse_node *) NULL) {
  1588.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1589.                             "Unmatched ')' in \"%s\" in file %s",
  1590.                 expr, r->filename);
  1591.                 ap_rputs(error, r);
  1592.                 goto RETURN;
  1593.             }
  1594.             break;
  1595.  
  1596.         case token_lbrace:
  1597. #ifdef DEBUG_INCLUDE
  1598.             ap_rputs("     Token: lbrace\n", r);
  1599. #endif
  1600.             if (current == (struct parse_node *) NULL) {
  1601.                 root = current = new;
  1602.                 break;
  1603.             }
  1604.             /* Percolate upwards */
  1605.             while (current != (struct parse_node *) NULL) {
  1606.                 switch (current->token.type) {
  1607.                 case token_not:
  1608.                 case token_eq:
  1609.                 case token_ne:
  1610.                 case token_and:
  1611.                 case token_or:
  1612.                 case token_lbrace:
  1613.                 case token_ge:
  1614.                 case token_gt:
  1615.                 case token_le:
  1616.                 case token_lt:
  1617.                     break;
  1618.                 case token_string:
  1619.                 case token_group:
  1620.                 default:
  1621.                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1622.                                 "Invalid expression \"%s\" in file %s",
  1623.                                 expr, r->filename);
  1624.                     ap_rputs(error, r);
  1625.                     goto RETURN;
  1626.                 }
  1627.                 break;
  1628.             }
  1629.             if (current == (struct parse_node *) NULL) {
  1630.                 new->left = root;
  1631.                 new->left->parent = new;
  1632.                 new->parent = (struct parse_node *) NULL;
  1633.                 root = new;
  1634.             }
  1635.             else {
  1636.                 new->left = current->right;
  1637.                 current->right = new;
  1638.                 new->parent = current;
  1639.             }
  1640.             current = new;
  1641.             break;
  1642.         default:
  1643.             break;
  1644.         }
  1645.     }
  1646.  
  1647.     /* Evaluate Parse Tree */
  1648.     current = root;
  1649.     while (current != (struct parse_node *) NULL) {
  1650.         switch (current->token.type) {
  1651.         case token_string:
  1652. #ifdef DEBUG_INCLUDE
  1653.             ap_rputs("     Evaluate string\n", r);
  1654. #endif
  1655.             parse_string(r, current->token.value, buffer, sizeof(buffer), 0);
  1656.         ap_cpystrn(current->token.value, buffer, sizeof(current->token.value));
  1657.             current->value = (current->token.value[0] != '\0');
  1658.             current->done = 1;
  1659.             current = current->parent;
  1660.             break;
  1661.  
  1662.         case token_and:
  1663.         case token_or:
  1664. #ifdef DEBUG_INCLUDE
  1665.             ap_rputs("     Evaluate and/or\n", r);
  1666. #endif
  1667.             if (current->left == (struct parse_node *) NULL ||
  1668.                 current->right == (struct parse_node *) NULL) {
  1669.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1670.                             "Invalid expression \"%s\" in file %s",
  1671.                             expr, r->filename);
  1672.                 ap_rputs(error, r);
  1673.                 goto RETURN;
  1674.             }
  1675.             if (!current->left->done) {
  1676.                 switch (current->left->token.type) {
  1677.                 case token_string:
  1678.                     parse_string(r, current->left->token.value,
  1679.                                  buffer, sizeof(buffer), 0);
  1680.                     ap_cpystrn(current->left->token.value, buffer,
  1681.                             sizeof(current->left->token.value));
  1682.             current->left->value = (current->left->token.value[0] != '\0');
  1683.                     current->left->done = 1;
  1684.                     break;
  1685.                 default:
  1686.                     current = current->left;
  1687.                     continue;
  1688.                 }
  1689.             }
  1690.             if (!current->right->done) {
  1691.                 switch (current->right->token.type) {
  1692.                 case token_string:
  1693.                     parse_string(r, current->right->token.value,
  1694.                                  buffer, sizeof(buffer), 0);
  1695.                     ap_cpystrn(current->right->token.value, buffer,
  1696.                             sizeof(current->right->token.value));
  1697.             current->right->value = (current->right->token.value[0] != '\0');
  1698.                     current->right->done = 1;
  1699.                     break;
  1700.                 default:
  1701.                     current = current->right;
  1702.                     continue;
  1703.                 }
  1704.             }
  1705. #ifdef DEBUG_INCLUDE
  1706.             ap_rvputs(r, "     Left: ", current->left->value ? "1" : "0",
  1707.                    "\n", NULL);
  1708.             ap_rvputs(r, "     Right: ", current->right->value ? "1" : "0",
  1709.                    "\n", NULL);
  1710. #endif
  1711.             if (current->token.type == token_and) {
  1712.                 current->value = current->left->value && current->right->value;
  1713.             }
  1714.             else {
  1715.                 current->value = current->left->value || current->right->value;
  1716.             }
  1717. #ifdef DEBUG_INCLUDE
  1718.             ap_rvputs(r, "     Returning ", current->value ? "1" : "0",
  1719.                    "\n", NULL);
  1720. #endif
  1721.             current->done = 1;
  1722.             current = current->parent;
  1723.             break;
  1724.  
  1725.         case token_eq:
  1726.         case token_ne:
  1727. #ifdef DEBUG_INCLUDE
  1728.             ap_rputs("     Evaluate eq/ne\n", r);
  1729. #endif
  1730.             if ((current->left == (struct parse_node *) NULL) ||
  1731.                 (current->right == (struct parse_node *) NULL) ||
  1732.                 (current->left->token.type != token_string) ||
  1733.                 (current->right->token.type != token_string)) {
  1734.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1735.                             "Invalid expression \"%s\" in file %s",
  1736.                             expr, r->filename);
  1737.                 ap_rputs(error, r);
  1738.                 goto RETURN;
  1739.             }
  1740.             parse_string(r, current->left->token.value,
  1741.                          buffer, sizeof(buffer), 0);
  1742.             ap_cpystrn(current->left->token.value, buffer,
  1743.             sizeof(current->left->token.value));
  1744.             parse_string(r, current->right->token.value,
  1745.                          buffer, sizeof(buffer), 0);
  1746.             ap_cpystrn(current->right->token.value, buffer,
  1747.             sizeof(current->right->token.value));
  1748.             if (current->right->token.value[0] == '/') {
  1749.                 int len;
  1750.                 len = strlen(current->right->token.value);
  1751.                 if (current->right->token.value[len - 1] == '/') {
  1752.                     current->right->token.value[len - 1] = '\0';
  1753.                 }
  1754.                 else {
  1755.                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1756.                                 "Invalid rexp \"%s\" in file %s",
  1757.                                 current->right->token.value, r->filename);
  1758.                     ap_rputs(error, r);
  1759.                     goto RETURN;
  1760.                 }
  1761. #ifdef DEBUG_INCLUDE
  1762.                 ap_rvputs(r, "     Re Compare (", current->left->token.value,
  1763.                   ") with /", ¤t->right->token.value[1], "/\n", NULL);
  1764. #endif
  1765.                 current->value =
  1766.                     re_check(r, current->left->token.value,
  1767.                              ¤t->right->token.value[1]);
  1768.             }
  1769.             else {
  1770. #ifdef DEBUG_INCLUDE
  1771.                 ap_rvputs(r, "     Compare (", current->left->token.value,
  1772.                        ") with (", current->right->token.value, ")\n", NULL);
  1773. #endif
  1774.                 current->value =
  1775.                     (strcmp(current->left->token.value,
  1776.                             current->right->token.value) == 0);
  1777.             }
  1778.             if (current->token.type == token_ne) {
  1779.                 current->value = !current->value;
  1780.             }
  1781. #ifdef DEBUG_INCLUDE
  1782.             ap_rvputs(r, "     Returning ", current->value ? "1" : "0",
  1783.                    "\n", NULL);
  1784. #endif
  1785.             current->done = 1;
  1786.             current = current->parent;
  1787.             break;
  1788.         case token_ge:
  1789.         case token_gt:
  1790.         case token_le:
  1791.         case token_lt:
  1792. #ifdef DEBUG_INCLUDE
  1793.             ap_rputs("     Evaluate ge/gt/le/lt\n", r);
  1794. #endif
  1795.             if ((current->left == (struct parse_node *) NULL) ||
  1796.                 (current->right == (struct parse_node *) NULL) ||
  1797.                 (current->left->token.type != token_string) ||
  1798.                 (current->right->token.type != token_string)) {
  1799.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1800.                             "Invalid expression \"%s\" in file %s",
  1801.                             expr, r->filename);
  1802.                 ap_rputs(error, r);
  1803.                 goto RETURN;
  1804.             }
  1805.             parse_string(r, current->left->token.value,
  1806.                          buffer, sizeof(buffer), 0);
  1807.             ap_cpystrn(current->left->token.value, buffer,
  1808.             sizeof(current->left->token.value));
  1809.             parse_string(r, current->right->token.value,
  1810.                          buffer, sizeof(buffer), 0);
  1811.             ap_cpystrn(current->right->token.value, buffer,
  1812.             sizeof(current->right->token.value));
  1813. #ifdef DEBUG_INCLUDE
  1814.             ap_rvputs(r, "     Compare (", current->left->token.value,
  1815.                    ") with (", current->right->token.value, ")\n", NULL);
  1816. #endif
  1817.             current->value =
  1818.                 strcmp(current->left->token.value,
  1819.                        current->right->token.value);
  1820.             if (current->token.type == token_ge) {
  1821.                 current->value = current->value >= 0;
  1822.             }
  1823.             else if (current->token.type == token_gt) {
  1824.                 current->value = current->value > 0;
  1825.             }
  1826.             else if (current->token.type == token_le) {
  1827.                 current->value = current->value <= 0;
  1828.             }
  1829.             else if (current->token.type == token_lt) {
  1830.                 current->value = current->value < 0;
  1831.             }
  1832.             else {
  1833.                 current->value = 0;     /* Don't return -1 if unknown token */
  1834.             }
  1835. #ifdef DEBUG_INCLUDE
  1836.             ap_rvputs(r, "     Returning ", current->value ? "1" : "0",
  1837.                    "\n", NULL);
  1838. #endif
  1839.             current->done = 1;
  1840.             current = current->parent;
  1841.             break;
  1842.  
  1843.         case token_not:
  1844.             if (current->right != (struct parse_node *) NULL) {
  1845.                 if (!current->right->done) {
  1846.                     current = current->right;
  1847.                     continue;
  1848.                 }
  1849.                 current->value = !current->right->value;
  1850.             }
  1851.             else {
  1852.                 current->value = 0;
  1853.             }
  1854. #ifdef DEBUG_INCLUDE
  1855.             ap_rvputs(r, "     Evaluate !: ", current->value ? "1" : "0",
  1856.                    "\n", NULL);
  1857. #endif
  1858.             current->done = 1;
  1859.             current = current->parent;
  1860.             break;
  1861.  
  1862.         case token_group:
  1863.             if (current->right != (struct parse_node *) NULL) {
  1864.                 if (!current->right->done) {
  1865.                     current = current->right;
  1866.                     continue;
  1867.                 }
  1868.                 current->value = current->right->value;
  1869.             }
  1870.             else {
  1871.                 current->value = 1;
  1872.             }
  1873. #ifdef DEBUG_INCLUDE
  1874.             ap_rvputs(r, "     Evaluate (): ", current->value ? "1" : "0",
  1875.                    "\n", NULL);
  1876. #endif
  1877.             current->done = 1;
  1878.             current = current->parent;
  1879.             break;
  1880.  
  1881.         case token_lbrace:
  1882.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1883.                         "Unmatched '(' in \"%s\" in file %s",
  1884.                         expr, r->filename);
  1885.             ap_rputs(error, r);
  1886.             goto RETURN;
  1887.  
  1888.         case token_rbrace:
  1889.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1890.                         "Unmatched ')' in \"%s\" in file %s",
  1891.                         expr, r->filename);
  1892.             ap_rputs(error, r);
  1893.             goto RETURN;
  1894.  
  1895.         default:
  1896.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1897.             "bad token type");
  1898.             ap_rputs(error, r);
  1899.             goto RETURN;
  1900.         }
  1901.     }
  1902.  
  1903.     retval = (root == (struct parse_node *) NULL) ? 0 : root->value;
  1904.   RETURN:
  1905.     ap_destroy_pool(expr_pool);
  1906.     return (retval);
  1907. }
  1908.  
  1909. static int handle_if(FILE *in, request_rec *r, const char *error,
  1910.                      int *conditional_status, int *printing)
  1911. {
  1912.     char tag[MAX_STRING_LEN];
  1913.     char *tag_val;
  1914.     char *expr;
  1915.  
  1916.     expr = NULL;
  1917.     while (1) {
  1918.         tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0);
  1919.         if (*tag == '\0') {
  1920.             return 1;
  1921.         }
  1922.         else if (!strcmp(tag, "done")) {
  1923.         if (expr == NULL) {
  1924.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1925.                 "missing expr in if statement: %s",
  1926.                 r->filename);
  1927.         ap_rputs(error, r);
  1928.         return 1;
  1929.         }
  1930.             *printing = *conditional_status = parse_expr(r, expr, error);
  1931. #ifdef DEBUG_INCLUDE
  1932.             ap_rvputs(r, "**** if conditional_status=\"",
  1933.                    *conditional_status ? "1" : "0", "\"\n", NULL);
  1934. #endif
  1935.             return 0;
  1936.         }
  1937.         else if (!strcmp(tag, "expr")) {
  1938.             expr = tag_val;
  1939. #ifdef DEBUG_INCLUDE
  1940.             ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL);
  1941. #endif
  1942.         }
  1943.         else {
  1944.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1945.                         "unknown parameter \"%s\" to tag if in %s",
  1946.                         tag, r->filename);
  1947.             ap_rputs(error, r);
  1948.         }
  1949.     }
  1950. }
  1951.  
  1952. static int handle_elif(FILE *in, request_rec *r, const char *error,
  1953.                        int *conditional_status, int *printing)
  1954. {
  1955.     char tag[MAX_STRING_LEN];
  1956.     char *tag_val;
  1957.     char *expr;
  1958.  
  1959.     expr = NULL;
  1960.     while (1) {
  1961.         tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0);
  1962.         if (*tag == '\0') {
  1963.             return 1;
  1964.         }
  1965.         else if (!strcmp(tag, "done")) {
  1966. #ifdef DEBUG_INCLUDE
  1967.             ap_rvputs(r, "**** elif conditional_status=\"",
  1968.                    *conditional_status ? "1" : "0", "\"\n", NULL);
  1969. #endif
  1970.             if (*conditional_status) {
  1971.                 *printing = 0;
  1972.                 return (0);
  1973.             }
  1974.         if (expr == NULL) {
  1975.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1976.                 "missing expr in elif statement: %s",
  1977.                 r->filename);
  1978.         ap_rputs(error, r);
  1979.         return 1;
  1980.         }
  1981.             *printing = *conditional_status = parse_expr(r, expr, error);
  1982. #ifdef DEBUG_INCLUDE
  1983.             ap_rvputs(r, "**** elif conditional_status=\"",
  1984.                    *conditional_status ? "1" : "0", "\"\n", NULL);
  1985. #endif
  1986.             return 0;
  1987.         }
  1988.         else if (!strcmp(tag, "expr")) {
  1989.             expr = tag_val;
  1990. #ifdef DEBUG_INCLUDE
  1991.             ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL);
  1992. #endif
  1993.         }
  1994.         else {
  1995.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1996.                         "unknown parameter \"%s\" to tag if in %s",
  1997.                         tag, r->filename);
  1998.             ap_rputs(error, r);
  1999.         }
  2000.     }
  2001. }
  2002.  
  2003. static int handle_else(FILE *in, request_rec *r, const char *error,
  2004.                        int *conditional_status, int *printing)
  2005. {
  2006.     char tag[MAX_STRING_LEN];
  2007.  
  2008.     if (!get_tag(r->pool, in, tag, sizeof(tag), 1)) {
  2009.         return 1;
  2010.     }
  2011.     else if (!strcmp(tag, "done")) {
  2012. #ifdef DEBUG_INCLUDE
  2013.         ap_rvputs(r, "**** else conditional_status=\"",
  2014.                *conditional_status ? "1" : "0", "\"\n", NULL);
  2015. #endif
  2016.         *printing = !(*conditional_status);
  2017.         *conditional_status = 1;
  2018.         return 0;
  2019.     }
  2020.     else {
  2021.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  2022.                     "else directive does not take tags in %s",
  2023.             r->filename);
  2024.         if (*printing) {
  2025.             ap_rputs(error, r);
  2026.         }
  2027.         return -1;
  2028.     }
  2029. }
  2030.  
  2031. static int handle_endif(FILE *in, request_rec *r, const char *error,
  2032.                         int *conditional_status, int *printing)
  2033. {
  2034.     char tag[MAX_STRING_LEN];
  2035.  
  2036.     if (!get_tag(r->pool, in, tag, sizeof(tag), 1)) {
  2037.         return 1;
  2038.     }
  2039.     else if (!strcmp(tag, "done")) {
  2040. #ifdef DEBUG_INCLUDE
  2041.         ap_rvputs(r, "**** endif conditional_status=\"",
  2042.                *conditional_status ? "1" : "0", "\"\n", NULL);
  2043. #endif
  2044.         *printing = 1;
  2045.         *conditional_status = 1;
  2046.         return 0;
  2047.     }
  2048.     else {
  2049.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  2050.                     "endif directive does not take tags in %s",
  2051.             r->filename);
  2052.         ap_rputs(error, r);
  2053.         return -1;
  2054.     }
  2055. }
  2056.  
  2057. static int handle_set(FILE *in, request_rec *r, const char *error)
  2058. {
  2059.     char tag[MAX_STRING_LEN];
  2060.     char parsed_string[MAX_STRING_LEN];
  2061.     char *tag_val;
  2062.     char *var;
  2063.  
  2064.     var = (char *) NULL;
  2065.     while (1) {
  2066.         if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
  2067.             return 1;
  2068.         }
  2069.         else if (!strcmp(tag, "done")) {
  2070.             return 0;
  2071.         }
  2072.         else if (!strcmp(tag, "var")) {
  2073.             var = tag_val;
  2074.         }
  2075.         else if (!strcmp(tag, "value")) {
  2076.             if (var == (char *) NULL) {
  2077.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  2078.                             "variable must precede value in set directive in %s",
  2079.                 r->filename);
  2080.                 ap_rputs(error, r);
  2081.                 return -1;
  2082.             }
  2083.             parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
  2084.             ap_table_setn(r->subprocess_env, var, ap_pstrdup(r->pool, parsed_string));
  2085.         }
  2086.         else {
  2087.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  2088.                         "Invalid tag for set directive in %s", r->filename);
  2089.             ap_rputs(error, r);
  2090.             return -1;
  2091.         }
  2092.     }
  2093. }
  2094.  
  2095. static int handle_printenv(FILE *in, request_rec *r, const char *error)
  2096. {
  2097.     char tag[MAX_STRING_LEN];
  2098.     char *tag_val;
  2099.     array_header *arr = ap_table_elts(r->subprocess_env);
  2100.     table_entry *elts = (table_entry *) arr->elts;
  2101.     int i;
  2102.  
  2103.     if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
  2104.         return 1;
  2105.     }
  2106.     else if (!strcmp(tag, "done")) {
  2107.         for (i = 0; i < arr->nelts; ++i) {
  2108.             ap_rvputs(r, elts[i].key, "=", elts[i].val, "\n", NULL);
  2109.         }
  2110.         return 0;
  2111.     }
  2112.     else {
  2113.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  2114.                     "printenv directive does not take tags in %s",
  2115.             r->filename);
  2116.         ap_rputs(error, r);
  2117.         return -1;
  2118.     }
  2119. }
  2120.  
  2121.  
  2122.  
  2123. /* -------------------------- The main function --------------------------- */
  2124.  
  2125. /* This is a stub which parses a file descriptor. */
  2126.  
  2127. static void send_parsed_content(FILE *f, request_rec *r)
  2128. {
  2129.     char directive[MAX_STRING_LEN], error[MAX_STRING_LEN];
  2130.     char timefmt[MAX_STRING_LEN];
  2131.     int noexec = ap_allow_options(r) & OPT_INCNOEXEC;
  2132.     int ret, sizefmt;
  2133.     int if_nesting;
  2134.     int printing;
  2135.     int conditional_status;
  2136.  
  2137.     ap_cpystrn(error, DEFAULT_ERROR_MSG, sizeof(error));
  2138.     ap_cpystrn(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt));
  2139.     sizefmt = SIZEFMT_KMG;
  2140.  
  2141. /*  Turn printing on */
  2142.     printing = conditional_status = 1;
  2143.     if_nesting = 0;
  2144.  
  2145. #ifndef WIN32
  2146.     ap_chdir_file(r->filename);
  2147. #endif
  2148.     if (r->args) {              /* add QUERY stuff to env cause it ain't yet */
  2149.         char *arg_copy = ap_pstrdup(r->pool, r->args);
  2150.  
  2151.         ap_table_setn(r->subprocess_env, "QUERY_STRING", r->args);
  2152.         ap_unescape_url(arg_copy);
  2153.         ap_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED",
  2154.                   ap_escape_shell_cmd(r->pool, arg_copy));
  2155.     }
  2156.  
  2157.     while (1) {
  2158.         if (!find_string(f, STARTING_SEQUENCE, r, printing)) {
  2159.             if (get_directive(f, directive, sizeof(directive), r->pool)) {
  2160.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  2161.                 "mod_include: error reading directive in %s",
  2162.                 r->filename);
  2163.         ap_rputs(error, r);
  2164.                 return;
  2165.             }
  2166.             if (!strcmp(directive, "if")) {
  2167.                 if (!printing) {
  2168.                     if_nesting++;
  2169.                 }
  2170.                 else {
  2171.                     ret = handle_if(f, r, error, &conditional_status,
  2172.                                     &printing);
  2173.                     if_nesting = 0;
  2174.                 }
  2175.                 continue;
  2176.             }
  2177.             else if (!strcmp(directive, "else")) {
  2178.                 if (!if_nesting) {
  2179.                     ret = handle_else(f, r, error, &conditional_status,
  2180.                                       &printing);
  2181.                 }
  2182.                 continue;
  2183.             }
  2184.             else if (!strcmp(directive, "elif")) {
  2185.                 if (!if_nesting) {
  2186.                     ret = handle_elif(f, r, error, &conditional_status,
  2187.                                       &printing);
  2188.                 }
  2189.                 continue;
  2190.             }
  2191.             else if (!strcmp(directive, "endif")) {
  2192.                 if (!if_nesting) {
  2193.                     ret = handle_endif(f, r, error, &conditional_status,
  2194.                                        &printing);
  2195.                 }
  2196.                 else {
  2197.                     if_nesting--;
  2198.                 }
  2199.                 continue;
  2200.             }
  2201.             if (!printing) {
  2202.                 continue;
  2203.             }
  2204.             if (!strcmp(directive, "exec")) {
  2205.                 if (noexec) {
  2206.                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  2207.                   "exec used but not allowed in %s",
  2208.                   r->filename);
  2209.                     if (printing) {
  2210.                         ap_rputs(error, r);
  2211.                     }
  2212.                     ret = find_string(f, ENDING_SEQUENCE, r, 0);
  2213.                 }
  2214.                 else {
  2215.                     ret = handle_exec(f, r, error);
  2216.                 }
  2217.             }
  2218.             else if (!strcmp(directive, "config")) {
  2219.                 ret = handle_config(f, r, error, timefmt, &sizefmt);
  2220.             }
  2221.             else if (!strcmp(directive, "set")) {
  2222.                 ret = handle_set(f, r, error);
  2223.             }
  2224.             else if (!strcmp(directive, "include")) {
  2225.                 ret = handle_include(f, r, error, noexec);
  2226.             }
  2227.             else if (!strcmp(directive, "echo")) {
  2228.                 ret = handle_echo(f, r, error);
  2229.             }
  2230.             else if (!strcmp(directive, "fsize")) {
  2231.                 ret = handle_fsize(f, r, error, sizefmt);
  2232.             }
  2233.             else if (!strcmp(directive, "flastmod")) {
  2234.                 ret = handle_flastmod(f, r, error, timefmt);
  2235.             }
  2236.             else if (!strcmp(directive, "printenv")) {
  2237.                 ret = handle_printenv(f, r, error);
  2238.             }
  2239. #ifdef USE_PERL_SSI
  2240.             else if (!strcmp(directive, "perl")) {
  2241.                 ret = handle_perl(f, r, error);
  2242.             }
  2243. #endif
  2244.             else {
  2245.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  2246.                   "unknown directive \"%s\" "
  2247.                   "in parsed doc %s",
  2248.                   directive, r->filename);
  2249.                 if (printing) {
  2250.                     ap_rputs(error, r);
  2251.                 }
  2252.                 ret = find_string(f, ENDING_SEQUENCE, r, 0);
  2253.             }
  2254.             if (ret) {
  2255.                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  2256.                   "premature EOF in parsed file %s",
  2257.                   r->filename);
  2258.                 return;
  2259.             }
  2260.         }
  2261.         else {
  2262.             return;
  2263.         }
  2264.     }
  2265. }
  2266.  
  2267. /*****************************************************************
  2268.  *
  2269.  * XBITHACK.  Sigh...  NB it's configurable per-directory; the compile-time
  2270.  * option only changes the default.
  2271.  */
  2272.  
  2273. module includes_module;
  2274. enum xbithack {
  2275.     xbithack_off, xbithack_on, xbithack_full
  2276. };
  2277.  
  2278. #ifdef XBITHACK
  2279. #define DEFAULT_XBITHACK xbithack_full
  2280. #else
  2281. #define DEFAULT_XBITHACK xbithack_off
  2282. #endif
  2283.  
  2284. static void *create_includes_dir_config(pool *p, char *dummy)
  2285. {
  2286.     enum xbithack *result = (enum xbithack *) ap_palloc(p, sizeof(enum xbithack));
  2287.     *result = DEFAULT_XBITHACK;
  2288.     return result;
  2289. }
  2290.  
  2291. static const char *set_xbithack(cmd_parms *cmd, void *xbp, char *arg)
  2292. {
  2293.     enum xbithack *state = (enum xbithack *) xbp;
  2294.  
  2295.     if (!strcasecmp(arg, "off")) {
  2296.         *state = xbithack_off;
  2297.     }
  2298.     else if (!strcasecmp(arg, "on")) {
  2299.         *state = xbithack_on;
  2300.     }
  2301.     else if (!strcasecmp(arg, "full")) {
  2302.         *state = xbithack_full;
  2303.     }
  2304.     else {
  2305.         return "XBitHack must be set to Off, On, or Full";
  2306.     }
  2307.  
  2308.     return NULL;
  2309. }
  2310.  
  2311. static int send_parsed_file(request_rec *r)
  2312. {
  2313.     FILE *f;
  2314.     enum xbithack *state =
  2315.     (enum xbithack *) ap_get_module_config(r->per_dir_config, &includes_module);
  2316.     int errstatus;
  2317.     request_rec *parent;
  2318.  
  2319.     if (!(ap_allow_options(r) & OPT_INCLUDES)) {
  2320.         return DECLINED;
  2321.     }
  2322.     r->allowed |= (1 << M_GET);
  2323.     if (r->method_number != M_GET) {
  2324.         return DECLINED;
  2325.     }
  2326.     if (r->finfo.st_mode == 0) {
  2327.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  2328.             "File does not exist: %s",
  2329.                     (r->path_info
  2330.                      ? ap_pstrcat(r->pool, r->filename, r->path_info, NULL)
  2331.                      : r->filename));
  2332.         return HTTP_NOT_FOUND;
  2333.     }
  2334.  
  2335.     if (!(f = ap_pfopen(r->pool, r->filename, "r"))) {
  2336.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  2337.                     "file permissions deny server access: %s", r->filename);
  2338.         return HTTP_FORBIDDEN;
  2339.     }
  2340.  
  2341.     if ((*state == xbithack_full)
  2342. #if !defined(OS2) && !defined(WIN32)
  2343.     /*  OS/2 dosen't support Groups. */
  2344.         && (r->finfo.st_mode & S_IXGRP)
  2345. #endif
  2346.         ) {
  2347.         ap_update_mtime(r, r->finfo.st_mtime);
  2348.         ap_set_last_modified(r);
  2349.     }
  2350.     if ((errstatus = ap_meets_conditions(r)) != OK) {
  2351.         return errstatus;
  2352.     }
  2353.  
  2354.     ap_send_http_header(r);
  2355.  
  2356.     if (r->header_only) {
  2357.         ap_pfclose(r->pool, f);
  2358.         return OK;
  2359.     }
  2360.  
  2361.     if ((parent = ap_get_module_config(r->request_config, &includes_module))) {
  2362.     /* Kludge --- for nested includes, we want to keep the subprocess
  2363.      * environment of the base document (for compatibility); that means
  2364.      * torquing our own last_modified date as well so that the
  2365.      * LAST_MODIFIED variable gets reset to the proper value if the
  2366.      * nested document resets <!--#config timefmt-->.
  2367.      * We also insist that the memory for this subrequest not be
  2368.      * destroyed, that's dealt with in handle_include().
  2369.      */
  2370.     r->subprocess_env = parent->subprocess_env;
  2371.     ap_pool_join(parent->pool, r->pool);
  2372.     r->finfo.st_mtime = parent->finfo.st_mtime;
  2373.     }
  2374.     else {
  2375.     /* we're not a nested include, so we create an initial
  2376.      * environment */
  2377.         ap_add_common_vars(r);
  2378.         ap_add_cgi_vars(r);
  2379.         add_include_vars(r, DEFAULT_TIME_FORMAT);
  2380.     }
  2381.     /* XXX: this is bogus, at some point we're going to do a subrequest,
  2382.      * and when we do it we're going to be subjecting code that doesn't
  2383.      * expect to be signal-ready to SIGALRM.  There is no clean way to
  2384.      * fix this, except to put alarm support into BUFF. -djg
  2385.      */
  2386.     ap_hard_timeout("send SSI", r);
  2387.  
  2388. #ifdef CHARSET_EBCDIC
  2389.     /* XXX:@@@ Is the generated/included output ALWAYS in text/ebcdic format? */
  2390.     ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
  2391. #endif
  2392.  
  2393.     send_parsed_content(f, r);
  2394.  
  2395.     if (parent) {
  2396.     /* signify that the sub request should not be killed */
  2397.     ap_set_module_config(r->request_config, &includes_module,
  2398.         NESTED_INCLUDE_MAGIC);
  2399.     }
  2400.  
  2401.     ap_kill_timeout(r);
  2402.     return OK;
  2403. }
  2404.  
  2405. static int send_shtml_file(request_rec *r)
  2406. {
  2407.     r->content_type = "text/html";
  2408.     return send_parsed_file(r);
  2409. }
  2410.  
  2411. static int xbithack_handler(request_rec *r)
  2412. {
  2413. #if defined(OS2) || defined(WIN32)
  2414.     /* OS/2 dosen't currently support the xbithack. This is being worked on. */
  2415.     return DECLINED;
  2416. #else
  2417.     enum xbithack *state;
  2418.  
  2419.     if (!(r->finfo.st_mode & S_IXUSR)) {
  2420.         return DECLINED;
  2421.     }
  2422.  
  2423.     state = (enum xbithack *) ap_get_module_config(r->per_dir_config,
  2424.                                                 &includes_module);
  2425.  
  2426.     if (*state == xbithack_off) {
  2427.         return DECLINED;
  2428.     }
  2429.     return send_parsed_file(r);
  2430. #endif
  2431. }
  2432.  
  2433. static const command_rec includes_cmds[] =
  2434. {
  2435.     {"XBitHack", set_xbithack, NULL, OR_OPTIONS, TAKE1, "Off, On, or Full"},
  2436.     {NULL}
  2437. };
  2438.  
  2439. static const handler_rec includes_handlers[] =
  2440. {
  2441.     {INCLUDES_MAGIC_TYPE, send_shtml_file},
  2442.     {INCLUDES_MAGIC_TYPE3, send_shtml_file},
  2443.     {"server-parsed", send_parsed_file},
  2444.     {"text/html", xbithack_handler},
  2445.     {NULL}
  2446. };
  2447.  
  2448. module MODULE_VAR_EXPORT includes_module =
  2449. {
  2450.     STANDARD_MODULE_STUFF,
  2451.     NULL,                       /* initializer */
  2452.     create_includes_dir_config, /* dir config creater */
  2453.     NULL,                       /* dir merger --- default is to override */
  2454.     NULL,                       /* server config */
  2455.     NULL,                       /* merge server config */
  2456.     includes_cmds,              /* command table */
  2457.     includes_handlers,          /* handlers */
  2458.     NULL,                       /* filename translation */
  2459.     NULL,                       /* check_user_id */
  2460.     NULL,                       /* check auth */
  2461.     NULL,                       /* check access */
  2462.     NULL,                       /* type_checker */
  2463.     NULL,                       /* fixups */
  2464.     NULL,                       /* logger */
  2465.     NULL,                       /* header parser */
  2466.     NULL,                       /* child_init */
  2467.     NULL,                       /* child_exit */
  2468.     NULL                        /* post read-request */
  2469. };
  2470.