diff options
author | Stefan Fritsch <sf@sfritsch.de> | 2011-12-27 19:42:17 +0100 |
---|---|---|
committer | Stefan Fritsch <sf@sfritsch.de> | 2011-12-27 19:42:17 +0100 |
commit | 9e615cb6aa4afcee97f8a1646e5a586261a7b81f (patch) | |
tree | 0e09fde2404555dc5daf167b38243b5f89c16549 /modules/filters | |
parent | 1acac7a6b494db24f8f58e44dab7657b6de68742 (diff) | |
download | apache2-9e615cb6aa4afcee97f8a1646e5a586261a7b81f.tar.gz |
Upstream tarball 2.2.8upstream/2.2.8
Diffstat (limited to 'modules/filters')
-rw-r--r-- | modules/filters/NWGNUmakefile | 1 | ||||
-rw-r--r-- | modules/filters/NWGNUsubstitute | 258 | ||||
-rw-r--r-- | modules/filters/config.m4 | 1 | ||||
-rw-r--r-- | modules/filters/mod_charset_lite.c | 151 | ||||
-rw-r--r-- | modules/filters/mod_deflate.c | 26 | ||||
-rw-r--r-- | modules/filters/mod_deflate.dsp | 4 | ||||
-rw-r--r-- | modules/filters/mod_ext_filter.dsp | 4 | ||||
-rw-r--r-- | modules/filters/mod_filter.c | 11 | ||||
-rw-r--r-- | modules/filters/mod_filter.dsp | 4 | ||||
-rw-r--r-- | modules/filters/mod_include.c | 68 | ||||
-rw-r--r-- | modules/filters/mod_substitute.c | 584 | ||||
-rw-r--r-- | modules/filters/mod_substitute.dsp | 111 |
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 ? ¤t->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 |