Factor get_filter() out of pcrs_filter_response()
[privoxy.git] / filters.c
index 3994638..fb5de23 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1,19 +1,11 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.155 2011/11/06 11:36:42 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.178 2013/11/24 14:22: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-2010 the
+ *
+ * Copyright   :  Written by and Copyright (C) 2001-2011 the
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -78,26 +70,12 @@ const char filters_rcs[] = "$Id: filters.c,v 1.155 2011/11/06 11:36:42 fabiankei
 #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
 
 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);
@@ -198,10 +176,8 @@ static int match_sockaddr(const struct sockaddr_storage *network,
    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);
@@ -224,10 +200,6 @@ static int match_sockaddr(const struct sockaddr_storage *network,
       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)
@@ -384,7 +356,7 @@ int acl_addr(const char *aspec, struct access_control_addr *aca)
    if ((p = strchr(acl_spec, '/')) != NULL)
    {
       *p++ = '\0';
-      if (ijb_isdigit(*p) == 0)
+      if (privoxy_isdigit(*p) == 0)
       {
          freez(acl_spec);
          return(-1);
@@ -601,7 +573,7 @@ struct http_response *block_url(struct client_state *csp)
       /* 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.");
       }
@@ -670,7 +642,7 @@ struct http_response *block_url(struct client_state *csp)
    }
    else
 #endif /* def FEATURE_IMAGE_BLOCKING */
-   if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
+   if (csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
    {
      /*
       *  Send empty document.
@@ -809,8 +781,8 @@ struct http_response *trust_url(struct client_state *csp)
    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;
 
    /*
@@ -1110,73 +1082,81 @@ char *get_last_url(char *subject, const char *redirect_mode)
       return NULL;
    }
 
-   if (0 == strcmpic(redirect_mode, "check-decoded-url"))
+   if (0 == strcmpic(redirect_mode, "check-decoded-url") && strchr(subject, '%'))
    {  
+      char *url_segment = NULL;
+      char **url_segments;
+      size_t max_segments;
+      int segments;
+
       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,
+       * go backwards through the segments, URL-decode them
        * and look for a URL in the decoded result.
-       * Keep the last one we spot.
+       * 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.
        */
-      char *found = NULL;
+      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);
+         return NULL;
+      }
+
+      segments = ssplit(subject, "?&", url_segments, max_segments);
 
-      privoxy_mutex_lock(&strtok_mutex);
-      char *token = strtok(subject, "?&");
-      while (token)
+      while (segments-- > 0)
       {
-         char *dtoken = url_decode(token);
+         char *dtoken = url_decode(url_segments[segments]);
          if (NULL == dtoken)
          {
-            log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", token);
+            log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", url_segments[segments]);
             continue;
          }
-         char *http_url = strstr(dtoken, "http://");
-         char *https_url = strstr(dtoken, "https://");
-         char *last_url = (http_url && https_url
-                          ? (http_url < https_url ? http_url : https_url)
-                          : (http_url ? http_url : https_url));
-         if (last_url)
+         url_segment = strstr(dtoken, "http://");
+         if (NULL == url_segment)
          {
-            freez(found);
-            found = strdup(last_url);
-            if (found == NULL)
+            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.");
-               privoxy_mutex_unlock(&strtok_mutex);
                return NULL;
             }
+            break;
          }
          freez(dtoken);
-         token = strtok(NULL, "?&");
       }
-      privoxy_mutex_unlock(&strtok_mutex);
       freez(subject);
+      freez(url_segments);
 
-      return found;
-#else
-      new_url = url_decode(subject);
-      if (new_url != NULL)
+      if (url_segment == NULL)
       {
-         freez(subject);
-         subject = new_url;
+         return NULL;
       }
-      else
-      {
-         log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", subject);
-      }
-#endif /* defined(MUTEX_LOCKS_AVAILABLE) && defined(HAVE_STRTOK) */
+      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);
    }
-
-   /* 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
@@ -1320,6 +1300,7 @@ struct http_response *redirect_url(struct client_state *csp)
             return cgi_error_memory();
          }
          new_url = encoded_url;
+         assert(FALSE == url_requires_percent_encoding(new_url));
       }
 
       if (0 == strcmpic(new_url, csp->http->url))
@@ -1428,7 +1409,7 @@ int is_untrusted_url(const struct client_state *csp)
 {
    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;
@@ -1544,6 +1525,66 @@ int is_untrusted_url(const struct client_state *csp)
 #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
@@ -1562,14 +1603,12 @@ int is_untrusted_url(const struct client_state *csp)
 static char *pcrs_filter_response(struct client_state *csp)
 {
    int hits = 0;
-   int i;
    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;
 
@@ -1591,39 +1630,19 @@ static char *pcrs_filter_response(struct client_state *csp)
    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)
+      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  */
@@ -1692,8 +1711,7 @@ static char *pcrs_filter_response(struct client_state *csp)
             hits += current_hits;
          }
       }
-   }
-   }
+
 
    /*
     * If there were no hits, destroy our copy and let
@@ -1707,7 +1725,7 @@ static char *pcrs_filter_response(struct client_state *csp)
 
    csp->flags |= CSP_FLAG_MODIFIED;
    csp->content_length = size;
-   IOB_RESET(csp);
+   clear_iob(csp->iob);
 
    return(new);
 
@@ -1853,19 +1871,15 @@ static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
          return JB_ERR_PARSE;
       }
 
-      if ((newsize += chunksize) >= *size)
+      if (chunksize >= *size - newsize)
       {
-         /*
-          * 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",
-            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);
@@ -2149,7 +2163,7 @@ const static struct forward_spec *get_forward_override_settings(struct client_st
       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;
@@ -2177,6 +2191,11 @@ const static struct forward_spec *get_forward_override_settings(struct client_st
          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)
       {
@@ -2229,7 +2248,7 @@ const static struct forward_spec *get_forward_override_settings(struct client_st
 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)