/[Apache-SVN]/httpd/httpd/trunk/modules/filters/mod_include.c
ViewVC logotype

Contents of /httpd/httpd/trunk/modules/filters/mod_include.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 730296 - (hide annotations)
Wed Dec 31 02:27:24 2008 UTC (10 months, 3 weeks ago) by niq
File MIME type: text/plain
File size: 104682 byte(s)
Add support for escaping all non-ascii chars to ap_escape_html, and use
it to fix PR#25202: encoding="entity" doesn't work as advertised in
mod_include.
For backport, this'll need an ABI-preserving version that'll be
a minor MMN bump.  But if we do that in /trunk/, it'll never change.
1 fielding 420983 /* Licensed to the Apache Software Foundation (ASF) under one or more
2     * contributor license agreements. See the NOTICE file distributed with
3     * this work for additional information regarding copyright ownership.
4     * The ASF licenses this file to You under the Apache License, Version 2.0
5     * (the "License"); you may not use this file except in compliance with
6     * the License. You may obtain a copy of the License at
7 fielding 83751 *
8 nd 102525 * http://www.apache.org/licenses/LICENSE-2.0
9 fielding 83751 *
10 nd 102525 * Unless required by applicable law or agreed to in writing, software
11     * distributed under the License is distributed on an "AS IS" BASIS,
12     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     * See the License for the specific language governing permissions and
14     * limitations under the License.
15 fielding 83751 */
16    
17 gstein 87080 #include "apr.h"
18     #include "apr_strings.h"
19     #include "apr_thread_proc.h"
20 rbb 87241 #include "apr_hash.h"
21 wrowe 87903 #include "apr_user.h"
22 gstein 88060 #include "apr_lib.h"
23 gstein 88061 #include "apr_optional.h"
24 gstein 87080
25 gstein 88061 #define APR_WANT_STRFUNC
26 nd 101048 #define APR_WANT_MEMFUNC
27 gstein 88061 #include "apr_want.h"
28    
29 rbb 84497 #include "ap_config.h"
30 rbb 86211 #include "util_filter.h"
31 fielding 83751 #include "httpd.h"
32     #include "http_config.h"
33 rbb 88417 #include "http_core.h"
34 fielding 83751 #include "http_request.h"
35     #include "http_core.h"
36     #include "http_protocol.h"
37     #include "http_log.h"
38     #include "http_main.h"
39     #include "util_script.h"
40 rbb 85449 #include "http_core.h"
41 rbb 87067 #include "mod_include.h"
42 niq 642559 #include "ap_expr.h"
43 nd 101046
44 nd 101048 /* helper for Latin1 <-> entity encoding */
45 nd 101046 #if APR_CHARSET_EBCDIC
46 trawick 85222 #include "util_ebcdic.h"
47 nd 101046 #define RAW_ASCII_CHAR(ch) apr_xlate_conv_byte(ap_hdrs_from_ascii, \
48     (unsigned char)ch)
49 nd 101049 #else /* APR_CHARSET_EBCDIC */
50 nd 101046 #define RAW_ASCII_CHAR(ch) (ch)
51 nd 101049 #endif /* !APR_CHARSET_EBCDIC */
52 fielding 83751
53 nd 101049
54     /*
55     * +-------------------------------------------------------+
56     * | |
57     * | Types and Structures
58     * | |
59     * +-------------------------------------------------------+
60 jerenkrantz 90872 */
61    
62 nd 101070 /* sll used for string expansion */
63     typedef struct result_item {
64     struct result_item *next;
65     apr_size_t len;
66     const char *string;
67     } result_item_t;
68    
69 nd 101052 typedef enum {
70     XBITHACK_OFF,
71     XBITHACK_ON,
72     XBITHACK_FULL
73     } xbithack_t;
74 jerenkrantz 90872
75 nd 101046 typedef struct {
76 nd 101052 const char *default_error_msg;
77     const char *default_time_fmt;
78 nd 101129 const char *undefined_echo;
79 nd 101052 xbithack_t xbithack;
80 minfrin 571872 const int accessenable;
81 jerenkrantz 90872 } include_dir_config;
82    
83 ianh 92660 typedef struct {
84 nd 101052 const char *default_start_tag;
85     const char *default_end_tag;
86 ianh 92660 } include_server_config;
87    
88 nd 101036 /* main parser states */
89     typedef enum {
90     PARSE_PRE_HEAD,
91     PARSE_HEAD,
92     PARSE_DIRECTIVE,
93     PARSE_DIRECTIVE_POSTNAME,
94     PARSE_DIRECTIVE_TAIL,
95     PARSE_DIRECTIVE_POSTTAIL,
96     PARSE_PRE_ARG,
97     PARSE_ARG,
98     PARSE_ARG_NAME,
99     PARSE_ARG_POSTNAME,
100     PARSE_ARG_EQ,
101     PARSE_ARG_PREVAL,
102     PARSE_ARG_VAL,
103     PARSE_ARG_VAL_ESC,
104     PARSE_ARG_POSTVAL,
105     PARSE_TAIL,
106     PARSE_TAIL_SEQ,
107     PARSE_EXECUTE
108     } parse_state_t;
109    
110 nd 101048 typedef struct arg_item {
111     struct arg_item *next;
112     char *name;
113     apr_size_t name_len;
114     char *value;
115 nd 101052 apr_size_t value_len;
116 nd 101048 } arg_item_t;
117 nd 101036
118 nd 101073 typedef struct {
119 nd 101078 unsigned int T[256];
120     unsigned int x;
121     apr_size_t pattern_len;
122     } bndm_t;
123    
124 nd 101046 struct ssi_internal_ctx {
125 nd 101036 parse_state_t state;
126     int seen_eos;
127     int error;
128     char quote; /* quote character value (or \0) */
129 nd 101052 apr_size_t parse_pos; /* parse position of partial matches */
130 nd 101046 apr_size_t bytes_read;
131 nd 101036
132     apr_bucket_brigade *tmp_bb;
133    
134 nd 101046 request_rec *r;
135     const char *start_seq;
136     bndm_t *start_seq_pat;
137     const char *end_seq;
138 nd 101036 apr_size_t end_seq_len;
139     char *directive; /* name of the current directive */
140 nd 101046 apr_size_t directive_len; /* length of the current directive name */
141 nd 101036
142 nd 101048 arg_item_t *current_arg; /* currently parsed argument */
143     arg_item_t *argv; /* all arguments */
144 nd 101036
145 nd 101073 backref_t *re; /* NULL if there wasn't a regex yet */
146 nd 101078
147 nd 101129 const char *undefined_echo;
148     apr_size_t undefined_echo_len;
149    
150 niq 642559 opt_func_t access_func; /* is using the access tests allowed? */
151 minfrin 572136
152 niq 642559 /* breadcrumb to track whether child request should have parent's env */
153     request_rec *kludge_child;
154 nd 101078 #ifdef DEBUG_INCLUDE
155     struct {
156     ap_filter_t *f;
157     apr_bucket_brigade *bb;
158     } debug;
159     #endif
160 nd 101046 };
161 nd 101036
162 nd 101049
163     /*
164     * +-------------------------------------------------------+
165     * | |
166 nd 101078 * | Debugging Utilities
167     * | |
168     * +-------------------------------------------------------+
169     */
170    
171     #ifdef DEBUG_INCLUDE
172 nd 101087
173     #define TYPE_TOKEN(token, ttype) do { \
174     (token)->type = ttype; \
175     (token)->s = #ttype; \
176     } while(0)
177    
178     #define CREATE_NODE(ctx, name) do { \
179     (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
180     (name)->parent = (name)->left = (name)->right = NULL; \
181     (name)->done = 0; \
182     (name)->dump_done = 0; \
183     } while(0)
184    
185 nd 101078 static void debug_printf(include_ctx_t *ctx, const char *fmt, ...)
186     {
187     va_list ap;
188     char *debug__str;
189    
190     va_start(ap, fmt);
191     debug__str = apr_pvsprintf(ctx->pool, fmt, ap);
192     va_end(ap);
193    
194     APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
195     debug__str, strlen(debug__str), ctx->pool,
196     ctx->intern->debug.f->c->bucket_alloc));
197     }
198    
199 nd 101087 #define DUMP__CHILD(ctx, is, node, child) if (1) { \
200     parse_node_t *d__c = node->child; \
201     if (d__c) { \
202     if (!d__c->dump_done) { \
203     if (d__c->parent != node) { \
204     debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
205     if (!d__c->parent) { \
206     debug_printf(ctx, "Parent of " #child " child node is " \
207     "NULL.\n"); \
208     } \
209     else { \
210     debug_printf(ctx, "Parent of " #child " child node " \
211     "points to another node (of type %s)!\n", \
212     d__c->parent->token.s); \
213     } \
214     return; \
215     } \
216     node = d__c; \
217     continue; \
218     } \
219     } \
220     else { \
221     debug_printf(ctx, "%s(missing)\n", is); \
222     } \
223     }
224    
225     static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
226     {
227     parse_node_t *current;
228     char *is;
229    
230     if (!root) {
231     debug_printf(ctx, " -- Parse Tree empty --\n\n");
232     return;
233     }
234    
235     debug_printf(ctx, " ----- Parse Tree -----\n");
236     current = root;
237     is = " ";
238    
239     while (current) {
240     switch (current->token.type) {
241     case TOKEN_STRING:
242     case TOKEN_RE:
243     debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
244     current->token.value);
245     current->dump_done = 1;
246     current = current->parent;
247     continue;
248    
249     case TOKEN_NOT:
250 nd 101115 case TOKEN_GROUP:
251 nd 101095 case TOKEN_RBRACE:
252     case TOKEN_LBRACE:
253 nd 101087 if (!current->dump_done) {
254     debug_printf(ctx, "%s%s\n", is, current->token.s);
255     is = apr_pstrcat(ctx->dpool, is, " ", NULL);
256     current->dump_done = 1;
257     }
258    
259     DUMP__CHILD(ctx, is, current, right)
260    
261     if (!current->right || current->right->dump_done) {
262     is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
263     if (current->right) current->right->dump_done = 0;
264     current = current->parent;
265     }
266     continue;
267    
268 nd 101095 default:
269 nd 101087 if (!current->dump_done) {
270 nd 101095 debug_printf(ctx, "%s%s\n", is, current->token.s);
271 nd 101087 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
272     current->dump_done = 1;
273     }
274    
275 nd 101095 DUMP__CHILD(ctx, is, current, left)
276 nd 101087 DUMP__CHILD(ctx, is, current, right)
277    
278 nd 101095 if ((!current->left || current->left->dump_done) &&
279     (!current->right || current->right->dump_done)) {
280    
281 nd 101087 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
282 nd 101095 if (current->left) current->left->dump_done = 0;
283 nd 101087 if (current->right) current->right->dump_done = 0;
284     current = current->parent;
285     }
286     continue;
287     }
288     }
289    
290     /* it is possible to call this function within the parser loop, to see
291     * how the tree is built. That way, we must cleanup after us to dump
292     * always the whole tree
293     */
294     root->dump_done = 0;
295     if (root->left) root->left->dump_done = 0;
296     if (root->right) root->right->dump_done = 0;
297    
298     debug_printf(ctx, " --- End Parse Tree ---\n\n");
299    
300     return;
301     }
302    
303 nd 101078 #define DEBUG_INIT(ctx, filter, brigade) do { \
304     (ctx)->intern->debug.f = filter; \
305     (ctx)->intern->debug.bb = brigade; \
306     } while(0)
307    
308     #define DEBUG_PRINTF(arg) debug_printf arg
309    
310     #define DEBUG_DUMP_TOKEN(ctx, token) do { \
311     token_t *d__t = (token); \
312     \
313     if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) { \
314 nd 101095 DEBUG_PRINTF(((ctx), " Found: %s (%s)\n", d__t->s, d__t->value)); \
315 nd 101078 } \
316     else { \
317 nd 101095 DEBUG_PRINTF((ctx, " Found: %s\n", d__t->s)); \
318 nd 101078 } \
319     } while(0)
320    
321 nd 101107 #define DEBUG_DUMP_EVAL(ctx, node) do { \
322     char c = '"'; \
323     switch ((node)->token.type) { \
324     case TOKEN_STRING: \
325     debug_printf((ctx), " Evaluate: %s (%s) -> %c\n", (node)->token.s,\
326     (node)->token.value, ((node)->value) ? '1':'0'); \
327     break; \
328     case TOKEN_AND: \
329     case TOKEN_OR: \
330     debug_printf((ctx), " Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
331     (node)->token.s, \
332     (((node)->left->done) ? ((node)->left->value ?"1":"0") \
333     : "short circuited"), \
334     (((node)->right->done) ? ((node)->right->value?"1":"0") \
335     : "short circuited"), \
336     (node)->value ? '1' : '0'); \
337     break; \
338     case TOKEN_EQ: \
339     case TOKEN_NE: \
340     case TOKEN_GT: \
341     case TOKEN_GE: \
342     case TOKEN_LT: \
343     case TOKEN_LE: \
344     if ((node)->right->token.type == TOKEN_RE) c = '/'; \
345     debug_printf((ctx), " Compare: %s (\"%s\" with %c%s%c) -> %c\n", \
346     (node)->token.s, \
347     (node)->left->token.value, \
348     c, (node)->right->token.value, c, \
349     (node)->value ? '1' : '0'); \
350     break; \
351     default: \
352     debug_printf((ctx), " Evaluate: %s -> %c\n", (node)->token.s, \
353     (node)->value ? '1' : '0'); \
354     break; \
355     } \
356     } while(0)
357    
358 nd 101081 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched) do { \
359     if (unmatched) { \
360     DEBUG_PRINTF(((ctx), " Unmatched %c\n", (char)(unmatched))); \
361     } \
362 nd 101078 } while(0)
363    
364     #define DEBUG_DUMP_COND(ctx, text) \
365     DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text), \
366     ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
367    
368 nd 101087 #define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
369    
370 nd 101078 #else /* DEBUG_INCLUDE */
371    
372     #define TYPE_TOKEN(token, ttype) (token)->type = ttype
373 nd 101087
374     #define CREATE_NODE(ctx, name) do { \
375     (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
376     (name)->parent = (name)->left = (name)->right = NULL; \
377     (name)->done = 0; \
378     } while(0)
379    
380 nd 101078 #define DEBUG_INIT(ctx, f, bb)
381     #define DEBUG_PRINTF(arg)
382     #define DEBUG_DUMP_TOKEN(ctx, token)
383 nd 101107 #define DEBUG_DUMP_EVAL(ctx, node)
384 nd 101078 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
385     #define DEBUG_DUMP_COND(ctx, text)
386 nd 101087 #define DEBUG_DUMP_TREE(ctx, root)
387 nd 101078
388     #endif /* !DEBUG_INCLUDE */
389    
390    
391     /*
392     * +-------------------------------------------------------+
393     * | |
394 nd 101049 * | Static Module Data
395     * | |
396     * +-------------------------------------------------------+
397     */
398    
399     /* global module structure */
400     module AP_MODULE_DECLARE_DATA include_module;
401    
402     /* function handlers for include directives */
403     static apr_hash_t *include_handlers;
404    
405     /* forward declaration of handler registry */
406     static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
407    
408     /* Sentinel value to store in subprocess_env for items that
409     * shouldn't be evaluated until/unless they're actually used
410     */
411     static const char lazy_eval_sentinel;
412     #define LAZY_VALUE (&lazy_eval_sentinel)
413    
414     /* default values */
415 nd 101048 #define DEFAULT_START_SEQUENCE "<!--#"
416     #define DEFAULT_END_SEQUENCE "-->"
417 nd 101046 #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
418     #define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
419     #define DEFAULT_UNDEFINED_ECHO "(none)"
420 nd 101036
421 jerenkrantz 90872 #ifdef XBITHACK
422 nd 101052 #define DEFAULT_XBITHACK XBITHACK_FULL
423 jerenkrantz 90872 #else
424 nd 101052 #define DEFAULT_XBITHACK XBITHACK_OFF
425 jerenkrantz 90872 #endif
426    
427 fielding 83751
428 nd 101049 /*
429     * +-------------------------------------------------------+
430     * | |
431     * | Environment/Expansion Functions
432     * | |
433     * +-------------------------------------------------------+
434 jerenkrantz 90872 */
435    
436 fielding 83751 /*
437     * decodes a string containing html entities or numeric character references.
438     * 's' is overwritten with the decoded string.
439     * If 's' is syntatically incorrect, then the followed fixups will be made:
440     * unknown entities will be left undecoded;
441     * references to unused numeric characters will be deleted.
442     * In particular, &#00; will not be decoded, but will be deleted.
443     */
444    
445     /* maximum length of any ISO-LATIN-1 HTML entity name. */
446     #define MAXENTLEN (6)
447    
448     /* The following is a shrinking transformation, therefore safe. */
449    
450     static void decodehtml(char *s)
451     {
452     int val, i, j;
453 brianp 92284 char *p;
454 fielding 83751 const char *ents;
455     static const char * const entlist[MAXENTLEN + 1] =
456     {
457 nd 101074 NULL, /* 0 */
458     NULL, /* 1 */
459     "lt\074gt\076", /* 2 */
460     "amp\046ETH\320eth\360", /* 3 */
461     "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml"
462     "\353iuml\357ouml\366uuml\374yuml\377", /* 4 */
463    
464     "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc"
465     "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352"
466     "icirc\356ocirc\364ucirc\373thorn\376", /* 5 */
467    
468     "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311"
469     "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde"
470     "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340"
471     "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave"
472     "\354iacute\355ntilde\361ograve\362oacute\363otilde\365"
473     "oslash\370ugrave\371uacute\372yacute\375" /* 6 */
474 fielding 83751 };
475    
476 brianp 92284 /* Do a fast scan through the string until we find anything
477     * that needs more complicated handling
478     */
479     for (; *s != '&'; s++) {
480     if (*s == '\0') {
481     return;
482     }
483     }
484    
485     for (p = s; *s != '\0'; s++, p++) {
486 fielding 83751 if (*s != '&') {
487     *p = *s;
488     continue;
489     }
490     /* find end of entity */
491     for (i = 1; s[i] != ';' && s[i] != '\0'; i++) {
492     continue;
493     }
494    
495     if (s[i] == '\0') { /* treat as normal data */
496     *p = *s;
497     continue;
498     }
499    
500     /* is it numeric ? */
501     if (s[1] == '#') {
502 wrowe 86008 for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
503 fielding 83751 val = val * 10 + s[j] - '0';
504     }
505     s += i;
506     if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
507     (val >= 127 && val <= 160) || val >= 256) {
508     p--; /* no data to output */
509     }
510     else {
511     *p = RAW_ASCII_CHAR(val);
512     }
513     }
514     else {
515     j = i - 1;
516     if (j > MAXENTLEN || entlist[j] == NULL) {
517     /* wrong length */
518     *p = '&';
519     continue; /* skip it */
520     }
521     for (ents = entlist[j]; *ents != '\0'; ents += i) {
522     if (strncmp(s + 1, ents, j) == 0) {
523     break;
524     }
525     }
526    
527     if (*ents == '\0') {
528     *p = '&'; /* unknown */
529     }
530     else {
531     *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]);
532     s += i;
533     }
534     }
535     }
536    
537     *p = '\0';
538     }
539    
540 nd 101052 static void add_include_vars(request_rec *r, const char *timefmt)
541 fielding 83751 {
542 nd 101049 apr_table_t *e = r->subprocess_env;
543     char *t;
544 nd 101036
545 nd 101049 apr_table_setn(e, "DATE_LOCAL", LAZY_VALUE);
546     apr_table_setn(e, "DATE_GMT", LAZY_VALUE);
547     apr_table_setn(e, "LAST_MODIFIED", LAZY_VALUE);
548     apr_table_setn(e, "DOCUMENT_URI", r->uri);
549     if (r->path_info && *r->path_info) {
550     apr_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
551 nd 101036 }
552 nd 101049 apr_table_setn(e, "USER_NAME", LAZY_VALUE);
553 nd 101461 if (r->filename && (t = strrchr(r->filename, '/'))) {
554 nd 101049 apr_table_setn(e, "DOCUMENT_NAME", ++t);
555     }
556     else {
557     apr_table_setn(e, "DOCUMENT_NAME", r->uri);
558     }
559     if (r->args) {
560     char *arg_copy = apr_pstrdup(r->pool, r->args);
561 nd 101036
562 nd 101049 ap_unescape_url(arg_copy);
563     apr_table_setn(e, "QUERY_STRING_UNESCAPED",
564     ap_escape_shell_cmd(r->pool, arg_copy));
565     }
566     }
567 nd 101036
568 nd 101049 static const char *add_include_vars_lazy(request_rec *r, const char *var)
569     {
570     char *val;
571     if (!strcasecmp(var, "DATE_LOCAL")) {
572     include_dir_config *conf =
573     (include_dir_config *)ap_get_module_config(r->per_dir_config,
574     &include_module);
575     val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 0);
576     }
577     else if (!strcasecmp(var, "DATE_GMT")) {
578     include_dir_config *conf =
579     (include_dir_config *)ap_get_module_config(r->per_dir_config,
580     &include_module);
581     val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 1);
582     }
583     else if (!strcasecmp(var, "LAST_MODIFIED")) {
584     include_dir_config *conf =
585     (include_dir_config *)ap_get_module_config(r->per_dir_config,
586     &include_module);
587     val = ap_ht_time(r->pool, r->finfo.mtime, conf->default_time_fmt, 0);
588     }
589     else if (!strcasecmp(var, "USER_NAME")) {
590 trawick 101154 if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
591 nd 101049 val = "<unknown>";
592     }
593     }
594     else {
595     val = NULL;
596     }
597 nd 101036
598 nd 101049 if (val) {
599     apr_table_setn(r->subprocess_env, var, val);
600 nd 101036 }
601 nd 101049 return val;
602     }
603 nd 101036
604 nd 101070 static const char *get_include_var(const char *var, include_ctx_t *ctx)
605 nd 101049 {
606     const char *val;
607 nd 101073 request_rec *r = ctx->intern->r;
608    
609 nd 101049 if (apr_isdigit(*var) && !var[1]) {
610 nd 101100 apr_size_t idx = *var - '0';
611 nd 101073 backref_t *re = ctx->intern->re;
612    
613 nd 101049 /* Handle $0 .. $9 from the last regex evaluated.
614     * The choice of returning NULL strings on not-found,
615     * v.s. empty strings on an empty match is deliberate.
616     */
617 nd 101073 if (!re) {
618 fielding 101234 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
619     "regex capture $%" APR_SIZE_T_FMT " refers to no regex in %s",
620     idx, r->filename);
621 nd 101049 return NULL;
622     }
623     else {
624 striker 101556 if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
625 nd 101073 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
626 fielding 101234 "regex capture $%" APR_SIZE_T_FMT
627     " is out of range (last regex was: '%s') in %s",
628     idx, re->rexp, r->filename);
629 nd 101049 return NULL;
630     }
631 nd 101073
632     if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
633     return NULL;
634     }
635    
636     val = apr_pstrmemdup(ctx->dpool, re->source + re->match[idx].rm_so,
637     re->match[idx].rm_eo - re->match[idx].rm_so);
638 nd 101049 }
639     }
640     else {
641 nd 101073 val = apr_table_get(r->subprocess_env, var);
642 nd 101049
643 nd 101073 if (val == LAZY_VALUE) {
644     val = add_include_vars_lazy(r, var);
645     }
646 nd 101049 }
647 nd 101073
648 nd 101049 return val;
649 nd 101036 }
650    
651 fielding 83751 /*
652     * Do variable substitution on strings
653 nd 101070 *
654 brianp 92747 * (Note: If out==NULL, this function allocs a buffer for the resulting
655 nd 101070 * string from ctx->pool. The return value is always the parsed string)
656 fielding 83751 */
657 nd 101069 static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
658 brianp 92747 apr_size_t length, int leave_name)
659 fielding 83751 {
660 nd 101069 request_rec *r = ctx->intern->r;
661 nd 101070 result_item_t *result = NULL, *current = NULL;
662     apr_size_t outlen = 0, inlen, span;
663     char *ret = NULL, *eout = NULL;
664     const char *p;
665 fielding 83751
666 nd 101070 if (out) {
667     /* sanity check, out && !length is not supported */
668     ap_assert(out && length);
669    
670     ret = out;
671     eout = out + length - 1;
672     }
673    
674     span = strcspn(in, "\\$");
675     inlen = strlen(in);
676    
677     /* fast exit */
678     if (inlen == span) {
679     if (out) {
680     apr_cpystrn(out, in, length);
681 brianp 92747 }
682 nd 101070 else {
683     ret = apr_pstrmemdup(ctx->pool, in, (length && length <= inlen)
684     ? length - 1 : inlen);
685     }
686    
687     return ret;
688 brianp 92747 }
689 nd 101070
690     /* well, actually something to do */
691     p = in + span;
692    
693     if (out) {
694     if (span) {
695     memcpy(out, in, (out+span <= eout) ? span : (eout-out));
696     out += span;
697     }
698     }
699 brianp 92747 else {
700 nd 101070 current = result = apr_palloc(ctx->dpool, sizeof(*result));
701     current->next = NULL;
702     current->string = in;
703     current->len = span;
704     outlen = span;
705 brianp 92747 }
706    
707 nd 101070 /* loop for specials */
708     do {
709     if ((out && out >= eout) || (length && outlen >= length)) {
710     break;
711     }
712 fielding 83751
713 nd 101070 /* prepare next entry */
714     if (!out && current->len) {
715     current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
716     current = current->next;
717     current->next = NULL;
718     current->len = 0;
719     }
720    
721     /*
722     * escaped character
723     */
724     if (*p == '\\') {
725     if (out) {
726     *out++ = (p[1] == '$') ? *++p : *p;
727     ++p;
728     }
729     else {
730     current->len = 1;
731     current->string = (p[1] == '$') ? ++p : p;
732     ++p;
733     ++outlen;
734     }
735     }
736    
737     /*
738     * variable expansion
739     */
740     else { /* *p == '$' */
741     const char *newp = NULL, *ep, *key = NULL;
742    
743     if (*++p == '{') {
744     ep = ap_strchr_c(++p, '}');
745     if (!ep) {
746     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing '}' on "
747     "variable \"%s\" in %s", p, r->filename);
748     break;
749 brianp 92747 }
750 nd 101070
751     if (p < ep) {
752     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
753     newp = ep + 1;
754 brianp 92747 }
755 nd 101070 p -= 2;
756 jerenkrantz 90874 }
757 fielding 83751 else {
758 nd 101070 ep = p;
759     while (*ep == '_' || apr_isalnum(*ep)) {
760     ++ep;
761     }
762    
763     if (p < ep) {
764     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
765     newp = ep;
766     }
767     --p;
768 fielding 83751 }
769    
770 nd 101070 /* empty name results in a copy of '$' in the output string */
771     if (!key) {
772     if (out) {
773     *out++ = *p++;
774 jerenkrantz 90874 }
775     else {
776 nd 101070 current->len = 1;
777     current->string = p++;
778     ++outlen;
779 jerenkrantz 90874 }
780 nd 101070 }
781     else {
782     const char *val = get_include_var(key, ctx);
783     apr_size_t len = 0;
784 fielding 83751
785 nd 101070 if (val) {
786     len = strlen(val);
787 jerenkrantz 90874 }
788 nd 101070 else if (leave_name) {
789     val = p;
790     len = ep - p;
791 jerenkrantz 90874 }
792 nd 101070
793     if (val && len) {
794     if (out) {
795     memcpy(out, val, (out+len <= eout) ? len : (eout-out));
796     out += len;
797 brianp 92747 }
798 nd 101070 else {
799     current->len = len;
800     current->string = val;
801     outlen += len;
802 brianp 92747 }
803     }
804 nd 101070
805     p = newp;
806 jerenkrantz 90874 }
807 nd 101070 }
808    
809     if ((out && out >= eout) || (length && outlen >= length)) {
810 fielding 83751 break;
811     }
812 nd 101070
813     /* check the remainder */
814     if (*p && (span = strcspn(p, "\\$")) > 0) {
815     if (!out && current->len) {
816     current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
817     current = current->next;
818     current->next = NULL;
819     }
820    
821     if (out) {
822     memcpy(out, p, (out+span <= eout) ? span : (eout-out));
823     out += span;
824     }
825     else {
826     current->len = span;
827     current->string = p;
828     outlen += span;
829     }
830    
831     p += span;
832     }
833     } while (p < in+inlen);
834    
835     /* assemble result */
836     if (out) {
837     if (out > eout) {
838     *eout = '\0';
839     }
840     else {
841     *out = '\0';
842     }
843 fielding 83751 }
844 nd 101070 else {
845     const char *ep;
846    
847     if (length && outlen > length) {
848     outlen = length - 1;
849     }
850    
851     ret = out = apr_palloc(ctx->pool, outlen + 1);
852     ep = ret + outlen;
853    
854     do {
855     if (result->len) {
856     memcpy(out, result->string, (out+result->len <= ep)
857     ? result->len : (ep-out));
858     out += result->len;
859     }
860     result = result->next;
861     } while (result && out < ep);
862    
863     ret[outlen] = '\0';
864     }
865    
866     return ret;
867 fielding 83751 }
868    
869 niq 642978 static const char *ssi_parse_string(request_rec *r, const char *in)
870 fielding 83751 {
871 niq 642559 include_ctx_t *ctx = ap_get_module_config(r->request_config,
872     &include_module);
873     return ap_ssi_parse_string(ctx, in, NULL, 0, SSI_EXPAND_DROP_NAME);
874 fielding 83751 }
875 niq 642978 static int ssi_access(request_rec *r, ap_parse_node_t *current,
876 niq 642559 string_func_t parse_string)
877 fielding 83751 {
878 niq 642559 request_rec *rr;
879     include_ctx_t *ctx = ap_get_module_config(r->request_config,
880     &include_module);
881 fielding 83751
882 niq 642559 /* if this arg isn't -A, just return */
883     if (current->token.type != TOKEN_ACCESS || current->token.value[0] != 'A') {
884     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
885     "Unsupported option -%s in file %s",
886     current->token.value, r->filename);
887     return 1;
888 nd 101078 }
889 niq 642559 if (current->left || !current->right ||
890     (current->right->token.type != TOKEN_STRING &&
891     current->right->token.type != TOKEN_RE)) {
892     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
893     "Invalid expression in file %s: Token '-A' must be followed by a URI string.",
894     r->filename);
895     return 1; /* was_error */
896 fielding 83751 }
897 niq 642559 current->right->token.value =
898     ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
899     SSI_EXPAND_DROP_NAME);
900     rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
901     /* 400 and higher are considered access denied */
902     if (rr->status < HTTP_BAD_REQUEST) {
903     current->value = 1;
904 fielding 83751 }
905 nd 101078 else {
906 niq 642559 current->value = 0;
907     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r,
908     "mod_include: The tested "
909     "subrequest -A \"%s\" returned an error code.",
910     current->right->token.value);
911 nd 101078 }
912 niq 642559 ap_destroy_sub_req(rr);
913     return 0;
914 nd 101078 }
915    
916 nd 101049 /*
917     * +-------------------------------------------------------+
918     * | |
919     * | Action Handlers
920     * | |
921     * +-------------------------------------------------------+
922     */
923    
924     /*
925     * Extract the next tag name and value.
926     * If there are no more tags, set the tag name to NULL.
927     * The tag value is html decoded if dodecode is non-zero.
928     * The tag value may be NULL if there is no tag value..
929     */
930     static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
931     char **tag_val, int dodecode)
932     {
933     if (!ctx->intern->argv) {
934     *tag = NULL;
935     *tag_val = NULL;
936    
937     return;
938     }
939    
940     *tag_val = ctx->intern->argv->value;
941     *tag = ctx->intern->argv->name;
942    
943     ctx->intern->argv = ctx->intern->argv->next;
944    
945     if (dodecode && *tag_val) {
946     decodehtml(*tag_val);
947     }
948    
949     return;
950     }
951    
952     static int find_file(request_rec *r, const char *directive, const char *tag,
953     char *tag_val, apr_finfo_t *finfo)
954     {
955     char *to_send = tag_val;
956     request_rec *rr = NULL;
957     int ret=0;
958     char *error_fmt = NULL;
959     apr_status_t rv = APR_SUCCESS;
960    
961     if (!strcmp(tag, "file")) {
962 nd 101080 char *newpath;
963    
964     /* be safe; only files in this directory or below allowed */
965     rv = apr_filepath_merge(&newpath, NULL, tag_val,
966     APR_FILEPATH_SECUREROOTTEST |
967     APR_FILEPATH_NOTABSOLUTE, r->pool);
968    
969 jerenkrantz 104439 if (rv != APR_SUCCESS) {
970 nd 101049 error_fmt = "unable to access file \"%s\" "
971     "in parsed file %s";
972     }
973     else {
974     /* note: it is okay to pass NULL for the "next filter" since
975     we never attempt to "run" this sub request. */
976 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, NULL);
977 nd 101049
978     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
979     to_send = rr->filename;
980 jim 332306 if ((rv = apr_stat(finfo, to_send,
981 nd 101049 APR_FINFO_GPROT | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS
982     && rv != APR_INCOMPLETE) {
983     error_fmt = "unable to get information about \"%s\" "
984     "in parsed file %s";
985     }
986     }
987     else {
988     error_fmt = "unable to lookup information about \"%s\" "
989     "in parsed file %s";
990     }
991     }
992    
993     if (error_fmt) {
994     ret = -1;
995     ap_log_rerror(APLOG_MARK, APLOG_ERR,
996     rv, r, error_fmt, to_send, r->filename);
997     }
998    
999     if (rr) ap_destroy_sub_req(rr);
1000 jim 332306
1001 nd 101049 return ret;
1002     }
1003     else if (!strcmp(tag, "virtual")) {
1004     /* note: it is okay to pass NULL for the "next filter" since
1005     we never attempt to "run" this sub request. */
1006     rr = ap_sub_req_lookup_uri(tag_val, r, NULL);
1007    
1008     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
1009     memcpy((char *) finfo, (const char *) &rr->finfo,
1010     sizeof(rr->finfo));
1011     ap_destroy_sub_req(rr);
1012     return 0;
1013     }
1014     else {
1015 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to get "
1016     "information about \"%s\" in parsed file %s",
1017     tag_val, r->filename);
1018 nd 101049 ap_destroy_sub_req(rr);
1019     return -1;
1020     }
1021     }
1022     else {
1023 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1024     "to tag %s in %s", tag, directive, r->filename);
1025 nd 101049 return -1;
1026     }
1027     }
1028    
1029 nd 101053 /*
1030     * <!--#include virtual|file="..." [virtual|file="..."] ... -->
1031     */
1032 nd 101049 static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
1033     apr_bucket_brigade *bb)
1034     {
1035     request_rec *r = f->r;
1036    
1037 nd 101053 if (!ctx->argc) {
1038     ap_log_rerror(APLOG_MARK,
1039     (ctx->flags & SSI_FLAG_PRINTING)
1040     ? APLOG_ERR : APLOG_WARNING,
1041     0, r, "missing argument for include element in %s",
1042     r->filename);
1043     }
1044 nd 101049
1045 nd 101053 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1046     return APR_SUCCESS;
1047     }
1048 nd 101049
1049 nd 101053 if (!ctx->argc) {
1050     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1051     return APR_SUCCESS;
1052     }
1053 nd 101049
1054 nd 101053 while (1) {
1055     char *tag = NULL;
1056     char *tag_val = NULL;
1057     request_rec *rr = NULL;
1058     char *error_fmt = NULL;
1059     char *parsed_string;
1060 nd 101049
1061 nd 101053 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1062     if (!tag || !tag_val) {
1063     break;
1064     }
1065 nd 101049
1066 nd 101053 if (strcmp(tag, "virtual") && strcmp(tag, "file")) {
1067     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1068     "\"%s\" to tag include in %s", tag, r->filename);
1069     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1070     break;
1071     }
1072 nd 101049
1073 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1074 nd 101053 SSI_EXPAND_DROP_NAME);
1075     if (tag[0] == 'f') {
1076 nd 101080 char *newpath;
1077     apr_status_t rv;
1078    
1079     /* be safe; only files in this directory or below allowed */
1080 nd 102147 rv = apr_filepath_merge(&newpath, NULL, parsed_string,
1081 nd 101080 APR_FILEPATH_SECUREROOTTEST |
1082     APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
1083    
1084 jerenkrantz 104439 if (rv != APR_SUCCESS) {
1085 nd 101053 error_fmt = "unable to include file \"%s\" in parsed file %s";
1086     }
1087     else {
1088 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, f->next);
1089 nd 101053 }
1090     }
1091     else {
1092 minfrin 592951 if (r->kept_body) {
1093     rr = ap_sub_req_method_uri(r->method, parsed_string, r, f->next);
1094     }
1095     else {
1096     rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
1097     }
1098 nd 101053 }
1099 nd 101049
1100 nd 101053 if (!error_fmt && rr->status != HTTP_OK) {
1101     error_fmt = "unable to include \"%s\" in parsed file %s";
1102     }
1103 nd 101049
1104 nd 101053 if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
1105     rr->content_type && strncmp(rr->content_type, "text/", 5)) {
1106    
1107     error_fmt = "unable to include potential exec \"%s\" in parsed "
1108     "file %s";
1109     }
1110    
1111     /* See the Kludge in includes_filter for why.
1112     * Basically, it puts a bread crumb in here, then looks
1113     * for the crumb later to see if its been here.
1114     */
1115 niq 642559 ctx->intern->kludge_child = rr;
1116 nd 101053
1117     if (!error_fmt && ap_run_sub_req(rr)) {
1118     error_fmt = "unable to include \"%s\" in parsed file %s";
1119     }
1120    
1121     if (error_fmt) {
1122     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val,
1123     r->filename);
1124     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1125     }
1126    
1127 jorton 179763 /* Do *not* destroy the subrequest here; it may have allocated
1128     * variables in this r->subprocess_env in the subrequest's
1129     * r->pool, so that pool must survive as long as this request.
1130     * Yes, this is a memory leak. */
1131 nd 101053
1132     if (error_fmt) {
1133     break;
1134     }
1135 nd 101049 }
1136    
1137     return APR_SUCCESS;
1138     }
1139    
1140 nd 101054 /*
1141     * <!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... -->
1142     */
1143 nd 101049 static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
1144     apr_bucket_brigade *bb)
1145     {
1146     enum {E_NONE, E_URL, E_ENTITY} encode;
1147     request_rec *r = f->r;
1148    
1149 nd 101054 if (!ctx->argc) {
1150     ap_log_rerror(APLOG_MARK,
1151     (ctx->flags & SSI_FLAG_PRINTING)
1152     ? APLOG_ERR : APLOG_WARNING,
1153     0, r, "missing argument for echo element in %s",
1154     r->filename);
1155     }
1156    
1157     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1158     return APR_SUCCESS;
1159     }
1160    
1161     if (!ctx->argc) {
1162     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1163     return APR_SUCCESS;
1164     }
1165    
1166 nd 101049 encode = E_ENTITY;
1167    
1168 nd 101054 while (1) {
1169     char *tag = NULL;
1170     char *tag_val = NULL;
1171 nd 101049
1172 nd 101054 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1173     if (!tag || !tag_val) {
1174     break;
1175     }
1176 nd 101049
1177 nd 101054 if (!strcmp(tag, "var")) {
1178     const char *val;
1179     const char *echo_text = NULL;
1180     apr_size_t e_len;
1181    
1182 nd 101070 val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
1183 nd 101079 0, SSI_EXPAND_DROP_NAME),
1184 nd 101070 ctx);
1185    
1186 nd 101054 if (val) {
1187     switch(encode) {
1188     case E_NONE:
1189     echo_text = val;
1190     break;
1191     case E_URL:
1192     echo_text = ap_escape_uri(ctx->dpool, val);
1193     break;
1194     case E_ENTITY:
1195 niq 730296 /* PR#25202: escape anything non-ascii here */
1196     echo_text = ap_escape_html2(ctx->dpool, val, 1);
1197 nd 101054 break;
1198 nd 101049 }
1199 nd 101054
1200     e_len = strlen(echo_text);
1201     }
1202     else {
1203 nd 101129 echo_text = ctx->intern->undefined_echo;
1204     e_len = ctx->intern->undefined_echo_len;
1205 nd 101049 }
1206 nd 101054
1207     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
1208 nd 101129 apr_pmemdup(ctx->pool, echo_text, e_len),
1209 nd 101054 e_len, ctx->pool, f->c->bucket_alloc));
1210     }
1211     else if (!strcmp(tag, "encoding")) {
1212     if (!strcasecmp(tag_val, "none")) {
1213     encode = E_NONE;
1214 nd 101049 }
1215 nd 101054 else if (!strcasecmp(tag_val, "url")) {
1216     encode = E_URL;
1217     }
1218     else if (!strcasecmp(tag_val, "entity")) {
1219     encode = E_ENTITY;
1220     }
1221 nd 101049 else {
1222 nd 101054 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1223     "\"%s\" to parameter \"encoding\" of tag echo in "
1224     "%s", tag_val, r->filename);
1225 nd 101049 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1226 nd 101054 break;
1227 nd 101049 }
1228     }
1229 nd 101054 else {
1230     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1231     "\"%s\" in tag echo of %s", tag, r->filename);
1232     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1233     break;
1234     }
1235 nd 101049 }
1236    
1237     return APR_SUCCESS;
1238     }
1239    
1240 nd 101055 /*
1241 nd 101130 * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
1242     * [echomsg="..."] -->
1243 nd 101049 */
1244     static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
1245     apr_bucket_brigade *bb)
1246     {
1247     request_rec *r = f->r;
1248     apr_table_t *env = r->subprocess_env;
1249    
1250 nd 101055 if (!ctx->argc) {
1251     ap_log_rerror(APLOG_MARK,
1252     (ctx->flags & SSI_FLAG_PRINTING)
1253     ? APLOG_ERR : APLOG_WARNING,
1254     0, r, "missing argument for config element in %s",
1255     r->filename);
1256     }
1257    
1258     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1259     return APR_SUCCESS;
1260     }
1261    
1262     if (!ctx->argc) {
1263     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1264     return APR_SUCCESS;
1265     }
1266    
1267     while (1) {
1268     char *tag = NULL;
1269     char *tag_val = NULL;
1270    
1271     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
1272     if (!tag || !tag_val) {
1273     break;
1274     }
1275    
1276     if (!strcmp(tag, "errmsg")) {
1277 nd 101071 ctx->error_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1278     SSI_EXPAND_DROP_NAME);
1279 nd 101055 }
1280 nd 101130 else if (!strcmp(tag, "echomsg")) {
1281     ctx->intern->undefined_echo =
1282     ap_ssi_parse_string(ctx, tag_val, NULL, 0,SSI_EXPAND_DROP_NAME);
1283     ctx->intern->undefined_echo_len=strlen(ctx->intern->undefined_echo);
1284     }
1285 nd 101055 else if (!strcmp(tag, "timefmt")) {
1286     apr_time_t date = r->request_time;
1287 nd 101049
1288 nd 101071 ctx->time_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1289     SSI_EXPAND_DROP_NAME);
1290    
1291 jim 332306 apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date,
1292 nd 101055 ctx->time_str, 0));
1293 jim 332306 apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date,
1294 nd 101055 ctx->time_str, 1));
1295     apr_table_setn(env, "LAST_MODIFIED",
1296 jim 332306 ap_ht_time(r->pool, r->finfo.mtime,
1297 nd 101055 ctx->time_str, 0));
1298     }
1299     else if (!strcmp(tag, "sizefmt")) {
1300 nd 101130 char *parsed_string;
1301    
1302 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1303 nd 101055 SSI_EXPAND_DROP_NAME);
1304     if (!strcmp(parsed_string, "bytes")) {
1305     ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
1306 nd 101049 }
1307 nd 101055 else if (!strcmp(parsed_string, "abbrev")) {
1308     ctx->flags &= SSI_FLAG_SIZE_ABBREV;
1309 nd 101049 }
1310     else {
1311 nd 101055 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1312     "\"%s\" to parameter \"sizefmt\" of tag config "
1313     "in %s", parsed_string, r->filename);
1314 nd 101049 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1315 nd 101055 break;
1316 nd 101049 }
1317     }
1318 nd 101055 else {
1319     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1320     "\"%s\" to tag config in %s", tag, r->filename);
1321     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1322     break;
1323     }
1324 nd 101049 }
1325    
1326     return APR_SUCCESS;
1327     }
1328    
1329 nd 101057 /*
1330     * <!--#fsize virtual|file="..." [virtual|file="..."] ... -->
1331     */
1332 nd 101049 static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
1333     apr_bucket_brigade *bb)
1334     {
1335     request_rec *r = f->r;
1336    
1337 nd 101057 if (!ctx->argc) {
1338     ap_log_rerror(APLOG_MARK,
1339     (ctx->flags & SSI_FLAG_PRINTING)
1340     ? APLOG_ERR : APLOG_WARNING,
1341     0, r, "missing argument for fsize element in %s",
1342     r->filename);
1343     }
1344    
1345     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1346     return APR_SUCCESS;
1347     }
1348    
1349     if (!ctx->argc) {
1350     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1351     return APR_SUCCESS;
1352     }
1353    
1354     while (1) {
1355     char *tag = NULL;
1356     char *tag_val = NULL;
1357     apr_finfo_t finfo;
1358     char *parsed_string;
1359    
1360     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1361     if (!tag || !tag_val) {
1362     break;
1363     }
1364    
1365 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1366 nd 101057 SSI_EXPAND_DROP_NAME);
1367    
1368     if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
1369     char *buf;
1370     apr_size_t len;
1371    
1372     if (!(ctx->flags & SSI_FLAG_SIZE_IN_BYTES)) {
1373     buf = apr_strfsize(finfo.size, apr_palloc(ctx->pool, 5));
1374     len = 4; /* omit the \0 terminator */
1375 nd 101049 }
1376     else {
1377 nd 101057 apr_size_t l, x, pos;
1378     char *tmp;
1379 nd 101049
1380 nd 101057 tmp = apr_psprintf(ctx->dpool, "%" APR_OFF_T_FMT, finfo.size);
1381     len = l = strlen(tmp);
1382 nd 101049
1383 nd 101057 for (x = 0; x < l; ++x) {
1384     if (x && !((l - x) % 3)) {
1385     ++len;
1386 nd 101049 }
1387 nd 101057 }
1388 nd 101049
1389 nd 101057 if (len == l) {
1390     buf = apr_pstrmemdup(ctx->pool, tmp, len);
1391 nd 101049 }
1392     else {
1393 nd 101057 buf = apr_palloc(ctx->pool, len);
1394    
1395     for (pos = x = 0; x < l; ++x) {
1396     if (x && !((l - x) % 3)) {
1397     buf[pos++] = ',';
1398     }
1399     buf[pos++] = tmp[x];
1400     }
1401 nd 101049 }
1402     }
1403 nd 101057
1404     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buf, len,
1405     ctx->pool, f->c->bucket_alloc));
1406 nd 101049 }
1407 nd 101057 else {
1408     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1409     break;
1410     }
1411 nd 101049 }
1412    
1413     return APR_SUCCESS;
1414     }
1415    
1416 nd 101063 /*
1417     * <!--#flastmod virtual|file="..." [virtual|file="..."] ... -->
1418     */
1419 nd 101049 static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
1420     apr_bucket_brigade *bb)
1421     {
1422     request_rec *r = f->r;
1423    
1424 nd 101063 if (!ctx->argc) {
1425     ap_log_rerror(APLOG_MARK,
1426     (ctx->flags & SSI_FLAG_PRINTING)
1427     ? APLOG_ERR : APLOG_WARNING,
1428     0, r, "missing argument for flastmod element in %s",
1429     r->filename);
1430     }
1431 nd 101049
1432 nd 101063 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1433     return APR_SUCCESS;
1434     }
1435 nd 101049
1436 nd 101063 if (!ctx->argc) {
1437     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1438     return APR_SUCCESS;
1439     }
1440    
1441     while (1) {
1442     char *tag = NULL;
1443     char *tag_val = NULL;
1444     apr_finfo_t finfo;
1445     char *parsed_string;
1446    
1447     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1448     if (!tag || !tag_val) {
1449     break;
1450 nd 101049 }
1451 nd 101063
1452 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1453 nd 101063 SSI_EXPAND_DROP_NAME);
1454    
1455     if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
1456     char *t_val;
1457     apr_size_t t_len;
1458    
1459     t_val = ap_ht_time(ctx->pool, finfo.mtime, ctx->time_str, 0);
1460     t_len = strlen(t_val);
1461    
1462     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(t_val, t_len,
1463     ctx->pool, f->c->bucket_alloc));
1464     }
1465     else {
1466     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1467     break;
1468     }
1469 nd 101049 }
1470    
1471     return APR_SUCCESS;
1472     }
1473    
1474 nd 101064 /*
1475     * <!--#if expr="..." -->
1476     */
1477 nd 101046 static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
1478     apr_bucket_brigade *bb)
1479 fielding 83751 {
1480 nd 101064 char *tag = NULL;
1481     char *expr = NULL;
1482 nd 101046 request_rec *r = f->r;
1483 nd 101078 int expr_ret, was_error;
1484 fielding 83751
1485 nd 101064 if (ctx->argc != 1) {
1486     ap_log_rerror(APLOG_MARK,
1487     (ctx->flags & SSI_FLAG_PRINTING)
1488     ? APLOG_ERR : APLOG_WARNING,
1489     0, r, (ctx->argc)
1490     ? "too many arguments for if element in %s"
1491     : "missing expr argument for if element in %s",
1492     r->filename);
1493     }
1494    
1495 nd 101048 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1496 nd 101064 ++(ctx->if_nesting_level);
1497     return APR_SUCCESS;
1498 rbb 87067 }
1499 nd 101046
1500 nd 101064 if (ctx->argc != 1) {
1501     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1502     return APR_SUCCESS;
1503     }
1504    
1505     ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
1506    
1507     if (strcmp(tag, "expr")) {
1508     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1509     "to tag if in %s", tag, r->filename);
1510     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1511     return APR_SUCCESS;
1512     }
1513    
1514     if (!expr) {
1515     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr value for if "
1516     "element in %s", r->filename);
1517     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1518     return APR_SUCCESS;
1519     }
1520    
1521 nd 101078 DEBUG_PRINTF((ctx, "**** if expr=\"%s\"\n", expr));
1522 rbb 87067
1523 niq 642559 expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
1524     ssi_parse_string, ctx->intern->access_func);
1525 nd 101064
1526     if (was_error) {
1527     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1528     return APR_SUCCESS;
1529 fielding 83751 }
1530 nd 101048
1531 nd 101064 if (expr_ret) {
1532     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1533     }
1534     else {
1535     ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
1536     }
1537    
1538 nd 101078 DEBUG_DUMP_COND(ctx, " if");
1539    
1540 nd 101064 ctx->if_nesting_level = 0;
1541    
1542 nd 101046 return APR_SUCCESS;
1543 fielding 83751 }
1544    
1545 nd 101065 /*
1546     * <!--#elif expr="..." -->
1547     */
1548 nd 101046 static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
1549     apr_bucket_brigade *bb)
1550 fielding 83751 {
1551 nd 101065 char *tag = NULL;
1552     char *expr = NULL;
1553     request_rec *r = f->r;
1554 nd 101078 int expr_ret, was_error;
1555 fielding 83751
1556 nd 101065 if (ctx->argc != 1) {
1557     ap_log_rerror(APLOG_MARK,
1558     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1559     0, r, (ctx->argc)
1560     ? "too many arguments for if element in %s"
1561     : "missing expr argument for if element in %s",
1562     r->filename);
1563     }
1564    
1565     if (ctx->if_nesting_level) {
1566     return APR_SUCCESS;
1567     }
1568    
1569     if (ctx->argc != 1) {
1570     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1571     return APR_SUCCESS;
1572     }
1573    
1574     ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
1575    
1576     if (strcmp(tag, "expr")) {
1577     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1578     "to tag if in %s", tag, r->filename);
1579     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1580     return APR_SUCCESS;
1581     }
1582    
1583     if (!expr) {
1584     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr in elif "
1585     "statement: %s", r->filename);
1586     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1587     return APR_SUCCESS;
1588     }
1589    
1590 nd 101078 DEBUG_PRINTF((ctx, "**** elif expr=\"%s\"\n", expr));
1591     DEBUG_DUMP_COND(ctx, " elif");
1592 nd 101065
1593     if (ctx->flags & SSI_FLAG_COND_TRUE) {
1594     ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
1595     return APR_SUCCESS;
1596 fielding 83751 }
1597 nd 101048
1598 niq 642559 expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
1599     ssi_parse_string, ctx->intern->access_func);
1600 nd 101065
1601     if (was_error) {
1602     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1603     return APR_SUCCESS;
1604     }
1605    
1606     if (expr_ret) {
1607     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1608     }
1609     else {
1610     ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
1611     }
1612    
1613 nd 101078 DEBUG_DUMP_COND(ctx, " elif");
1614 nd 101065
1615 nd 101046 return APR_SUCCESS;
1616 fielding 83751 }
1617    
1618 nd 101066 /*
1619     * <!--#else -->
1620     */
1621 nd 101046 static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f,
1622     apr_bucket_brigade *bb)
1623 fielding 83751 {
1624 nd 101046 request_rec *r = f->r;
1625 fielding 83751
1626 nd 101066 if (ctx->argc) {
1627     ap_log_rerror(APLOG_MARK,
1628     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1629     0, r, "else directive does not take tags in %s",
1630     r->filename);
1631     }
1632    
1633     if (ctx->if_nesting_level) {
1634     return APR_SUCCESS;
1635     }
1636    
1637     if (ctx->argc) {
1638     if (ctx->flags & SSI_FLAG_PRINTING) {
1639     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1640 fielding 83751 }
1641 nd 101066
1642     return APR_SUCCESS;
1643     }
1644    
1645 nd 101078 DEBUG_DUMP_COND(ctx, " else");
1646 jim 332306
1647 nd 101066 if (ctx->flags & SSI_FLAG_COND_TRUE) {
1648     ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
1649 fielding 83751 }
1650 nd 101066 else {
1651     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1652     }
1653 nd 101048
1654 nd 101046 return APR_SUCCESS;
1655 fielding 83751 }
1656    
1657 nd 101066 /*
1658     * <!--#endif -->
1659     */
1660 nd 101046 static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f,
1661     apr_bucket_brigade *bb)
1662 fielding 83751 {
1663 nd 101046 request_rec *r = f->r;
1664 fielding 83751
1665 nd 101066 if (ctx->argc) {
1666     ap_log_rerror(APLOG_MARK,
1667     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1668     0, r, "endif directive does not take tags in %s",
1669     r->filename);
1670 fielding 83751 }
1671 nd 101066
1672     if (ctx->if_nesting_level) {
1673     --(ctx->if_nesting_level);
1674 nd 101046 return APR_SUCCESS;
1675 fielding 83751 }
1676 nd 101066
1677     if (ctx->argc) {
1678     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1679     return APR_SUCCESS;
1680     }
1681    
1682 nd 101078 DEBUG_DUMP_COND(ctx, "endif");
1683    
1684 nd 101066 ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1685    
1686     return APR_SUCCESS;
1687 fielding 83751 }
1688    
1689 nd 101067 /*
1690     * <!--#set var="..." value="..." ... -->
1691     */
1692 nd 101046 static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
1693     apr_bucket_brigade *bb)
1694 fielding 83751 {
1695 nd 101067 char *var = NULL;
1696 nd 101046 request_rec *r = f->r;
1697 ianh 93556 request_rec *sub = r->main;
1698     apr_pool_t *p = r->pool;
1699 fielding 83751
1700 nd 101067 if (ctx->argc < 2) {
1701     ap_log_rerror(APLOG_MARK,
1702     (ctx->flags & SSI_FLAG_PRINTING)
1703     ? APLOG_ERR : APLOG_WARNING,
1704     0, r, "missing argument for set element in %s",
1705     r->filename);
1706     }
1707    
1708     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1709     return APR_SUCCESS;
1710     }
1711    
1712     if (ctx->argc < 2) {
1713     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1714     return APR_SUCCESS;
1715     }
1716    
1717 jim 332306 /* we need to use the 'main' request pool to set notes as that is
1718 ianh 93556 * a notes lifetime
1719     */
1720     while (sub) {
1721     p = sub->pool;
1722     sub = sub->main;
1723     }
1724    
1725 nd 101067 while (1) {
1726     char *tag = NULL;
1727     char *tag_val = NULL;
1728    
1729     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1730    
1731     if (!tag || !tag_val) {
1732     break;
1733     }
1734    
1735     if (!strcmp(tag, "var")) {
1736 nd 101079 var = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1737 nd 101067 SSI_EXPAND_DROP_NAME);
1738     }
1739     else if (!strcmp(tag, "value")) {
1740     char *parsed_string;
1741    
1742     if (!var) {
1743     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "variable must "
1744     "precede value in set directive in %s",
1745     r->filename);
1746 nd 101046 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1747 nd 101067 break;
1748 fielding 83751 }
1749 nd 101067
1750 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1751 nd 101067 SSI_EXPAND_DROP_NAME);
1752     apr_table_setn(r->subprocess_env, apr_pstrdup(p, var),
1753     apr_pstrdup(p, parsed_string));
1754 fielding 83751 }
1755 nd 101067 else {
1756     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid tag for set "
1757     "directive in %s", r->filename);
1758     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1759     break;
1760     }
1761 fielding 83751 }
1762 nd 101048
1763 nd 101046 return APR_SUCCESS;
1764 fielding 83751 }
1765    
1766 nd 101068 /*
1767     * <!--#printenv -->
1768     */
1769 nd 101046 static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f,
1770     apr_bucket_brigade *bb)
1771 fielding 83751 {
1772 nd 101046 request_rec *r = f->r;
1773 nd 101068 const apr_array_header_t *arr;
1774     const apr_table_entry_t *elts;
1775     int i;
1776 fielding 83751
1777 nd 101068 if (ctx->argc) {
1778     ap_log_rerror(APLOG_MARK,
1779     (ctx->flags & SSI_FLAG_PRINTING)
1780     ? APLOG_ERR : APLOG_WARNING,
1781     0, r, "printenv directive does not take tags in %s",
1782     r->filename);
1783     }
1784 rbb 87067
1785 nd 101068 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1786     return APR_SUCCESS;
1787     }
1788    
1789     if (ctx->argc) {
1790     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1791     return APR_SUCCESS;
1792     }
1793    
1794     arr = apr_table_elts(r->subprocess_env);
1795     elts = (apr_table_entry_t *)arr->elts;
1796    
1797     for (i = 0; i < arr->nelts; ++i) {
1798     const char *key_text, *val_text;
1799     char *key_val, *next;
1800     apr_size_t k_len, v_len, kv_length;
1801    
1802     /* get key */
1803     key_text = ap_escape_html(ctx->dpool, elts[i].key);
1804     k_len = strlen(key_text);
1805    
1806     /* get value */
1807     val_text = elts[i].val;
1808     if (val_text == LAZY_VALUE) {
1809     val_text = add_include_vars_lazy(r, elts[i].key);
1810 fielding 83751 }
1811 nd 101068 val_text = ap_escape_html(ctx->dpool, elts[i].val);
1812     v_len = strlen(val_text);
1813    
1814     /* assemble result */
1815     kv_length = k_len + v_len + sizeof("=\n");
1816     key_val = apr_palloc(ctx->pool, kv_length);
1817     next = key_val;
1818    
1819     memcpy(next, key_text, k_len);
1820     next += k_len;
1821     *next++ = '=';
1822     memcpy(next, val_text, v_len);
1823     next += v_len;
1824     *next++ = '\n';
1825     *next = 0;
1826    
1827     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(key_val, kv_length-1,
1828     ctx->pool, f->c->bucket_alloc));
1829 fielding 83751 }
1830 nd 101048
1831 nd 101068 ctx->flush_now = 1;
1832 nd 101046 return APR_SUCCESS;
1833 fielding 83751 }
1834    
1835    
1836 nd 101036 /*
1837 nd 101049 * +-------------------------------------------------------+
1838     * | |
1839     * | Main Includes-Filter Engine
1840     * | |
1841     * +-------------------------------------------------------+
1842     */
1843    
1844     /* This is an implementation of the BNDM search algorithm.
1845     *
1846 jim 332306 * Fast and Flexible String Matching by Combining Bit-parallelism and
1847     * Suffix Automata (2001)
1848 nd 101049 * Gonzalo Navarro, Mathieu Raffinot
1849     *
1850     * http://www-igm.univ-mlv.fr/~raffinot/ftp/jea2001.ps.gz
1851     *
1852     * Initial code submitted by Sascha Schumann.
1853     */
1854 jim 332306
1855 nd 101049 /* Precompile the bndm_t data structure. */
1856 nd 101052 static bndm_t *bndm_compile(apr_pool_t *pool, const char *n, apr_size_t nl)
1857 nd 101049 {
1858     unsigned int x;
1859     const char *ne = n + nl;
1860 nd 101052 bndm_t *t = apr_palloc(pool, sizeof(*t));
1861 nd 101049
1862     memset(t->T, 0, sizeof(unsigned int) * 256);
1863 nd 101052 t->pattern_len = nl;
1864    
1865     for (x = 1; n < ne; x <<= 1) {
1866 nd 101049 t->T[(unsigned char) *n++] |= x;
1867 nd 101052 }
1868 nd 101049
1869     t->x = x - 1;
1870 nd 101052
1871     return t;
1872 nd 101049 }
1873    
1874     /* Implements the BNDM search algorithm (as described above).
1875     *
1876     * h - the string to look in
1877     * hl - length of the string to look for
1878 jim 332306 * t - precompiled bndm structure against the pattern
1879 nd 101049 *
1880     * Returns the count of character that is the first match or hl if no
1881     * match is found.
1882     */
1883 nd 101052 static apr_size_t bndm(bndm_t *t, const char *h, apr_size_t hl)
1884 nd 101049 {
1885     const char *skip;
1886     const char *he, *p, *pi;
1887     unsigned int *T, x, d;
1888 nd 101052 apr_size_t nl;
1889 nd 101049
1890     he = h + hl;
1891    
1892     T = t->T;
1893     x = t->x;
1894 nd 101052 nl = t->pattern_len;