mod_proxy_html.c revision 63a5c72a2b2f46b899c32eee216f9d5dd4072acb
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor/* Copyright (c) 2003-11, WebThing Ltd
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * Copyright (c) 2011-, The Apache Software Foundation
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * Licensed to the Apache Software Foundation (ASF) under one or more
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * contributor license agreements. See the NOTICE file distributed with
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * this work for additional information regarding copyright ownership.
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * The ASF licenses this file to You under the Apache License, Version 2.0
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * (the "License"); you may not use this file except in compliance with
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * the License. You may obtain a copy of the License at
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen * Unless required by applicable law or agreed to in writing, software
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen * distributed under the License is distributed on an "AS IS" BASIS,
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * See the License for the specific language governing permissions and
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * limitations under the License.
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor/* GO_FASTER
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor You can #define GO_FASTER to disable trace logging.
645cf915f6bc22be17750bc5bb34ade8de6744dfnd/* libxml2 */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor/* globals set once at startup */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic apr_status_t (*xml2enc_charset)(request_rec*, xmlCharEncoding*, const char**) = NULL;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic apr_status_t (*xml2enc_filter)(request_rec*, const char*, unsigned int) = NULL;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzortypedef struct {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor const char *val;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzortypedef struct {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor unsigned int start;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor unsigned int end;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzortypedef struct urlmap {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor unsigned int flags;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor unsigned int regflags;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor const char *c;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor const char *to;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzortypedef struct {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor const char *doctype;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor const char *etag;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor unsigned int flags;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzortypedef struct {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor const char *encoding;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzortypedef enum { ATTR_IGNORE, ATTR_URI, ATTR_EVENT } rewrite_t;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic const char *const fpi_html =
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n";
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic const char *const fpi_html_legacy =
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic const char *const fpi_xhtml =
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic const char *const fpi_xhtml_legacy =
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic const char *const fpi_html5 = "<!DOCTYPE html>\n";
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor/*#define DEFAULT_DOCTYPE fpi_html */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor for (p = str; *p; ++p)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor for (p = ap_strchr(str, '\\'); p; p = ap_strchr(p+1, '\\'))
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor/* This is always utf-8 on entry. We can convert charset within FLUSH */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor#define FLUSH AP_fwrite(ctx, (chars+begin), (i-begin), 0); begin = i+1
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic void pcharacters(void *ctxt, const xmlChar *uchars, int length)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor switch (chars[i]) {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor case '&' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, "&"); break;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor case '<' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, "<"); break;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor case '>' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, ">"); break;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor case '"' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, """); break;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor default : break;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor (int(*)(void*))free);
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic void pappend(saxctxt *ctx, const char *buf, const size_t len)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor char c = 0;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* parse the text for URLs */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor while (!ap_regexec(m->from.r, ctx->buf+offs, nmatch, pmatch, 0)) {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor subs = ap_pregsub(ctx->f->r->pool, m->to, ctx->buf+offs,
da81fc93dd9cb59b907544c3f2d47114ce8f5eeaigalic ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r,
da81fc93dd9cb59b907544c3f2d47114ce8f5eeaigalic if ((m->flags & M_ATEND) && (match < (len - s_from)))
da81fc93dd9cb59b907544c3f2d47114ce8f5eeaigalic VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r,
da81fc93dd9cb59b907544c3f2d47114ce8f5eeaigalic "C: matched %s, substituting %s",
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic void pcdata(void *ctxt, const xmlChar *uchars, int length)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* not sure if this should force-flush
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor * (i.e. can one cdata section come in multiple calls?)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic void pcomment(void *ctxt, const xmlChar *uchars)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic void pendElement(void *ctxt, const xmlChar *uname)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor if ((ctx->cfg->doctype == fpi_html) || (ctx->cfg->doctype == fpi_xhtml)) {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* enforce html */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* enforce html legacy */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* TODO - implement HTML "allowed here" using the stack */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* nah. Keeping the stack is too much overhead */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor ctx->offset = 0; /* having dumped it, we can re-use the memory */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzorstatic void pstartElement(void *ctxt, const xmlChar *uname,
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor const char** a;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor const void** descp;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor if ((ctx->cfg->doctype == fpi_html) || (ctx->cfg->doctype == fpi_xhtml)) {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* enforce html */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* enforce html legacy */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01416)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01417)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* TODO - implement HTML "allowed here" */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor if ((enforce > 0) && (desc != NULL) && (desc->attrs_req != NULL))
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor linkattrs = apr_hash_get(ctx->cfg->links, name, APR_HASH_KEY_STRING);
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor switch (htmlAttrAllowed(desc, (xmlChar*)*a, 2-enforce)) {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01418)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor "Bogus HTML attribute %s of %s dropped",
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01419)
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor "Deprecated HTML attribute %s of %s dropped",
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor required_attrs--; /* cross off the number still needed */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* fallthrough - required implies valid */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor if (a[1]) {
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor const char *f;
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor "H/RX: match at %s, substituting %s",
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor "H: matched %s, substituting %s",
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor else { /* it fits in the existing space */
cc8190433d13f5e9de618c5d7f10c824c0c1919cgryzor /* URIs only want one match unless overridden in the config */
d229f940abfb2490dee17979e9a5ff31b7012eb5rbowen num_match = 0; /* reset here since we're working per-rule */
VERBOSE({
ctx->f->r,
f, subs);
++num_match;
while (found) {
m->from.c);
m->from.c);
0, ctx->f->r,
++num_match;
case ATTR_IGNORE:
char *header;
char *content;
char delim;
while (!apr_isalpha(*++p));
if (p != NULL) {
while (apr_isspace(*p))
while (*p && apr_isspace(*++p));
delim = *p++;
for (q = p; *q != delim; ++q);
#ifndef GO_FASTER
return ret;
const char *start;
const char *end;
const char *delim;
const char *before;
const char *after;
const char *replacement;
const char *var;
if (delim) {
if (!replacement) {
if (delim)
return str;
urlmap *p;
const char *err;
if (err) {
if (ok == 0) {
if (prev)
if (!f->ctx) {
const char *force;
if (!force) {
if (!f->r->proxyreq) {
else if (!f->r->content_type) {
if (errmsg) {
#ifndef GO_FASTER
return NULL;
fctx->f = f;
return f->ctx;
apr_bucket* b;
const char *buf = 0;
#ifndef USE_OLD_LIBXML2
if (!ctxt)
b = APR_BUCKET_NEXT(b)) {
if (APR_BUCKET_IS_METADATA(b)) {
if (APR_BUCKET_IS_EOS(b)) {
else if (APR_BUCKET_IS_FLUSH(b)) {
== APR_SUCCESS) {
const char *cenc;
if (!xml2enc_charset ||
if (!xml2enc_charset)
ap_set_content_type(f->r,
return rv;
(int(*)(void*))htmlFreeParserCtxt,
#ifndef USE_OLD_LIBXML2
return APR_SUCCESS;
return ret;
urlmap *a;
return conf;
return err;
const char *usage =
const char *from;
const char *to;
const char *flags;
return usage;
return usage;
return NULL;
return NULL;
return NULL;
if (!attrs) {
return NULL;
{ NULL }
if (!xml2enc_charset) {
return OK;
if (xml2enc_filter)
NULL,
NULL,