/[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 795445 - (hide annotations)
Sat Jul 18 23:12:58 2009 UTC (4 months, 1 week ago) by niq
File MIME type: text/plain
File size: 104327 byte(s)
Fix mod_include potential segfault checking backref from unmatched regexp
http://markmail.org/message/jlc7t5edsjujbe37
Patch by rpluem, lars, niq
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 jorton 757376 static void add_include_vars(request_rec *r)
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 jorton 757376 static const char *add_include_vars_lazy(request_rec *r, const char *var, const char *timefmt)
569 nd 101049 {
570     char *val;
571     if (!strcasecmp(var, "DATE_LOCAL")) {
572 jorton 757376 val = ap_ht_time(r->pool, r->request_time, timefmt, 0);
573 nd 101049 }
574     else if (!strcasecmp(var, "DATE_GMT")) {
575 jorton 757376 val = ap_ht_time(r->pool, r->request_time, timefmt, 1);
576 nd 101049 }
577     else if (!strcasecmp(var, "LAST_MODIFIED")) {
578 jorton 757376 val = ap_ht_time(r->pool, r->finfo.mtime, timefmt, 0);
579 nd 101049 }
580     else if (!strcasecmp(var, "USER_NAME")) {
581 trawick 101154 if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
582 nd 101049 val = "<unknown>";
583     }
584     }
585     else {
586     val = NULL;
587     }
588 nd 101036
589 nd 101049 if (val) {
590     apr_table_setn(r->subprocess_env, var, val);
591 nd 101036 }
592 nd 101049 return val;
593     }
594 nd 101036
595 nd 101070 static const char *get_include_var(const char *var, include_ctx_t *ctx)
596 nd 101049 {
597     const char *val;
598 nd 101073 request_rec *r = ctx->intern->r;
599    
600 nd 101049 if (apr_isdigit(*var) && !var[1]) {
601 nd 101100 apr_size_t idx = *var - '0';
602 nd 101073 backref_t *re = ctx->intern->re;
603    
604 nd 101049 /* Handle $0 .. $9 from the last regex evaluated.
605     * The choice of returning NULL strings on not-found,
606     * v.s. empty strings on an empty match is deliberate.
607     */
608 niq 795445 if (!re || !re->have_match) {
609 fielding 101234 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
610     "regex capture $%" APR_SIZE_T_FMT " refers to no regex in %s",
611     idx, r->filename);
612 nd 101049 return NULL;
613     }
614 niq 795445 else if (re->match[idx]rm_so == re->match[idx].rm_eo) {
615     return NULL;
616     }
617     else if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
618     /* I don't think this can happen if have_match is true.
619     * But let's not risk a regression by dropping this
620     */
621     return NULL;
622     }
623     else if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
624     ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
625     "regex capture $%" APR_SIZE_T_FMT
626     " is out of range (last regex was: '%s') in %s",
627     idx, re->rexp, r->filename);
628     return NULL;
629     }
630    
631 nd 101049 else {
632 nd 101073 val = apr_pstrmemdup(ctx->dpool, re->source + re->match[idx].rm_so,
633     re->match[idx].rm_eo - re->match[idx].rm_so);
634 nd 101049 }
635     }
636     else {
637 nd 101073 val = apr_table_get(r->subprocess_env, var);
638 nd 101049
639 nd 101073 if (val == LAZY_VALUE) {
640 jorton 757376 val = add_include_vars_lazy(r, var, ctx->time_str);
641 nd 101073 }
642 nd 101049 }
643 nd 101073
644 nd 101049 return val;
645 nd 101036 }
646    
647 fielding 83751 /*
648     * Do variable substitution on strings
649 nd 101070 *
650 brianp 92747 * (Note: If out==NULL, this function allocs a buffer for the resulting
651 nd 101070 * string from ctx->pool. The return value is always the parsed string)
652 fielding 83751 */
653 nd 101069 static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
654 brianp 92747 apr_size_t length, int leave_name)
655 fielding 83751 {
656 nd 101069 request_rec *r = ctx->intern->r;
657 nd 101070 result_item_t *result = NULL, *current = NULL;
658     apr_size_t outlen = 0, inlen, span;
659     char *ret = NULL, *eout = NULL;
660     const char *p;
661 fielding 83751
662 nd 101070 if (out) {
663     /* sanity check, out && !length is not supported */
664     ap_assert(out && length);
665    
666     ret = out;
667     eout = out + length - 1;
668     }
669    
670     span = strcspn(in, "\\$");
671     inlen = strlen(in);
672    
673     /* fast exit */
674     if (inlen == span) {
675     if (out) {
676     apr_cpystrn(out, in, length);
677 brianp 92747 }
678 nd 101070 else {
679     ret = apr_pstrmemdup(ctx->pool, in, (length && length <= inlen)
680     ? length - 1 : inlen);
681     }
682    
683     return ret;
684 brianp 92747 }
685 nd 101070
686     /* well, actually something to do */
687     p = in + span;
688    
689     if (out) {
690     if (span) {
691     memcpy(out, in, (out+span <= eout) ? span : (eout-out));
692     out += span;
693     }
694     }
695 brianp 92747 else {
696 nd 101070 current = result = apr_palloc(ctx->dpool, sizeof(*result));
697     current->next = NULL;
698     current->string = in;
699     current->len = span;
700     outlen = span;
701 brianp 92747 }
702    
703 nd 101070 /* loop for specials */
704     do {
705     if ((out && out >= eout) || (length && outlen >= length)) {
706     break;
707     }
708 fielding 83751
709 nd 101070 /* prepare next entry */
710     if (!out && current->len) {
711     current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
712     current = current->next;
713     current->next = NULL;
714     current->len = 0;
715     }
716    
717     /*
718     * escaped character
719     */
720     if (*p == '\\') {
721     if (out) {
722     *out++ = (p[1] == '$') ? *++p : *p;
723     ++p;
724     }
725     else {
726     current->len = 1;
727     current->string = (p[1] == '$') ? ++p : p;
728     ++p;
729     ++outlen;
730     }
731     }
732    
733     /*
734     * variable expansion
735     */
736     else { /* *p == '$' */
737     const char *newp = NULL, *ep, *key = NULL;
738    
739     if (*++p == '{') {
740     ep = ap_strchr_c(++p, '}');
741     if (!ep) {
742     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing '}' on "
743     "variable \"%s\" in %s", p, r->filename);
744     break;
745 brianp 92747 }
746 nd 101070
747     if (p < ep) {
748     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
749     newp = ep + 1;
750 brianp 92747 }
751 nd 101070 p -= 2;
752 jerenkrantz 90874 }
753 fielding 83751 else {
754 nd 101070 ep = p;
755     while (*ep == '_' || apr_isalnum(*ep)) {
756     ++ep;
757     }
758    
759     if (p < ep) {
760     key = apr_pstrmemdup(ctx->dpool, p, ep - p);
761     newp = ep;
762     }
763     --p;
764 fielding 83751 }
765    
766 nd 101070 /* empty name results in a copy of '$' in the output string */
767     if (!key) {
768     if (out) {
769     *out++ = *p++;
770 jerenkrantz 90874 }
771     else {
772 nd 101070 current->len = 1;
773     current->string = p++;
774     ++outlen;
775 jerenkrantz 90874 }
776 nd 101070 }
777     else {
778     const char *val = get_include_var(key, ctx);
779     apr_size_t len = 0;
780 fielding 83751
781 nd 101070 if (val) {
782     len = strlen(val);
783 jerenkrantz 90874 }
784 nd 101070 else if (leave_name) {
785     val = p;
786     len = ep - p;
787 jerenkrantz 90874 }
788 nd 101070
789     if (val && len) {
790     if (out) {
791     memcpy(out, val, (out+len <= eout) ? len : (eout-out));
792     out += len;
793 brianp 92747 }
794 nd 101070 else {
795     current->len = len;
796     current->string = val;
797     outlen += len;
798 brianp 92747 }
799     }
800 nd 101070
801     p = newp;
802 jerenkrantz 90874 }
803 nd 101070 }
804    
805     if ((out && out >= eout) || (length && outlen >= length)) {
806 fielding 83751 break;
807     }
808 nd 101070
809     /* check the remainder */
810     if (*p && (span = strcspn(p, "\\$")) > 0) {
811     if (!out && current->len) {
812     current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
813     current = current->next;
814     current->next = NULL;
815     }
816    
817     if (out) {
818     memcpy(out, p, (out+span <= eout) ? span : (eout-out));
819     out += span;
820     }
821     else {
822     current->len = span;
823     current->string = p;
824     outlen += span;
825     }
826    
827     p += span;
828     }
829     } while (p < in+inlen);
830    
831     /* assemble result */
832     if (out) {
833     if (out > eout) {
834     *eout = '\0';
835     }
836     else {
837     *out = '\0';
838     }
839 fielding 83751 }
840 nd 101070 else {
841     const char *ep;
842    
843     if (length && outlen > length) {
844     outlen = length - 1;
845     }
846    
847     ret = out = apr_palloc(ctx->pool, outlen + 1);
848     ep = ret + outlen;
849    
850     do {
851     if (result->len) {
852     memcpy(out, result->string, (out+result->len <= ep)
853     ? result->len : (ep-out));
854     out += result->len;
855     }
856     result = result->next;
857     } while (result && out < ep);
858    
859     ret[outlen] = '\0';
860     }
861    
862     return ret;
863 fielding 83751 }
864    
865 niq 642978 static const char *ssi_parse_string(request_rec *r, const char *in)
866 fielding 83751 {
867 niq 642559 include_ctx_t *ctx = ap_get_module_config(r->request_config,
868     &include_module);
869     return ap_ssi_parse_string(ctx, in, NULL, 0, SSI_EXPAND_DROP_NAME);
870 fielding 83751 }
871 niq 642978 static int ssi_access(request_rec *r, ap_parse_node_t *current,
872 niq 642559 string_func_t parse_string)
873 fielding 83751 {
874 niq 642559 request_rec *rr;
875     include_ctx_t *ctx = ap_get_module_config(r->request_config,
876     &include_module);
877 fielding 83751
878 niq 642559 /* if this arg isn't -A, just return */
879     if (current->token.type != TOKEN_ACCESS || current->token.value[0] != 'A') {
880     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
881     "Unsupported option -%s in file %s",
882     current->token.value, r->filename);
883     return 1;
884 nd 101078 }
885 niq 642559 if (current->left || !current->right ||
886     (current->right->token.type != TOKEN_STRING &&
887     current->right->token.type != TOKEN_RE)) {
888     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
889     "Invalid expression in file %s: Token '-A' must be followed by a URI string.",
890     r->filename);
891     return 1; /* was_error */
892 fielding 83751 }
893 niq 642559 current->right->token.value =
894     ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
895     SSI_EXPAND_DROP_NAME);
896     rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
897     /* 400 and higher are considered access denied */
898     if (rr->status < HTTP_BAD_REQUEST) {
899     current->value = 1;
900 fielding 83751 }
901 nd 101078 else {
902 niq 642559 current->value = 0;
903     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r,
904     "mod_include: The tested "
905     "subrequest -A \"%s\" returned an error code.",
906     current->right->token.value);
907 nd 101078 }
908 niq 642559 ap_destroy_sub_req(rr);
909     return 0;
910 nd 101078 }
911    
912 nd 101049 /*
913     * +-------------------------------------------------------+
914     * | |
915     * | Action Handlers
916     * | |
917     * +-------------------------------------------------------+
918     */
919    
920     /*
921     * Extract the next tag name and value.
922     * If there are no more tags, set the tag name to NULL.
923     * The tag value is html decoded if dodecode is non-zero.
924     * The tag value may be NULL if there is no tag value..
925     */
926     static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
927     char **tag_val, int dodecode)
928     {
929     if (!ctx->intern->argv) {
930     *tag = NULL;
931     *tag_val = NULL;
932    
933     return;
934     }
935    
936     *tag_val = ctx->intern->argv->value;
937     *tag = ctx->intern->argv->name;
938    
939     ctx->intern->argv = ctx->intern->argv->next;
940    
941     if (dodecode && *tag_val) {
942     decodehtml(*tag_val);
943     }
944    
945     return;
946     }
947    
948     static int find_file(request_rec *r, const char *directive, const char *tag,
949     char *tag_val, apr_finfo_t *finfo)
950     {
951     char *to_send = tag_val;
952     request_rec *rr = NULL;
953     int ret=0;
954     char *error_fmt = NULL;
955     apr_status_t rv = APR_SUCCESS;
956    
957     if (!strcmp(tag, "file")) {
958 nd 101080 char *newpath;
959    
960     /* be safe; only files in this directory or below allowed */
961     rv = apr_filepath_merge(&newpath, NULL, tag_val,
962     APR_FILEPATH_SECUREROOTTEST |
963     APR_FILEPATH_NOTABSOLUTE, r->pool);
964    
965 jerenkrantz 104439 if (rv != APR_SUCCESS) {
966 nd 101049 error_fmt = "unable to access file \"%s\" "
967     "in parsed file %s";
968     }
969     else {
970     /* note: it is okay to pass NULL for the "next filter" since
971     we never attempt to "run" this sub request. */
972 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, NULL);
973 nd 101049
974     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
975     to_send = rr->filename;
976 jim 332306 if ((rv = apr_stat(finfo, to_send,
977 nd 101049 APR_FINFO_GPROT | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS
978     && rv != APR_INCOMPLETE) {
979     error_fmt = "unable to get information about \"%s\" "
980     "in parsed file %s";
981     }
982     }
983     else {
984     error_fmt = "unable to lookup information about \"%s\" "
985     "in parsed file %s";
986     }
987     }
988    
989     if (error_fmt) {
990     ret = -1;
991     ap_log_rerror(APLOG_MARK, APLOG_ERR,
992     rv, r, error_fmt, to_send, r->filename);
993     }
994    
995     if (rr) ap_destroy_sub_req(rr);
996 jim 332306
997 nd 101049 return ret;
998     }
999     else if (!strcmp(tag, "virtual")) {
1000     /* note: it is okay to pass NULL for the "next filter" since
1001     we never attempt to "run" this sub request. */
1002     rr = ap_sub_req_lookup_uri(tag_val, r, NULL);
1003    
1004     if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
1005     memcpy((char *) finfo, (const char *) &rr->finfo,
1006     sizeof(rr->finfo));
1007     ap_destroy_sub_req(rr);
1008     return 0;
1009     }
1010     else {
1011 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to get "
1012     "information about \"%s\" in parsed file %s",
1013     tag_val, r->filename);
1014 nd 101049 ap_destroy_sub_req(rr);
1015     return -1;
1016     }
1017     }
1018     else {
1019 nd 101080 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1020     "to tag %s in %s", tag, directive, r->filename);
1021 nd 101049 return -1;
1022     }
1023     }
1024    
1025 nd 101053 /*
1026     * <!--#include virtual|file="..." [virtual|file="..."] ... -->
1027     */
1028 nd 101049 static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
1029     apr_bucket_brigade *bb)
1030     {
1031     request_rec *r = f->r;
1032    
1033 nd 101053 if (!ctx->argc) {
1034     ap_log_rerror(APLOG_MARK,
1035     (ctx->flags & SSI_FLAG_PRINTING)
1036     ? APLOG_ERR : APLOG_WARNING,
1037     0, r, "missing argument for include element in %s",
1038     r->filename);
1039     }
1040 nd 101049
1041 nd 101053 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1042     return APR_SUCCESS;
1043     }
1044 nd 101049
1045 nd 101053 if (!ctx->argc) {
1046     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1047     return APR_SUCCESS;
1048     }
1049 nd 101049
1050 nd 101053 while (1) {
1051     char *tag = NULL;
1052     char *tag_val = NULL;
1053     request_rec *rr = NULL;
1054     char *error_fmt = NULL;
1055     char *parsed_string;
1056 nd 101049
1057 nd 101053 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1058     if (!tag || !tag_val) {
1059     break;
1060     }
1061 nd 101049
1062 nd 101053 if (strcmp(tag, "virtual") && strcmp(tag, "file")) {
1063     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1064     "\"%s\" to tag include in %s", tag, r->filename);
1065     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1066     break;
1067     }
1068 nd 101049
1069 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1070 nd 101053 SSI_EXPAND_DROP_NAME);
1071     if (tag[0] == 'f') {
1072 nd 101080 char *newpath;
1073     apr_status_t rv;
1074    
1075     /* be safe; only files in this directory or below allowed */
1076 nd 102147 rv = apr_filepath_merge(&newpath, NULL, parsed_string,
1077 nd 101080 APR_FILEPATH_SECUREROOTTEST |
1078     APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
1079    
1080 jerenkrantz 104439 if (rv != APR_SUCCESS) {
1081 nd 101053 error_fmt = "unable to include file \"%s\" in parsed file %s";
1082     }
1083     else {
1084 nd 101080 rr = ap_sub_req_lookup_file(newpath, r, f->next);
1085 nd 101053 }
1086     }
1087     else {
1088 minfrin 592951 if (r->kept_body) {
1089     rr = ap_sub_req_method_uri(r->method, parsed_string, r, f->next);
1090     }
1091     else {
1092     rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
1093     }
1094 nd 101053 }
1095 nd 101049
1096 nd 101053 if (!error_fmt && rr->status != HTTP_OK) {
1097     error_fmt = "unable to include \"%s\" in parsed file %s";
1098     }
1099 nd 101049
1100 nd 101053 if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
1101     rr->content_type && strncmp(rr->content_type, "text/", 5)) {
1102    
1103     error_fmt = "unable to include potential exec \"%s\" in parsed "
1104     "file %s";
1105     }
1106    
1107     /* See the Kludge in includes_filter for why.
1108     * Basically, it puts a bread crumb in here, then looks
1109     * for the crumb later to see if its been here.
1110     */
1111 niq 642559 ctx->intern->kludge_child = rr;
1112 nd 101053
1113     if (!error_fmt && ap_run_sub_req(rr)) {
1114     error_fmt = "unable to include \"%s\" in parsed file %s";
1115     }
1116    
1117     if (error_fmt) {
1118     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val,
1119     r->filename);
1120     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1121     }
1122    
1123 jorton 179763 /* Do *not* destroy the subrequest here; it may have allocated
1124     * variables in this r->subprocess_env in the subrequest's
1125     * r->pool, so that pool must survive as long as this request.
1126     * Yes, this is a memory leak. */
1127 nd 101053
1128     if (error_fmt) {
1129     break;
1130     }
1131 nd 101049 }
1132    
1133     return APR_SUCCESS;
1134     }
1135    
1136 nd 101054 /*
1137     * <!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... -->
1138     */
1139 nd 101049 static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
1140     apr_bucket_brigade *bb)
1141     {
1142     enum {E_NONE, E_URL, E_ENTITY} encode;
1143     request_rec *r = f->r;
1144    
1145 nd 101054 if (!ctx->argc) {
1146     ap_log_rerror(APLOG_MARK,
1147     (ctx->flags & SSI_FLAG_PRINTING)
1148     ? APLOG_ERR : APLOG_WARNING,
1149     0, r, "missing argument for echo element in %s",
1150     r->filename);
1151     }
1152    
1153     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1154     return APR_SUCCESS;
1155     }
1156    
1157     if (!ctx->argc) {
1158     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1159     return APR_SUCCESS;
1160     }
1161    
1162 nd 101049 encode = E_ENTITY;
1163    
1164 nd 101054 while (1) {
1165     char *tag = NULL;
1166     char *tag_val = NULL;
1167 nd 101049
1168 nd 101054 ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1169     if (!tag || !tag_val) {
1170     break;
1171     }
1172 nd 101049
1173 nd 101054 if (!strcmp(tag, "var")) {
1174     const char *val;
1175     const char *echo_text = NULL;
1176     apr_size_t e_len;
1177    
1178 nd 101070 val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
1179 nd 101079 0, SSI_EXPAND_DROP_NAME),
1180 nd 101070 ctx);
1181    
1182 nd 101054 if (val) {
1183     switch(encode) {
1184     case E_NONE:
1185     echo_text = val;
1186     break;
1187     case E_URL:
1188     echo_text = ap_escape_uri(ctx->dpool, val);
1189     break;
1190     case E_ENTITY:
1191 niq 730296 /* PR#25202: escape anything non-ascii here */
1192     echo_text = ap_escape_html2(ctx->dpool, val, 1);
1193 nd 101054 break;
1194 nd 101049 }
1195 nd 101054
1196     e_len = strlen(echo_text);
1197     }
1198     else {
1199 nd 101129 echo_text = ctx->intern->undefined_echo;
1200     e_len = ctx->intern->undefined_echo_len;
1201 nd 101049 }
1202 nd 101054
1203     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
1204 nd 101129 apr_pmemdup(ctx->pool, echo_text, e_len),
1205 nd 101054 e_len, ctx->pool, f->c->bucket_alloc));
1206     }
1207     else if (!strcmp(tag, "encoding")) {
1208     if (!strcasecmp(tag_val, "none")) {
1209     encode = E_NONE;
1210 nd 101049 }
1211 nd 101054 else if (!strcasecmp(tag_val, "url")) {
1212     encode = E_URL;
1213     }
1214     else if (!strcasecmp(tag_val, "entity")) {
1215     encode = E_ENTITY;
1216     }
1217 nd 101049 else {
1218 nd 101054 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1219     "\"%s\" to parameter \"encoding\" of tag echo in "
1220     "%s", tag_val, r->filename);
1221 nd 101049 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1222 nd 101054 break;
1223 nd 101049 }
1224     }
1225 nd 101054 else {
1226     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1227     "\"%s\" in tag echo of %s", tag, r->filename);
1228     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1229     break;
1230     }
1231 nd 101049 }
1232    
1233     return APR_SUCCESS;
1234     }
1235    
1236 nd 101055 /*
1237 nd 101130 * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
1238     * [echomsg="..."] -->
1239 nd 101049 */
1240     static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
1241     apr_bucket_brigade *bb)
1242     {
1243     request_rec *r = f->r;
1244     apr_table_t *env = r->subprocess_env;
1245    
1246 nd 101055 if (!ctx->argc) {
1247     ap_log_rerror(APLOG_MARK,
1248     (ctx->flags & SSI_FLAG_PRINTING)
1249     ? APLOG_ERR : APLOG_WARNING,
1250     0, r, "missing argument for config element in %s",
1251     r->filename);
1252     }
1253    
1254     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1255     return APR_SUCCESS;
1256     }
1257    
1258     if (!ctx->argc) {
1259     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1260     return APR_SUCCESS;
1261     }
1262    
1263     while (1) {
1264     char *tag = NULL;
1265     char *tag_val = NULL;
1266    
1267     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
1268     if (!tag || !tag_val) {
1269     break;
1270     }
1271    
1272     if (!strcmp(tag, "errmsg")) {
1273 nd 101071 ctx->error_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1274     SSI_EXPAND_DROP_NAME);
1275 nd 101055 }
1276 nd 101130 else if (!strcmp(tag, "echomsg")) {
1277     ctx->intern->undefined_echo =
1278     ap_ssi_parse_string(ctx, tag_val, NULL, 0,SSI_EXPAND_DROP_NAME);
1279     ctx->intern->undefined_echo_len=strlen(ctx->intern->undefined_echo);
1280     }
1281 nd 101055 else if (!strcmp(tag, "timefmt")) {
1282     apr_time_t date = r->request_time;
1283 nd 101049
1284 nd 101071 ctx->time_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1285     SSI_EXPAND_DROP_NAME);
1286    
1287 jim 332306 apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date,
1288 nd 101055 ctx->time_str, 0));
1289 jim 332306 apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date,
1290 nd 101055 ctx->time_str, 1));
1291     apr_table_setn(env, "LAST_MODIFIED",
1292 jim 332306 ap_ht_time(r->pool, r->finfo.mtime,
1293 nd 101055 ctx->time_str, 0));
1294     }
1295     else if (!strcmp(tag, "sizefmt")) {
1296 nd 101130 char *parsed_string;
1297    
1298 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1299 nd 101055 SSI_EXPAND_DROP_NAME);
1300     if (!strcmp(parsed_string, "bytes")) {
1301     ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
1302 nd 101049 }
1303 nd 101055 else if (!strcmp(parsed_string, "abbrev")) {
1304     ctx->flags &= SSI_FLAG_SIZE_ABBREV;
1305 nd 101049 }
1306     else {
1307 nd 101055 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
1308     "\"%s\" to parameter \"sizefmt\" of tag config "
1309     "in %s", parsed_string, r->filename);
1310 nd 101049 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1311 nd 101055 break;
1312 nd 101049 }
1313     }
1314 nd 101055 else {
1315     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
1316     "\"%s\" to tag config in %s", tag, r->filename);
1317     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1318     break;
1319     }
1320 nd 101049 }
1321    
1322     return APR_SUCCESS;
1323     }
1324    
1325 nd 101057 /*
1326     * <!--#fsize virtual|file="..." [virtual|file="..."] ... -->
1327     */
1328 nd 101049 static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
1329     apr_bucket_brigade *bb)
1330     {
1331     request_rec *r = f->r;
1332    
1333 nd 101057 if (!ctx->argc) {
1334     ap_log_rerror(APLOG_MARK,
1335     (ctx->flags & SSI_FLAG_PRINTING)
1336     ? APLOG_ERR : APLOG_WARNING,
1337     0, r, "missing argument for fsize element in %s",
1338     r->filename);
1339     }
1340    
1341     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1342     return APR_SUCCESS;
1343     }
1344    
1345     if (!ctx->argc) {
1346     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1347     return APR_SUCCESS;
1348     }
1349    
1350     while (1) {
1351     char *tag = NULL;
1352     char *tag_val = NULL;
1353     apr_finfo_t finfo;
1354     char *parsed_string;
1355    
1356     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1357     if (!tag || !tag_val) {
1358     break;
1359     }
1360    
1361 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1362 nd 101057 SSI_EXPAND_DROP_NAME);
1363    
1364     if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
1365     char *buf;
1366     apr_size_t len;
1367    
1368     if (!(ctx->flags & SSI_FLAG_SIZE_IN_BYTES)) {
1369     buf = apr_strfsize(finfo.size, apr_palloc(ctx->pool, 5));
1370     len = 4; /* omit the \0 terminator */
1371 nd 101049 }
1372     else {
1373 nd 101057 apr_size_t l, x, pos;
1374     char *tmp;
1375 nd 101049
1376 nd 101057 tmp = apr_psprintf(ctx->dpool, "%" APR_OFF_T_FMT, finfo.size);
1377     len = l = strlen(tmp);
1378 nd 101049
1379 nd 101057 for (x = 0; x < l; ++x) {
1380     if (x && !((l - x) % 3)) {
1381     ++len;
1382 nd 101049 }
1383 nd 101057 }
1384 nd 101049
1385 nd 101057 if (len == l) {
1386     buf = apr_pstrmemdup(ctx->pool, tmp, len);
1387 nd 101049 }
1388     else {
1389 nd 101057 buf = apr_palloc(ctx->pool, len);
1390    
1391     for (pos = x = 0; x < l; ++x) {
1392     if (x && !((l - x) % 3)) {
1393     buf[pos++] = ',';
1394     }
1395     buf[pos++] = tmp[x];
1396     }
1397 nd 101049 }
1398     }
1399 nd 101057
1400     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buf, len,
1401     ctx->pool, f->c->bucket_alloc));
1402 nd 101049 }
1403 nd 101057 else {
1404     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1405     break;
1406     }
1407 nd 101049 }
1408    
1409     return APR_SUCCESS;
1410     }
1411    
1412 nd 101063 /*
1413     * <!--#flastmod virtual|file="..." [virtual|file="..."] ... -->
1414     */
1415 nd 101049 static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
1416     apr_bucket_brigade *bb)
1417     {
1418     request_rec *r = f->r;
1419    
1420 nd 101063 if (!ctx->argc) {
1421     ap_log_rerror(APLOG_MARK,
1422     (ctx->flags & SSI_FLAG_PRINTING)
1423     ? APLOG_ERR : APLOG_WARNING,
1424     0, r, "missing argument for flastmod element in %s",
1425     r->filename);
1426     }
1427 nd 101049
1428 nd 101063 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1429     return APR_SUCCESS;
1430     }
1431 nd 101049
1432 nd 101063 if (!ctx->argc) {
1433     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1434     return APR_SUCCESS;
1435     }
1436    
1437     while (1) {
1438     char *tag = NULL;
1439     char *tag_val = NULL;
1440     apr_finfo_t finfo;
1441     char *parsed_string;
1442    
1443     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1444     if (!tag || !tag_val) {
1445     break;
1446 nd 101049 }
1447 nd 101063
1448 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1449 nd 101063 SSI_EXPAND_DROP_NAME);
1450    
1451     if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
1452     char *t_val;
1453     apr_size_t t_len;
1454    
1455     t_val = ap_ht_time(ctx->pool, finfo.mtime, ctx->time_str, 0);
1456     t_len = strlen(t_val);
1457    
1458     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(t_val, t_len,
1459     ctx->pool, f->c->bucket_alloc));
1460     }
1461     else {
1462     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1463     break;
1464     }
1465 nd 101049 }
1466    
1467     return APR_SUCCESS;
1468     }
1469    
1470 nd 101064 /*
1471     * <!--#if expr="..." -->
1472     */
1473 nd 101046 static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
1474     apr_bucket_brigade *bb)
1475 fielding 83751 {
1476 nd 101064 char *tag = NULL;
1477     char *expr = NULL;
1478 nd 101046 request_rec *r = f->r;
1479 nd 101078 int expr_ret, was_error;
1480 fielding 83751
1481 nd 101064 if (ctx->argc != 1) {
1482     ap_log_rerror(APLOG_MARK,
1483     (ctx->flags & SSI_FLAG_PRINTING)
1484     ? APLOG_ERR : APLOG_WARNING,
1485     0, r, (ctx->argc)
1486     ? "too many arguments for if element in %s"
1487     : "missing expr argument for if element in %s",
1488     r->filename);
1489     }
1490    
1491 nd 101048 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1492 nd 101064 ++(ctx->if_nesting_level);
1493     return APR_SUCCESS;
1494 rbb 87067 }
1495 nd 101046
1496 nd 101064 if (ctx->argc != 1) {
1497     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1498     return APR_SUCCESS;
1499     }
1500    
1501     ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
1502    
1503     if (strcmp(tag, "expr")) {
1504     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1505     "to tag if in %s", tag, r->filename);
1506     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1507     return APR_SUCCESS;
1508     }
1509    
1510     if (!expr) {
1511     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr value for if "
1512     "element in %s", r->filename);
1513     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1514     return APR_SUCCESS;
1515     }
1516    
1517 nd 101078 DEBUG_PRINTF((ctx, "**** if expr=\"%s\"\n", expr));
1518 rbb 87067
1519 niq 642559 expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
1520     ssi_parse_string, ctx->intern->access_func);
1521 nd 101064
1522     if (was_error) {
1523     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1524     return APR_SUCCESS;
1525 fielding 83751 }
1526 nd 101048
1527 nd 101064 if (expr_ret) {
1528     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1529     }
1530     else {
1531     ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
1532     }
1533    
1534 nd 101078 DEBUG_DUMP_COND(ctx, " if");
1535    
1536 nd 101064 ctx->if_nesting_level = 0;
1537    
1538 nd 101046 return APR_SUCCESS;
1539 fielding 83751 }
1540    
1541 nd 101065 /*
1542     * <!--#elif expr="..." -->
1543     */
1544 nd 101046 static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
1545     apr_bucket_brigade *bb)
1546 fielding 83751 {
1547 nd 101065 char *tag = NULL;
1548     char *expr = NULL;
1549     request_rec *r = f->r;
1550 nd 101078 int expr_ret, was_error;
1551 fielding 83751
1552 nd 101065 if (ctx->argc != 1) {
1553     ap_log_rerror(APLOG_MARK,
1554     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1555     0, r, (ctx->argc)
1556     ? "too many arguments for if element in %s"
1557     : "missing expr argument for if element in %s",
1558     r->filename);
1559     }
1560    
1561     if (ctx->if_nesting_level) {
1562     return APR_SUCCESS;
1563     }
1564    
1565     if (ctx->argc != 1) {
1566     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1567     return APR_SUCCESS;
1568     }
1569    
1570     ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
1571    
1572     if (strcmp(tag, "expr")) {
1573     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
1574     "to tag if in %s", tag, r->filename);
1575     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1576     return APR_SUCCESS;
1577     }
1578    
1579     if (!expr) {
1580     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr in elif "
1581     "statement: %s", r->filename);
1582     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1583     return APR_SUCCESS;
1584     }
1585    
1586 nd 101078 DEBUG_PRINTF((ctx, "**** elif expr=\"%s\"\n", expr));
1587     DEBUG_DUMP_COND(ctx, " elif");
1588 nd 101065
1589     if (ctx->flags & SSI_FLAG_COND_TRUE) {
1590     ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
1591     return APR_SUCCESS;
1592 fielding 83751 }
1593 nd 101048
1594 niq 642559 expr_ret = ap_expr_evalstring(r, expr, &was_error, &ctx->intern->re,
1595     ssi_parse_string, ctx->intern->access_func);
1596 nd 101065
1597     if (was_error) {
1598     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1599     return APR_SUCCESS;
1600     }
1601    
1602     if (expr_ret) {
1603     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1604     }
1605     else {
1606     ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
1607     }
1608    
1609 nd 101078 DEBUG_DUMP_COND(ctx, " elif");
1610 nd 101065
1611 nd 101046 return APR_SUCCESS;
1612 fielding 83751 }
1613    
1614 nd 101066 /*
1615     * <!--#else -->
1616     */
1617 nd 101046 static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f,
1618     apr_bucket_brigade *bb)
1619 fielding 83751 {
1620 nd 101046 request_rec *r = f->r;
1621 fielding 83751
1622 nd 101066 if (ctx->argc) {
1623     ap_log_rerror(APLOG_MARK,
1624     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1625     0, r, "else directive does not take tags in %s",
1626     r->filename);
1627     }
1628    
1629     if (ctx->if_nesting_level) {
1630     return APR_SUCCESS;
1631     }
1632    
1633     if (ctx->argc) {
1634     if (ctx->flags & SSI_FLAG_PRINTING) {
1635     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1636 fielding 83751 }
1637 nd 101066
1638     return APR_SUCCESS;
1639     }
1640    
1641 nd 101078 DEBUG_DUMP_COND(ctx, " else");
1642 jim 332306
1643 nd 101066 if (ctx->flags & SSI_FLAG_COND_TRUE) {
1644     ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
1645 fielding 83751 }
1646 nd 101066 else {
1647     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1648     }
1649 nd 101048
1650 nd 101046 return APR_SUCCESS;
1651 fielding 83751 }
1652    
1653 nd 101066 /*
1654     * <!--#endif -->
1655     */
1656 nd 101046 static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f,
1657     apr_bucket_brigade *bb)
1658 fielding 83751 {
1659 nd 101046 request_rec *r = f->r;
1660 fielding 83751
1661 nd 101066 if (ctx->argc) {
1662     ap_log_rerror(APLOG_MARK,
1663     (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
1664     0, r, "endif directive does not take tags in %s",
1665     r->filename);
1666 fielding 83751 }
1667 nd 101066
1668     if (ctx->if_nesting_level) {
1669     --(ctx->if_nesting_level);
1670 nd 101046 return APR_SUCCESS;
1671 fielding 83751 }
1672 nd 101066
1673     if (ctx->argc) {
1674     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1675     return APR_SUCCESS;
1676     }
1677    
1678 nd 101078 DEBUG_DUMP_COND(ctx, "endif");
1679    
1680 nd 101066 ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
1681    
1682     return APR_SUCCESS;
1683 fielding 83751 }
1684    
1685 nd 101067 /*
1686     * <!--#set var="..." value="..." ... -->
1687     */
1688 nd 101046 static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
1689     apr_bucket_brigade *bb)
1690 fielding 83751 {
1691 nd 101067 char *var = NULL;
1692 nd 101046 request_rec *r = f->r;
1693 ianh 93556 request_rec *sub = r->main;
1694     apr_pool_t *p = r->pool;
1695 fielding 83751
1696 nd 101067 if (ctx->argc < 2) {
1697     ap_log_rerror(APLOG_MARK,
1698     (ctx->flags & SSI_FLAG_PRINTING)
1699     ? APLOG_ERR : APLOG_WARNING,
1700     0, r, "missing argument for set element in %s",
1701     r->filename);
1702     }
1703    
1704     if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1705     return APR_SUCCESS;
1706     }
1707    
1708     if (ctx->argc < 2) {
1709     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1710     return APR_SUCCESS;
1711     }
1712    
1713 jim 332306 /* we need to use the 'main' request pool to set notes as that is
1714 ianh 93556 * a notes lifetime
1715     */
1716     while (sub) {
1717     p = sub->pool;
1718     sub = sub->main;
1719     }
1720    
1721 nd 101067 while (1) {
1722     char *tag = NULL;
1723     char *tag_val = NULL;
1724    
1725     ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
1726    
1727     if (!tag || !tag_val) {
1728     break;
1729     }
1730    
1731     if (!strcmp(tag, "var")) {
1732 nd 101079 var = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1733 nd 101067 SSI_EXPAND_DROP_NAME);
1734     }
1735     else if (!strcmp(tag, "value")) {
1736     char *parsed_string;
1737    
1738     if (!var) {
1739     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "variable must "
1740     "precede value in set directive in %s",
1741     r->filename);
1742 nd 101046 SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1743 nd 101067 break;
1744 fielding 83751 }
1745 nd 101067
1746 nd 101079 parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
1747 nd 101067 SSI_EXPAND_DROP_NAME);
1748     apr_table_setn(r->subprocess_env, apr_pstrdup(p, var),
1749     apr_pstrdup(p, parsed_string));
1750 fielding 83751 }
1751 nd 101067 else {
1752     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid tag for set "
1753     "directive in %s", r->filename);
1754     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1755     break;
1756     }
1757 fielding 83751 }
1758 nd 101048
1759 nd 101046 return APR_SUCCESS;
1760 fielding 83751 }
1761    
1762 nd 101068 /*
1763     * <!--#printenv -->
1764     */
1765 nd 101046 static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f,
1766     apr_bucket_brigade *bb)
1767 fielding 83751 {
1768 nd 101046 request_rec *r = f->r;
1769 nd 101068 const apr_array_header_t *arr;
1770     const apr_table_entry_t *elts;
1771     int i;
1772 fielding 83751
1773 nd 101068 if (ctx->argc) {
1774     ap_log_rerror(APLOG_MARK,
1775     (ctx->flags & SSI_FLAG_PRINTING)
1776     ? APLOG_ERR : APLOG_WARNING,
1777     0, r, "printenv directive does not take tags in %s",
1778     r->filename);
1779     }
1780 rbb 87067
1781 nd 101068 if (!(ctx->flags & SSI_FLAG_PRINTING)) {
1782     return APR_SUCCESS;
1783     }
1784    
1785     if (ctx->argc) {
1786     SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
1787     return APR_SUCCESS;
1788     }
1789    
1790     arr = apr_table_elts(r->subprocess_env);
1791     elts = (apr_table_entry_t *)arr->elts;
1792    
1793     for (i = 0; i < arr->nelts; ++i) {
1794     const char *key_text, *val_text;
1795     char *key_val, *next;
1796     apr_size_t k_len, v_len, kv_length;
1797    
1798     /* get key */
1799     key_text = ap_escape_html(ctx->dpool, elts[i].key);
1800     k_len = strlen(key_text);
1801    
1802     /* get value */
1803     val_text = elts[i].val;
1804     if (val_text == LAZY_VALUE) {
1805 jorton 757376 val_text = add_include_vars_lazy(r, elts[i].key, ctx->time_str);
1806 fielding 83751 }
1807 nd 101068 val_text = ap_escape_html(ctx->dpool, elts[i].val);
1808     v_len = strlen(val_text);
1809    
1810     /* assemble result */
1811     kv_length = k_len + v_len + sizeof("=\n");
1812     key_val = apr_palloc(ctx->pool, kv_length);
1813     next = key_val;
1814    
1815     memcpy(next, key_text, k_len);
1816     next += k_len;
1817     *next++ = '=';
1818     memcpy(next, val_text, v_len);
1819     next += v_len;
1820     *next++ = '\n';
1821     *next = 0;
1822    
1823     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(key_val, kv_length-1,
1824     ctx->pool, f->c->bucket_alloc));
1825 fielding 83751 }
1826 nd 101048
1827 nd 101068 ctx->flush_now = 1;
1828 nd 101046 return APR_SUCCESS;
1829 fielding 83751 }
1830    
1831    
1832 nd 101036 /*
1833 nd 101049 * +-------------------------------------------------------+
1834     * | |
1835     * | Main Includes-Filter Engine
1836     * | |
1837     * +-------------------------------------------------------+
1838     */
1839    
1840     /* This is an implementation of the BNDM search algorithm.
1841     *
1842 jim 332306 * Fast and Flexible String Matching by Combining Bit-parallelism and
1843     * Suffix Automata (2001)
1844 nd 101049 * Gonzalo Navarro, Mathieu Raffinot
1845     *
1846     * http://www-igm.univ-mlv.fr/~raffinot/ftp/jea2001.ps.gz
1847     *
1848     * Initial code submitted by Sascha Schumann.
1849     */
1850 jim 332306
1851 nd 101049 /* Precompile the bndm_t data structure. */
1852 nd 101052 static bndm_t *bndm_compile(apr_pool_t *pool, const char *n, apr_size_t nl)
1853 nd 101049 {
1854     unsigned int x;
1855     const char *ne = n + nl;
1856 nd 101052 bndm_t *t = apr_palloc(pool, sizeof(*t));
1857 nd 101049
1858     memset(t->T, 0, sizeof(unsigned int) * 256);
1859 nd 101052 t->pattern_len = nl;
1860    
1861     for (x = 1; n < ne; x <<= 1) {
1862 nd 101049 t->T[(unsigned char) *n++] |= x;
1863 nd 101052 }
1864 nd 101049
1865     t->x = x - 1;
1866 nd 101052
1867     return t;
1868 nd 101049 }
1869    
1870     /* Implements the BNDM search algorithm (as described above).
1871     *
1872     * h - the string to look in
1873     * hl - length of the string to look for
1874 jim 332306 * t - precompiled bndm structure against the pattern
1875 nd 101049 *
1876     * Returns the count of character that is the first match or hl if no
1877     * match is found.
1878     */
1879 nd 101052 static apr_size_t bndm(bndm_t *t, const char *h, apr_size_t hl)
1880 nd 101049 {
1881     const char *skip;
1882     const char *he, *p, *pi;
1883     unsigned int *T, x, d;
1884 nd 101052 apr_size_t nl;
1885 nd 101049
1886     he = h + hl;
1887    
1888     T = t->T;
1889     x = t->x;
1890 nd 101052 nl = t->pattern_len;