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