#ifdef USE_PERL_SSI
#include "config.h"
#undef VOIDUSED
#ifdef USE_SFIO
#undef USE_SFIO
#define USE_STDIO
#endif
#include "modules/perl/mod_perl.h"
#else
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_log.h"
#include "http_main.h"
#include "util_script.h"
#endif
#define STARTING_SEQUENCE "<!--#"
#define ENDING_SEQUENCE "-->"
#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
#define SIZEFMT_BYTES 0
#define SIZEFMT_KMG 1
#ifdef CHARSET_EBCDIC
#define RAW_ASCII_CHAR(ch) os_toebcdic[(unsigned char)ch]
#else /*CHARSET_EBCDIC*/
#define RAW_ASCII_CHAR(ch) (ch)
#endif /*CHARSET_EBCDIC*/
module MODULE_VAR_EXPORT includes_module;
static
void
add_include_vars(request_rec *r,
char
*timefmt)
{
#if !defined(WIN32) && !defined(NETWARE)
struct
passwd *pw;
#endif /* ndef WIN32 */
table *e = r->subprocess_env;
char
*t;
time_t
date = r->request_time;
ap_table_setn(e,
"DATE_LOCAL"
, ap_ht_time(r->pool, date, timefmt, 0));
ap_table_setn(e,
"DATE_GMT"
, ap_ht_time(r->pool, date, timefmt, 1));
ap_table_setn(e,
"LAST_MODIFIED"
,
ap_ht_time(r->pool, r->finfo.st_mtime, timefmt, 0));
ap_table_setn(e,
"DOCUMENT_URI"
, r->uri);
ap_table_setn(e,
"DOCUMENT_PATH_INFO"
, r->path_info);
#if !defined(WIN32) && !defined(NETWARE)
pw = getpwuid(r->finfo.st_uid);
if
(pw) {
ap_table_setn(e,
"USER_NAME"
, ap_pstrdup(r->pool, pw->pw_name));
}
else
{
ap_table_setn(e,
"USER_NAME"
, ap_psprintf(r->pool,
"user#%lu"
,
(unsigned
long
) r->finfo.st_uid));
}
#endif /* ndef WIN32 */
if
((t =
strrchr
(r->filename,
'/'
))) {
ap_table_setn(e,
"DOCUMENT_NAME"
, ++t);
}
else
{
ap_table_setn(e,
"DOCUMENT_NAME"
, r->uri);
}
if
(r->args) {
char
*arg_copy = ap_pstrdup(r->pool, r->args);
ap_unescape_url(arg_copy);
ap_table_setn(e,
"QUERY_STRING_UNESCAPED"
,
ap_escape_shell_cmd(r->pool, arg_copy));
}
}
#define OUTBUFSIZE 4096
#define PUT_CHAR(c,r) \
{ \
outbuf[outind++] = c; \
if
(outind == OUTBUFSIZE) { \
FLUSH_BUF(r) \
}; \
}
#define FLUSH_BUF(r) \
{ \
ap_rwrite(outbuf, outind, r); \
outind = 0; \
}
#define GET_CHAR(f,c,ret,r) \
{ \
int
i =
getc
(f); \
if
(i == EOF) {
\
if
(
ferror
(f)) { \
fprintf
(stderr,
"encountered error in GET_CHAR macro, "
\
"mod_include.\n"
); \
} \
FLUSH_BUF(r); \
ap_pfclose(r->pool, f); \
return
ret; \
} \
c = (
char
)i; \
}
static
int
find_string(
FILE
*in,
const
char
*str, request_rec *r,
int
printing)
{
int
x, l =
strlen
(str), p;
char
outbuf[OUTBUFSIZE];
int
outind = 0;
char
c;
p = 0;
while
(1) {
GET_CHAR(in, c, 1, r);
if
(c == str[p]) {
if
((++p) == l) {
FLUSH_BUF(r);
return
0;
}
}
else
{
if
(printing) {
for
(x = 0; x < p; x++) {
PUT_CHAR(str[x], r);
}
PUT_CHAR(c, r);
}
p = 0;
}
}
}
#undef FLUSH_BUF
#undef PUT_CHAR
#undef GET_CHAR
#define GET_CHAR(f,c,r,p) \
{ \
int
i =
getc
(f); \
if
(i == EOF) {
\
if
(
ferror
(f)) { \
fprintf
(stderr,
"encountered error in GET_CHAR macro, "
\
"mod_include.\n"
); \
} \
ap_pfclose(p, f); \
return
r; \
} \
c = (
char
)i; \
}
#define MAXENTLEN (6)
static
void
decodehtml(
char
*s)
{
int
val, i, j;
char
*p = s;
const
char
*ents;
static
const
char
*
const
entlist[MAXENTLEN + 1] =
{
NULL,
NULL,
"lt\074gt\076"
,
"amp\046ETH\320eth\360"
,
"quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\
iuml\357ouml\366uuml\374yuml\377",
"Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\
THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\
ucirc\373thorn\376",
"Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\
Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\
Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\
egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\
otilde\365oslash\370ugrave\371uacute\372yacute\375"
};
for
(; *s !=
'\0'
; s++, p++) {
if
(*s !=
'&'
) {
*p = *s;
continue
;
}
for
(i = 1; s[i] !=
';'
&& s[i] !=
'\0'
; i++) {
continue
;
}
if
(s[i] ==
'\0'
) {
*p = *s;
continue
;
}
if
(s[1] ==
'#'
) {
for
(j = 2, val = 0; j < i && ap_isdigit(s[j]); j++) {
val = val * 10 + s[j] -
'0'
;
}
s += i;
if
(j < i || val <= 8 || (val >= 11 && val <= 31) ||
(val >= 127 && val <= 160) || val >= 256) {
p--;
}
else
{
*p = RAW_ASCII_CHAR(val);
}
}
else
{
j = i - 1;
if
(j > MAXENTLEN || entlist[j] == NULL) {
*p =
'&'
;
continue
;
}
for
(ents = entlist[j]; *ents !=
'\0'
; ents += i) {
if
(
strncmp
(s + 1, ents, j) == 0) {
break
;
}
}
if
(*ents ==
'\0'
) {
*p =
'&'
;
}
else
{
*p = RAW_ASCII_CHAR(((
const
unsigned
char
*) ents)[j]);
s += i;
}
}
}
*p =
'\0'
;
}
static
char
*get_tag(pool *p,
FILE
*in,
char
*tag,
int
tagbuf_len,
int
dodecode)
{
char
*t = tag, *tag_val, c, term;
--tagbuf_len;
do
{
GET_CHAR(in, c, NULL, p);
}
while
(ap_isspace(c));
if
(c ==
'-'
) {
GET_CHAR(in, c, NULL, p);
if
(c ==
'-'
) {
do
{
GET_CHAR(in, c, NULL, p);
}
while
(ap_isspace(c));
if
(c ==
'>'
) {
ap_cpystrn(tag,
"done"
, tagbuf_len);
return
tag;
}
}
return
NULL;
}
while
(1) {
if
(t - tag == tagbuf_len) {
*t =
'\0'
;
return
NULL;
}
if
(c ==
'='
|| ap_isspace(c)) {
break
;
}
*(t++) = ap_tolower(c);
GET_CHAR(in, c, NULL, p);
}
*t++ =
'\0'
;
tag_val = t;
while
(ap_isspace(c)) {
GET_CHAR(in, c, NULL, p);
}
if
(c !=
'='
) {
ungetc
(c, in);
return
NULL;
}
do
{
GET_CHAR(in, c, NULL, p);
}
while
(ap_isspace(c));
if
(c !=
'"'
&& c !=
'\''
) {
return
NULL;
}
term = c;
while
(1) {
GET_CHAR(in, c, NULL, p);
if
(t - tag == tagbuf_len) {
*t =
'\0'
;
return
NULL;
}
if
(c ==
'\\'
) {
*(t++) = c;
GET_CHAR(in, c, NULL, p);
if
(c == term) {
*(--t) = c;
}
}
else
if
(c == term) {
break
;
}
*(t++) = c;
}
*t =
'\0'
;
if
(dodecode) {
decodehtml(tag_val);
}
return
ap_pstrdup(p, tag_val);
}
static
int
get_directive(
FILE
*in,
char
*dest,
size_t
len, pool *p)
{
char
*d = dest;
char
c;
--len;
while
(1) {
GET_CHAR(in, c, 1, p);
if
(!ap_isspace(c)) {
break
;
}
}
while
(1) {
if
(d == len + dest) {
return
1;
}
*d++ = ap_tolower(c);
GET_CHAR(in, c, 1, p);
if
(ap_isspace(c)) {
break
;
}
}
*d =
'\0'
;
return
0;
}
static
void
parse_string(request_rec *r,
const
char
*in,
char
*out,
size_t
length,
int
leave_name)
{
char
ch;
char
*next = out;
char
*end_out;
end_out = out + length - 1;
while
((ch = *in++) !=
'\0'
) {
switch
(ch) {
case
'\\'
:
if
(next == end_out) {
*next =
'\0'
;
return
;
}
if
(*in ==
'$'
) {
*next++ = *in++;
}
else
{
*next++ = ch;
}
break
;
case
'$'
:
{
char
var[MAX_STRING_LEN];
const
char
*start_of_var_name;
const
char
*end_of_var_name;
const
char
*expansion;
const
char
*val;
size_t
l;
expansion = in - 1;
if
(*in ==
'{'
) {
++in;
start_of_var_name = in;
in =
strchr
(in,
'}'
);
if
(in == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
r,
"Missing '}' on variable \"%s\""
,
expansion);
*next =
'\0'
;
return
;
}
end_of_var_name = in;
++in;
}
else
{
start_of_var_name = in;
while
(ap_isalnum(*in) || *in ==
'_'
) {
++in;
}
end_of_var_name = in;
}
l = end_of_var_name - start_of_var_name;
if
(l != 0) {
l = (l >
sizeof
(var) - 1) ? (
sizeof
(var) - 1) : l;
memcpy
(var, start_of_var_name, l);
var[l] =
'\0'
;
val = ap_table_get(r->subprocess_env, var);
if
(val) {
expansion = val;
l =
strlen
(expansion);
}
else
if
(leave_name) {
l = in - expansion;
}
else
{
break
;
}
}
else
{
l = 1;
}
l = (l + next > end_out) ? (end_out - next) : l;
memcpy
(next, expansion, l);
next += l;
break
;
}
default
:
if
(next == end_out) {
*next =
'\0'
;
return
;
}
*next++ = ch;
break
;
}
}
*next =
'\0'
;
return
;
}
static
int
include_cgi(
char
*s, request_rec *r)
{
request_rec *rr = ap_sub_req_lookup_uri(s, r);
int
rr_status;
if
(rr->status != HTTP_OK) {
return
-1;
}
if
((rr->path_info && rr->path_info[0]) || rr->args) {
return
-1;
}
if
(rr->finfo.st_mode == 0) {
return
-1;
}
rr->path_info = r->path_info;
rr->args = r->args;
rr->content_type = CGI_MAGIC_TYPE;
rr_status = ap_run_sub_req(rr);
if
(ap_is_HTTP_REDIRECT(rr_status)) {
const
char
*location = ap_table_get(rr->headers_out,
"Location"
);
location = ap_escape_html(rr->pool, location);
ap_rvputs(r,
"<A HREF=\""
, location,
"\">"
, location,
"</A>"
, NULL);
}
ap_destroy_sub_req(rr);
#if !defined(WIN32) && !defined(NETWARE)
ap_chdir_file(r->filename);
#endif
return
0;
}
static
int
is_only_below(
const
char
*path)
{
#ifdef HAVE_DRIVE_LETTERS
if
(path[1] ==
':'
)
return
0;
#endif
#ifdef NETWARE
if
(
strchr
(path,
':'
))
return
0;
#endif
if
(path[0] ==
'/'
) {
return
0;
}
if
(path[0] ==
'.'
&& path[1] ==
'.'
&& (path[2] ==
'\0'
|| path[2] ==
'/'
)) {
return
0;
}
while
(*path) {
if
(*path ==
'/'
&& path[1] ==
'.'
&& path[2] ==
'.'
&& (path[3] ==
'\0'
|| path[3] ==
'/'
)) {
return
0;
}
++path;
}
return
1;
}
static
int
handle_include(
FILE
*in, request_rec *r,
const
char
*error,
int
noexec)
{
char
tag[MAX_STRING_LEN];
char
parsed_string[MAX_STRING_LEN];
char
*tag_val;
while
(1) {
if
(!(tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 1))) {
return
1;
}
if
(!
strcmp
(tag,
"file"
) || !
strcmp
(tag,
"virtual"
)) {
request_rec *rr = NULL;
char
*error_fmt = NULL;
parse_string(r, tag_val, parsed_string,
sizeof
(parsed_string), 0);
if
(tag[0] ==
'f'
) {
if
(!is_only_below(parsed_string)) {
error_fmt =
"unable to include file \"%s\" "
"in parsed file %s"
;
}
else
{
rr = ap_sub_req_lookup_file(parsed_string, r);
}
}
else
{
rr = ap_sub_req_lookup_uri(parsed_string, r);
}
if
(!error_fmt && rr->status != HTTP_OK) {
error_fmt =
"unable to include \"%s\" in parsed file %s"
;
}
if
(!error_fmt && noexec && rr->content_type
&& (
strncmp
(rr->content_type,
"text/"
, 5))) {
error_fmt =
"unable to include potential exec \"%s\" "
"in parsed file %s"
;
}
if
(error_fmt == NULL) {
int
founddupe = 0;
request_rec *p;
for
(p = r; p != NULL && !founddupe; p = p->main) {
request_rec *q;
for
(q = p; q != NULL; q = q->prev) {
if
( (q->filename &&
strcmp
(q->filename, rr->filename) == 0) ||
(
strcmp
(q->uri, rr->uri) == 0) ){
founddupe = 1;
break
;
}
}
}
if
(p != NULL) {
error_fmt =
"Recursive include of \"%s\" "
"in parsed file %s"
;
}
}
if
(rr)
ap_set_module_config(rr->request_config, &includes_module, r);
if
(!error_fmt && ap_run_sub_req(rr)) {
error_fmt =
"unable to include \"%s\" in parsed file %s"
;
}
#if !defined(WIN32) && !defined(NETWARE)
ap_chdir_file(r->filename);
#endif
if
(error_fmt) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
r, error_fmt, tag_val, r->filename);
ap_rputs(error, r);
}
if
(rr != NULL) {
ap_destroy_sub_req(rr);
}
}
else
if
(!
strcmp
(tag,
"done"
)) {
return
0;
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag include in %s"
,
tag, r->filename);
ap_rputs(error, r);
}
}
}
typedef
struct
{
#ifdef TPF
TPF_FORK_CHILD t;
#endif
request_rec *r;
char
*s;
} include_cmd_arg;
static
int
include_cmd_child(
void
*arg, child_info *pinfo)
{
request_rec *r = ((include_cmd_arg *) arg)->r;
char
*s = ((include_cmd_arg *) arg)->s;
table *env = r->subprocess_env;
int
child_pid = 0;
#ifdef DEBUG_INCLUDE_CMD
#ifdef OS2
FILE
*dbg =
fopen
(
"con"
,
"w"
);
#else
FILE
*dbg =
fopen
(
"/dev/tty"
,
"w"
);
#endif
#endif
#if !defined(WIN32) && !defined(OS2)
char
err_string[MAX_STRING_LEN];
#endif
#ifdef DEBUG_INCLUDE_CMD
fprintf
(dbg,
"Attempting to include command '%s'\n"
, s);
#endif
if
(r->path_info && r->path_info[0] !=
'\0'
) {
request_rec *pa_req;
ap_table_setn(env,
"PATH_INFO"
, ap_escape_shell_cmd(r->pool, r->path_info));
pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r);
if
(pa_req->filename) {
ap_table_setn(env,
"PATH_TRANSLATED"
,
ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info,
NULL));
}
}
if
(r->args) {
char
*arg_copy = ap_pstrdup(r->pool, r->args);
ap_table_setn(env,
"QUERY_STRING"
, r->args);
ap_unescape_url(arg_copy);
ap_table_setn(env,
"QUERY_STRING_UNESCAPED"
,
ap_escape_shell_cmd(r->pool, arg_copy));
}
ap_error_log2stderr(r->server);
#ifdef DEBUG_INCLUDE_CMD
fprintf
(dbg,
"Attempting to exec '%s'\n"
, s);
#endif
#ifdef TPF
return
(0);
#else
ap_cleanup_for_exec();
child_pid = ap_call_exec(r, pinfo, s, ap_create_environment(r->pool, env),
1);
#if defined(WIN32) || defined(OS2)
return
(child_pid);
#else
#ifdef DEBUG_INCLUDE_CMD
fprintf
(dbg,
"Exec failed\n"
);
#endif
ap_snprintf(err_string,
sizeof
(err_string),
"exec of %s failed, reason: %s (errno = %d)\n"
,
SHELL_PATH,
strerror
(
errno
),
errno
);
write(STDERR_FILENO, err_string,
strlen
(err_string));
exit
(0);
return
(child_pid);
#endif /* WIN32 */
#endif /* TPF */
}
static
int
include_cmd(
char
*s, request_rec *r)
{
include_cmd_arg arg;
BUFF *script_in;
arg.r = r;
arg.s = s;
#ifdef TPF
arg.t.filename = r->filename;
arg.t.subprocess_env = r->subprocess_env;
arg.t.prog_type = FORK_FILE;
#endif
if
(!ap_bspawn_child(r->pool, include_cmd_child, &arg,
kill_after_timeout, NULL, &script_in, NULL)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"couldn't spawn include command"
);
return
-1;
}
ap_send_fb(script_in, r);
ap_bclose(script_in);
return
0;
}
static
int
handle_exec(
FILE
*in, request_rec *r,
const
char
*error)
{
char
tag[MAX_STRING_LEN];
char
*tag_val;
char
*file = r->filename;
char
parsed_string[MAX_STRING_LEN];
while
(1) {
if
(!(tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 1))) {
return
1;
}
if
(!
strcmp
(tag,
"cmd"
)) {
parse_string(r, tag_val, parsed_string,
sizeof
(parsed_string), 1);
if
(include_cmd(parsed_string, r) == -1) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"execution failure for parameter \"%s\" "
"to tag exec in file %s"
,
tag, r->filename);
ap_rputs(error, r);
}
#if !defined(WIN32) && !defined(NETWARE)
ap_chdir_file(r->filename);
#endif
}
else
if
(!
strcmp
(tag,
"cgi"
)) {
parse_string(r, tag_val, parsed_string,
sizeof
(parsed_string), 0);
if
(include_cgi(parsed_string, r) == -1) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"invalid CGI ref \"%s\" in %s"
, tag_val, file);
ap_rputs(error, r);
}
#if !defined(WIN32) && !defined(NETWARE)
ap_chdir_file(r->filename);
#endif
}
else
if
(!
strcmp
(tag,
"done"
)) {
return
0;
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag exec in %s"
,
tag, file);
ap_rputs(error, r);
}
}
}
static
int
handle_echo(
FILE
*in, request_rec *r,
const
char
*error)
{
char
tag[MAX_STRING_LEN];
char
*tag_val;
enum
{E_NONE, E_URL, E_ENTITY} encode;
encode = E_ENTITY;
while
(1) {
if
(!(tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 1))) {
return
1;
}
if
(!
strcmp
(tag,
"var"
)) {
const
char
*val = ap_table_get(r->subprocess_env, tag_val);
if
(val) {
if
(encode == E_NONE) {
ap_rputs(val, r);
}
else
if
(encode == E_URL) {
ap_rputs(ap_escape_uri(r->pool, val), r);
}
else
if
(encode == E_ENTITY) {
ap_rputs(ap_escape_html(r->pool, val), r);
}
}
else
{
ap_rputs(
"(none)"
, r);
}
}
else
if
(!
strcmp
(tag,
"done"
)) {
return
0;
}
else
if
(!
strcmp
(tag,
"encoding"
)) {
if
(!strcasecmp(tag_val,
"none"
)) encode = E_NONE;
else
if
(!strcasecmp(tag_val,
"url"
)) encode = E_URL;
else
if
(!strcasecmp(tag_val,
"entity"
)) encode = E_ENTITY;
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown value \"%s\" to parameter \"encoding\" of "
"tag echo in %s"
,
tag_val, r->filename);
ap_rputs(error, r);
}
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag echo in %s"
,
tag, r->filename);
ap_rputs(error, r);
}
}
}
#ifdef USE_PERL_SSI
static
int
handle_perl(
FILE
*in, request_rec *r,
const
char
*error)
{
char
tag[MAX_STRING_LEN];
char
parsed_string[MAX_STRING_LEN];
char
*tag_val;
SV *sub = Nullsv;
AV *av = newAV();
if
(ap_allow_options(r) & OPT_INCNOEXEC) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"#perl SSI disallowed by IncludesNoExec in %s"
,
r->filename);
return
DECLINED;
}
while
(1) {
if
(!(tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 1))) {
break
;
}
if
(strnEQ(tag,
"sub"
, 3)) {
sub = newSVpv(tag_val, 0);
}
else
if
(strnEQ(tag,
"arg"
, 3)) {
parse_string(r, tag_val, parsed_string,
sizeof
(parsed_string), 0);
av_push(av, newSVpv(parsed_string, 0));
}
else
if
(strnEQ(tag,
"done"
, 4)) {
break
;
}
}
perl_stdout2client(r);
perl_setup_env(r);
perl_call_handler(sub, r, av);
return
OK;
}
#endif
static
int
handle_config(
FILE
*in, request_rec *r,
char
*error,
char
*tf,
int
*sizefmt)
{
char
tag[MAX_STRING_LEN];
char
*tag_val;
char
parsed_string[MAX_STRING_LEN];
table *env = r->subprocess_env;
while
(1) {
if
(!(tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 0))) {
return
1;
}
if
(!
strcmp
(tag,
"errmsg"
)) {
parse_string(r, tag_val, error, MAX_STRING_LEN, 0);
}
else
if
(!
strcmp
(tag,
"timefmt"
)) {
time_t
date = r->request_time;
parse_string(r, tag_val, tf, MAX_STRING_LEN, 0);
ap_table_setn(env,
"DATE_LOCAL"
, ap_ht_time(r->pool, date, tf, 0));
ap_table_setn(env,
"DATE_GMT"
, ap_ht_time(r->pool, date, tf, 1));
ap_table_setn(env,
"LAST_MODIFIED"
,
ap_ht_time(r->pool, r->finfo.st_mtime, tf, 0));
}
else
if
(!
strcmp
(tag,
"sizefmt"
)) {
parse_string(r, tag_val, parsed_string,
sizeof
(parsed_string), 0);
decodehtml(parsed_string);
if
(!
strcmp
(parsed_string,
"bytes"
)) {
*sizefmt = SIZEFMT_BYTES;
}
else
if
(!
strcmp
(parsed_string,
"abbrev"
)) {
*sizefmt = SIZEFMT_KMG;
}
}
else
if
(!
strcmp
(tag,
"done"
)) {
return
0;
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag config in %s"
,
tag, r->filename);
ap_rputs(error, r);
}
}
}
static
int
find_file(request_rec *r,
const
char
*directive,
const
char
*tag,
char
*tag_val,
struct
stat *finfo,
const
char
*error)
{
char
*to_send = tag_val;
request_rec *rr = NULL;
int
ret=0;
char
*error_fmt = NULL;
if
(!
strcmp
(tag,
"file"
)) {
if
(!is_only_below(tag_val)) {
error_fmt =
"unable to access file \"%s\" "
"in parsed file %s"
;
}
else
{
ap_getparents(tag_val);
rr = ap_sub_req_lookup_file(tag_val, r);
if
(rr->status == HTTP_OK && rr->finfo.st_mode != 0) {
to_send = rr->filename;
if
(stat(to_send, finfo)) {
error_fmt =
"unable to get information about \"%s\" "
"in parsed file %s"
;
}
}
else
{
error_fmt =
"unable to lookup information about \"%s\" "
"in parsed file %s"
;
}
}
if
(error_fmt) {
ret = -1;
ap_log_rerror(APLOG_MARK, APLOG_ERR, r, error_fmt, to_send, r->filename);
ap_rputs(error, r);
}
if
(rr) ap_destroy_sub_req(rr);
return
ret;
}
else
if
(!
strcmp
(tag,
"virtual"
)) {
rr = ap_sub_req_lookup_uri(tag_val, r);
if
(rr->status == HTTP_OK && rr->finfo.st_mode != 0) {
memcpy
((
char
*) finfo, (
const
char
*) &rr->finfo,
sizeof
(
struct
stat));
ap_destroy_sub_req(rr);
return
0;
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unable to get information about \"%s\" "
"in parsed file %s"
,
tag_val, r->filename);
ap_rputs(error, r);
ap_destroy_sub_req(rr);
return
-1;
}
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag %s in %s"
,
tag, directive, r->filename);
ap_rputs(error, r);
return
-1;
}
}
static
int
handle_fsize(
FILE
*in, request_rec *r,
const
char
*error,
int
sizefmt)
{
char
tag[MAX_STRING_LEN];
char
*tag_val;
struct
stat finfo;
char
parsed_string[MAX_STRING_LEN];
while
(1) {
if
(!(tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 1))) {
return
1;
}
else
if
(!
strcmp
(tag,
"done"
)) {
return
0;
}
else
{
parse_string(r, tag_val, parsed_string,
sizeof
(parsed_string), 0);
if
(!find_file(r,
"fsize"
, tag, parsed_string, &finfo, error)) {
if
(sizefmt == SIZEFMT_KMG) {
ap_send_size(finfo.st_size, r);
}
else
{
int
l, x;
#if defined(AP_OFF_T_IS_QUAD)
ap_snprintf(tag,
sizeof
(tag),
"%qd"
, finfo.st_size);
#else
ap_snprintf(tag,
sizeof
(tag),
"%ld"
, finfo.st_size);
#endif
l =
strlen
(tag);
for
(x = 0; x < l; x++) {
if
(x && (!((l - x) % 3))) {
ap_rputc(
','
, r);
}
ap_rputc(tag[x], r);
}
}
}
}
}
}
static
int
handle_flastmod(
FILE
*in, request_rec *r,
const
char
*error,
const
char
*tf)
{
char
tag[MAX_STRING_LEN];
char
*tag_val;
struct
stat finfo;
char
parsed_string[MAX_STRING_LEN];
while
(1) {
if
(!(tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 1))) {
return
1;
}
else
if
(!
strcmp
(tag,
"done"
)) {
return
0;
}
else
{
parse_string(r, tag_val, parsed_string,
sizeof
(parsed_string), 0);
if
(!find_file(r,
"flastmod"
, tag, parsed_string, &finfo, error)) {
ap_rputs(ap_ht_time(r->pool, finfo.st_mtime, tf, 0), r);
}
}
}
}
static
int
re_check(request_rec *r,
char
*string,
char
*rexp)
{
regex_t *compiled;
int
regex_error;
compiled = ap_pregcomp(r->pool, rexp, REG_EXTENDED | REG_NOSUB);
if
(compiled == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unable to compile pattern \"%s\""
, rexp);
return
-1;
}
regex_error = ap_regexec(compiled, string, 0, (regmatch_t *) NULL, 0);
ap_pregfree(r->pool, compiled);
return
(!regex_error);
}
enum
token_type {
token_string,
token_and, token_or, token_not, token_eq, token_ne,
token_rbrace, token_lbrace, token_group,
token_ge, token_le, token_gt, token_lt
};
struct
token {
enum
token_type type;
char
value[MAX_STRING_LEN];
};
static
const
char
*get_ptoken(request_rec *r,
const
char
*string,
struct
token *token)
{
char
ch;
int
next = 0;
int
qs = 0;
if
(string == (
char
*) NULL) {
return
(
char
*) NULL;
}
while
((ch = *string++)) {
if
(!ap_isspace(ch)) {
break
;
}
}
if
(ch ==
'\0'
) {
return
(
char
*) NULL;
}
token->type = token_string;
switch
(ch) {
case
'('
:
token->type = token_lbrace;
return
(string);
case
')'
:
token->type = token_rbrace;
return
(string);
case
'='
:
token->type = token_eq;
return
(string);
case
'!'
:
if
(*string ==
'='
) {
token->type = token_ne;
return
(string + 1);
}
else
{
token->type = token_not;
return
(string);
}
case
'\''
:
token->type = token_string;
qs = 1;
break
;
case
'|'
:
if
(*string ==
'|'
) {
token->type = token_or;
return
(string + 1);
}
break
;
case
'&'
:
if
(*string ==
'&'
) {
token->type = token_and;
return
(string + 1);
}
break
;
case
'>'
:
if
(*string ==
'='
) {
token->type = token_ge;
return
(string + 1);
}
else
{
token->type = token_gt;
return
(string);
}
case
'<'
:
if
(*string ==
'='
) {
token->type = token_le;
return
(string + 1);
}
else
{
token->type = token_lt;
return
(string);
}
default
:
token->type = token_string;
break
;
}
if
(!qs) {
token->value[next++] = ch;
}
for
(ch = *string; ch !=
'\0'
; ch = *++string) {
if
(ch ==
'\\'
) {
if
((ch = *++string) ==
'\0'
) {
goto
TOKEN_DONE;
}
token->value[next++] = ch;
continue
;
}
if
(!qs) {
if
(ap_isspace(ch)) {
goto
TOKEN_DONE;
}
switch
(ch) {
case
'('
:
goto
TOKEN_DONE;
case
')'
:
goto
TOKEN_DONE;
case
'='
:
goto
TOKEN_DONE;
case
'!'
:
goto
TOKEN_DONE;
case
'|'
:
if
(*(string + 1) ==
'|'
) {
goto
TOKEN_DONE;
}
break
;
case
'&'
:
if
(*(string + 1) ==
'&'
) {
goto
TOKEN_DONE;
}
break
;
case
'<'
:
goto
TOKEN_DONE;
case
'>'
:
goto
TOKEN_DONE;
}
token->value[next++] = ch;
}
else
{
if
(ch ==
'\''
) {
qs = 0;
++string;
goto
TOKEN_DONE;
}
token->value[next++] = ch;
}
}
TOKEN_DONE:
if
(qs) {
ap_rputs(
"\nUnmatched '\n"
, r);
next = 0;
}
token->value[next] =
'\0'
;
return
(string);
}
static
int
parse_expr(request_rec *r,
const
char
*expr,
const
char
*error)
{
struct
parse_node {
struct
parse_node *left, *right, *parent;
struct
token token;
int
value, done;
} *root, *current, *
new
;
const
char
*parse;
char
buffer[MAX_STRING_LEN];
pool *expr_pool;
int
retval = 0;
if
((parse = expr) == (
char
*) NULL) {
return
(0);
}
root = current = (
struct
parse_node *) NULL;
expr_pool = ap_make_sub_pool(r->pool);
while
(1) {
new
= (
struct
parse_node *) ap_palloc(expr_pool,
sizeof
(
struct
parse_node));
new
->parent =
new
->left =
new
->right = (
struct
parse_node *) NULL;
new
->done = 0;
if
((parse = get_ptoken(r, parse, &
new
->token)) == (
char
*) NULL) {
break
;
}
switch
(
new
->token.type) {
case
token_string:
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
" Token: string ("
,
new
->token.value,
")\n"
, NULL);
#endif
if
(current == (
struct
parse_node *) NULL) {
root = current =
new
;
break
;
}
switch
(current->token.type) {
case
token_string:
if
(current->token.value[0] !=
'\0'
) {
strncat
(current->token.value,
" "
,
sizeof
(current->token.value)
-
strlen
(current->token.value) - 1);
}
strncat
(current->token.value,
new
->token.value,
sizeof
(current->token.value)
-
strlen
(current->token.value) - 1);
current->token.value[
sizeof
(current->token.value) - 1] =
'\0'
;
break
;
case
token_eq:
case
token_ne:
case
token_and:
case
token_or:
case
token_lbrace:
case
token_not:
case
token_ge:
case
token_gt:
case
token_le:
case
token_lt:
new
->parent = current;
current = current->right =
new
;
break
;
default
:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
break
;
case
token_and:
case
token_or:
#ifdef DEBUG_INCLUDE
ap_rputs(
" Token: and/or\n"
, r);
#endif
if
(current == (
struct
parse_node *) NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
while
(current != (
struct
parse_node *) NULL) {
switch
(current->token.type) {
case
token_string:
case
token_group:
case
token_not:
case
token_eq:
case
token_ne:
case
token_and:
case
token_or:
case
token_ge:
case
token_gt:
case
token_le:
case
token_lt:
current = current->parent;
continue
;
case
token_lbrace:
break
;
default
:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
break
;
}
if
(current == (
struct
parse_node *) NULL) {
new
->left = root;
new
->left->parent =
new
;
new
->parent = (
struct
parse_node *) NULL;
root =
new
;
}
else
{
new
->left = current->right;
current->right =
new
;
new
->parent = current;
}
current =
new
;
break
;
case
token_not:
#ifdef DEBUG_INCLUDE
ap_rputs(
" Token: not\n"
, r);
#endif
if
(current == (
struct
parse_node *) NULL) {
root = current =
new
;
break
;
}
while
(current != (
struct
parse_node *) NULL) {
switch
(current->token.type) {
case
token_not:
case
token_eq:
case
token_ne:
case
token_and:
case
token_or:
case
token_lbrace:
case
token_ge:
case
token_gt:
case
token_le:
case
token_lt:
break
;
default
:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
break
;
}
if
(current == (
struct
parse_node *) NULL) {
new
->left = root;
new
->left->parent =
new
;
new
->parent = (
struct
parse_node *) NULL;
root =
new
;
}
else
{
new
->left = current->right;
current->right =
new
;
new
->parent = current;
}
current =
new
;
break
;
case
token_eq:
case
token_ne:
case
token_ge:
case
token_gt:
case
token_le:
case
token_lt:
#ifdef DEBUG_INCLUDE
ap_rputs(
" Token: eq/ne/ge/gt/le/lt\n"
, r);
#endif
if
(current == (
struct
parse_node *) NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
while
(current != (
struct
parse_node *) NULL) {
switch
(current->token.type) {
case
token_string:
case
token_group:
current = current->parent;
continue
;
case
token_lbrace:
case
token_and:
case
token_or:
break
;
case
token_not:
case
token_eq:
case
token_ne:
case
token_ge:
case
token_gt:
case
token_le:
case
token_lt:
default
:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
break
;
}
if
(current == (
struct
parse_node *) NULL) {
new
->left = root;
new
->left->parent =
new
;
new
->parent = (
struct
parse_node *) NULL;
root =
new
;
}
else
{
new
->left = current->right;
current->right =
new
;
new
->parent = current;
}
current =
new
;
break
;
case
token_rbrace:
#ifdef DEBUG_INCLUDE
ap_rputs(
" Token: rbrace\n"
, r);
#endif
while
(current != (
struct
parse_node *) NULL) {
if
(current->token.type == token_lbrace) {
current->token.type = token_group;
break
;
}
current = current->parent;
}
if
(current == (
struct
parse_node *) NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Unmatched ')' in \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
break
;
case
token_lbrace:
#ifdef DEBUG_INCLUDE
ap_rputs(
" Token: lbrace\n"
, r);
#endif
if
(current == (
struct
parse_node *) NULL) {
root = current =
new
;
break
;
}
while
(current != (
struct
parse_node *) NULL) {
switch
(current->token.type) {
case
token_not:
case
token_eq:
case
token_ne:
case
token_and:
case
token_or:
case
token_lbrace:
case
token_ge:
case
token_gt:
case
token_le:
case
token_lt:
break
;
case
token_string:
case
token_group:
default
:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
break
;
}
if
(current == (
struct
parse_node *) NULL) {
new
->left = root;
new
->left->parent =
new
;
new
->parent = (
struct
parse_node *) NULL;
root =
new
;
}
else
{
new
->left = current->right;
current->right =
new
;
new
->parent = current;
}
current =
new
;
break
;
default
:
break
;
}
}
current = root;
while
(current != (
struct
parse_node *) NULL) {
switch
(current->token.type) {
case
token_string:
#ifdef DEBUG_INCLUDE
ap_rputs(
" Evaluate string\n"
, r);
#endif
parse_string(r, current->token.value, buffer,
sizeof
(buffer), 0);
ap_cpystrn(current->token.value, buffer,
sizeof
(current->token.value));
current->value = (current->token.value[0] !=
'\0'
);
current->done = 1;
current = current->parent;
break
;
case
token_and:
case
token_or:
#ifdef DEBUG_INCLUDE
ap_rputs(
" Evaluate and/or\n"
, r);
#endif
if
(current->left == (
struct
parse_node *) NULL ||
current->right == (
struct
parse_node *) NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
if
(!current->left->done) {
switch
(current->left->token.type) {
case
token_string:
parse_string(r, current->left->token.value,
buffer,
sizeof
(buffer), 0);
ap_cpystrn(current->left->token.value, buffer,
sizeof
(current->left->token.value));
current->left->value = (current->left->token.value[0] !=
'\0'
);
current->left->done = 1;
break
;
default
:
current = current->left;
continue
;
}
}
if
(!current->right->done) {
switch
(current->right->token.type) {
case
token_string:
parse_string(r, current->right->token.value,
buffer,
sizeof
(buffer), 0);
ap_cpystrn(current->right->token.value, buffer,
sizeof
(current->right->token.value));
current->right->value = (current->right->token.value[0] !=
'\0'
);
current->right->done = 1;
break
;
default
:
current = current->right;
continue
;
}
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
" Left: "
, current->left->value ?
"1"
:
"0"
,
"\n"
, NULL);
ap_rvputs(r,
" Right: "
, current->right->value ?
"1"
:
"0"
,
"\n"
, NULL);
#endif
if
(current->token.type == token_and) {
current->value = current->left->value && current->right->value;
}
else
{
current->value = current->left->value || current->right->value;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
" Returning "
, current->value ?
"1"
:
"0"
,
"\n"
, NULL);
#endif
current->done = 1;
current = current->parent;
break
;
case
token_eq:
case
token_ne:
#ifdef DEBUG_INCLUDE
ap_rputs(
" Evaluate eq/ne\n"
, r);
#endif
if
((current->left == (
struct
parse_node *) NULL) ||
(current->right == (
struct
parse_node *) NULL) ||
(current->left->token.type != token_string) ||
(current->right->token.type != token_string)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
parse_string(r, current->left->token.value,
buffer,
sizeof
(buffer), 0);
ap_cpystrn(current->left->token.value, buffer,
sizeof
(current->left->token.value));
parse_string(r, current->right->token.value,
buffer,
sizeof
(buffer), 0);
ap_cpystrn(current->right->token.value, buffer,
sizeof
(current->right->token.value));
if
(current->right->token.value[0] ==
'/'
) {
int
len;
len =
strlen
(current->right->token.value);
if
(current->right->token.value[len - 1] ==
'/'
) {
current->right->token.value[len - 1] =
'\0'
;
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid rexp \"%s\" in file %s"
,
current->right->token.value, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
" Re Compare ("
, current->left->token.value,
") with /"
, ¤t->right->token.value[1],
"/\n"
, NULL);
#endif
current->value =
re_check(r, current->left->token.value,
¤t->right->token.value[1]);
}
else
{
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
" Compare ("
, current->left->token.value,
") with ("
, current->right->token.value,
")\n"
, NULL);
#endif
current->value =
(
strcmp
(current->left->token.value,
current->right->token.value) == 0);
}
if
(current->token.type == token_ne) {
current->value = !current->value;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
" Returning "
, current->value ?
"1"
:
"0"
,
"\n"
, NULL);
#endif
current->done = 1;
current = current->parent;
break
;
case
token_ge:
case
token_gt:
case
token_le:
case
token_lt:
#ifdef DEBUG_INCLUDE
ap_rputs(
" Evaluate ge/gt/le/lt\n"
, r);
#endif
if
((current->left == (
struct
parse_node *) NULL) ||
(current->right == (
struct
parse_node *) NULL) ||
(current->left->token.type != token_string) ||
(current->right->token.type != token_string)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
}
parse_string(r, current->left->token.value,
buffer,
sizeof
(buffer), 0);
ap_cpystrn(current->left->token.value, buffer,
sizeof
(current->left->token.value));
parse_string(r, current->right->token.value,
buffer,
sizeof
(buffer), 0);
ap_cpystrn(current->right->token.value, buffer,
sizeof
(current->right->token.value));
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
" Compare ("
, current->left->token.value,
") with ("
, current->right->token.value,
")\n"
, NULL);
#endif
current->value =
strcmp
(current->left->token.value,
current->right->token.value);
if
(current->token.type == token_ge) {
current->value = current->value >= 0;
}
else
if
(current->token.type == token_gt) {
current->value = current->value > 0;
}
else
if
(current->token.type == token_le) {
current->value = current->value <= 0;
}
else
if
(current->token.type == token_lt) {
current->value = current->value < 0;
}
else
{
current->value = 0;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
" Returning "
, current->value ?
"1"
:
"0"
,
"\n"
, NULL);
#endif
current->done = 1;
current = current->parent;
break
;
case
token_not:
if
(current->right != (
struct
parse_node *) NULL) {
if
(!current->right->done) {
current = current->right;
continue
;
}
current->value = !current->right->value;
}
else
{
current->value = 0;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
" Evaluate !: "
, current->value ?
"1"
:
"0"
,
"\n"
, NULL);
#endif
current->done = 1;
current = current->parent;
break
;
case
token_group:
if
(current->right != (
struct
parse_node *) NULL) {
if
(!current->right->done) {
current = current->right;
continue
;
}
current->value = current->right->value;
}
else
{
current->value = 1;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
" Evaluate (): "
, current->value ?
"1"
:
"0"
,
"\n"
, NULL);
#endif
current->done = 1;
current = current->parent;
break
;
case
token_lbrace:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Unmatched '(' in \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
case
token_rbrace:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Unmatched ')' in \"%s\" in file %s"
,
expr, r->filename);
ap_rputs(error, r);
goto
RETURN;
default
:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"bad token type"
);
ap_rputs(error, r);
goto
RETURN;
}
}
retval = (root == (
struct
parse_node *) NULL) ? 0 : root->value;
RETURN:
ap_destroy_pool(expr_pool);
return
(retval);
}
static
int
handle_if(
FILE
*in, request_rec *r,
const
char
*error,
int
*conditional_status,
int
*printing)
{
char
tag[MAX_STRING_LEN];
char
*tag_val;
char
*expr;
expr = NULL;
while
(1) {
tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 0);
if
(*tag ==
'\0'
) {
return
1;
}
else
if
(!
strcmp
(tag,
"done"
)) {
if
(expr == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"missing expr in if statement: %s"
,
r->filename);
ap_rputs(error, r);
return
1;
}
*printing = *conditional_status = parse_expr(r, expr, error);
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
"**** if conditional_status=\""
,
*conditional_status ?
"1"
:
"0"
,
"\"\n"
, NULL);
#endif
return
0;
}
else
if
(!
strcmp
(tag,
"expr"
)) {
expr = tag_val;
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
"**** if expr=\""
, expr,
"\"\n"
, NULL);
#endif
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag if in %s"
,
tag, r->filename);
ap_rputs(error, r);
}
}
}
static
int
handle_elif(
FILE
*in, request_rec *r,
const
char
*error,
int
*conditional_status,
int
*printing)
{
char
tag[MAX_STRING_LEN];
char
*tag_val;
char
*expr;
expr = NULL;
while
(1) {
tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 0);
if
(*tag ==
'\0'
) {
return
1;
}
else
if
(!
strcmp
(tag,
"done"
)) {
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
"**** elif conditional_status=\""
,
*conditional_status ?
"1"
:
"0"
,
"\"\n"
, NULL);
#endif
if
(*conditional_status) {
*printing = 0;
return
(0);
}
if
(expr == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"missing expr in elif statement: %s"
,
r->filename);
ap_rputs(error, r);
return
1;
}
*printing = *conditional_status = parse_expr(r, expr, error);
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
"**** elif conditional_status=\""
,
*conditional_status ?
"1"
:
"0"
,
"\"\n"
, NULL);
#endif
return
0;
}
else
if
(!
strcmp
(tag,
"expr"
)) {
expr = tag_val;
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
"**** if expr=\""
, expr,
"\"\n"
, NULL);
#endif
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag if in %s"
,
tag, r->filename);
ap_rputs(error, r);
}
}
}
static
int
handle_else(
FILE
*in, request_rec *r,
const
char
*error,
int
*conditional_status,
int
*printing)
{
char
tag[MAX_STRING_LEN];
if
(!get_tag(r->pool, in, tag,
sizeof
(tag), 1)) {
return
1;
}
else
if
(!
strcmp
(tag,
"done"
)) {
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
"**** else conditional_status=\""
,
*conditional_status ?
"1"
:
"0"
,
"\"\n"
, NULL);
#endif
*printing = !(*conditional_status);
*conditional_status = 1;
return
0;
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"else directive does not take tags in %s"
,
r->filename);
if
(*printing) {
ap_rputs(error, r);
}
return
-1;
}
}
static
int
handle_endif(
FILE
*in, request_rec *r,
const
char
*error,
int
*conditional_status,
int
*printing)
{
char
tag[MAX_STRING_LEN];
if
(!get_tag(r->pool, in, tag,
sizeof
(tag), 1)) {
return
1;
}
else
if
(!
strcmp
(tag,
"done"
)) {
#ifdef DEBUG_INCLUDE
ap_rvputs(r,
"**** endif conditional_status=\""
,
*conditional_status ?
"1"
:
"0"
,
"\"\n"
, NULL);
#endif
*printing = 1;
*conditional_status = 1;
return
0;
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"endif directive does not take tags in %s"
,
r->filename);
ap_rputs(error, r);
return
-1;
}
}
static
int
handle_set(
FILE
*in, request_rec *r,
const
char
*error)
{
char
tag[MAX_STRING_LEN];
char
parsed_string[MAX_STRING_LEN];
char
*tag_val;
char
*var;
var = (
char
*) NULL;
while
(1) {
if
(!(tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 1))) {
return
1;
}
else
if
(!
strcmp
(tag,
"done"
)) {
return
0;
}
else
if
(!
strcmp
(tag,
"var"
)) {
var = tag_val;
}
else
if
(!
strcmp
(tag,
"value"
)) {
if
(var == (
char
*) NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"variable must precede value in set directive in %s"
,
r->filename);
ap_rputs(error, r);
return
-1;
}
parse_string(r, tag_val, parsed_string,
sizeof
(parsed_string), 0);
ap_table_setn(r->subprocess_env, var, ap_pstrdup(r->pool, parsed_string));
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid tag for set directive in %s"
, r->filename);
ap_rputs(error, r);
return
-1;
}
}
}
static
int
handle_printenv(
FILE
*in, request_rec *r,
const
char
*error)
{
char
tag[MAX_STRING_LEN];
char
*tag_val;
array_header *arr = ap_table_elts(r->subprocess_env);
table_entry *elts = (table_entry *) arr->elts;
int
i;
if
(!(tag_val = get_tag(r->pool, in, tag,
sizeof
(tag), 1))) {
return
1;
}
else
if
(!
strcmp
(tag,
"done"
)) {
for
(i = 0; i < arr->nelts; ++i) {
ap_rvputs(r, ap_escape_html(r->pool, elts[i].key),
"="
,
ap_escape_html(r->pool, elts[i].val),
"\n"
, NULL);
}
return
0;
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"printenv directive does not take tags in %s"
,
r->filename);
ap_rputs(error, r);
return
-1;
}
}
static
void
send_parsed_content(
FILE
*f, request_rec *r)
{
#ifdef NETWARE
char
directive[512], error[512];
char
timefmt[512];
#else
char
directive[MAX_STRING_LEN], error[MAX_STRING_LEN];
char
timefmt[MAX_STRING_LEN];
#endif
int
noexec = ap_allow_options(r) & OPT_INCNOEXEC;
int
ret, sizefmt;
int
if_nesting;
int
printing;
int
conditional_status;
ap_cpystrn(error, DEFAULT_ERROR_MSG,
sizeof
(error));
ap_cpystrn(timefmt, DEFAULT_TIME_FORMAT,
sizeof
(timefmt));
sizefmt = SIZEFMT_KMG;
printing = conditional_status = 1;
if_nesting = 0;
#if !defined(WIN32) && !defined(NETWARE)
ap_chdir_file(r->filename);
#endif
if
(r->args) {
char
*arg_copy = ap_pstrdup(r->pool, r->args);
ap_table_setn(r->subprocess_env,
"QUERY_STRING"
, r->args);
ap_unescape_url(arg_copy);
ap_table_setn(r->subprocess_env,
"QUERY_STRING_UNESCAPED"
,
ap_escape_shell_cmd(r->pool, arg_copy));
}
while
(1) {
if
(!find_string(f, STARTING_SEQUENCE, r, printing)) {
if
(get_directive(f, directive,
sizeof
(directive), r->pool)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"mod_include: error reading directive in %s"
,
r->filename);
ap_rputs(error, r);
return
;
}
if
(!
strcmp
(directive,
"if"
)) {
if
(!printing) {
if_nesting++;
}
else
{
ret = handle_if(f, r, error, &conditional_status,
&printing);
if_nesting = 0;
}
continue
;
}
else
if
(!
strcmp
(directive,
"else"
)) {
if
(!if_nesting) {
ret = handle_else(f, r, error, &conditional_status,
&printing);
}
continue
;
}
else
if
(!
strcmp
(directive,
"elif"
)) {
if
(!if_nesting) {
ret = handle_elif(f, r, error, &conditional_status,
&printing);
}
continue
;
}
else
if
(!
strcmp
(directive,
"endif"
)) {
if
(!if_nesting) {
ret = handle_endif(f, r, error, &conditional_status,
&printing);
}
else
{
if_nesting--;
}
continue
;
}
if
(!printing) {
continue
;
}
if
(!
strcmp
(directive,
"exec"
)) {
if
(noexec) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"exec used but not allowed in %s"
,
r->filename);
if
(printing) {
ap_rputs(error, r);
}
ret = find_string(f, ENDING_SEQUENCE, r, 0);
}
else
{
ret = handle_exec(f, r, error);
}
}
else
if
(!
strcmp
(directive,
"config"
)) {
ret = handle_config(f, r, error, timefmt, &sizefmt);
}
else
if
(!
strcmp
(directive,
"set"
)) {
ret = handle_set(f, r, error);
}
else
if
(!
strcmp
(directive,
"include"
)) {
ret = handle_include(f, r, error, noexec);
}
else
if
(!
strcmp
(directive,
"echo"
)) {
ret = handle_echo(f, r, error);
}
else
if
(!
strcmp
(directive,
"fsize"
)) {
ret = handle_fsize(f, r, error, sizefmt);
}
else
if
(!
strcmp
(directive,
"flastmod"
)) {
ret = handle_flastmod(f, r, error, timefmt);
}
else
if
(!
strcmp
(directive,
"printenv"
)) {
ret = handle_printenv(f, r, error);
}
#ifdef USE_PERL_SSI
else
if
(!
strcmp
(directive,
"perl"
)) {
ret = handle_perl(f, r, error);
}
#endif
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown directive \"%s\" "
"in parsed doc %s"
,
directive, r->filename);
if
(printing) {
ap_rputs(error, r);
}
ret = find_string(f, ENDING_SEQUENCE, r, 0);
}
if
(ret) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"premature EOF in parsed file %s"
,
r->filename);
return
;
}
}
else
{
return
;
}
}
}
module includes_module;
enum
xbithack {
xbithack_off, xbithack_on, xbithack_full
};
#ifdef XBITHACK
#define DEFAULT_XBITHACK xbithack_full
#else
#define DEFAULT_XBITHACK xbithack_off
#endif
static
void
*create_includes_dir_config(pool *p,
char
*dummy)
{
enum
xbithack *result = (
enum
xbithack *) ap_palloc(p,
sizeof
(
enum
xbithack));
*result = DEFAULT_XBITHACK;
return
result;
}
static
const
char
*set_xbithack(cmd_parms *cmd,
void
*xbp,
char
*arg)
{
enum
xbithack *state = (
enum
xbithack *) xbp;
if
(!strcasecmp(arg,
"off"
)) {
*state = xbithack_off;
}
else
if
(!strcasecmp(arg,
"on"
)) {
*state = xbithack_on;
}
else
if
(!strcasecmp(arg,
"full"
)) {
*state = xbithack_full;
}
else
{
return
"XBitHack must be set to Off, On, or Full"
;
}
return
NULL;
}
static
int
send_parsed_file(request_rec *r)
{
FILE
*f;
enum
xbithack *state =
(
enum
xbithack *) ap_get_module_config(r->per_dir_config, &includes_module);
int
errstatus;
request_rec *parent;
if
(!(ap_allow_options(r) & OPT_INCLUDES)) {
return
DECLINED;
}
r->allowed |= (1 << M_GET);
if
(r->method_number != M_GET) {
return
DECLINED;
}
if
(r->finfo.st_mode == 0) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"File does not exist: %s"
,
(r->path_info
? ap_pstrcat(r->pool, r->filename, r->path_info, NULL)
: r->filename));
return
HTTP_NOT_FOUND;
}
if
(!(f = ap_pfopen(r->pool, r->filename,
"r"
))) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"file permissions deny server access: %s"
, r->filename);
return
HTTP_FORBIDDEN;
}
if
((*state == xbithack_full)
#if !defined(OS2) && !defined(WIN32) && !defined(NETWARE)
&& (r->finfo.st_mode & S_IXGRP)
#endif
) {
ap_update_mtime(r, r->finfo.st_mtime);
ap_set_last_modified(r);
}
if
((errstatus = ap_meets_conditions(r)) != OK) {
return
errstatus;
}
ap_send_http_header(r);
if
(r->header_only) {
ap_pfclose(r->pool, f);
return
OK;
}
#define SUB_REQ_STRING "Sub request to mod_include"
#define PARENT_STRING "Parent request to mod_include"
if
(ap_table_get(r->notes, SUB_REQ_STRING)) {
request_rec *p = r->main;
request_rec *q = p;
while
(q) {
if
(ap_table_get(q->notes, PARENT_STRING)) {
ap_set_module_config(r->request_config, &includes_module, q);
ap_add_common_vars(q);
ap_add_cgi_vars(q);
add_include_vars(q, DEFAULT_TIME_FORMAT);
ap_table_unset(r->notes, SUB_REQ_STRING);
ap_table_unset(q->notes, PARENT_STRING);
break
;
}
if
(q->prev != NULL) {
q = q->prev;
}
else
{
p = p->main;
q = p;
}
}
}
if
((parent = ap_get_module_config(r->request_config, &includes_module))) {
r->subprocess_env = parent->subprocess_env;
ap_pool_join(parent->pool, r->pool);
r->finfo.st_mtime = parent->finfo.st_mtime;
}
else
{
ap_add_common_vars(r);
ap_add_cgi_vars(r);
add_include_vars(r, DEFAULT_TIME_FORMAT);
}
ap_hard_timeout(
"send SSI"
, r);
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
#endif
send_parsed_content(f, r);
if
(parent) {
r->pool = ap_make_sub_pool(r->pool);
}
ap_kill_timeout(r);
return
OK;
}
static
int
send_shtml_file(request_rec *r)
{
r->content_type =
"text/html"
;
return
send_parsed_file(r);
}
static
int
xbithack_handler(request_rec *r)
{
#if defined(OS2) || defined(WIN32) || defined(NETWARE)
return
DECLINED;
#else
enum
xbithack *state;
if
(!(r->finfo.st_mode & S_IXUSR)) {
return
DECLINED;
}
state = (
enum
xbithack *) ap_get_module_config(r->per_dir_config,
&includes_module);
if
(*state == xbithack_off) {
return
DECLINED;
}
return
send_parsed_file(r);
#endif
}
static
const
command_rec includes_cmds[] =
{
{
"XBitHack"
, set_xbithack, NULL, OR_OPTIONS, TAKE1,
"Off, On, or Full"
},
{NULL}
};
static
const
handler_rec includes_handlers[] =
{
{INCLUDES_MAGIC_TYPE, send_shtml_file},
{INCLUDES_MAGIC_TYPE3, send_shtml_file},
{
"server-parsed"
, send_parsed_file},
{
"text/html"
, xbithack_handler},
{NULL}
};
module MODULE_VAR_EXPORT includes_module =
{
STANDARD_MODULE_STUFF,
NULL,
create_includes_dir_config,
NULL,
NULL,
NULL,
includes_cmds,
includes_handlers,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};