static char *http2env(request_rec *r, const char *w) { char *res = (char *)apr_palloc(r->pool, sizeof("HTTP_") + strlen(w)); char *cp = res; char c; *cp++ = 'H'; *cp++ = 'T'; *cp++ = 'T'; *cp++ = 'P'; *cp++ = '_'; while ((c = *w++) != 0) { if (apr_isalnum(c)) { *cp++ = apr_toupper(c); } else if (c == '-') { *cp++ = '_'; } else { if (APLOGrtrace1(r)) ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Not exporting header with invalid name as envvar: %s", ap_escape_logitem(r->pool, w)); return NULL; } } *cp = 0; return res; }
static void dump_content(saxctxt *ctx) { urlmap *m; char *found; size_t s_from, s_to; size_t match; char c = 0; int nmatch; ap_regmatch_t pmatch[10]; char *subs; size_t len, offs; urlmap *themap = ctx->map; #ifndef GO_FASTER int verbose = APLOGrtrace1(ctx->f->r); #endif pappend(ctx, &c, 1); /* append null byte */ /* parse the text for URLs */ for (m = themap; m; m = m->next) { if (!(m->flags & M_CDATA)) continue; if (m->flags & M_REGEX) { nmatch = 10; offs = 0; while (!ap_regexec(m->from.r, ctx->buf+offs, nmatch, pmatch, 0)) { match = pmatch[0].rm_so; s_from = pmatch[0].rm_eo - match; subs = ap_pregsub(ctx->f->r->pool, m->to, ctx->buf+offs, nmatch, pmatch); s_to = strlen(subs); len = strlen(ctx->buf); offs += match; VERBOSEB( const char *f = apr_pstrndup(ctx->f->r->pool, ctx->buf + offs, s_from); ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r, "C/RX: match at %s, substituting %s", f, subs); ) if (s_to > s_from) { preserve(ctx, s_to - s_from); memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from, len + 1 - s_from - offs); memcpy(ctx->buf+offs, subs, s_to); } else { memcpy(ctx->buf + offs, subs, s_to); memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from, len + 1 - s_from - offs); } offs += s_to; } }
/* chk_filter_chain() is called once per filter instance; it tries to * determine if the current filter instance should be disabled because * its translation is incompatible with the translation of an existing * instance of the translate filter * * Example bad scenario: * * configured filter chain for the request: * INCLUDES XLATEOUT(8859-1->UTS-16) * configured filter chain for the subrequest: * XLATEOUT(8859-1->UTS-16) * * When the subrequest is processed, the filter chain will be * XLATEOUT(8859-1->UTS-16) XLATEOUT(8859-1->UTS-16) * This makes no sense, so the instance of XLATEOUT added for the * subrequest will be noop-ed. * * Example good scenario: * * configured filter chain for the request: * INCLUDES XLATEOUT(8859-1->UTS-16) * configured filter chain for the subrequest: * XLATEOUT(IBM-1047->8859-1) * * When the subrequest is processed, the filter chain will be * XLATEOUT(IBM-1047->8859-1) XLATEOUT(8859-1->UTS-16) * This makes sense, so the instance of XLATEOUT added for the * subrequest will be left alone and it will translate from * IBM-1047->8859-1. */ static void chk_filter_chain(ap_filter_t *f) { ap_filter_t *curf; charset_filter_ctx_t *curctx, *last_xlate_ctx = NULL, *ctx = f->ctx; int output = !strcasecmp(f->frec->name, XLATEOUT_FILTER_NAME); if (ctx->noop) { return; } /* walk the filter chain; see if it makes sense for our filter to * do any translation */ curf = output ? f->r->output_filters : f->r->input_filters; while (curf) { if (!strcasecmp(curf->frec->name, f->frec->name) && curf->ctx) { curctx = (charset_filter_ctx_t *)curf->ctx; if (!last_xlate_ctx) { last_xlate_ctx = curctx; } else { if (strcmp(last_xlate_ctx->dc->charset_default, curctx->dc->charset_source)) { /* incompatible translation * if our filter instance is incompatible with an instance * already in place, noop our instance * Notes: * . We are only willing to noop our own instance. * . It is possible to noop another instance which has not * yet run, but this is not currently implemented. * Hopefully it will not be needed. * . It is not possible to noop an instance which has * already run. */ if (last_xlate_ctx == f->ctx) { last_xlate_ctx->noop = 1; if (APLOGrtrace1(f->r)) { const char *symbol = output ? "->" : "<-"; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(01451) "%s %s - disabling " "translation %s%s%s; existing " "translation %s%s%s", f->r->uri ? "uri" : "file", f->r->uri ? f->r->uri : f->r->filename, last_xlate_ctx->dc->charset_source, symbol, last_xlate_ctx->dc->charset_default, curctx->dc->charset_source, symbol, curctx->dc->charset_default); } } else { const char *symbol = output ? "->" : "<-"; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01452) "chk_filter_chain() - can't disable " "translation %s%s%s; existing " "translation %s%s%s", last_xlate_ctx->dc->charset_source, symbol, last_xlate_ctx->dc->charset_default, curctx->dc->charset_source, symbol, curctx->dc->charset_default); } break; } } } curf = curf->next; } }
AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer, int (*getsfunc) (char *, int, void *), void *getsfunc_data, int module_index) { char x[MAX_STRING_LEN]; char *w, *l; int p; int cgi_status = HTTP_UNSET; apr_table_t *merge; apr_table_t *cookie_table; int trace_log = APLOG_R_MODULE_IS_LEVEL(r, module_index, APLOG_TRACE1); int first_header = 1; if (buffer) { *buffer = '\0'; } w = buffer ? buffer : x; /* temporary place to hold headers to merge in later */ merge = apr_table_make(r->pool, 10); /* The HTTP specification says that it is legal to merge duplicate * headers into one. Some browsers that support Cookies don't like * merged headers and prefer that each Set-Cookie header is sent * separately. Lets humour those browsers by not merging. * Oh what a pain it is. */ cookie_table = apr_table_make(r->pool, 2); apr_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL); while (1) { int rv = (*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data); if (rv == 0) { const char *msg = "Premature end of script headers"; if (first_header) msg = "End of script output before headers"; ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, "%s: %s", msg, apr_filepath_name_get(r->filename)); return HTTP_INTERNAL_SERVER_ERROR; } else if (rv == -1) { ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, "Script timed out before returning headers: %s", apr_filepath_name_get(r->filename)); return HTTP_GATEWAY_TIME_OUT; } /* Delete terminal (CR?)LF */ p = strlen(w); /* Indeed, the host's '\n': '\012' for UNIX; '\015' for MacOS; '\025' for OS/390 -- whatever the script generates. */ if (p > 0 && w[p - 1] == '\n') { if (p > 1 && w[p - 2] == CR) { w[p - 2] = '\0'; } else { w[p - 1] = '\0'; } } /* * If we've finished reading the headers, check to make sure any * HTTP/1.1 conditions are met. If so, we're done; normal processing * will handle the script's output. If not, just return the error. * The appropriate thing to do would be to send the script process a * SIGPIPE to let it know we're ignoring it, close the channel to the * script process, and *then* return the failed-to-meet-condition * error. Otherwise we'd be waiting for the script to finish * blithering before telling the client the output was no good. * However, we don't have the information to do that, so we have to * leave it to an upper layer. */ if (w[0] == '\0') { int cond_status = OK; /* PR#38070: This fails because it gets confused when a * CGI Status header overrides ap_meets_conditions. * * We can fix that by dropping ap_meets_conditions when * Status has been set. Since this is the only place * cgi_status gets used, let's test it explicitly. * * The alternative would be to ignore CGI Status when * ap_meets_conditions returns anything interesting. * That would be safer wrt HTTP, but would break CGI. */ if ((cgi_status == HTTP_UNSET) && (r->method_number == M_GET)) { cond_status = ap_meets_conditions(r); } apr_table_overlap(r->err_headers_out, merge, APR_OVERLAP_TABLES_MERGE); if (!apr_is_empty_table(cookie_table)) { /* the cookies have already been copied to the cookie_table */ apr_table_unset(r->err_headers_out, "Set-Cookie"); r->err_headers_out = apr_table_overlay(r->pool, r->err_headers_out, cookie_table); } return cond_status; } if (trace_log) { if (first_header) ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE4, 0, r, "Headers from script '%s':", apr_filepath_name_get(r->filename)); ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE4, 0, r, " %s", w); } /* if we see a bogus header don't ignore it. Shout and scream */ #if APR_CHARSET_EBCDIC /* Chances are that we received an ASCII header text instead of * the expected EBCDIC header lines. Try to auto-detect: */ if (!(l = strchr(w, ':'))) { int maybeASCII = 0, maybeEBCDIC = 0; unsigned char *cp, native; apr_size_t inbytes_left, outbytes_left; for (cp = w; *cp != '\0'; ++cp) { native = apr_xlate_conv_byte(ap_hdrs_from_ascii, *cp); if (apr_isprint(*cp) && !apr_isprint(native)) ++maybeEBCDIC; if (!apr_isprint(*cp) && apr_isprint(native)) ++maybeASCII; } if (maybeASCII > maybeEBCDIC) { ap_log_error(SCRIPT_LOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02660) "CGI Interface Error: " "Script headers apparently ASCII: (CGI = %s)", r->filename); inbytes_left = outbytes_left = cp - w; apr_xlate_conv_buffer(ap_hdrs_from_ascii, w, &inbytes_left, w, &outbytes_left); } } #endif /*APR_CHARSET_EBCDIC*/ if (!(l = strchr(w, ':'))) { if (!buffer) { /* Soak up all the script output - may save an outright kill */ while ((*getsfunc)(w, MAX_STRING_LEN - 1, getsfunc_data) > 0) { continue; } } ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, "malformed header from script '%s': Bad header: %.30s", apr_filepath_name_get(r->filename), w); return HTTP_INTERNAL_SERVER_ERROR; } *l++ = '\0'; while (apr_isspace(*l)) { ++l; } if (!strcasecmp(w, "Content-type")) { char *tmp; /* Nuke trailing whitespace */ char *endp = l + strlen(l) - 1; while (endp > l && apr_isspace(*endp)) { *endp-- = '\0'; } tmp = apr_pstrdup(r->pool, l); ap_content_type_tolower(tmp); ap_set_content_type(r, tmp); } /* * If the script returned a specific status, that's what * we'll use - otherwise we assume 200 OK. */ else if (!strcasecmp(w, "Status")) { r->status = cgi_status = atoi(l); if (!ap_is_HTTP_VALID_RESPONSE(cgi_status)) ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, "Invalid status line from script '%s': %.30s", apr_filepath_name_get(r->filename), l); else if (APLOGrtrace1(r)) ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE1, 0, r, "Status line from script '%s': %.30s", apr_filepath_name_get(r->filename), l); r->status_line = apr_pstrdup(r->pool, l); } else if (!strcasecmp(w, "Location")) { apr_table_set(r->headers_out, w, l); } else if (!strcasecmp(w, "Content-Length")) { apr_table_set(r->headers_out, w, l); } else if (!strcasecmp(w, "Content-Range")) { apr_table_set(r->headers_out, w, l); } else if (!strcasecmp(w, "Transfer-Encoding")) { apr_table_set(r->headers_out, w, l); } else if (!strcasecmp(w, "ETag")) { apr_table_set(r->headers_out, w, l); } /* * If the script gave us a Last-Modified header, we can't just * pass it on blindly because of restrictions on future values. */ else if (!strcasecmp(w, "Last-Modified")) { ap_update_mtime(r, apr_date_parse_http(l)); ap_set_last_modified(r); } else if (!strcasecmp(w, "Set-Cookie")) { apr_table_add(cookie_table, w, l); } else { apr_table_add(merge, w, l); } first_header = 0; } /* never reached - we leave this function within the while loop above */ return OK; }
static apr_status_t init_filter_instance(ap_filter_t *f) { ef_ctx_t *ctx; ef_dir_t *dc; apr_status_t rv; f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(ef_ctx_t)); dc = ap_get_module_config(f->r->per_dir_config, &ext_filter_module); ctx->dc = dc; /* look for the user-defined filter */ ctx->filter = find_filter_def(f->r->server, f->frec->name); if (!ctx->filter) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01459) "couldn't find definition of filter '%s'", f->frec->name); return APR_EINVAL; } ctx->p = f->r->pool; if (ctx->filter->intype && ctx->filter->intype != INTYPE_ALL) { const char *ctypes; if (ctx->filter->mode == INPUT_FILTER) { ctypes = apr_table_get(f->r->headers_in, "Content-Type"); } else { ctypes = f->r->content_type; } if (ctypes) { const char *ctype = ap_getword(f->r->pool, &ctypes, ';'); if (strcasecmp(ctx->filter->intype, ctype)) { /* wrong IMT for us; don't mess with the output */ ctx->noop = 1; } } else { ctx->noop = 1; } } if (ctx->filter->enable_env && !apr_table_get(f->r->subprocess_env, ctx->filter->enable_env)) { /* an environment variable that enables the filter isn't set; bail */ ctx->noop = 1; } if (ctx->filter->disable_env && apr_table_get(f->r->subprocess_env, ctx->filter->disable_env)) { /* an environment variable that disables the filter is set; bail */ ctx->noop = 1; } if (!ctx->noop) { rv = init_ext_filter_process(f); if (rv != APR_SUCCESS) { return rv; } if (ctx->filter->outtype && ctx->filter->outtype != OUTTYPE_UNCHANGED) { ap_set_content_type(f->r, ctx->filter->outtype); } if (ctx->filter->preserves_content_length != 1) { /* nasty, but needed to avoid confusing the browser */ apr_table_unset(f->r->headers_out, "Content-Length"); } } if (APLOGrtrace1(f->r)) { ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, f->r, "%sfiltering `%s' of type `%s' through `%s', cfg %s", ctx->noop ? "NOT " : "", f->r->uri ? f->r->uri : f->r->filename, f->r->content_type ? f->r->content_type : "(unspecified)", ctx->filter->command, get_cfg_string(dc, ctx->filter, f->r->pool)); } return APR_SUCCESS; }