-const char filters_rcs[] = "$Id: filters.c,v 1.128 2010/04/03 13:23:28 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.152 2011/10/30 16:18:12 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/filters.c,v $
#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>
#include "urlmatch.h"
#include "loaders.h"
+#ifdef HAVE_STRTOK
+/* Only used for locks */
+#include "jcc.h"
+#endif /* def HAVE_STRTOK */
+
#ifdef _WIN32
#include "win32.h"
#endif
*/
#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);
{
return(0);
}
+ else
+ {
+ return(1);
+ }
}
else if (
#ifdef HAVE_RFC2553
* 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
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];
{
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);
* 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.
if (0 == strcmpic(redirect_mode, "check-decoded-url"))
{
- log_error(LOG_LEVEL_REDIRECTS, "Decoding \"%s\" if necessary.", subject);
+ log_error(LOG_LEVEL_REDIRECTS,
+ "Checking \"%s\" for encoded redirects.", subject);
+
+#if defined(MUTEX_LOCKS_AVAILABLE) && defined(HAVE_STRTOK)
+ /*
+ * Check each parameter in the URL separately.
+ * Sectionize the URL at "?" and "&",
+ * then URL-decode each component,
+ * and look for a URL in the decoded result.
+ * Keep the last one we spot.
+ */
+ char *found = NULL;
+
+ privoxy_mutex_lock(&strtok_mutex);
+ char *token = strtok(subject, "?&");
+ while (token)
+ {
+ char *dtoken = url_decode(token);
+ if (NULL == dtoken)
+ {
+ log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", token);
+ continue;
+ }
+ char *h1 = strstr(dtoken, "http://");
+ char *h2 = strstr(dtoken, "https://");
+ char *h = (h1 && h2
+ ? (h1 < h2 ? h1 : h2)
+ : (h1 ? h1 : h2));
+ if (h)
+ {
+ freez(found);
+ found = strdup(h);
+ if (found == NULL)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Out of memory while searching for redirects.");
+ privoxy_mutex_unlock(&strtok_mutex);
+ return NULL;
+ }
+ }
+ freez(dtoken);
+ token = strtok(NULL, "?&");
+ }
+ privoxy_mutex_unlock(&strtok_mutex);
+ freez(subject);
+
+ return found;
+#else
new_url = url_decode(subject);
if (new_url != NULL)
{
{
log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", subject);
}
+#endif /* defined(MUTEX_LOCKS_AVAILABLE) && defined(HAVE_STRTOK) */
}
- log_error(LOG_LEVEL_REDIRECTS, "Checking \"%s\" for redirects.", subject);
+ /* Else, just look for a URL inside this one, without decoding anything. */
+
+ log_error(LOG_LEVEL_REDIRECTS,
+ "Checking \"%s\" for unencoded 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 (0 == strcmpic(new_url, csp->http->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 re_filterfile_spec *b;
struct list_entry *filtername;
- /*
+ /*
* Sanity first
*/
if (csp->iob->cur >= csp->iob->eod)
* 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.
if ((newsize += chunksize) >= *size)
{
+ /*
+ * XXX: The message is a bit confusing. Isn't the real problem that
+ * the specified chunk size is greater than the number of bytes
+ * left in the buffer? This probably means the connection got
+ * closed prematurely. To be investigated after 3.0.17 is out.
+ */
log_error(LOG_LEVEL_ERROR,
- "Chunk size %d exceeds buffer size %d in \"chunked\" transfer coding",
+ "Chunk size %d exceeds buffer size %d in \"chunked\" transfer coding",
chunksize, *size);
return JB_ERR_PARSE;
}
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));
}
/*********************************************************************
*
- * 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