#include "util_script.h"
#include "http_core.h"
#include "mod_include.h"
+#include "ap_expr.h"
/* helper for Latin1 <-> entity encoding */
#if APR_CHARSET_EBCDIC
typedef enum {
XBITHACK_OFF,
XBITHACK_ON,
- XBITHACK_FULL
+ XBITHACK_FULL,
+ XBITHACK_UNSET
} xbithack_t;
typedef struct {
const char *default_time_fmt;
const char *undefined_echo;
xbithack_t xbithack;
- const int accessenable;
+ signed char accessenable;
+ signed char lastmodified;
+ signed char etag;
+ signed char legacy_expr;
} include_dir_config;
typedef struct {
const char *rexp;
apr_size_t nsub;
ap_regmatch_t match[AP_MAX_REG_MATCH];
+ int have_match;
} backref_t;
typedef struct {
apr_bucket_brigade *tmp_bb;
- request_rec *r;
const char *start_seq;
bndm_t *start_seq_pat;
const char *end_seq;
const char *undefined_echo;
apr_size_t undefined_echo_len;
- int accessenable; /* is using the access tests allowed? */
+ char accessenable; /* is using the access tests allowed? */
+ char legacy_expr; /* use ap_expr or legacy mod_include
+ expression parser? */
+ ap_expr_eval_ctx_t *expr_eval_ctx; /* NULL if there wasn't an ap_expr yet */
+ const char *expr_vary_this; /* for use by ap_expr_eval_ctx */
+ const char *expr_err; /* for use by ap_expr_eval_ctx */
#ifdef DEBUG_INCLUDE
struct {
ap_filter_t *f;
/* Sentinel value to store in subprocess_env for items that
* shouldn't be evaluated until/unless they're actually used
*/
-static const char lazy_eval_sentinel;
+static const char lazy_eval_sentinel = '\0';
#define LAZY_VALUE (&lazy_eval_sentinel)
/* default values */
#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
#define DEFAULT_UNDEFINED_ECHO "(none)"
+#define UNSET -1
+
#ifdef XBITHACK
#define DEFAULT_XBITHACK XBITHACK_FULL
#else
*p = '\0';
}
-static void add_include_vars(request_rec *r, const char *timefmt)
+static void add_include_vars(request_rec *r)
{
apr_table_t *e = r->subprocess_env;
char *t;
}
}
-static const char *add_include_vars_lazy(request_rec *r, const char *var)
+static const char *add_include_vars_lazy(request_rec *r, const char *var, const char *timefmt)
{
char *val;
if (!strcasecmp(var, "DATE_LOCAL")) {
- include_dir_config *conf =
- (include_dir_config *)ap_get_module_config(r->per_dir_config,
- &include_module);
- val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 0);
+ val = ap_ht_time(r->pool, r->request_time, timefmt, 0);
}
else if (!strcasecmp(var, "DATE_GMT")) {
- include_dir_config *conf =
- (include_dir_config *)ap_get_module_config(r->per_dir_config,
- &include_module);
- val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 1);
+ val = ap_ht_time(r->pool, r->request_time, timefmt, 1);
}
else if (!strcasecmp(var, "LAST_MODIFIED")) {
- include_dir_config *conf =
- (include_dir_config *)ap_get_module_config(r->per_dir_config,
- &include_module);
- val = ap_ht_time(r->pool, r->finfo.mtime, conf->default_time_fmt, 0);
+ val = ap_ht_time(r->pool, r->finfo.mtime, timefmt, 0);
}
else if (!strcasecmp(var, "USER_NAME")) {
if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
static const char *get_include_var(const char *var, include_ctx_t *ctx)
{
const char *val;
- request_rec *r = ctx->intern->r;
+ request_rec *r = ctx->r;
if (apr_isdigit(*var) && !var[1]) {
apr_size_t idx = *var - '0';
* The choice of returning NULL strings on not-found,
* v.s. empty strings on an empty match is deliberate.
*/
- if (!re) {
+ if (!re || !re->have_match) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
"regex capture $%" APR_SIZE_T_FMT " refers to no regex in %s",
idx, r->filename);
return NULL;
}
- else {
- if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
- "regex capture $%" APR_SIZE_T_FMT
- " is out of range (last regex was: '%s') in %s",
- idx, re->rexp, r->filename);
- return NULL;
- }
-
- if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
- return NULL;
- }
+ else if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "regex capture $%" APR_SIZE_T_FMT
+ " is out of range (last regex was: '%s') in %s",
+ idx, re->rexp, r->filename);
+ return NULL;
+ }
+ else if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
+ /* I don't think this can happen if have_match is true.
+ * But let's not risk a regression by dropping this
+ */
+ return NULL;
+ }
+ else {
val = apr_pstrmemdup(ctx->dpool, re->source + re->match[idx].rm_so,
re->match[idx].rm_eo - re->match[idx].rm_so);
}
val = apr_table_get(r->subprocess_env, var);
if (val == LAZY_VALUE) {
- val = add_include_vars_lazy(r, var);
+ val = add_include_vars_lazy(r, var, ctx->time_str);
}
}
return val;
}
+static const char *include_expr_var_fn(ap_expr_eval_ctx_t *eval_ctx,
+ const void *data,
+ const char *arg)
+{
+ const char *res, *name = data;
+ include_ctx_t *ctx = eval_ctx->data;
+ if (name[0] == 'e') {
+ /* keep legacy "env" semantics */
+ if ((res = apr_table_get(ctx->r->notes, arg)) != NULL)
+ return res;
+ else if ((res = get_include_var(arg, ctx)) != NULL)
+ return res;
+ else
+ return getenv(arg);
+ }
+ else {
+ return get_include_var(arg, ctx);
+ }
+}
+
+static int include_expr_lookup(ap_expr_lookup_parms *parms)
+{
+ switch (parms->type) {
+ case AP_EXPR_FUNC_STRING:
+ if (strcasecmp(parms->name, "v") == 0 ||
+ strcasecmp(parms->name, "reqenv") == 0 ||
+ strcasecmp(parms->name, "env") == 0) {
+ *parms->func = include_expr_var_fn;
+ *parms->data = parms->name;
+ return OK;
+ }
+ break;
+ /*
+ * We could also make the SSI vars available as %{...} style variables
+ * (AP_EXPR_FUNC_VAR), but this would create problems if we ever want
+ * to cache parsed expressions for performance reasons.
+ */
+ }
+ return ap_run_expr_lookup(parms);
+}
+
+
/*
* Do variable substitution on strings
*
static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
apr_size_t length, int leave_name)
{
- request_rec *r = ctx->intern->r;
+ request_rec *r = ctx->r;
result_item_t *result = NULL, *current = NULL;
apr_size_t outlen = 0, inlen, span;
char *ret = NULL, *eout = NULL;
{
ap_regex_t *compiled;
backref_t *re = ctx->intern->re;
- int rc;
compiled = ap_pregcomp(ctx->dpool, rexp, AP_REG_EXTENDED);
if (!compiled) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->intern->r, "unable to "
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "unable to "
"compile pattern \"%s\"", rexp);
return -1;
}
re->source = apr_pstrdup(ctx->pool, string);
re->rexp = apr_pstrdup(ctx->pool, rexp);
re->nsub = compiled->re_nsub;
- rc = !ap_regexec(compiled, string, AP_MAX_REG_MATCH, re->match, 0);
+ re->have_match = !ap_regexec(compiled, string, AP_MAX_REG_MATCH,
+ re->match, 0);
ap_pregfree(ctx->dpool, compiled);
- return rc;
+ return re->have_match;
}
static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous)
static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
{
parse_node_t *new, *root = NULL, *current = NULL;
- request_rec *r = ctx->intern->r;
+ request_rec *r = ctx->r;
request_rec *rr = NULL;
const char *error = "Invalid expression \"%s\" in file %s";
const char *parse = expr;
- int was_unmatched = 0;
unsigned regex = 0;
*was_error = 0;
*/
CREATE_NODE(ctx, new);
- was_unmatched = get_ptoken(ctx, &parse, &new->token,
- (current != NULL ? ¤t->token : NULL));
- if (!parse) {
- break;
- }
+ {
+#ifdef DEBUG_INCLUDE
+ int was_unmatched =
+#endif
+ get_ptoken(ctx, &parse, &new->token,
+ (current != NULL ? ¤t->token : NULL));
+ if (!parse)
+ break;
- DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
- DEBUG_DUMP_TOKEN(ctx, &new->token);
+ DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
+ DEBUG_DUMP_TOKEN(ctx, &new->token);
+ }
if (!current) {
switch (new->token.type) {
return (root ? root->value : 0);
}
+/* same as above, but use common ap_expr syntax / API */
+static int parse_ap_expr(include_ctx_t *ctx, const char *expr, int *was_error)
+{
+ ap_expr_info_t expr_info;
+ const char *err;
+ int ret;
+ backref_t *re = ctx->intern->re;
+ ap_expr_eval_ctx_t *eval_ctx = ctx->intern->expr_eval_ctx;
+
+ expr_info.filename = ctx->r->filename;
+ expr_info.line_number = 0;
+ expr_info.module_index = APLOG_MODULE_INDEX;
+ expr_info.flags = AP_EXPR_FLAGS_RESTRICTED;
+ err = ap_expr_parse(ctx->r->pool, ctx->r->pool, &expr_info, expr,
+ include_expr_lookup);
+ if (err) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r,
+ "Could not parse expr \"%s\" in %s: %s", expr,
+ ctx->r->filename, err);
+ *was_error = 1;
+ return 0;
+ }
+
+ if (!re) {
+ ctx->intern->re = re = apr_pcalloc(ctx->pool, sizeof(*re));
+ }
+ else {
+ /* ap_expr_exec_ctx() does not care about re->have_match but only about
+ * re->source
+ */
+ if (!re->have_match)
+ re->source = NULL;
+ }
+
+ if (!eval_ctx) {
+ eval_ctx = apr_pcalloc(ctx->pool, sizeof(*eval_ctx));
+ ctx->intern->expr_eval_ctx = eval_ctx;
+ eval_ctx->r = ctx->r;
+ eval_ctx->c = ctx->r->connection;
+ eval_ctx->s = ctx->r->server;
+ eval_ctx->p = ctx->r->pool;
+ eval_ctx->data = ctx;
+ eval_ctx->err = &ctx->intern->expr_err;
+ eval_ctx->vary_this = &ctx->intern->expr_vary_this;
+ eval_ctx->re_nmatch = AP_MAX_REG_MATCH;
+ eval_ctx->re_pmatch = re->match;
+ eval_ctx->re_source = &re->source;
+ }
+
+ eval_ctx->info = &expr_info;
+ ret = ap_expr_exec_ctx(eval_ctx);
+ if (ret < 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r,
+ "Could not evaluate expr \"%s\" in %s: %s", expr,
+ ctx->r->filename, ctx->intern->expr_err);
+ *was_error = 1;
+ return 0;
+ }
+ *was_error = 0;
+ if (re->source)
+ re->have_match = 1;
+ return ret;
+}
/*
* +-------------------------------------------------------+
we never attempt to "run" this sub request. */
rr = ap_sub_req_lookup_file(newpath, r, NULL);
- if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
+ if (rr->status == HTTP_OK && rr->finfo.filetype != APR_NOFILE) {
to_send = rr->filename;
if ((rv = apr_stat(finfo, to_send,
APR_FINFO_GPROT | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS
we never attempt to "run" this sub request. */
rr = ap_sub_req_lookup_uri(tag_val, r, NULL);
- if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
+ if (rr->status == HTTP_OK && rr->finfo.filetype != APR_NOFILE) {
memcpy((char *) finfo, (const char *) &rr->finfo,
sizeof(rr->finfo));
ap_destroy_sub_req(rr);
}
/*
- * <!--#include virtual|file="..." [virtual|file="..."] ... -->
+ * <!--#include virtual|file="..." [onerror|virtual|file="..."] ... -->
+ *
+ * Output each file/virtual in turn until one of them returns an error.
+ * On error, ignore all further file/virtual attributes until we reach
+ * an onerror attribute, where we make an attempt to serve the onerror
+ * virtual url. If onerror fails, or no onerror is present, the default
+ * error string is inserted into the stream.
*/
static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
apr_bucket_brigade *bb)
{
request_rec *r = f->r;
+ char *last_error;
if (!ctx->argc) {
ap_log_rerror(APLOG_MARK,
return APR_SUCCESS;
}
+ last_error = NULL;
while (1) {
char *tag = NULL;
char *tag_val = NULL;
break;
}
- if (strcmp(tag, "virtual") && strcmp(tag, "file")) {
+ if (strcmp(tag, "virtual") && strcmp(tag, "file") && strcmp(tag,
+ "onerror")) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
"\"%s\" to tag include in %s", tag, r->filename);
SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
rr = ap_sub_req_lookup_file(newpath, r, f->next);
}
}
+ else if ((tag[0] == 'v' && !last_error)
+ || (tag[0] == 'o' && last_error)) {
+ if (r->kept_body) {
+ rr = ap_sub_req_method_uri(r->method, parsed_string, r, f->next);
+ }
+ else {
+ rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
+ }
+ }
else {
- rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
+ continue;
}
if (!error_fmt && rr->status != HTTP_OK) {
if (error_fmt) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val,
- r->filename);
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ r->filename);
+ if (last_error) {
+ /* onerror threw an error, give up completely */
+ break;
+ }
+ last_error = error_fmt;
+ }
+ else {
+ last_error = NULL;
}
/* Do *not* destroy the subrequest here; it may have allocated
* r->pool, so that pool must survive as long as this request.
* Yes, this is a memory leak. */
- if (error_fmt) {
- break;
- }
+ }
+
+ if (last_error) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
}
return APR_SUCCESS;
}
/*
- * <!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... -->
+ * <!--#echo [decoding="..."] [encoding="..."] var="..." [decoding="..."]
+ * [encoding="..."] var="..." ... -->
*/
static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
apr_bucket_brigade *bb)
{
- enum {E_NONE, E_URL, E_ENTITY} encode;
+ const char *encoding = "entity", *decoding = "none";
request_rec *r = f->r;
+ int error = 0;
if (!ctx->argc) {
ap_log_rerror(APLOG_MARK,
return APR_SUCCESS;
}
- encode = E_ENTITY;
-
while (1) {
char *tag = NULL;
char *tag_val = NULL;
ctx);
if (val) {
- switch(encode) {
- case E_NONE:
- echo_text = val;
- break;
- case E_URL:
- echo_text = ap_escape_uri(ctx->dpool, val);
- break;
- case E_ENTITY:
- echo_text = ap_escape_html(ctx->dpool, val);
- break;
+ char *last = NULL;
+ char *e, *d, *token;
+
+ echo_text = val;
+
+ d = apr_pstrdup(ctx->pool, decoding);
+ token = apr_strtok(d, ", \t", &last);
+
+ while(token) {
+ if (!strcasecmp(token, "none")) {
+ /* do nothing */
+ }
+ else if (!strcasecmp(token, "url")) {
+ char *buf = apr_pstrdup(ctx->pool, echo_text);
+ ap_unescape_url(buf);
+ echo_text = buf;
+ }
+ else if (!strcasecmp(token, "entity")) {
+ char *buf = apr_pstrdup(ctx->pool, echo_text);
+ decodehtml(buf);
+ echo_text = buf;
+ }
+ else if (!strcasecmp(token, "base64")) {
+ echo_text = ap_pbase64decode(ctx->dpool, echo_text);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
+ "\"%s\" to parameter \"decoding\" of tag echo in "
+ "%s", token, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ error = 1;
+ break;
+ }
+ token = apr_strtok(NULL, ", \t", &last);
+ }
+
+ e = apr_pstrdup(ctx->pool, encoding);
+ token = apr_strtok(e, ", \t", &last);
+
+ while(token) {
+ if (!strcasecmp(token, "none")) {
+ /* do nothing */
+ }
+ else if (!strcasecmp(token, "url")) {
+ echo_text = ap_escape_uri(ctx->dpool, echo_text);
+ }
+ else if (!strcasecmp(token, "entity")) {
+ echo_text = ap_escape_html2(ctx->dpool, echo_text, 0);
+ }
+ else if (!strcasecmp(token, "base64")) {
+ char *buf;
+ buf = ap_pbase64encode(ctx->dpool, (char *)echo_text);
+ echo_text = buf;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
+ "\"%s\" to parameter \"encoding\" of tag echo in "
+ "%s", token, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ error = 1;
+ break;
+ }
+ token = apr_strtok(NULL, ", \t", &last);
}
e_len = strlen(echo_text);
e_len = ctx->intern->undefined_echo_len;
}
+ if (error) {
+ break;
+ }
+
APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
apr_pmemdup(ctx->pool, echo_text, e_len),
e_len, ctx->pool, f->c->bucket_alloc));
}
+ else if (!strcmp(tag, "decoding")) {
+ decoding = tag_val;
+ }
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_ERR, 0, r, "unknown value "
- "\"%s\" to parameter \"encoding\" of tag echo in "
- "%s", tag_val, r->filename);
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
- break;
- }
+ encoding = tag_val;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
DEBUG_PRINTF((ctx, "**** if expr=\"%s\"\n", expr));
- expr_ret = parse_expr(ctx, expr, &was_error);
+ if (ctx->intern->legacy_expr)
+ expr_ret = parse_expr(ctx, expr, &was_error);
+ else
+ expr_ret = parse_ap_expr(ctx, expr, &was_error);
if (was_error) {
SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
apr_bucket_brigade *bb)
{
+ const char *encoding = "none", *decoding = "none";
char *var = NULL;
request_rec *r = f->r;
request_rec *sub = r->main;
apr_pool_t *p = r->pool;
+ int error = 0;
if (ctx->argc < 2) {
ap_log_rerror(APLOG_MARK,
char *tag = NULL;
char *tag_val = NULL;
- ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
if (!tag || !tag_val) {
break;
}
if (!strcmp(tag, "var")) {
+ decodehtml(tag_val);
var = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
SSI_EXPAND_DROP_NAME);
}
+ else if (!strcmp(tag, "decoding")) {
+ decoding = tag_val;
+ }
+ else if (!strcmp(tag, "encoding")) {
+ encoding = tag_val;
+ }
else if (!strcmp(tag, "value")) {
char *parsed_string;
parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
SSI_EXPAND_DROP_NAME);
+
+ if (parsed_string) {
+ char *last = NULL;
+ char *e, *d, *token;
+
+ d = apr_pstrdup(ctx->pool, decoding);
+ token = apr_strtok(d, ", \t", &last);
+
+ while(token) {
+ if (!strcasecmp(token, "none")) {
+ /* do nothing */
+ }
+ else if (!strcasecmp(token, "url")) {
+ char *buf = apr_pstrdup(ctx->pool, parsed_string);
+ ap_unescape_url(buf);
+ parsed_string = buf;
+ }
+ else if (!strcasecmp(token, "entity")) {
+ char *buf = apr_pstrdup(ctx->pool, parsed_string);
+ decodehtml(buf);
+ parsed_string = buf;
+ }
+ else if (!strcasecmp(token, "base64")) {
+ parsed_string = ap_pbase64decode(ctx->dpool, parsed_string);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
+ "\"%s\" to parameter \"decoding\" of tag set in "
+ "%s", token, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ error = 1;
+ break;
+ }
+ token = apr_strtok(NULL, ", \t", &last);
+ }
+
+ e = apr_pstrdup(ctx->pool, encoding);
+ token = apr_strtok(e, ", \t", &last);
+
+ while(token) {
+ if (!strcasecmp(token, "none")) {
+ /* do nothing */
+ }
+ else if (!strcasecmp(token, "url")) {
+ parsed_string = ap_escape_uri(ctx->dpool, parsed_string);
+ }
+ else if (!strcasecmp(token, "entity")) {
+ parsed_string = ap_escape_html2(ctx->dpool, parsed_string, 0);
+ }
+ else if (!strcasecmp(token, "base64")) {
+ char *buf;
+ buf = ap_pbase64encode(ctx->dpool, (char *)parsed_string);
+ parsed_string = buf;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
+ "\"%s\" to parameter \"encoding\" of tag set in "
+ "%s", token, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ error = 1;
+ break;
+ }
+ token = apr_strtok(NULL, ", \t", &last);
+ }
+
+ }
+
+ if (error) {
+ break;
+ }
+
apr_table_setn(r->subprocess_env, apr_pstrdup(p, var),
apr_pstrdup(p, parsed_string));
}
/* get value */
val_text = elts[i].val;
if (val_text == LAZY_VALUE) {
- val_text = add_include_vars_lazy(r, elts[i].key);
+ val_text = add_include_vars_lazy(r, elts[i].key, ctx->time_str);
}
- val_text = ap_escape_html(ctx->dpool, elts[i].val);
+ val_text = ap_escape_html(ctx->dpool, val_text);
v_len = strlen(val_text);
/* assemble result */
if (!intern->directive_len) {
intern->error = 1;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing "
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing "
"directive name in parsed document %s",
- intern->r->filename);
+ ctx->r->filename);
}
else {
char *sp = intern->directive;
intern->current_arg->name_len = 0;
intern->error = 1;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing "
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing "
"argument name for value to tag %s in %s",
- apr_pstrmemdup(intern->r->pool, intern->directive,
+ apr_pstrmemdup(ctx->r->pool, intern->directive,
intern->directive_len),
- intern->r->filename);
+ ctx->r->filename);
return (p - data);
intern->current_arg->name_len);
if (!intern->current_arg->name_len) {
intern->error = 1;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing "
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing "
"argument name for value to tag %s in %s",
- apr_pstrmemdup(intern->r->pool, intern->directive,
+ apr_pstrmemdup(ctx->r->pool, intern->directive,
intern->directive_len),
- intern->r->filename);
+ ctx->r->filename);
}
else {
char *sp = intern->current_arg->name;
/* initialization for this loop */
intern->bytes_read = 0;
intern->error = 0;
- intern->r = r;
ctx->flush_now = 0;
/* loop over the current bucket brigade */
if (store) {
if (index) {
APR_BUCKET_REMOVE(b);
+ apr_bucket_setaside(b, r->pool);
APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
b = newb;
}
if (store) {
if (index) {
APR_BUCKET_REMOVE(b);
+ apr_bucket_setaside(b, r->pool);
APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
b = newb;
}
default: /* partial match */
newb = APR_BUCKET_NEXT(b);
APR_BUCKET_REMOVE(b);
+ apr_bucket_setaside(b, r->pool);
APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
b = newb;
break;
* We don't know if we are going to be including a file or executing
* a program - in either case a strong ETag header will likely be invalid.
*/
- apr_table_setn(f->r->notes, "no-etag", "");
+ if (conf->etag <= 0) {
+ apr_table_setn(f->r->notes, "no-etag", "");
+ }
return OK;
}
/* create context for this filter */
f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx));
+ ctx->r = r;
ctx->intern = intern = apr_palloc(r->pool, sizeof(*ctx->intern));
ctx->pool = r->pool;
apr_pool_create(&ctx->dpool, ctx->pool);
intern->seen_eos = 0;
intern->state = PARSE_PRE_HEAD;
ctx->flags = (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
- if (ap_allow_options(r) & OPT_INCNOEXEC) {
+ if ((ap_allow_options(r) & OPT_INC_WITH_EXEC) == 0) {
ctx->flags |= SSI_FLAG_NO_EXEC;
}
- intern->accessenable = conf->accessenable;
+ intern->accessenable = (conf->accessenable > 0);
+ intern->legacy_expr = (conf->legacy_expr > 0);
+ intern->expr_eval_ctx = NULL;
+ intern->expr_err = NULL;
+ intern->expr_vary_this = NULL;
ctx->if_nesting_level = 0;
intern->re = NULL;
- ctx->error_str = conf->default_error_msg;
- ctx->time_str = conf->default_time_fmt;
+ ctx->error_str = conf->default_error_msg ? conf->default_error_msg :
+ DEFAULT_ERROR_MSG;
+ ctx->time_str = conf->default_time_fmt ? conf->default_time_fmt :
+ DEFAULT_TIME_FORMAT;
intern->start_seq = sconf->default_start_tag;
intern->start_seq_pat = bndm_compile(ctx->pool, intern->start_seq,
strlen(intern->start_seq));
intern->end_seq = sconf->default_end_tag;
intern->end_seq_len = strlen(intern->end_seq);
- intern->undefined_echo = conf->undefined_echo;
- intern->undefined_echo_len = strlen(conf->undefined_echo);
+ intern->undefined_echo = conf->undefined_echo ? conf->undefined_echo :
+ DEFAULT_UNDEFINED_ECHO;
+ intern->undefined_echo_len = strlen(intern->undefined_echo);
}
if ((parent = ap_get_module_config(r->request_config, &include_module))) {
* environment */
ap_add_common_vars(r);
ap_add_cgi_vars(r);
- add_include_vars(r, conf->default_time_fmt);
+ add_include_vars(r);
}
/* Always unset the content-length. There is no way to know if
* the content will be modified at some point by send_parsed_content.
* a program which may change the Last-Modified header or make the
* content completely dynamic. Therefore, we can't support these
* headers.
- * Exception: XBitHack full means we *should* set the Last-Modified field.
+ *
+ * Exception: XBitHack full means we *should* set the
+ * Last-Modified field.
+ *
+ * SSILastModified on means we *should* set the Last-Modified field
+ * if not present, or respect an existing value if present.
*/
+ /* Must we respect the last modified header? By default, no */
+ if (conf->lastmodified > 0) {
+
+ /* update the last modified if we have a valid time, and only if
+ * we don't already have a valid last modified.
+ */
+ if (r->finfo.valid & APR_FINFO_MTIME
+ && !apr_table_get(f->r->headers_out, "Last-Modified")) {
+ ap_update_mtime(r, r->finfo.mtime);
+ ap_set_last_modified(r);
+ }
+
+ }
+
/* Assure the platform supports Group protections */
- if ((conf->xbithack == XBITHACK_FULL)
+ else if (((conf->xbithack == XBITHACK_FULL ||
+ (conf->xbithack == XBITHACK_UNSET &&
+ DEFAULT_XBITHACK == XBITHACK_FULL))
&& (r->finfo.valid & APR_FINFO_GPROT)
- && (r->finfo.protection & APR_GEXECUTE)) {
+ && (r->finfo.protection & APR_GEXECUTE))) {
ap_update_mtime(r, r->finfo.mtime);
ap_set_last_modified(r);
}
static int include_fixup(request_rec *r)
{
- include_dir_config *conf;
-
- conf = ap_get_module_config(r->per_dir_config, &include_module);
-
if (r->handler && (strcmp(r->handler, "server-parsed") == 0))
{
if (!r->content_type || !*r->content_type) {
}
#else
{
- if (conf->xbithack == XBITHACK_OFF) {
+ include_dir_config *conf = ap_get_module_config(r->per_dir_config,
+ &include_module);
+
+ if (conf->xbithack == XBITHACK_OFF ||
+ (DEFAULT_XBITHACK == XBITHACK_OFF &&
+ conf->xbithack == XBITHACK_UNSET))
+ {
return DECLINED;
}
return DECLINED;
}
- if (!r->content_type || strcmp(r->content_type, "text/html")) {
+ if (!r->content_type || strncmp(r->content_type, "text/html", 9)) {
return DECLINED;
}
}
static void *create_includes_dir_config(apr_pool_t *p, char *dummy)
{
- include_dir_config *result = apr_palloc(p, sizeof(include_dir_config));
+ include_dir_config *result = apr_pcalloc(p, sizeof(include_dir_config));
- result->default_error_msg = DEFAULT_ERROR_MSG;
- result->default_time_fmt = DEFAULT_TIME_FORMAT;
- result->undefined_echo = DEFAULT_UNDEFINED_ECHO;
- result->xbithack = DEFAULT_XBITHACK;
+ result->xbithack = XBITHACK_UNSET;
+ result->accessenable = UNSET;
+ result->lastmodified = UNSET;
+ result->etag = UNSET;
+ result->accessenable = UNSET;
return result;
}
+#define MERGE(b,o,n,val,unset) n->val = o->val != unset ? o->val : b->val
+static void *merge_includes_dir_config(apr_pool_t *p, void *basev, void *overridesv)
+{
+ include_dir_config *base = (include_dir_config *)basev,
+ *over = (include_dir_config *)overridesv,
+ *new = apr_palloc(p, sizeof(include_dir_config));
+ MERGE(base, over, new, default_error_msg, NULL);
+ MERGE(base, over, new, default_time_fmt, NULL);
+ MERGE(base, over, new, undefined_echo, NULL);
+ MERGE(base, over, new, xbithack, XBITHACK_UNSET);
+ MERGE(base, over, new, accessenable, UNSET);
+ MERGE(base, over, new, lastmodified, UNSET);
+ MERGE(base, over, new, etag, UNSET);
+ MERGE(base, over, new, legacy_expr, UNSET);
+ return new;
+}
+
static void *create_includes_server_config(apr_pool_t *p, server_rec *server)
{
include_server_config *result;
"SSI End String Tag"),
AP_INIT_TAKE1("SSIUndefinedEcho", set_undefined_echo, NULL, OR_ALL,
"String to be displayed if an echoed variable is undefined"),
- AP_INIT_FLAG("SSIAccessEnable", ap_set_flag_slot,
+ AP_INIT_FLAG("SSIAccessEnable", ap_set_flag_slot_char,
(void *)APR_OFFSETOF(include_dir_config, accessenable),
OR_LIMIT, "Whether testing access is enabled. Limited to 'on' or 'off'"),
+ AP_INIT_FLAG("SSILegacyExprParser", ap_set_flag_slot_char,
+ (void *)APR_OFFSETOF(include_dir_config, legacy_expr),
+ OR_LIMIT,
+ "Whether to use the legacy expression parser compatible "
+ "with <= 2.2.x. Limited to 'on' or 'off'"),
+ AP_INIT_FLAG("SSILastModified", ap_set_flag_slot_char,
+ (void *)APR_OFFSETOF(include_dir_config, lastmodified),
+ OR_LIMIT, "Whether to set the last modified header or respect "
+ "an existing header. Limited to 'on' or 'off'"),
+ AP_INIT_FLAG("SSIEtag", ap_set_flag_slot_char,
+ (void *)APR_OFFSETOF(include_dir_config, etag),
+ OR_LIMIT, "Whether to allow the generation of ETags within the server. "
+ "Existing ETags will be preserved. Limited to 'on' or 'off'"),
{NULL}
};
AP_FTYPE_RESOURCE);
}
-module AP_MODULE_DECLARE_DATA include_module =
+AP_DECLARE_MODULE(include) =
{
STANDARD20_MODULE_STUFF,
create_includes_dir_config, /* dir config creater */
- NULL, /* dir merger --- default is to override */
+ merge_includes_dir_config, /* dir config merger */
create_includes_server_config,/* server config */
NULL, /* merge server config */
includes_cmds, /* command apr_table_t */