summaryrefslogtreecommitdiff
path: root/modules/filters
diff options
context:
space:
mode:
authorStefan Fritsch <sf@sfritsch.de>2011-12-27 19:42:17 +0100
committerStefan Fritsch <sf@sfritsch.de>2011-12-27 19:42:17 +0100
commit9e615cb6aa4afcee97f8a1646e5a586261a7b81f (patch)
tree0e09fde2404555dc5daf167b38243b5f89c16549 /modules/filters
parent1acac7a6b494db24f8f58e44dab7657b6de68742 (diff)
downloadapache2-9e615cb6aa4afcee97f8a1646e5a586261a7b81f.tar.gz
Upstream tarball 2.2.8upstream/2.2.8
Diffstat (limited to 'modules/filters')
-rw-r--r--modules/filters/NWGNUmakefile1
-rw-r--r--modules/filters/NWGNUsubstitute258
-rw-r--r--modules/filters/config.m41
-rw-r--r--modules/filters/mod_charset_lite.c151
-rw-r--r--modules/filters/mod_deflate.c26
-rw-r--r--modules/filters/mod_deflate.dsp4
-rw-r--r--modules/filters/mod_ext_filter.dsp4
-rw-r--r--modules/filters/mod_filter.c11
-rw-r--r--modules/filters/mod_filter.dsp4
-rw-r--r--modules/filters/mod_include.c68
-rw-r--r--modules/filters/mod_substitute.c584
-rw-r--r--modules/filters/mod_substitute.dsp111
12 files changed, 1114 insertions, 109 deletions
diff --git a/modules/filters/NWGNUmakefile b/modules/filters/NWGNUmakefile
index 4433689e..04762d60 100644
--- a/modules/filters/NWGNUmakefile
+++ b/modules/filters/NWGNUmakefile
@@ -155,6 +155,7 @@ TARGET_nlm = \
$(OBJDIR)/extfiltr.nlm \
$(OBJDIR)/charsetl.nlm \
$(OBJDIR)/mod_filter.nlm \
+ $(OBJDIR)/substitute.nlm \
$(EOLIST)
# If the zlib libraries source exists then build the mod_deflate module
diff --git a/modules/filters/NWGNUsubstitute b/modules/filters/NWGNUsubstitute
new file mode 100644
index 00000000..ca19eb11
--- /dev/null
+++ b/modules/filters/NWGNUsubstitute
@@ -0,0 +1,258 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+include $(AP_WORK)\build\NWGNUhead.inc
+
+#
+# build this level's files
+
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(AP_WORK)/include \
+ $(NWOS) \
+ $(AP_WORK)/modules/arch/netware \
+ $(APR)/include \
+ $(APRUTIL)/include \
+ $(APR) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = substitute
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Substitute Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = Substitute Module
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM = _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM = _LibCPostlude
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/substitute.nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/mod_substitute.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ libcpre.o \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @$(APR)/aprlib.imp \
+ @$(NWOS)/httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ substitute_module \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(AP_WORK)\build\NWGNUtail.inc
+
+
diff --git a/modules/filters/config.m4 b/modules/filters/config.m4
index c9c8f386..9c1f6083 100644
--- a/modules/filters/config.m4
+++ b/modules/filters/config.m4
@@ -7,6 +7,7 @@ APACHE_MODPATH_INIT(filters)
APACHE_MODULE(ext_filter, external filter module, , , most)
APACHE_MODULE(include, Server Side Includes, , , yes)
APACHE_MODULE(filter, Smart Filtering, , , yes)
+APACHE_MODULE(substitute, response content rewrite-like filtering, , , most)
if test "$ac_cv_ebcdic" = "yes"; then
# mod_charset_lite can be very useful on an ebcdic system,
diff --git a/modules/filters/mod_charset_lite.c b/modules/filters/mod_charset_lite.c
index e7df32b1..ed8ecbe5 100644
--- a/modules/filters/mod_charset_lite.c
+++ b/modules/filters/mod_charset_lite.c
@@ -85,6 +85,7 @@ typedef struct charset_dir_t {
*/
typedef struct charset_filter_ctx_t {
apr_xlate_t *xlate;
+ int is_sb; /* single-byte translation? */
charset_dir_t *dc;
ees_t ees; /* extended error status */
apr_size_t saved;
@@ -188,9 +189,9 @@ static const char *add_charset_options(cmd_parms *cmd, void *in_dc,
return NULL;
}
-/* find_code_page() is a fixup hook that decides if translation should be
- * enabled; if so, it sets up request data for use by the filter registration
- * hook so that it knows what to do
+/* find_code_page() is a fixup hook that checks if the module is
+ * configured and the input or output potentially need to be translated.
+ * If so, context is initialized for the filters.
*/
static int find_code_page(request_rec *r)
{
@@ -199,13 +200,14 @@ static int find_code_page(request_rec *r)
charset_req_t *reqinfo;
charset_filter_ctx_t *input_ctx, *output_ctx;
apr_status_t rv;
- const char *mime_type;
if (dc->debug >= DBGLVL_FLOW) {
ap_log_rerror(APLOG_MARK,APLOG_DEBUG, 0, r,
"uri: %s file: %s method: %d "
"imt: %s flags: %s%s%s %s->%s",
- r->uri, r->filename, r->method_number,
+ r->uri,
+ r->filename ? r->filename : "(none)",
+ r->method_number,
r->content_type ? r->content_type : "(unknown)",
r->main ? "S" : "", /* S if subrequest */
r->prev ? "R" : "", /* R if redirect */
@@ -226,60 +228,22 @@ static int find_code_page(request_rec *r)
}
/* catch proxy requests */
- if (r->proxyreq) return DECLINED;
- /* mod_rewrite indicators */
- if (!strncmp(r->filename, "redirect:", 9)) return DECLINED;
- if (!strncmp(r->filename, "gone:", 5)) return DECLINED;
- if (!strncmp(r->filename, "passthrough:", 12)) return DECLINED;
- if (!strncmp(r->filename, "forbidden:", 10)) return DECLINED;
- /* no translation when server and network charsets are set to the same value */
- if (!strcasecmp(dc->charset_source, dc->charset_default)) return DECLINED;
-
- mime_type = r->content_type ? r->content_type : ap_default_type(r);
-
- /* If mime type isn't text or message, bail out.
- */
-
-/* XXX When we handle translation of the request body, watch out here as
- * 1.3 allowed additional mime types: multipart and
- * application/x-www-form-urlencoded
- */
+ if (r->proxyreq) {
+ return DECLINED;
+ }
- if (strncasecmp(mime_type, "text/", 5) &&
-#if APR_CHARSET_EBCDIC || AP_WANT_DIR_TRANSLATION
- /* On an EBCDIC machine, be willing to translate mod_autoindex-
- * generated output. Otherwise, it doesn't look too cool.
- *
- * XXX This isn't a perfect fix because this doesn't trigger us
- * to convert from the charset of the source code to ASCII. The
- * general solution seems to be to allow a generator to set an
- * indicator in the r specifying that the body is coded in the
- * implementation character set (i.e., the charset of the source
- * code). This would get several different types of documents
- * translated properly: mod_autoindex output, mod_status output,
- * mod_info output, hard-coded error documents, etc.
- */
- strcmp(mime_type, DIR_MAGIC_TYPE) &&
-#endif
- strncasecmp(mime_type, "message/", 8)) {
- if (dc->debug >= DBGLVL_GORY) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
- "mime type is %s; no translation selected",
- mime_type);
- }
- /* We must not bail out here (i.e., the MIME test must be in the filter
- * itself, not in the fixup, because only then is the final MIME type known.
- * Examples for late changes to the MIME type include CGI handling (MIME
- * type is set in the Content-Type header produced by the CGI script), or
- * PHP (until PHP runs, the MIME type is set to application/x-httpd-php)
- */
+ /* mod_rewrite indicators */
+ if (r->filename
+ && (!strncmp(r->filename, "redirect:", 9)
+ || !strncmp(r->filename, "gone:", 5)
+ || !strncmp(r->filename, "passthrough:", 12)
+ || !strncmp(r->filename, "forbidden:", 10))) {
+ return DECLINED;
}
- if (dc->debug >= DBGLVL_GORY) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
- "charset_source: %s charset_default: %s",
- dc && dc->charset_source ? dc->charset_source : "(none)",
- dc && dc->charset_default ? dc->charset_default : "(none)");
+ /* no translation when server and network charsets are set to the same value */
+ if (!strcasecmp(dc->charset_source, dc->charset_default)) {
+ return DECLINED;
}
/* Get storage for the request data and the output filter context.
@@ -296,14 +260,6 @@ static int find_code_page(request_rec *r)
reqinfo->output_ctx = output_ctx;
- /* We must not open the xlation table here yet, because the final MIME
- * type is not known until we are actually called in the output filter.
- * With POST or PUT request, the case is different, because their MIME
- * type is set in the request headers, and their data are prerequisites
- * for actually calling, e.g., the CGI handler later on.
- */
- output_ctx->xlate = NULL;
-
switch (r->method_number) {
case M_PUT:
case M_POST:
@@ -325,6 +281,9 @@ static int find_code_page(request_rec *r)
dc->charset_default, dc->charset_source);
return HTTP_INTERNAL_SERVER_ERROR;
}
+ if (apr_xlate_sb_get(input_ctx->xlate, &input_ctx->is_sb) != APR_SUCCESS) {
+ input_ctx->is_sb = 0;
+ }
}
return DECLINED;
@@ -823,18 +782,11 @@ static apr_status_t xlate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
}
}
- /* Opening the output translation (this used to be done in the fixup hook,
- * but that was too early: a subsequent type modification, e.g., by a
- * CGI script, would go unnoticed. Now we do it in the filter itself.)
+ /* Check the mime type to see if translation should be performed.
*/
- if (!ctx->noop && ctx->xlate == NULL)
- {
+ if (!ctx->noop && ctx->xlate == NULL) {
const char *mime_type = f->r->content_type ? f->r->content_type : ap_default_type(f->r);
- /* XXX When we handle translation of the request body, watch out here as
- * 1.3 allowed additional mime types: multipart and
- * application/x-www-form-urlencoded
- */
if (strncasecmp(mime_type, "text/", 5) == 0 ||
#if APR_CHARSET_EBCDIC
/* On an EBCDIC machine, be willing to translate mod_autoindex-
@@ -849,39 +801,51 @@ static apr_status_t xlate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
* translated properly: mod_autoindex output, mod_status output,
* mod_info output, hard-coded error documents, etc.
*/
- strcmp(mime_type, DIR_MAGIC_TYPE) == 0 ||
+ strcmp(mime_type, DIR_MAGIC_TYPE) == 0 ||
#endif
- strncasecmp(mime_type, "message/", 8) == 0) {
+ strncasecmp(mime_type, "message/", 8) == 0) {
rv = apr_xlate_open(&ctx->xlate,
- dc->charset_default, dc->charset_source, f->r->pool);
+ dc->charset_default, dc->charset_source, f->r->pool);
if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
"can't open translation %s->%s",
dc->charset_source, dc->charset_default);
ctx->noop = 1;
}
+ else {
+ if (apr_xlate_sb_get(ctx->xlate, &ctx->is_sb) != APR_SUCCESS) {
+ ctx->is_sb = 0;
+ }
+ }
}
else {
- ctx->noop = 1;
- if (dc->debug >= DBGLVL_GORY)
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
- "mime type is %s; no translation selected",
- mime_type);
+ ctx->noop = 1;
+ if (dc->debug >= DBGLVL_GORY) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
+ "mime type is %s; no translation selected",
+ mime_type);
}
+ }
}
if (dc->debug >= DBGLVL_GORY) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
- "xlate_out_filter() - "
- "charset_source: %s charset_default: %s",
- dc && dc->charset_source ? dc->charset_source : "(none)",
- dc && dc->charset_default ? dc->charset_default : "(none)");
+ "xlate_out_filter() - "
+ "charset_source: %s charset_default: %s",
+ dc && dc->charset_source ? dc->charset_source : "(none)",
+ dc && dc->charset_default ? dc->charset_default : "(none)");
}
if (!ctx->ran) { /* filter never ran before */
chk_filter_chain(f);
ctx->ran = 1;
+ if (!ctx->noop && !ctx->is_sb) {
+ /* We're not converting between two single-byte charsets, so unset
+ * Content-Length since it is unlikely to remain the same.
+ */
+ apr_table_unset(f->r->headers_out, "Content-Length");
+ }
}
if (ctx->noop) {
@@ -1040,6 +1004,23 @@ static int xlate_in_filter(ap_filter_t *f, apr_bucket_brigade *bb,
if (!ctx->ran) { /* filter never ran before */
chk_filter_chain(f);
ctx->ran = 1;
+ if (!ctx->noop && !ctx->is_sb
+ && apr_table_get(f->r->headers_in, "Content-Length")) {
+ /* A Content-Length header is present, but it won't be valid after
+ * conversion because we're not converting between two single-byte
+ * charsets. This will affect most CGI scripts and may affect
+ * some modules.
+ * Content-Length can't be unset here because that would break
+ * being able to read the request body.
+ * Processing of chunked request bodies is not impacted by this
+ * filter since the the length was not declared anyway.
+ */
+ if (dc->debug >= DBGLVL_PMC) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
+ "Request body length may change, resulting in "
+ "misprocessing by some modules or scripts");
+ }
+ }
}
if (ctx->noop) {
diff --git a/modules/filters/mod_deflate.c b/modules/filters/mod_deflate.c
index f82b75b7..de1a57d7 100644
--- a/modules/filters/mod_deflate.c
+++ b/modules/filters/mod_deflate.c
@@ -293,6 +293,7 @@ typedef struct deflate_ctx_t
int (*libz_end_func)(z_streamp);
unsigned char *validation_buffer;
apr_size_t validation_buffer_length;
+ int inflate_init;
} deflate_ctx;
/* Number of validation bytes (CRC and length) after the compressed data */
@@ -371,7 +372,23 @@ static apr_status_t deflate_ctx_cleanup(void *data)
ctx->libz_end_func(&ctx->stream);
return APR_SUCCESS;
}
-
+/* PR 39727: we're screwing up our clients if we leave a strong ETag
+ * header while transforming content. Henrik Nordstrom suggests
+ * appending ";gzip".
+ *
+ * Pending a more thorough review of our Etag handling, let's just
+ * implement his suggestion. It fixes the bug, or at least turns it
+ * from a showstopper to an inefficiency. And it breaks nothing that
+ * wasn't already broken.
+ */
+static void deflate_check_etag(request_rec *r, const char *transform)
+{
+ const char *etag = apr_table_get(r->headers_out, "ETag");
+ if (etag && (((etag[0] != 'W') && (etag[0] !='w')) || (etag[1] != '/'))) {
+ apr_table_set(r->headers_out, "ETag",
+ apr_pstrcat(r->pool, etag, "-", transform, NULL));
+ }
+}
static apr_status_t deflate_out_filter(ap_filter_t *f,
apr_bucket_brigade *bb)
{
@@ -569,6 +586,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f,
}
apr_table_unset(r->headers_out, "Content-Length");
apr_table_unset(r->headers_out, "Content-MD5");
+ deflate_check_etag(r, "gzip");
/* initialize deflate output buffer */
ctx->stream.next_out = ctx->buffer;
@@ -983,7 +1001,6 @@ static apr_status_t inflate_out_filter(ap_filter_t *f,
{
int zlib_method;
int zlib_flags;
- int inflate_init = 1;
apr_bucket *e;
request_rec *r = f->r;
deflate_ctx *ctx = f->ctx;
@@ -1062,12 +1079,13 @@ static apr_status_t inflate_out_filter(ap_filter_t *f,
/* these are unlikely to be set anyway, but ... */
apr_table_unset(r->headers_out, "Content-Length");
apr_table_unset(r->headers_out, "Content-MD5");
+ deflate_check_etag(r, "gunzip");
/* initialize inflate output buffer */
ctx->stream.next_out = ctx->buffer;
ctx->stream.avail_out = c->bufferSize;
- inflate_init = 0;
+ ctx->inflate_init = 0;
}
while (!APR_BRIGADE_EMPTY(bb))
@@ -1172,7 +1190,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f,
apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
/* first bucket contains zlib header */
- if (!inflate_init++) {
+ if (!ctx->inflate_init++) {
if (len < 10) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Insufficient data for inflate");
diff --git a/modules/filters/mod_deflate.dsp b/modules/filters/mod_deflate.dsp
index 478eec66..f99035ed 100644
--- a/modules/filters/mod_deflate.dsp
+++ b/modules/filters/mod_deflate.dsp
@@ -53,7 +53,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_deflate.so" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so
-# ADD LINK32 kernel32.lib zlib.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_deflate.so" /libpath:"../../srclib/zlib" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so /opt:ref
+# ADD LINK32 kernel32.lib zdll.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_deflate.so" /libpath:"../../srclib/zlib" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so /opt:ref
# Begin Special Build Tool
TargetPath=.\Release\mod_deflate.so
SOURCE="$(InputPath)"
@@ -85,7 +85,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_deflate.so" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so
-# ADD LINK32 kernel32.lib zlib.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_deflate.so" /libpath:"../../srclib/zlib" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so
+# ADD LINK32 kernel32.lib zdll.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_deflate.so" /libpath:"../../srclib/zlib" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so
# Begin Special Build Tool
TargetPath=.\Debug\mod_deflate.so
SOURCE="$(InputPath)"
diff --git a/modules/filters/mod_ext_filter.dsp b/modules/filters/mod_ext_filter.dsp
index 7b77b674..f87d5d7e 100644
--- a/modules/filters/mod_ext_filter.dsp
+++ b/modules/filters/mod_ext_filter.dsp
@@ -47,7 +47,7 @@ RSC=rc.exe
# ADD BASE MTL /nologo /D "NDEBUG" /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /fo"Release/mod_deflate.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_deflate.so" /d LONG_NAME="deflate_module for Apache"
+# ADD RSC /l 0x409 /fo"Release/mod_ext_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_ext_filter.so" /d LONG_NAME="ext_filter_module for Apache"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
@@ -79,7 +79,7 @@ PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).ma
# ADD BASE MTL /nologo /D "_DEBUG" /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /fo"Debug/mod_deflate.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_deflate.so" /d LONG_NAME="deflate_module for Apache"
+# ADD RSC /l 0x409 /fo"Debug/mod_ext_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_ext_filter.so" /d LONG_NAME="ext_filter_module for Apache"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
diff --git a/modules/filters/mod_filter.c b/modules/filters/mod_filter.c
index 778895b4..3ba13c95 100644
--- a/modules/filters/mod_filter.c
+++ b/modules/filters/mod_filter.c
@@ -137,7 +137,12 @@ static int filter_init(ap_filter_t *f)
harness_ctx *fctx = apr_pcalloc(f->r->pool, sizeof(harness_ctx));
for (p = filter->providers; p; p = p->next) {
- if (p->frec->filter_init_func) {
+ if (p->frec->filter_init_func == filter_init) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c,
+ "Chaining of FilterProviders not supported");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ else if (p->frec->filter_init_func) {
f->ctx = NULL;
if ((err = p->frec->filter_init_func(f)) != OK) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c,
@@ -533,10 +538,6 @@ static const char *filter_provider(cmd_parms *cmd, void *CFG, const char *args)
/* if provider has been registered, we can look it up */
provider_frec = ap_get_output_filter_handle(pname);
if (!provider_frec) {
- provider_frec = apr_hash_get(cfg->live_filters, pname,
- APR_HASH_KEY_STRING);
- }
- if (!provider_frec) {
return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname);
}
diff --git a/modules/filters/mod_filter.dsp b/modules/filters/mod_filter.dsp
index 1729238a..ee4d484f 100644
--- a/modules/filters/mod_filter.dsp
+++ b/modules/filters/mod_filter.dsp
@@ -47,7 +47,7 @@ RSC=rc.exe
# ADD BASE MTL /nologo /D "NDEBUG" /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /fo"Release/mod_deflate.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_deflate.so" /d LONG_NAME="deflate_module for Apache"
+# ADD RSC /l 0x409 /fo"Release/mod_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_filter.so" /d LONG_NAME="filter_module for Apache"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
@@ -79,7 +79,7 @@ PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).ma
# ADD BASE MTL /nologo /D "_DEBUG" /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /fo"Debug/mod_deflate.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_deflate.so" /d LONG_NAME="deflate_module for Apache"
+# ADD RSC /l 0x409 /fo"Debug/mod_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_filter.so" /d LONG_NAME="filter_module for Apache"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c
index b89a459b..db8b5faa 100644
--- a/modules/filters/mod_include.c
+++ b/modules/filters/mod_include.c
@@ -80,7 +80,8 @@ typedef enum {
TOKEN_GE,
TOKEN_LE,
TOKEN_GT,
- TOKEN_LT
+ TOKEN_LT,
+ TOKEN_ACCESS
} token_type_t;
typedef struct {
@@ -114,6 +115,7 @@ typedef struct {
const char *default_time_fmt;
const char *undefined_echo;
xbithack_t xbithack;
+ const int accessenable;
} include_dir_config;
typedef struct {
@@ -190,6 +192,8 @@ struct ssi_internal_ctx {
const char *undefined_echo;
apr_size_t undefined_echo_len;
+ int accessenable; /* is using the access tests allowed? */
+
#ifdef DEBUG_INCLUDE
struct {
ap_filter_t *f;
@@ -941,7 +945,7 @@ static APR_INLINE int re_check(include_ctx_t *ctx, const char *string,
return rc;
}
-static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token)
+static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous)
{
const char *p;
apr_size_t shift;
@@ -990,6 +994,10 @@ static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token)
unmatched = '\'';
break;
case '/':
+ /* if last token was ACCESS, this token is STRING */
+ if (previous != NULL && TOKEN_ACCESS == previous->type) {
+ break;
+ }
TYPE_TOKEN(token, TOKEN_RE);
unmatched = '/';
break;
@@ -1023,6 +1031,13 @@ static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token)
}
TYPE_TOKEN(token, TOKEN_LT);
return 0;
+ case '-':
+ if (**parse == 'A' && (ctx->intern->accessenable)) {
+ TYPE_TOKEN(token, TOKEN_ACCESS);
+ ++*parse;
+ return 0;
+ }
+ break;
}
/* It's a string or regex token
@@ -1079,11 +1094,11 @@ static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token)
}
if (unmatched) {
- token->value = apr_pstrdup(pool, "");
+ token->value = apr_pstrdup(ctx->dpool, "");
}
else {
apr_size_t len = p - token->value - shift;
- char *c = apr_palloc(pool, len + 1);
+ char *c = apr_palloc(ctx->dpool, len + 1);
p = token->value;
token->value = c;
@@ -1111,6 +1126,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
{
parse_node_t *new, *root = NULL, *current = NULL;
request_rec *r = ctx->intern->r;
+ request_rec *rr = NULL;
const char *error = "Invalid expression \"%s\" in file %s";
const char *parse = expr;
int was_unmatched = 0;
@@ -1130,7 +1146,8 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
*/
CREATE_NODE(ctx, new);
- was_unmatched = get_ptoken(ctx->dpool, &parse, &new->token);
+ was_unmatched = get_ptoken(ctx, &parse, &new->token,
+ (current != NULL ? &current->token : NULL));
if (!parse) {
break;
}
@@ -1142,6 +1159,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
switch (new->token.type) {
case TOKEN_STRING:
case TOKEN_NOT:
+ case TOKEN_ACCESS:
case TOKEN_LBRACE:
root = current = new;
continue;
@@ -1276,6 +1294,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
break;
case TOKEN_NOT:
+ case TOKEN_ACCESS:
case TOKEN_LBRACE:
switch (current->token.type) {
case TOKEN_STRING:
@@ -1462,6 +1481,34 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
}
break;
+ case TOKEN_ACCESS:
+ if (current->left || !current->right ||
+ (current->right->token.type != TOKEN_STRING &&
+ current->right->token.type != TOKEN_RE)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Invalid expression \"%s\" in file %s: Token '-A' must be followed by a URI string.",
+ expr, r->filename);
+ *was_error = 1;
+ return 0;
+ }
+ current->right->token.value =
+ ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+ rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
+ /* 400 and higher are considered access denied */
+ if (rr->status < HTTP_BAD_REQUEST) {
+ current->value = 1;
+ }
+ else {
+ current->value = 0;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r,
+ "mod_include: The tested "
+ "subrequest -A \"%s\" returned an error code.",
+ current->right->token.value);
+ }
+ ap_destroy_sub_req(rr);
+ break;
+
case TOKEN_RE:
if (!error) {
error = "No operator before regex in expr \"%s\" in file %s";
@@ -3225,9 +3272,8 @@ static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb)
/* check if we mismatched earlier and have to release some chars */
if (release && (ctx->flags & SSI_FLAG_PRINTING)) {
- char *to_release = apr_palloc(ctx->pool, release);
+ char *to_release = apr_pmemdup(ctx->pool, intern->start_seq, release);
- memcpy(to_release, intern->start_seq, release);
newb = apr_bucket_pool_create(to_release, release, ctx->pool,
f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(pass_bb, newb);
@@ -3414,9 +3460,9 @@ static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb)
if (intern->seen_eos) {
if (PARSE_HEAD == intern->state) {
if (ctx->flags & SSI_FLAG_PRINTING) {
- char *to_release = apr_palloc(ctx->pool, intern->parse_pos);
+ char *to_release = apr_pmemdup(ctx->pool, intern->start_seq,
+ intern->parse_pos);
- memcpy(to_release, intern->start_seq, intern->parse_pos);
APR_BRIGADE_INSERT_TAIL(pass_bb,
apr_bucket_pool_create(to_release,
intern->parse_pos, ctx->pool,
@@ -3527,6 +3573,7 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
if (ap_allow_options(r) & OPT_INCNOEXEC) {
ctx->flags |= SSI_FLAG_NO_EXEC;
}
+ intern->accessenable = conf->accessenable;
ctx->if_nesting_level = 0;
intern->re = NULL;
@@ -3809,6 +3856,9 @@ static const command_rec includes_cmds[] =
"SSI End String Tag"),
AP_INIT_TAKE1("SSIUndefinedEcho", set_undefined_echo, NULL, OR_ALL,
"String to be displayed if an echoed variable is undefined"),
+ AP_INIT_FLAG("SSIAccessEnable", ap_set_flag_slot,
+ (void *)APR_OFFSETOF(include_dir_config, accessenable),
+ OR_LIMIT, "Whether testing access is enabled. Limited to 'on' or 'off'"),
{NULL}
};
diff --git a/modules/filters/mod_substitute.c b/modules/filters/mod_substitute.c
new file mode 100644
index 00000000..592d1404
--- /dev/null
+++ b/modules/filters/mod_substitute.c
@@ -0,0 +1,584 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * mod_substitute.c: Perform content rewriting on the fly
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_strmatch.h"
+#include "apr_lib.h"
+#include "util_filter.h"
+#include "apr_buckets.h"
+#include "http_request.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+static const char substitute_filter_name[] = "SUBSTITUTE";
+
+module AP_MODULE_DECLARE_DATA substitute_module;
+
+typedef struct subst_pattern_t {
+ const apr_strmatch_pattern *pattern;
+ const ap_regex_t *regexp;
+ const char *replacement;
+ apr_size_t replen;
+ apr_size_t patlen;
+ int flatten;
+} subst_pattern_t;
+
+typedef struct {
+ apr_array_header_t *patterns;
+} subst_dir_conf;
+
+typedef struct {
+ apr_bucket_brigade *linebb;
+ apr_bucket_brigade *linesbb;
+ apr_bucket_brigade *passbb;
+ apr_bucket_brigade *pattbb;
+ apr_pool_t *tpool;
+} substitute_module_ctx;
+
+static void *create_substitute_dcfg(apr_pool_t *p, char *d)
+{
+ subst_dir_conf *dcfg =
+ (subst_dir_conf *) apr_pcalloc(p, sizeof(subst_dir_conf));
+
+ dcfg->patterns = apr_array_make(p, 10, sizeof(subst_pattern_t));
+ return dcfg;
+}
+
+static void *merge_substitute_dcfg(apr_pool_t *p, void *basev, void *overv)
+{
+ subst_dir_conf *a =
+ (subst_dir_conf *) apr_pcalloc(p, sizeof(subst_dir_conf));
+ subst_dir_conf *base = (subst_dir_conf *) basev;
+ subst_dir_conf *over = (subst_dir_conf *) overv;
+
+ a->patterns = apr_array_append(p, over->patterns,
+ base->patterns);
+ return a;
+}
+
+#define AP_MAX_BUCKETS 1000
+
+#define SEDSCAT(s1, s2, pool, buff, blen, repl) do { \
+ if (!s1) { \
+ s1 = apr_pstrmemdup(pool, buff, blen); \
+ } \
+ else { \
+ s2 = apr_pstrmemdup(pool, buff, blen); \
+ s1 = apr_pstrcat(pool, s1, s2, NULL); \
+ } \
+ s1 = apr_pstrcat(pool, s1, repl, NULL); \
+} while (0)
+
+#define SEDRMPATBCKT(b, offset, tmp_b, patlen) do { \
+ apr_bucket_split(b, offset); \
+ tmp_b = APR_BUCKET_NEXT(b); \
+ apr_bucket_split(tmp_b, patlen); \
+ b = APR_BUCKET_NEXT(tmp_b); \
+ apr_bucket_delete(tmp_b); \
+} while (0)
+
+static void do_pattmatch(ap_filter_t *f, apr_bucket *inb,
+ apr_bucket_brigade *mybb,
+ apr_pool_t *tmp_pool)
+{
+ int i;
+ ap_regmatch_t regm[AP_MAX_REG_MATCH];
+ apr_size_t bytes;
+ apr_size_t len;
+ apr_size_t fbytes;
+ const char *buff;
+ const char *repl;
+ char *scratch;
+ char *p;
+ char *s1;
+ char *s2;
+ apr_bucket *b;
+ apr_bucket *tmp_b;
+ apr_pool_t *tpool;
+
+ subst_dir_conf *cfg =
+ (subst_dir_conf *) ap_get_module_config(f->r->per_dir_config,
+ &substitute_module);
+ subst_pattern_t *script;
+
+ APR_BRIGADE_INSERT_TAIL(mybb, inb);
+
+ script = (subst_pattern_t *) cfg->patterns->elts;
+ apr_pool_create(&tpool, tmp_pool);
+ scratch = NULL;
+ fbytes = 0;
+ for (i = 0; i < cfg->patterns->nelts; i++) {
+ for (b = APR_BRIGADE_FIRST(mybb);
+ b != APR_BRIGADE_SENTINEL(mybb);
+ b = APR_BUCKET_NEXT(b)) {
+ if (APR_BUCKET_IS_METADATA(b)) {
+ /*
+ * we should NEVER see this, because we should never
+ * be passed any, but "handle" it just in case.
+ */
+ continue;
+ }
+ if (apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ)
+ == APR_SUCCESS) {
+ s1 = NULL;
+ if (script->pattern) {
+ while ((repl = apr_strmatch(script->pattern, buff, bytes)))
+ {
+ /* get offset into buff for pattern */
+ len = (apr_size_t) (repl - buff);
+ if (script->flatten) {
+ /*
+ * We are flattening the buckets here, meaning
+ * that we don't do the fast bucket splits.
+ * Instead we copy over what the buckets would
+ * contain and use them. This is slow, since we
+ * are constanting allocing space and copying
+ * strings.
+ */
+ SEDSCAT(s1, s2, tmp_pool, buff, len,
+ script->replacement);
+ }
+ else {
+ /*
+ * We now split off the stuff before the regex
+ * as its own bucket, then isolate the pattern
+ * and delete it.
+ */
+ SEDRMPATBCKT(b, len, tmp_b, script->patlen);
+ /*
+ * Finally, we create a bucket that contains the
+ * replacement...
+ */
+ tmp_b = apr_bucket_transient_create(script->replacement,
+ script->replen,
+ f->r->connection->bucket_alloc);
+ /* ... and insert it */
+ APR_BUCKET_INSERT_BEFORE(b, tmp_b);
+ }
+ /* now we need to adjust buff for all these changes */
+ len += script->patlen;
+ bytes -= len;
+ buff += len;
+ }
+ if (script->flatten && s1) {
+ /*
+ * we've finished looking at the bucket, so remove the
+ * old one and add in our new one
+ */
+ s2 = apr_pstrmemdup(tmp_pool, buff, bytes);
+ s1 = apr_pstrcat(tmp_pool, s1, s2, NULL);
+ tmp_b = apr_bucket_transient_create(s1, strlen(s1),
+ f->r->connection->bucket_alloc);
+ APR_BUCKET_INSERT_BEFORE(b, tmp_b);
+ tmp_b = APR_BUCKET_NEXT(b);
+ apr_bucket_delete(b);
+ b = tmp_b;
+ }
+
+ }
+ else if (script->regexp) {
+ /*
+ * we need a null terminated string here :(. To hopefully
+ * save time and memory, we don't alloc for each run
+ * through, but only if we need to have a larger chunk
+ * to save the string to. So we keep track of how much
+ * we've allocated and only re-alloc when we need it.
+ * NOTE: this screams for a macro.
+ */
+ if (!scratch || (bytes > (fbytes + 1))) {
+ fbytes = bytes + 1;
+ scratch = apr_palloc(tpool, fbytes);
+ }
+ /* reset pointer to the scratch space */
+ p = scratch;
+ memcpy(p, buff, bytes);
+ p[bytes] = '\0';
+ while (!ap_regexec(script->regexp, p,
+ AP_MAX_REG_MATCH, regm, 0)) {
+ /* first, grab the replacement string */
+ repl = ap_pregsub(tmp_pool, script->replacement, p,
+ AP_MAX_REG_MATCH, regm);
+ if (script->flatten) {
+ SEDSCAT(s1, s2, tmp_pool, p, regm[0].rm_so, repl);
+ }
+ else {
+ len = (apr_size_t) (regm[0].rm_eo - regm[0].rm_so);
+ SEDRMPATBCKT(b, regm[0].rm_so, tmp_b, len);
+ tmp_b = apr_bucket_transient_create(repl,
+ strlen(repl),
+ f->r->connection->bucket_alloc);
+ APR_BUCKET_INSERT_BEFORE(b, tmp_b);
+ }
+ /*
+ * reset to past what we just did. buff now maps to b
+ * again
+ */
+ p += regm[0].rm_eo;
+ }
+ if (script->flatten && s1) {
+ s1 = apr_pstrcat(tmp_pool, s1, p, NULL);
+ tmp_b = apr_bucket_transient_create(s1, strlen(s1),
+ f->r->connection->bucket_alloc);
+ APR_BUCKET_INSERT_BEFORE(b, tmp_b);
+ tmp_b = APR_BUCKET_NEXT(b);
+ apr_bucket_delete(b);
+ b = tmp_b;
+ }
+
+ }
+ else {
+ /* huh? */
+ continue;
+ }
+ }
+ }
+ script++;
+ }
+
+ apr_pool_destroy(tpool);
+
+ return;
+}
+
+static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ apr_size_t bytes;
+ apr_size_t len;
+ apr_size_t fbytes;
+ const char *buff;
+ const char *nl = NULL;
+ char *bflat;
+ apr_bucket *b;
+ apr_bucket *tmp_b;
+ apr_bucket_brigade *tmp_bb = NULL;
+ apr_status_t rv;
+
+ substitute_module_ctx *ctx = f->ctx;
+
+ /*
+ * First time around? Create the saved bb that we used for each pass
+ * through. Note that we can also get here when we explicitly clear ctx,
+ * for error handling
+ */
+ if (!ctx) {
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ /*
+ * Create all the temporary brigades we need and reuse them to avoid
+ * creating them over and over again from r->pool which would cost a
+ * lot of memory in some cases.
+ */
+ ctx->linebb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ ctx->linesbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ ctx->pattbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ /*
+ * Everything to be passed to the next filter goes in
+ * here, our pass brigade.
+ */
+ ctx->passbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ /* Create our temporary pool only once */
+ apr_pool_create(&(ctx->tpool), f->r->pool);
+ apr_table_unset(f->r->headers_out, "Content-Length");
+ }
+
+ /*
+ * Shortcircuit processing
+ */
+ if (APR_BRIGADE_EMPTY(bb))
+ return APR_SUCCESS;
+
+ /*
+ * Here's the concept:
+ * Read in the data and look for newlines. Once we
+ * find a full "line", add it to our working brigade.
+ * If we've finished reading the brigade and we have
+ * any left over data (not a "full" line), store that
+ * for the next pass.
+ *
+ * Note: anything stored in ctx->linebb for sure does not have
+ * a newline char, so we don't concat that bb with the
+ * new bb, since we would spending time searching for the newline
+ * in data we know it doesn't exist. So instead, we simply scan
+ * our current bb and, if we see a newline, prepend ctx->linebb
+ * to the front of it. This makes the code much less straight-
+ * forward (otherwise we could APR_BRIGADE_CONCAT(ctx->linebb, bb)
+ * and just scan for newlines and not bother with needing to know
+ * when ctx->linebb needs to be reset) but also faster. We'll take
+ * the speed.
+ *
+ * Note: apr_brigade_split_line would be nice here, but we
+ * really can't use it since we need more control and we want
+ * to re-use already read bucket data.
+ *
+ * See mod_include if still confused :)
+ */
+
+ while ((b = APR_BRIGADE_FIRST(bb)) && (b != APR_BRIGADE_SENTINEL(bb))) {
+ if (APR_BUCKET_IS_EOS(b)) {
+ /*
+ * if we see the EOS, then we need to pass along everything we
+ * have. But if the ctx->linebb isn't empty, then we need to add
+ * that to the end of what we'll be passing.
+ */
+ if (!APR_BRIGADE_EMPTY(ctx->linebb)) {
+ rv = apr_brigade_pflatten(ctx->linebb, &bflat,
+ &fbytes, ctx->tpool);
+ tmp_b = apr_bucket_transient_create(bflat, fbytes,
+ f->r->connection->bucket_alloc);
+ do_pattmatch(f, tmp_b, ctx->pattbb, ctx->tpool);
+ APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb);
+ }
+ apr_brigade_cleanup(ctx->linebb);
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(ctx->passbb, b);
+ }
+ /*
+ * No need to handle FLUSH buckets separately as we call
+ * ap_pass_brigade anyway at the end of the loop.
+ */
+ else if (APR_BUCKET_IS_METADATA(b)) {
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(ctx->passbb, b);
+ }
+ else {
+ /*
+ * We have actual "data" so read in as much as we can and start
+ * scanning and splitting from our read buffer
+ */
+ rv = apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS || bytes == 0) {
+ APR_BUCKET_REMOVE(b);
+ }
+ else {
+ int num = 0;
+ while (bytes > 0) {
+ nl = memchr(buff, APR_ASCII_LF, bytes);
+ if (nl) {
+ len = (apr_size_t) (nl - buff) + 1;
+ /* split *after* the newline */
+ apr_bucket_split(b, len);
+ /*
+ * We've likely read more data, so bypass rereading
+ * bucket data and continue scanning through this
+ * buffer
+ */
+ bytes -= len;
+ buff += len;
+ /*
+ * we need b to be updated for future potential
+ * splitting
+ */
+ tmp_b = APR_BUCKET_NEXT(b);
+ APR_BUCKET_REMOVE(b);
+ /*
+ * Hey, we found a newline! Don't forget the old
+ * stuff that needs to be added to the front. So we
+ * add the split bucket to the end, flatten the whole
+ * bb, morph the whole shebang into a bucket which is
+ * then added to the tail of the newline bb.
+ */
+ if (!APR_BRIGADE_EMPTY(ctx->linebb)) {
+ APR_BRIGADE_INSERT_TAIL(ctx->linebb, b);
+ rv = apr_brigade_pflatten(ctx->linebb, &bflat,
+ &fbytes, ctx->tpool);
+ b = apr_bucket_transient_create(bflat, fbytes,
+ f->r->connection->bucket_alloc);
+ apr_brigade_cleanup(ctx->linebb);
+ }
+ do_pattmatch(f, b, ctx->pattbb, ctx->tpool);
+ /*
+ * Count how many buckets we have in ctx->passbb
+ * so far. Yes, this is correct we count ctx->passbb
+ * and not ctx->pattbb as we do not reset num on every
+ * iteration.
+ */
+ for (b = APR_BRIGADE_FIRST(ctx->pattbb);
+ b != APR_BRIGADE_SENTINEL(ctx->pattbb);
+ b = APR_BUCKET_NEXT(b)) {
+ num++;
+ }
+ APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb);
+ /*
+ * If the number of buckets in ctx->passbb reaches an
+ * "insane" level, we consume much memory for all the
+ * buckets as such. So lets flush them down the chain
+ * in this case and thus clear ctx->passbb. This frees
+ * the buckets memory for further processing.
+ * Usually this condition should not become true, but
+ * it is a safety measure for edge cases.
+ */
+ if (num > AP_MAX_BUCKETS) {
+ b = apr_bucket_flush_create(
+ f->r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->passbb, b);
+ rv = ap_pass_brigade(f->next, ctx->passbb);
+ apr_brigade_cleanup(ctx->passbb);
+ num = 0;
+ apr_pool_clear(ctx->tpool);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+ b = tmp_b;
+ }
+ else {
+ /*
+ * no newline in whatever is left of this buffer so
+ * tuck data away and get next bucket
+ */
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(ctx->linebb, b);
+ bytes = 0;
+ }
+ }
+ }
+ }
+ if (!APR_BRIGADE_EMPTY(ctx->passbb)) {
+ rv = ap_pass_brigade(f->next, ctx->passbb);
+ apr_brigade_cleanup(ctx->passbb);
+ if (rv != APR_SUCCESS) {
+ apr_pool_clear(ctx->tpool);
+ return rv;
+ }
+ }
+ apr_pool_clear(ctx->tpool);
+ }
+
+ /* Anything left we want to save/setaside for the next go-around */
+ if (!APR_BRIGADE_EMPTY(ctx->linebb)) {
+ /*
+ * Provide ap_save_brigade with an existing empty brigade
+ * (ctx->linesbb) to avoid creating a new one.
+ */
+ ap_save_brigade(f, &(ctx->linesbb), &(ctx->linebb), f->r->pool);
+ tmp_bb = ctx->linebb;
+ ctx->linebb = ctx->linesbb;
+ ctx->linesbb = tmp_bb;
+ }
+
+ return APR_SUCCESS;
+}
+
+static const char *set_pattern(cmd_parms *cmd, void *cfg, const char *line)
+{
+ char *from = NULL;
+ char *to = NULL;
+ char *flags = NULL;
+ char *ourline;
+ char delim;
+ subst_pattern_t *nscript;
+ int is_pattern = 0;
+ int ignore_case = 0;
+ int flatten = 0;
+ ap_regex_t *r = NULL;
+
+ if (apr_tolower(*line) != 's') {
+ return "Bad Substitute format, must be an s/// pattern";
+ }
+ ourline = apr_pstrdup(cmd->pool, line);
+ delim = *++ourline;
+ if (delim)
+ from = ++ourline;
+ if (from) {
+ while (*++ourline && *ourline != delim);
+ if (*ourline) {
+ *ourline = '\0';
+ to = ++ourline;
+ }
+ }
+ if (to) {
+ while (*++ourline && *ourline != delim);
+ if (*ourline) {
+ *ourline = '\0';
+ flags = ++ourline;
+ }
+ }
+
+ if (!delim || !from || !to) {
+ return "Bad Substitute format, must be a complete s/// pattern";
+ }
+
+ while (*flags) {
+ delim = apr_tolower(*flags); /* re-use */
+ if (delim == 'i')
+ ignore_case = 1;
+ else if (delim == 'n')
+ is_pattern = 1;
+ else if (delim == 'f')
+ flatten = 1;
+ else
+ return "Bad Substitute flag, only s///[inf] are supported";
+ flags++;
+ }
+
+ /* first see if we can compile the regex */
+ if (!is_pattern) {
+ r = ap_pregcomp(cmd->pool, from, AP_REG_EXTENDED |
+ (ignore_case ? AP_REG_ICASE : 0));
+ if (!r)
+ return "Substitute could not compile regex";
+ }
+ nscript = apr_array_push(((subst_dir_conf *) cfg)->patterns);
+ /* init the new entries */
+ nscript->pattern = NULL;
+ nscript->regexp = NULL;
+ nscript->replacement = NULL;
+ nscript->patlen = 0;
+
+ if (is_pattern) {
+ nscript->patlen = strlen(from);
+ nscript->pattern = apr_strmatch_precompile(cmd->pool, from,
+ !ignore_case);
+ }
+ else {
+ nscript->regexp = r;
+ }
+
+ nscript->replacement = to;
+ nscript->replen = strlen(to);
+ nscript->flatten = flatten;
+
+ return NULL;
+}
+
+#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH
+static void register_hooks(apr_pool_t *pool)
+{
+ ap_register_output_filter(substitute_filter_name, substitute_filter,
+ NULL, AP_FTYPE_RESOURCE);
+}
+
+static const command_rec substitute_cmds[] = {
+ AP_INIT_TAKE1("Substitute", set_pattern, NULL, OR_ALL,
+ "Pattern to filter the response content (s/foo/bar/[inf])"),
+ {NULL}
+};
+
+module AP_MODULE_DECLARE_DATA substitute_module = {
+ STANDARD20_MODULE_STUFF,
+ create_substitute_dcfg, /* dir config creater */
+ merge_substitute_dcfg, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ substitute_cmds, /* command table */
+ register_hooks /* register hooks */
+};
diff --git a/modules/filters/mod_substitute.dsp b/modules/filters/mod_substitute.dsp
new file mode 100644
index 00000000..def4157e
--- /dev/null
+++ b/modules/filters/mod_substitute.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_substitute" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_substitute - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_substitute.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For substitute:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_substitute.mak" CFG="mod_substitute - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_substitute - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_substitute - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_substitute - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_substitute_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_substitute.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_substitute.so" /d LONG_NAME="substitute_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_substitute.so" /base:@..\..\os\win32\BaseAddr.ref,mod_substitute.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_substitute.so" /base:@..\..\os\win32\BaseAddr.ref,mod_substitute.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_substitute.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_substitute - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_substitute_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_substitute.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_substitute.so" /d LONG_NAME="substitute_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_substitute.so" /base:@..\..\os\win32\BaseAddr.ref,mod_substitute.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_substitute.so" /base:@..\..\os\win32\BaseAddr.ref,mod_substitute.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_substitute.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_substitute - Win32 Release"
+# Name "mod_substitute - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_substitute.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project