-const char filters_rcs[] = "$Id: filters.c,v 1.122 2009/06/08 16:48:09 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.179 2013/12/24 13:32:51 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/filters.c,v $
*
* Purpose : Declares functions to parse/crunch headers and pages.
- * Functions declared include:
- * `acl_addr', `add_stats', `block_acl', `block_imageurl',
- * `block_url', `url_actions', `domain_split',
- * `filter_popups', `forward_url', 'redirect_url',
- * `ij_untrusted_url', `intercept_url', `pcrs_filter_respose',
- * `ijb_send_banner', `trust_url', `gif_deanimate_response',
- * `execute_single_pcrs_command', `rewrite_url',
- * `get_last_url'
- *
- * Copyright : Written by and Copyright (C) 2001, 2004-2009 the
+ *
+ * Copyright : Written by and Copyright (C) 2001-2011 the
* Privoxy team. http://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
#include <string.h>
#include <assert.h>
-#ifdef HAVE_RFC2553
-#include <netdb.h>
-#include <sys/socket.h>
-#endif /* def HAVE_RFC2553 */
-
#ifndef _WIN32
#ifndef __OS2__
#include <unistd.h>
const char filters_h_rcs[] = FILTERS_H_VERSION;
-/* Fix a problem with Solaris. There should be no effect on other
- * platforms.
- * Solaris's isspace() is a macro which uses it's argument directly
- * as an array index. Therefore we need to make sure that high-bit
- * characters generate +ve values, and ideally we also want to make
- * the argument match the declared parameter type of "int".
- */
-#define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
-
+typedef char *(*filter_function_ptr)();
+static filter_function_ptr get_filter_function(const struct client_state *csp);
static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size);
static jb_err prepare_for_filtering(struct client_state *csp);
if (network->ss_family != netmask->ss_family)
{
/* This should never happen */
- log_error(LOG_LEVEL_ERROR,
- "Internal error at %s:%llu: network and netmask differ in family",
- __FILE__, __LINE__);
- return 0;
+ assert(network->ss_family == netmask->ss_family);
+ log_error(LOG_LEVEL_FATAL, "Network and netmask differ in family.");
}
sockaddr_storage_to_ip(network, &network_addr, &addr_len, &network_port);
netmask_addr += 12;
addr_len = 4;
}
- else if (network->ss_family != address->ss_family)
- {
- return 0;
- }
/* XXX: Port check is signaled in netmask */
if (*netmask_port && *network_port != *address_port)
{
return(0);
}
+ else
+ {
+ return(1);
+ }
}
else if (
#ifdef HAVE_RFC2553
if ((p = strchr(acl_spec, '/')) != NULL)
{
*p++ = '\0';
- if (ijb_isdigit(*p) == 0)
+ if (privoxy_isdigit(*p) == 0)
{
freez(acl_spec);
return(-1);
hints.ai_socktype = SOCK_STREAM;
i = getaddrinfo(acl_spec, ((p) ? ++p : NULL), &hints, &result);
- freez(acl_spec);
if (i != 0)
{
log_error(LOG_LEVEL_ERROR, "Can not resolve [%s]:%s: %s",
acl_spec, p, gai_strerror(i));
+ freez(acl_spec);
return(-1);
}
+ freez(acl_spec);
/* TODO: Allow multihomed hostnames */
memcpy(&(aca->addr), result->ai_addr, result->ai_addrlen);
* of octets (128-bit CPU could do it in one iteration).
*/
/*
- * Octets after prefix can be ommitted because of
+ * Octets after prefix can be omitted because of
* previous initialization to zeros.
*/
for (i = 0; (i < addr_len) && masklength; i++)
}
if (csp->action->flags & ACTION_REDIRECT)
{
- log_error(LOG_LEVEL_ERROR, "redirect{} overruled by block.");
+ log_error(LOG_LEVEL_ERROR, "redirect{} overruled by block.");
}
/*
* Else, prepare a response
/* determine HOW images should be blocked */
p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
- if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
+ if (csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
{
log_error(LOG_LEVEL_ERROR, "handle-as-empty-document overruled by handle-as-image.");
}
-#if 1 /* Two alternative strategies, use this one for now: */
/* and handle accordingly: */
if ((p == NULL) || (0 == strcmpic(p, "pattern")))
return cgi_error_memory();
}
}
-
else if (0 == strcmpic(p, "blank"))
{
rsp->status = strdup("403 Request blocked by Privoxy");
return cgi_error_memory();
}
}
-
else
{
rsp->status = strdup("302 Local Redirect from Privoxy");
}
}
-#else /* Following code is disabled for now */
-
- /* and handle accordingly: */
- if ((p == NULL) || (0 == strcmpic(p, "pattern")))
- {
- p = CGI_PREFIX "send-banner?type=pattern";
- }
- else if (0 == strcmpic(p, "blank"))
- {
- p = CGI_PREFIX "send-banner?type=blank";
- }
- rsp->status = strdup("302 Local Redirect from Privoxy");
- if (rsp->status == NULL)
- {
- free_http_response(rsp);
- return cgi_error_memory();
- }
-
- if (enlist_unique_header(rsp->headers, "Location", p))
- {
- free_http_response(rsp);
- return cgi_error_memory();
- }
-#endif /* Preceeding code is disabled for now */
}
- else if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
+ else
+#endif /* def FEATURE_IMAGE_BLOCKING */
+ if (csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
{
/*
- * Send empty document.
+ * Send empty document.
*/
new_content_type = csp->action->string[ACTION_STRING_CONTENT_TYPE];
rsp->body = strdup(" ");
rsp->content_length = 1;
- rsp->status = strdup("403 Request blocked by Privoxy");
+ if (csp->config->feature_flags & RUNTIME_FEATURE_EMPTY_DOC_RETURNS_OK)
+ {
+ /*
+ * Workaround for firefox bug 492459
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=492459
+ * Return a 200 OK status for pages blocked with +handle-as-empty-document
+ * if the "handle-as-empty-doc-returns-ok" runtime config option is set.
+ */
+ rsp->status = strdup("200 Request blocked by Privoxy");
+ }
+ else
+ {
+ rsp->status = strdup("403 Request blocked by Privoxy");
+ }
+
if (rsp->status == NULL)
{
free_http_response(rsp);
}
}
else
-#endif /* def FEATURE_IMAGE_BLOCKING */
/*
* Else, generate an HTML "blocked" message:
{
jb_err err;
struct map * exports;
- char *p;
-
- /*
- * Workaround for stupid Netscape bug which prevents
- * pages from being displayed if loading a referenced
- * JavaScript or style sheet fails. So make it appear
- * as if it succeeded.
- */
- if ( NULL != (p = get_header_value(csp->headers, "User-Agent:"))
- && !strncmpic(p, "mozilla", 7) /* Catch Netscape but */
- && !strstr(p, "Gecko") /* save Mozilla, */
- && !strstr(p, "compatible") /* MSIE */
- && !strstr(p, "Opera")) /* and Opera. */
- {
- rsp->status = strdup("200 Request for blocked URL");
- }
- else
- {
- rsp->status = strdup("403 Request for blocked URL");
- }
+ rsp->status = strdup("403 Request blocked by Privoxy");
if (rsp->status == NULL)
{
free_http_response(rsp);
return cgi_error_memory();
}
}
- rsp->reason = RSP_REASON_BLOCKED;
+ rsp->crunch_reason = BLOCKED;
return finish_http_response(csp, rsp);
struct map * exports;
char buf[BUFFER_SIZE];
char *p;
- struct url_spec **tl;
- struct url_spec *t;
+ struct pattern_spec **tl;
+ struct pattern_spec *t;
jb_err err;
/*
* Export the protocol, host, port, and referrer information
*/
err = map(exports, "hostport", 1, csp->http->hostport, 1);
- if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
+ if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
if (!err) err = map(exports, "path", 1, csp->http->path, 1);
if (NULL != (p = get_header_value(csp->headers, "Referer:")))
free_http_response(rsp);
return cgi_error_memory();
}
- rsp->reason = RSP_REASON_UNTRUSTED;
+ rsp->crunch_reason = UNTRUSTED;
return finish_http_response(csp, rsp);
}
* 2 : b = The filter list to compile
*
* Returns : NULL in case of errors, otherwise the
- * pcrs job list.
+ * pcrs job list.
*
*********************************************************************/
pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const struct re_filterfile_spec *b)
dummy = pcrs_compile_dynamic_command(pattern->str, variables, &error);
if (NULL == dummy)
{
- assert(error < 0);
log_error(LOG_LEVEL_ERROR,
- "Adding filter job \'%s\' to dynamic filter %s failed: %s",
- pattern->str, b->name, pcrs_strerror(error));
+ "Compiling dynamic pcrs job '%s' for '%s' failed with error code %d: %s",
+ pattern->str, b->name, error, pcrs_strerror(error));
continue;
}
else
* 2 : pcrs_command = pcrs command formatted as string (s@foo@bar@)
*
*
- * Returns : NULL if the pcrs_command didn't change the url, or
+ * Returns : NULL if the pcrs_command didn't change the url, or
* the result of the modification.
*
*********************************************************************/
*
* Parameters :
* 1 : subject = the string to check
- * 2 : redirect_mode = +fast-redirect{} mode
+ * 2 : redirect_mode = +fast-redirect{} mode
*
* Returns : NULL if no URL was found, or
* the last URL found.
return NULL;
}
- if (0 == strcmpic(redirect_mode, "check-decoded-url"))
+ if (0 == strcmpic(redirect_mode, "check-decoded-url") && strchr(subject, '%'))
{
- log_error(LOG_LEVEL_REDIRECTS, "Decoding \"%s\" if necessary.", subject);
- new_url = url_decode(subject);
- if (new_url != NULL)
+ char *url_segment = NULL;
+ char **url_segments;
+ size_t max_segments;
+ int segments;
+
+ log_error(LOG_LEVEL_REDIRECTS,
+ "Checking \"%s\" for encoded redirects.", subject);
+
+ /*
+ * Check each parameter in the URL separately.
+ * Sectionize the URL at "?" and "&",
+ * go backwards through the segments, URL-decode them
+ * and look for a URL in the decoded result.
+ * Stop the search after the first match.
+ *
+ * XXX: This estimate is guaranteed to be high enough as we
+ * let ssplit() ignore empty fields, but also a bit wasteful.
+ */
+ max_segments = strlen(subject) / 2;
+ url_segments = malloc(max_segments * sizeof(char *));
+
+ if (NULL == url_segments)
{
+ log_error(LOG_LEVEL_ERROR,
+ "Out of memory while decoding URL: %s", subject);
freez(subject);
- subject = new_url;
+ return NULL;
}
- else
+
+ segments = ssplit(subject, "?&", url_segments, max_segments);
+
+ while (segments-- > 0)
+ {
+ char *dtoken = url_decode(url_segments[segments]);
+ if (NULL == dtoken)
+ {
+ log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", url_segments[segments]);
+ continue;
+ }
+ url_segment = strstr(dtoken, "http://");
+ if (NULL == url_segment)
+ {
+ url_segment = strstr(dtoken, "https://");
+ }
+ if (NULL != url_segment)
+ {
+ url_segment = strdup(url_segment);
+ freez(dtoken);
+ if (url_segment == NULL)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Out of memory while searching for redirects.");
+ return NULL;
+ }
+ break;
+ }
+ freez(dtoken);
+ }
+ freez(subject);
+ freez(url_segments);
+
+ if (url_segment == NULL)
{
- log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", subject);
+ return NULL;
}
+ subject = url_segment;
+ }
+ else
+ {
+ /* Look for a URL inside this one, without decoding anything. */
+ log_error(LOG_LEVEL_REDIRECTS,
+ "Checking \"%s\" for unencoded redirects.", subject);
}
-
- log_error(LOG_LEVEL_REDIRECTS, "Checking \"%s\" for redirects.", subject);
/*
* Find the last URL encoded in the request
))
{
/*
- * Return new URL if we found a redirect
+ * Return new URL if we found a redirect
* or if the subject already was a URL.
*
* The second case makes sure that we can
#endif /* def FEATURE_FAST_REDIRECTS */
csp->action->flags &= ~ACTION_REDIRECT;
- /* Did any redirect action trigger? */
+ /* Did any redirect action trigger? */
if (new_url)
{
+ if (url_requires_percent_encoding(new_url))
+ {
+ char *encoded_url;
+ log_error(LOG_LEVEL_REDIRECTS, "Percent-encoding redirect URL: %N",
+ strlen(new_url), new_url);
+ encoded_url = percent_encode_url(new_url);
+ freez(new_url);
+ if (encoded_url == NULL)
+ {
+ return cgi_error_memory();
+ }
+ new_url = encoded_url;
+ assert(FALSE == url_requires_percent_encoding(new_url));
+ }
+
if (0 == strcmpic(new_url, csp->http->url))
{
log_error(LOG_LEVEL_ERROR,
return cgi_error_memory();
}
- if ( enlist_unique_header(rsp->headers, "Location", new_url)
- || (NULL == (rsp->status = strdup("302 Local Redirect from Privoxy"))) )
+ if (enlist_unique_header(rsp->headers, "Location", new_url)
+ || (NULL == (rsp->status = strdup("302 Local Redirect from Privoxy"))))
{
freez(new_url);
free_http_response(rsp);
return cgi_error_memory();
}
- rsp->reason = RSP_REASON_REDIRECTED;
+ rsp->crunch_reason = REDIRECTED;
freez(new_url);
return finish_http_response(csp, rsp);
{
struct file_list *fl;
struct block_spec *b;
- struct url_spec **trusted_url;
+ struct pattern_spec **trusted_url;
struct http_request rhttp[1];
const char * referer;
jb_err err;
#endif /* def FEATURE_TRUST */
+/*********************************************************************
+ *
+ * Function : get_filter
+ *
+ * Description : Get a filter with a given name and type.
+ * Note that taggers are filters, too.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : requested_name = Name of the content filter to get
+ * 3 : requested_type = Type of the filter to tagger to lookup
+ *
+ * Returns : A pointer to the requested filter
+ * or NULL if the filter wasn't found
+ *
+ *********************************************************************/
+struct re_filterfile_spec *get_filter(const struct client_state *csp,
+ const char *requested_name,
+ enum filter_type requested_type)
+{
+ int i;
+ struct re_filterfile_spec *b;
+ struct file_list *fl;
+
+ for (i = 0; i < MAX_AF_FILES; i++)
+ {
+ fl = csp->rlist[i];
+ if ((NULL == fl) || (NULL == fl->f))
+ {
+ /*
+ * Either there are no filter files left or this
+ * filter file just contains no valid filters.
+ *
+ * Continue to be sure we don't miss valid filter
+ * files that are chained after empty or invalid ones.
+ */
+ continue;
+ }
+
+ for (b = fl->f; b != NULL; b = b->next)
+ {
+ if (b->type != requested_type)
+ {
+ /* The callers isn't interested in this filter type. */
+ continue;
+ }
+ if (strcmp(b->name, requested_name) == 0)
+ {
+ /* The requested filter has been found. Abort search. */
+ return b;
+ }
+ }
+ }
+
+ /* No filter with the given name and type exists. */
+ return NULL;
+
+}
+
+
/*********************************************************************
*
* Function : pcrs_filter_response
*********************************************************************/
static char *pcrs_filter_response(struct client_state *csp)
{
- int hits=0;
+ int hits = 0;
size_t size, prev_size;
char *old = NULL;
char *new = NULL;
pcrs_job *job;
- struct file_list *fl;
struct re_filterfile_spec *b;
struct list_entry *filtername;
- int i, found_filters = 0;
-
- /*
+ /*
* Sanity first
*/
if (csp->iob->cur >= csp->iob->eod)
return(NULL);
}
- /*
- * Need to check the set of re_filterfiles...
- */
- for (i = 0; i < MAX_AF_FILES; i++)
- {
- fl = csp->rlist[i];
- if (NULL != fl)
- {
- if (NULL != fl->f)
- {
- found_filters = 1;
- break;
- }
- }
- }
-
- if (0 == found_filters)
+ if (filters_available(csp) == FALSE)
{
log_error(LOG_LEVEL_ERROR, "Inconsistent configuration: "
"content filtering enabled, but no content filters available.");
size = (size_t)(csp->iob->eod - csp->iob->cur);
old = csp->iob->cur;
- for (i = 0; i < MAX_AF_FILES; i++)
- {
- fl = csp->rlist[i];
- if ((NULL == fl) || (NULL == fl->f))
- {
- /*
- * Either there are no filter files
- * left, or this filter file just
- * contains no valid filters.
- *
- * Continue to be sure we don't miss
- * valid filter files that are chained
- * after empty or invalid ones.
- */
- continue;
- }
/*
* For all applying +filter actions, look if a filter by that
* name exists and if yes, execute it's pcrs_joblist on the
* buffer.
*/
- for (b = fl->f; b; b = b->next)
+ for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first;
+ filtername != NULL; filtername = filtername->next)
{
- if (b->type != FT_CONTENT_FILTER)
+ int current_hits = 0; /* Number of hits caused by this filter */
+ int job_number = 0; /* Which job we're currently executing */
+ int job_hits = 0; /* How many hits the current job caused */
+ pcrs_job *joblist;
+
+ b = get_filter(csp, filtername->str, FT_CONTENT_FILTER);
+ if (b == NULL)
{
- /* Skip header filters */
continue;
}
- for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first;
- filtername ; filtername = filtername->next)
- {
- if (strcmp(b->name, filtername->str) == 0)
- {
- int current_hits = 0; /* Number of hits caused by this filter */
- int job_number = 0; /* Which job we're currently executing */
- int job_hits = 0; /* How many hits the current job caused */
- pcrs_job *joblist = b->joblist;
+ joblist = b->joblist;
- if (b->dynamic) joblist = compile_dynamic_pcrs_job_list(csp, b);
+ if (b->dynamic) joblist = compile_dynamic_pcrs_job_list(csp, b);
- if (NULL == joblist)
- {
- log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name);
- continue;
- }
+ if (NULL == joblist)
+ {
+ log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name);
+ continue;
+ }
- prev_size = size;
- /* Apply all jobs from the joblist */
- for (job = joblist; NULL != job; job = job->next)
- {
- job_number++;
- job_hits = pcrs_execute(job, old, size, &new, &size);
+ prev_size = size;
+ /* Apply all jobs from the joblist */
+ for (job = joblist; NULL != job; job = job->next)
+ {
+ job_number++;
+ job_hits = pcrs_execute(job, old, size, &new, &size);
- if (job_hits >= 0)
- {
- /*
- * That went well. Continue filtering
- * and use the result of this job as
- * input for the next one.
- */
- current_hits += job_hits;
- if (old != csp->iob->cur)
- {
- freez(old);
- }
- old = new;
- }
- else
- {
- /*
- * This job caused an unexpected error. Inform the user
- * and skip the rest of the jobs in this filter. We could
- * continue with the next job, but usually the jobs
- * depend on each other or are similar enough to
- * fail for the same reason.
- *
- * At the moment our pcrs expects the error codes of pcre 3.4,
- * but newer pcre versions can return additional error codes.
- * As a result pcrs_strerror()'s error message might be
- * "Unknown error ...", therefore we print the numerical value
- * as well.
- *
- * XXX: Is this important enough for LOG_LEVEL_ERROR or
- * should we use LOG_LEVEL_RE_FILTER instead?
- */
- log_error(LOG_LEVEL_ERROR, "Skipped filter \'%s\' after job number %u: %s (%d)",
- b->name, job_number, pcrs_strerror(job_hits), job_hits);
- break;
- }
+ if (job_hits >= 0)
+ {
+ /*
+ * That went well. Continue filtering
+ * and use the result of this job as
+ * input for the next one.
+ */
+ current_hits += job_hits;
+ if (old != csp->iob->cur)
+ {
+ freez(old);
}
+ old = new;
+ }
+ else
+ {
+ /*
+ * This job caused an unexpected error. Inform the user
+ * and skip the rest of the jobs in this filter. We could
+ * continue with the next job, but usually the jobs
+ * depend on each other or are similar enough to
+ * fail for the same reason.
+ *
+ * At the moment our pcrs expects the error codes of pcre 3.4,
+ * but newer pcre versions can return additional error codes.
+ * As a result pcrs_strerror()'s error message might be
+ * "Unknown error ...", therefore we print the numerical value
+ * as well.
+ *
+ * XXX: Is this important enough for LOG_LEVEL_ERROR or
+ * should we use LOG_LEVEL_RE_FILTER instead?
+ */
+ log_error(LOG_LEVEL_ERROR, "Skipped filter \'%s\' after job number %u: %s (%d)",
+ b->name, job_number, pcrs_strerror(job_hits), job_hits);
+ break;
+ }
+ }
- if (b->dynamic) pcrs_free_joblist(joblist);
+ if (b->dynamic) pcrs_free_joblist(joblist);
- log_error(LOG_LEVEL_RE_FILTER,
- "filtering %s%s (size %d) with \'%s\' produced %d hits (new size %d).",
- csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
+ log_error(LOG_LEVEL_RE_FILTER,
+ "filtering %s%s (size %d) with \'%s\' produced %d hits (new size %d).",
+ csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
- hits += current_hits;
- }
- }
- }
+ hits += current_hits;
}
/*
csp->flags |= CSP_FLAG_MODIFIED;
csp->content_length = size;
- IOB_RESET(csp);
+ clear_iob(csp->iob);
return(new);
* NULL if no content filter is active
*
*********************************************************************/
-filter_function_ptr get_filter_function(struct client_state *csp)
+static filter_function_ptr get_filter_function(const struct client_state *csp)
{
filter_function_ptr filter_function = NULL;
- if ((csp->content_type & CT_TABOO)
- && !(csp->action->flags & ACTION_FORCE_TEXT_MODE))
- {
- return NULL;
- }
-
- /*
- * Are we enabling text mode by force?
- */
- if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
- {
- /*
- * Do we really have to?
- */
- if (csp->content_type & CT_TEXT)
- {
- log_error(LOG_LEVEL_HEADER, "Text mode is already enabled.");
- }
- else
- {
- csp->content_type |= CT_TEXT;
- log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!");
- }
- }
-
- if (!(csp->content_type & CT_DECLARED))
- {
- /*
- * The server didn't bother to declare a MIME-Type.
- * Assume it's text that can be filtered.
- *
- * This also regulary happens with 304 responses,
- * therefore logging anything here would cause
- * too much noise.
- */
- csp->content_type |= CT_TEXT;
- }
-
/*
* Choose the applying filter function based on
* the content type and action settings.
return JB_ERR_PARSE;
}
- if ((newsize += chunksize) >= *size)
+ if (chunksize >= *size - newsize)
{
log_error(LOG_LEVEL_ERROR,
- "Chunk size %d exceeds buffer size %d in \"chunked\" transfer coding",
- chunksize, *size);
+ "Chunk size %u exceeds buffered data left. "
+ "Already digested %u of %u buffered bytes.",
+ chunksize, (unsigned int)newsize, (unsigned int)*size);
return JB_ERR_PARSE;
}
+ newsize += chunksize;
from_p += 2;
memmove(to_p, from_p, (size_t) chunksize);
break;
}
}
-
+
/* XXX: Should get its own loglevel. */
log_error(LOG_LEVEL_RE_FILTER, "De-chunking successful. Shrunk from %d to %d", *size, newsize);
/*********************************************************************
*
- * Function : execute_content_filter
+ * Function : execute_content_filters
*
* Description : Executes a given content filter.
*
* Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
- * 2 : content_filter = The filter function to execute
*
* Returns : Pointer to the modified buffer, or
* NULL if filtering failed or wasn't necessary.
*
*********************************************************************/
-char *execute_content_filter(struct client_state *csp, filter_function_ptr content_filter)
+char *execute_content_filters(struct client_state *csp)
{
+ filter_function_ptr content_filter;
+
+ assert(content_filters_enabled(csp->action));
+
if (0 == csp->iob->eod - csp->iob->cur)
{
/*
return NULL;
}
+ content_filter = get_filter_function(csp);
+
return ((*content_filter)(csp));
}
return NULL;
}
- vec_count = ssplit(forward_settings, " \t", vec, SZ(vec), 1, 1);
+ vec_count = ssplit(forward_settings, " \t", vec, SZ(vec));
if ((vec_count == 2) && !strcasecmp(vec[0], "forward"))
{
fwd->type = SOCKS_NONE;
fwd->type = SOCKS_5;
socks_proxy = vec[1];
}
+ else if (!strcasecmp(vec[0], "forward-socks5t"))
+ {
+ fwd->type = SOCKS_5T;
+ socks_proxy = vec[1];
+ }
if (NULL != socks_proxy)
{
const struct forward_spec *forward_url(struct client_state *csp,
const struct http_request *http)
{
- static const struct forward_spec fwd_default[1] = { FORWARD_SPEC_INITIALIZER };
+ static const struct forward_spec fwd_default[1]; /* Zero'ed due to being static. */
struct forward_spec *fwd = csp->config->forward;
if (csp->action->flags & ACTION_FORWARD_OVERRIDE)
/*********************************************************************
*
- * Function : direct_response
+ * Function : direct_response
*
* Description : Check if Max-Forwards == 0 for an OPTIONS or TRACE
* request and if so, return a HTTP 501 to the client.
* requests properly. Still, what we do here is rfc-
* compliant, whereas ignoring or forwarding are not.
*
- * Parameters :
+ * Parameters :
* 1 : csp = Current client state (buffers, headers, etc...)
*
* Returns : http_response if , NULL if nonmatch or handler fail
{
for (p = csp->headers->first; (p != NULL) ; p = p->next)
{
- if (!strncmpic("Max-Forwards:", p->str, 13))
+ if (!strncmpic(p->str, "Max-Forwards:", 13))
{
unsigned int max_forwards;
{
return cgi_error_memory();
}
-
+
if (NULL == (rsp->status = strdup("501 Not Implemented")))
{
free_http_response(rsp);
}
rsp->is_static = 1;
- rsp->reason = RSP_REASON_UNSUPPORTED;
+ rsp->crunch_reason = UNSUPPORTED;
return(finish_http_response(csp, rsp));
}
}
+/*********************************************************************
+ *
+ * Function : content_requires_filtering
+ *
+ * Description : Checks whether there are any content filters
+ * enabled for the current request and if they
+ * can actually be applied..
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : TRUE for yes, FALSE otherwise
+ *
+ *********************************************************************/
+int content_requires_filtering(struct client_state *csp)
+{
+ if ((csp->content_type & CT_TABOO)
+ && !(csp->action->flags & ACTION_FORCE_TEXT_MODE))
+ {
+ return FALSE;
+ }
+
+ /*
+ * Are we enabling text mode by force?
+ */
+ if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
+ {
+ /*
+ * Do we really have to?
+ */
+ if (csp->content_type & CT_TEXT)
+ {
+ log_error(LOG_LEVEL_HEADER, "Text mode is already enabled.");
+ }
+ else
+ {
+ csp->content_type |= CT_TEXT;
+ log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!");
+ }
+ }
+
+ if (!(csp->content_type & CT_DECLARED))
+ {
+ /*
+ * The server didn't bother to declare a MIME-Type.
+ * Assume it's text that can be filtered.
+ *
+ * This also regulary happens with 304 responses,
+ * therefore logging anything here would cause
+ * too much noise.
+ */
+ csp->content_type |= CT_TEXT;
+ }
+
+ /*
+ * Choose the applying filter function based on
+ * the content type and action settings.
+ */
+ if ((csp->content_type & CT_TEXT) &&
+ (csp->rlist != NULL) &&
+ (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])))
+ {
+ return TRUE;
+ }
+ else if ((csp->content_type & CT_GIF) &&
+ (csp->action->flags & ACTION_DEANIMATE))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+
+}
+
+
/*********************************************************************
*
* Function : content_filters_enabled
* Description : Checks whether there are any content filters
* enabled for the current request.
*
- * Parameters :
+ * Parameters :
* 1 : action = Action spec to check.
*
* Returns : TRUE for yes, FALSE otherwise
!list_is_empty(action->multi[ACTION_MULTI_FILTER]));
}
+
+/*********************************************************************
+ *
+ * Function : filters_available
+ *
+ * Description : Checks whether there are any filters available.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : TRUE for yes, FALSE otherwise.
+ *
+ *********************************************************************/
+int filters_available(const struct client_state *csp)
+{
+ int i;
+ for (i = 0; i < MAX_AF_FILES; i++)
+ {
+ const struct file_list *fl = csp->rlist[i];
+ if ((NULL != fl) && (NULL != fl->f))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
/*
Local Variables:
tab-width: 3