1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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
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.
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.
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.
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
47 * ====================================================================
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/>.
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.
60 * http_include.c: Handles the server-parsed HTML documents
62 * Original by Rob McCool; substantial fixups by David Robinson;
63 * incorporated into the Apache module framework by rst.
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 {}
71 * Child <!--#perl sub="sub {print $$}" --> accessed
72 * <!--#perl sub="sub {print ++$Access::Cnt }" --> times. <br>
74 * <!--#perl arg="one" sub="mymod::includer" -->
88 #include "modules/perl/mod_perl.h"
90 #include "ap_config.h"
92 #include "http_config.h"
93 #include "http_request.h"
94 #include "http_core.h"
95 #include "http_protocol.h"
97 #include "http_main.h"
98 #include "util_script.h"
99 #include "http_core.h"
103 #ifdef HAVE_STRINGS_H
110 #include "util_ebcdic.h"
112 #define STARTING_SEQUENCE "<!--#"
113 #define ENDING_SEQUENCE "-->"
114 #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
115 #define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
116 #define SIZEFMT_BYTES 0
117 #define SIZEFMT_KMG 1
118 #ifdef CHARSET_EBCDIC
119 #define RAW_ASCII_CHAR(ch) ap_xlate_conv_byte(ap_hdrs_from_ascii, (unsigned char)ch)
120 #else /*CHARSET_EBCDIC*/
121 #define RAW_ASCII_CHAR(ch) (ch)
122 #endif /*CHARSET_EBCDIC*/
124 module MODULE_VAR_EXPORT includes_module;
126 /* just need some arbitrary non-NULL pointer which can't also be a request_rec */
127 #define NESTED_INCLUDE_MAGIC (&includes_module)
129 /* TODO: changing directory should be handled by CreateProcess */
130 #define ap_chdir_file(x) do {} while(0)
132 /* ------------------------ Environment function -------------------------- */
134 /* XXX: could use ap_table_overlap here */
135 static void add_include_vars(request_rec *r, char *timefmt)
139 #endif /* ndef WIN32 */
140 ap_table_t *e = r->subprocess_env;
142 ap_time_t date = r->request_time;
144 ap_table_setn(e, "DATE_LOCAL", ap_ht_time(r->pool, date, timefmt, 0));
145 ap_table_setn(e, "DATE_GMT", ap_ht_time(r->pool, date, timefmt, 1));
146 ap_table_setn(e, "LAST_MODIFIED",
147 ap_ht_time(r->pool, r->finfo.mtime, timefmt, 0));
148 ap_table_setn(e, "DOCUMENT_URI", r->uri);
149 ap_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
151 pw = getpwuid(r->finfo.user);
153 ap_table_setn(e, "USER_NAME", ap_pstrdup(r->pool, pw->pw_name));
156 ap_table_setn(e, "USER_NAME", ap_psprintf(r->pool, "user#%lu",
157 (unsigned long) r->finfo.user));
159 #endif /* ndef WIN32 */
161 if ((t = strrchr(r->filename, '/'))) {
162 ap_table_setn(e, "DOCUMENT_NAME", ++t);
165 ap_table_setn(e, "DOCUMENT_NAME", r->uri);
168 char *arg_copy = ap_pstrdup(r->pool, r->args);
170 ap_unescape_url(arg_copy);
171 ap_table_setn(e, "QUERY_STRING_UNESCAPED",
172 ap_escape_shell_cmd(r->pool, arg_copy));
178 /* --------------------------- Parser functions --------------------------- */
180 #define OUTBUFSIZE 4096
181 /* PUT_CHAR and FLUSH_BUF currently only work within the scope of
182 * find_string(); they are hacks to avoid calling rputc for each and
183 * every character output. A common set of buffering calls for this
184 * type of output SHOULD be implemented.
186 #define PUT_CHAR(c,r) \
188 outbuf[outind++] = c; \
189 if (outind == OUTBUFSIZE) { \
194 /* there SHOULD be some error checking on the return value of
195 * rwrite, however it is unclear what the API for rwrite returning
196 * errors is and little can really be done to help the error in
199 #define FLUSH_BUF(r) \
201 ap_rwrite(outbuf, outind, r); \
206 * f: file handle being read from
207 * c: character to read into
208 * ret: return value to use if input fails
209 * r: current request_rec
211 * This macro is redefined after find_string() for historical reasons
212 * to avoid too many code changes. This is one of the many things
213 * that should be fixed.
215 #define GET_CHAR(f,c,ret,r) \
217 ap_status_t status = ap_getc(&c, f); \
218 if (status != APR_SUCCESS) { /* either EOF or error -- needs error handling if latter */ \
219 if (status != APR_EOF) { \
220 ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, \
221 "encountered error in GET_CHAR macro, " \
230 static int find_string(ap_file_t *in, const char *str, request_rec *r, int printing)
232 int x, l = strlen(str), p;
233 char outbuf[OUTBUFSIZE];
239 GET_CHAR(in, c, 1, r);
248 for (x = 0; x < p; x++) {
261 #define GET_CHAR(f,c,r,p) \
263 ap_status_t status = ap_getc(&c, f); \
264 if (status != APR_SUCCESS) { /* either EOF or error -- needs error handling if latter */ \
265 if (status != APR_EOF) { \
266 ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, \
267 "encountered error in GET_CHAR macro, " \
276 * decodes a string containing html entities or numeric character references.
277 * 's' is overwritten with the decoded string.
278 * If 's' is syntatically incorrect, then the followed fixups will be made:
279 * unknown entities will be left undecoded;
280 * references to unused numeric characters will be deleted.
281 * In particular, � will not be decoded, but will be deleted.
286 /* maximum length of any ISO-LATIN-1 HTML entity name. */
287 #define MAXENTLEN (6)
289 /* The following is a shrinking transformation, therefore safe. */
291 static void decodehtml(char *s)
296 static const char * const entlist[MAXENTLEN + 1] =
300 "lt\074gt\076", /* 2 */
301 "amp\046ETH\320eth\360", /* 3 */
302 "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\
303 iuml\357ouml\366uuml\374yuml\377", /* 4 */
304 "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\
305 THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\
306 ucirc\373thorn\376", /* 5 */
307 "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\
308 Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\
309 Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\
310 egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\
311 otilde\365oslash\370ugrave\371uacute\372yacute\375" /* 6 */
314 for (; *s != '\0'; s++, p++) {
319 /* find end of entity */
320 for (i = 1; s[i] != ';' && s[i] != '\0'; i++) {
324 if (s[i] == '\0') { /* treat as normal data */
329 /* is it numeric ? */
331 for (j = 2, val = 0; j < i && ap_isdigit(s[j]); j++) {
332 val = val * 10 + s[j] - '0';
335 if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
336 (val >= 127 && val <= 160) || val >= 256) {
337 p--; /* no data to output */
340 *p = RAW_ASCII_CHAR(val);
345 if (j > MAXENTLEN || entlist[j] == NULL) {
348 continue; /* skip it */
350 for (ents = entlist[j]; *ents != '\0'; ents += i) {
351 if (strncmp(s + 1, ents, j) == 0) {
357 *p = '&'; /* unknown */
360 *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]);
370 * extract the next tag name and value.
371 * if there are no more tags, set the tag name to 'done'
372 * the tag value is html decoded if dodecode is non-zero
375 static char *get_tag(ap_pool_t *p, ap_file_t *in, char *tag, int tagbuf_len, int dodecode)
377 char *t = tag, *tag_val, c, term;
379 /* makes code below a little less cluttered */
382 do { /* skip whitespace */
383 GET_CHAR(in, c, NULL, p);
384 } while (ap_isspace(c));
386 /* tags can't start with - */
388 GET_CHAR(in, c, NULL, p);
391 GET_CHAR(in, c, NULL, p);
392 } while (ap_isspace(c));
394 ap_cpystrn(tag, "done", tagbuf_len);
398 return NULL; /* failed */
401 /* find end of tag name */
403 if (t - tag == tagbuf_len) {
407 if (c == '=' || ap_isspace(c)) {
410 *(t++) = ap_tolower(c);
411 GET_CHAR(in, c, NULL, p);
417 while (ap_isspace(c)) {
418 GET_CHAR(in, c, NULL, p); /* space before = */
426 GET_CHAR(in, c, NULL, p); /* space after = */
427 } while (ap_isspace(c));
429 /* we should allow a 'name' as a value */
431 if (c != '"' && c != '\'') {
436 GET_CHAR(in, c, NULL, p);
437 if (t - tag == tagbuf_len) {
441 /* Want to accept \" as a valid character within a string. */
443 *(t++) = c; /* Add backslash */
444 GET_CHAR(in, c, NULL, p);
445 if (c == term) { /* Only if */
446 *(--t) = c; /* Replace backslash ONLY for terminator */
449 else if (c == term) {
458 return ap_pstrdup(p, tag_val);
461 static int get_directive(ap_file_t *in, char *dest, size_t len, ap_pool_t *p)
466 /* make room for nul terminator */
469 /* skip initial whitespace */
471 GET_CHAR(in, c, 1, p);
472 if (!ap_isspace(c)) {
476 /* now get directive */
478 if (d - dest == len) {
481 *d++ = ap_tolower(c);
482 GET_CHAR(in, c, 1, p);
492 * Do variable substitution on strings
494 static void parse_string(request_rec *r, const char *in, char *out,
495 size_t length, int leave_name)
501 /* leave room for nul terminator */
502 end_out = out + length - 1;
504 while ((ch = *in++) != '\0') {
507 if (next == end_out) {
521 char var[MAX_STRING_LEN];
522 const char *start_of_var_name;
523 const char *end_of_var_name; /* end of var name + 1 */
524 const char *expansion;
528 /* guess that the expansion won't happen */
532 start_of_var_name = in;
533 in = ap_strchr_c(in, '}');
535 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
536 0, r, "Missing '}' on variable \"%s\"",
541 end_of_var_name = in;
545 start_of_var_name = in;
546 while (ap_isalnum(*in) || *in == '_') {
549 end_of_var_name = in;
551 /* what a pain, too bad there's no table_getn where you can
552 * pass a non-nul terminated string */
553 l = end_of_var_name - start_of_var_name;
555 l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l;
556 memcpy(var, start_of_var_name, l);
559 val = ap_table_get(r->subprocess_env, var);
562 l = strlen(expansion);
564 else if (leave_name) {
568 break; /* no expansion to be done */
572 /* zero-length variable name causes just the $ to be copied */
575 l = (l > end_out - next) ? (end_out - next) : l;
576 memcpy(next, expansion, l);
581 if (next == end_out) {
594 /* --------------------------- Action handlers ---------------------------- */
596 static int include_cgi(char *s, request_rec *r)
598 request_rec *rr = ap_sub_req_lookup_uri(s, r);
601 if (rr->status != HTTP_OK) {
605 /* No hardwired path info or query allowed */
607 if ((rr->path_info && rr->path_info[0]) || rr->args) {
610 if (rr->finfo.protection == 0) {
614 /* Script gets parameters of the *document*, for back compatibility */
616 rr->path_info = r->path_info; /* hard to get right; see mod_cgi.c */
619 /* Force sub_req to be treated as a CGI request, even if ordinary
620 * typing rules would have called it something else.
623 rr->content_type = CGI_MAGIC_TYPE;
627 rr_status = ap_run_sub_req(rr);
628 if (ap_is_HTTP_REDIRECT(rr_status)) {
629 const char *location = ap_table_get(rr->headers_out, "Location");
630 location = ap_escape_html(rr->pool, location);
631 ap_rvputs(r, "<A HREF=\"", location, "\">", location, "</A>", NULL);
634 ap_destroy_sub_req(rr);
635 ap_chdir_file(r->filename);
640 /* ensure that path is relative, and does not contain ".." elements
641 * ensentially ensure that it does not match the regex:
642 * (^/|(^|/)\.\.(/|$))
643 * XXX: this needs os abstraction... consider c:..\foo in win32
645 static int is_only_below(const char *path)
647 #ifdef HAVE_DRIVE_LETTERS
651 if (path[0] == '/') {
654 if (path[0] == '.' && path[1] == '.'
655 && (path[2] == '\0' || path[2] == '/')) {
659 if (*path == '/' && path[1] == '.' && path[2] == '.'
660 && (path[3] == '\0' || path[3] == '/')) {
668 static int handle_include(ap_file_t *in, request_rec *r, const char *error, int noexec)
670 char tag[MAX_STRING_LEN];
671 char parsed_string[MAX_STRING_LEN];
675 if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
678 if (!strcmp(tag, "file") || !strcmp(tag, "virtual")) {
679 request_rec *rr = NULL;
680 char *error_fmt = NULL;
682 parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
684 /* be safe; only files in this directory or below allowed */
685 if (!is_only_below(parsed_string)) {
686 error_fmt = "unable to include file \"%s\" "
690 rr = ap_sub_req_lookup_file(parsed_string, r);
694 rr = ap_sub_req_lookup_uri(parsed_string, r);
697 if (!error_fmt && rr->status != HTTP_OK) {
698 error_fmt = "unable to include \"%s\" in parsed file %s";
701 if (!error_fmt && noexec && rr->content_type
702 && (strncmp(rr->content_type, "text/", 5))) {
703 error_fmt = "unable to include potential exec \"%s\" "
706 if (error_fmt == NULL) {
707 /* try to avoid recursive includes. We do this by walking
708 * up the r->main list of subrequests, and at each level
709 * walking back through any internal redirects. At each
710 * step, we compare the filenames and the URIs.
712 * The filename comparison catches a recursive include
713 * with an ever-changing URL, eg.
714 * <!--#include virtual=
715 * "$REQUEST_URI/$QUERY_STRING?$QUERY_STRING/x"-->
716 * which, although they would eventually be caught because
717 * we have a limit on the length of files, etc., can
718 * recurse for a while.
720 * The URI comparison catches the case where the filename
721 * is changed while processing the request, so the
722 * current name is never the same as any previous one.
723 * This can happen with "DocumentRoot /foo" when you
724 * request "/" on the server and it includes "/".
725 * This only applies to modules such as mod_dir that
726 * (somewhat improperly) mess with r->filename outside
727 * of a filename translation phase.
731 for (p = r; p != NULL && !founddupe; p = p->main) {
733 for (q = p; q != NULL; q = q->prev) {
734 if ( (strcmp(q->filename, rr->filename) == 0) ||
735 (strcmp(q->uri, rr->uri) == 0) ){
743 error_fmt = "Recursive include of \"%s\" "
748 /* see the Kludge in send_parsed_file for why */
750 ap_set_module_config(rr->request_config, &includes_module, r);
752 if (!error_fmt && ap_run_sub_req(rr)) {
753 error_fmt = "unable to include \"%s\" in parsed file %s";
755 ap_chdir_file(r->filename);
757 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
758 0, r, error_fmt, tag_val, r->filename);
762 /* destroy the sub request if it's not a nested include */
764 && ap_get_module_config(rr->request_config, &includes_module)
765 != NESTED_INCLUDE_MAGIC) {
766 ap_destroy_sub_req(rr);
769 else if (!strcmp(tag, "done")) {
773 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
774 "unknown parameter \"%s\" to tag include in %s",
791 static ap_status_t build_argv_list(char ***argv, request_rec *r, ap_pool_t *p)
793 int numwords, x, idx;
795 const char *args = r->args;
797 if (!args || !args[0] || ap_strchr_c(args, '=')) {
801 /* count the number of keywords */
802 for (x = 0, numwords = 1; args[x]; x++) {
803 if (args[x] == '+') {
808 /* Everything is - 1 to account for the first parameter which is the
809 * program name. We didn't used to have to do this, but APR wants it.
811 if (numwords > APACHE_ARG_MAX - 1) {
812 numwords = APACHE_ARG_MAX - 1; /* Truncate args to prevent overrun */
814 *argv = (char **) ap_palloc(p, (numwords + 2) * sizeof(char *));
816 for (x = 1, idx = 1; x < numwords; x++) {
817 w = ap_getword_nulls(p, &args, '+');
819 (*argv)[idx++] = ap_escape_shell_cmd(p, w);
828 static int include_cmd(char *s, request_rec *r)
832 ap_procattr_t *procattr;
835 ap_table_t *env = r->subprocess_env;
837 ap_file_t *file = NULL;
839 #if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \
840 defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
841 core_dir_config *conf;
842 conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,
849 arg.t.filename = r->filename;
850 arg.t.subprocess_env = r->subprocess_env;
851 arg.t.prog_type = FORK_FILE;
854 if (r->path_info && r->path_info[0] != '\0') {
857 ap_table_setn(env, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info));
859 pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r);
860 if (pa_req->filename) {
861 ap_table_setn(env, "PATH_TRANSLATED",
862 ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info,
868 char *arg_copy = ap_pstrdup(r->pool, r->args);
870 ap_table_setn(env, "QUERY_STRING", r->args);
871 ap_unescape_url(arg_copy);
872 ap_table_setn(env, "QUERY_STRING_UNESCAPED",
873 ap_escape_shell_cmd(r->pool, arg_copy));
876 if ((ap_createprocattr_init(&procattr, r->pool) != APR_SUCCESS) ||
877 (ap_setprocattr_io(procattr, APR_NO_PIPE,
878 APR_FULL_BLOCK, APR_NO_PIPE) != APR_SUCCESS) ||
879 (ap_setprocattr_dir(procattr, ap_make_dirstr_parent(r->pool, r->filename)) != APR_SUCCESS) ||
881 ((rc = ap_setprocattr_limit(procattr, APR_LIMIT_CPU, conf->limit_cpu)) != APR_SUCCESS) ||
883 #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
884 ((rc = ap_setprocattr_limit(procattr, APR_LIMIT_MEM, conf->limit_mem)) != APR_SUCCESS) ||
887 ((rc = ap_setprocattr_limit(procattr, APR_LIMIT_NPROC, conf->limit_nproc)) != APR_SUCCESS) ||
889 (ap_setprocattr_cmdtype(procattr, APR_SHELLCMD) != APR_SUCCESS)) {
890 /* Something bad happened, tell the world. */
891 ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
892 "couldn't initialize proc attributes: %s %s", r->filename, s);
896 build_argv_list(&argv, r, r->pool);
897 argv[0] = ap_pstrdup(r->pool, s);
898 rc = ap_create_process(&procnew, s, argv, ap_create_environment(r->pool, env), procattr, r->pool);
900 if (rc != APR_SUCCESS) {
901 /* Bad things happened. Everyone should have cleaned up. */
902 ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
903 "couldn't create child process: %d: %s", rc, s);
906 ap_note_subprocess(r->pool, &procnew, kill_after_timeout);
907 /* Fill in BUFF structure for parents pipe to child's stdout */
909 iol = ap_create_file_iol(file);
912 script_in = ap_bcreate(r->pool, B_RD);
913 ap_bpush_iol(script_in, iol);
914 ap_send_fb(script_in, r);
915 ap_bclose(script_in);
922 static int handle_exec(ap_file_t *in, request_rec *r, const char *error)
924 char tag[MAX_STRING_LEN];
926 char *file = r->filename;
927 char parsed_string[MAX_STRING_LEN];
930 if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
933 if (!strcmp(tag, "cmd")) {
934 parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1);
935 if (include_cmd(parsed_string, r) == -1) {
936 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
937 "execution failure for parameter \"%s\" "
938 "to tag exec in file %s",
942 /* just in case some stooge changed directories */
943 ap_chdir_file(r->filename);
945 else if (!strcmp(tag, "cgi")) {
946 parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
947 if (include_cgi(parsed_string, r) == -1) {
948 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
949 "invalid CGI ref \"%s\" in %s", tag_val, file);
952 ap_chdir_file(r->filename);
954 else if (!strcmp(tag, "done")) {
958 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
959 "unknown parameter \"%s\" to tag exec in %s",
967 static int handle_echo(ap_file_t *in, request_rec *r, const char *error)
969 char tag[MAX_STRING_LEN];
971 enum {E_NONE, E_URL, E_ENTITY} encode;
976 if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
979 if (!strcmp(tag, "var")) {
980 const char *val = ap_table_get(r->subprocess_env, tag_val);
983 if (encode == E_NONE) {
986 else if (encode == E_URL) {
987 ap_rputs(ap_escape_uri(r->pool, val), r);
989 else if (encode == E_ENTITY) {
990 ap_rputs(ap_escape_html(r->pool, val), r);
994 ap_rputs("(none)", r);
997 else if (!strcmp(tag, "done")) {
1000 else if (!strcmp(tag, "encoding")) {
1001 if (!strcasecmp(tag_val, "none")) encode = E_NONE;
1002 else if (!strcasecmp(tag_val, "url")) encode = E_URL;
1003 else if (!strcasecmp(tag_val, "entity")) encode = E_ENTITY;
1005 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1006 "unknown value \"%s\" to parameter \"encoding\" of "
1008 tag_val, r->filename);
1014 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1015 "unknown parameter \"%s\" to tag echo in %s",
1023 static int handle_perl(ap_file_t *in, request_rec *r, const char *error)
1025 char tag[MAX_STRING_LEN];
1026 char parsed_string[MAX_STRING_LEN];
1031 if (ap_allow_options(r) & OPT_INCNOEXEC) {
1032 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1033 "#perl SSI disallowed by IncludesNoExec in %s",
1038 if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
1041 if (strnEQ(tag, "sub", 3)) {
1042 sub = newSVpv(tag_val, 0);
1044 else if (strnEQ(tag, "arg", 3)) {
1045 parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
1046 av_push(av, newSVpv(parsed_string, 0));
1048 else if (strnEQ(tag, "done", 4)) {
1052 perl_stdout2client(r);
1054 perl_call_handler(sub, r, av);
1059 /* error and tf must point to a string with room for at
1060 * least MAX_STRING_LEN characters
1062 static int handle_config(ap_file_t *in, request_rec *r, char *error, char *tf,
1065 char tag[MAX_STRING_LEN];
1067 char parsed_string[MAX_STRING_LEN];
1068 ap_table_t *env = r->subprocess_env;
1071 if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0))) {
1074 if (!strcmp(tag, "errmsg")) {
1075 parse_string(r, tag_val, error, MAX_STRING_LEN, 0);
1077 else if (!strcmp(tag, "timefmt")) {
1078 ap_time_t date = r->request_time;
1080 parse_string(r, tag_val, tf, MAX_STRING_LEN, 0);
1081 ap_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, tf, 0));
1082 ap_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, tf, 1));
1083 ap_table_setn(env, "LAST_MODIFIED",
1084 ap_ht_time(r->pool, r->finfo.mtime, tf, 0));
1086 else if (!strcmp(tag, "sizefmt")) {
1087 parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
1088 decodehtml(parsed_string);
1089 if (!strcmp(parsed_string, "bytes")) {
1090 *sizefmt = SIZEFMT_BYTES;
1092 else if (!strcmp(parsed_string, "abbrev")) {
1093 *sizefmt = SIZEFMT_KMG;
1096 else if (!strcmp(tag, "done")) {
1100 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1101 "unknown parameter \"%s\" to tag config in %s",
1109 static int find_file(request_rec *r, const char *directive, const char *tag,
1110 char *tag_val, ap_finfo_t *finfo, const char *error)
1112 char *to_send = tag_val;
1113 request_rec *rr = NULL;
1115 char *error_fmt = NULL;
1117 if (!strcmp(tag, "file")) {
1118 /* be safe; only files in this directory or below allowed */
1119 if (!is_only_below(tag_val)) {
1120 error_fmt = "unable to access file \"%s\" "
1121 "in parsed file %s";
1124 ap_getparents(tag_val); /* get rid of any nasties */
1125 rr = ap_sub_req_lookup_file(tag_val, r);
1127 if (rr->status == HTTP_OK && rr->finfo.protection != 0) {
1128 to_send = rr->filename;
1129 if (ap_stat(finfo, to_send, rr->pool) != APR_SUCCESS) {
1130 error_fmt = "unable to get information about \"%s\" "
1131 "in parsed file %s";
1135 error_fmt = "unable to lookup information about \"%s\" "
1136 "in parsed file %s";
1142 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, to_send, r->filename);
1146 if (rr) ap_destroy_sub_req(rr);
1150 else if (!strcmp(tag, "virtual")) {
1151 rr = ap_sub_req_lookup_uri(tag_val, r);
1153 if (rr->status == HTTP_OK && rr->finfo.protection != 0) {
1154 memcpy((char *) finfo, (const char *) &rr->finfo,
1156 ap_destroy_sub_req(rr);
1160 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1161 "unable to get information about \"%s\" "
1162 "in parsed file %s",
1163 tag_val, r->filename);
1165 ap_destroy_sub_req(rr);
1170 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1171 "unknown parameter \"%s\" to tag %s in %s",
1172 tag, directive, r->filename);
1179 static int handle_fsize(ap_file_t *in, request_rec *r, const char *error, int sizefmt)
1181 char tag[MAX_STRING_LEN];
1184 char parsed_string[MAX_STRING_LEN];
1187 if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
1190 else if (!strcmp(tag, "done")) {
1194 parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
1195 if (!find_file(r, "fsize", tag, parsed_string, &finfo, error)) {
1196 if (sizefmt == SIZEFMT_KMG) {
1197 ap_send_size(finfo.size, r);
1201 ap_snprintf(tag, sizeof(tag), "%" APR_OFF_T_FMT, finfo.size);
1202 l = strlen(tag); /* grrr */
1203 for (x = 0; x < l; x++) {
1204 if (x && (!((l - x) % 3))) {
1207 ap_rputc(tag[x], r);
1215 static int handle_flastmod(ap_file_t *in, request_rec *r, const char *error, const char *tf)
1217 char tag[MAX_STRING_LEN];
1220 char parsed_string[MAX_STRING_LEN];
1223 if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
1226 else if (!strcmp(tag, "done")) {
1230 parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
1231 if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) {
1232 ap_rputs(ap_ht_time(r->pool, finfo.mtime, tf, 0), r);
1238 static int re_check(request_rec *r, char *string, char *rexp)
1243 compiled = ap_pregcomp(r->pool, rexp, REG_EXTENDED | REG_NOSUB);
1244 if (compiled == NULL) {
1245 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1246 "unable to compile pattern \"%s\"", rexp);
1249 regex_error = ap_regexec(compiled, string, 0, (regmatch_t *) NULL, 0);
1250 ap_pregfree(r->pool, compiled);
1251 return (!regex_error);
1256 token_and, token_or, token_not, token_eq, token_ne,
1257 token_rbrace, token_lbrace, token_group,
1258 token_ge, token_le, token_gt, token_lt
1261 enum token_type type;
1262 char value[MAX_STRING_LEN];
1265 /* there is an implicit assumption here that string is at most MAX_STRING_LEN-1
1266 * characters long...
1268 static const char *get_ptoken(request_rec *r, const char *string, struct token *token)
1274 /* Skip leading white space */
1275 if (string == (char *) NULL) {
1276 return (char *) NULL;
1278 while ((ch = *string++)) {
1279 if (!ap_isspace(ch)) {
1284 return (char *) NULL;
1287 token->type = token_string; /* the default type */
1290 token->type = token_lbrace;
1293 token->type = token_rbrace;
1296 token->type = token_eq;
1299 if (*string == '=') {
1300 token->type = token_ne;
1301 return (string + 1);
1304 token->type = token_not;
1308 token->type = token_string;
1312 if (*string == '|') {
1313 token->type = token_or;
1314 return (string + 1);
1318 if (*string == '&') {
1319 token->type = token_and;
1320 return (string + 1);
1324 if (*string == '=') {
1325 token->type = token_ge;
1326 return (string + 1);
1329 token->type = token_gt;
1333 if (*string == '=') {
1334 token->type = token_le;
1335 return (string + 1);
1338 token->type = token_lt;
1342 token->type = token_string;
1345 /* We should only be here if we are in a string */
1347 token->value[next++] = ch;
1351 * Yes I know that goto's are BAD. But, c doesn't allow me to
1352 * exit a loop from a switch statement. Yes, I could use a flag,
1353 * but that is (IMHO) even less readable/maintainable than the goto.
1356 * I used the ++string throughout this section so that string
1357 * ends up pointing to the next token and I can just return it
1359 for (ch = *string; ch != '\0'; ch = *++string) {
1361 if ((ch = *++string) == '\0') {
1364 token->value[next++] = ch;
1368 if (ap_isspace(ch)) {
1381 if (*(string + 1) == '|') {
1386 if (*(string + 1) == '&') {
1395 token->value[next++] = ch;
1403 token->value[next++] = ch;
1407 /* If qs is still set, I have an unmatched ' */
1409 ap_rputs("\nUnmatched '\n", r);
1412 token->value[next] = '\0';
1418 * Hey I still know that goto's are BAD. I don't think that I've ever
1419 * used two in the same project, let alone the same file before. But,
1420 * I absolutely want to make sure that I clean up the memory in all
1421 * cases. And, without rewriting this completely, the easiest way
1422 * is to just branch to the return code which cleans it up.
1424 /* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1
1425 * characters long...
1427 static int parse_expr(request_rec *r, const char *expr, const char *error)
1430 struct parse_node *left, *right, *parent;
1433 } *root, *current, *new;
1435 char buffer[MAX_STRING_LEN];
1436 ap_pool_t *expr_pool;
1439 if ((parse = expr) == (char *) NULL) {
1442 root = current = (struct parse_node *) NULL;
1443 if (ap_create_pool(&expr_pool, r->pool) != APR_SUCCESS)
1446 /* Create Parse Tree */
1448 new = (struct parse_node *) ap_palloc(expr_pool,
1449 sizeof(struct parse_node));
1450 new->parent = new->left = new->right = (struct parse_node *) NULL;
1452 if ((parse = get_ptoken(r, parse, &new->token)) == (char *) NULL) {
1455 switch (new->token.type) {
1458 #ifdef DEBUG_INCLUDE
1459 ap_rvputs(r, " Token: string (", new->token.value, ")\n", NULL);
1461 if (current == (struct parse_node *) NULL) {
1462 root = current = new;
1465 switch (current->token.type) {
1467 if (current->token.value[0] != '\0') {
1468 strncat(current->token.value, " ",
1469 sizeof(current->token.value)
1470 - strlen(current->token.value) - 1);
1472 strncat(current->token.value, new->token.value,
1473 sizeof(current->token.value)
1474 - strlen(current->token.value) - 1);
1475 current->token.value[sizeof(current->token.value) - 1] = '\0';
1487 new->parent = current;
1488 current = current->right = new;
1491 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1492 "Invalid expression \"%s\" in file %s",
1501 #ifdef DEBUG_INCLUDE
1502 ap_rputs(" Token: and/or\n", r);
1504 if (current == (struct parse_node *) NULL) {
1505 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1506 "Invalid expression \"%s\" in file %s",
1511 /* Percolate upwards */
1512 while (current != (struct parse_node *) NULL) {
1513 switch (current->token.type) {
1525 current = current->parent;
1530 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1531 "Invalid expression \"%s\" in file %s",
1538 if (current == (struct parse_node *) NULL) {
1540 new->left->parent = new;
1541 new->parent = (struct parse_node *) NULL;
1545 new->left = current->right;
1546 current->right = new;
1547 new->parent = current;
1553 #ifdef DEBUG_INCLUDE
1554 ap_rputs(" Token: not\n", r);
1556 if (current == (struct parse_node *) NULL) {
1557 root = current = new;
1560 /* Percolate upwards */
1561 while (current != (struct parse_node *) NULL) {
1562 switch (current->token.type) {
1575 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1576 "Invalid expression \"%s\" in file %s",
1583 if (current == (struct parse_node *) NULL) {
1585 new->left->parent = new;
1586 new->parent = (struct parse_node *) NULL;
1590 new->left = current->right;
1591 current->right = new;
1592 new->parent = current;
1603 #ifdef DEBUG_INCLUDE
1604 ap_rputs(" Token: eq/ne/ge/gt/le/lt\n", r);
1606 if (current == (struct parse_node *) NULL) {
1607 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1608 "Invalid expression \"%s\" in file %s",
1613 /* Percolate upwards */
1614 while (current != (struct parse_node *) NULL) {
1615 switch (current->token.type) {
1618 current = current->parent;
1632 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1633 "Invalid expression \"%s\" in file %s",
1640 if (current == (struct parse_node *) NULL) {
1642 new->left->parent = new;
1643 new->parent = (struct parse_node *) NULL;
1647 new->left = current->right;
1648 current->right = new;
1649 new->parent = current;
1655 #ifdef DEBUG_INCLUDE
1656 ap_rputs(" Token: rbrace\n", r);
1658 while (current != (struct parse_node *) NULL) {
1659 if (current->token.type == token_lbrace) {
1660 current->token.type = token_group;
1663 current = current->parent;
1665 if (current == (struct parse_node *) NULL) {
1666 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1667 "Unmatched ')' in \"%s\" in file %s",
1675 #ifdef DEBUG_INCLUDE
1676 ap_rputs(" Token: lbrace\n", r);
1678 if (current == (struct parse_node *) NULL) {
1679 root = current = new;
1682 /* Percolate upwards */
1683 while (current != (struct parse_node *) NULL) {
1684 switch (current->token.type) {
1699 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1700 "Invalid expression \"%s\" in file %s",
1707 if (current == (struct parse_node *) NULL) {
1709 new->left->parent = new;
1710 new->parent = (struct parse_node *) NULL;
1714 new->left = current->right;
1715 current->right = new;
1716 new->parent = current;
1725 /* Evaluate Parse Tree */
1727 while (current != (struct parse_node *) NULL) {
1728 switch (current->token.type) {
1730 #ifdef DEBUG_INCLUDE
1731 ap_rputs(" Evaluate string\n", r);
1733 parse_string(r, current->token.value, buffer, sizeof(buffer), 0);
1734 ap_cpystrn(current->token.value, buffer, sizeof(current->token.value));
1735 current->value = (current->token.value[0] != '\0');
1737 current = current->parent;
1742 #ifdef DEBUG_INCLUDE
1743 ap_rputs(" Evaluate and/or\n", r);
1745 if (current->left == (struct parse_node *) NULL ||
1746 current->right == (struct parse_node *) NULL) {
1747 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1748 "Invalid expression \"%s\" in file %s",
1753 if (!current->left->done) {
1754 switch (current->left->token.type) {
1756 parse_string(r, current->left->token.value,
1757 buffer, sizeof(buffer), 0);
1758 ap_cpystrn(current->left->token.value, buffer,
1759 sizeof(current->left->token.value));
1760 current->left->value = (current->left->token.value[0] != '\0');
1761 current->left->done = 1;
1764 current = current->left;
1768 if (!current->right->done) {
1769 switch (current->right->token.type) {
1771 parse_string(r, current->right->token.value,
1772 buffer, sizeof(buffer), 0);
1773 ap_cpystrn(current->right->token.value, buffer,
1774 sizeof(current->right->token.value));
1775 current->right->value = (current->right->token.value[0] != '\0');
1776 current->right->done = 1;
1779 current = current->right;
1783 #ifdef DEBUG_INCLUDE
1784 ap_rvputs(r, " Left: ", current->left->value ? "1" : "0",
1786 ap_rvputs(r, " Right: ", current->right->value ? "1" : "0",
1789 if (current->token.type == token_and) {
1790 current->value = current->left->value && current->right->value;
1793 current->value = current->left->value || current->right->value;
1795 #ifdef DEBUG_INCLUDE
1796 ap_rvputs(r, " Returning ", current->value ? "1" : "0",
1800 current = current->parent;
1805 #ifdef DEBUG_INCLUDE
1806 ap_rputs(" Evaluate eq/ne\n", r);
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, 0, r,
1813 "Invalid expression \"%s\" in file %s",
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 if (current->right->token.value[0] == '/') {
1828 len = strlen(current->right->token.value);
1829 if (current->right->token.value[len - 1] == '/') {
1830 current->right->token.value[len - 1] = '\0';
1833 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1834 "Invalid rexp \"%s\" in file %s",
1835 current->right->token.value, r->filename);
1839 #ifdef DEBUG_INCLUDE
1840 ap_rvputs(r, " Re Compare (", current->left->token.value,
1841 ") with /", ¤t->right->token.value[1], "/\n", NULL);
1844 re_check(r, current->left->token.value,
1845 ¤t->right->token.value[1]);
1848 #ifdef DEBUG_INCLUDE
1849 ap_rvputs(r, " Compare (", current->left->token.value,
1850 ") with (", current->right->token.value, ")\n", NULL);
1853 (strcmp(current->left->token.value,
1854 current->right->token.value) == 0);
1856 if (current->token.type == token_ne) {
1857 current->value = !current->value;
1859 #ifdef DEBUG_INCLUDE
1860 ap_rvputs(r, " Returning ", current->value ? "1" : "0",
1864 current = current->parent;
1870 #ifdef DEBUG_INCLUDE
1871 ap_rputs(" Evaluate ge/gt/le/lt\n", r);
1873 if ((current->left == (struct parse_node *) NULL) ||
1874 (current->right == (struct parse_node *) NULL) ||
1875 (current->left->token.type != token_string) ||
1876 (current->right->token.type != token_string)) {
1877 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1878 "Invalid expression \"%s\" in file %s",
1883 parse_string(r, current->left->token.value,
1884 buffer, sizeof(buffer), 0);
1885 ap_cpystrn(current->left->token.value, buffer,
1886 sizeof(current->left->token.value));
1887 parse_string(r, current->right->token.value,
1888 buffer, sizeof(buffer), 0);
1889 ap_cpystrn(current->right->token.value, buffer,
1890 sizeof(current->right->token.value));
1891 #ifdef DEBUG_INCLUDE
1892 ap_rvputs(r, " Compare (", current->left->token.value,
1893 ") with (", current->right->token.value, ")\n", NULL);
1896 strcmp(current->left->token.value,
1897 current->right->token.value);
1898 if (current->token.type == token_ge) {
1899 current->value = current->value >= 0;
1901 else if (current->token.type == token_gt) {
1902 current->value = current->value > 0;
1904 else if (current->token.type == token_le) {
1905 current->value = current->value <= 0;
1907 else if (current->token.type == token_lt) {
1908 current->value = current->value < 0;
1911 current->value = 0; /* Don't return -1 if unknown token */
1913 #ifdef DEBUG_INCLUDE
1914 ap_rvputs(r, " Returning ", current->value ? "1" : "0",
1918 current = current->parent;
1922 if (current->right != (struct parse_node *) NULL) {
1923 if (!current->right->done) {
1924 current = current->right;
1927 current->value = !current->right->value;
1932 #ifdef DEBUG_INCLUDE
1933 ap_rvputs(r, " Evaluate !: ", current->value ? "1" : "0",
1937 current = current->parent;
1941 if (current->right != (struct parse_node *) NULL) {
1942 if (!current->right->done) {
1943 current = current->right;
1946 current->value = current->right->value;
1951 #ifdef DEBUG_INCLUDE
1952 ap_rvputs(r, " Evaluate (): ", current->value ? "1" : "0",
1956 current = current->parent;
1960 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1961 "Unmatched '(' in \"%s\" in file %s",
1967 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1968 "Unmatched ')' in \"%s\" in file %s",
1974 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
1981 retval = (root == (struct parse_node *) NULL) ? 0 : root->value;
1983 ap_destroy_pool(expr_pool);
1987 static int handle_if(ap_file_t *in, request_rec *r, const char *error,
1988 int *conditional_status, int *printing)
1990 char tag[MAX_STRING_LEN];
1996 tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0);
2000 else if (!strcmp(tag, "done")) {
2002 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2003 "missing expr in if statement: %s",
2008 *printing = *conditional_status = parse_expr(r, expr, error);
2009 #ifdef DEBUG_INCLUDE
2010 ap_rvputs(r, "**** if conditional_status=\"",
2011 *conditional_status ? "1" : "0", "\"\n", NULL);
2015 else if (!strcmp(tag, "expr")) {
2017 #ifdef DEBUG_INCLUDE
2018 ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL);
2022 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2023 "unknown parameter \"%s\" to tag if in %s",
2030 static int handle_elif(ap_file_t *in, request_rec *r, const char *error,
2031 int *conditional_status, int *printing)
2033 char tag[MAX_STRING_LEN];
2039 tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0);
2043 else if (!strcmp(tag, "done")) {
2044 #ifdef DEBUG_INCLUDE
2045 ap_rvputs(r, "**** elif conditional_status=\"",
2046 *conditional_status ? "1" : "0", "\"\n", NULL);
2048 if (*conditional_status) {
2053 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2054 "missing expr in elif statement: %s",
2059 *printing = *conditional_status = parse_expr(r, expr, error);
2060 #ifdef DEBUG_INCLUDE
2061 ap_rvputs(r, "**** elif conditional_status=\"",
2062 *conditional_status ? "1" : "0", "\"\n", NULL);
2066 else if (!strcmp(tag, "expr")) {
2068 #ifdef DEBUG_INCLUDE
2069 ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL);
2073 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2074 "unknown parameter \"%s\" to tag if in %s",
2081 static int handle_else(ap_file_t *in, request_rec *r, const char *error,
2082 int *conditional_status, int *printing)
2084 char tag[MAX_STRING_LEN];
2086 if (!get_tag(r->pool, in, tag, sizeof(tag), 1)) {
2089 else if (!strcmp(tag, "done")) {
2090 #ifdef DEBUG_INCLUDE
2091 ap_rvputs(r, "**** else conditional_status=\"",
2092 *conditional_status ? "1" : "0", "\"\n", NULL);
2094 *printing = !(*conditional_status);
2095 *conditional_status = 1;
2099 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2100 "else directive does not take tags in %s",
2109 static int handle_endif(ap_file_t *in, request_rec *r, const char *error,
2110 int *conditional_status, int *printing)
2112 char tag[MAX_STRING_LEN];
2114 if (!get_tag(r->pool, in, tag, sizeof(tag), 1)) {
2117 else if (!strcmp(tag, "done")) {
2118 #ifdef DEBUG_INCLUDE
2119 ap_rvputs(r, "**** endif conditional_status=\"",
2120 *conditional_status ? "1" : "0", "\"\n", NULL);
2123 *conditional_status = 1;
2127 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2128 "endif directive does not take tags in %s",
2135 static int handle_set(ap_file_t *in, request_rec *r, const char *error)
2137 char tag[MAX_STRING_LEN];
2138 char parsed_string[MAX_STRING_LEN];
2142 var = (char *) NULL;
2144 if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
2147 else if (!strcmp(tag, "done")) {
2150 else if (!strcmp(tag, "var")) {
2153 else if (!strcmp(tag, "value")) {
2154 if (var == (char *) NULL) {
2155 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2156 "variable must precede value in set directive in %s",
2161 parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
2162 ap_table_setn(r->subprocess_env, var, ap_pstrdup(r->pool, parsed_string));
2165 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2166 "Invalid tag for set directive in %s", r->filename);
2173 static int handle_printenv(ap_file_t *in, request_rec *r, const char *error)
2175 char tag[MAX_STRING_LEN];
2177 ap_array_header_t *arr = ap_table_elts(r->subprocess_env);
2178 ap_table_entry_t *elts = (ap_table_entry_t *)arr->elts;
2181 if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
2184 else if (!strcmp(tag, "done")) {
2185 for (i = 0; i < arr->nelts; ++i) {
2186 ap_rvputs(r, ap_escape_html(r->pool, elts[i].key), "=",
2187 ap_escape_html(r->pool, elts[i].val), "\n", NULL);
2192 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2193 "printenv directive does not take tags in %s",
2202 /* -------------------------- The main function --------------------------- */
2204 /* This is a stub which parses a file descriptor. */
2206 static void send_parsed_content(ap_file_t *f, request_rec *r)
2208 char directive[MAX_STRING_LEN], error[MAX_STRING_LEN];
2209 char timefmt[MAX_STRING_LEN];
2210 int noexec = ap_allow_options(r) & OPT_INCNOEXEC;
2214 int conditional_status;
2216 ap_cpystrn(error, DEFAULT_ERROR_MSG, sizeof(error));
2217 ap_cpystrn(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt));
2218 sizefmt = SIZEFMT_KMG;
2220 /* Turn printing on */
2221 printing = conditional_status = 1;
2224 ap_chdir_file(r->filename);
2225 if (r->args) { /* add QUERY stuff to env cause it ain't yet */
2226 char *arg_copy = ap_pstrdup(r->pool, r->args);
2228 ap_table_setn(r->subprocess_env, "QUERY_STRING", r->args);
2229 ap_unescape_url(arg_copy);
2230 ap_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED",
2231 ap_escape_shell_cmd(r->pool, arg_copy));
2235 if (!find_string(f, STARTING_SEQUENCE, r, printing)) {
2236 if (get_directive(f, directive, sizeof(directive), r->pool)) {
2237 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2238 "mod_include: error reading directive in %s",
2243 if (!strcmp(directive, "if")) {
2248 ret = handle_if(f, r, error, &conditional_status,
2254 else if (!strcmp(directive, "else")) {
2256 ret = handle_else(f, r, error, &conditional_status,
2261 else if (!strcmp(directive, "elif")) {
2263 ret = handle_elif(f, r, error, &conditional_status,
2268 else if (!strcmp(directive, "endif")) {
2270 ret = handle_endif(f, r, error, &conditional_status,
2281 if (!strcmp(directive, "exec")) {
2283 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2284 "exec used but not allowed in %s",
2289 ret = find_string(f, ENDING_SEQUENCE, r, 0);
2292 ret = handle_exec(f, r, error);
2295 else if (!strcmp(directive, "config")) {
2296 ret = handle_config(f, r, error, timefmt, &sizefmt);
2298 else if (!strcmp(directive, "set")) {
2299 ret = handle_set(f, r, error);
2301 else if (!strcmp(directive, "include")) {
2302 ret = handle_include(f, r, error, noexec);
2304 else if (!strcmp(directive, "echo")) {
2305 ret = handle_echo(f, r, error);
2307 else if (!strcmp(directive, "fsize")) {
2308 ret = handle_fsize(f, r, error, sizefmt);
2310 else if (!strcmp(directive, "flastmod")) {
2311 ret = handle_flastmod(f, r, error, timefmt);
2313 else if (!strcmp(directive, "printenv")) {
2314 ret = handle_printenv(f, r, error);
2317 else if (!strcmp(directive, "perl")) {
2318 ret = handle_perl(f, r, error);
2322 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2323 "unknown directive \"%s\" "
2325 directive, r->filename);
2329 ret = find_string(f, ENDING_SEQUENCE, r, 0);
2332 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2333 "premature EOF in parsed file %s",
2344 /*****************************************************************
2346 * XBITHACK. Sigh... NB it's configurable per-directory; the compile-time
2347 * option only changes the default.
2350 module includes_module;
2352 xbithack_off, xbithack_on, xbithack_full
2356 #define DEFAULT_XBITHACK xbithack_full
2358 #define DEFAULT_XBITHACK xbithack_off
2361 static void *create_includes_dir_config(ap_pool_t *p, char *dummy)
2363 enum xbithack *result = (enum xbithack *) ap_palloc(p, sizeof(enum xbithack));
2364 *result = DEFAULT_XBITHACK;
2368 static const char *set_xbithack(cmd_parms *cmd, void *xbp, const char *arg)
2370 enum xbithack *state = (enum xbithack *) xbp;
2372 if (!strcasecmp(arg, "off")) {
2373 *state = xbithack_off;
2375 else if (!strcasecmp(arg, "on")) {
2376 *state = xbithack_on;
2378 else if (!strcasecmp(arg, "full")) {
2379 *state = xbithack_full;
2382 return "XBitHack must be set to Off, On, or Full";
2388 static int send_parsed_file(request_rec *r)
2390 ap_file_t *f = NULL;
2391 enum xbithack *state =
2392 (enum xbithack *) ap_get_module_config(r->per_dir_config, &includes_module);
2394 request_rec *parent;
2396 if (!(ap_allow_options(r) & OPT_INCLUDES)) {
2399 r->allowed |= (1 << M_GET);
2400 if (r->method_number != M_GET) {
2403 if (r->finfo.protection == 0) {
2404 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
2405 "File does not exist: %s",
2407 ? ap_pstrcat(r->pool, r->filename, r->path_info, NULL)
2409 return HTTP_NOT_FOUND;
2412 errstatus = ap_open(&f, r->filename, APR_READ|APR_BUFFERED, 0, r->pool);
2414 if (errstatus != APR_SUCCESS) {
2415 ap_log_rerror(APLOG_MARK, APLOG_ERR, errstatus, r,
2416 "file permissions deny server access: %s", r->filename);
2417 return HTTP_FORBIDDEN;
2420 if ((*state == xbithack_full)
2421 #if !defined(OS2) && !defined(WIN32)
2422 /* OS/2 dosen't support Groups. */
2423 && (r->finfo.protection & APR_GEXECUTE)
2426 ap_update_mtime(r, r->finfo.mtime);
2427 ap_set_last_modified(r);
2429 if ((errstatus = ap_meets_conditions(r)) != OK) {
2433 ap_send_http_header(r);
2435 if (r->header_only) {
2440 if ((parent = ap_get_module_config(r->request_config, &includes_module))) {
2441 /* Kludge --- for nested includes, we want to keep the subprocess
2442 * environment of the base document (for compatibility); that means
2443 * torquing our own last_modified date as well so that the
2444 * LAST_MODIFIED variable gets reset to the proper value if the
2445 * nested document resets <!--#config timefmt-->.
2446 * We also insist that the memory for this subrequest not be
2447 * destroyed, that's dealt with in handle_include().
2449 r->subprocess_env = parent->subprocess_env;
2450 r->finfo.mtime = parent->finfo.mtime;
2453 /* we're not a nested include, so we create an initial
2455 ap_add_common_vars(r);
2457 add_include_vars(r, DEFAULT_TIME_FORMAT);
2459 /* XXX: this is bogus, at some point we're going to do a subrequest,
2460 * and when we do it we're going to be subjecting code that doesn't
2461 * expect to be signal-ready to SIGALRM. There is no clean way to
2462 * fix this, except to put alarm support into BUFF. -djg
2464 #ifdef CHARSET_EBCDIC
2465 /* XXX:@@@ Is the generated/included output ALWAYS in text/ebcdic format? */
2466 ap_bsetopt(r->connection->client, BO_WXLATE, &ap_hdrs_to_ascii);
2469 send_parsed_content(f, r);
2472 /* signify that the sub request should not be killed */
2473 ap_set_module_config(r->request_config, &includes_module,
2474 NESTED_INCLUDE_MAGIC);
2480 static int send_shtml_file(request_rec *r)
2482 r->content_type = "text/html";
2483 return send_parsed_file(r);
2486 static int xbithack_handler(request_rec *r)
2488 #if defined(OS2) || defined(WIN32)
2489 /* OS/2 dosen't currently support the xbithack. This is being worked on. */
2492 enum xbithack *state;
2494 if (!(r->finfo.protection & APR_UEXECUTE)) {
2498 state = (enum xbithack *) ap_get_module_config(r->per_dir_config,
2501 if (*state == xbithack_off) {
2504 return send_parsed_file(r);
2508 static const command_rec includes_cmds[] =
2510 AP_INIT_TAKE1("XBitHack", set_xbithack, NULL, OR_OPTIONS,
2511 "Off, On, or Full"),
2515 static const handler_rec includes_handlers[] =
2517 {INCLUDES_MAGIC_TYPE, send_shtml_file},
2518 {INCLUDES_MAGIC_TYPE3, send_shtml_file},
2519 {"server-parsed", send_parsed_file},
2520 {"text/html", xbithack_handler},
2524 module MODULE_VAR_EXPORT includes_module =
2526 STANDARD20_MODULE_STUFF,
2527 create_includes_dir_config, /* dir config creater */
2528 NULL, /* dir merger --- default is to override */
2529 NULL, /* server config */
2530 NULL, /* merge server config */
2531 includes_cmds, /* command ap_table_t */
2532 includes_handlers, /* handlers */
2533 NULL /* register hooks */