~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/filters/mod_include.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 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
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * 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
 */
 
16
 
 
17
#include "apr.h"
 
18
#include "apr_strings.h"
 
19
#include "apr_thread_proc.h"
 
20
#include "apr_hash.h"
 
21
#include "apr_user.h"
 
22
#include "apr_lib.h"
 
23
#include "apr_optional.h"
 
24
 
 
25
#define APR_WANT_STRFUNC
 
26
#define APR_WANT_MEMFUNC
 
27
#include "apr_want.h"
 
28
 
 
29
#include "ap_config.h"
 
30
#include "util_filter.h"
 
31
#include "httpd.h"
 
32
#include "http_config.h"
 
33
#include "http_core.h"
 
34
#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
#include "http_core.h"
 
41
#include "mod_include.h"
 
42
 
 
43
/* helper for Latin1 <-> entity encoding */
 
44
#if APR_CHARSET_EBCDIC
 
45
#include "util_ebcdic.h"
 
46
#define RAW_ASCII_CHAR(ch)  apr_xlate_conv_byte(ap_hdrs_from_ascii, \
 
47
                                                (unsigned char)ch)
 
48
#else /* APR_CHARSET_EBCDIC */
 
49
#define RAW_ASCII_CHAR(ch)  (ch)
 
50
#endif /* !APR_CHARSET_EBCDIC */
 
51
 
 
52
 
 
53
/*
 
54
 * +-------------------------------------------------------+
 
55
 * |                                                       |
 
56
 * |                 Types and Structures
 
57
 * |                                                       |
 
58
 * +-------------------------------------------------------+
 
59
 */
 
60
 
 
61
/* sll used for string expansion */
 
62
typedef struct result_item {
 
63
    struct result_item *next;
 
64
    apr_size_t len;
 
65
    const char *string;
 
66
} result_item_t;
 
67
 
 
68
/* conditional expression parser stuff */
 
69
typedef enum {
 
70
    TOKEN_STRING,
 
71
    TOKEN_RE,
 
72
    TOKEN_AND,
 
73
    TOKEN_OR,
 
74
    TOKEN_NOT,
 
75
    TOKEN_EQ,
 
76
    TOKEN_NE,
 
77
    TOKEN_RBRACE,
 
78
    TOKEN_LBRACE,
 
79
    TOKEN_GROUP,
 
80
    TOKEN_GE,
 
81
    TOKEN_LE,
 
82
    TOKEN_GT,
 
83
    TOKEN_LT
 
84
} token_type_t;
 
85
 
 
86
typedef struct {
 
87
    token_type_t  type;
 
88
    const char   *value;
 
89
#ifdef DEBUG_INCLUDE
 
90
    const char   *s;
 
91
#endif
 
92
} token_t;
 
93
 
 
94
typedef struct parse_node {
 
95
    struct parse_node *parent;
 
96
    struct parse_node *left;
 
97
    struct parse_node *right;
 
98
    token_t token;
 
99
    int value;
 
100
    int done;
 
101
#ifdef DEBUG_INCLUDE
 
102
    int dump_done;
 
103
#endif
 
104
} parse_node_t;
 
105
 
 
106
typedef enum {
 
107
    XBITHACK_OFF,
 
108
    XBITHACK_ON,
 
109
    XBITHACK_FULL
 
110
} xbithack_t;
 
111
 
 
112
typedef struct {
 
113
    const char *default_error_msg;
 
114
    const char *default_time_fmt;
 
115
    const char *undefined_echo;
 
116
    xbithack_t  xbithack;
 
117
} include_dir_config;
 
118
 
 
119
typedef struct {
 
120
    const char *default_start_tag;
 
121
    const char *default_end_tag;
 
122
} include_server_config;
 
123
 
 
124
/* main parser states */
 
125
typedef enum {
 
126
    PARSE_PRE_HEAD,
 
127
    PARSE_HEAD,
 
128
    PARSE_DIRECTIVE,
 
129
    PARSE_DIRECTIVE_POSTNAME,
 
130
    PARSE_DIRECTIVE_TAIL,
 
131
    PARSE_DIRECTIVE_POSTTAIL,
 
132
    PARSE_PRE_ARG,
 
133
    PARSE_ARG,
 
134
    PARSE_ARG_NAME,
 
135
    PARSE_ARG_POSTNAME,
 
136
    PARSE_ARG_EQ,
 
137
    PARSE_ARG_PREVAL,
 
138
    PARSE_ARG_VAL,
 
139
    PARSE_ARG_VAL_ESC,
 
140
    PARSE_ARG_POSTVAL,
 
141
    PARSE_TAIL,
 
142
    PARSE_TAIL_SEQ,
 
143
    PARSE_EXECUTE
 
144
} parse_state_t;
 
145
 
 
146
typedef struct arg_item {
 
147
    struct arg_item  *next;
 
148
    char             *name;
 
149
    apr_size_t        name_len;
 
150
    char             *value;
 
151
    apr_size_t        value_len;
 
152
} arg_item_t;
 
153
 
 
154
typedef struct {
 
155
    const char *source;
 
156
    const char *rexp;
 
157
    apr_size_t  nsub;
 
158
    ap_regmatch_t match[AP_MAX_REG_MATCH];
 
159
} backref_t;
 
160
 
 
161
typedef struct {
 
162
    unsigned int T[256];
 
163
    unsigned int x;
 
164
    apr_size_t pattern_len;
 
165
} bndm_t;
 
166
 
 
167
struct ssi_internal_ctx {
 
168
    parse_state_t state;
 
169
    int           seen_eos;
 
170
    int           error;
 
171
    char          quote;         /* quote character value (or \0) */
 
172
    apr_size_t    parse_pos;     /* parse position of partial matches */
 
173
    apr_size_t    bytes_read;
 
174
 
 
175
    apr_bucket_brigade *tmp_bb;
 
176
 
 
177
    request_rec  *r;
 
178
    const char   *start_seq;
 
179
    bndm_t       *start_seq_pat;
 
180
    const char   *end_seq;
 
181
    apr_size_t    end_seq_len;
 
182
    char         *directive;     /* name of the current directive */
 
183
    apr_size_t    directive_len; /* length of the current directive name */
 
184
 
 
185
    arg_item_t   *current_arg;   /* currently parsed argument */
 
186
    arg_item_t   *argv;          /* all arguments */
 
187
 
 
188
    backref_t    *re;            /* NULL if there wasn't a regex yet */
 
189
 
 
190
    const char   *undefined_echo;
 
191
    apr_size_t    undefined_echo_len;
 
192
 
 
193
#ifdef DEBUG_INCLUDE
 
194
    struct {
 
195
        ap_filter_t *f;
 
196
        apr_bucket_brigade *bb;
 
197
    } debug;
 
198
#endif
 
199
};
 
200
 
 
201
 
 
202
/*
 
203
 * +-------------------------------------------------------+
 
204
 * |                                                       |
 
205
 * |                  Debugging Utilities
 
206
 * |                                                       |
 
207
 * +-------------------------------------------------------+
 
208
 */
 
209
 
 
210
#ifdef DEBUG_INCLUDE
 
211
 
 
212
#define TYPE_TOKEN(token, ttype) do { \
 
213
    (token)->type = ttype;            \
 
214
    (token)->s = #ttype;              \
 
215
} while(0)
 
216
 
 
217
#define CREATE_NODE(ctx, name) do {                       \
 
218
    (name) = apr_palloc((ctx)->dpool, sizeof(*(name)));   \
 
219
    (name)->parent = (name)->left = (name)->right = NULL; \
 
220
    (name)->done = 0;                                     \
 
221
    (name)->dump_done = 0;                                \
 
222
} while(0)
 
223
 
 
224
static void debug_printf(include_ctx_t *ctx, const char *fmt, ...)
 
225
{
 
226
    va_list ap;
 
227
    char *debug__str;
 
228
 
 
229
    va_start(ap, fmt);
 
230
    debug__str = apr_pvsprintf(ctx->pool, fmt, ap);
 
231
    va_end(ap);
 
232
 
 
233
    APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
 
234
                            debug__str, strlen(debug__str), ctx->pool,
 
235
                            ctx->intern->debug.f->c->bucket_alloc));
 
236
}
 
237
 
 
238
#define DUMP__CHILD(ctx, is, node, child) if (1) {                           \
 
239
    parse_node_t *d__c = node->child;                                        \
 
240
    if (d__c) {                                                              \
 
241
        if (!d__c->dump_done) {                                              \
 
242
            if (d__c->parent != node) {                                      \
 
243
                debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
 
244
                if (!d__c->parent) {                                         \
 
245
                    debug_printf(ctx, "Parent of " #child " child node is "  \
 
246
                                 "NULL.\n");                                 \
 
247
                }                                                            \
 
248
                else {                                                       \
 
249
                    debug_printf(ctx, "Parent of " #child " child node "     \
 
250
                                 "points to another node (of type %s)!\n",   \
 
251
                                 d__c->parent->token.s);                     \
 
252
                }                                                            \
 
253
                return;                                                      \
 
254
            }                                                                \
 
255
            node = d__c;                                                     \
 
256
            continue;                                                        \
 
257
        }                                                                    \
 
258
    }                                                                        \
 
259
    else {                                                                   \
 
260
        debug_printf(ctx, "%s(missing)\n", is);                              \
 
261
    }                                                                        \
 
262
}
 
263
 
 
264
static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
 
265
{
 
266
    parse_node_t *current;
 
267
    char *is;
 
268
 
 
269
    if (!root) {
 
270
        debug_printf(ctx, "     -- Parse Tree empty --\n\n");
 
271
        return;
 
272
    }
 
273
 
 
274
    debug_printf(ctx, "     ----- Parse Tree -----\n");
 
275
    current = root;
 
276
    is = "     ";
 
277
 
 
278
    while (current) {
 
279
        switch (current->token.type) {
 
280
        case TOKEN_STRING:
 
281
        case TOKEN_RE:
 
282
            debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
 
283
                         current->token.value);
 
284
            current->dump_done = 1;
 
285
            current = current->parent;
 
286
            continue;
 
287
 
 
288
        case TOKEN_NOT:
 
289
        case TOKEN_GROUP:
 
290
        case TOKEN_RBRACE:
 
291
        case TOKEN_LBRACE:
 
292
            if (!current->dump_done) {
 
293
                debug_printf(ctx, "%s%s\n", is, current->token.s);
 
294
                is = apr_pstrcat(ctx->dpool, is, "    ", NULL);
 
295
                current->dump_done = 1;
 
296
            }
 
297
 
 
298
            DUMP__CHILD(ctx, is, current, right)
 
299
 
 
300
            if (!current->right || current->right->dump_done) {
 
301
                is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
 
302
                if (current->right) current->right->dump_done = 0;
 
303
                current = current->parent;
 
304
            }
 
305
            continue;
 
306
 
 
307
        default:
 
308
            if (!current->dump_done) {
 
309
                debug_printf(ctx, "%s%s\n", is, current->token.s);
 
310
                is = apr_pstrcat(ctx->dpool, is, "    ", NULL);
 
311
                current->dump_done = 1;
 
312
            }
 
313
 
 
314
            DUMP__CHILD(ctx, is, current, left)
 
315
            DUMP__CHILD(ctx, is, current, right)
 
316
 
 
317
            if ((!current->left || current->left->dump_done) &&
 
318
                (!current->right || current->right->dump_done)) {
 
319
 
 
320
                is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
 
321
                if (current->left) current->left->dump_done = 0;
 
322
                if (current->right) current->right->dump_done = 0;
 
323
                current = current->parent;
 
324
            }
 
325
            continue;
 
326
        }
 
327
    }
 
328
 
 
329
    /* it is possible to call this function within the parser loop, to see
 
330
     * how the tree is built. That way, we must cleanup after us to dump
 
331
     * always the whole tree
 
332
     */
 
333
    root->dump_done = 0;
 
334
    if (root->left) root->left->dump_done = 0;
 
335
    if (root->right) root->right->dump_done = 0;
 
336
 
 
337
    debug_printf(ctx, "     --- End Parse Tree ---\n\n");
 
338
 
 
339
    return;
 
340
}
 
341
 
 
342
#define DEBUG_INIT(ctx, filter, brigade) do { \
 
343
    (ctx)->intern->debug.f = filter;          \
 
344
    (ctx)->intern->debug.bb = brigade;        \
 
345
} while(0)
 
346
 
 
347
#define DEBUG_PRINTF(arg) debug_printf arg
 
348
 
 
349
#define DEBUG_DUMP_TOKEN(ctx, token) do {                                     \
 
350
    token_t *d__t = (token);                                                  \
 
351
                                                                              \
 
352
    if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) {               \
 
353
        DEBUG_PRINTF(((ctx), "     Found: %s (%s)\n", d__t->s, d__t->value)); \
 
354
    }                                                                         \
 
355
    else {                                                                    \
 
356
        DEBUG_PRINTF((ctx, "     Found: %s\n", d__t->s));                     \
 
357
    }                                                                         \
 
358
} while(0)
 
359
 
 
360
#define DEBUG_DUMP_EVAL(ctx, node) do {                                       \
 
361
    char c = '"';                                                             \
 
362
    switch ((node)->token.type) {                                             \
 
363
    case TOKEN_STRING:                                                        \
 
364
        debug_printf((ctx), "     Evaluate: %s (%s) -> %c\n", (node)->token.s,\
 
365
                     (node)->token.value, ((node)->value) ? '1':'0');         \
 
366
        break;                                                                \
 
367
    case TOKEN_AND:                                                           \
 
368
    case TOKEN_OR:                                                            \
 
369
        debug_printf((ctx), "     Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
 
370
                     (node)->token.s,                                         \
 
371
                     (((node)->left->done) ? ((node)->left->value ?"1":"0")   \
 
372
                                          : "short circuited"),               \
 
373
                     (((node)->right->done) ? ((node)->right->value?"1":"0")  \
 
374
                                          : "short circuited"),               \
 
375
                     (node)->value ? '1' : '0');                              \
 
376
        break;                                                                \
 
377
    case TOKEN_EQ:                                                            \
 
378
    case TOKEN_NE:                                                            \
 
379
    case TOKEN_GT:                                                            \
 
380
    case TOKEN_GE:                                                            \
 
381
    case TOKEN_LT:                                                            \
 
382
    case TOKEN_LE:                                                            \
 
383
        if ((node)->right->token.type == TOKEN_RE) c = '/';                   \
 
384
        debug_printf((ctx), "     Compare:  %s (\"%s\" with %c%s%c) -> %c\n", \
 
385
                     (node)->token.s,                                         \
 
386
                     (node)->left->token.value,                               \
 
387
                     c, (node)->right->token.value, c,                        \
 
388
                     (node)->value ? '1' : '0');                              \
 
389
        break;                                                                \
 
390
    default:                                                                  \
 
391
        debug_printf((ctx), "     Evaluate: %s -> %c\n", (node)->token.s,     \
 
392
                     (node)->value ? '1' : '0');                              \
 
393
        break;                                                                \
 
394
    }                                                                         \
 
395
} while(0)
 
396
 
 
397
#define DEBUG_DUMP_UNMATCHED(ctx, unmatched) do {                        \
 
398
    if (unmatched) {                                                     \
 
399
        DEBUG_PRINTF(((ctx), "     Unmatched %c\n", (char)(unmatched))); \
 
400
    }                                                                    \
 
401
} while(0)
 
402
 
 
403
#define DEBUG_DUMP_COND(ctx, text)                                 \
 
404
    DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text),   \
 
405
                  ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
 
406
 
 
407
#define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
 
408
 
 
409
#else /* DEBUG_INCLUDE */
 
410
 
 
411
#define TYPE_TOKEN(token, ttype) (token)->type = ttype
 
412
 
 
413
#define CREATE_NODE(ctx, name) do {                       \
 
414
    (name) = apr_palloc((ctx)->dpool, sizeof(*(name)));   \
 
415
    (name)->parent = (name)->left = (name)->right = NULL; \
 
416
    (name)->done = 0;                                     \
 
417
} while(0)
 
418
 
 
419
#define DEBUG_INIT(ctx, f, bb)
 
420
#define DEBUG_PRINTF(arg)
 
421
#define DEBUG_DUMP_TOKEN(ctx, token)
 
422
#define DEBUG_DUMP_EVAL(ctx, node)
 
423
#define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
 
424
#define DEBUG_DUMP_COND(ctx, text)
 
425
#define DEBUG_DUMP_TREE(ctx, root)
 
426
 
 
427
#endif /* !DEBUG_INCLUDE */
 
428
 
 
429
 
 
430
/*
 
431
 * +-------------------------------------------------------+
 
432
 * |                                                       |
 
433
 * |                 Static Module Data
 
434
 * |                                                       |
 
435
 * +-------------------------------------------------------+
 
436
 */
 
437
 
 
438
/* global module structure */
 
439
module AP_MODULE_DECLARE_DATA include_module;
 
440
 
 
441
/* function handlers for include directives */
 
442
static apr_hash_t *include_handlers;
 
443
 
 
444
/* forward declaration of handler registry */
 
445
static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
 
446
 
 
447
/* Sentinel value to store in subprocess_env for items that
 
448
 * shouldn't be evaluated until/unless they're actually used
 
449
 */
 
450
static const char lazy_eval_sentinel;
 
451
#define LAZY_VALUE (&lazy_eval_sentinel)
 
452
 
 
453
/* default values */
 
454
#define DEFAULT_START_SEQUENCE "<!--#"
 
455
#define DEFAULT_END_SEQUENCE "-->"
 
456
#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
 
457
#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
 
458
#define DEFAULT_UNDEFINED_ECHO "(none)"
 
459
 
 
460
#ifdef XBITHACK
 
461
#define DEFAULT_XBITHACK XBITHACK_FULL
 
462
#else
 
463
#define DEFAULT_XBITHACK XBITHACK_OFF
 
464
#endif
 
465
 
 
466
 
 
467
/*
 
468
 * +-------------------------------------------------------+
 
469
 * |                                                       |
 
470
 * |            Environment/Expansion Functions
 
471
 * |                                                       |
 
472
 * +-------------------------------------------------------+
 
473
 */
 
474
 
 
475
/*
 
476
 * decodes a string containing html entities or numeric character references.
 
477
 * 's' is overwritten with the decoded string.
 
478
 * If 's' is syntatically incorrect, then the followed fixups will be made:
 
479
 *   unknown entities will be left undecoded;
 
480
 *   references to unused numeric characters will be deleted.
 
481
 *   In particular, &#00; will not be decoded, but will be deleted.
 
482
 */
 
483
 
 
484
/* maximum length of any ISO-LATIN-1 HTML entity name. */
 
485
#define MAXENTLEN (6)
 
486
 
 
487
/* The following is a shrinking transformation, therefore safe. */
 
488
 
 
489
static void decodehtml(char *s)
 
490
{
 
491
    int val, i, j;
 
492
    char *p;
 
493
    const char *ents;
 
494
    static const char * const entlist[MAXENTLEN + 1] =
 
495
    {
 
496
        NULL,                     /* 0 */
 
497
        NULL,                     /* 1 */
 
498
        "lt\074gt\076",           /* 2 */
 
499
        "amp\046ETH\320eth\360",  /* 3 */
 
500
        "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml"
 
501
        "\353iuml\357ouml\366uuml\374yuml\377",                         /* 4 */
 
502
 
 
503
        "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc"
 
504
        "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352"
 
505
        "icirc\356ocirc\364ucirc\373thorn\376",                         /* 5 */
 
506
 
 
507
        "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311"
 
508
        "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde"
 
509
        "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340"
 
510
        "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave"
 
511
        "\354iacute\355ntilde\361ograve\362oacute\363otilde\365"
 
512
        "oslash\370ugrave\371uacute\372yacute\375"                      /* 6 */
 
513
    };
 
514
 
 
515
    /* Do a fast scan through the string until we find anything
 
516
     * that needs more complicated handling
 
517
     */
 
518
    for (; *s != '&'; s++) {
 
519
        if (*s == '\0') {
 
520
            return;
 
521
        }
 
522
    }
 
523
 
 
524
    for (p = s; *s != '\0'; s++, p++) {
 
525
        if (*s != '&') {
 
526
            *p = *s;
 
527
            continue;
 
528
        }
 
529
        /* find end of entity */
 
530
        for (i = 1; s[i] != ';' && s[i] != '\0'; i++) {
 
531
            continue;
 
532
        }
 
533
 
 
534
        if (s[i] == '\0') {     /* treat as normal data */
 
535
            *p = *s;
 
536
            continue;
 
537
        }
 
538
 
 
539
        /* is it numeric ? */
 
540
        if (s[1] == '#') {
 
541
            for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
 
542
                val = val * 10 + s[j] - '0';
 
543
            }
 
544
            s += i;
 
545
            if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
 
546
                (val >= 127 && val <= 160) || val >= 256) {
 
547
                p--;            /* no data to output */
 
548
            }
 
549
            else {
 
550
                *p = RAW_ASCII_CHAR(val);
 
551
            }
 
552
        }
 
553
        else {
 
554
            j = i - 1;
 
555
            if (j > MAXENTLEN || entlist[j] == NULL) {
 
556
                /* wrong length */
 
557
                *p = '&';
 
558
                continue;       /* skip it */
 
559
            }
 
560
            for (ents = entlist[j]; *ents != '\0'; ents += i) {
 
561
                if (strncmp(s + 1, ents, j) == 0) {
 
562
                    break;
 
563
                }
 
564
            }
 
565
 
 
566
            if (*ents == '\0') {
 
567
                *p = '&';       /* unknown */
 
568
            }
 
569
            else {
 
570
                *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]);
 
571
                s += i;
 
572
            }
 
573
        }
 
574
    }
 
575
 
 
576
    *p = '\0';
 
577
}
 
578
 
 
579
static void add_include_vars(request_rec *r, const char *timefmt)
 
580
{
 
581
    apr_table_t *e = r->subprocess_env;
 
582
    char *t;
 
583
 
 
584
    apr_table_setn(e, "DATE_LOCAL", LAZY_VALUE);
 
585
    apr_table_setn(e, "DATE_GMT", LAZY_VALUE);
 
586
    apr_table_setn(e, "LAST_MODIFIED", LAZY_VALUE);
 
587
    apr_table_setn(e, "DOCUMENT_URI", r->uri);
 
588
    if (r->path_info && *r->path_info) {
 
589
        apr_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info);
 
590
    }
 
591
    apr_table_setn(e, "USER_NAME", LAZY_VALUE);
 
592
    if (r->filename && (t = strrchr(r->filename, '/'))) {
 
593
        apr_table_setn(e, "DOCUMENT_NAME", ++t);
 
594
    }
 
595
    else {
 
596
        apr_table_setn(e, "DOCUMENT_NAME", r->uri);
 
597
    }
 
598
    if (r->args) {
 
599
        char *arg_copy = apr_pstrdup(r->pool, r->args);
 
600
 
 
601
        ap_unescape_url(arg_copy);
 
602
        apr_table_setn(e, "QUERY_STRING_UNESCAPED",
 
603
                  ap_escape_shell_cmd(r->pool, arg_copy));
 
604
    }
 
605
}
 
606
 
 
607
static const char *add_include_vars_lazy(request_rec *r, const char *var)
 
608
{
 
609
    char *val;
 
610
    if (!strcasecmp(var, "DATE_LOCAL")) {
 
611
        include_dir_config *conf =
 
612
            (include_dir_config *)ap_get_module_config(r->per_dir_config,
 
613
                                                       &include_module);
 
614
        val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 0);
 
615
    }
 
616
    else if (!strcasecmp(var, "DATE_GMT")) {
 
617
        include_dir_config *conf =
 
618
            (include_dir_config *)ap_get_module_config(r->per_dir_config,
 
619
                                                       &include_module);
 
620
        val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 1);
 
621
    }
 
622
    else if (!strcasecmp(var, "LAST_MODIFIED")) {
 
623
        include_dir_config *conf =
 
624
            (include_dir_config *)ap_get_module_config(r->per_dir_config,
 
625
                                                       &include_module);
 
626
        val = ap_ht_time(r->pool, r->finfo.mtime, conf->default_time_fmt, 0);
 
627
    }
 
628
    else if (!strcasecmp(var, "USER_NAME")) {
 
629
        if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
 
630
            val = "<unknown>";
 
631
        }
 
632
    }
 
633
    else {
 
634
        val = NULL;
 
635
    }
 
636
 
 
637
    if (val) {
 
638
        apr_table_setn(r->subprocess_env, var, val);
 
639
    }
 
640
    return val;
 
641
}
 
642
 
 
643
static const char *get_include_var(const char *var, include_ctx_t *ctx)
 
644
{
 
645
    const char *val;
 
646
    request_rec *r = ctx->intern->r;
 
647
 
 
648
    if (apr_isdigit(*var) && !var[1]) {
 
649
        apr_size_t idx = *var - '0';
 
650
        backref_t *re = ctx->intern->re;
 
651
 
 
652
        /* Handle $0 .. $9 from the last regex evaluated.
 
653
         * The choice of returning NULL strings on not-found,
 
654
         * v.s. empty strings on an empty match is deliberate.
 
655
         */
 
656
        if (!re) {
 
657
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
658
                "regex capture $%" APR_SIZE_T_FMT " refers to no regex in %s",
 
659
                idx, r->filename);
 
660
            return NULL;
 
661
        }
 
662
        else {
 
663
            if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
 
664
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
665
                              "regex capture $%" APR_SIZE_T_FMT
 
666
                              " is out of range (last regex was: '%s') in %s",
 
667
                              idx, re->rexp, r->filename);
 
668
                return NULL;
 
669
            }
 
670
 
 
671
            if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
 
672
                return NULL;
 
673
            }
 
674
 
 
675
            val = apr_pstrmemdup(ctx->dpool, re->source + re->match[idx].rm_so,
 
676
                                 re->match[idx].rm_eo - re->match[idx].rm_so);
 
677
        }
 
678
    }
 
679
    else {
 
680
        val = apr_table_get(r->subprocess_env, var);
 
681
 
 
682
        if (val == LAZY_VALUE) {
 
683
            val = add_include_vars_lazy(r, var);
 
684
        }
 
685
    }
 
686
 
 
687
    return val;
 
688
}
 
689
 
 
690
/*
 
691
 * Do variable substitution on strings
 
692
 *
 
693
 * (Note: If out==NULL, this function allocs a buffer for the resulting
 
694
 * string from ctx->pool. The return value is always the parsed string)
 
695
 */
 
696
static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
 
697
                                 apr_size_t length, int leave_name)
 
698
{
 
699
    request_rec *r = ctx->intern->r;
 
700
    result_item_t *result = NULL, *current = NULL;
 
701
    apr_size_t outlen = 0, inlen, span;
 
702
    char *ret = NULL, *eout = NULL;
 
703
    const char *p;
 
704
 
 
705
    if (out) {
 
706
        /* sanity check, out && !length is not supported */
 
707
        ap_assert(out && length);
 
708
 
 
709
        ret = out;
 
710
        eout = out + length - 1;
 
711
    }
 
712
 
 
713
    span = strcspn(in, "\\$");
 
714
    inlen = strlen(in);
 
715
 
 
716
    /* fast exit */
 
717
    if (inlen == span) {
 
718
        if (out) {
 
719
            apr_cpystrn(out, in, length);
 
720
        }
 
721
        else {
 
722
            ret = apr_pstrmemdup(ctx->pool, in, (length && length <= inlen)
 
723
                                                ? length - 1 : inlen);
 
724
        }
 
725
 
 
726
        return ret;
 
727
    }
 
728
 
 
729
    /* well, actually something to do */
 
730
    p = in + span;
 
731
 
 
732
    if (out) {
 
733
        if (span) {
 
734
            memcpy(out, in, (out+span <= eout) ? span : (eout-out));
 
735
            out += span;
 
736
        }
 
737
    }
 
738
    else {
 
739
        current = result = apr_palloc(ctx->dpool, sizeof(*result));
 
740
        current->next = NULL;
 
741
        current->string = in;
 
742
        current->len = span;
 
743
        outlen = span;
 
744
    }
 
745
 
 
746
    /* loop for specials */
 
747
    do {
 
748
        if ((out && out >= eout) || (length && outlen >= length)) {
 
749
            break;
 
750
        }
 
751
 
 
752
        /* prepare next entry */
 
753
        if (!out && current->len) {
 
754
            current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
 
755
            current = current->next;
 
756
            current->next = NULL;
 
757
            current->len = 0;
 
758
        }
 
759
 
 
760
        /*
 
761
         * escaped character
 
762
         */
 
763
        if (*p == '\\') {
 
764
            if (out) {
 
765
                *out++ = (p[1] == '$') ? *++p : *p;
 
766
                ++p;
 
767
            }
 
768
            else {
 
769
                current->len = 1;
 
770
                current->string = (p[1] == '$') ? ++p : p;
 
771
                ++p;
 
772
                ++outlen;
 
773
            }
 
774
        }
 
775
 
 
776
        /*
 
777
         * variable expansion
 
778
         */
 
779
        else {       /* *p == '$' */
 
780
            const char *newp = NULL, *ep, *key = NULL;
 
781
 
 
782
            if (*++p == '{') {
 
783
                ep = ap_strchr_c(++p, '}');
 
784
                if (!ep) {
 
785
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing '}' on "
 
786
                                  "variable \"%s\" in %s", p, r->filename);
 
787
                    break;
 
788
                }
 
789
 
 
790
                if (p < ep) {
 
791
                    key = apr_pstrmemdup(ctx->dpool, p, ep - p);
 
792
                    newp = ep + 1;
 
793
                }
 
794
                p -= 2;
 
795
            }
 
796
            else {
 
797
                ep = p;
 
798
                while (*ep == '_' || apr_isalnum(*ep)) {
 
799
                    ++ep;
 
800
                }
 
801
 
 
802
                if (p < ep) {
 
803
                    key = apr_pstrmemdup(ctx->dpool, p, ep - p);
 
804
                    newp = ep;
 
805
                }
 
806
                --p;
 
807
            }
 
808
 
 
809
            /* empty name results in a copy of '$' in the output string */
 
810
            if (!key) {
 
811
                if (out) {
 
812
                    *out++ = *p++;
 
813
                }
 
814
                else {
 
815
                    current->len = 1;
 
816
                    current->string = p++;
 
817
                    ++outlen;
 
818
                }
 
819
            }
 
820
            else {
 
821
                const char *val = get_include_var(key, ctx);
 
822
                apr_size_t len = 0;
 
823
 
 
824
                if (val) {
 
825
                    len = strlen(val);
 
826
                }
 
827
                else if (leave_name) {
 
828
                    val = p;
 
829
                    len = ep - p;
 
830
                }
 
831
 
 
832
                if (val && len) {
 
833
                    if (out) {
 
834
                        memcpy(out, val, (out+len <= eout) ? len : (eout-out));
 
835
                        out += len;
 
836
                    }
 
837
                    else {
 
838
                        current->len = len;
 
839
                        current->string = val;
 
840
                        outlen += len;
 
841
                    }
 
842
                }
 
843
 
 
844
                p = newp;
 
845
            }
 
846
        }
 
847
 
 
848
        if ((out && out >= eout) || (length && outlen >= length)) {
 
849
            break;
 
850
        }
 
851
 
 
852
        /* check the remainder */
 
853
        if (*p && (span = strcspn(p, "\\$")) > 0) {
 
854
            if (!out && current->len) {
 
855
                current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
 
856
                current = current->next;
 
857
                current->next = NULL;
 
858
            }
 
859
 
 
860
            if (out) {
 
861
                memcpy(out, p, (out+span <= eout) ? span : (eout-out));
 
862
                out += span;
 
863
            }
 
864
            else {
 
865
                current->len = span;
 
866
                current->string = p;
 
867
                outlen += span;
 
868
            }
 
869
 
 
870
            p += span;
 
871
        }
 
872
    } while (p < in+inlen);
 
873
 
 
874
    /* assemble result */
 
875
    if (out) {
 
876
        if (out > eout) {
 
877
            *eout = '\0';
 
878
        }
 
879
        else {
 
880
            *out = '\0';
 
881
        }
 
882
    }
 
883
    else {
 
884
        const char *ep;
 
885
 
 
886
        if (length && outlen > length) {
 
887
            outlen = length - 1;
 
888
        }
 
889
 
 
890
        ret = out = apr_palloc(ctx->pool, outlen + 1);
 
891
        ep = ret + outlen;
 
892
 
 
893
        do {
 
894
            if (result->len) {
 
895
                memcpy(out, result->string, (out+result->len <= ep)
 
896
                                            ? result->len : (ep-out));
 
897
                out += result->len;
 
898
            }
 
899
            result = result->next;
 
900
        } while (result && out < ep);
 
901
 
 
902
        ret[outlen] = '\0';
 
903
    }
 
904
 
 
905
    return ret;
 
906
}
 
907
 
 
908
 
 
909
/*
 
910
 * +-------------------------------------------------------+
 
911
 * |                                                       |
 
912
 * |              Conditional Expression Parser
 
913
 * |                                                       |
 
914
 * +-------------------------------------------------------+
 
915
 */
 
916
 
 
917
static APR_INLINE int re_check(include_ctx_t *ctx, const char *string,
 
918
                               const char *rexp)
 
919
{
 
920
    ap_regex_t *compiled;
 
921
    backref_t *re = ctx->intern->re;
 
922
    int rc;
 
923
 
 
924
    compiled = ap_pregcomp(ctx->dpool, rexp, AP_REG_EXTENDED);
 
925
    if (!compiled) {
 
926
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->intern->r, "unable to "
 
927
                      "compile pattern \"%s\"", rexp);
 
928
        return -1;
 
929
    }
 
930
 
 
931
    if (!re) {
 
932
        re = ctx->intern->re = apr_palloc(ctx->pool, sizeof(*re));
 
933
    }
 
934
 
 
935
    re->source = apr_pstrdup(ctx->pool, string);
 
936
    re->rexp = apr_pstrdup(ctx->pool, rexp);
 
937
    re->nsub = compiled->re_nsub;
 
938
    rc = !ap_regexec(compiled, string, AP_MAX_REG_MATCH, re->match, 0);
 
939
 
 
940
    ap_pregfree(ctx->dpool, compiled);
 
941
    return rc;
 
942
}
 
943
 
 
944
static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token)
 
945
{
 
946
    const char *p;
 
947
    apr_size_t shift;
 
948
    int unmatched;
 
949
 
 
950
    token->value = NULL;
 
951
 
 
952
    if (!*parse) {
 
953
        return 0;
 
954
    }
 
955
 
 
956
    /* Skip leading white space */
 
957
    while (apr_isspace(**parse)) {
 
958
        ++*parse;
 
959
    }
 
960
 
 
961
    if (!**parse) {
 
962
        *parse = NULL;
 
963
        return 0;
 
964
    }
 
965
 
 
966
    TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
 
967
    p = *parse;
 
968
    unmatched = 0;
 
969
 
 
970
    switch (*(*parse)++) {
 
971
    case '(':
 
972
        TYPE_TOKEN(token, TOKEN_LBRACE);
 
973
        return 0;
 
974
    case ')':
 
975
        TYPE_TOKEN(token, TOKEN_RBRACE);
 
976
        return 0;
 
977
    case '=':
 
978
        if (**parse == '=') ++*parse;
 
979
        TYPE_TOKEN(token, TOKEN_EQ);
 
980
        return 0;
 
981
    case '!':
 
982
        if (**parse == '=') {
 
983
            TYPE_TOKEN(token, TOKEN_NE);
 
984
            ++*parse;
 
985
            return 0;
 
986
        }
 
987
        TYPE_TOKEN(token, TOKEN_NOT);
 
988
        return 0;
 
989
    case '\'':
 
990
        unmatched = '\'';
 
991
        break;
 
992
    case '/':
 
993
        TYPE_TOKEN(token, TOKEN_RE);
 
994
        unmatched = '/';
 
995
        break;
 
996
    case '|':
 
997
        if (**parse == '|') {
 
998
            TYPE_TOKEN(token, TOKEN_OR);
 
999
            ++*parse;
 
1000
            return 0;
 
1001
        }
 
1002
        break;
 
1003
    case '&':
 
1004
        if (**parse == '&') {
 
1005
            TYPE_TOKEN(token, TOKEN_AND);
 
1006
            ++*parse;
 
1007
            return 0;
 
1008
        }
 
1009
        break;
 
1010
    case '>':
 
1011
        if (**parse == '=') {
 
1012
            TYPE_TOKEN(token, TOKEN_GE);
 
1013
            ++*parse;
 
1014
            return 0;
 
1015
        }
 
1016
        TYPE_TOKEN(token, TOKEN_GT);
 
1017
        return 0;
 
1018
    case '<':
 
1019
        if (**parse == '=') {
 
1020
            TYPE_TOKEN(token, TOKEN_LE);
 
1021
            ++*parse;
 
1022
            return 0;
 
1023
        }
 
1024
        TYPE_TOKEN(token, TOKEN_LT);
 
1025
        return 0;
 
1026
    }
 
1027
 
 
1028
    /* It's a string or regex token
 
1029
     * Now search for the next token, which finishes this string
 
1030
     */
 
1031
    shift = 0;
 
1032
    p = *parse = token->value = unmatched ? *parse : p;
 
1033
 
 
1034
    for (; **parse; p = ++*parse) {
 
1035
        if (**parse == '\\') {
 
1036
            if (!*(++*parse)) {
 
1037
                p = *parse;
 
1038
                break;
 
1039
            }
 
1040
 
 
1041
            ++shift;
 
1042
        }
 
1043
        else {
 
1044
            if (unmatched) {
 
1045
                if (**parse == unmatched) {
 
1046
                    unmatched = 0;
 
1047
                    ++*parse;
 
1048
                    break;
 
1049
                }
 
1050
            } else if (apr_isspace(**parse)) {
 
1051
                break;
 
1052
            }
 
1053
            else {
 
1054
                int found = 0;
 
1055
 
 
1056
                switch (**parse) {
 
1057
                case '(':
 
1058
                case ')':
 
1059
                case '=':
 
1060
                case '!':
 
1061
                case '<':
 
1062
                case '>':
 
1063
                    ++found;
 
1064
                    break;
 
1065
 
 
1066
                case '|':
 
1067
                case '&':
 
1068
                    if ((*parse)[1] == **parse) {
 
1069
                        ++found;
 
1070
                    }
 
1071
                    break;
 
1072
                }
 
1073
 
 
1074
                if (found) {
 
1075
                    break;
 
1076
                }
 
1077
            }
 
1078
        }
 
1079
    }
 
1080
 
 
1081
    if (unmatched) {
 
1082
        token->value = apr_pstrdup(pool, "");
 
1083
    }
 
1084
    else {
 
1085
        apr_size_t len = p - token->value - shift;
 
1086
        char *c = apr_palloc(pool, len + 1);
 
1087
 
 
1088
        p = token->value;
 
1089
        token->value = c;
 
1090
 
 
1091
        while (shift--) {
 
1092
            const char *e = ap_strchr_c(p, '\\');
 
1093
 
 
1094
            memcpy(c, p, e-p);
 
1095
            c   += e-p;
 
1096
            *c++ = *++e;
 
1097
            len -= e-p;
 
1098
            p    = e+1;
 
1099
        }
 
1100
 
 
1101
        if (len) {
 
1102
            memcpy(c, p, len);
 
1103
        }
 
1104
        c[len] = '\0';
 
1105
    }
 
1106
 
 
1107
    return unmatched;
 
1108
}
 
1109
 
 
1110
static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
 
1111
{
 
1112
    parse_node_t *new, *root = NULL, *current = NULL;
 
1113
    request_rec *r = ctx->intern->r;
 
1114
    const char *error = "Invalid expression \"%s\" in file %s";
 
1115
    const char *parse = expr;
 
1116
    int was_unmatched = 0;
 
1117
    unsigned regex = 0;
 
1118
 
 
1119
    *was_error = 0;
 
1120
 
 
1121
    if (!parse) {
 
1122
        return 0;
 
1123
    }
 
1124
 
 
1125
    /* Create Parse Tree */
 
1126
    while (1) {
 
1127
        /* uncomment this to see how the tree a built:
 
1128
         *
 
1129
         * DEBUG_DUMP_TREE(ctx, root);
 
1130
         */
 
1131
        CREATE_NODE(ctx, new);
 
1132
 
 
1133
        was_unmatched = get_ptoken(ctx->dpool, &parse, &new->token);
 
1134
        if (!parse) {
 
1135
            break;
 
1136
        }
 
1137
 
 
1138
        DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
 
1139
        DEBUG_DUMP_TOKEN(ctx, &new->token);
 
1140
 
 
1141
        if (!current) {
 
1142
            switch (new->token.type) {
 
1143
            case TOKEN_STRING:
 
1144
            case TOKEN_NOT:
 
1145
            case TOKEN_LBRACE:
 
1146
                root = current = new;
 
1147
                continue;
 
1148
 
 
1149
            default:
 
1150
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,
 
1151
                              r->filename);
 
1152
                *was_error = 1;
 
1153
                return 0;
 
1154
            }
 
1155
        }
 
1156
 
 
1157
        switch (new->token.type) {
 
1158
        case TOKEN_STRING:
 
1159
            switch (current->token.type) {
 
1160
            case TOKEN_STRING:
 
1161
                current->token.value =
 
1162
                    apr_pstrcat(ctx->dpool, current->token.value,
 
1163
                                *current->token.value ? " " : "",
 
1164
                                new->token.value, NULL);
 
1165
                continue;
 
1166
 
 
1167
            case TOKEN_RE:
 
1168
            case TOKEN_RBRACE:
 
1169
            case TOKEN_GROUP:
 
1170
                break;
 
1171
 
 
1172
            default:
 
1173
                new->parent = current;
 
1174
                current = current->right = new;
 
1175
                continue;
 
1176
            }
 
1177
            break;
 
1178
 
 
1179
        case TOKEN_RE:
 
1180
            switch (current->token.type) {
 
1181
            case TOKEN_EQ:
 
1182
            case TOKEN_NE:
 
1183
                new->parent = current;
 
1184
                current = current->right = new;
 
1185
                ++regex;
 
1186
                continue;
 
1187
 
 
1188
            default:
 
1189
                break;
 
1190
            }
 
1191
            break;
 
1192
 
 
1193
        case TOKEN_AND:
 
1194
        case TOKEN_OR:
 
1195
            switch (current->token.type) {
 
1196
            case TOKEN_STRING:
 
1197
            case TOKEN_RE:
 
1198
            case TOKEN_GROUP:
 
1199
                current = current->parent;
 
1200
 
 
1201
                while (current) {
 
1202
                    switch (current->token.type) {
 
1203
                    case TOKEN_AND:
 
1204
                    case TOKEN_OR:
 
1205
                    case TOKEN_LBRACE:
 
1206
                        break;
 
1207
 
 
1208
                    default:
 
1209
                        current = current->parent;
 
1210
                        continue;
 
1211
                    }
 
1212
                    break;
 
1213
                }
 
1214
 
 
1215
                if (!current) {
 
1216
                    new->left = root;
 
1217
                    root->parent = new;
 
1218
                    current = root = new;
 
1219
                    continue;
 
1220
                }
 
1221
 
 
1222
                new->left = current->right;
 
1223
                new->left->parent = new;
 
1224
                new->parent = current;
 
1225
                current = current->right = new;
 
1226
                continue;
 
1227
 
 
1228
            default:
 
1229
                break;
 
1230
            }
 
1231
            break;
 
1232
 
 
1233
        case TOKEN_EQ:
 
1234
        case TOKEN_NE:
 
1235
        case TOKEN_GE:
 
1236
        case TOKEN_GT:
 
1237
        case TOKEN_LE:
 
1238
        case TOKEN_LT:
 
1239
            if (current->token.type == TOKEN_STRING) {
 
1240
                current = current->parent;
 
1241
 
 
1242
                if (!current) {
 
1243
                    new->left = root;
 
1244
                    root->parent = new;
 
1245
                    current = root = new;
 
1246
                    continue;
 
1247
                }
 
1248
 
 
1249
                switch (current->token.type) {
 
1250
                case TOKEN_LBRACE:
 
1251
                case TOKEN_AND:
 
1252
                case TOKEN_OR:
 
1253
                    new->left = current->right;
 
1254
                    new->left->parent = new;
 
1255
                    new->parent = current;
 
1256
                    current = current->right = new;
 
1257
                    continue;
 
1258
 
 
1259
                default:
 
1260
                    break;
 
1261
                }
 
1262
            }
 
1263
            break;
 
1264
 
 
1265
        case TOKEN_RBRACE:
 
1266
            while (current && current->token.type != TOKEN_LBRACE) {
 
1267
                current = current->parent;
 
1268
            }
 
1269
 
 
1270
            if (current) {
 
1271
                TYPE_TOKEN(&current->token, TOKEN_GROUP);
 
1272
                continue;
 
1273
            }
 
1274
 
 
1275
            error = "Unmatched ')' in \"%s\" in file %s";
 
1276
            break;
 
1277
 
 
1278
        case TOKEN_NOT:
 
1279
        case TOKEN_LBRACE:
 
1280
            switch (current->token.type) {
 
1281
            case TOKEN_STRING:
 
1282
            case TOKEN_RE:
 
1283
            case TOKEN_RBRACE:
 
1284
            case TOKEN_GROUP:
 
1285
                break;
 
1286
 
 
1287
            default:
 
1288
                current->right = new;
 
1289
                new->parent = current;
 
1290
                current = new;
 
1291
                continue;
 
1292
            }
 
1293
            break;
 
1294
 
 
1295
        default:
 
1296
            break;
 
1297
        }
 
1298
 
 
1299
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr, r->filename);
 
1300
        *was_error = 1;
 
1301
        return 0;
 
1302
    }
 
1303
 
 
1304
    DEBUG_DUMP_TREE(ctx, root);
 
1305
 
 
1306
    /* Evaluate Parse Tree */
 
1307
    current = root;
 
1308
    error = NULL;
 
1309
    while (current) {
 
1310
        switch (current->token.type) {
 
1311
        case TOKEN_STRING:
 
1312
            current->token.value =
 
1313
                ap_ssi_parse_string(ctx, current->token.value, NULL, 0,
 
1314
                                    SSI_EXPAND_DROP_NAME);
 
1315
            current->value = !!*current->token.value;
 
1316
            break;
 
1317
 
 
1318
        case TOKEN_AND:
 
1319
        case TOKEN_OR:
 
1320
            if (!current->left || !current->right) {
 
1321
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
1322
                              "Invalid expression \"%s\" in file %s",
 
1323
                              expr, r->filename);
 
1324
                *was_error = 1;
 
1325
                return 0;
 
1326
            }
 
1327
 
 
1328
            if (!current->left->done) {
 
1329
                switch (current->left->token.type) {
 
1330
                case TOKEN_STRING:
 
1331
                    current->left->token.value =
 
1332
                        ap_ssi_parse_string(ctx, current->left->token.value,
 
1333
                                            NULL, 0, SSI_EXPAND_DROP_NAME);
 
1334
                    current->left->value = !!*current->left->token.value;
 
1335
                    DEBUG_DUMP_EVAL(ctx, current->left);
 
1336
                    current->left->done = 1;
 
1337
                    break;
 
1338
 
 
1339
                default:
 
1340
                    current = current->left;
 
1341
                    continue;
 
1342
                }
 
1343
            }
 
1344
 
 
1345
            /* short circuit evaluation */
 
1346
            if (!current->right->done && !regex &&
 
1347
                ((current->token.type == TOKEN_AND && !current->left->value) ||
 
1348
                (current->token.type == TOKEN_OR && current->left->value))) {
 
1349
                current->value = current->left->value;
 
1350
            }
 
1351
            else {
 
1352
                if (!current->right->done) {
 
1353
                    switch (current->right->token.type) {
 
1354
                    case TOKEN_STRING:
 
1355
                        current->right->token.value =
 
1356
                            ap_ssi_parse_string(ctx,current->right->token.value,
 
1357
                                                NULL, 0, SSI_EXPAND_DROP_NAME);
 
1358
                        current->right->value = !!*current->right->token.value;
 
1359
                        DEBUG_DUMP_EVAL(ctx, current->right);
 
1360
                        current->right->done = 1;
 
1361
                        break;
 
1362
 
 
1363
                    default:
 
1364
                        current = current->right;
 
1365
                        continue;
 
1366
                    }
 
1367
                }
 
1368
 
 
1369
                if (current->token.type == TOKEN_AND) {
 
1370
                    current->value = current->left->value &&
 
1371
                                     current->right->value;
 
1372
                }
 
1373
                else {
 
1374
                    current->value = current->left->value ||
 
1375
                                     current->right->value;
 
1376
                }
 
1377
            }
 
1378
            break;
 
1379
 
 
1380
        case TOKEN_EQ:
 
1381
        case TOKEN_NE:
 
1382
            if (!current->left || !current->right ||
 
1383
                current->left->token.type != TOKEN_STRING ||
 
1384
                (current->right->token.type != TOKEN_STRING &&
 
1385
                 current->right->token.type != TOKEN_RE)) {
 
1386
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
1387
                            "Invalid expression \"%s\" in file %s",
 
1388
                            expr, r->filename);
 
1389
                *was_error = 1;
 
1390
                return 0;
 
1391
            }
 
1392
            current->left->token.value =
 
1393
                ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
 
1394
                                    SSI_EXPAND_DROP_NAME);
 
1395
            current->right->token.value =
 
1396
                ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
 
1397
                                    SSI_EXPAND_DROP_NAME);
 
1398
 
 
1399
            if (current->right->token.type == TOKEN_RE) {
 
1400
                current->value = re_check(ctx, current->left->token.value,
 
1401
                                          current->right->token.value);
 
1402
                --regex;
 
1403
            }
 
1404
            else {
 
1405
                current->value = !strcmp(current->left->token.value,
 
1406
                                         current->right->token.value);
 
1407
            }
 
1408
 
 
1409
            if (current->token.type == TOKEN_NE) {
 
1410
                current->value = !current->value;
 
1411
            }
 
1412
            break;
 
1413
 
 
1414
        case TOKEN_GE:
 
1415
        case TOKEN_GT:
 
1416
        case TOKEN_LE:
 
1417
        case TOKEN_LT:
 
1418
            if (!current->left || !current->right ||
 
1419
                current->left->token.type != TOKEN_STRING ||
 
1420
                current->right->token.type != TOKEN_STRING) {
 
1421
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
1422
                              "Invalid expression \"%s\" in file %s",
 
1423
                              expr, r->filename);
 
1424
                *was_error = 1;
 
1425
                return 0;
 
1426
            }
 
1427
 
 
1428
            current->left->token.value =
 
1429
                ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
 
1430
                                    SSI_EXPAND_DROP_NAME);
 
1431
            current->right->token.value =
 
1432
                ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
 
1433
                                    SSI_EXPAND_DROP_NAME);
 
1434
 
 
1435
            current->value = strcmp(current->left->token.value,
 
1436
                                    current->right->token.value);
 
1437
 
 
1438
            switch (current->token.type) {
 
1439
            case TOKEN_GE: current->value = current->value >= 0; break;
 
1440
            case TOKEN_GT: current->value = current->value >  0; break;
 
1441
            case TOKEN_LE: current->value = current->value <= 0; break;
 
1442
            case TOKEN_LT: current->value = current->value <  0; break;
 
1443
            default: current->value = 0; break; /* should not happen */
 
1444
            }
 
1445
            break;
 
1446
 
 
1447
        case TOKEN_NOT:
 
1448
        case TOKEN_GROUP:
 
1449
            if (current->right) {
 
1450
                if (!current->right->done) {
 
1451
                    current = current->right;
 
1452
                    continue;
 
1453
                }
 
1454
                current->value = current->right->value;
 
1455
            }
 
1456
            else {
 
1457
                current->value = 1;
 
1458
            }
 
1459
 
 
1460
            if (current->token.type == TOKEN_NOT) {
 
1461
                current->value = !current->value;
 
1462
            }
 
1463
            break;
 
1464
 
 
1465
        case TOKEN_RE:
 
1466
            if (!error) {
 
1467
                error = "No operator before regex in expr \"%s\" in file %s";
 
1468
            }
 
1469
        case TOKEN_LBRACE:
 
1470
            if (!error) {
 
1471
                error = "Unmatched '(' in \"%s\" in file %s";
 
1472
            }
 
1473
        default:
 
1474
            if (!error) {
 
1475
                error = "internal parser error in \"%s\" in file %s";
 
1476
            }
 
1477
 
 
1478
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,r->filename);
 
1479
            *was_error = 1;
 
1480
            return 0;
 
1481
        }
 
1482
 
 
1483
        DEBUG_DUMP_EVAL(ctx, current);
 
1484
        current->done = 1;
 
1485
        current = current->parent;
 
1486
    }
 
1487
 
 
1488
    return (root ? root->value : 0);
 
1489
}
 
1490
 
 
1491
 
 
1492
/*
 
1493
 * +-------------------------------------------------------+
 
1494
 * |                                                       |
 
1495
 * |                    Action Handlers
 
1496
 * |                                                       |
 
1497
 * +-------------------------------------------------------+
 
1498
 */
 
1499
 
 
1500
/*
 
1501
 * Extract the next tag name and value.
 
1502
 * If there are no more tags, set the tag name to NULL.
 
1503
 * The tag value is html decoded if dodecode is non-zero.
 
1504
 * The tag value may be NULL if there is no tag value..
 
1505
 */
 
1506
static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
 
1507
                                     char **tag_val, int dodecode)
 
1508
{
 
1509
    if (!ctx->intern->argv) {
 
1510
        *tag = NULL;
 
1511
        *tag_val = NULL;
 
1512
 
 
1513
        return;
 
1514
    }
 
1515
 
 
1516
    *tag_val = ctx->intern->argv->value;
 
1517
    *tag = ctx->intern->argv->name;
 
1518
 
 
1519
    ctx->intern->argv = ctx->intern->argv->next;
 
1520
 
 
1521
    if (dodecode && *tag_val) {
 
1522
        decodehtml(*tag_val);
 
1523
    }
 
1524
 
 
1525
    return;
 
1526
}
 
1527
 
 
1528
static int find_file(request_rec *r, const char *directive, const char *tag,
 
1529
                     char *tag_val, apr_finfo_t *finfo)
 
1530
{
 
1531
    char *to_send = tag_val;
 
1532
    request_rec *rr = NULL;
 
1533
    int ret=0;
 
1534
    char *error_fmt = NULL;
 
1535
    apr_status_t rv = APR_SUCCESS;
 
1536
 
 
1537
    if (!strcmp(tag, "file")) {
 
1538
        char *newpath;
 
1539
 
 
1540
        /* be safe; only files in this directory or below allowed */
 
1541
        rv = apr_filepath_merge(&newpath, NULL, tag_val,
 
1542
                                APR_FILEPATH_SECUREROOTTEST |
 
1543
                                APR_FILEPATH_NOTABSOLUTE, r->pool);
 
1544
 
 
1545
        if (rv != APR_SUCCESS) {
 
1546
            error_fmt = "unable to access file \"%s\" "
 
1547
                        "in parsed file %s";
 
1548
        }
 
1549
        else {
 
1550
            /* note: it is okay to pass NULL for the "next filter" since
 
1551
               we never attempt to "run" this sub request. */
 
1552
            rr = ap_sub_req_lookup_file(newpath, r, NULL);
 
1553
 
 
1554
            if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
 
1555
                to_send = rr->filename;
 
1556
                if ((rv = apr_stat(finfo, to_send,
 
1557
                    APR_FINFO_GPROT | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS
 
1558
                    && rv != APR_INCOMPLETE) {
 
1559
                    error_fmt = "unable to get information about \"%s\" "
 
1560
                        "in parsed file %s";
 
1561
                }
 
1562
            }
 
1563
            else {
 
1564
                error_fmt = "unable to lookup information about \"%s\" "
 
1565
                            "in parsed file %s";
 
1566
            }
 
1567
        }
 
1568
 
 
1569
        if (error_fmt) {
 
1570
            ret = -1;
 
1571
            ap_log_rerror(APLOG_MARK, APLOG_ERR,
 
1572
                          rv, r, error_fmt, to_send, r->filename);
 
1573
        }
 
1574
 
 
1575
        if (rr) ap_destroy_sub_req(rr);
 
1576
 
 
1577
        return ret;
 
1578
    }
 
1579
    else if (!strcmp(tag, "virtual")) {
 
1580
        /* note: it is okay to pass NULL for the "next filter" since
 
1581
           we never attempt to "run" this sub request. */
 
1582
        rr = ap_sub_req_lookup_uri(tag_val, r, NULL);
 
1583
 
 
1584
        if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
 
1585
            memcpy((char *) finfo, (const char *) &rr->finfo,
 
1586
                   sizeof(rr->finfo));
 
1587
            ap_destroy_sub_req(rr);
 
1588
            return 0;
 
1589
        }
 
1590
        else {
 
1591
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to get "
 
1592
                          "information about \"%s\" in parsed file %s",
 
1593
                          tag_val, r->filename);
 
1594
            ap_destroy_sub_req(rr);
 
1595
            return -1;
 
1596
        }
 
1597
    }
 
1598
    else {
 
1599
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
 
1600
                      "to tag %s in %s", tag, directive, r->filename);
 
1601
        return -1;
 
1602
    }
 
1603
}
 
1604
 
 
1605
/*
 
1606
 * <!--#include virtual|file="..." [virtual|file="..."] ... -->
 
1607
 */
 
1608
static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
 
1609
                                   apr_bucket_brigade *bb)
 
1610
{
 
1611
    request_rec *r = f->r;
 
1612
 
 
1613
    if (!ctx->argc) {
 
1614
        ap_log_rerror(APLOG_MARK,
 
1615
                      (ctx->flags & SSI_FLAG_PRINTING)
 
1616
                          ? APLOG_ERR : APLOG_WARNING,
 
1617
                      0, r, "missing argument for include element in %s",
 
1618
                      r->filename);
 
1619
    }
 
1620
 
 
1621
    if (!(ctx->flags & SSI_FLAG_PRINTING)) {
 
1622
        return APR_SUCCESS;
 
1623
    }
 
1624
 
 
1625
    if (!ctx->argc) {
 
1626
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1627
        return APR_SUCCESS;
 
1628
    }
 
1629
 
 
1630
    while (1) {
 
1631
        char *tag     = NULL;
 
1632
        char *tag_val = NULL;
 
1633
        request_rec *rr = NULL;
 
1634
        char *error_fmt = NULL;
 
1635
        char *parsed_string;
 
1636
 
 
1637
        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
 
1638
        if (!tag || !tag_val) {
 
1639
            break;
 
1640
        }
 
1641
 
 
1642
        if (strcmp(tag, "virtual") && strcmp(tag, "file")) {
 
1643
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
 
1644
                          "\"%s\" to tag include in %s", tag, r->filename);
 
1645
            SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1646
            break;
 
1647
        }
 
1648
 
 
1649
        parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
 
1650
                                            SSI_EXPAND_DROP_NAME);
 
1651
        if (tag[0] == 'f') {
 
1652
            char *newpath;
 
1653
            apr_status_t rv;
 
1654
 
 
1655
            /* be safe; only files in this directory or below allowed */
 
1656
            rv = apr_filepath_merge(&newpath, NULL, parsed_string,
 
1657
                                    APR_FILEPATH_SECUREROOTTEST |
 
1658
                                    APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
 
1659
 
 
1660
            if (rv != APR_SUCCESS) {
 
1661
                error_fmt = "unable to include file \"%s\" in parsed file %s";
 
1662
            }
 
1663
            else {
 
1664
                rr = ap_sub_req_lookup_file(newpath, r, f->next);
 
1665
            }
 
1666
        }
 
1667
        else {
 
1668
            rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
 
1669
        }
 
1670
 
 
1671
        if (!error_fmt && rr->status != HTTP_OK) {
 
1672
            error_fmt = "unable to include \"%s\" in parsed file %s";
 
1673
        }
 
1674
 
 
1675
        if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
 
1676
            rr->content_type && strncmp(rr->content_type, "text/", 5)) {
 
1677
 
 
1678
            error_fmt = "unable to include potential exec \"%s\" in parsed "
 
1679
                        "file %s";
 
1680
        }
 
1681
 
 
1682
        /* See the Kludge in includes_filter for why.
 
1683
         * Basically, it puts a bread crumb in here, then looks
 
1684
         * for the crumb later to see if its been here.
 
1685
         */
 
1686
        if (rr) {
 
1687
            ap_set_module_config(rr->request_config, &include_module, r);
 
1688
        }
 
1689
 
 
1690
        if (!error_fmt && ap_run_sub_req(rr)) {
 
1691
            error_fmt = "unable to include \"%s\" in parsed file %s";
 
1692
        }
 
1693
 
 
1694
        if (error_fmt) {
 
1695
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val,
 
1696
                          r->filename);
 
1697
            SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1698
        }
 
1699
 
 
1700
        /* Do *not* destroy the subrequest here; it may have allocated
 
1701
         * variables in this r->subprocess_env in the subrequest's
 
1702
         * r->pool, so that pool must survive as long as this request.
 
1703
         * Yes, this is a memory leak. */
 
1704
 
 
1705
        if (error_fmt) {
 
1706
            break;
 
1707
        }
 
1708
    }
 
1709
 
 
1710
    return APR_SUCCESS;
 
1711
}
 
1712
 
 
1713
/*
 
1714
 * <!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... -->
 
1715
 */
 
1716
static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
 
1717
                                apr_bucket_brigade *bb)
 
1718
{
 
1719
    enum {E_NONE, E_URL, E_ENTITY} encode;
 
1720
    request_rec *r = f->r;
 
1721
 
 
1722
    if (!ctx->argc) {
 
1723
        ap_log_rerror(APLOG_MARK,
 
1724
                      (ctx->flags & SSI_FLAG_PRINTING)
 
1725
                          ? APLOG_ERR : APLOG_WARNING,
 
1726
                      0, r, "missing argument for echo element in %s",
 
1727
                      r->filename);
 
1728
    }
 
1729
 
 
1730
    if (!(ctx->flags & SSI_FLAG_PRINTING)) {
 
1731
        return APR_SUCCESS;
 
1732
    }
 
1733
 
 
1734
    if (!ctx->argc) {
 
1735
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1736
        return APR_SUCCESS;
 
1737
    }
 
1738
 
 
1739
    encode = E_ENTITY;
 
1740
 
 
1741
    while (1) {
 
1742
        char *tag = NULL;
 
1743
        char *tag_val = NULL;
 
1744
 
 
1745
        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
 
1746
        if (!tag || !tag_val) {
 
1747
            break;
 
1748
        }
 
1749
 
 
1750
        if (!strcmp(tag, "var")) {
 
1751
            const char *val;
 
1752
            const char *echo_text = NULL;
 
1753
            apr_size_t e_len;
 
1754
 
 
1755
            val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
 
1756
                                                      0, SSI_EXPAND_DROP_NAME),
 
1757
                                  ctx);
 
1758
 
 
1759
            if (val) {
 
1760
                switch(encode) {
 
1761
                case E_NONE:
 
1762
                    echo_text = val;
 
1763
                    break;
 
1764
                case E_URL:
 
1765
                    echo_text = ap_escape_uri(ctx->dpool, val);
 
1766
                    break;
 
1767
                case E_ENTITY:
 
1768
                    echo_text = ap_escape_html(ctx->dpool, val);
 
1769
                    break;
 
1770
                }
 
1771
 
 
1772
                e_len = strlen(echo_text);
 
1773
            }
 
1774
            else {
 
1775
                echo_text = ctx->intern->undefined_echo;
 
1776
                e_len = ctx->intern->undefined_echo_len;
 
1777
            }
 
1778
 
 
1779
            APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
 
1780
                                    apr_pmemdup(ctx->pool, echo_text, e_len),
 
1781
                                    e_len, ctx->pool, f->c->bucket_alloc));
 
1782
        }
 
1783
        else if (!strcmp(tag, "encoding")) {
 
1784
            if (!strcasecmp(tag_val, "none")) {
 
1785
                encode = E_NONE;
 
1786
            }
 
1787
            else if (!strcasecmp(tag_val, "url")) {
 
1788
                encode = E_URL;
 
1789
            }
 
1790
            else if (!strcasecmp(tag_val, "entity")) {
 
1791
                encode = E_ENTITY;
 
1792
            }
 
1793
            else {
 
1794
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
 
1795
                              "\"%s\" to parameter \"encoding\" of tag echo in "
 
1796
                              "%s", tag_val, r->filename);
 
1797
                SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1798
                break;
 
1799
            }
 
1800
        }
 
1801
        else {
 
1802
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
 
1803
                          "\"%s\" in tag echo of %s", tag, r->filename);
 
1804
            SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1805
            break;
 
1806
        }
 
1807
    }
 
1808
 
 
1809
    return APR_SUCCESS;
 
1810
}
 
1811
 
 
1812
/*
 
1813
 * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
 
1814
 *             [echomsg="..."] -->
 
1815
 */
 
1816
static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
 
1817
                                  apr_bucket_brigade *bb)
 
1818
{
 
1819
    request_rec *r = f->r;
 
1820
    apr_table_t *env = r->subprocess_env;
 
1821
 
 
1822
    if (!ctx->argc) {
 
1823
        ap_log_rerror(APLOG_MARK,
 
1824
                      (ctx->flags & SSI_FLAG_PRINTING)
 
1825
                          ? APLOG_ERR : APLOG_WARNING,
 
1826
                      0, r, "missing argument for config element in %s",
 
1827
                      r->filename);
 
1828
    }
 
1829
 
 
1830
    if (!(ctx->flags & SSI_FLAG_PRINTING)) {
 
1831
        return APR_SUCCESS;
 
1832
    }
 
1833
 
 
1834
    if (!ctx->argc) {
 
1835
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1836
        return APR_SUCCESS;
 
1837
    }
 
1838
 
 
1839
    while (1) {
 
1840
        char *tag     = NULL;
 
1841
        char *tag_val = NULL;
 
1842
 
 
1843
        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
 
1844
        if (!tag || !tag_val) {
 
1845
            break;
 
1846
        }
 
1847
 
 
1848
        if (!strcmp(tag, "errmsg")) {
 
1849
            ctx->error_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
 
1850
                                                 SSI_EXPAND_DROP_NAME);
 
1851
        }
 
1852
        else if (!strcmp(tag, "echomsg")) {
 
1853
            ctx->intern->undefined_echo =
 
1854
                ap_ssi_parse_string(ctx, tag_val, NULL, 0,SSI_EXPAND_DROP_NAME);
 
1855
            ctx->intern->undefined_echo_len=strlen(ctx->intern->undefined_echo);
 
1856
        }
 
1857
        else if (!strcmp(tag, "timefmt")) {
 
1858
            apr_time_t date = r->request_time;
 
1859
 
 
1860
            ctx->time_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
 
1861
                                                SSI_EXPAND_DROP_NAME);
 
1862
 
 
1863
            apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date,
 
1864
                           ctx->time_str, 0));
 
1865
            apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date,
 
1866
                           ctx->time_str, 1));
 
1867
            apr_table_setn(env, "LAST_MODIFIED",
 
1868
                           ap_ht_time(r->pool, r->finfo.mtime,
 
1869
                           ctx->time_str, 0));
 
1870
        }
 
1871
        else if (!strcmp(tag, "sizefmt")) {
 
1872
            char *parsed_string;
 
1873
 
 
1874
            parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
 
1875
                                                SSI_EXPAND_DROP_NAME);
 
1876
            if (!strcmp(parsed_string, "bytes")) {
 
1877
                ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
 
1878
            }
 
1879
            else if (!strcmp(parsed_string, "abbrev")) {
 
1880
                ctx->flags &= SSI_FLAG_SIZE_ABBREV;
 
1881
            }
 
1882
            else {
 
1883
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
 
1884
                              "\"%s\" to parameter \"sizefmt\" of tag config "
 
1885
                              "in %s", parsed_string, r->filename);
 
1886
                SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1887
                break;
 
1888
            }
 
1889
        }
 
1890
        else {
 
1891
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
 
1892
                          "\"%s\" to tag config in %s", tag, r->filename);
 
1893
            SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1894
            break;
 
1895
        }
 
1896
    }
 
1897
 
 
1898
    return APR_SUCCESS;
 
1899
}
 
1900
 
 
1901
/*
 
1902
 * <!--#fsize virtual|file="..." [virtual|file="..."] ... -->
 
1903
 */
 
1904
static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
 
1905
                                 apr_bucket_brigade *bb)
 
1906
{
 
1907
    request_rec *r = f->r;
 
1908
 
 
1909
    if (!ctx->argc) {
 
1910
        ap_log_rerror(APLOG_MARK,
 
1911
                      (ctx->flags & SSI_FLAG_PRINTING)
 
1912
                          ? APLOG_ERR : APLOG_WARNING,
 
1913
                      0, r, "missing argument for fsize element in %s",
 
1914
                      r->filename);
 
1915
    }
 
1916
 
 
1917
    if (!(ctx->flags & SSI_FLAG_PRINTING)) {
 
1918
        return APR_SUCCESS;
 
1919
    }
 
1920
 
 
1921
    if (!ctx->argc) {
 
1922
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1923
        return APR_SUCCESS;
 
1924
    }
 
1925
 
 
1926
    while (1) {
 
1927
        char *tag     = NULL;
 
1928
        char *tag_val = NULL;
 
1929
        apr_finfo_t finfo;
 
1930
        char *parsed_string;
 
1931
 
 
1932
        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
 
1933
        if (!tag || !tag_val) {
 
1934
            break;
 
1935
        }
 
1936
 
 
1937
        parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
 
1938
                                            SSI_EXPAND_DROP_NAME);
 
1939
 
 
1940
        if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
 
1941
            char *buf;
 
1942
            apr_size_t len;
 
1943
 
 
1944
            if (!(ctx->flags & SSI_FLAG_SIZE_IN_BYTES)) {
 
1945
                buf = apr_strfsize(finfo.size, apr_palloc(ctx->pool, 5));
 
1946
                len = 4; /* omit the \0 terminator */
 
1947
            }
 
1948
            else {
 
1949
                apr_size_t l, x, pos;
 
1950
                char *tmp;
 
1951
 
 
1952
                tmp = apr_psprintf(ctx->dpool, "%" APR_OFF_T_FMT, finfo.size);
 
1953
                len = l = strlen(tmp);
 
1954
 
 
1955
                for (x = 0; x < l; ++x) {
 
1956
                    if (x && !((l - x) % 3)) {
 
1957
                        ++len;
 
1958
                    }
 
1959
                }
 
1960
 
 
1961
                if (len == l) {
 
1962
                    buf = apr_pstrmemdup(ctx->pool, tmp, len);
 
1963
                }
 
1964
                else {
 
1965
                    buf = apr_palloc(ctx->pool, len);
 
1966
 
 
1967
                    for (pos = x = 0; x < l; ++x) {
 
1968
                        if (x && !((l - x) % 3)) {
 
1969
                            buf[pos++] = ',';
 
1970
                        }
 
1971
                        buf[pos++] = tmp[x];
 
1972
                    }
 
1973
                }
 
1974
            }
 
1975
 
 
1976
            APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buf, len,
 
1977
                                    ctx->pool, f->c->bucket_alloc));
 
1978
        }
 
1979
        else {
 
1980
            SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1981
            break;
 
1982
        }
 
1983
    }
 
1984
 
 
1985
    return APR_SUCCESS;
 
1986
}
 
1987
 
 
1988
/*
 
1989
 * <!--#flastmod virtual|file="..." [virtual|file="..."] ... -->
 
1990
 */
 
1991
static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
 
1992
                                    apr_bucket_brigade *bb)
 
1993
{
 
1994
    request_rec *r = f->r;
 
1995
 
 
1996
    if (!ctx->argc) {
 
1997
        ap_log_rerror(APLOG_MARK,
 
1998
                      (ctx->flags & SSI_FLAG_PRINTING)
 
1999
                          ? APLOG_ERR : APLOG_WARNING,
 
2000
                      0, r, "missing argument for flastmod element in %s",
 
2001
                      r->filename);
 
2002
    }
 
2003
 
 
2004
    if (!(ctx->flags & SSI_FLAG_PRINTING)) {
 
2005
        return APR_SUCCESS;
 
2006
    }
 
2007
 
 
2008
    if (!ctx->argc) {
 
2009
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2010
        return APR_SUCCESS;
 
2011
    }
 
2012
 
 
2013
    while (1) {
 
2014
        char *tag     = NULL;
 
2015
        char *tag_val = NULL;
 
2016
        apr_finfo_t  finfo;
 
2017
        char *parsed_string;
 
2018
 
 
2019
        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
 
2020
        if (!tag || !tag_val) {
 
2021
            break;
 
2022
        }
 
2023
 
 
2024
        parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
 
2025
                                            SSI_EXPAND_DROP_NAME);
 
2026
 
 
2027
        if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
 
2028
            char *t_val;
 
2029
            apr_size_t t_len;
 
2030
 
 
2031
            t_val = ap_ht_time(ctx->pool, finfo.mtime, ctx->time_str, 0);
 
2032
            t_len = strlen(t_val);
 
2033
 
 
2034
            APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(t_val, t_len,
 
2035
                                    ctx->pool, f->c->bucket_alloc));
 
2036
        }
 
2037
        else {
 
2038
            SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2039
            break;
 
2040
        }
 
2041
    }
 
2042
 
 
2043
    return APR_SUCCESS;
 
2044
}
 
2045
 
 
2046
/*
 
2047
 * <!--#if expr="..." -->
 
2048
 */
 
2049
static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
 
2050
                              apr_bucket_brigade *bb)
 
2051
{
 
2052
    char *tag = NULL;
 
2053
    char *expr = NULL;
 
2054
    request_rec *r = f->r;
 
2055
    int expr_ret, was_error;
 
2056
 
 
2057
    if (ctx->argc != 1) {
 
2058
        ap_log_rerror(APLOG_MARK,
 
2059
                      (ctx->flags & SSI_FLAG_PRINTING)
 
2060
                          ? APLOG_ERR : APLOG_WARNING,
 
2061
                      0, r, (ctx->argc)
 
2062
                                ? "too many arguments for if element in %s"
 
2063
                                : "missing expr argument for if element in %s",
 
2064
                      r->filename);
 
2065
    }
 
2066
 
 
2067
    if (!(ctx->flags & SSI_FLAG_PRINTING)) {
 
2068
        ++(ctx->if_nesting_level);
 
2069
        return APR_SUCCESS;
 
2070
    }
 
2071
 
 
2072
    if (ctx->argc != 1) {
 
2073
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2074
        return APR_SUCCESS;
 
2075
    }
 
2076
 
 
2077
    ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
 
2078
 
 
2079
    if (strcmp(tag, "expr")) {
 
2080
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
 
2081
                      "to tag if in %s", tag, r->filename);
 
2082
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2083
        return APR_SUCCESS;
 
2084
    }
 
2085
 
 
2086
    if (!expr) {
 
2087
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr value for if "
 
2088
                      "element in %s", r->filename);
 
2089
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2090
        return APR_SUCCESS;
 
2091
    }
 
2092
 
 
2093
    DEBUG_PRINTF((ctx, "****    if expr=\"%s\"\n", expr));
 
2094
 
 
2095
    expr_ret = parse_expr(ctx, expr, &was_error);
 
2096
 
 
2097
    if (was_error) {
 
2098
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2099
        return APR_SUCCESS;
 
2100
    }
 
2101
 
 
2102
    if (expr_ret) {
 
2103
        ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
 
2104
    }
 
2105
    else {
 
2106
        ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
 
2107
    }
 
2108
 
 
2109
    DEBUG_DUMP_COND(ctx, "   if");
 
2110
 
 
2111
    ctx->if_nesting_level = 0;
 
2112
 
 
2113
    return APR_SUCCESS;
 
2114
}
 
2115
 
 
2116
/*
 
2117
 * <!--#elif expr="..." -->
 
2118
 */
 
2119
static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
 
2120
                                apr_bucket_brigade *bb)
 
2121
{
 
2122
    char *tag = NULL;
 
2123
    char *expr = NULL;
 
2124
    request_rec *r = f->r;
 
2125
    int expr_ret, was_error;
 
2126
 
 
2127
    if (ctx->argc != 1) {
 
2128
        ap_log_rerror(APLOG_MARK,
 
2129
                      (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
 
2130
                      0, r, (ctx->argc)
 
2131
                                ? "too many arguments for if element in %s"
 
2132
                                : "missing expr argument for if element in %s",
 
2133
                      r->filename);
 
2134
    }
 
2135
 
 
2136
    if (ctx->if_nesting_level) {
 
2137
        return APR_SUCCESS;
 
2138
    }
 
2139
 
 
2140
    if (ctx->argc != 1) {
 
2141
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2142
        return APR_SUCCESS;
 
2143
    }
 
2144
 
 
2145
    ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
 
2146
 
 
2147
    if (strcmp(tag, "expr")) {
 
2148
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
 
2149
                      "to tag if in %s", tag, r->filename);
 
2150
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2151
        return APR_SUCCESS;
 
2152
    }
 
2153
 
 
2154
    if (!expr) {
 
2155
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "missing expr in elif "
 
2156
                      "statement: %s", r->filename);
 
2157
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2158
        return APR_SUCCESS;
 
2159
    }
 
2160
 
 
2161
    DEBUG_PRINTF((ctx, "****  elif expr=\"%s\"\n", expr));
 
2162
    DEBUG_DUMP_COND(ctx, " elif");
 
2163
 
 
2164
    if (ctx->flags & SSI_FLAG_COND_TRUE) {
 
2165
        ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
 
2166
        return APR_SUCCESS;
 
2167
    }
 
2168
 
 
2169
    expr_ret = parse_expr(ctx, expr, &was_error);
 
2170
 
 
2171
    if (was_error) {
 
2172
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2173
        return APR_SUCCESS;
 
2174
    }
 
2175
 
 
2176
    if (expr_ret) {
 
2177
        ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
 
2178
    }
 
2179
    else {
 
2180
        ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
 
2181
    }
 
2182
 
 
2183
    DEBUG_DUMP_COND(ctx, " elif");
 
2184
 
 
2185
    return APR_SUCCESS;
 
2186
}
 
2187
 
 
2188
/*
 
2189
 * <!--#else -->
 
2190
 */
 
2191
static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f,
 
2192
                                apr_bucket_brigade *bb)
 
2193
{
 
2194
    request_rec *r = f->r;
 
2195
 
 
2196
    if (ctx->argc) {
 
2197
        ap_log_rerror(APLOG_MARK,
 
2198
                      (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
 
2199
                      0, r, "else directive does not take tags in %s",
 
2200
                      r->filename);
 
2201
    }
 
2202
 
 
2203
    if (ctx->if_nesting_level) {
 
2204
        return APR_SUCCESS;
 
2205
    }
 
2206
 
 
2207
    if (ctx->argc) {
 
2208
        if (ctx->flags & SSI_FLAG_PRINTING) {
 
2209
            SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2210
        }
 
2211
 
 
2212
        return APR_SUCCESS;
 
2213
    }
 
2214
 
 
2215
    DEBUG_DUMP_COND(ctx, " else");
 
2216
 
 
2217
    if (ctx->flags & SSI_FLAG_COND_TRUE) {
 
2218
        ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
 
2219
    }
 
2220
    else {
 
2221
        ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
 
2222
    }
 
2223
 
 
2224
    return APR_SUCCESS;
 
2225
}
 
2226
 
 
2227
/*
 
2228
 * <!--#endif -->
 
2229
 */
 
2230
static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f,
 
2231
                                 apr_bucket_brigade *bb)
 
2232
{
 
2233
    request_rec *r = f->r;
 
2234
 
 
2235
    if (ctx->argc) {
 
2236
        ap_log_rerror(APLOG_MARK,
 
2237
                      (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
 
2238
                      0, r, "endif directive does not take tags in %s",
 
2239
                      r->filename);
 
2240
    }
 
2241
 
 
2242
    if (ctx->if_nesting_level) {
 
2243
        --(ctx->if_nesting_level);
 
2244
        return APR_SUCCESS;
 
2245
    }
 
2246
 
 
2247
    if (ctx->argc) {
 
2248
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2249
        return APR_SUCCESS;
 
2250
    }
 
2251
 
 
2252
    DEBUG_DUMP_COND(ctx, "endif");
 
2253
 
 
2254
    ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
 
2255
 
 
2256
    return APR_SUCCESS;
 
2257
}
 
2258
 
 
2259
/*
 
2260
 * <!--#set var="..." value="..." ... -->
 
2261
 */
 
2262
static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
 
2263
                               apr_bucket_brigade *bb)
 
2264
{
 
2265
    char *var = NULL;
 
2266
    request_rec *r = f->r;
 
2267
    request_rec *sub = r->main;
 
2268
    apr_pool_t *p = r->pool;
 
2269
 
 
2270
    if (ctx->argc < 2) {
 
2271
        ap_log_rerror(APLOG_MARK,
 
2272
                      (ctx->flags & SSI_FLAG_PRINTING)
 
2273
                          ? APLOG_ERR : APLOG_WARNING,
 
2274
                      0, r, "missing argument for set element in %s",
 
2275
                      r->filename);
 
2276
    }
 
2277
 
 
2278
    if (!(ctx->flags & SSI_FLAG_PRINTING)) {
 
2279
        return APR_SUCCESS;
 
2280
    }
 
2281
 
 
2282
    if (ctx->argc < 2) {
 
2283
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2284
        return APR_SUCCESS;
 
2285
    }
 
2286
 
 
2287
    /* we need to use the 'main' request pool to set notes as that is
 
2288
     * a notes lifetime
 
2289
     */
 
2290
    while (sub) {
 
2291
        p = sub->pool;
 
2292
        sub = sub->main;
 
2293
    }
 
2294
 
 
2295
    while (1) {
 
2296
        char *tag = NULL;
 
2297
        char *tag_val = NULL;
 
2298
 
 
2299
        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
 
2300
 
 
2301
        if (!tag || !tag_val) {
 
2302
            break;
 
2303
        }
 
2304
 
 
2305
        if (!strcmp(tag, "var")) {
 
2306
            var = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
 
2307
                                      SSI_EXPAND_DROP_NAME);
 
2308
        }
 
2309
        else if (!strcmp(tag, "value")) {
 
2310
            char *parsed_string;
 
2311
 
 
2312
            if (!var) {
 
2313
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "variable must "
 
2314
                              "precede value in set directive in %s",
 
2315
                              r->filename);
 
2316
                SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2317
                break;
 
2318
            }
 
2319
 
 
2320
            parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
 
2321
                                                SSI_EXPAND_DROP_NAME);
 
2322
            apr_table_setn(r->subprocess_env, apr_pstrdup(p, var),
 
2323
                           apr_pstrdup(p, parsed_string));
 
2324
        }
 
2325
        else {
 
2326
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid tag for set "
 
2327
                          "directive in %s", r->filename);
 
2328
            SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2329
            break;
 
2330
        }
 
2331
    }
 
2332
 
 
2333
    return APR_SUCCESS;
 
2334
}
 
2335
 
 
2336
/*
 
2337
 * <!--#printenv -->
 
2338
 */
 
2339
static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f,
 
2340
                                    apr_bucket_brigade *bb)
 
2341
{
 
2342
    request_rec *r = f->r;
 
2343
    const apr_array_header_t *arr;
 
2344
    const apr_table_entry_t *elts;
 
2345
    int i;
 
2346
 
 
2347
    if (ctx->argc) {
 
2348
        ap_log_rerror(APLOG_MARK,
 
2349
                      (ctx->flags & SSI_FLAG_PRINTING)
 
2350
                          ? APLOG_ERR : APLOG_WARNING,
 
2351
                      0, r, "printenv directive does not take tags in %s",
 
2352
                      r->filename);
 
2353
    }
 
2354
 
 
2355
    if (!(ctx->flags & SSI_FLAG_PRINTING)) {
 
2356
        return APR_SUCCESS;
 
2357
    }
 
2358
 
 
2359
    if (ctx->argc) {
 
2360
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
2361
        return APR_SUCCESS;
 
2362
    }
 
2363
 
 
2364
    arr = apr_table_elts(r->subprocess_env);
 
2365
    elts = (apr_table_entry_t *)arr->elts;
 
2366
 
 
2367
    for (i = 0; i < arr->nelts; ++i) {
 
2368
        const char *key_text, *val_text;
 
2369
        char *key_val, *next;
 
2370
        apr_size_t k_len, v_len, kv_length;
 
2371
 
 
2372
        /* get key */
 
2373
        key_text = ap_escape_html(ctx->dpool, elts[i].key);
 
2374
        k_len = strlen(key_text);
 
2375
 
 
2376
        /* get value */
 
2377
        val_text = elts[i].val;
 
2378
        if (val_text == LAZY_VALUE) {
 
2379
            val_text = add_include_vars_lazy(r, elts[i].key);
 
2380
        }
 
2381
        val_text = ap_escape_html(ctx->dpool, elts[i].val);
 
2382
        v_len = strlen(val_text);
 
2383
 
 
2384
        /* assemble result */
 
2385
        kv_length = k_len + v_len + sizeof("=\n");
 
2386
        key_val = apr_palloc(ctx->pool, kv_length);
 
2387
        next = key_val;
 
2388
 
 
2389
        memcpy(next, key_text, k_len);
 
2390
        next += k_len;
 
2391
        *next++ = '=';
 
2392
        memcpy(next, val_text, v_len);
 
2393
        next += v_len;
 
2394
        *next++ = '\n';
 
2395
        *next = 0;
 
2396
 
 
2397
        APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(key_val, kv_length-1,
 
2398
                                ctx->pool, f->c->bucket_alloc));
 
2399
    }
 
2400
 
 
2401
    ctx->flush_now = 1;
 
2402
    return APR_SUCCESS;
 
2403
}
 
2404
 
 
2405
 
 
2406
/*
 
2407
 * +-------------------------------------------------------+
 
2408
 * |                                                       |
 
2409
 * |               Main Includes-Filter Engine
 
2410
 * |                                                       |
 
2411
 * +-------------------------------------------------------+
 
2412
 */
 
2413
 
 
2414
/* This is an implementation of the BNDM search algorithm.
 
2415
 *
 
2416
 * Fast and Flexible String Matching by Combining Bit-parallelism and
 
2417
 * Suffix Automata (2001)
 
2418
 * Gonzalo Navarro, Mathieu Raffinot
 
2419
 *
 
2420
 * http://www-igm.univ-mlv.fr/~raffinot/ftp/jea2001.ps.gz
 
2421
 *
 
2422
 * Initial code submitted by Sascha Schumann.
 
2423
 */
 
2424
 
 
2425
/* Precompile the bndm_t data structure. */
 
2426
static bndm_t *bndm_compile(apr_pool_t *pool, const char *n, apr_size_t nl)
 
2427
{
 
2428
    unsigned int x;
 
2429
    const char *ne = n + nl;
 
2430
    bndm_t *t = apr_palloc(pool, sizeof(*t));
 
2431
 
 
2432
    memset(t->T, 0, sizeof(unsigned int) * 256);
 
2433
    t->pattern_len = nl;
 
2434
 
 
2435
    for (x = 1; n < ne; x <<= 1) {
 
2436
        t->T[(unsigned char) *n++] |= x;
 
2437
    }
 
2438
 
 
2439
    t->x = x - 1;
 
2440
 
 
2441
    return t;
 
2442
}
 
2443
 
 
2444
/* Implements the BNDM search algorithm (as described above).
 
2445
 *
 
2446
 * h  - the string to look in
 
2447
 * hl - length of the string to look for
 
2448
 * t  - precompiled bndm structure against the pattern
 
2449
 *
 
2450
 * Returns the count of character that is the first match or hl if no
 
2451
 * match is found.
 
2452
 */
 
2453
static apr_size_t bndm(bndm_t *t, const char *h, apr_size_t hl)
 
2454
{
 
2455
    const char *skip;
 
2456
    const char *he, *p, *pi;
 
2457
    unsigned int *T, x, d;
 
2458
    apr_size_t nl;
 
2459
 
 
2460
    he = h + hl;
 
2461
 
 
2462
    T = t->T;
 
2463
    x = t->x;
 
2464
    nl = t->pattern_len;
 
2465
 
 
2466
    pi = h - 1; /* pi: p initial */
 
2467
    p = pi + nl; /* compare window right to left. point to the first char */
 
2468
 
 
2469
    while (p < he) {
 
2470
        skip = p;
 
2471
        d = x;
 
2472
        do {
 
2473
            d &= T[(unsigned char) *p--];
 
2474
            if (!d) {
 
2475
                break;
 
2476
            }
 
2477
            if ((d & 1)) {
 
2478
                if (p != pi) {
 
2479
                    skip = p;
 
2480
                }
 
2481
                else {
 
2482
                    return p - h + 1;
 
2483
                }
 
2484
            }
 
2485
            d >>= 1;
 
2486
        } while (d);
 
2487
 
 
2488
        pi = skip;
 
2489
        p = pi + nl;
 
2490
    }
 
2491
 
 
2492
    return hl;
 
2493
}
 
2494
 
 
2495
/*
 
2496
 * returns the index position of the first byte of start_seq (or the len of
 
2497
 * the buffer as non-match)
 
2498
 */
 
2499
static apr_size_t find_start_sequence(include_ctx_t *ctx, const char *data,
 
2500
                                      apr_size_t len)
 
2501
{
 
2502
    struct ssi_internal_ctx *intern = ctx->intern;
 
2503
    apr_size_t slen = intern->start_seq_pat->pattern_len;
 
2504
    apr_size_t index;
 
2505
    const char *p, *ep;
 
2506
 
 
2507
    if (len < slen) {
 
2508
        p = data; /* try partial match at the end of the buffer (below) */
 
2509
    }
 
2510
    else {
 
2511
        /* try fast bndm search over the buffer
 
2512
         * (hopefully the whole start sequence can be found in this buffer)
 
2513
         */
 
2514
        index = bndm(intern->start_seq_pat, data, len);
 
2515
 
 
2516
        /* wow, found it. ready. */
 
2517
        if (index < len) {
 
2518
            intern->state = PARSE_DIRECTIVE;
 
2519
            return index;
 
2520
        }
 
2521
        else {
 
2522
            /* ok, the pattern can't be found as whole in the buffer,
 
2523
             * check the end for a partial match
 
2524
             */
 
2525
            p = data + len - slen + 1;
 
2526
        }
 
2527
    }
 
2528
 
 
2529
    ep = data + len;
 
2530
    do {
 
2531
        while (p < ep && *p != *intern->start_seq) {
 
2532
            ++p;
 
2533
        }
 
2534
 
 
2535
        index = p - data;
 
2536
 
 
2537
        /* found a possible start_seq start */
 
2538
        if (p < ep) {
 
2539
            apr_size_t pos = 1;
 
2540
 
 
2541
            ++p;
 
2542
            while (p < ep && *p == intern->start_seq[pos]) {
 
2543
                ++p;
 
2544
                ++pos;
 
2545
            }
 
2546
 
 
2547
            /* partial match found. Store the info for the next round */
 
2548
            if (p == ep) {
 
2549
                intern->state = PARSE_HEAD;
 
2550
                intern->parse_pos = pos;
 
2551
                return index;
 
2552
            }
 
2553
        }
 
2554
 
 
2555
        /* we must try all combinations; consider (e.g.) SSIStartTag "--->"
 
2556
         * and a string data of "--.-" and the end of the buffer
 
2557
         */
 
2558
        p = data + index + 1;
 
2559
    } while (p < ep);
 
2560
 
 
2561
    /* no match */
 
2562
    return len;
 
2563
}
 
2564
 
 
2565
/*
 
2566
 * returns the first byte *after* the partial (or final) match.
 
2567
 *
 
2568
 * If we had to trick with the start_seq start, 'release' returns the
 
2569
 * number of chars of the start_seq which appeared not to be part of a
 
2570
 * full tag and may have to be passed down the filter chain.
 
2571
 */
 
2572
static apr_size_t find_partial_start_sequence(include_ctx_t *ctx,
 
2573
                                              const char *data,
 
2574
                                              apr_size_t len,
 
2575
                                              apr_size_t *release)
 
2576
{
 
2577
    struct ssi_internal_ctx *intern = ctx->intern;
 
2578
    apr_size_t pos, spos = 0;
 
2579
    apr_size_t slen = intern->start_seq_pat->pattern_len;
 
2580
    const char *p, *ep;
 
2581
 
 
2582
    pos = intern->parse_pos;
 
2583
    ep = data + len;
 
2584
    *release = 0;
 
2585
 
 
2586
    do {
 
2587
        p = data;
 
2588
 
 
2589
        while (p < ep && pos < slen && *p == intern->start_seq[pos]) {
 
2590
            ++p;
 
2591
            ++pos;
 
2592
        }
 
2593
 
 
2594
        /* full match */
 
2595
        if (pos == slen) {
 
2596
            intern->state = PARSE_DIRECTIVE;
 
2597
            return (p - data);
 
2598
        }
 
2599
 
 
2600
        /* the whole buffer is a partial match */
 
2601
        if (p == ep) {
 
2602
            intern->parse_pos = pos;
 
2603
            return (p - data);
 
2604
        }
 
2605
 
 
2606
        /* No match so far, but again:
 
2607
         * We must try all combinations, since the start_seq is a random
 
2608
         * user supplied string
 
2609
         *
 
2610
         * So: look if the first char of start_seq appears somewhere within
 
2611
         * the current partial match. If it does, try to start a match that
 
2612
         * begins with this offset. (This can happen, if a strange
 
2613
         * start_seq like "---->" spans buffers)
 
2614
         */
 
2615
        if (spos < intern->parse_pos) {
 
2616
            do {
 
2617
                ++spos;
 
2618
                ++*release;
 
2619
                p = intern->start_seq + spos;
 
2620
                pos = intern->parse_pos - spos;
 
2621
 
 
2622
                while (pos && *p != *intern->start_seq) {
 
2623
                    ++p;
 
2624
                    ++spos;
 
2625
                    ++*release;
 
2626
                    --pos;
 
2627
                }
 
2628
 
 
2629
                /* if a matching beginning char was found, try to match the
 
2630
                 * remainder of the old buffer.
 
2631
                 */
 
2632
                if (pos > 1) {
 
2633
                    apr_size_t t = 1;
 
2634
 
 
2635
                    ++p;
 
2636
                    while (t < pos && *p == intern->start_seq[t]) {
 
2637
                        ++p;
 
2638
                        ++t;
 
2639
                    }
 
2640
 
 
2641
                    if (t == pos) {
 
2642
                        /* yeah, another partial match found in the *old*
 
2643
                         * buffer, now test the *current* buffer for
 
2644
                         * continuing match
 
2645
                         */
 
2646
                        break;
 
2647
                    }
 
2648
                }
 
2649
            } while (pos > 1);
 
2650
 
 
2651
            if (pos) {
 
2652
                continue;
 
2653
            }
 
2654
        }
 
2655
 
 
2656
        break;
 
2657
    } while (1); /* work hard to find a match ;-) */
 
2658
 
 
2659
    /* no match at all, release all (wrongly) matched chars so far */
 
2660
    *release = intern->parse_pos;
 
2661
    intern->state = PARSE_PRE_HEAD;
 
2662
    return 0;
 
2663
}
 
2664
 
 
2665
/*
 
2666
 * returns the position after the directive
 
2667
 */
 
2668
static apr_size_t find_directive(include_ctx_t *ctx, const char *data,
 
2669
                                 apr_size_t len, char ***store,
 
2670
                                 apr_size_t **store_len)
 
2671
{
 
2672
    struct ssi_internal_ctx *intern = ctx->intern;
 
2673
    const char *p = data;
 
2674
    const char *ep = data + len;
 
2675
    apr_size_t pos;
 
2676
 
 
2677
    switch (intern->state) {
 
2678
    case PARSE_DIRECTIVE:
 
2679
        while (p < ep && !apr_isspace(*p)) {
 
2680
            /* we have to consider the case of missing space between directive
 
2681
             * and end_seq (be somewhat lenient), e.g. <!--#printenv-->
 
2682
             */
 
2683
            if (*p == *intern->end_seq) {
 
2684
                intern->state = PARSE_DIRECTIVE_TAIL;
 
2685
                intern->parse_pos = 1;
 
2686
                ++p;
 
2687
                return (p - data);
 
2688
            }
 
2689
            ++p;
 
2690
        }
 
2691
 
 
2692
        if (p < ep) { /* found delimiter whitespace */
 
2693
            intern->state = PARSE_DIRECTIVE_POSTNAME;
 
2694
            *store = &intern->directive;
 
2695
            *store_len = &intern->directive_len;
 
2696
        }
 
2697
 
 
2698
        break;
 
2699
 
 
2700
    case PARSE_DIRECTIVE_TAIL:
 
2701
        pos = intern->parse_pos;
 
2702
 
 
2703
        while (p < ep && pos < intern->end_seq_len &&
 
2704
               *p == intern->end_seq[pos]) {
 
2705
            ++p;
 
2706
            ++pos;
 
2707
        }
 
2708
 
 
2709
        /* full match, we're done */
 
2710
        if (pos == intern->end_seq_len) {
 
2711
            intern->state = PARSE_DIRECTIVE_POSTTAIL;
 
2712
            *store = &intern->directive;
 
2713
            *store_len = &intern->directive_len;
 
2714
            break;
 
2715
        }
 
2716
 
 
2717
        /* partial match, the buffer is too small to match fully */
 
2718
        if (p == ep) {
 
2719
            intern->parse_pos = pos;
 
2720
            break;
 
2721
        }
 
2722
 
 
2723
        /* no match. continue normal parsing */
 
2724
        intern->state = PARSE_DIRECTIVE;
 
2725
        return 0;
 
2726
 
 
2727
    case PARSE_DIRECTIVE_POSTTAIL:
 
2728
        intern->state = PARSE_EXECUTE;
 
2729
        intern->directive_len -= intern->end_seq_len;
 
2730
        /* continue immediately with the next state */
 
2731
 
 
2732
    case PARSE_DIRECTIVE_POSTNAME:
 
2733
        if (PARSE_DIRECTIVE_POSTNAME == intern->state) {
 
2734
            intern->state = PARSE_PRE_ARG;
 
2735
        }
 
2736
        ctx->argc = 0;
 
2737
        intern->argv = NULL;
 
2738
 
 
2739
        if (!intern->directive_len) {
 
2740
            intern->error = 1;
 
2741
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing "
 
2742
                          "directive name in parsed document %s",
 
2743
                          intern->r->filename);
 
2744
        }
 
2745
        else {
 
2746
            char *sp = intern->directive;
 
2747
            char *sep = intern->directive + intern->directive_len;
 
2748
 
 
2749
            /* normalize directive name */
 
2750
            for (; sp < sep; ++sp) {
 
2751
                *sp = apr_tolower(*sp);
 
2752
            }
 
2753
        }
 
2754
 
 
2755
        return 0;
 
2756
 
 
2757
    default:
 
2758
        /* get a rid of a gcc warning about unhandled enumerations */
 
2759
        break;
 
2760
    }
 
2761
 
 
2762
    return (p - data);
 
2763
}
 
2764
 
 
2765
/*
 
2766
 * find out whether the next token is (a possible) end_seq or an argument
 
2767
 */
 
2768
static apr_size_t find_arg_or_tail(include_ctx_t *ctx, const char *data,
 
2769
                                   apr_size_t len)
 
2770
{
 
2771
    struct ssi_internal_ctx *intern = ctx->intern;
 
2772
    const char *p = data;
 
2773
    const char *ep = data + len;
 
2774
 
 
2775
    /* skip leading WS */
 
2776
    while (p < ep && apr_isspace(*p)) {
 
2777
        ++p;
 
2778
    }
 
2779
 
 
2780
    /* buffer doesn't consist of whitespaces only */
 
2781
    if (p < ep) {
 
2782
        intern->state = (*p == *intern->end_seq) ? PARSE_TAIL : PARSE_ARG;
 
2783
    }
 
2784
 
 
2785
    return (p - data);
 
2786
}
 
2787
 
 
2788
/*
 
2789
 * test the stream for end_seq. If it doesn't match at all, it must be an
 
2790
 * argument
 
2791
 */
 
2792
static apr_size_t find_tail(include_ctx_t *ctx, const char *data,
 
2793
                            apr_size_t len)
 
2794
{
 
2795
    struct ssi_internal_ctx *intern = ctx->intern;
 
2796
    const char *p = data;
 
2797
    const char *ep = data + len;
 
2798
    apr_size_t pos = intern->parse_pos;
 
2799
 
 
2800
    if (PARSE_TAIL == intern->state) {
 
2801
        intern->state = PARSE_TAIL_SEQ;
 
2802
        pos = intern->parse_pos = 0;
 
2803
    }
 
2804
 
 
2805
    while (p < ep && pos < intern->end_seq_len && *p == intern->end_seq[pos]) {
 
2806
        ++p;
 
2807
        ++pos;
 
2808
    }
 
2809
 
 
2810
    /* bingo, full match */
 
2811
    if (pos == intern->end_seq_len) {
 
2812
        intern->state = PARSE_EXECUTE;
 
2813
        return (p - data);
 
2814
    }
 
2815
 
 
2816
    /* partial match, the buffer is too small to match fully */
 
2817
    if (p == ep) {
 
2818
        intern->parse_pos = pos;
 
2819
        return (p - data);
 
2820
    }
 
2821
 
 
2822
    /* no match. It must be an argument string then
 
2823
     * The caller should cleanup and rewind to the reparse point
 
2824
     */
 
2825
    intern->state = PARSE_ARG;
 
2826
    return 0;
 
2827
}
 
2828
 
 
2829
/*
 
2830
 * extract name=value from the buffer
 
2831
 * A pcre-pattern could look (similar to):
 
2832
 * name\s*(?:=\s*(["'`]?)value\1(?>\s*))?
 
2833
 */
 
2834
static apr_size_t find_argument(include_ctx_t *ctx, const char *data,
 
2835
                                apr_size_t len, char ***store,
 
2836
                                apr_size_t **store_len)
 
2837
{
 
2838
    struct ssi_internal_ctx *intern = ctx->intern;
 
2839
    const char *p = data;
 
2840
    const char *ep = data + len;
 
2841
 
 
2842
    switch (intern->state) {
 
2843
    case PARSE_ARG:
 
2844
        /*
 
2845
         * create argument structure and append it to the current list
 
2846
         */
 
2847
        intern->current_arg = apr_palloc(ctx->dpool,
 
2848
                                         sizeof(*intern->current_arg));
 
2849
        intern->current_arg->next = NULL;
 
2850
 
 
2851
        ++(ctx->argc);
 
2852
        if (!intern->argv) {
 
2853
            intern->argv = intern->current_arg;
 
2854
        }
 
2855
        else {
 
2856
            arg_item_t *newarg = intern->argv;
 
2857
 
 
2858
            while (newarg->next) {
 
2859
                newarg = newarg->next;
 
2860
            }
 
2861
            newarg->next = intern->current_arg;
 
2862
        }
 
2863
 
 
2864
        /* check whether it's a valid one. If it begins with a quote, we
 
2865
         * can safely assume, someone forgot the name of the argument
 
2866
         */
 
2867
        switch (*p) {
 
2868
        case '"': case '\'': case '`':
 
2869
            *store = NULL;
 
2870
 
 
2871
            intern->state = PARSE_ARG_VAL;
 
2872
            intern->quote = *p++;
 
2873
            intern->current_arg->name = NULL;
 
2874
            intern->current_arg->name_len = 0;
 
2875
            intern->error = 1;
 
2876
 
 
2877
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing "
 
2878
                          "argument name for value to tag %s in %s",
 
2879
                          apr_pstrmemdup(intern->r->pool, intern->directive,
 
2880
                                         intern->directive_len),
 
2881
                                         intern->r->filename);
 
2882
 
 
2883
            return (p - data);
 
2884
 
 
2885
        default:
 
2886
            intern->state = PARSE_ARG_NAME;
 
2887
        }
 
2888
        /* continue immediately with next state */
 
2889
 
 
2890
    case PARSE_ARG_NAME:
 
2891
        while (p < ep && !apr_isspace(*p) && *p != '=') {
 
2892
            ++p;
 
2893
        }
 
2894
 
 
2895
        if (p < ep) {
 
2896
            intern->state = PARSE_ARG_POSTNAME;
 
2897
            *store = &intern->current_arg->name;
 
2898
            *store_len = &intern->current_arg->name_len;
 
2899
            return (p - data);
 
2900
        }
 
2901
        break;
 
2902
 
 
2903
    case PARSE_ARG_POSTNAME:
 
2904
        intern->current_arg->name = apr_pstrmemdup(ctx->dpool,
 
2905
                                                 intern->current_arg->name,
 
2906
                                                 intern->current_arg->name_len);
 
2907
        if (!intern->current_arg->name_len) {
 
2908
            intern->error = 1;
 
2909
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing "
 
2910
                          "argument name for value to tag %s in %s",
 
2911
                          apr_pstrmemdup(intern->r->pool, intern->directive,
 
2912
                                         intern->directive_len),
 
2913
                                         intern->r->filename);
 
2914
        }
 
2915
        else {
 
2916
            char *sp = intern->current_arg->name;
 
2917
 
 
2918
            /* normalize the name */
 
2919
            while (*sp) {
 
2920
                *sp = apr_tolower(*sp);
 
2921
                ++sp;
 
2922
            }
 
2923
        }
 
2924
 
 
2925
        intern->state = PARSE_ARG_EQ;
 
2926
        /* continue with next state immediately */
 
2927
 
 
2928
    case PARSE_ARG_EQ:
 
2929
        *store = NULL;
 
2930
 
 
2931
        while (p < ep && apr_isspace(*p)) {
 
2932
            ++p;
 
2933
        }
 
2934
 
 
2935
        if (p < ep) {
 
2936
            if (*p == '=') {
 
2937
                intern->state = PARSE_ARG_PREVAL;
 
2938
                ++p;
 
2939
            }
 
2940
            else { /* no value */
 
2941
                intern->current_arg->value = NULL;
 
2942
                intern->state = PARSE_PRE_ARG;
 
2943
            }
 
2944
 
 
2945
            return (p - data);
 
2946
        }
 
2947
        break;
 
2948
 
 
2949
    case PARSE_ARG_PREVAL:
 
2950
        *store = NULL;
 
2951
 
 
2952
        while (p < ep && apr_isspace(*p)) {
 
2953
            ++p;
 
2954
        }
 
2955
 
 
2956
        /* buffer doesn't consist of whitespaces only */
 
2957
        if (p < ep) {
 
2958
            intern->state = PARSE_ARG_VAL;
 
2959
            switch (*p) {
 
2960
            case '"': case '\'': case '`':
 
2961
                intern->quote = *p++;
 
2962
                break;
 
2963
            default:
 
2964
                intern->quote = '\0';
 
2965
                break;
 
2966
            }
 
2967
 
 
2968
            return (p - data);
 
2969
        }
 
2970
        break;
 
2971
 
 
2972
    case PARSE_ARG_VAL_ESC:
 
2973
        if (*p == intern->quote) {
 
2974
            ++p;
 
2975
        }
 
2976
        intern->state = PARSE_ARG_VAL;
 
2977
        /* continue with next state immediately */
 
2978
 
 
2979
    case PARSE_ARG_VAL:
 
2980
        for (; p < ep; ++p) {
 
2981
            if (intern->quote && *p == '\\') {
 
2982
                ++p;
 
2983
                if (p == ep) {
 
2984
                    intern->state = PARSE_ARG_VAL_ESC;
 
2985
                    break;
 
2986
                }
 
2987
 
 
2988
                if (*p != intern->quote) {
 
2989
                    --p;
 
2990
                }
 
2991
            }
 
2992
            else if (intern->quote && *p == intern->quote) {
 
2993
                ++p;
 
2994
                *store = &intern->current_arg->value;
 
2995
                *store_len = &intern->current_arg->value_len;
 
2996
                intern->state = PARSE_ARG_POSTVAL;
 
2997
                break;
 
2998
            }
 
2999
            else if (!intern->quote && apr_isspace(*p)) {
 
3000
                ++p;
 
3001
                *store = &intern->current_arg->value;
 
3002
                *store_len = &intern->current_arg->value_len;
 
3003
                intern->state = PARSE_ARG_POSTVAL;
 
3004
                break;
 
3005
            }
 
3006
        }
 
3007
 
 
3008
        return (p - data);
 
3009
 
 
3010
    case PARSE_ARG_POSTVAL:
 
3011
        /*
 
3012
         * The value is still the raw input string. Finally clean it up.
 
3013
         */
 
3014
        --(intern->current_arg->value_len);
 
3015
 
 
3016
        /* strip quote escaping \ from the string */
 
3017
        if (intern->quote) {
 
3018
            apr_size_t shift = 0;
 
3019
            char *sp;
 
3020
 
 
3021
            sp = intern->current_arg->value;
 
3022
            ep = intern->current_arg->value + intern->current_arg->value_len;
 
3023
            while (sp < ep && *sp != '\\') {
 
3024
                ++sp;
 
3025
            }
 
3026
            for (; sp < ep; ++sp) {
 
3027
                if (*sp == '\\' && sp[1] == intern->quote) {
 
3028
                    ++sp;
 
3029
                    ++shift;
 
3030
                }
 
3031
                if (shift) {
 
3032
                    *(sp-shift) = *sp;
 
3033
                }
 
3034
            }
 
3035
 
 
3036
            intern->current_arg->value_len -= shift;
 
3037
        }
 
3038
 
 
3039
        intern->current_arg->value[intern->current_arg->value_len] = '\0';
 
3040
        intern->state = PARSE_PRE_ARG;
 
3041
 
 
3042
        return 0;
 
3043
 
 
3044
    default:
 
3045
        /* get a rid of a gcc warning about unhandled enumerations */
 
3046
        break;
 
3047
    }
 
3048
 
 
3049
    return len; /* partial match of something */
 
3050
}
 
3051
 
 
3052
/*
 
3053
 * This is the main loop over the current bucket brigade.
 
3054
 */
 
3055
static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb)
 
3056
{
 
3057
    include_ctx_t *ctx = f->ctx;
 
3058
    struct ssi_internal_ctx *intern = ctx->intern;
 
3059
    request_rec *r = f->r;
 
3060
    apr_bucket *b = APR_BRIGADE_FIRST(bb);
 
3061
    apr_bucket_brigade *pass_bb;
 
3062
    apr_status_t rv = APR_SUCCESS;
 
3063
    char *magic; /* magic pointer for sentinel use */
 
3064
 
 
3065
    /* fast exit */
 
3066
    if (APR_BRIGADE_EMPTY(bb)) {
 
3067
        return APR_SUCCESS;
 
3068
    }
 
3069
 
 
3070
    /* we may crash, since already cleaned up; hand over the responsibility
 
3071
     * to the next filter;-)
 
3072
     */
 
3073
    if (intern->seen_eos) {
 
3074
        return ap_pass_brigade(f->next, bb);
 
3075
    }
 
3076
 
 
3077
    /* All stuff passed along has to be put into that brigade */
 
3078
    pass_bb = apr_brigade_create(ctx->pool, f->c->bucket_alloc);
 
3079
 
 
3080
    /* initialization for this loop */
 
3081
    intern->bytes_read = 0;
 
3082
    intern->error = 0;
 
3083
    intern->r = r;
 
3084
    ctx->flush_now = 0;
 
3085
 
 
3086
    /* loop over the current bucket brigade */
 
3087
    while (b != APR_BRIGADE_SENTINEL(bb)) {
 
3088
        const char *data = NULL;
 
3089
        apr_size_t len, index, release;
 
3090
        apr_bucket *newb = NULL;
 
3091
        char **store = &magic;
 
3092
        apr_size_t *store_len = NULL;
 
3093
 
 
3094
        /* handle meta buckets before reading any data */
 
3095
        if (APR_BUCKET_IS_METADATA(b)) {
 
3096
            newb = APR_BUCKET_NEXT(b);
 
3097
 
 
3098
            APR_BUCKET_REMOVE(b);
 
3099
 
 
3100
            if (APR_BUCKET_IS_EOS(b)) {
 
3101
                intern->seen_eos = 1;
 
3102
 
 
3103
                /* Hit end of stream, time for cleanup ... But wait!
 
3104
                 * Perhaps we're not ready yet. We may have to loop one or
 
3105
                 * two times again to finish our work. In that case, we
 
3106
                 * just re-insert the EOS bucket to allow for an extra loop.
 
3107
                 *
 
3108
                 * PARSE_EXECUTE means, we've hit a directive just before the
 
3109
                 *    EOS, which is now waiting for execution.
 
3110
                 *
 
3111
                 * PARSE_DIRECTIVE_POSTTAIL means, we've hit a directive with
 
3112
                 *    no argument and no space between directive and end_seq
 
3113
                 *    just before the EOS. (consider <!--#printenv--> as last
 
3114
                 *    or only string within the stream). This state, however,
 
3115
                 *    just cleans up and turns itself to PARSE_EXECUTE, which
 
3116
                 *    will be passed through within the next (and actually
 
3117
                 *    last) round.
 
3118
                 */
 
3119
                if (PARSE_EXECUTE            == intern->state ||
 
3120
                    PARSE_DIRECTIVE_POSTTAIL == intern->state) {
 
3121
                    APR_BUCKET_INSERT_BEFORE(newb, b);
 
3122
                }
 
3123
                else {
 
3124
                    break; /* END OF STREAM */
 
3125
                }
 
3126
            }
 
3127
            else {
 
3128
                APR_BRIGADE_INSERT_TAIL(pass_bb, b);
 
3129
 
 
3130
                if (APR_BUCKET_IS_FLUSH(b)) {
 
3131
                    ctx->flush_now = 1;
 
3132
                }
 
3133
 
 
3134
                b = newb;
 
3135
                continue;
 
3136
            }
 
3137
        }
 
3138
 
 
3139
        /* enough is enough ... */
 
3140
        if (ctx->flush_now ||
 
3141
            intern->bytes_read > AP_MIN_BYTES_TO_WRITE) {
 
3142
 
 
3143
            if (!APR_BRIGADE_EMPTY(pass_bb)) {
 
3144
                rv = ap_pass_brigade(f->next, pass_bb);
 
3145
                if (rv != APR_SUCCESS) {
 
3146
                    apr_brigade_destroy(pass_bb);
 
3147
                    return rv;
 
3148
                }
 
3149
            }
 
3150
 
 
3151
            ctx->flush_now = 0;
 
3152
            intern->bytes_read = 0;
 
3153
        }
 
3154
 
 
3155
        /* read the current bucket data */
 
3156
        len = 0;
 
3157
        if (!intern->seen_eos) {
 
3158
            if (intern->bytes_read > 0) {
 
3159
                rv = apr_bucket_read(b, &data, &len, APR_NONBLOCK_READ);
 
3160
                if (APR_STATUS_IS_EAGAIN(rv)) {
 
3161
                    ctx->flush_now = 1;
 
3162
                    continue;
 
3163
                }
 
3164
            }
 
3165
 
 
3166
            if (!len || rv != APR_SUCCESS) {
 
3167
                rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
 
3168
            }
 
3169
 
 
3170
            if (rv != APR_SUCCESS) {
 
3171
                apr_brigade_destroy(pass_bb);
 
3172
                return rv;
 
3173
            }
 
3174
 
 
3175
            intern->bytes_read += len;
 
3176
        }
 
3177
 
 
3178
        /* zero length bucket, fetch next one */
 
3179
        if (!len && !intern->seen_eos) {
 
3180
            b = APR_BUCKET_NEXT(b);
 
3181
            continue;
 
3182
        }
 
3183
 
 
3184
        /*
 
3185
         * it's actually a data containing bucket, start/continue parsing
 
3186
         */
 
3187
 
 
3188
        switch (intern->state) {
 
3189
        /* no current tag; search for start sequence */
 
3190
        case PARSE_PRE_HEAD:
 
3191
            index = find_start_sequence(ctx, data, len);
 
3192
 
 
3193
            if (index < len) {
 
3194
                apr_bucket_split(b, index);
 
3195
            }
 
3196
 
 
3197
            newb = APR_BUCKET_NEXT(b);
 
3198
            if (ctx->flags & SSI_FLAG_PRINTING) {
 
3199
                APR_BUCKET_REMOVE(b);
 
3200
                APR_BRIGADE_INSERT_TAIL(pass_bb, b);
 
3201
            }
 
3202
            else {
 
3203
                apr_bucket_delete(b);
 
3204
            }
 
3205
 
 
3206
            if (index < len) {
 
3207
                /* now delete the start_seq stuff from the remaining bucket */
 
3208
                if (PARSE_DIRECTIVE == intern->state) { /* full match */
 
3209
                    apr_bucket_split(newb, intern->start_seq_pat->pattern_len);
 
3210
                    ctx->flush_now = 1; /* pass pre-tag stuff */
 
3211
                }
 
3212
 
 
3213
                b = APR_BUCKET_NEXT(newb);
 
3214
                apr_bucket_delete(newb);
 
3215
            }
 
3216
            else {
 
3217
                b = newb;
 
3218
            }
 
3219
 
 
3220
            break;
 
3221
 
 
3222
        /* we're currently looking for the end of the start sequence */
 
3223
        case PARSE_HEAD:
 
3224
            index = find_partial_start_sequence(ctx, data, len, &release);
 
3225
 
 
3226
            /* check if we mismatched earlier and have to release some chars */
 
3227
            if (release && (ctx->flags & SSI_FLAG_PRINTING)) {
 
3228
                char *to_release = apr_palloc(ctx->pool, release);
 
3229
 
 
3230
                memcpy(to_release, intern->start_seq, release);
 
3231
                newb = apr_bucket_pool_create(to_release, release, ctx->pool,
 
3232
                                              f->c->bucket_alloc);
 
3233
                APR_BRIGADE_INSERT_TAIL(pass_bb, newb);
 
3234
            }
 
3235
 
 
3236
            if (index) { /* any match */
 
3237
                /* now delete the start_seq stuff from the remaining bucket */
 
3238
                if (PARSE_DIRECTIVE == intern->state) { /* final match */
 
3239
                    apr_bucket_split(b, index);
 
3240
                    ctx->flush_now = 1; /* pass pre-tag stuff */
 
3241
                }
 
3242
                newb = APR_BUCKET_NEXT(b);
 
3243
                apr_bucket_delete(b);
 
3244
                b = newb;
 
3245
            }
 
3246
 
 
3247
            break;
 
3248
 
 
3249
        /* we're currently grabbing the directive name */
 
3250
        case PARSE_DIRECTIVE:
 
3251
        case PARSE_DIRECTIVE_POSTNAME:
 
3252
        case PARSE_DIRECTIVE_TAIL:
 
3253
        case PARSE_DIRECTIVE_POSTTAIL:
 
3254
            index = find_directive(ctx, data, len, &store, &store_len);
 
3255
 
 
3256
            if (index) {
 
3257
                apr_bucket_split(b, index);
 
3258
                newb = APR_BUCKET_NEXT(b);
 
3259
            }
 
3260
 
 
3261
            if (store) {
 
3262
                if (index) {
 
3263
                    APR_BUCKET_REMOVE(b);
 
3264
                    APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
 
3265
                    b = newb;
 
3266
                }
 
3267
 
 
3268
                /* time for cleanup? */
 
3269
                if (store != &magic) {
 
3270
                    apr_brigade_pflatten(intern->tmp_bb, store, store_len,
 
3271
                                         ctx->dpool);
 
3272
                    apr_brigade_cleanup(intern->tmp_bb);
 
3273
                }
 
3274
            }
 
3275
            else if (index) {
 
3276
                apr_bucket_delete(b);
 
3277
                b = newb;
 
3278
            }
 
3279
 
 
3280
            break;
 
3281
 
 
3282
        /* skip WS and find out what comes next (arg or end_seq) */
 
3283
        case PARSE_PRE_ARG:
 
3284
            index = find_arg_or_tail(ctx, data, len);
 
3285
 
 
3286
            if (index) { /* skipped whitespaces */
 
3287
                if (index < len) {
 
3288
                    apr_bucket_split(b, index);
 
3289
                }
 
3290
                newb = APR_BUCKET_NEXT(b);
 
3291
                apr_bucket_delete(b);
 
3292
                b = newb;
 
3293
            }
 
3294
 
 
3295
            break;
 
3296
 
 
3297
        /* currently parsing name[=val] */
 
3298
        case PARSE_ARG:
 
3299
        case PARSE_ARG_NAME:
 
3300
        case PARSE_ARG_POSTNAME:
 
3301
        case PARSE_ARG_EQ:
 
3302
        case PARSE_ARG_PREVAL:
 
3303
        case PARSE_ARG_VAL:
 
3304
        case PARSE_ARG_VAL_ESC:
 
3305
        case PARSE_ARG_POSTVAL:
 
3306
            index = find_argument(ctx, data, len, &store, &store_len);
 
3307
 
 
3308
            if (index) {
 
3309
                apr_bucket_split(b, index);
 
3310
                newb = APR_BUCKET_NEXT(b);
 
3311
            }
 
3312
 
 
3313
            if (store) {
 
3314
                if (index) {
 
3315
                    APR_BUCKET_REMOVE(b);
 
3316
                    APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
 
3317
                    b = newb;
 
3318
                }
 
3319
 
 
3320
                /* time for cleanup? */
 
3321
                if (store != &magic) {
 
3322
                    apr_brigade_pflatten(intern->tmp_bb, store, store_len,
 
3323
                                         ctx->dpool);
 
3324
                    apr_brigade_cleanup(intern->tmp_bb);
 
3325
                }
 
3326
            }
 
3327
            else if (index) {
 
3328
                apr_bucket_delete(b);
 
3329
                b = newb;
 
3330
            }
 
3331
 
 
3332
            break;
 
3333
 
 
3334
        /* try to match end_seq at current pos. */
 
3335
        case PARSE_TAIL:
 
3336
        case PARSE_TAIL_SEQ:
 
3337
            index = find_tail(ctx, data, len);
 
3338
 
 
3339
            switch (intern->state) {
 
3340
            case PARSE_EXECUTE:  /* full match */
 
3341
                apr_bucket_split(b, index);
 
3342
                newb = APR_BUCKET_NEXT(b);
 
3343
                apr_bucket_delete(b);
 
3344
                b = newb;
 
3345
                break;
 
3346
 
 
3347
            case PARSE_ARG:      /* no match */
 
3348
                /* PARSE_ARG must reparse at the beginning */
 
3349
                APR_BRIGADE_PREPEND(bb, intern->tmp_bb);
 
3350
                b = APR_BRIGADE_FIRST(bb);
 
3351
                break;
 
3352
 
 
3353
            default:             /* partial match */
 
3354
                newb = APR_BUCKET_NEXT(b);
 
3355
                APR_BUCKET_REMOVE(b);
 
3356
                APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
 
3357
                b = newb;
 
3358
                break;
 
3359
            }
 
3360
 
 
3361
            break;
 
3362
 
 
3363
        /* now execute the parsed directive, cleanup the space and
 
3364
         * start again with PARSE_PRE_HEAD
 
3365
         */
 
3366
        case PARSE_EXECUTE:
 
3367
            /* if there was an error, it was already logged; just stop here */
 
3368
            if (intern->error) {
 
3369
                if (ctx->flags & SSI_FLAG_PRINTING) {
 
3370
                    SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
 
3371
                    intern->error = 0;
 
3372
                }
 
3373
            }
 
3374
            else {
 
3375
                include_handler_fn_t *handle_func;
 
3376
 
 
3377
                handle_func =
 
3378
                    (include_handler_fn_t *)apr_hash_get(include_handlers, intern->directive,
 
3379
                                                         intern->directive_len);
 
3380
 
 
3381
                if (handle_func) {
 
3382
                    DEBUG_INIT(ctx, f, pass_bb);
 
3383
                    rv = handle_func(ctx, f, pass_bb);
 
3384
                    if (rv != APR_SUCCESS) {
 
3385
                        apr_brigade_destroy(pass_bb);
 
3386
                        return rv;
 
3387
                    }
 
3388
                }
 
3389
                else {
 
3390
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
3391
                                  "unknown directive \"%s\" in parsed doc %s",
 
3392
                                  apr_pstrmemdup(r->pool, intern->directive,
 
3393
                                                 intern->directive_len),
 
3394
                                                 r->filename);
 
3395
                    if (ctx->flags & SSI_FLAG_PRINTING) {
 
3396
                        SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
 
3397
                    }
 
3398
                }
 
3399
            }
 
3400
 
 
3401
            /* cleanup */
 
3402
            apr_pool_clear(ctx->dpool);
 
3403
            apr_brigade_cleanup(intern->tmp_bb);
 
3404
 
 
3405
            /* Oooof. Done here, start next round */
 
3406
            intern->state = PARSE_PRE_HEAD;
 
3407
            break;
 
3408
 
 
3409
        } /* switch(ctx->state) */
 
3410
 
 
3411
    } /* while(brigade) */
 
3412
 
 
3413
    /* End of stream. Final cleanup */
 
3414
    if (intern->seen_eos) {
 
3415
        if (PARSE_HEAD == intern->state) {
 
3416
            if (ctx->flags & SSI_FLAG_PRINTING) {
 
3417
                char *to_release = apr_palloc(ctx->pool, intern->parse_pos);
 
3418
 
 
3419
                memcpy(to_release, intern->start_seq, intern->parse_pos);
 
3420
                APR_BRIGADE_INSERT_TAIL(pass_bb,
 
3421
                                        apr_bucket_pool_create(to_release,
 
3422
                                        intern->parse_pos, ctx->pool,
 
3423
                                        f->c->bucket_alloc));
 
3424
            }
 
3425
        }
 
3426
        else if (PARSE_PRE_HEAD != intern->state) {
 
3427
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
3428
                          "SSI directive was not properly finished at the end "
 
3429
                          "of parsed document %s", r->filename);
 
3430
            if (ctx->flags & SSI_FLAG_PRINTING) {
 
3431
                SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
 
3432
            }
 
3433
        }
 
3434
 
 
3435
        if (!(ctx->flags & SSI_FLAG_PRINTING)) {
 
3436
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
3437
                          "missing closing endif directive in parsed document"
 
3438
                          " %s", r->filename);
 
3439
        }
 
3440
 
 
3441
        /* cleanup our temporary memory */
 
3442
        apr_brigade_destroy(intern->tmp_bb);
 
3443
        apr_pool_destroy(ctx->dpool);
 
3444
 
 
3445
        /* don't forget to finally insert the EOS bucket */
 
3446
        APR_BRIGADE_INSERT_TAIL(pass_bb, b);
 
3447
    }
 
3448
 
 
3449
    /* if something's left over, pass it along */
 
3450
    if (!APR_BRIGADE_EMPTY(pass_bb)) {
 
3451
        rv = ap_pass_brigade(f->next, pass_bb);
 
3452
    }
 
3453
    else {
 
3454
        rv = APR_SUCCESS;
 
3455
        apr_brigade_destroy(pass_bb);
 
3456
    }
 
3457
    return rv;
 
3458
}
 
3459
 
 
3460
 
 
3461
/*
 
3462
 * +-------------------------------------------------------+
 
3463
 * |                                                       |
 
3464
 * |                     Runtime Hooks
 
3465
 * |                                                       |
 
3466
 * +-------------------------------------------------------+
 
3467
 */
 
3468
 
 
3469
static int includes_setup(ap_filter_t *f)
 
3470
{
 
3471
    include_dir_config *conf = ap_get_module_config(f->r->per_dir_config,
 
3472
                                                    &include_module);
 
3473
 
 
3474
    /* When our xbithack value isn't set to full or our platform isn't
 
3475
     * providing group-level protection bits or our group-level bits do not
 
3476
     * have group-execite on, we will set the no_local_copy value to 1 so
 
3477
     * that we will not send 304s.
 
3478
     */
 
3479
    if ((conf->xbithack != XBITHACK_FULL)
 
3480
        || !(f->r->finfo.valid & APR_FINFO_GPROT)
 
3481
        || !(f->r->finfo.protection & APR_GEXECUTE)) {
 
3482
        f->r->no_local_copy = 1;
 
3483
    }
 
3484
 
 
3485
    /* Don't allow ETag headers to be generated - see RFC2616 - 13.3.4.
 
3486
     * We don't know if we are going to be including a file or executing
 
3487
     * a program - in either case a strong ETag header will likely be invalid.
 
3488
     */
 
3489
    apr_table_setn(f->r->notes, "no-etag", "");
 
3490
 
 
3491
    return OK;
 
3492
}
 
3493
 
 
3494
static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
 
3495
{
 
3496
    request_rec *r = f->r;
 
3497
    include_ctx_t *ctx = f->ctx;
 
3498
    request_rec *parent;
 
3499
    include_dir_config *conf = ap_get_module_config(r->per_dir_config,
 
3500
                                                    &include_module);
 
3501
 
 
3502
    include_server_config *sconf= ap_get_module_config(r->server->module_config,
 
3503
                                                       &include_module);
 
3504
 
 
3505
    if (!(ap_allow_options(r) & OPT_INCLUDES)) {
 
3506
        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
 
3507
                      "mod_include: Options +Includes (or IncludesNoExec) "
 
3508
                      "wasn't set, INCLUDES filter removed");
 
3509
        ap_remove_output_filter(f);
 
3510
        return ap_pass_brigade(f->next, b);
 
3511
    }
 
3512
 
 
3513
    if (!f->ctx) {
 
3514
        struct ssi_internal_ctx *intern;
 
3515
 
 
3516
        /* create context for this filter */
 
3517
        f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx));
 
3518
        ctx->intern = intern = apr_palloc(r->pool, sizeof(*ctx->intern));
 
3519
        ctx->pool = r->pool;
 
3520
        apr_pool_create(&ctx->dpool, ctx->pool);
 
3521
 
 
3522
        /* runtime data */
 
3523
        intern->tmp_bb = apr_brigade_create(ctx->pool, f->c->bucket_alloc);
 
3524
        intern->seen_eos = 0;
 
3525
        intern->state = PARSE_PRE_HEAD;
 
3526
        ctx->flags = (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
 
3527
        if (ap_allow_options(r) & OPT_INCNOEXEC) {
 
3528
            ctx->flags |= SSI_FLAG_NO_EXEC;
 
3529
        }
 
3530
 
 
3531
        ctx->if_nesting_level = 0;
 
3532
        intern->re = NULL;
 
3533
 
 
3534
        ctx->error_str = conf->default_error_msg;
 
3535
        ctx->time_str = conf->default_time_fmt;
 
3536
        intern->start_seq  = sconf->default_start_tag;
 
3537
        intern->start_seq_pat = bndm_compile(ctx->pool, intern->start_seq,
 
3538
                                             strlen(intern->start_seq));
 
3539
        intern->end_seq = sconf->default_end_tag;
 
3540
        intern->end_seq_len = strlen(intern->end_seq);
 
3541
        intern->undefined_echo = conf->undefined_echo;
 
3542
        intern->undefined_echo_len = strlen(conf->undefined_echo);
 
3543
    }
 
3544
 
 
3545
    if ((parent = ap_get_module_config(r->request_config, &include_module))) {
 
3546
        /* Kludge --- for nested includes, we want to keep the subprocess
 
3547
         * environment of the base document (for compatibility); that means
 
3548
         * torquing our own last_modified date as well so that the
 
3549
         * LAST_MODIFIED variable gets reset to the proper value if the
 
3550
         * nested document resets <!--#config timefmt -->.
 
3551
         */
 
3552
        r->subprocess_env = r->main->subprocess_env;
 
3553
        apr_pool_join(r->main->pool, r->pool);
 
3554
        r->finfo.mtime = r->main->finfo.mtime;
 
3555
    }
 
3556
    else {
 
3557
        /* we're not a nested include, so we create an initial
 
3558
         * environment */
 
3559
        ap_add_common_vars(r);
 
3560
        ap_add_cgi_vars(r);
 
3561
        add_include_vars(r, conf->default_time_fmt);
 
3562
    }
 
3563
    /* Always unset the content-length.  There is no way to know if
 
3564
     * the content will be modified at some point by send_parsed_content.
 
3565
     * It is very possible for us to not find any content in the first
 
3566
     * 9k of the file, but still have to modify the content of the file.
 
3567
     * If we are going to pass the file through send_parsed_content, then
 
3568
     * the content-length should just be unset.
 
3569
     */
 
3570
    apr_table_unset(f->r->headers_out, "Content-Length");
 
3571
 
 
3572
    /* Always unset the Last-Modified field - see RFC2616 - 13.3.4.
 
3573
     * We don't know if we are going to be including a file or executing
 
3574
     * a program which may change the Last-Modified header or make the
 
3575
     * content completely dynamic.  Therefore, we can't support these
 
3576
     * headers.
 
3577
     * Exception: XBitHack full means we *should* set the Last-Modified field.
 
3578
     */
 
3579
 
 
3580
    /* Assure the platform supports Group protections */
 
3581
    if ((conf->xbithack == XBITHACK_FULL)
 
3582
        && (r->finfo.valid & APR_FINFO_GPROT)
 
3583
        && (r->finfo.protection & APR_GEXECUTE)) {
 
3584
        ap_update_mtime(r, r->finfo.mtime);
 
3585
        ap_set_last_modified(r);
 
3586
    }
 
3587
    else {
 
3588
        apr_table_unset(f->r->headers_out, "Last-Modified");
 
3589
    }
 
3590
 
 
3591
    /* add QUERY stuff to env cause it ain't yet */
 
3592
    if (r->args) {
 
3593
        char *arg_copy = apr_pstrdup(r->pool, r->args);
 
3594
 
 
3595
        apr_table_setn(r->subprocess_env, "QUERY_STRING", r->args);
 
3596
        ap_unescape_url(arg_copy);
 
3597
        apr_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED",
 
3598
                  ap_escape_shell_cmd(r->pool, arg_copy));
 
3599
    }
 
3600
 
 
3601
    return send_parsed_content(f, b);
 
3602
}
 
3603
 
 
3604
static int include_fixup(request_rec *r)
 
3605
{
 
3606
    include_dir_config *conf;
 
3607
 
 
3608
    conf = ap_get_module_config(r->per_dir_config, &include_module);
 
3609
 
 
3610
    if (r->handler && (strcmp(r->handler, "server-parsed") == 0))
 
3611
    {
 
3612
        if (!r->content_type || !*r->content_type) {
 
3613
            ap_set_content_type(r, "text/html");
 
3614
        }
 
3615
        r->handler = "default-handler";
 
3616
    }
 
3617
    else
 
3618
#if defined(OS2) || defined(WIN32) || defined(NETWARE)
 
3619
    /* These OS's don't support xbithack. This is being worked on. */
 
3620
    {
 
3621
        return DECLINED;
 
3622
    }
 
3623
#else
 
3624
    {
 
3625
        if (conf->xbithack == XBITHACK_OFF) {
 
3626
            return DECLINED;
 
3627
        }
 
3628
 
 
3629
        if (!(r->finfo.protection & APR_UEXECUTE)) {
 
3630
            return DECLINED;
 
3631
        }
 
3632
 
 
3633
        if (!r->content_type || strcmp(r->content_type, "text/html")) {
 
3634
            return DECLINED;
 
3635
        }
 
3636
    }
 
3637
#endif
 
3638
 
 
3639
    /* We always return declined, because the default handler actually
 
3640
     * serves the file.  All we have to do is add the filter.
 
3641
     */
 
3642
    ap_add_output_filter("INCLUDES", NULL, r, r->connection);
 
3643
    return DECLINED;
 
3644
}
 
3645
 
 
3646
 
 
3647
/*
 
3648
 * +-------------------------------------------------------+
 
3649
 * |                                                       |
 
3650
 * |                Configuration Handling
 
3651
 * |                                                       |
 
3652
 * +-------------------------------------------------------+
 
3653
 */
 
3654
 
 
3655
static void *create_includes_dir_config(apr_pool_t *p, char *dummy)
 
3656
{
 
3657
    include_dir_config *result = apr_palloc(p, sizeof(include_dir_config));
 
3658
 
 
3659
    result->default_error_msg = DEFAULT_ERROR_MSG;
 
3660
    result->default_time_fmt  = DEFAULT_TIME_FORMAT;
 
3661
    result->undefined_echo    = DEFAULT_UNDEFINED_ECHO;
 
3662
    result->xbithack          = DEFAULT_XBITHACK;
 
3663
 
 
3664
    return result;
 
3665
}
 
3666
 
 
3667
static void *create_includes_server_config(apr_pool_t *p, server_rec *server)
 
3668
{
 
3669
    include_server_config *result;
 
3670
 
 
3671
    result = apr_palloc(p, sizeof(include_server_config));
 
3672
    result->default_end_tag    = DEFAULT_END_SEQUENCE;
 
3673
    result->default_start_tag  = DEFAULT_START_SEQUENCE;
 
3674
 
 
3675
    return result;
 
3676
}
 
3677
 
 
3678
static const char *set_xbithack(cmd_parms *cmd, void *mconfig, const char *arg)
 
3679
{
 
3680
    include_dir_config *conf = mconfig;
 
3681
 
 
3682
    if (!strcasecmp(arg, "off")) {
 
3683
        conf->xbithack = XBITHACK_OFF;
 
3684
    }
 
3685
    else if (!strcasecmp(arg, "on")) {
 
3686
        conf->xbithack = XBITHACK_ON;
 
3687
    }
 
3688
    else if (!strcasecmp(arg, "full")) {
 
3689
        conf->xbithack = XBITHACK_FULL;
 
3690
    }
 
3691
    else {
 
3692
        return "XBitHack must be set to Off, On, or Full";
 
3693
    }
 
3694
 
 
3695
    return NULL;
 
3696
}
 
3697
 
 
3698
static const char *set_default_start_tag(cmd_parms *cmd, void *mconfig,
 
3699
                                         const char *tag)
 
3700
{
 
3701
    include_server_config *conf;
 
3702
    const char *p = tag;
 
3703
 
 
3704
    /* be consistent. (See below in set_default_end_tag) */
 
3705
    while (*p) {
 
3706
        if (apr_isspace(*p)) {
 
3707
            return "SSIStartTag may not contain any whitespaces";
 
3708
        }
 
3709
        ++p;
 
3710
    }
 
3711
 
 
3712
    conf= ap_get_module_config(cmd->server->module_config , &include_module);
 
3713
    conf->default_start_tag = tag;
 
3714
 
 
3715
    return NULL;
 
3716
}
 
3717
 
 
3718
static const char *set_default_end_tag(cmd_parms *cmd, void *mconfig,
 
3719
                                       const char *tag)
 
3720
{
 
3721
    include_server_config *conf;
 
3722
    const char *p = tag;
 
3723
 
 
3724
    /* sanity check. The parser may fail otherwise */
 
3725
    while (*p) {
 
3726
        if (apr_isspace(*p)) {
 
3727
            return "SSIEndTag may not contain any whitespaces";
 
3728
        }
 
3729
        ++p;
 
3730
    }
 
3731
 
 
3732
    conf= ap_get_module_config(cmd->server->module_config , &include_module);
 
3733
    conf->default_end_tag = tag;
 
3734
 
 
3735
    return NULL;
 
3736
}
 
3737
 
 
3738
static const char *set_undefined_echo(cmd_parms *cmd, void *mconfig,
 
3739
                                      const char *msg)
 
3740
{
 
3741
    include_dir_config *conf = mconfig;
 
3742
    conf->undefined_echo = msg;
 
3743
 
 
3744
    return NULL;
 
3745
}
 
3746
 
 
3747
static const char *set_default_error_msg(cmd_parms *cmd, void *mconfig,
 
3748
                                         const char *msg)
 
3749
{
 
3750
    include_dir_config *conf = mconfig;
 
3751
    conf->default_error_msg = msg;
 
3752
 
 
3753
    return NULL;
 
3754
}
 
3755
 
 
3756
static const char *set_default_time_fmt(cmd_parms *cmd, void *mconfig,
 
3757
                                        const char *fmt)
 
3758
{
 
3759
    include_dir_config *conf = mconfig;
 
3760
    conf->default_time_fmt = fmt;
 
3761
 
 
3762
    return NULL;
 
3763
}
 
3764
 
 
3765
 
 
3766
/*
 
3767
 * +-------------------------------------------------------+
 
3768
 * |                                                       |
 
3769
 * |        Module Initialization and Configuration
 
3770
 * |                                                       |
 
3771
 * +-------------------------------------------------------+
 
3772
 */
 
3773
 
 
3774
static int include_post_config(apr_pool_t *p, apr_pool_t *plog,
 
3775
                                apr_pool_t *ptemp, server_rec *s)
 
3776
{
 
3777
    include_handlers = apr_hash_make(p);
 
3778
 
 
3779
    ssi_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
 
3780
 
 
3781
    if(ssi_pfn_register) {
 
3782
        ssi_pfn_register("if", handle_if);
 
3783
        ssi_pfn_register("set", handle_set);
 
3784
        ssi_pfn_register("else", handle_else);
 
3785
        ssi_pfn_register("elif", handle_elif);
 
3786
        ssi_pfn_register("echo", handle_echo);
 
3787
        ssi_pfn_register("endif", handle_endif);
 
3788
        ssi_pfn_register("fsize", handle_fsize);
 
3789
        ssi_pfn_register("config", handle_config);
 
3790
        ssi_pfn_register("include", handle_include);
 
3791
        ssi_pfn_register("flastmod", handle_flastmod);
 
3792
        ssi_pfn_register("printenv", handle_printenv);
 
3793
    }
 
3794
 
 
3795
    return OK;
 
3796
}
 
3797
 
 
3798
static const command_rec includes_cmds[] =
 
3799
{
 
3800
    AP_INIT_TAKE1("XBitHack", set_xbithack, NULL, OR_OPTIONS,
 
3801
                  "Off, On, or Full"),
 
3802
    AP_INIT_TAKE1("SSIErrorMsg", set_default_error_msg, NULL, OR_ALL,
 
3803
                  "a string"),
 
3804
    AP_INIT_TAKE1("SSITimeFormat", set_default_time_fmt, NULL, OR_ALL,
 
3805
                  "a strftime(3) formatted string"),
 
3806
    AP_INIT_TAKE1("SSIStartTag", set_default_start_tag, NULL, RSRC_CONF,
 
3807
                  "SSI Start String Tag"),
 
3808
    AP_INIT_TAKE1("SSIEndTag", set_default_end_tag, NULL, RSRC_CONF,
 
3809
                  "SSI End String Tag"),
 
3810
    AP_INIT_TAKE1("SSIUndefinedEcho", set_undefined_echo, NULL, OR_ALL,
 
3811
                  "String to be displayed if an echoed variable is undefined"),
 
3812
    {NULL}
 
3813
};
 
3814
 
 
3815
static void ap_register_include_handler(char *tag, include_handler_fn_t *func)
 
3816
{
 
3817
    apr_hash_set(include_handlers, tag, strlen(tag), (const void *)func);
 
3818
}
 
3819
 
 
3820
static void register_hooks(apr_pool_t *p)
 
3821
{
 
3822
    APR_REGISTER_OPTIONAL_FN(ap_ssi_get_tag_and_value);
 
3823
    APR_REGISTER_OPTIONAL_FN(ap_ssi_parse_string);
 
3824
    APR_REGISTER_OPTIONAL_FN(ap_register_include_handler);
 
3825
    ap_hook_post_config(include_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
 
3826
    ap_hook_fixups(include_fixup, NULL, NULL, APR_HOOK_LAST);
 
3827
    ap_register_output_filter("INCLUDES", includes_filter, includes_setup,
 
3828
                              AP_FTYPE_RESOURCE);
 
3829
}
 
3830
 
 
3831
module AP_MODULE_DECLARE_DATA include_module =
 
3832
{
 
3833
    STANDARD20_MODULE_STUFF,
 
3834
    create_includes_dir_config,   /* dir config creater */
 
3835
    NULL,                         /* dir merger --- default is to override */
 
3836
    create_includes_server_config,/* server config */
 
3837
    NULL,                         /* merge server config */
 
3838
    includes_cmds,                /* command apr_table_t */
 
3839
    register_hooks                /* register hooks */
 
3840
};