Blame modules/filters/mod_filter.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#define APR_WANT_STRFUNC
Packit 90a5c9
#include "apr_want.h"
Packit 90a5c9
#include "apr_lib.h"
Packit 90a5c9
#include "apr_strings.h"
Packit 90a5c9
#include "apr_hash.h"
Packit 90a5c9
#include "httpd.h"
Packit 90a5c9
#include "http_config.h"
Packit 90a5c9
#include "http_request.h"
Packit 90a5c9
#include "http_log.h"
Packit 90a5c9
#include "util_filter.h"
Packit 90a5c9
#include "ap_expr.h"
Packit 90a5c9
Packit 90a5c9
module AP_MODULE_DECLARE_DATA filter_module;
Packit 90a5c9
Packit 90a5c9
/**
Packit 90a5c9
 * @brief is a filter provider, as defined and implemented by mod_filter.
Packit 90a5c9
 *
Packit 90a5c9
 * The struct is a linked list, with dispatch criteria
Packit 90a5c9
 * defined for each filter.  The provider implementation itself is a
Packit 90a5c9
 * (2.0-compatible) ap_filter_rec_t* frec.
Packit 90a5c9
 */
Packit 90a5c9
struct ap_filter_provider_t {
Packit 90a5c9
    ap_expr_info_t *expr;
Packit 90a5c9
    const char **types;
Packit 90a5c9
Packit 90a5c9
    /** The filter that implements this provider */
Packit 90a5c9
    ap_filter_rec_t *frec;
Packit 90a5c9
Packit 90a5c9
    /** The next provider in the list */
Packit 90a5c9
    ap_filter_provider_t *next;
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
/** we need provider_ctx to save ctx values set by providers in filter_init */
Packit 90a5c9
typedef struct provider_ctx provider_ctx;
Packit 90a5c9
struct provider_ctx {
Packit 90a5c9
    ap_filter_provider_t *provider;
Packit 90a5c9
    void *ctx;
Packit 90a5c9
    provider_ctx *next;
Packit 90a5c9
};
Packit 90a5c9
typedef struct {
Packit 90a5c9
    ap_out_filter_func func;
Packit 90a5c9
    void *fctx;
Packit 90a5c9
    provider_ctx *init_ctx;
Packit 90a5c9
} harness_ctx;
Packit 90a5c9
Packit 90a5c9
typedef struct mod_filter_chain {
Packit 90a5c9
    const char *fname;
Packit 90a5c9
    struct mod_filter_chain *next;
Packit 90a5c9
} mod_filter_chain;
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    apr_hash_t *live_filters;
Packit 90a5c9
    mod_filter_chain *chain;
Packit 90a5c9
} mod_filter_cfg;
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    const char* range ;
Packit 90a5c9
} mod_filter_ctx ;
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static void filter_trace(conn_rec *c, int debug, const char *fname,
Packit 90a5c9
                         apr_bucket_brigade *bb)
Packit 90a5c9
{
Packit 90a5c9
    apr_bucket *b;
Packit 90a5c9
Packit 90a5c9
    switch (debug) {
Packit 90a5c9
    case 0:        /* normal, operational use */
Packit 90a5c9
        return;
Packit 90a5c9
    case 1:        /* mod_diagnostics level */
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01375) "%s", fname);
Packit 90a5c9
        for (b = APR_BRIGADE_FIRST(bb);
Packit 90a5c9
             b != APR_BRIGADE_SENTINEL(bb);
Packit 90a5c9
             b = APR_BUCKET_NEXT(b)) {
Packit 90a5c9
Packit 90a5c9
            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01376)
Packit 90a5c9
                          "%s: type: %s, length: %" APR_SIZE_T_FMT,
Packit 90a5c9
                          fname, b->type->name ? b->type->name : "(unknown)",
Packit 90a5c9
                          b->length);
Packit 90a5c9
        }
Packit 90a5c9
        break;
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int filter_init(ap_filter_t *f)
Packit 90a5c9
{
Packit 90a5c9
    ap_filter_provider_t *p;
Packit 90a5c9
    provider_ctx *pctx;
Packit 90a5c9
    int err;
Packit 90a5c9
    ap_filter_rec_t *filter = f->frec;
Packit 90a5c9
Packit 90a5c9
    harness_ctx *fctx = apr_pcalloc(f->r->pool, sizeof(harness_ctx));
Packit 90a5c9
    for (p = filter->providers; p; p = p->next) {
Packit 90a5c9
        if (p->frec->filter_init_func == filter_init) {
Packit 90a5c9
            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(01377)
Packit 90a5c9
                          "Chaining of FilterProviders not supported");
Packit 90a5c9
            return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
        }
Packit 90a5c9
        else if (p->frec->filter_init_func) {
Packit 90a5c9
            f->ctx = NULL;
Packit 90a5c9
            if ((err = p->frec->filter_init_func(f)) != OK) {
Packit 90a5c9
                ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(01378)
Packit 90a5c9
                              "filter_init for %s failed", p->frec->name);
Packit 90a5c9
                return err;   /* if anyone errors out here, so do we */
Packit 90a5c9
            }
Packit 90a5c9
            if (f->ctx != NULL) {
Packit 90a5c9
                /* the filter init function set a ctx - we need to record it */
Packit 90a5c9
                pctx = apr_pcalloc(f->r->pool, sizeof(provider_ctx));
Packit 90a5c9
                pctx->provider = p;
Packit 90a5c9
                pctx->ctx = f->ctx;
Packit 90a5c9
                pctx->next = fctx->init_ctx;
Packit 90a5c9
                fctx->init_ctx = pctx;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    f->ctx = fctx;
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
Packit 90a5c9
{
Packit 90a5c9
    ap_filter_provider_t *provider;
Packit 90a5c9
    int match = 0;
Packit 90a5c9
    const char *err = NULL;
Packit 90a5c9
    request_rec *r = f->r;
Packit 90a5c9
    harness_ctx *ctx = f->ctx;
Packit 90a5c9
    provider_ctx *pctx;
Packit 90a5c9
#ifndef NO_PROTOCOL
Packit 90a5c9
    unsigned int proto_flags;
Packit 90a5c9
    mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
Packit 90a5c9
                                                &filter_module);
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
    /* Check registered providers in order */
Packit 90a5c9
    for (provider = filter->providers; provider; provider = provider->next) {
Packit 90a5c9
        if (provider->expr) {
Packit 90a5c9
            match = ap_expr_exec(r, provider->expr, &err;;
Packit 90a5c9
            if (err) {
Packit 90a5c9
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01379)
Packit 90a5c9
                              "Error evaluating filter dispatch condition: %s",
Packit 90a5c9
                              err);
Packit 90a5c9
                match = 0;
Packit 90a5c9
            }
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
Packit 90a5c9
                          "Expression condition for '%s' %s",
Packit 90a5c9
                          provider->frec->name,
Packit 90a5c9
                          match ? "matched" : "did not match");
Packit 90a5c9
        }
Packit 90a5c9
        else if (r->content_type) {
Packit 90a5c9
            const char **type = provider->types;
Packit 90a5c9
            size_t len = strcspn(r->content_type, "; \t");
Packit 90a5c9
            AP_DEBUG_ASSERT(type != NULL);
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
Packit 90a5c9
                          "Content-Type '%s' ...", r->content_type);
Packit 90a5c9
            while (*type) {
Packit 90a5c9
                /* Handle 'content-type;charset=...' correctly */
Packit 90a5c9
                if (strncmp(*type, r->content_type, len) == 0
Packit 90a5c9
                    && (*type)[len] == '\0') {
Packit 90a5c9
                    ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
Packit 90a5c9
                                  "... matched '%s'", *type);
Packit 90a5c9
                    match = 1;
Packit 90a5c9
                    break;
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
Packit 90a5c9
                                  "... did not match '%s'", *type);
Packit 90a5c9
                }
Packit 90a5c9
                type++;
Packit 90a5c9
            }
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
Packit 90a5c9
                          "Content-Type condition for '%s' %s",
Packit 90a5c9
                          provider->frec->name,
Packit 90a5c9
                          match ? "matched" : "did not match");
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
Packit 90a5c9
                          "Content-Type condition for '%s' did not match: "
Packit 90a5c9
                          "no Content-Type", provider->frec->name);
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (match) {
Packit 90a5c9
            /* condition matches this provider */
Packit 90a5c9
#ifndef NO_PROTOCOL
Packit 90a5c9
            /* check protocol
Packit 90a5c9
             *
Packit 90a5c9
             * FIXME:
Packit 90a5c9
             * This is a quick hack and almost certainly buggy.
Packit 90a5c9
             * The idea is that by putting this in mod_filter, we relieve
Packit 90a5c9
             * filter implementations of the burden of fixing up HTTP headers
Packit 90a5c9
             * for cases that are routinely affected by filters.
Packit 90a5c9
             *
Packit 90a5c9
             * Default is ALWAYS to do nothing, so as not to tread on the
Packit 90a5c9
             * toes of filters which want to do it themselves.
Packit 90a5c9
             *
Packit 90a5c9
             */
Packit 90a5c9
            proto_flags = provider->frec->proto_flags;
Packit 90a5c9
Packit 90a5c9
            /* some specific things can't happen in a proxy */
Packit 90a5c9
            if (r->proxyreq) {
Packit 90a5c9
                if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
Packit 90a5c9
                    /* can't use this provider; try next */
Packit 90a5c9
                    continue;
Packit 90a5c9
                }
Packit 90a5c9
Packit 90a5c9
                if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
Packit 90a5c9
                    const char *str = apr_table_get(r->headers_out,
Packit 90a5c9
                                                    "Cache-Control");
Packit 90a5c9
                    if (str) {
Packit 90a5c9
                        if (ap_strcasestr(str, "no-transform")) {
Packit 90a5c9
                            /* can't use this provider; try next */
Packit 90a5c9
                            continue;
Packit 90a5c9
                        }
Packit 90a5c9
                    }
Packit 90a5c9
                    apr_table_addn(r->headers_out, "Warning",
Packit 90a5c9
                                   apr_psprintf(r->pool,
Packit 90a5c9
                                                "214 %s Transformation applied",
Packit 90a5c9
                                                r->hostname));
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            /* things that are invalidated if the filter transforms content */
Packit 90a5c9
            if (proto_flags & AP_FILTER_PROTO_CHANGE) {
Packit 90a5c9
                apr_table_unset(r->headers_out, "Content-MD5");
Packit 90a5c9
                apr_table_unset(r->headers_out, "ETag");
Packit 90a5c9
                if (proto_flags & AP_FILTER_PROTO_CHANGE_LENGTH) {
Packit 90a5c9
                    apr_table_unset(r->headers_out, "Content-Length");
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            /* no-cache is for a filter that has different effect per-hit */
Packit 90a5c9
            if (proto_flags & AP_FILTER_PROTO_NO_CACHE) {
Packit 90a5c9
                apr_table_unset(r->headers_out, "Last-Modified");
Packit 90a5c9
                apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) {
Packit 90a5c9
                apr_table_setn(r->headers_out, "Accept-Ranges", "none");
Packit 90a5c9
            }
Packit 90a5c9
            else if (rctx && rctx->range) {
Packit 90a5c9
                /* restore range header we saved earlier */
Packit 90a5c9
                apr_table_setn(r->headers_in, "Range", rctx->range);
Packit 90a5c9
                rctx->range = NULL;
Packit 90a5c9
            }
Packit 90a5c9
#endif
Packit 90a5c9
            for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) {
Packit 90a5c9
                if (pctx->provider == provider) {
Packit 90a5c9
                    ctx->fctx = pctx->ctx ;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            ctx->func = provider->frec->filter_func.out_func;
Packit 90a5c9
            return 1;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* No provider matched */
Packit 90a5c9
    return 0;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t ret;
Packit 90a5c9
#ifndef NO_PROTOCOL
Packit 90a5c9
    const char *cachecontrol;
Packit 90a5c9
#endif
Packit 90a5c9
    harness_ctx *ctx = f->ctx;
Packit 90a5c9
    ap_filter_rec_t *filter = f->frec;
Packit 90a5c9
Packit 90a5c9
    if (f->r->status != 200
Packit 90a5c9
        && !apr_table_get(f->r->subprocess_env, "filter-errordocs")) {
Packit 90a5c9
        ap_remove_output_filter(f);
Packit 90a5c9
        return ap_pass_brigade(f->next, bb);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    filter_trace(f->c, filter->debug, f->frec->name, bb);
Packit 90a5c9
Packit 90a5c9
    /* look up a handler function if we haven't already set it */
Packit 90a5c9
    if (!ctx->func) {
Packit 90a5c9
#ifndef NO_PROTOCOL
Packit 90a5c9
        if (f->r->proxyreq) {
Packit 90a5c9
            if (filter->proto_flags & AP_FILTER_PROTO_NO_PROXY) {
Packit 90a5c9
                ap_remove_output_filter(f);
Packit 90a5c9
                return ap_pass_brigade(f->next, bb);
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            if (filter->proto_flags & AP_FILTER_PROTO_TRANSFORM) {
Packit 90a5c9
                cachecontrol = apr_table_get(f->r->headers_out,
Packit 90a5c9
                                             "Cache-Control");
Packit 90a5c9
                if (cachecontrol) {
Packit 90a5c9
                    if (ap_strcasestr(cachecontrol, "no-transform")) {
Packit 90a5c9
                        ap_remove_output_filter(f);
Packit 90a5c9
                        return ap_pass_brigade(f->next, bb);
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
#endif
Packit 90a5c9
        if (!filter_lookup(f, filter)) {
Packit 90a5c9
            ap_remove_output_filter(f);
Packit 90a5c9
            return ap_pass_brigade(f->next, bb);
Packit 90a5c9
        }
Packit 90a5c9
        AP_DEBUG_ASSERT(ctx->func != NULL);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* call the content filter with its own context, then restore our
Packit 90a5c9
     * context
Packit 90a5c9
     */
Packit 90a5c9
    f->ctx = ctx->fctx;
Packit 90a5c9
    ret = ctx->func(f, bb);
Packit 90a5c9
    ctx->fctx = f->ctx;
Packit 90a5c9
    f->ctx = ctx;
Packit 90a5c9
Packit 90a5c9
    return ret;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
#ifndef NO_PROTOCOL
Packit 90a5c9
static const char *filter_protocol(cmd_parms *cmd, void *CFG, const char *fname,
Packit 90a5c9
                                   const char *pname, const char *proto)
Packit 90a5c9
{
Packit 90a5c9
    static const char *sep = ";, \t";
Packit 90a5c9
    char *arg;
Packit 90a5c9
    char *tok = 0;
Packit 90a5c9
    unsigned int flags = 0;
Packit 90a5c9
    mod_filter_cfg *cfg = CFG;
Packit 90a5c9
    ap_filter_provider_t *provider = NULL;
Packit 90a5c9
    ap_filter_rec_t *filter = apr_hash_get(cfg->live_filters, fname,
Packit 90a5c9
                                           APR_HASH_KEY_STRING);
Packit 90a5c9
Packit 90a5c9
    if (!filter) {
Packit 90a5c9
        return "FilterProtocol: No such filter";
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Fixup the args: it's really pname that's optional */
Packit 90a5c9
    if (proto == NULL) {
Packit 90a5c9
        proto = pname;
Packit 90a5c9
        pname = NULL;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        /* Find provider */
Packit 90a5c9
        for (provider = filter->providers; provider; provider = provider->next) {
Packit 90a5c9
            if (!strcasecmp(provider->frec->name, pname)) {
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        if (!provider) {
Packit 90a5c9
            return "FilterProtocol: No such provider for this filter";
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Now set flags from our args */
Packit 90a5c9
    for (arg = apr_strtok(apr_pstrdup(cmd->temp_pool, proto), sep, &tok;;
Packit 90a5c9
         arg; arg = apr_strtok(NULL, sep, &tok)) {
Packit 90a5c9
Packit 90a5c9
        if (!strcasecmp(arg, "change=yes")) {
Packit 90a5c9
            flags |= AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH;
Packit 90a5c9
        }
Packit 90a5c9
        if (!strcasecmp(arg, "change=no")) {
Packit 90a5c9
            flags &= ~(AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH);
Packit 90a5c9
        }
Packit 90a5c9
        else if (!strcasecmp(arg, "change=1:1")) {
Packit 90a5c9
            flags |= AP_FILTER_PROTO_CHANGE;
Packit 90a5c9
        }
Packit 90a5c9
        else if (!strcasecmp(arg, "byteranges=no")) {
Packit 90a5c9
            flags |= AP_FILTER_PROTO_NO_BYTERANGE;
Packit 90a5c9
        }
Packit 90a5c9
        else if (!strcasecmp(arg, "proxy=no")) {
Packit 90a5c9
            flags |= AP_FILTER_PROTO_NO_PROXY;
Packit 90a5c9
        }
Packit 90a5c9
        else if (!strcasecmp(arg, "proxy=transform")) {
Packit 90a5c9
            flags |= AP_FILTER_PROTO_TRANSFORM;
Packit 90a5c9
        }
Packit 90a5c9
        else if (!strcasecmp(arg, "cache=no")) {
Packit 90a5c9
            flags |= AP_FILTER_PROTO_NO_CACHE;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (pname) {
Packit 90a5c9
        provider->frec->proto_flags = flags;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        filter->proto_flags = flags;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname,
Packit 90a5c9
                                  const char *place)
Packit 90a5c9
{
Packit 90a5c9
    mod_filter_cfg *cfg = (mod_filter_cfg *)CFG;
Packit 90a5c9
    ap_filter_rec_t *filter;
Packit 90a5c9
Packit 90a5c9
    filter = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
Packit 90a5c9
    apr_hash_set(cfg->live_filters, fname, APR_HASH_KEY_STRING, filter);
Packit 90a5c9
Packit 90a5c9
    filter->name = fname;
Packit 90a5c9
    filter->filter_init_func = filter_init;
Packit 90a5c9
    filter->filter_func.out_func = filter_harness;
Packit 90a5c9
    filter->ftype = AP_FTYPE_RESOURCE;
Packit 90a5c9
    filter->next = NULL;
Packit 90a5c9
Packit 90a5c9
    if (place) {
Packit 90a5c9
        if (!strcasecmp(place, "CONTENT_SET")) {
Packit 90a5c9
            filter->ftype = AP_FTYPE_CONTENT_SET;
Packit 90a5c9
        }
Packit 90a5c9
        else if (!strcasecmp(place, "PROTOCOL")) {
Packit 90a5c9
            filter->ftype = AP_FTYPE_PROTOCOL;
Packit 90a5c9
        }
Packit 90a5c9
        else if (!strcasecmp(place, "CONNECTION")) {
Packit 90a5c9
            filter->ftype = AP_FTYPE_CONNECTION;
Packit 90a5c9
        }
Packit 90a5c9
        else if (!strcasecmp(place, "NETWORK")) {
Packit 90a5c9
            filter->ftype = AP_FTYPE_NETWORK;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *add_filter(cmd_parms *cmd, void *CFG,
Packit 90a5c9
                              const char *fname, const char *pname,
Packit 90a5c9
                              const char *expr, const char **types)
Packit 90a5c9
{
Packit 90a5c9
    mod_filter_cfg *cfg = CFG;
Packit 90a5c9
    ap_filter_provider_t *provider;
Packit 90a5c9
    const char *c;
Packit 90a5c9
    ap_filter_rec_t* frec;
Packit 90a5c9
    ap_filter_rec_t* provider_frec;
Packit 90a5c9
    ap_expr_info_t *node;
Packit 90a5c9
    const char *err = NULL;
Packit 90a5c9
Packit 90a5c9
    /* if provider has been registered, we can look it up */
Packit 90a5c9
    provider_frec = ap_get_output_filter_handle(pname);
Packit 90a5c9
    if (!provider_frec) {
Packit 90a5c9
        return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* fname has been declared with DeclareFilter, so we can look it up */
Packit 90a5c9
    frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
Packit 90a5c9
Packit 90a5c9
    /* or if provider is mod_filter itself, we can also look it up */
Packit 90a5c9
    if (!frec) {
Packit 90a5c9
        c = filter_declare(cmd, CFG, fname, NULL);
Packit 90a5c9
        if ( c ) {
Packit 90a5c9
            return c;
Packit 90a5c9
        }
Packit 90a5c9
        frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
Packit 90a5c9
        frec->ftype = provider_frec->ftype;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!frec) {
Packit 90a5c9
        return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t));
Packit 90a5c9
    if (expr) {
Packit 90a5c9
        node = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
Packit 90a5c9
        if (err) {
Packit 90a5c9
            return apr_pstrcat(cmd->pool,
Packit 90a5c9
                               "Error parsing FilterProvider expression:", err,
Packit 90a5c9
                               NULL);
Packit 90a5c9
        }
Packit 90a5c9
        provider->expr = node;
Packit 90a5c9
        provider->types = NULL;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        provider->types = types;
Packit 90a5c9
        provider->expr = NULL;
Packit 90a5c9
    }
Packit 90a5c9
    provider->frec = provider_frec;
Packit 90a5c9
    provider->next = frec->providers;
Packit 90a5c9
    frec->providers = provider;
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *filter_provider(cmd_parms *cmd, void *CFG,
Packit 90a5c9
                                   const char *fname, const char *pname,
Packit 90a5c9
                                   const char *expr)
Packit 90a5c9
{
Packit 90a5c9
    return add_filter(cmd, CFG, fname, pname, expr, NULL);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    mod_filter_chain *p;
Packit 90a5c9
    mod_filter_chain *q;
Packit 90a5c9
    mod_filter_cfg *cfg = CFG;
Packit 90a5c9
Packit 90a5c9
    switch (arg[0]) {
Packit 90a5c9
    case '+':        /* add to end of chain */
Packit 90a5c9
        p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
Packit 90a5c9
        p->fname = arg+1;
Packit 90a5c9
        if (cfg->chain) {
Packit 90a5c9
            for (q = cfg->chain; q->next; q = q->next);
Packit 90a5c9
            q->next = p;
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            cfg->chain = p;
Packit 90a5c9
        }
Packit 90a5c9
        break;
Packit 90a5c9
Packit 90a5c9
    case '@':        /* add to start of chain */
Packit 90a5c9
        p = apr_palloc(cmd->pool, sizeof(mod_filter_chain));
Packit 90a5c9
        p->fname = arg+1;
Packit 90a5c9
        p->next = cfg->chain;
Packit 90a5c9
        cfg->chain = p;
Packit 90a5c9
        break;
Packit 90a5c9
Packit 90a5c9
    case '-':        /* remove from chain */
Packit 90a5c9
        if (cfg->chain) {
Packit 90a5c9
            if (strcasecmp(cfg->chain->fname, arg+1)) {
Packit 90a5c9
                for (p = cfg->chain; p->next; p = p->next) {
Packit 90a5c9
                    if (!strcasecmp(p->next->fname, arg+1)) {
Packit 90a5c9
                        p->next = p->next->next;
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                cfg->chain = cfg->chain->next;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        break;
Packit 90a5c9
Packit 90a5c9
    case '!':        /* Empty the chain */
Packit 90a5c9
                     /** IG: Add a NULL provider to the beginning so that
Packit 90a5c9
                      *  we can ensure that we'll empty everything before
Packit 90a5c9
                      *  this when doing config merges later */
Packit 90a5c9
        p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
Packit 90a5c9
        p->fname = NULL;
Packit 90a5c9
        cfg->chain = p;
Packit 90a5c9
        break;
Packit 90a5c9
Packit 90a5c9
    case '=':        /* initialise chain with this arg */
Packit 90a5c9
                     /** IG: Prepend a NULL provider to the beginning as above */
Packit 90a5c9
        p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
Packit 90a5c9
        p->fname = NULL;
Packit 90a5c9
        p->next = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
Packit 90a5c9
        p->next->fname = arg+1;
Packit 90a5c9
        cfg->chain = p;
Packit 90a5c9
        break;
Packit 90a5c9
Packit 90a5c9
    default:        /* add to end */
Packit 90a5c9
        p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
Packit 90a5c9
        p->fname = arg;
Packit 90a5c9
        if (cfg->chain) {
Packit 90a5c9
            for (q = cfg->chain; q->next; q = q->next);
Packit 90a5c9
            q->next = p;
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            cfg->chain = p;
Packit 90a5c9
        }
Packit 90a5c9
        break;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *filter_bytype1(cmd_parms *cmd, void *CFG,
Packit 90a5c9
                                  const char *pname, const char **types)
Packit 90a5c9
{
Packit 90a5c9
    const char *rv;
Packit 90a5c9
    const char *fname;
Packit 90a5c9
    int seen_name = 0;
Packit 90a5c9
    mod_filter_cfg *cfg = CFG;
Packit 90a5c9
Packit 90a5c9
    /* construct fname from name */
Packit 90a5c9
    fname = apr_pstrcat(cmd->pool, "BYTYPE:", pname, NULL);
Packit 90a5c9
Packit 90a5c9
    /* check whether this is already registered, in which case
Packit 90a5c9
     * it's already in the filter chain
Packit 90a5c9
     */
Packit 90a5c9
    if (apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING)) {
Packit 90a5c9
        seen_name = 1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    rv = add_filter(cmd, CFG, fname, pname, NULL, types);
Packit 90a5c9
Packit 90a5c9
    /* If it's the first time through, add to filterchain */
Packit 90a5c9
    if (rv == NULL && !seen_name) {
Packit 90a5c9
        rv = filter_chain(cmd, CFG, fname);
Packit 90a5c9
    }
Packit 90a5c9
    return rv;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *filter_bytype(cmd_parms *cmd, void *CFG,
Packit 90a5c9
                                 int argc, char *const argv[])
Packit 90a5c9
{
Packit 90a5c9
    /* back compatibility, need to parse multiple components in filter name */
Packit 90a5c9
    char *pname;
Packit 90a5c9
    char *strtok_state = NULL;
Packit 90a5c9
    char *name;
Packit 90a5c9
    const char **types;
Packit 90a5c9
    const char *rv = NULL;
Packit 90a5c9
    if (argc < 2)
Packit 90a5c9
        return "AddOutputFilterByType requires at least two arguments";
Packit 90a5c9
    name = apr_pstrdup(cmd->temp_pool, argv[0]);
Packit 90a5c9
    types = apr_palloc(cmd->pool, argc * sizeof(char *));
Packit 90a5c9
    memcpy(types, &argv[1], (argc - 1) * sizeof(char *));
Packit 90a5c9
    types[argc-1] = NULL;
Packit 90a5c9
    for (pname = apr_strtok(name, ";", &strtok_state);
Packit 90a5c9
         pname != NULL && rv == NULL;
Packit 90a5c9
         pname = apr_strtok(NULL, ";", &strtok_state)) {
Packit 90a5c9
        rv = filter_bytype1(cmd, CFG, pname, types);
Packit 90a5c9
    }
Packit 90a5c9
    return rv;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *filter_debug(cmd_parms *cmd, void *CFG, const char *fname,
Packit 90a5c9
                                const char *level)
Packit 90a5c9
{
Packit 90a5c9
    mod_filter_cfg *cfg = CFG;
Packit 90a5c9
    ap_filter_rec_t *frec = apr_hash_get(cfg->live_filters, fname,
Packit 90a5c9
                                         APR_HASH_KEY_STRING);
Packit 90a5c9
    if (!frec) {
Packit 90a5c9
        return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
Packit 90a5c9
    }
Packit 90a5c9
    frec->debug = atoi(level);
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void filter_insert(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    mod_filter_chain *p;
Packit 90a5c9
    ap_filter_rec_t *filter;
Packit 90a5c9
    mod_filter_cfg *cfg = ap_get_module_config(r->per_dir_config,
Packit 90a5c9
                                               &filter_module);
Packit 90a5c9
#ifndef NO_PROTOCOL
Packit 90a5c9
    int ranges = 1;
Packit 90a5c9
    mod_filter_ctx *ctx = apr_pcalloc(r->pool, sizeof(mod_filter_ctx));
Packit 90a5c9
    ap_set_module_config(r->request_config, &filter_module, ctx);
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
    /** IG: Now that we've merged to the final config, go one last time
Packit 90a5c9
     *  through the chain, and prune out the NULL filters */
Packit 90a5c9
Packit 90a5c9
    for (p = cfg->chain; p; p = p->next) {
Packit 90a5c9
        if (p->fname == NULL)
Packit 90a5c9
            cfg->chain = p->next;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    for (p = cfg->chain; p; p = p->next) {
Packit 90a5c9
        filter = apr_hash_get(cfg->live_filters, p->fname, APR_HASH_KEY_STRING);
Packit 90a5c9
        if (filter == NULL) {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01380)
Packit 90a5c9
                          "Unknown filter %s not added", p->fname);
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
        ap_add_output_filter_handle(filter, NULL, r, r->connection);
Packit 90a5c9
#ifndef NO_PROTOCOL
Packit 90a5c9
        if (ranges && (filter->proto_flags
Packit 90a5c9
                       & (AP_FILTER_PROTO_NO_BYTERANGE
Packit 90a5c9
                          | AP_FILTER_PROTO_CHANGE_LENGTH))) {
Packit 90a5c9
            ctx->range = apr_table_get(r->headers_in, "Range");
Packit 90a5c9
            apr_table_unset(r->headers_in, "Range");
Packit 90a5c9
            ranges = 0;
Packit 90a5c9
        }
Packit 90a5c9
#endif
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void filter_hooks(apr_pool_t *pool)
Packit 90a5c9
{
Packit 90a5c9
    ap_hook_insert_filter(filter_insert, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void *filter_config(apr_pool_t *pool, char *x)
Packit 90a5c9
{
Packit 90a5c9
    mod_filter_cfg *cfg = apr_palloc(pool, sizeof(mod_filter_cfg));
Packit 90a5c9
    cfg->live_filters = apr_hash_make(pool);
Packit 90a5c9
    cfg->chain = NULL;
Packit 90a5c9
    return cfg;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void *filter_merge(apr_pool_t *pool, void *BASE, void *ADD)
Packit 90a5c9
{
Packit 90a5c9
    mod_filter_cfg *base = BASE;
Packit 90a5c9
    mod_filter_cfg *add = ADD;
Packit 90a5c9
    mod_filter_chain *savelink = 0;
Packit 90a5c9
    mod_filter_chain *newlink;
Packit 90a5c9
    mod_filter_chain *p;
Packit 90a5c9
    mod_filter_cfg *conf = apr_palloc(pool, sizeof(mod_filter_cfg));
Packit 90a5c9
Packit 90a5c9
    conf->live_filters = apr_hash_overlay(pool, add->live_filters,
Packit 90a5c9
                                          base->live_filters);
Packit 90a5c9
    if (base->chain && add->chain) {
Packit 90a5c9
        for (p = base->chain; p; p = p->next) {
Packit 90a5c9
            newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
Packit 90a5c9
            if (newlink->fname == NULL) {
Packit 90a5c9
                conf->chain = savelink = newlink;
Packit 90a5c9
            }
Packit 90a5c9
            else if (savelink) {
Packit 90a5c9
                savelink->next = newlink;
Packit 90a5c9
                savelink = newlink;
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                conf->chain = savelink = newlink;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        for (p = add->chain; p; p = p->next) {
Packit 90a5c9
            newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
Packit 90a5c9
            /** Filter out merged chain resets */
Packit 90a5c9
            if (newlink->fname == NULL) {
Packit 90a5c9
                conf->chain = savelink = newlink;
Packit 90a5c9
            }
Packit 90a5c9
            else if (savelink) {
Packit 90a5c9
                savelink->next = newlink;
Packit 90a5c9
                savelink = newlink;
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                conf->chain = savelink = newlink;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    else if (add->chain) {
Packit 90a5c9
        conf->chain = add->chain;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        conf->chain = base->chain;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return conf;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const command_rec filter_cmds[] = {
Packit 90a5c9
    AP_INIT_TAKE12("FilterDeclare", filter_declare, NULL, OR_OPTIONS,
Packit 90a5c9
        "filter-name [filter-type]"),
Packit 90a5c9
    AP_INIT_TAKE3("FilterProvider", filter_provider, NULL, OR_OPTIONS,
Packit 90a5c9
        "filter-name provider-name match-expression"),
Packit 90a5c9
    AP_INIT_ITERATE("FilterChain", filter_chain, NULL, OR_OPTIONS,
Packit 90a5c9
        "list of filter names with optional [+-=!@]"),
Packit 90a5c9
    AP_INIT_TAKE2("FilterTrace", filter_debug, NULL, RSRC_CONF | ACCESS_CONF,
Packit 90a5c9
        "filter-name debug-level"),
Packit 90a5c9
    AP_INIT_TAKE_ARGV("AddOutputFilterByType", filter_bytype, NULL, OR_FILEINFO,
Packit 90a5c9
        "output filter name followed by one or more content-types"),
Packit 90a5c9
#ifndef NO_PROTOCOL
Packit 90a5c9
    AP_INIT_TAKE23("FilterProtocol", filter_protocol, NULL, OR_OPTIONS,
Packit 90a5c9
        "filter-name [provider-name] protocol-args"),
Packit 90a5c9
#endif
Packit 90a5c9
    { NULL }
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(filter) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    filter_config,
Packit 90a5c9
    filter_merge,
Packit 90a5c9
    NULL,
Packit 90a5c9
    NULL,
Packit 90a5c9
    filter_cmds,
Packit 90a5c9
    filter_hooks
Packit 90a5c9
};