Add a couple of +fast-redirect{} tests
[privoxy.git] / filters.c
index 8849be2..4de7c90 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1,4 +1,4 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.139 2011/03/03 14:46:37 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 $
@@ -78,6 +78,11 @@ const char filters_rcs[] = "$Id: filters.c,v 1.139 2011/03/03 14:46:37 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
@@ -485,7 +490,7 @@ int acl_addr(const char *aspec, struct access_control_addr *aca)
     * 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++)
@@ -574,7 +579,7 @@ struct http_response *block_url(struct client_state *csp)
    }
    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
@@ -668,7 +673,7 @@ struct http_response *block_url(struct client_state *csp)
    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];
 
@@ -714,27 +719,8 @@ struct http_response *block_url(struct client_state *csp)
    {
       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 blocked by Privoxy");
-      }
-      else
-      {
-         rsp->status = strdup("403 Request blocked by Privoxy");
-      }
 
+      rsp->status = strdup("403 Request blocked by Privoxy");
       if (rsp->status == NULL)
       {
          free_http_response(rsp);
@@ -855,7 +841,7 @@ struct http_response *trust_url(struct client_state *csp)
     * 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:")))
@@ -975,7 +961,7 @@ struct http_response *trust_url(struct client_state *csp)
  *          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)
@@ -1003,7 +989,7 @@ pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const st
       if (NULL == dummy)
       {
          log_error(LOG_LEVEL_ERROR,
-            "Adding filter job \'%s\' to dynamic filter %s failed: %d %s",
+            "Compiling dynamic pcrs job '%s' for '%s' failed with error code %d: %s",
             pattern->str, b->name, error, pcrs_strerror(error));
          continue;
       }
@@ -1044,7 +1030,7 @@ pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const st
  *          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.
  *
  *********************************************************************/
@@ -1103,7 +1089,7 @@ char *rewrite_url(char *old_url, const char *pcrs_command)
  *
  * 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.
@@ -1126,7 +1112,54 @@ char *get_last_url(char *subject, const char *redirect_mode)
 
    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)
       {
@@ -1137,9 +1170,13 @@ char *get_last_url(char *subject, const char *redirect_mode)
       {
          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
@@ -1162,7 +1199,7 @@ char *get_last_url(char *subject, const char *redirect_mode)
          ))
    {
       /*
-       * 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
@@ -1268,7 +1305,7 @@ struct http_response *redirect_url(struct client_state *csp)
 #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))
@@ -1522,7 +1559,7 @@ static char *pcrs_filter_response(struct client_state *csp)
    struct re_filterfile_spec *b;
    struct list_entry *filtername;
 
-   /* 
+   /*
     * Sanity first
     */
    if (csp->iob->cur >= csp->iob->eod)
@@ -1827,7 +1864,7 @@ static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
          break;
       }
    }
-   
+
    /* XXX: Should get its own loglevel. */
    log_error(LOG_LEVEL_RE_FILTER, "De-chunking successful. Shrunk from %d to %d", *size, newsize);
 
@@ -2206,7 +2243,7 @@ const struct forward_spec *forward_url(struct client_state *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.
@@ -2215,7 +2252,7 @@ const struct forward_spec *forward_url(struct client_state *csp,
  *                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
@@ -2231,7 +2268,7 @@ struct http_response *direct_response(struct client_state *csp)
    {
       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;
 
@@ -2255,7 +2292,7 @@ struct http_response *direct_response(struct client_state *csp)
                {
                   return cgi_error_memory();
                }
-            
+
                if (NULL == (rsp->status = strdup("501 Not Implemented")))
                {
                   free_http_response(rsp);
@@ -2356,7 +2393,7 @@ int content_requires_filtering(struct client_state *csp)
  * 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