Blame modules/filters/mod_proxy_html.c

Packit 90a5c9
/*      Copyright (c) 2003-11, WebThing Ltd
Packit 90a5c9
 *      Copyright (c) 2011-, The Apache Software Foundation
Packit 90a5c9
 *
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
/*      GO_FASTER
Packit 90a5c9
        You can #define GO_FASTER to disable trace logging.
Packit 90a5c9
*/
Packit 90a5c9
Packit 90a5c9
#ifdef GO_FASTER
Packit 90a5c9
#define VERBOSE(x)
Packit 90a5c9
#define VERBOSEB(x)
Packit 90a5c9
#else
Packit 90a5c9
#define VERBOSE(x) if (verbose) x
Packit 90a5c9
#define VERBOSEB(x) if (verbose) {x}
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
/* libxml2 */
Packit 90a5c9
#include <libxml/HTMLparser.h>
Packit 90a5c9
Packit 90a5c9
#include "http_protocol.h"
Packit 90a5c9
#include "http_config.h"
Packit 90a5c9
#include "http_log.h"
Packit 90a5c9
#include "apr_strings.h"
Packit 90a5c9
#include "apr_hash.h"
Packit 90a5c9
#include "apr_strmatch.h"
Packit 90a5c9
#include "apr_lib.h"
Packit 90a5c9
Packit 90a5c9
#include "apr_optional.h"
Packit 90a5c9
#include "mod_xml2enc.h"
Packit 90a5c9
#include "http_request.h"
Packit 90a5c9
#include "ap_expr.h"
Packit 90a5c9
Packit 90a5c9
/* globals set once at startup */
Packit 90a5c9
static ap_rxplus_t *old_expr;
Packit 90a5c9
static ap_regex_t *seek_meta;
Packit 90a5c9
static const apr_strmatch_pattern* seek_content;
Packit 90a5c9
static apr_status_t (*xml2enc_charset)(request_rec*, xmlCharEncoding*, const char**) = NULL;
Packit 90a5c9
static apr_status_t (*xml2enc_filter)(request_rec*, const char*, unsigned int) = NULL;
Packit 90a5c9
Packit 90a5c9
module AP_MODULE_DECLARE_DATA proxy_html_module;
Packit 90a5c9
Packit 90a5c9
#define M_HTML                  0x01
Packit 90a5c9
#define M_EVENTS                0x02
Packit 90a5c9
#define M_CDATA                 0x04
Packit 90a5c9
#define M_REGEX                 0x08
Packit 90a5c9
#define M_ATSTART               0x10
Packit 90a5c9
#define M_ATEND                 0x20
Packit 90a5c9
#define M_LAST                  0x40
Packit 90a5c9
#define M_NOTLAST               0x80
Packit 90a5c9
#define M_INTERPOLATE_TO        0x100
Packit 90a5c9
#define M_INTERPOLATE_FROM      0x200
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    const char *val;
Packit 90a5c9
} tattr;
Packit 90a5c9
typedef struct {
Packit 90a5c9
    unsigned int start;
Packit 90a5c9
    unsigned int end;
Packit 90a5c9
} meta;
Packit 90a5c9
typedef struct urlmap {
Packit 90a5c9
    struct urlmap *next;
Packit 90a5c9
    unsigned int flags;
Packit 90a5c9
    unsigned int regflags;
Packit 90a5c9
    union {
Packit 90a5c9
        const char *c;
Packit 90a5c9
        ap_regex_t *r;
Packit 90a5c9
    } from;
Packit 90a5c9
    const char *to;
Packit 90a5c9
    ap_expr_info_t *cond;
Packit 90a5c9
} urlmap;
Packit 90a5c9
typedef struct {
Packit 90a5c9
    urlmap *map;
Packit 90a5c9
    const char *doctype;
Packit 90a5c9
    const char *etag;
Packit 90a5c9
    unsigned int flags;
Packit 90a5c9
    size_t bufsz;
Packit 90a5c9
    apr_hash_t *links;
Packit 90a5c9
    apr_array_header_t *events;
Packit 90a5c9
    const char *charset_out;
Packit 90a5c9
    int extfix;
Packit 90a5c9
    int metafix;
Packit 90a5c9
    int strip_comments;
Packit 90a5c9
    int interp;
Packit 90a5c9
    int enabled;
Packit 90a5c9
} proxy_html_conf;
Packit 90a5c9
typedef struct {
Packit 90a5c9
    ap_filter_t *f;
Packit 90a5c9
    proxy_html_conf *cfg;
Packit 90a5c9
    htmlParserCtxtPtr parser;
Packit 90a5c9
    apr_bucket_brigade *bb;
Packit 90a5c9
    char *buf;
Packit 90a5c9
    size_t offset;
Packit 90a5c9
    size_t avail;
Packit 90a5c9
    const char *encoding;
Packit 90a5c9
    urlmap *map;
Packit 90a5c9
    char rbuf[4];
Packit 90a5c9
    apr_size_t rlen;
Packit 90a5c9
    apr_size_t rmin;
Packit 90a5c9
} saxctxt;
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
#define NORM_LC 0x1
Packit 90a5c9
#define NORM_MSSLASH 0x2
Packit 90a5c9
#define NORM_RESET 0x4
Packit 90a5c9
static htmlSAXHandler sax;
Packit 90a5c9
Packit 90a5c9
typedef enum { ATTR_IGNORE, ATTR_URI, ATTR_EVENT } rewrite_t;
Packit 90a5c9
Packit 90a5c9
static const char *const fpi_html =
Packit 90a5c9
        "\n";
Packit 90a5c9
static const char *const fpi_html_legacy =
Packit 90a5c9
        "\n";
Packit 90a5c9
static const char *const fpi_xhtml =
Packit 90a5c9
        "\n";
Packit 90a5c9
static const char *const fpi_xhtml_legacy =
Packit 90a5c9
        "\n";
Packit 90a5c9
static const char *const fpi_html5 = "\n";
Packit 90a5c9
static const char *const html_etag = ">";
Packit 90a5c9
static const char *const xhtml_etag = " />";
Packit 90a5c9
/*#define DEFAULT_DOCTYPE fpi_html */
Packit 90a5c9
static const char *const DEFAULT_DOCTYPE = "";
Packit 90a5c9
#define DEFAULT_ETAG html_etag
Packit 90a5c9
Packit 90a5c9
static void normalise(unsigned int flags, char *str)
Packit 90a5c9
{
Packit 90a5c9
    char *p;
Packit 90a5c9
    if (flags & NORM_LC)
Packit 90a5c9
        for (p = str; *p; ++p)
Packit 90a5c9
            if (isupper(*p))
Packit 90a5c9
                *p = tolower(*p);
Packit 90a5c9
Packit 90a5c9
    if (flags & NORM_MSSLASH)
Packit 90a5c9
        for (p = ap_strchr(str, '\\'); p; p = ap_strchr(p+1, '\\'))
Packit 90a5c9
            *p = '/';
Packit 90a5c9
Packit 90a5c9
}
Packit 90a5c9
#define consume_buffer(ctx,inbuf,bytes,flag) \
Packit 90a5c9
        htmlParseChunk(ctx->parser, inbuf, bytes, flag)
Packit 90a5c9
Packit 90a5c9
#define AP_fwrite(ctx,inbuf,bytes,flush) \
Packit 90a5c9
        ap_fwrite(ctx->f->next, ctx->bb, inbuf, bytes);
Packit 90a5c9
Packit 90a5c9
/* This is always utf-8 on entry.  We can convert charset within FLUSH */
Packit 90a5c9
#define FLUSH AP_fwrite(ctx, (chars+begin), (i-begin), 0); begin = i+1
Packit 90a5c9
static void pcharacters(void *ctxt, const xmlChar *uchars, int length)
Packit 90a5c9
{
Packit 90a5c9
    const char *chars = (const char*) uchars;
Packit 90a5c9
    saxctxt *ctx = (saxctxt*) ctxt;
Packit 90a5c9
    int i;
Packit 90a5c9
    int begin;
Packit 90a5c9
    for (begin=i=0; i
Packit 90a5c9
        switch (chars[i]) {
Packit 90a5c9
        case '&' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, "&"); break;
Packit 90a5c9
        case '<' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, "<"); break;
Packit 90a5c9
        case '>' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, ">"); break;
Packit 90a5c9
        case '"' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, """); break;
Packit 90a5c9
        default : break;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    FLUSH;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void preserve(saxctxt *ctx, const size_t len)
Packit 90a5c9
{
Packit 90a5c9
    char *newbuf;
Packit 90a5c9
    if (len <= (ctx->avail - ctx->offset))
Packit 90a5c9
        return;
Packit 90a5c9
    else while (len > (ctx->avail - ctx->offset))
Packit 90a5c9
        ctx->avail += ctx->cfg->bufsz;
Packit 90a5c9
Packit 90a5c9
    newbuf = realloc(ctx->buf, ctx->avail);
Packit 90a5c9
    if (newbuf != ctx->buf) {
Packit 90a5c9
        if (ctx->buf)
Packit 90a5c9
            apr_pool_cleanup_kill(ctx->f->r->pool, ctx->buf,
Packit 90a5c9
                                  (int(*)(void*))free);
Packit 90a5c9
        apr_pool_cleanup_register(ctx->f->r->pool, newbuf,
Packit 90a5c9
                                  (int(*)(void*))free, apr_pool_cleanup_null);
Packit 90a5c9
        ctx->buf = newbuf;
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void pappend(saxctxt *ctx, const char *buf, const size_t len)
Packit 90a5c9
{
Packit 90a5c9
    preserve(ctx, len);
Packit 90a5c9
    memcpy(ctx->buf+ctx->offset, buf, len);
Packit 90a5c9
    ctx->offset += len;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void dump_content(saxctxt *ctx)
Packit 90a5c9
{
Packit 90a5c9
    urlmap *m;
Packit 90a5c9
    char *found;
Packit 90a5c9
    size_t s_from, s_to;
Packit 90a5c9
    size_t match;
Packit 90a5c9
    char c = 0;
Packit 90a5c9
    int nmatch;
Packit 90a5c9
    ap_regmatch_t pmatch[10];
Packit 90a5c9
    char *subs;
Packit 90a5c9
    size_t len, offs;
Packit 90a5c9
    urlmap *themap = ctx->map;
Packit 90a5c9
#ifndef GO_FASTER
Packit 90a5c9
    int verbose = APLOGrtrace1(ctx->f->r);
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
    pappend(ctx, &c, 1);        /* append null byte */
Packit 90a5c9
        /* parse the text for URLs */
Packit 90a5c9
    for (m = themap; m; m = m->next) {
Packit 90a5c9
        if (!(m->flags & M_CDATA))
Packit 90a5c9
            continue;
Packit 90a5c9
        if (m->flags & M_REGEX) {
Packit 90a5c9
            nmatch = 10;
Packit 90a5c9
            offs = 0;
Packit 90a5c9
            while (!ap_regexec(m->from.r, ctx->buf+offs, nmatch, pmatch, 0)) {
Packit 90a5c9
                match = pmatch[0].rm_so;
Packit 90a5c9
                s_from = pmatch[0].rm_eo - match;
Packit 90a5c9
                subs = ap_pregsub(ctx->f->r->pool, m->to, ctx->buf+offs,
Packit 90a5c9
                                  nmatch, pmatch);
Packit 90a5c9
                s_to = strlen(subs);
Packit 90a5c9
                len = strlen(ctx->buf);
Packit 90a5c9
                offs += match;
Packit 90a5c9
                VERBOSEB(
Packit 90a5c9
                    const char *f = apr_pstrndup(ctx->f->r->pool,
Packit 90a5c9
                    ctx->buf + offs, s_from);
Packit 90a5c9
                    ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r,
Packit 90a5c9
                                  "C/RX: match at %s, substituting %s", f, subs);
Packit 90a5c9
                )
Packit 90a5c9
                if (s_to > s_from) {
Packit 90a5c9
                    preserve(ctx, s_to - s_from);
Packit 90a5c9
                    memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from,
Packit 90a5c9
                            len + 1 - s_from - offs);
Packit 90a5c9
                    memcpy(ctx->buf+offs, subs, s_to);
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    memcpy(ctx->buf + offs, subs, s_to);
Packit 90a5c9
                    memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from,
Packit 90a5c9
                            len + 1 - s_from - offs);
Packit 90a5c9
                }
Packit 90a5c9
                offs += s_to;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            s_from = strlen(m->from.c);
Packit 90a5c9
            s_to = strlen(m->to);
Packit 90a5c9
            for (found = strstr(ctx->buf, m->from.c); found;
Packit 90a5c9
                 found = strstr(ctx->buf+match+s_to, m->from.c)) {
Packit 90a5c9
                match = found - ctx->buf;
Packit 90a5c9
                if ((m->flags & M_ATSTART) && (match != 0))
Packit 90a5c9
                    break;
Packit 90a5c9
                len = strlen(ctx->buf);
Packit 90a5c9
                if ((m->flags & M_ATEND) && (match < (len - s_from)))
Packit 90a5c9
                    continue;
Packit 90a5c9
                VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r,
Packit 90a5c9
                                      "C: matched %s, substituting %s",
Packit 90a5c9
                                      m->from.c, m->to));
Packit 90a5c9
                if (s_to > s_from) {
Packit 90a5c9
                    preserve(ctx, s_to - s_from);
Packit 90a5c9
                    memmove(ctx->buf+match+s_to, ctx->buf+match+s_from,
Packit 90a5c9
                            len + 1 - s_from - match);
Packit 90a5c9
                    memcpy(ctx->buf+match, m->to, s_to);
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    memcpy(ctx->buf+match, m->to, s_to);
Packit 90a5c9
                    memmove(ctx->buf+match+s_to, ctx->buf+match+s_from,
Packit 90a5c9
                            len + 1 - s_from - match);
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    AP_fwrite(ctx, ctx->buf, strlen(ctx->buf), 1);
Packit 90a5c9
}
Packit 90a5c9
static void pcdata(void *ctxt, const xmlChar *uchars, int length)
Packit 90a5c9
{
Packit 90a5c9
    const char *chars = (const char*) uchars;
Packit 90a5c9
    saxctxt *ctx = (saxctxt*) ctxt;
Packit 90a5c9
    if (ctx->cfg->extfix) {
Packit 90a5c9
        pappend(ctx, chars, length);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        /* not sure if this should force-flush
Packit 90a5c9
         * (i.e. can one cdata section come in multiple calls?)
Packit 90a5c9
         */
Packit 90a5c9
        AP_fwrite(ctx, chars, length, 0);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
static void pcomment(void *ctxt, const xmlChar *uchars)
Packit 90a5c9
{
Packit 90a5c9
    const char *chars = (const char*) uchars;
Packit 90a5c9
    saxctxt *ctx = (saxctxt*) ctxt;
Packit 90a5c9
    if (ctx->cfg->strip_comments)
Packit 90a5c9
        return;
Packit 90a5c9
Packit 90a5c9
    if (ctx->cfg->extfix) {
Packit 90a5c9
        pappend(ctx, "
Packit 90a5c9
        pappend(ctx, chars, strlen(chars));
Packit 90a5c9
        pappend(ctx, "-->", 3);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        ap_fputs(ctx->f->next, ctx->bb, "
Packit 90a5c9
        AP_fwrite(ctx, chars, strlen(chars), 1);
Packit 90a5c9
        ap_fputs(ctx->f->next, ctx->bb, "-->");
Packit 90a5c9
        dump_content(ctx);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
static void pendElement(void *ctxt, const xmlChar *uname)
Packit 90a5c9
{
Packit 90a5c9
    saxctxt *ctx = (saxctxt*) ctxt;
Packit 90a5c9
    const char *name = (const char*) uname;
Packit 90a5c9
    const htmlElemDesc* desc = htmlTagLookup(uname);
Packit 90a5c9
Packit 90a5c9
    if ((ctx->cfg->doctype == fpi_html) || (ctx->cfg->doctype == fpi_xhtml)) {
Packit 90a5c9
        /* enforce html */
Packit 90a5c9
        if (!desc || desc->depr)
Packit 90a5c9
            return;
Packit 90a5c9
    
Packit 90a5c9
    }
Packit 90a5c9
    else if ((ctx->cfg->doctype == fpi_html_legacy)
Packit 90a5c9
             || (ctx->cfg->doctype == fpi_xhtml_legacy)) {
Packit 90a5c9
        /* enforce html legacy */
Packit 90a5c9
        if (!desc)
Packit 90a5c9
            return;
Packit 90a5c9
    }
Packit 90a5c9
    /* TODO - implement HTML "allowed here" using the stack */
Packit 90a5c9
    /* nah.  Keeping the stack is too much overhead */
Packit 90a5c9
Packit 90a5c9
    if (ctx->offset > 0) {
Packit 90a5c9
        dump_content(ctx);
Packit 90a5c9
        ctx->offset = 0;        /* having dumped it, we can re-use the memory */
Packit 90a5c9
    }
Packit 90a5c9
    if (!desc || !desc->empty) {
Packit 90a5c9
        ap_fprintf(ctx->f->next, ctx->bb, "</%s>", name);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void pstartElement(void *ctxt, const xmlChar *uname,
Packit 90a5c9
                          const xmlChar** uattrs)
Packit 90a5c9
{
Packit 90a5c9
    int required_attrs;
Packit 90a5c9
    int num_match;
Packit 90a5c9
    size_t offs, len;
Packit 90a5c9
    char *subs;
Packit 90a5c9
    rewrite_t is_uri;
Packit 90a5c9
    const char** a;
Packit 90a5c9
    urlmap *m;
Packit 90a5c9
    size_t s_to, s_from, match;
Packit 90a5c9
    char *found;
Packit 90a5c9
    saxctxt *ctx = (saxctxt*) ctxt;
Packit 90a5c9
    size_t nmatch;
Packit 90a5c9
    ap_regmatch_t pmatch[10];
Packit 90a5c9
#ifndef GO_FASTER
Packit 90a5c9
    int verbose = APLOGrtrace1(ctx->f->r);
Packit 90a5c9
#endif
Packit 90a5c9
    apr_array_header_t *linkattrs;
Packit 90a5c9
    int i;
Packit 90a5c9
    const char *name = (const char*) uname;
Packit 90a5c9
    const char** attrs = (const char**) uattrs;
Packit 90a5c9
    const htmlElemDesc* desc = htmlTagLookup(uname);
Packit 90a5c9
    urlmap *themap = ctx->map;
Packit 90a5c9
#ifdef HAVE_STACK
Packit 90a5c9
    const void** descp;
Packit 90a5c9
#endif
Packit 90a5c9
    int enforce = 0;
Packit 90a5c9
    if ((ctx->cfg->doctype == fpi_html) || (ctx->cfg->doctype == fpi_xhtml)) {
Packit 90a5c9
        /* enforce html */
Packit 90a5c9
        if (!desc || desc->depr) {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01416)
Packit 90a5c9
                          "Bogus HTML element %s dropped", name);
Packit 90a5c9
            return;
Packit 90a5c9
        }
Packit 90a5c9
        enforce = 2;
Packit 90a5c9
    }
Packit 90a5c9
    else if ((ctx->cfg->doctype == fpi_html_legacy)
Packit 90a5c9
             || (ctx->cfg->doctype == fpi_xhtml_legacy)) {
Packit 90a5c9
        /* enforce html legacy */
Packit 90a5c9
        if (!desc) {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01417)
Packit 90a5c9
                          "Deprecated HTML element %s dropped", name);
Packit 90a5c9
            return;
Packit 90a5c9
        }
Packit 90a5c9
        enforce = 1;
Packit 90a5c9
    }
Packit 90a5c9
#ifdef HAVE_STACK
Packit 90a5c9
    descp = apr_array_push(ctx->stack);
Packit 90a5c9
    *descp = desc;
Packit 90a5c9
    /* TODO - implement HTML "allowed here" */
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
    ap_fputc(ctx->f->next, ctx->bb, '<');
Packit 90a5c9
    ap_fputs(ctx->f->next, ctx->bb, name);
Packit 90a5c9
Packit 90a5c9
    required_attrs = 0;
Packit 90a5c9
    if ((enforce > 0) && (desc != NULL) && (desc->attrs_req != NULL))
Packit 90a5c9
        for (a = desc->attrs_req; *a; a++)
Packit 90a5c9
            ++required_attrs;
Packit 90a5c9
Packit 90a5c9
    if (attrs) {
Packit 90a5c9
        linkattrs = apr_hash_get(ctx->cfg->links, name, APR_HASH_KEY_STRING);
Packit 90a5c9
        for (a = attrs; *a; a += 2) {
Packit 90a5c9
            if (desc && enforce > 0) {
Packit 90a5c9
                switch (htmlAttrAllowed(desc, (xmlChar*)*a, 2-enforce)) {
Packit 90a5c9
                case HTML_INVALID:
Packit 90a5c9
                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01418)
Packit 90a5c9
                                  "Bogus HTML attribute %s of %s dropped",
Packit 90a5c9
                                  *a, name);
Packit 90a5c9
                    continue;
Packit 90a5c9
                case HTML_DEPRECATED:
Packit 90a5c9
                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01419)
Packit 90a5c9
                                  "Deprecated HTML attribute %s of %s dropped",
Packit 90a5c9
                                  *a, name);
Packit 90a5c9
                    continue;
Packit 90a5c9
                case HTML_REQUIRED:
Packit 90a5c9
                    required_attrs--;   /* cross off the number still needed */
Packit 90a5c9
                /* fallthrough - required implies valid */
Packit 90a5c9
                default:
Packit 90a5c9
                    break;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            ctx->offset = 0;
Packit 90a5c9
            if (a[1]) {
Packit 90a5c9
                pappend(ctx, a[1], strlen(a[1])+1);
Packit 90a5c9
                is_uri = ATTR_IGNORE;
Packit 90a5c9
                if (linkattrs) {
Packit 90a5c9
                    tattr *attrs = (tattr*) linkattrs->elts;
Packit 90a5c9
                    for (i=0; i < linkattrs->nelts; ++i) {
Packit 90a5c9
                        if (!strcmp(*a, attrs[i].val)) {
Packit 90a5c9
                            is_uri = ATTR_URI;
Packit 90a5c9
                            break;
Packit 90a5c9
                        }
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
                if ((is_uri == ATTR_IGNORE) && ctx->cfg->extfix
Packit 90a5c9
                    && (ctx->cfg->events != NULL)) {
Packit 90a5c9
                    for (i=0; i < ctx->cfg->events->nelts; ++i) {
Packit 90a5c9
                        tattr *attrs = (tattr*) ctx->cfg->events->elts;
Packit 90a5c9
                        if (!strcmp(*a, attrs[i].val)) {
Packit 90a5c9
                            is_uri = ATTR_EVENT;
Packit 90a5c9
                            break;
Packit 90a5c9
                        }
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
                switch (is_uri) {
Packit 90a5c9
                case ATTR_URI:
Packit 90a5c9
                    num_match = 0;
Packit 90a5c9
                    for (m = themap; m; m = m->next) {
Packit 90a5c9
                        if (!(m->flags & M_HTML))
Packit 90a5c9
                            continue;
Packit 90a5c9
                        if (m->flags & M_REGEX) {
Packit 90a5c9
                            nmatch = 10;
Packit 90a5c9
                            if (!ap_regexec(m->from.r, ctx->buf, nmatch,
Packit 90a5c9
                                            pmatch, 0)) {
Packit 90a5c9
                                ++num_match;
Packit 90a5c9
                                offs = match = pmatch[0].rm_so;
Packit 90a5c9
                                s_from = pmatch[0].rm_eo - match;
Packit 90a5c9
                                subs = ap_pregsub(ctx->f->r->pool, m->to,
Packit 90a5c9
                                                  ctx->buf, nmatch, pmatch);
Packit 90a5c9
                                VERBOSE({
Packit 90a5c9
                                    const char *f;
Packit 90a5c9
                                    f = apr_pstrndup(ctx->f->r->pool,
Packit 90a5c9
                                                     ctx->buf + offs, s_from);
Packit 90a5c9
                                    ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0,
Packit 90a5c9
                                                  ctx->f->r,
Packit 90a5c9
                                         "H/RX: match at %s, substituting %s",
Packit 90a5c9
                                                  f, subs);
Packit 90a5c9
                                })
Packit 90a5c9
                                s_to = strlen(subs);
Packit 90a5c9
                                len = strlen(ctx->buf);
Packit 90a5c9
                                if (s_to > s_from) {
Packit 90a5c9
                                    preserve(ctx, s_to - s_from);
Packit 90a5c9
                                    memmove(ctx->buf+offs+s_to,
Packit 90a5c9
                                            ctx->buf+offs+s_from,
Packit 90a5c9
                                            len + 1 - s_from - offs);
Packit 90a5c9
                                    memcpy(ctx->buf+offs, subs, s_to);
Packit 90a5c9
                                }
Packit 90a5c9
                                else {
Packit 90a5c9
                                    memcpy(ctx->buf + offs, subs, s_to);
Packit 90a5c9
                                    memmove(ctx->buf+offs+s_to,
Packit 90a5c9
                                            ctx->buf+offs+s_from,
Packit 90a5c9
                                            len + 1 - s_from - offs);
Packit 90a5c9
                                }
Packit 90a5c9
                            }
Packit 90a5c9
                        } else {
Packit 90a5c9
                            s_from = strlen(m->from.c);
Packit 90a5c9
                            if (!strncasecmp(ctx->buf, m->from.c, s_from)) {
Packit 90a5c9
                                ++num_match;
Packit 90a5c9
                                s_to = strlen(m->to);
Packit 90a5c9
                                len = strlen(ctx->buf);
Packit 90a5c9
                                VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3,
Packit 90a5c9
                                                      0, ctx->f->r,
Packit 90a5c9
                                              "H: matched %s, substituting %s",
Packit 90a5c9
                                                      m->from.c, m->to));
Packit 90a5c9
                                if (s_to > s_from) {
Packit 90a5c9
                                    preserve(ctx, s_to - s_from);
Packit 90a5c9
                                    memmove(ctx->buf+s_to, ctx->buf+s_from,
Packit 90a5c9
                                            len + 1 - s_from);
Packit 90a5c9
                                    memcpy(ctx->buf, m->to, s_to);
Packit 90a5c9
                                }
Packit 90a5c9
                                else {     /* it fits in the existing space */
Packit 90a5c9
                                    memcpy(ctx->buf, m->to, s_to);
Packit 90a5c9
                                    memmove(ctx->buf+s_to, ctx->buf+s_from,
Packit 90a5c9
                                            len + 1 - s_from);
Packit 90a5c9
                                }
Packit 90a5c9
                                break;
Packit 90a5c9
                            }
Packit 90a5c9
                        }
Packit 90a5c9
                        /* URIs only want one match unless overridden in the config */
Packit 90a5c9
                        if ((num_match > 0) && !(m->flags & M_NOTLAST))
Packit 90a5c9
                            break;
Packit 90a5c9
                    }
Packit 90a5c9
                    break;
Packit 90a5c9
                case ATTR_EVENT:
Packit 90a5c9
                    for (m = themap; m; m = m->next) {
Packit 90a5c9
                        num_match = 0;        /* reset here since we're working per-rule */
Packit 90a5c9
                        if (!(m->flags & M_EVENTS))
Packit 90a5c9
                            continue;
Packit 90a5c9
                        if (m->flags & M_REGEX) {
Packit 90a5c9
                            nmatch = 10;
Packit 90a5c9
                            offs = 0;
Packit 90a5c9
                            while (!ap_regexec(m->from.r, ctx->buf+offs,
Packit 90a5c9
                                               nmatch, pmatch, 0)) {
Packit 90a5c9
                                match = pmatch[0].rm_so;
Packit 90a5c9
                                s_from = pmatch[0].rm_eo - match;
Packit 90a5c9
                                subs = ap_pregsub(ctx->f->r->pool, m->to, ctx->buf+offs,
Packit 90a5c9
                                                    nmatch, pmatch);
Packit 90a5c9
                                VERBOSE({
Packit 90a5c9
                                    const char *f;
Packit 90a5c9
                                    f = apr_pstrndup(ctx->f->r->pool,
Packit 90a5c9
                                                     ctx->buf + offs, s_from);
Packit 90a5c9
                                    ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0,
Packit 90a5c9
                                                  ctx->f->r,
Packit 90a5c9
                                           "E/RX: match at %s, substituting %s",
Packit 90a5c9
                                                  f, subs);
Packit 90a5c9
                                })
Packit 90a5c9
                                s_to = strlen(subs);
Packit 90a5c9
                                offs += match;
Packit 90a5c9
                                len = strlen(ctx->buf);
Packit 90a5c9
                                if (s_to > s_from) {
Packit 90a5c9
                                    preserve(ctx, s_to - s_from);
Packit 90a5c9
                                    memmove(ctx->buf+offs+s_to,
Packit 90a5c9
                                            ctx->buf+offs+s_from,
Packit 90a5c9
                                            len + 1 - s_from - offs);
Packit 90a5c9
                                    memcpy(ctx->buf+offs, subs, s_to);
Packit 90a5c9
                                }
Packit 90a5c9
                                else {
Packit 90a5c9
                                    memcpy(ctx->buf + offs, subs, s_to);
Packit 90a5c9
                                    memmove(ctx->buf+offs+s_to,
Packit 90a5c9
                                            ctx->buf+offs+s_from,
Packit 90a5c9
                                            len + 1 - s_from - offs);
Packit 90a5c9
                                }
Packit 90a5c9
                                offs += s_to;
Packit 90a5c9
                                ++num_match;
Packit 90a5c9
                            }
Packit 90a5c9
                        }
Packit 90a5c9
                        else {
Packit 90a5c9
                            found = strstr(ctx->buf, m->from.c);
Packit 90a5c9
                            if ((m->flags & M_ATSTART) && (found != ctx->buf))
Packit 90a5c9
                                continue;
Packit 90a5c9
                            while (found) {
Packit 90a5c9
                                s_from = strlen(m->from.c);
Packit 90a5c9
                                s_to = strlen(m->to);
Packit 90a5c9
                                match = found - ctx->buf;
Packit 90a5c9
                                if ((s_from < strlen(found))
Packit 90a5c9
                                    && (m->flags & M_ATEND)) {
Packit 90a5c9
                                    found = strstr(ctx->buf+match+s_from,
Packit 90a5c9
                                                   m->from.c);
Packit 90a5c9
                                    continue;
Packit 90a5c9
                                }
Packit 90a5c9
                                else {
Packit 90a5c9
                                    found = strstr(ctx->buf+match+s_to,
Packit 90a5c9
                                                   m->from.c);
Packit 90a5c9
                                }
Packit 90a5c9
                                VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3,
Packit 90a5c9
                                                      0, ctx->f->r,
Packit 90a5c9
                                              "E: matched %s, substituting %s",
Packit 90a5c9
                                                      m->from.c, m->to));
Packit 90a5c9
                                len = strlen(ctx->buf);
Packit 90a5c9
                                if (s_to > s_from) {
Packit 90a5c9
                                    preserve(ctx, s_to - s_from);
Packit 90a5c9
                                    memmove(ctx->buf+match+s_to,
Packit 90a5c9
                                            ctx->buf+match+s_from,
Packit 90a5c9
                                            len + 1 - s_from - match);
Packit 90a5c9
                                    memcpy(ctx->buf+match, m->to, s_to);
Packit 90a5c9
                                }
Packit 90a5c9
                                else {
Packit 90a5c9
                                    memcpy(ctx->buf+match, m->to, s_to);
Packit 90a5c9
                                    memmove(ctx->buf+match+s_to,
Packit 90a5c9
                                            ctx->buf+match+s_from,
Packit 90a5c9
                                            len + 1 - s_from - match);
Packit 90a5c9
                                }
Packit 90a5c9
                                ++num_match;
Packit 90a5c9
                            }
Packit 90a5c9
                        }
Packit 90a5c9
                        if (num_match && (m->flags & M_LAST))
Packit 90a5c9
                            break;
Packit 90a5c9
                    }
Packit 90a5c9
                    break;
Packit 90a5c9
                case ATTR_IGNORE:
Packit 90a5c9
                    break;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            if (!a[1])
Packit 90a5c9
                ap_fputstrs(ctx->f->next, ctx->bb, " ", a[0], NULL);
Packit 90a5c9
            else {
Packit 90a5c9
Packit 90a5c9
                if (ctx->cfg->flags != 0)
Packit 90a5c9
                    normalise(ctx->cfg->flags, ctx->buf);
Packit 90a5c9
Packit 90a5c9
                /* write the attribute, using pcharacters to html-escape
Packit 90a5c9
                   anything that needs it in the value.
Packit 90a5c9
                */
Packit 90a5c9
                ap_fputstrs(ctx->f->next, ctx->bb, " ", a[0], "=\"", NULL);
Packit 90a5c9
                pcharacters(ctx, (const xmlChar*)ctx->buf, strlen(ctx->buf));
Packit 90a5c9
                ap_fputc(ctx->f->next, ctx->bb, '"');
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    ctx->offset = 0;
Packit 90a5c9
    if (desc && desc->empty)
Packit 90a5c9
        ap_fputs(ctx->f->next, ctx->bb, ctx->cfg->etag);
Packit 90a5c9
    else
Packit 90a5c9
        ap_fputc(ctx->f->next, ctx->bb, '>');
Packit 90a5c9
Packit 90a5c9
    if ((enforce > 0) && (required_attrs > 0)) {
Packit 90a5c9
        /* if there are more required attributes than we found then complain */
Packit 90a5c9
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01420)
Packit 90a5c9
                      "HTML element %s is missing %d required attributes",
Packit 90a5c9
                      name, required_attrs);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static meta *metafix(request_rec *r, const char *buf, apr_size_t len)
Packit 90a5c9
{
Packit 90a5c9
    meta *ret = NULL;
Packit 90a5c9
    size_t offs = 0;
Packit 90a5c9
    const char *p;
Packit 90a5c9
    const char *q;
Packit 90a5c9
    char *header;
Packit 90a5c9
    char *content;
Packit 90a5c9
    ap_regmatch_t pmatch[2];
Packit 90a5c9
    char delim;
Packit 90a5c9
Packit 90a5c9
    while (offs < len &&
Packit 90a5c9
           !ap_regexec_len(seek_meta, buf + offs, len - offs, 2, pmatch, 0)) {
Packit 90a5c9
        header = NULL;
Packit 90a5c9
        content = NULL;
Packit 90a5c9
        p = buf+offs+pmatch[1].rm_eo;
Packit 90a5c9
        while (!apr_isalpha(*++p));
Packit 90a5c9
        for (q = p; apr_isalnum(*q) || (*q == '-'); ++q);
Packit 90a5c9
        header = apr_pstrmemdup(r->pool, p, q-p);
Packit 90a5c9
        if (strncasecmp(header, "Content-", 8)) {
Packit 90a5c9
            /* find content=... string */
Packit 90a5c9
            p = apr_strmatch(seek_content, buf+offs+pmatch[0].rm_so,
Packit 90a5c9
                              pmatch[0].rm_eo - pmatch[0].rm_so);
Packit 90a5c9
            /* if it doesn't contain "content", ignore, don't crash! */
Packit 90a5c9
            if (p != NULL) {
Packit 90a5c9
                while (*p) {
Packit 90a5c9
                    p += 7;
Packit 90a5c9
                    while (apr_isspace(*p))
Packit 90a5c9
                        ++p;
Packit 90a5c9
                    /* XXX Should we search for another content= pattern? */
Packit 90a5c9
                    if (*p != '=')
Packit 90a5c9
                        break;
Packit 90a5c9
                    while (*p && apr_isspace(*++p));
Packit 90a5c9
                    if ((*p == '\'') || (*p == '"')) {
Packit 90a5c9
                        delim = *p++;
Packit 90a5c9
                        for (q = p; *q && *q != delim; ++q);
Packit 90a5c9
                        /* No terminating delimiter found? Skip the boggus directive */
Packit 90a5c9
                        if (*q != delim)
Packit 90a5c9
                           break;
Packit 90a5c9
                    } else {
Packit 90a5c9
                        for (q = p; *q && !apr_isspace(*q) && (*q != '>'); ++q);
Packit 90a5c9
                    }
Packit 90a5c9
                    content = apr_pstrmemdup(r->pool, p, q-p);
Packit 90a5c9
                    break;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else if (!strncasecmp(header, "Content-Type", 12)) {
Packit 90a5c9
            ret = apr_palloc(r->pool, sizeof(meta));
Packit 90a5c9
            ret->start = offs+pmatch[0].rm_so;
Packit 90a5c9
            ret->end = offs+pmatch[0].rm_eo;
Packit 90a5c9
        }
Packit 90a5c9
        if (header && content) {
Packit 90a5c9
#ifndef GO_FASTER
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
Packit 90a5c9
                          "Adding header [%s: %s] from HTML META",
Packit 90a5c9
                          header, content); 
Packit 90a5c9
#endif
Packit 90a5c9
            apr_table_setn(r->headers_out, header, content);
Packit 90a5c9
        }
Packit 90a5c9
        offs += pmatch[0].rm_eo;
Packit 90a5c9
    }
Packit 90a5c9
    return ret;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *interpolate_vars(request_rec *r, const char *str)
Packit 90a5c9
{
Packit 90a5c9
    const char *start;
Packit 90a5c9
    const char *end;
Packit 90a5c9
    const char *delim;
Packit 90a5c9
    const char *before;
Packit 90a5c9
    const char *after;
Packit 90a5c9
    const char *replacement;
Packit 90a5c9
    const char *var;
Packit 90a5c9
    for (;;) {
Packit 90a5c9
        if ((start = ap_strstr_c(str, "${")) == NULL)
Packit 90a5c9
            break;
Packit 90a5c9
Packit 90a5c9
        if ((end = ap_strchr_c(start+2, '}')) == NULL)
Packit 90a5c9
            break;
Packit 90a5c9
Packit 90a5c9
        delim = ap_strchr_c(start+2, '|');
Packit 90a5c9
Packit 90a5c9
        /* Restrict delim to ${...} */
Packit 90a5c9
        if (delim && delim >= end) {
Packit 90a5c9
            delim = NULL;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        before = apr_pstrmemdup(r->pool, str, start-str);
Packit 90a5c9
        after = end+1;
Packit 90a5c9
        if (delim) {
Packit 90a5c9
            var = apr_pstrmemdup(r->pool, start+2, delim-start-2);
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            var = apr_pstrmemdup(r->pool, start+2, end-start-2);
Packit 90a5c9
        }
Packit 90a5c9
        replacement = apr_table_get(r->subprocess_env, var);
Packit 90a5c9
        if (!replacement) {
Packit 90a5c9
            if (delim)
Packit 90a5c9
                replacement = apr_pstrmemdup(r->pool, delim+1, end-delim-1);
Packit 90a5c9
            else
Packit 90a5c9
                replacement = "";
Packit 90a5c9
        }
Packit 90a5c9
        str = apr_pstrcat(r->pool, before, replacement, after, NULL);
Packit 90a5c9
        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
Packit 90a5c9
                      "Interpolating %s  =>  %s", var, replacement);
Packit 90a5c9
    }
Packit 90a5c9
    return str;
Packit 90a5c9
}
Packit 90a5c9
static void fixup_rules(saxctxt *ctx)
Packit 90a5c9
{
Packit 90a5c9
    urlmap *newp;
Packit 90a5c9
    urlmap *p;
Packit 90a5c9
    urlmap *prev = NULL;
Packit 90a5c9
    request_rec *r = ctx->f->r;
Packit 90a5c9
Packit 90a5c9
    for (p = ctx->cfg->map; p; p = p->next) {
Packit 90a5c9
        if (p->cond != NULL) {
Packit 90a5c9
            const char *err;
Packit 90a5c9
            int ok = ap_expr_exec(r, p->cond, &err;;
Packit 90a5c9
            if (err) {
Packit 90a5c9
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01421)
Packit 90a5c9
                              "Error evaluating expr: %s", err);
Packit 90a5c9
            }
Packit 90a5c9
            if (ok == 0) {
Packit 90a5c9
                continue;  /* condition is unsatisfied */
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        newp = apr_pmemdup(r->pool, p, sizeof(urlmap));
Packit 90a5c9
Packit 90a5c9
        if (newp->flags & M_INTERPOLATE_FROM) {
Packit 90a5c9
            newp->from.c = interpolate_vars(r, newp->from.c);
Packit 90a5c9
            if (!newp->from.c || !*newp->from.c)
Packit 90a5c9
                continue;        /* don't use empty from-pattern */
Packit 90a5c9
            if (newp->flags & M_REGEX) {
Packit 90a5c9
                newp->from.r = ap_pregcomp(r->pool, newp->from.c,
Packit 90a5c9
                                           newp->regflags);
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        if (newp->flags & M_INTERPOLATE_TO) {
Packit 90a5c9
            newp->to = interpolate_vars(r, newp->to);
Packit 90a5c9
        }
Packit 90a5c9
        /* evaluate p->cond; continue if unsatisfied */
Packit 90a5c9
        /* create new urlmap with memcpy and append to map */
Packit 90a5c9
        /* interpolate from if flagged to do so */
Packit 90a5c9
        /* interpolate to if flagged to do so */
Packit 90a5c9
Packit 90a5c9
        if (prev != NULL)
Packit 90a5c9
            prev->next = newp;
Packit 90a5c9
        else
Packit 90a5c9
            ctx->map = newp;
Packit 90a5c9
        prev = newp;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (prev)
Packit 90a5c9
        prev->next = NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static saxctxt *check_filter_init (ap_filter_t *f)
Packit 90a5c9
{
Packit 90a5c9
    saxctxt *fctx;
Packit 90a5c9
    if (!f->ctx) {
Packit 90a5c9
        proxy_html_conf *cfg;
Packit 90a5c9
        const char *force;
Packit 90a5c9
        const char *errmsg = NULL;
Packit 90a5c9
        cfg = ap_get_module_config(f->r->per_dir_config, &proxy_html_module);
Packit 90a5c9
        force = apr_table_get(f->r->subprocess_env, "PROXY_HTML_FORCE");
Packit 90a5c9
Packit 90a5c9
        if (!force) {
Packit 90a5c9
            if (!f->r->proxyreq) {
Packit 90a5c9
                errmsg = "Non-proxy request; not inserting proxy-html filter";
Packit 90a5c9
            }
Packit 90a5c9
            else if (!f->r->content_type) {
Packit 90a5c9
                errmsg = "No content-type; bailing out of proxy-html filter";
Packit 90a5c9
            }
Packit 90a5c9
            else if (strncasecmp(f->r->content_type, "text/html", 9) &&
Packit 90a5c9
                     strncasecmp(f->r->content_type,
Packit 90a5c9
                                 "application/xhtml+xml", 21)) {
Packit 90a5c9
                errmsg = "Non-HTML content; not inserting proxy-html filter";
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        if (!cfg->links) {
Packit 90a5c9
            errmsg = "No links configured: nothing for proxy-html filter to do";
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (errmsg) {
Packit 90a5c9
#ifndef GO_FASTER
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, f->r, "%s", errmsg);
Packit 90a5c9
#endif
Packit 90a5c9
            ap_remove_output_filter(f);
Packit 90a5c9
            return NULL;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        fctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(saxctxt));
Packit 90a5c9
        fctx->f = f;
Packit 90a5c9
        fctx->bb = apr_brigade_create(f->r->pool,
Packit 90a5c9
                                      f->r->connection->bucket_alloc);
Packit 90a5c9
        fctx->cfg = cfg;
Packit 90a5c9
        apr_table_unset(f->r->headers_out, "Content-Length");
Packit 90a5c9
Packit 90a5c9
        if (cfg->interp)
Packit 90a5c9
            fixup_rules(fctx);
Packit 90a5c9
        else
Packit 90a5c9
            fctx->map = cfg->map;
Packit 90a5c9
        /* defer dealing with charset_out until after sniffing charset_in
Packit 90a5c9
         * so we can support setting one to t'other.
Packit 90a5c9
         */
Packit 90a5c9
    }
Packit 90a5c9
    return f->ctx;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void prepend_rbuf(saxctxt *ctxt, apr_bucket_brigade *bb)
Packit 90a5c9
{
Packit 90a5c9
    if (ctxt->rlen) {
Packit 90a5c9
        apr_bucket *b = apr_bucket_transient_create(ctxt->rbuf,
Packit 90a5c9
                                                    ctxt->rlen,
Packit 90a5c9
                                                    bb->bucket_alloc);
Packit 90a5c9
        APR_BRIGADE_INSERT_HEAD(bb, b);
Packit 90a5c9
        ctxt->rlen = 0;
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t proxy_html_filter(ap_filter_t *f, apr_bucket_brigade *bb)
Packit 90a5c9
{
Packit 90a5c9
    apr_bucket* b;
Packit 90a5c9
    meta *m = NULL;
Packit 90a5c9
    xmlCharEncoding enc;
Packit 90a5c9
    const char *buf = 0;
Packit 90a5c9
    apr_size_t bytes = 0;
Packit 90a5c9
#ifndef USE_OLD_LIBXML2
Packit 90a5c9
    int xmlopts = XML_PARSE_RECOVER | XML_PARSE_NONET |
Packit 90a5c9
                  XML_PARSE_NOBLANKS | XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
    saxctxt *ctxt = check_filter_init(f);
Packit 90a5c9
    if (!ctxt)
Packit 90a5c9
        return ap_pass_brigade(f->next, bb);
Packit 90a5c9
    for (b = APR_BRIGADE_FIRST(bb);
Packit 90a5c9
         b != APR_BRIGADE_SENTINEL(bb);
Packit 90a5c9
         b = APR_BUCKET_NEXT(b)) {
Packit 90a5c9
        if (APR_BUCKET_IS_METADATA(b)) {
Packit 90a5c9
            if (APR_BUCKET_IS_EOS(b)) {
Packit 90a5c9
                if (ctxt->parser != NULL) {
Packit 90a5c9
                    consume_buffer(ctxt, "", 0, 1);
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    prepend_rbuf(ctxt, ctxt->bb);
Packit 90a5c9
                }
Packit 90a5c9
                APR_BRIGADE_INSERT_TAIL(ctxt->bb,
Packit 90a5c9
                    apr_bucket_eos_create(ctxt->bb->bucket_alloc));
Packit 90a5c9
                ap_pass_brigade(ctxt->f->next, ctxt->bb);
Packit 90a5c9
                apr_brigade_cleanup(ctxt->bb);
Packit 90a5c9
            }
Packit 90a5c9
            else if (APR_BUCKET_IS_FLUSH(b)) {
Packit 90a5c9
                /* pass on flush, except at start where it would cause
Packit 90a5c9
                 * headers to be sent before doc sniffing
Packit 90a5c9
                 */
Packit 90a5c9
                if (ctxt->parser != NULL) {
Packit 90a5c9
                    ap_fflush(ctxt->f->next, ctxt->bb);
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
Packit 90a5c9
                 == APR_SUCCESS) {
Packit 90a5c9
            if (ctxt->parser == NULL) {
Packit 90a5c9
                const char *cenc;
Packit 90a5c9
Packit 90a5c9
                /* For documents smaller than four bytes, there is no reason to do
Packit 90a5c9
                 * HTML rewriting. The URL schema (i.e. 'http') needs four bytes alone.
Packit 90a5c9
                 * And the HTML parser needs at least four bytes to initialise correctly.
Packit 90a5c9
                 */
Packit 90a5c9
                ctxt->rmin += bytes;
Packit 90a5c9
                if (ctxt->rmin < sizeof(ctxt->rbuf)) {
Packit 90a5c9
                    memcpy(ctxt->rbuf + ctxt->rlen, buf, bytes);
Packit 90a5c9
                    ctxt->rlen += bytes;
Packit 90a5c9
                    continue;
Packit 90a5c9
                }
Packit 90a5c9
                if (ctxt->rlen && ctxt->rlen < sizeof(ctxt->rbuf)) {
Packit 90a5c9
                    apr_size_t rem = sizeof(ctxt->rbuf) - ctxt->rlen;
Packit 90a5c9
                    memcpy(ctxt->rbuf + ctxt->rlen, buf, rem);
Packit 90a5c9
                    ctxt->rlen += rem;
Packit 90a5c9
                    buf += rem;
Packit 90a5c9
                    bytes -= rem;
Packit 90a5c9
                }
Packit 90a5c9
Packit 90a5c9
                if (!xml2enc_charset ||
Packit 90a5c9
                    (xml2enc_charset(f->r, &enc, &cenc) != APR_SUCCESS)) {
Packit 90a5c9
                    if (!xml2enc_charset)
Packit 90a5c9
                        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, APLOGNO(01422)
Packit 90a5c9
                                      "No i18n support found.  Install mod_xml2enc if required");
Packit 90a5c9
                    enc = XML_CHAR_ENCODING_NONE;
Packit 90a5c9
                    ap_set_content_type(f->r, "text/html;charset=utf-8");
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    /* if we wanted a non-default charset_out, insert the
Packit 90a5c9
                     * xml2enc filter now that we've sniffed it
Packit 90a5c9
                     */
Packit 90a5c9
                    if (ctxt->cfg->charset_out && xml2enc_filter) {
Packit 90a5c9
                        if (*ctxt->cfg->charset_out != '*')
Packit 90a5c9
                            cenc = ctxt->cfg->charset_out;
Packit 90a5c9
                        xml2enc_filter(f->r, cenc, ENCIO_OUTPUT);
Packit 90a5c9
                        ap_set_content_type(f->r,
Packit 90a5c9
                                            apr_pstrcat(f->r->pool,
Packit 90a5c9
                                                        "text/html;charset=",
Packit 90a5c9
                                                        cenc, NULL));
Packit 90a5c9
                    }
Packit 90a5c9
                    else /* Normal case, everything worked, utf-8 output */
Packit 90a5c9
                        ap_set_content_type(f->r, "text/html;charset=utf-8");
Packit 90a5c9
                }
Packit 90a5c9
Packit 90a5c9
                ap_fputs(f->next, ctxt->bb, ctxt->cfg->doctype);
Packit 90a5c9
Packit 90a5c9
                if (ctxt->rlen) {
Packit 90a5c9
                    ctxt->parser = htmlCreatePushParserCtxt(&sax, ctxt,
Packit 90a5c9
                                                            ctxt->rbuf,
Packit 90a5c9
                                                            ctxt->rlen,
Packit 90a5c9
                                                            NULL, enc);
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    ctxt->parser = htmlCreatePushParserCtxt(&sax, ctxt, buf, 4,
Packit 90a5c9
                                                            NULL, enc);
Packit 90a5c9
                    buf += 4;
Packit 90a5c9
                    bytes -= 4;
Packit 90a5c9
                }
Packit 90a5c9
                if (ctxt->parser == NULL) {
Packit 90a5c9
                    prepend_rbuf(ctxt, bb);
Packit 90a5c9
                    ap_remove_output_filter(f);
Packit 90a5c9
                    return ap_pass_brigade(f->next, bb);
Packit 90a5c9
                }
Packit 90a5c9
                ctxt->rlen = 0;
Packit 90a5c9
                apr_pool_cleanup_register(f->r->pool, ctxt->parser,
Packit 90a5c9
                                          (int(*)(void*))htmlFreeParserCtxt,
Packit 90a5c9
                                          apr_pool_cleanup_null);
Packit 90a5c9
#ifndef USE_OLD_LIBXML2
Packit 90a5c9
                if (xmlopts = xmlCtxtUseOptions(ctxt->parser, xmlopts), xmlopts)
Packit 90a5c9
                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, APLOGNO(01423)
Packit 90a5c9
                                  "Unsupported parser opts %x", xmlopts);
Packit 90a5c9
#endif
Packit 90a5c9
                if (ctxt->cfg->metafix)
Packit 90a5c9
                    m = metafix(f->r, buf, bytes);
Packit 90a5c9
                if (m) {
Packit 90a5c9
                    consume_buffer(ctxt, buf, m->start, 0);
Packit 90a5c9
                    consume_buffer(ctxt, buf+m->end, bytes-m->end, 0);
Packit 90a5c9
                }
Packit 90a5c9
                else {
Packit 90a5c9
                    consume_buffer(ctxt, buf, bytes, 0);
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                consume_buffer(ctxt, buf, bytes, 0);
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01424)
Packit 90a5c9
                          "Error in bucket read");
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    /*ap_fflush(ctxt->f->next, ctxt->bb);        // uncomment for debug */
Packit 90a5c9
    apr_brigade_cleanup(bb);
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void *proxy_html_config(apr_pool_t *pool, char *x)
Packit 90a5c9
{
Packit 90a5c9
    proxy_html_conf *ret = apr_pcalloc(pool, sizeof(proxy_html_conf));
Packit 90a5c9
    ret->doctype = DEFAULT_DOCTYPE;
Packit 90a5c9
    ret->etag = DEFAULT_ETAG;
Packit 90a5c9
    ret->bufsz = 8192;
Packit 90a5c9
    /* ret->interp = 1; */
Packit 90a5c9
    /* don't initialise links and events until they get set/used */
Packit 90a5c9
    return ret;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void *proxy_html_merge(apr_pool_t *pool, void *BASE, void *ADD)
Packit 90a5c9
{
Packit 90a5c9
    proxy_html_conf *base = (proxy_html_conf *) BASE;
Packit 90a5c9
    proxy_html_conf *add = (proxy_html_conf *) ADD;
Packit 90a5c9
    proxy_html_conf *conf = apr_palloc(pool, sizeof(proxy_html_conf));
Packit 90a5c9
Packit 90a5c9
    /* don't merge declarations - just use the most specific */
Packit 90a5c9
    conf->links = (add->links == NULL) ? base->links : add->links;
Packit 90a5c9
    conf->events = (add->events == NULL) ? base->events : add->events;
Packit 90a5c9
Packit 90a5c9
    conf->charset_out = (add->charset_out == NULL)
Packit 90a5c9
                        ? base->charset_out : add->charset_out;
Packit 90a5c9
Packit 90a5c9
    if (add->map && base->map) {
Packit 90a5c9
        urlmap *a;
Packit 90a5c9
        conf->map = NULL;
Packit 90a5c9
        for (a = base->map; a; a = a->next) {
Packit 90a5c9
            urlmap *save = conf->map;
Packit 90a5c9
            conf->map = apr_pmemdup(pool, a, sizeof(urlmap));
Packit 90a5c9
            conf->map->next = save;
Packit 90a5c9
        }
Packit 90a5c9
        for (a = add->map; a; a = a->next) {
Packit 90a5c9
            urlmap *save = conf->map;
Packit 90a5c9
            conf->map = apr_pmemdup(pool, a, sizeof(urlmap));
Packit 90a5c9
            conf->map->next = save;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    else
Packit 90a5c9
        conf->map = add->map ? add->map : base->map;
Packit 90a5c9
Packit 90a5c9
    conf->doctype = (add->doctype == DEFAULT_DOCTYPE)
Packit 90a5c9
                    ? base->doctype : add->doctype;
Packit 90a5c9
    conf->etag = (add->etag == DEFAULT_ETAG) ? base->etag : add->etag;
Packit 90a5c9
    conf->bufsz = add->bufsz;
Packit 90a5c9
    if (add->flags & NORM_RESET) {
Packit 90a5c9
        conf->flags = add->flags ^ NORM_RESET;
Packit 90a5c9
        conf->metafix = add->metafix;
Packit 90a5c9
        conf->extfix = add->extfix;
Packit 90a5c9
        conf->interp = add->interp;
Packit 90a5c9
        conf->strip_comments = add->strip_comments;
Packit 90a5c9
        conf->enabled = add->enabled;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        conf->flags = base->flags | add->flags;
Packit 90a5c9
        conf->metafix = base->metafix | add->metafix;
Packit 90a5c9
        conf->extfix = base->extfix | add->extfix;
Packit 90a5c9
        conf->interp = base->interp | add->interp;
Packit 90a5c9
        conf->strip_comments = base->strip_comments | add->strip_comments;
Packit 90a5c9
        conf->enabled = add->enabled | base->enabled;
Packit 90a5c9
    }
Packit 90a5c9
    return conf;
Packit 90a5c9
}
Packit 90a5c9
#define REGFLAG(n,s,c) ((s&&(ap_strchr_c((s),(c))!=NULL)) ? (n) : 0)
Packit 90a5c9
#define XREGFLAG(n,s,c) ((!s||(ap_strchr_c((s),(c))==NULL)) ? (n) : 0)
Packit 90a5c9
static const char *comp_urlmap(cmd_parms *cmd, urlmap *newmap,
Packit 90a5c9
                               const char *from, const char *to,
Packit 90a5c9
                               const char *flags, const char *cond)
Packit 90a5c9
{
Packit 90a5c9
    const char *err = NULL;
Packit 90a5c9
    newmap->flags
Packit 90a5c9
        = XREGFLAG(M_HTML,flags,'h')
Packit 90a5c9
        | XREGFLAG(M_EVENTS,flags,'e')
Packit 90a5c9
        | XREGFLAG(M_CDATA,flags,'c')
Packit 90a5c9
        | REGFLAG(M_ATSTART,flags,'^')
Packit 90a5c9
        | REGFLAG(M_ATEND,flags,'$')
Packit 90a5c9
        | REGFLAG(M_REGEX,flags,'R')
Packit 90a5c9
        | REGFLAG(M_LAST,flags,'L')
Packit 90a5c9
        | REGFLAG(M_NOTLAST,flags,'l')
Packit 90a5c9
        | REGFLAG(M_INTERPOLATE_TO,flags,'V')
Packit 90a5c9
        | REGFLAG(M_INTERPOLATE_FROM,flags,'v');
Packit 90a5c9
Packit 90a5c9
    if ((newmap->flags & M_INTERPOLATE_FROM) || !(newmap->flags & M_REGEX)) {
Packit 90a5c9
        newmap->from.c = from;
Packit 90a5c9
        newmap->to = to;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        newmap->regflags
Packit 90a5c9
            = REGFLAG(AP_REG_EXTENDED,flags,'x')
Packit 90a5c9
            | REGFLAG(AP_REG_ICASE,flags,'i')
Packit 90a5c9
            | REGFLAG(AP_REG_NOSUB,flags,'n')
Packit 90a5c9
            | REGFLAG(AP_REG_NEWLINE,flags,'s');
Packit 90a5c9
        newmap->from.r = ap_pregcomp(cmd->pool, from, newmap->regflags);
Packit 90a5c9
        newmap->to = to;
Packit 90a5c9
    }
Packit 90a5c9
    if (cond != NULL) {
Packit 90a5c9
        /* back-compatibility: support old-style ENV expressions
Packit 90a5c9
         * by converting to ap_expr syntax.
Packit 90a5c9
         *
Packit 90a5c9
         * 1. var --> env(var)
Packit 90a5c9
         * 2. var=val --> env(var)=val
Packit 90a5c9
         * 3. !var --> !env(var)
Packit 90a5c9
         * 4. !var=val --> env(var)!=val
Packit 90a5c9
         */
Packit 90a5c9
        char *newcond = NULL;
Packit 90a5c9
        if (ap_rxplus_exec(cmd->temp_pool, old_expr, cond, &newcond)) {
Packit 90a5c9
           /* we got a substitution.  Check for the case (3) above
Packit 90a5c9
            * that the regexp gets wrong: a negation without a comparison.
Packit 90a5c9
            */
Packit 90a5c9
            if ((cond[0] == '!') && !ap_strchr_c(cond, '=')) {
Packit 90a5c9
                memmove(newcond+1, newcond, strlen(newcond)-1);
Packit 90a5c9
                newcond[0] = '!';
Packit 90a5c9
            }
Packit 90a5c9
            cond = newcond;
Packit 90a5c9
        }
Packit 90a5c9
        newmap->cond = ap_expr_parse_cmd(cmd, cond, 0, &err, NULL);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        newmap->cond = NULL;
Packit 90a5c9
    }
Packit 90a5c9
    return err;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *set_urlmap(cmd_parms *cmd, void *CFG, const char *args)
Packit 90a5c9
{
Packit 90a5c9
    proxy_html_conf *cfg = (proxy_html_conf *)CFG;
Packit 90a5c9
    urlmap *map;
Packit 90a5c9
    apr_pool_t *pool = cmd->pool;
Packit 90a5c9
    urlmap *newmap;
Packit 90a5c9
    const char *usage =
Packit 90a5c9
              "Usage: ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]";
Packit 90a5c9
    const char *from;
Packit 90a5c9
    const char *to;
Packit 90a5c9
    const char *flags;
Packit 90a5c9
    const char *cond = NULL;
Packit 90a5c9
  
Packit 90a5c9
    if (from = ap_getword_conf(cmd->pool, &args), !from)
Packit 90a5c9
        return usage;
Packit 90a5c9
    if (to = ap_getword_conf(cmd->pool, &args), !to)
Packit 90a5c9
        return usage;
Packit 90a5c9
    flags = ap_getword_conf(cmd->pool, &args);
Packit 90a5c9
    if (flags && *flags)
Packit 90a5c9
        cond = ap_getword_conf(cmd->pool, &args);
Packit 90a5c9
    if (cond && !*cond)
Packit 90a5c9
        cond = NULL;
Packit 90a5c9
Packit 90a5c9
    /* the args look OK, so let's use them */
Packit 90a5c9
    newmap = apr_palloc(pool, sizeof(urlmap));
Packit 90a5c9
    newmap->next = NULL;
Packit 90a5c9
    if (cfg->map) {
Packit 90a5c9
        for (map = cfg->map; map->next; map = map->next);
Packit 90a5c9
        map->next = newmap;
Packit 90a5c9
    }
Packit 90a5c9
    else
Packit 90a5c9
        cfg->map = newmap;
Packit 90a5c9
Packit 90a5c9
    return comp_urlmap(cmd, newmap, from, to, flags, cond);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *set_doctype(cmd_parms *cmd, void *CFG,
Packit 90a5c9
                               const char *t, const char *l)
Packit 90a5c9
{
Packit 90a5c9
    proxy_html_conf *cfg = (proxy_html_conf *)CFG;
Packit 90a5c9
    if (!strcasecmp(t, "xhtml")) {
Packit 90a5c9
        cfg->etag = xhtml_etag;
Packit 90a5c9
        if (l && !strcasecmp(l, "legacy"))
Packit 90a5c9
            cfg->doctype = fpi_xhtml_legacy;
Packit 90a5c9
        else
Packit 90a5c9
            cfg->doctype = fpi_xhtml;
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(t, "html")) {
Packit 90a5c9
        cfg->etag = html_etag;
Packit 90a5c9
        if (l && !strcasecmp(l, "legacy"))
Packit 90a5c9
            cfg->doctype = fpi_html_legacy;
Packit 90a5c9
        else
Packit 90a5c9
            cfg->doctype = fpi_html;
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(t, "html5")) {
Packit 90a5c9
        cfg->etag = html_etag;
Packit 90a5c9
        cfg->doctype = fpi_html5;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        cfg->doctype = t;
Packit 90a5c9
        if (l && ((l[0] == 'x') || (l[0] == 'X')))
Packit 90a5c9
            cfg->etag = xhtml_etag;
Packit 90a5c9
        else
Packit 90a5c9
            cfg->etag = html_etag;
Packit 90a5c9
    }
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *set_flags(cmd_parms *cmd, void *CFG, const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    proxy_html_conf *cfg = CFG;
Packit 90a5c9
    if (arg && *arg) {
Packit 90a5c9
        if (!strcasecmp(arg, "lowercase"))
Packit 90a5c9
            cfg->flags |= NORM_LC;
Packit 90a5c9
        else if (!strcasecmp(arg, "dospath"))
Packit 90a5c9
            cfg->flags |= NORM_MSSLASH;
Packit 90a5c9
        else if (!strcasecmp(arg, "reset"))
Packit 90a5c9
            cfg->flags |= NORM_RESET;
Packit 90a5c9
    }
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *set_events(cmd_parms *cmd, void *CFG, const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    tattr *attr;
Packit 90a5c9
    proxy_html_conf *cfg = CFG;
Packit 90a5c9
    if (cfg->events == NULL)
Packit 90a5c9
        cfg->events = apr_array_make(cmd->pool, 20, sizeof(tattr));
Packit 90a5c9
    attr = apr_array_push(cfg->events);
Packit 90a5c9
    attr->val = arg;
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *set_links(cmd_parms *cmd, void *CFG,
Packit 90a5c9
                             const char *elt, const char *att)
Packit 90a5c9
{
Packit 90a5c9
    apr_array_header_t *attrs;
Packit 90a5c9
    tattr *attr;
Packit 90a5c9
    proxy_html_conf *cfg = CFG;
Packit 90a5c9
Packit 90a5c9
    if (cfg->links == NULL)
Packit 90a5c9
        cfg->links = apr_hash_make(cmd->pool);
Packit 90a5c9
Packit 90a5c9
    attrs = apr_hash_get(cfg->links, elt, APR_HASH_KEY_STRING);
Packit 90a5c9
    if (!attrs) {
Packit 90a5c9
        attrs = apr_array_make(cmd->pool, 2, sizeof(tattr*));
Packit 90a5c9
        apr_hash_set(cfg->links, elt, APR_HASH_KEY_STRING, attrs);
Packit 90a5c9
    }
Packit 90a5c9
    attr = apr_array_push(attrs);
Packit 90a5c9
    attr->val = att;
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
static const command_rec proxy_html_cmds[] = {
Packit 90a5c9
    AP_INIT_ITERATE("ProxyHTMLEvents", set_events, NULL,
Packit 90a5c9
                    RSRC_CONF|ACCESS_CONF,
Packit 90a5c9
                    "Strings to be treated as scripting events"),
Packit 90a5c9
    AP_INIT_ITERATE2("ProxyHTMLLinks", set_links, NULL,
Packit 90a5c9
                     RSRC_CONF|ACCESS_CONF, "Declare HTML Attributes"),
Packit 90a5c9
    AP_INIT_RAW_ARGS("ProxyHTMLURLMap", set_urlmap, NULL,
Packit 90a5c9
                     RSRC_CONF|ACCESS_CONF, "Map URL From To"),
Packit 90a5c9
    AP_INIT_TAKE12("ProxyHTMLDoctype", set_doctype, NULL,
Packit 90a5c9
                   RSRC_CONF|ACCESS_CONF, "(HTML|XHTML) [Legacy]"),
Packit 90a5c9
    AP_INIT_ITERATE("ProxyHTMLFixups", set_flags, NULL,
Packit 90a5c9
                    RSRC_CONF|ACCESS_CONF, "Options are lowercase, dospath"),
Packit 90a5c9
    AP_INIT_FLAG("ProxyHTMLMeta", ap_set_flag_slot,
Packit 90a5c9
                 (void*)APR_OFFSETOF(proxy_html_conf, metafix),
Packit 90a5c9
                 RSRC_CONF|ACCESS_CONF, "Fix META http-equiv elements"),
Packit 90a5c9
    AP_INIT_FLAG("ProxyHTMLInterp", ap_set_flag_slot,
Packit 90a5c9
                 (void*)APR_OFFSETOF(proxy_html_conf, interp),
Packit 90a5c9
                 RSRC_CONF|ACCESS_CONF,
Packit 90a5c9
                 "Support interpolation and conditions in URLMaps"),
Packit 90a5c9
    AP_INIT_FLAG("ProxyHTMLExtended", ap_set_flag_slot,
Packit 90a5c9
                 (void*)APR_OFFSETOF(proxy_html_conf, extfix),
Packit 90a5c9
                 RSRC_CONF|ACCESS_CONF, "Map URLs in Javascript and CSS"),
Packit 90a5c9
    AP_INIT_FLAG("ProxyHTMLStripComments", ap_set_flag_slot,
Packit 90a5c9
                 (void*)APR_OFFSETOF(proxy_html_conf, strip_comments),
Packit 90a5c9
                 RSRC_CONF|ACCESS_CONF, "Strip out comments"),
Packit 90a5c9
    AP_INIT_TAKE1("ProxyHTMLBufSize", ap_set_int_slot,
Packit 90a5c9
                  (void*)APR_OFFSETOF(proxy_html_conf, bufsz),
Packit 90a5c9
                  RSRC_CONF|ACCESS_CONF, "Buffer size"),
Packit 90a5c9
    AP_INIT_TAKE1("ProxyHTMLCharsetOut", ap_set_string_slot,
Packit 90a5c9
                  (void*)APR_OFFSETOF(proxy_html_conf, charset_out),
Packit 90a5c9
                  RSRC_CONF|ACCESS_CONF, "Usage: ProxyHTMLCharsetOut charset"),
Packit 90a5c9
    AP_INIT_FLAG("ProxyHTMLEnable", ap_set_flag_slot,
Packit 90a5c9
                 (void*)APR_OFFSETOF(proxy_html_conf, enabled),
Packit 90a5c9
                 RSRC_CONF|ACCESS_CONF,
Packit 90a5c9
                 "Enable proxy-html and xml2enc filters"),
Packit 90a5c9
    { NULL }
Packit 90a5c9
};
Packit 90a5c9
static int mod_proxy_html(apr_pool_t *p, apr_pool_t *p1, apr_pool_t *p2)
Packit 90a5c9
{
Packit 90a5c9
    seek_meta = ap_pregcomp(p, "<meta[^>]*(http-equiv)[^>]*>",
Packit 90a5c9
                            AP_REG_EXTENDED|AP_REG_ICASE);
Packit 90a5c9
    seek_content = apr_strmatch_precompile(p, "content", 0);
Packit 90a5c9
    memset(&sax, 0, sizeof(htmlSAXHandler));
Packit 90a5c9
    sax.startElement = pstartElement;
Packit 90a5c9
    sax.endElement = pendElement;
Packit 90a5c9
    sax.characters = pcharacters;
Packit 90a5c9
    sax.comment = pcomment;
Packit 90a5c9
    sax.cdataBlock = pcdata;
Packit 90a5c9
    xml2enc_charset = APR_RETRIEVE_OPTIONAL_FN(xml2enc_charset);
Packit 90a5c9
    xml2enc_filter = APR_RETRIEVE_OPTIONAL_FN(xml2enc_filter);
Packit 90a5c9
    if (!xml2enc_charset) {
Packit 90a5c9
        ap_log_perror(APLOG_MARK, APLOG_NOTICE, 0, p2, APLOGNO(01425)
Packit 90a5c9
                      "I18n support in mod_proxy_html requires mod_xml2enc. "
Packit 90a5c9
                      "Without it, non-ASCII characters in proxied pages are "
Packit 90a5c9
                      "likely to display incorrectly.");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* old_expr only needs to last the life of the config phase */
Packit 90a5c9
    old_expr = ap_rxplus_compile(p1, "s/^(!)?(\\w+)((=)(.+))?$/reqenv('$2')$1$4'$5'/");
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
static void proxy_html_insert(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    proxy_html_conf *cfg;
Packit 90a5c9
    cfg = ap_get_module_config(r->per_dir_config, &proxy_html_module);
Packit 90a5c9
    if (cfg->enabled) {
Packit 90a5c9
        if (xml2enc_filter)
Packit 90a5c9
            xml2enc_filter(r, NULL, ENCIO_INPUT_CHECKS);
Packit 90a5c9
        ap_add_output_filter("proxy-html", NULL, r, r->connection);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
static void proxy_html_hooks(apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    static const char *aszSucc[] = { "mod_filter.c", NULL };
Packit 90a5c9
    ap_register_output_filter_protocol("proxy-html", proxy_html_filter,
Packit 90a5c9
                                       NULL, AP_FTYPE_RESOURCE,
Packit 90a5c9
                          AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH);
Packit 90a5c9
    /* move this to pre_config so old_expr is available to interpret
Packit 90a5c9
     * old-style conditions on URL maps.
Packit 90a5c9
     */
Packit 90a5c9
    ap_hook_pre_config(mod_proxy_html, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
    ap_hook_insert_filter(proxy_html_insert, NULL, aszSucc, APR_HOOK_MIDDLE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(proxy_html) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    proxy_html_config,
Packit 90a5c9
    proxy_html_merge,
Packit 90a5c9
    NULL,
Packit 90a5c9
    NULL,
Packit 90a5c9
    proxy_html_cmds,
Packit 90a5c9
    proxy_html_hooks
Packit 90a5c9
};