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 /*
18 * mod_substitute.c: Perform content rewriting on the fly
19 */
20
21 #include "httpd.h"
22 #include "http_config.h"
23 #include "http_core.h"
24 #include "http_log.h"
25 #include "apr_general.h"
26 #include "apr_strings.h"
27 #include "apr_strmatch.h"
28 #include "apr_lib.h"
29 #include "util_filter.h"
30 #include "util_varbuf.h"
31 #include "apr_buckets.h"
32 #include "http_request.h"
33 #define APR_WANT_STRFUNC
34 #include "apr_want.h"
35
36 /*
37 * We want to limit the memory usage in a way that is predictable.
38 * Therefore we limit the resulting length of the line.
39 * This is the default value.
40 */
41 #define AP_SUBST_MAX_LINE_LENGTH (1024*1024)
42
43 static const char substitute_filter_name[] = "SUBSTITUTE";
44
45 module AP_MODULE_DECLARE_DATA substitute_module;
46
47 typedef struct subst_pattern_t {
48 const apr_strmatch_pattern *pattern;
49 const ap_regex_t *regexp;
50 const char *replacement;
51 apr_size_t replen;
52 apr_size_t patlen;
53 int flatten;
54 const char *from;
55 } subst_pattern_t;
56
57 typedef struct {
58 apr_array_header_t *patterns;
59 apr_size_t max_line_length;
60 int max_line_length_set;
61 int inherit_before;
62 } subst_dir_conf;
63
64 typedef struct {
65 apr_bucket_brigade *linebb;
66 apr_bucket_brigade *linesbb;
67 apr_bucket_brigade *passbb;
68 apr_bucket_brigade *pattbb;
69 apr_pool_t *tpool;
70 } substitute_module_ctx;
71
create_substitute_dcfg(apr_pool_t * p,char * d)72 static void *create_substitute_dcfg(apr_pool_t *p, char *d)
73 {
74 subst_dir_conf *dcfg =
75 (subst_dir_conf *) apr_palloc(p, sizeof(subst_dir_conf));
76
77 dcfg->patterns = apr_array_make(p, 10, sizeof(subst_pattern_t));
78 dcfg->max_line_length = AP_SUBST_MAX_LINE_LENGTH;
79 dcfg->max_line_length_set = 0;
80 dcfg->inherit_before = -1;
81 return dcfg;
82 }
83
merge_substitute_dcfg(apr_pool_t * p,void * basev,void * overv)84 static void *merge_substitute_dcfg(apr_pool_t *p, void *basev, void *overv)
85 {
86 subst_dir_conf *a =
87 (subst_dir_conf *) apr_palloc(p, sizeof(subst_dir_conf));
88 subst_dir_conf *base = (subst_dir_conf *) basev;
89 subst_dir_conf *over = (subst_dir_conf *) overv;
90
91 a->inherit_before = (over->inherit_before != -1)
92 ? over->inherit_before
93 : base->inherit_before;
94 /* SubstituteInheritBefore wasn't the default behavior until 2.5.x,
95 * and may be re-disabled as desired; the original default behavior
96 * was to apply inherited subst patterns after locally scoped patterns.
97 * In later 2.2 and 2.4 versions, SubstituteInheritBefore may be toggled
98 * 'on' to follow the corrected/expected behavior, without violating POLS.
99 */
100 if (a->inherit_before == 1) {
101 a->patterns = apr_array_append(p, base->patterns,
102 over->patterns);
103 }
104 else {
105 a->patterns = apr_array_append(p, over->patterns,
106 base->patterns);
107 }
108 a->max_line_length = over->max_line_length_set ?
109 over->max_line_length : base->max_line_length;
110 a->max_line_length_set = over->max_line_length_set
111 | base->max_line_length_set;
112 return a;
113 }
114
115 #define AP_MAX_BUCKETS 1000
116
117 #define SEDRMPATBCKT(b, offset, tmp_b, patlen) do { \
118 apr_bucket_split(b, offset); \
119 tmp_b = APR_BUCKET_NEXT(b); \
120 apr_bucket_split(tmp_b, patlen); \
121 b = APR_BUCKET_NEXT(tmp_b); \
122 apr_bucket_delete(tmp_b); \
123 } while (0)
124
125 #define CAP2LINEMAX(n) ((n) < (apr_size_t)200 ? (int)(n) : 200)
126
do_pattmatch(ap_filter_t * f,apr_bucket * inb,apr_bucket_brigade * mybb,apr_pool_t * pool)127 static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
128 apr_bucket_brigade *mybb,
129 apr_pool_t *pool)
130 {
131 int i;
132 int force_quick = 0;
133 ap_regmatch_t regm[AP_MAX_REG_MATCH];
134 apr_size_t bytes;
135 apr_size_t len;
136 const char *buff;
137 struct ap_varbuf vb;
138 apr_bucket *b;
139 apr_bucket *tmp_b;
140
141 subst_dir_conf *cfg =
142 (subst_dir_conf *) ap_get_module_config(f->r->per_dir_config,
143 &substitute_module);
144 subst_pattern_t *script;
145
146 APR_BRIGADE_INSERT_TAIL(mybb, inb);
147 ap_varbuf_init(pool, &vb, 0);
148
149 script = (subst_pattern_t *) cfg->patterns->elts;
150 /*
151 * Simple optimization. If we only have one pattern, then
152 * we can safely avoid the overhead of flattening
153 */
154 if (cfg->patterns->nelts == 1) {
155 force_quick = 1;
156 }
157 for (i = 0; i < cfg->patterns->nelts; i++) {
158 for (b = APR_BRIGADE_FIRST(mybb);
159 b != APR_BRIGADE_SENTINEL(mybb);
160 b = APR_BUCKET_NEXT(b)) {
161 if (APR_BUCKET_IS_METADATA(b)) {
162 /*
163 * we should NEVER see this, because we should never
164 * be passed any, but "handle" it just in case.
165 */
166 continue;
167 }
168 if (apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ)
169 == APR_SUCCESS) {
170 int have_match = 0;
171
172 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
173 "Line read (%" APR_SIZE_T_FMT " bytes): %.*s",
174 bytes, CAP2LINEMAX(bytes), buff);
175 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
176 "Replacing %s:'%s' by '%s'",
177 script->pattern ? "string" :
178 script->regexp ? "regex" :
179 "unknown",
180 script->from, script->replacement);
181
182 vb.strlen = 0;
183 if (script->pattern) {
184 const char *repl;
185 /*
186 * space_left counts how many bytes we have left until the
187 * line length reaches max_line_length.
188 */
189 apr_size_t space_left = cfg->max_line_length;
190 apr_size_t repl_len = strlen(script->replacement);
191 while ((repl = apr_strmatch(script->pattern, buff, bytes)))
192 {
193 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
194 "Matching found, result: '%s'",
195 script->replacement);
196 have_match = 1;
197 /* get offset into buff for pattern */
198 len = (apr_size_t) (repl - buff);
199 if (script->flatten && !force_quick) {
200 /*
201 * We are flattening the buckets here, meaning
202 * that we don't do the fast bucket splits.
203 * Instead we copy over what the buckets would
204 * contain and use them. This is slow, since we
205 * are constanting allocing space and copying
206 * strings.
207 */
208 if (vb.strlen + len + repl_len > cfg->max_line_length)
209 return APR_ENOMEM;
210 ap_varbuf_strmemcat(&vb, buff, len);
211 ap_varbuf_strmemcat(&vb, script->replacement, repl_len);
212 }
213 else {
214 /*
215 * The string before the match but after the
216 * previous match (if any) has length 'len'.
217 * Check if we still have space for this string and
218 * the replacement string.
219 */
220 if (space_left < len + repl_len)
221 return APR_ENOMEM;
222 space_left -= len + repl_len;
223 /*
224 * We now split off the string before the match
225 * as its own bucket, then isolate the matched
226 * string and delete it.
227 */
228 SEDRMPATBCKT(b, len, tmp_b, script->patlen);
229 /*
230 * Finally, we create a bucket that contains the
231 * replacement...
232 */
233 tmp_b = apr_bucket_transient_create(script->replacement,
234 script->replen,
235 f->r->connection->bucket_alloc);
236 /* ... and insert it */
237 APR_BUCKET_INSERT_BEFORE(b, tmp_b);
238 }
239 /* now we need to adjust buff for all these changes */
240 len += script->patlen;
241 bytes -= len;
242 buff += len;
243 }
244 if (have_match) {
245 if (script->flatten && !force_quick) {
246 /* XXX: we should check for AP_MAX_BUCKETS here and
247 * XXX: call ap_pass_brigade accordingly
248 */
249 char *copy = ap_varbuf_pdup(pool, &vb, NULL, 0,
250 buff, bytes, &len);
251 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
252 "New line (%" APR_SIZE_T_FMT " bytes): %.*s",
253 len, CAP2LINEMAX(len), copy);
254 tmp_b = apr_bucket_pool_create(copy, len, pool,
255 f->r->connection->bucket_alloc);
256 APR_BUCKET_INSERT_BEFORE(b, tmp_b);
257 apr_bucket_delete(b);
258 b = tmp_b;
259 }
260 else {
261 /*
262 * We want the behaviour to be predictable.
263 * Therefore we try to always error out if the
264 * line length is larger than the limit,
265 * regardless of the content of the line. So,
266 * let's check if the remaining non-matching
267 * string does not exceed the limit.
268 */
269 if (space_left < b->length)
270 return APR_ENOMEM;
271 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
272 "New line (%" APR_SIZE_T_FMT " bytes): %.*s",
273 bytes, CAP2LINEMAX(bytes), buff);
274 }
275 }
276 }
277 else if (script->regexp) {
278 int left = bytes;
279 const char *pos = buff;
280 char *repl;
281 apr_size_t space_left = cfg->max_line_length;
282 while (!ap_regexec_len(script->regexp, pos, left,
283 AP_MAX_REG_MATCH, regm, 0)) {
284 apr_status_t rv;
285 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
286 "Matching found");
287 have_match = 1;
288 if (script->flatten && !force_quick) {
289 /* check remaining buffer size */
290 /* Note that the last param in ap_varbuf_regsub below
291 * must stay positive. If it gets 0, it would mean
292 * unlimited space available. */
293 if (vb.strlen + regm[0].rm_so >= cfg->max_line_length)
294 return APR_ENOMEM;
295 /* copy bytes before the match */
296 if (regm[0].rm_so > 0)
297 ap_varbuf_strmemcat(&vb, pos, regm[0].rm_so);
298 /* add replacement string, last argument is unsigned! */
299 rv = ap_varbuf_regsub(&vb, script->replacement, pos,
300 AP_MAX_REG_MATCH, regm,
301 cfg->max_line_length - vb.strlen);
302 if (rv != APR_SUCCESS)
303 return rv;
304 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
305 "Result: '%s'", vb.buf);
306 }
307 else {
308 apr_size_t repl_len;
309 /* account for string before the match */
310 if (space_left <= regm[0].rm_so)
311 return APR_ENOMEM;
312 space_left -= regm[0].rm_so;
313 rv = ap_pregsub_ex(pool, &repl,
314 script->replacement, pos,
315 AP_MAX_REG_MATCH, regm,
316 space_left);
317 if (rv != APR_SUCCESS)
318 return rv;
319 repl_len = strlen(repl);
320 space_left -= repl_len;
321 len = (apr_size_t) (regm[0].rm_eo - regm[0].rm_so);
322 SEDRMPATBCKT(b, regm[0].rm_so, tmp_b, len);
323 tmp_b = apr_bucket_transient_create(repl, repl_len,
324 f->r->connection->bucket_alloc);
325 APR_BUCKET_INSERT_BEFORE(b, tmp_b);
326 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
327 "Result: '%s'", repl);
328 }
329 /*
330 * reset to past what we just did. pos now maps to b
331 * again
332 */
333 pos += regm[0].rm_eo;
334 left -= regm[0].rm_eo;
335 }
336 if (have_match && script->flatten && !force_quick) {
337 char *copy;
338 /* Copy result plus the part after the last match into
339 * a bucket.
340 */
341 copy = ap_varbuf_pdup(pool, &vb, NULL, 0, pos, left,
342 &len);
343 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
344 "New line (%" APR_SIZE_T_FMT " bytes): %.*s",
345 len, CAP2LINEMAX(len), copy);
346 tmp_b = apr_bucket_pool_create(copy, len, pool,
347 f->r->connection->bucket_alloc);
348 APR_BUCKET_INSERT_BEFORE(b, tmp_b);
349 apr_bucket_delete(b);
350 b = tmp_b;
351 }
352 }
353 else {
354 ap_assert(0);
355 continue;
356 }
357 }
358 }
359 script++;
360 }
361 ap_varbuf_free(&vb);
362 return APR_SUCCESS;
363 }
364
substitute_filter(ap_filter_t * f,apr_bucket_brigade * bb)365 static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb)
366 {
367 apr_size_t bytes;
368 apr_size_t len;
369 apr_size_t fbytes;
370 const char *buff;
371 const char *nl = NULL;
372 char *bflat;
373 apr_bucket *b;
374 apr_bucket *tmp_b;
375 apr_bucket_brigade *tmp_bb = NULL;
376 apr_status_t rv;
377 subst_dir_conf *cfg =
378 (subst_dir_conf *) ap_get_module_config(f->r->per_dir_config,
379 &substitute_module);
380
381 substitute_module_ctx *ctx = f->ctx;
382
383 /*
384 * First time around? Create the saved bb that we used for each pass
385 * through. Note that we can also get here when we explicitly clear ctx,
386 * for error handling
387 */
388 if (!ctx) {
389 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
390 /*
391 * Create all the temporary brigades we need and reuse them to avoid
392 * creating them over and over again from r->pool which would cost a
393 * lot of memory in some cases.
394 */
395 ctx->linebb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
396 ctx->linesbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
397 ctx->pattbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
398 /*
399 * Everything to be passed to the next filter goes in
400 * here, our pass brigade.
401 */
402 ctx->passbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
403 /* Create our temporary pool only once */
404 apr_pool_create(&(ctx->tpool), f->r->pool);
405 apr_pool_tag(ctx->tpool, "substitute_tpool");
406 apr_table_unset(f->r->headers_out, "Content-Length");
407 }
408
409 /*
410 * Shortcircuit processing
411 */
412 if (APR_BRIGADE_EMPTY(bb))
413 return APR_SUCCESS;
414
415 /*
416 * Here's the concept:
417 * Read in the data and look for newlines. Once we
418 * find a full "line", add it to our working brigade.
419 * If we've finished reading the brigade and we have
420 * any left over data (not a "full" line), store that
421 * for the next pass.
422 *
423 * Note: anything stored in ctx->linebb for sure does not have
424 * a newline char, so we don't concat that bb with the
425 * new bb, since we would spending time searching for the newline
426 * in data we know it doesn't exist. So instead, we simply scan
427 * our current bb and, if we see a newline, prepend ctx->linebb
428 * to the front of it. This makes the code much less straight-
429 * forward (otherwise we could APR_BRIGADE_CONCAT(ctx->linebb, bb)
430 * and just scan for newlines and not bother with needing to know
431 * when ctx->linebb needs to be reset) but also faster. We'll take
432 * the speed.
433 *
434 * Note: apr_brigade_split_line would be nice here, but we
435 * really can't use it since we need more control and we want
436 * to re-use already read bucket data.
437 *
438 * See mod_include if still confused :)
439 */
440
441 while ((b = APR_BRIGADE_FIRST(bb)) && (b != APR_BRIGADE_SENTINEL(bb))) {
442 if (APR_BUCKET_IS_EOS(b)) {
443 /*
444 * if we see the EOS, then we need to pass along everything we
445 * have. But if the ctx->linebb isn't empty, then we need to add
446 * that to the end of what we'll be passing.
447 */
448 if (!APR_BRIGADE_EMPTY(ctx->linebb)) {
449 rv = apr_brigade_pflatten(ctx->linebb, &bflat,
450 &fbytes, ctx->tpool);
451 if (rv != APR_SUCCESS)
452 goto err;
453 if (fbytes > cfg->max_line_length) {
454 rv = APR_ENOMEM;
455 goto err;
456 }
457 tmp_b = apr_bucket_transient_create(bflat, fbytes,
458 f->r->connection->bucket_alloc);
459 rv = do_pattmatch(f, tmp_b, ctx->pattbb, ctx->tpool);
460 if (rv != APR_SUCCESS)
461 goto err;
462 APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb);
463 apr_brigade_cleanup(ctx->linebb);
464 }
465 APR_BUCKET_REMOVE(b);
466 APR_BRIGADE_INSERT_TAIL(ctx->passbb, b);
467 }
468 /*
469 * No need to handle FLUSH buckets separately as we call
470 * ap_pass_brigade anyway at the end of the loop.
471 */
472 else if (APR_BUCKET_IS_METADATA(b)) {
473 APR_BUCKET_REMOVE(b);
474 APR_BRIGADE_INSERT_TAIL(ctx->passbb, b);
475 }
476 else {
477 /*
478 * We have actual "data" so read in as much as we can and start
479 * scanning and splitting from our read buffer
480 */
481 rv = apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ);
482 if (rv != APR_SUCCESS || bytes == 0) {
483 apr_bucket_delete(b);
484 }
485 else {
486 int num = 0;
487 while (bytes > 0) {
488 nl = memchr(buff, '\n', bytes);
489 if (nl) {
490 len = (apr_size_t) (nl - buff) + 1;
491 /* split *after* the newline */
492 apr_bucket_split(b, len);
493 /*
494 * We've likely read more data, so bypass rereading
495 * bucket data and continue scanning through this
496 * buffer
497 */
498 bytes -= len;
499 buff += len;
500 /*
501 * we need b to be updated for future potential
502 * splitting
503 */
504 tmp_b = APR_BUCKET_NEXT(b);
505 APR_BUCKET_REMOVE(b);
506 /*
507 * Hey, we found a newline! Don't forget the old
508 * stuff that needs to be added to the front. So we
509 * add the split bucket to the end, flatten the whole
510 * bb, morph the whole shebang into a bucket which is
511 * then added to the tail of the newline bb.
512 */
513 if (!APR_BRIGADE_EMPTY(ctx->linebb)) {
514 APR_BRIGADE_INSERT_TAIL(ctx->linebb, b);
515 rv = apr_brigade_pflatten(ctx->linebb, &bflat,
516 &fbytes, ctx->tpool);
517 if (rv != APR_SUCCESS)
518 goto err;
519 if (fbytes > cfg->max_line_length) {
520 /* Avoid pflattening further lines, we will
521 * abort later on anyway.
522 */
523 rv = APR_ENOMEM;
524 goto err;
525 }
526 b = apr_bucket_transient_create(bflat, fbytes,
527 f->r->connection->bucket_alloc);
528 apr_brigade_cleanup(ctx->linebb);
529 }
530 rv = do_pattmatch(f, b, ctx->pattbb, ctx->tpool);
531 if (rv != APR_SUCCESS)
532 goto err;
533 /*
534 * Count how many buckets we have in ctx->passbb
535 * so far. Yes, this is correct we count ctx->passbb
536 * and not ctx->pattbb as we do not reset num on every
537 * iteration.
538 */
539 for (b = APR_BRIGADE_FIRST(ctx->pattbb);
540 b != APR_BRIGADE_SENTINEL(ctx->pattbb);
541 b = APR_BUCKET_NEXT(b)) {
542 num++;
543 }
544 APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb);
545 /*
546 * If the number of buckets in ctx->passbb reaches an
547 * "insane" level, we consume much memory for all the
548 * buckets as such. So lets flush them down the chain
549 * in this case and thus clear ctx->passbb. This frees
550 * the buckets memory for further processing.
551 * Usually this condition should not become true, but
552 * it is a safety measure for edge cases.
553 */
554 if (num > AP_MAX_BUCKETS) {
555 b = apr_bucket_flush_create(
556 f->r->connection->bucket_alloc);
557 APR_BRIGADE_INSERT_TAIL(ctx->passbb, b);
558 rv = ap_pass_brigade(f->next, ctx->passbb);
559 apr_brigade_cleanup(ctx->passbb);
560 num = 0;
561 apr_pool_clear(ctx->tpool);
562 if (rv != APR_SUCCESS)
563 goto err;
564 }
565 b = tmp_b;
566 }
567 else {
568 /*
569 * no newline in whatever is left of this buffer so
570 * tuck data away and get next bucket
571 */
572 APR_BUCKET_REMOVE(b);
573 APR_BRIGADE_INSERT_TAIL(ctx->linebb, b);
574 bytes = 0;
575 }
576 }
577 }
578 }
579 if (!APR_BRIGADE_EMPTY(ctx->passbb)) {
580 rv = ap_pass_brigade(f->next, ctx->passbb);
581 apr_brigade_cleanup(ctx->passbb);
582 if (rv != APR_SUCCESS)
583 goto err;
584 }
585 apr_pool_clear(ctx->tpool);
586 }
587
588 /* Anything left we want to save/setaside for the next go-around */
589 if (!APR_BRIGADE_EMPTY(ctx->linebb)) {
590 /*
591 * Provide ap_save_brigade with an existing empty brigade
592 * (ctx->linesbb) to avoid creating a new one.
593 */
594 ap_save_brigade(f, &(ctx->linesbb), &(ctx->linebb), f->r->pool);
595 tmp_bb = ctx->linebb;
596 ctx->linebb = ctx->linesbb;
597 ctx->linesbb = tmp_bb;
598 }
599
600 return APR_SUCCESS;
601 err:
602 if (rv == APR_ENOMEM)
603 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01328) "Line too long, URI %s",
604 f->r->uri);
605 apr_pool_clear(ctx->tpool);
606 return rv;
607 }
608
set_pattern(cmd_parms * cmd,void * cfg,const char * line)609 static const char *set_pattern(cmd_parms *cmd, void *cfg, const char *line)
610 {
611 char *from = NULL;
612 char *to = NULL;
613 char *flags = NULL;
614 char *ourline;
615 char delim;
616 subst_pattern_t *nscript;
617 int is_pattern = 0;
618 int ignore_case = 0;
619 int flatten = 1;
620 ap_regex_t *r = NULL;
621
622 if (apr_tolower(*line) != 's') {
623 return "Bad Substitute format, must be an s/// pattern";
624 }
625 ourline = apr_pstrdup(cmd->pool, line);
626 delim = *++ourline;
627 if (delim)
628 from = ++ourline;
629 if (from) {
630 if (*ourline != delim) {
631 while (*++ourline && *ourline != delim);
632 }
633 if (*ourline) {
634 *ourline = '\0';
635 to = ++ourline;
636 }
637 }
638 if (to) {
639 if (*ourline != delim) {
640 while (*++ourline && *ourline != delim);
641 }
642 if (*ourline) {
643 *ourline = '\0';
644 flags = ++ourline;
645 }
646 }
647
648 if (!delim || !from || !*from || !to) {
649 return "Bad Substitute format, must be a complete s/// pattern";
650 }
651
652 if (flags) {
653 while (*flags) {
654 delim = apr_tolower(*flags); /* re-use */
655 if (delim == 'i')
656 ignore_case = 1;
657 else if (delim == 'n')
658 is_pattern = 1;
659 else if (delim == 'f')
660 flatten = 1;
661 else if (delim == 'q')
662 flatten = 0;
663 else
664 return "Bad Substitute flag, only s///[infq] are supported";
665 flags++;
666 }
667 }
668
669 /* first see if we can compile the regex */
670 if (!is_pattern) {
671 int flags = AP_REG_NO_DEFAULT
672 | (ap_regcomp_get_default_cflags() & AP_REG_DOLLAR_ENDONLY)
673 | (ignore_case ? AP_REG_ICASE : 0);
674 r = ap_pregcomp(cmd->pool, from, flags);
675 if (!r)
676 return "Substitute could not compile regex";
677 }
678 nscript = apr_array_push(((subst_dir_conf *) cfg)->patterns);
679 /* init the new entries */
680 nscript->pattern = NULL;
681 nscript->regexp = NULL;
682 nscript->replacement = NULL;
683 nscript->patlen = 0;
684 nscript->from = from;
685
686 if (is_pattern) {
687 nscript->patlen = strlen(from);
688 nscript->pattern = apr_strmatch_precompile(cmd->pool, from,
689 !ignore_case);
690 }
691 else {
692 nscript->regexp = r;
693 }
694
695 nscript->replacement = to;
696 nscript->replen = strlen(to);
697 nscript->flatten = flatten;
698
699 return NULL;
700 }
701
702 #define KBYTE 1024
703 #define MBYTE 1048576
704 #define GBYTE 1073741824
705
set_max_line_length(cmd_parms * cmd,void * cfg,const char * arg)706 static const char *set_max_line_length(cmd_parms *cmd, void *cfg, const char *arg)
707 {
708 subst_dir_conf *dcfg = (subst_dir_conf *)cfg;
709 apr_off_t max;
710 char *end;
711 apr_status_t rv;
712
713 rv = apr_strtoff(&max, arg, &end, 10);
714 if (rv == APR_SUCCESS) {
715 if ((*end == 'K' || *end == 'k') && !end[1]) {
716 max *= KBYTE;
717 }
718 else if ((*end == 'M' || *end == 'm') && !end[1]) {
719 max *= MBYTE;
720 }
721 else if ((*end == 'G' || *end == 'g') && !end[1]) {
722 max *= GBYTE;
723 }
724 else if (*end && /* neither empty nor [Bb] */
725 ((*end != 'B' && *end != 'b') || end[1])) {
726 rv = APR_EGENERAL;
727 }
728 }
729
730 if (rv != APR_SUCCESS || max < 0)
731 {
732 return "SubstituteMaxLineLength must be a non-negative integer optionally "
733 "suffixed with 'b', 'k', 'm' or 'g'.";
734 }
735 dcfg->max_line_length = (apr_size_t)max;
736 dcfg->max_line_length_set = 1;
737 return NULL;
738 }
739
740 #define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH
register_hooks(apr_pool_t * pool)741 static void register_hooks(apr_pool_t *pool)
742 {
743 ap_register_output_filter(substitute_filter_name, substitute_filter,
744 NULL, AP_FTYPE_RESOURCE);
745 }
746
747 static const command_rec substitute_cmds[] = {
748 AP_INIT_TAKE1("Substitute", set_pattern, NULL, OR_FILEINFO,
749 "Pattern to filter the response content (s/foo/bar/[inf])"),
750 AP_INIT_TAKE1("SubstituteMaxLineLength", set_max_line_length, NULL, OR_FILEINFO,
751 "Maximum line length"),
752 AP_INIT_FLAG("SubstituteInheritBefore", ap_set_flag_slot,
753 (void *)APR_OFFSETOF(subst_dir_conf, inherit_before), OR_FILEINFO,
754 "Apply inherited patterns before those of the current context"),
755 {NULL}
756 };
757
758 AP_DECLARE_MODULE(substitute) = {
759 STANDARD20_MODULE_STUFF,
760 create_substitute_dcfg, /* dir config creater */
761 merge_substitute_dcfg, /* dir merger --- default is to override */
762 NULL, /* server config */
763 NULL, /* merge server config */
764 substitute_cmds, /* command table */
765 register_hooks /* register hooks */
766 };
767