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, � 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 /", ¤t->right->token.value[1], "/\n", NULL);
1777 #endif
1778 current->value =
1779 re_check(r, current->left->token.value,
1780 ¤t->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