Reference action files in CGI URLs by id instead
[privoxy.git] / filters.c
index 73b7761..6dd679e 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1,4 +1,4 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.72 2006/12/22 18:52:53 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.84 2007/03/20 15:16:34 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
@@ -13,7 +13,7 @@ const char filters_rcs[] = "$Id: filters.c,v 1.72 2006/12/22 18:52:53 fabiankeil
  *                   `jpeg_inspect_response', `execute_single_pcrs_command',
  *                   `rewrite_url', `get_last_url'
  *
- * Copyright   :  Written by and Copyright (C) 2001, 2004-2006 the SourceForge
+ * Copyright   :  Written by and Copyright (C) 2001, 2004-2007 the SourceForge
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -40,6 +40,58 @@ const char filters_rcs[] = "$Id: filters.c,v 1.72 2006/12/22 18:52:53 fabiankeil
  *
  * Revisions   :
  *    $Log: filters.c,v $
+ *    Revision 1.84  2007/03/20 15:16:34  fabiankeil
+ *    Use dedicated header filter actions instead of abusing "filter".
+ *    Replace "filter-client-headers" and "filter-client-headers"
+ *    with "server-header-filter" and "client-header-filter".
+ *
+ *    Revision 1.83  2007/03/17 15:20:05  fabiankeil
+ *    New config option: enforce-blocks.
+ *
+ *    Revision 1.82  2007/03/13 11:28:43  fabiankeil
+ *    - Fix port handling in acl_addr() and use a temporary acl spec
+ *      copy so error messages don't contain a truncated version.
+ *    - Log size of iob before and after decompression.
+ *
+ *    Revision 1.81  2007/03/05 14:40:53  fabiankeil
+ *    - Cosmetical changes for LOG_LEVEL_RE_FILTER messages.
+ *    - Hide the "Go there anyway" link for blocked CONNECT
+ *      requests where going there anyway doesn't work anyway.
+ *
+ *    Revision 1.80  2007/02/07 10:55:20  fabiankeil
+ *    - Save the reason for generating http_responses.
+ *    - Block (+block) with status code 403 instead of 404.
+ *    - Use a different kludge to remember a failed decompression.
+ *
+ *    Revision 1.79  2007/01/31 16:21:38  fabiankeil
+ *    Search for Max-Forwards headers case-insensitive,
+ *    don't generate the "501 unsupported" message for invalid
+ *    Max-Forwards values and don't increase negative ones.
+ *
+ *    Revision 1.78  2007/01/28 13:41:18  fabiankeil
+ *    - Add HEAD support to finish_http_response.
+ *    - Add error favicon to internal HTML error messages.
+ *
+ *    Revision 1.77  2007/01/12 15:36:44  fabiankeil
+ *    Mark *csp as immutable for is_untrusted_url()
+ *    and is_imageurl(). Closes FR 1237736.
+ *
+ *    Revision 1.76  2007/01/01 19:36:37  fabiankeil
+ *    Integrate a modified version of Wil Mahan's
+ *    zlib patch (PR #895531).
+ *
+ *    Revision 1.75  2006/12/29 18:30:46  fabiankeil
+ *    Fixed gcc43 conversion warnings,
+ *    changed sprintf calls to snprintf.
+ *
+ *    Revision 1.74  2006/12/24 17:37:38  fabiankeil
+ *    Adjust comment in pcrs_filter_response()
+ *    to recent pcrs changes. Hohoho.
+ *
+ *    Revision 1.73  2006/12/23 16:01:02  fabiankeil
+ *    Don't crash if pcre returns an error code
+ *    that pcrs didn't expect. Fixes BR 1621173.
+ *
  *    Revision 1.72  2006/12/22 18:52:53  fabiankeil
  *    Modified is_untrusted_url to complain in case of
  *    write errors and to give a reason when adding new
@@ -616,20 +668,33 @@ int block_acl(struct access_control_addr *dst, struct client_state *csp)
  * Returns     :  0 => Ok, everything else is an error.
  *
  *********************************************************************/
-int acl_addr(char *aspec, struct access_control_addr *aca)
+int acl_addr(const char *aspec, struct access_control_addr *aca)
 {
-   int i, masklength, port;
+   int i, masklength;
+   long port;
    char *p;
+   char *acl_spec = NULL;
 
    masklength = 32;
    port       =  0;
 
-   if ((p = strchr(aspec, '/')) != NULL)
+   /*
+    * Use a temporary acl spec copy so we can log
+    * the unmodified original in case of parse errors.
+    */
+   acl_spec = strdup(aspec);
+   if (acl_spec == NULL)
    {
-      *p++ = '\0';
+      /* XXX: This will be logged as parse error. */
+      return(-1);
+   }
 
+   if ((p = strchr(acl_spec, '/')) != NULL)
+   {
+      *p++ = '\0';
       if (ijb_isdigit(*p) == 0)
       {
+         free(acl_spec);
          return(-1);
       }
       masklength = atoi(p);
@@ -637,26 +702,32 @@ int acl_addr(char *aspec, struct access_control_addr *aca)
 
    if ((masklength < 0) || (masklength > 32))
    {
+      free(acl_spec);
       return(-1);
    }
 
-   if ((p = strchr(aspec, ':')) != NULL)
+   if ((p = strchr(acl_spec, ':')) != NULL)
    {
+      char *endptr;
+
       *p++ = '\0';
+      port = strtol(p, &endptr, 10);
 
-      if (ijb_isdigit(*p) == 0)
+      if (port <= 0 || port > 65535 || *endptr != '\0')
       {
+         free(acl_spec);
          return(-1);
       }
-      port = atoi(p);
    }
 
-   aca->port = port;
+   aca->port = (unsigned long)port;
 
-   aca->addr = ntohl(resolve_hostname_to_ip(aspec));
+   aca->addr = ntohl(resolve_hostname_to_ip(acl_spec));
+   free(acl_spec);
 
    if (aca->addr == INADDR_NONE)
    {
+      /* XXX: This will be logged as parse error. */
       return(-1);
    }
 
@@ -926,7 +997,6 @@ struct http_response *block_url(struct client_state *csp)
             return cgi_error_memory();
          }
       }
-
    }
    else
 #endif /* def FEATURE_IMAGE_BLOCKING */
@@ -955,7 +1025,7 @@ struct http_response *block_url(struct client_state *csp)
       }
       else
       {
-         rsp->status = strdup("404 Request for blocked URL");
+         rsp->status = strdup("403 Request for blocked URL");
       }
 
       if (rsp->status == NULL)
@@ -973,7 +1043,15 @@ struct http_response *block_url(struct client_state *csp)
 
 #ifdef FEATURE_FORCE_LOAD
       err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
-      if (csp->http->ssl != 0)
+      /*
+       * Export the force conditional block killer if
+       *
+       * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
+       * - Privoxy is configured to enforce blocks, or
+       * - it's a CONNECT request and enforcing wouldn't work anyway.
+       */
+      if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
+       || (0 == strcmpic(csp->http->gpc, "connect")))
 #endif /* ndef FEATURE_FORCE_LOAD */
       {
          err = map_block_killer(exports, "force-support");
@@ -998,8 +1076,9 @@ struct http_response *block_url(struct client_state *csp)
          return cgi_error_memory();
       }
    }
+   rsp->reason = RSP_REASON_BLOCKED;
 
-   return finish_http_response(rsp);
+   return finish_http_response(csp, rsp);
 
 }
 
@@ -1081,7 +1160,7 @@ struct http_response *trust_url(struct client_state *csp)
    p = strdup("");
    for (tl = csp->config->trust_list; (t = *tl) != NULL ; tl++)
    {
-      sprintf(buf, "<li>%s</li>\n", t->spec);
+      snprintf(buf, sizeof(buf), "<li>%s</li>\n", t->spec);
       string_append(&p, buf);
    }
    err = map(exports, "trusted-referrers", 1, p, 0);
@@ -1103,7 +1182,7 @@ struct http_response *trust_url(struct client_state *csp)
       p = strdup("");
       for (l = csp->config->trust_info->first; l ; l = l->next)
       {
-         sprintf(buf, "<li> <a href=\"%s\">%s</a><br>\n",l->str, l->str);
+         snprintf(buf, sizeof(buf), "<li> <a href=\"%s\">%s</a><br>\n", l->str, l->str);
          string_append(&p, buf);
       }
       err = map(exports, "trust-info", 1, p, 0);
@@ -1121,10 +1200,22 @@ struct http_response *trust_url(struct client_state *csp)
    }
 
    /*
-    * Export the force prefix or the force conditional block killer
+    * Export the force conditional block killer if
+    *
+    * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
+    * - Privoxy is configured to enforce blocks, or
+    * - it's a CONNECT request and enforcing wouldn't work anyway.
     */
 #ifdef FEATURE_FORCE_LOAD
-   err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
+   if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
+    || (0 == strcmpic(csp->http->gpc, "connect")))
+   {
+      err = map_block_killer(exports, "force-support");
+   }
+   else
+   {
+      err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
+   }
 #else /* ifndef FEATURE_FORCE_LOAD */
    err = map_block_killer(exports, "force-support");
 #endif /* ndef FEATURE_FORCE_LOAD */
@@ -1145,8 +1236,9 @@ struct http_response *trust_url(struct client_state *csp)
       free_http_response(rsp);
       return cgi_error_memory();
    }
+   rsp->reason = RSP_REASON_UNTRUSTED;
 
-   return finish_http_response(rsp);
+   return finish_http_response(csp, rsp);
 }
 #endif /* def FEATURE_TRUST */
 
@@ -1414,7 +1506,7 @@ struct http_response *redirect_url(struct client_state *csp)
        * If it exists, use the previously rewritten URL as input
        * otherwise just use the old path.
        */
-      old_url = new_url ? new_url : strdup(csp->http->path);
+      old_url = (new_url != NULL) ? new_url : strdup(csp->http->path);
       new_url = get_last_url(old_url, redirect_mode);
       freez(old_url);
    }
@@ -1447,8 +1539,10 @@ struct http_response *redirect_url(struct client_state *csp)
             free_http_response(rsp);
             return cgi_error_memory();
          }
+         rsp->reason = RSP_REASON_REDIRECTED;
          freez(new_url);
-         return finish_http_response(rsp);
+
+         return finish_http_response(csp, rsp);
       }
    }
 
@@ -1476,7 +1570,7 @@ struct http_response *redirect_url(struct client_state *csp)
  *                otherwise
  *
  *********************************************************************/
-int is_imageurl(struct client_state *csp)
+int is_imageurl(const struct client_state *csp)
 {
 #ifdef FEATURE_IMAGE_DETECT_MSIE
    char *tmp;
@@ -1523,7 +1617,7 @@ int is_imageurl(struct client_state *csp)
  * Returns     :  0 => trusted, 1 => untrusted
  *
  *********************************************************************/
-int is_untrusted_url(struct client_state *csp)
+int is_untrusted_url(const struct client_state *csp)
 {
    struct file_list *fl;
    struct block_spec *b;
@@ -1637,6 +1731,7 @@ int is_untrusted_url(struct client_state *csp)
          return 0;
       }
    }
+
    return 1;
 }
 #endif /* def FEATURE_TRUST */
@@ -1652,6 +1747,11 @@ int is_untrusted_url(struct client_state *csp)
  *                csp->content_length to the modified size and raise the
  *                CSP_FLAG_MODIFIED flag.
  *
+ *                XXX: Currently pcrs_filter_response is also responsible
+ *                for dechunking and decompressing. Both should be
+ *                done in separate functions so other content modifiers
+ *                profit as well, even if pcrs filtering is disabled.
+ *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
  *
@@ -1680,7 +1780,7 @@ char *pcrs_filter_response(struct client_state *csp)
    {
       return(NULL);
    }
-   size = csp->iob->eod - csp->iob->cur;
+   size = (size_t)(csp->iob->eod - csp->iob->cur);
 
    /*
     * Need to check the set of re_filterfiles...
@@ -1701,7 +1801,7 @@ char *pcrs_filter_response(struct client_state *csp)
    if (0 == found_filters)
    {
       log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
-         return(NULL);
+      return(NULL);
    }
 
    /*
@@ -1719,6 +1819,45 @@ char *pcrs_filter_response(struct client_state *csp)
       csp->flags |= CSP_FLAG_MODIFIED;
    }
 
+#ifdef FEATURE_ZLIB
+   /*
+    * If the body has a compressed transfer-encoding,
+    * uncompress it first, adjusting size and iob->eod.
+    * Note that decompression occurs after de-chunking.
+    */
+   if (csp->content_type & (CT_GZIP | CT_DEFLATE))
+   {
+      /* Notice that we at least tried to decompress. */
+      if (JB_ERR_OK != decompress_iob(csp))
+      {
+         /*
+          * We failed to decompress the data; there's no point
+          * in continuing since we can't filter.
+          *
+          * XXX: Actually the Accept-Encoding header may
+          * just be incorrect in which case we could continue
+          * with filtering.
+          *
+          * Unset CT_GZIP and CT_DEFLATE to remember not
+          * to modify the Content-Encoding header later.
+          */
+          csp->content_type &= ~CT_GZIP;
+          csp->content_type &= ~CT_DEFLATE;
+          return(NULL);
+      }
+
+      /*
+       * Decompression gives us a completely new iob,
+       * so we need to update.
+       */
+      size = (size_t)(csp->iob->eod - csp->iob->cur);
+      old  = csp->iob->cur;
+
+      csp->flags |= CSP_FLAG_MODIFIED;
+      csp->content_type &= ~CT_TABOO;
+   }
+#endif
+
    for (i = 0; i < MAX_AF_FILES; i++)
    {
      fl = csp->rlist[i];
@@ -1742,6 +1881,12 @@ char *pcrs_filter_response(struct client_state *csp)
     */
    for (b = fl->f; b; b = b->next)
    {
+      if (b->type != FT_CONTENT_FILTER)
+      {
+         /* Skip header filters */
+         continue;
+      }
+
       for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first;
            filtername ; filtername = filtername->next)
       {
@@ -1781,29 +1926,29 @@ char *pcrs_filter_response(struct client_state *csp)
                else
                {
                   /*
-                   * The job caused an unexpected error. Inform the user
-                   * and skip the rest of jobs in this filter. We could
+                   * 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 with the same reason.
+                   * fail for the same reason.
                    *
-                   * XXX: In theory pcrs_strerror() would
-                   * return a proper error message here.
+                   * 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.
                    *
-                   * At the moment, however, our pcrs expects the
-                   * error codes of pcre 3.4 and newer pcre version
-                   * return different error codes. As a result
-                   * pcrs_strerror()'s error message might be bogus,
-                   * 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);
+                     b->name, job_number, pcrs_strerror(job_hits), job_hits);
                   break;
                }
             }
 
             log_error(LOG_LEVEL_RE_FILTER,
-               "re_filtering %s%s (size %d) with filter %s produced %d hits (new size %d).",
+               "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;
@@ -1850,7 +1995,9 @@ char *gif_deanimate_response(struct client_state *csp)
 {
    struct binbuffer *in, *out;
    char *p;
-   size_t size = csp->iob->eod - csp->iob->cur;
+   size_t size;
+
+   size = (size_t)(csp->iob->eod - csp->iob->cur);
 
    /*
     * If the body has a "chunked" transfer-encoding,
@@ -1920,9 +2067,12 @@ char *gif_deanimate_response(struct client_state *csp)
  *********************************************************************/
 char *jpeg_inspect_response(struct client_state *csp)
 {
-   struct binbuffer *in = NULL, *out = NULL;
+   struct binbuffer  *in = NULL;
+   struct binbuffer *out = NULL;
    char *p = NULL;
-   size_t size = csp->iob->eod - csp->iob->cur;
+   size_t size;
+
+   size = (size_t)(csp->iob->eod - csp->iob->cur);
 
    /*
     * If the body has a "chunked" transfer-encoding,
@@ -1995,7 +2145,7 @@ char *jpeg_inspect_response(struct client_state *csp)
  *                went wrong
  *
  *********************************************************************/
-int remove_chunked_transfer_coding(char *buffer, const size_t size)
+size_t remove_chunked_transfer_coding(char *buffer, const size_t size)
 {
    size_t newsize = 0;
    unsigned int chunksize = 0;
@@ -2177,29 +2327,42 @@ struct http_response *direct_response(struct client_state *csp)
    {
       for (p = csp->headers->first; (p != NULL) ; p = p->next)
       {
-         if (!strncmp("Max-Forwards:", p->str, 13)
-             && (*(p->str+13) != '\0') && (atoi(p->str+13) == 0))
+         if (!strncmpic("Max-Forwards:", p->str, 13))
          {
-            /* FIXME: We could handle at least TRACE here,
-               but that would require a verbatim copy of
-               the request which we don't have anymore */
-
-            log_error(LOG_LEVEL_HEADER, "Found Max-Forwards:0 in OPTIONS or TRACE request -- Returning 501");
+            unsigned int max_forwards;
 
-            /* Get mem for response or fail*/
-            if (NULL == (rsp = alloc_http_response()))
+            /*
+             * If it's a Max-Forwards value of zero,
+             * we have to intercept the request.
+             */
+            if (1 == sscanf(p->str+12, ": %u", &max_forwards) && max_forwards == 0)
             {
-               return cgi_error_memory();
-            }
+               /*
+                * FIXME: We could handle at least TRACE here,
+                * but that would require a verbatim copy of
+                * the request which we don't have anymore
+                */
+                log_error(LOG_LEVEL_HEADER,
+                  "Detected header \'%s\' in OPTIONS or TRACE request. Returning 501.",
+                  p->str);
+
+               /* Get mem for response or fail*/
+               if (NULL == (rsp = alloc_http_response()))
+               {
+                  return cgi_error_memory();
+               }
             
-            if (NULL == (rsp->status = strdup("501 Not Implemented")))
-            {
-               free_http_response(rsp);
-               return cgi_error_memory();
-            }
+               if (NULL == (rsp->status = strdup("501 Not Implemented")))
+               {
+                  free_http_response(rsp);
+                  return cgi_error_memory();
+               }
+
+               rsp->is_static = 1;
+               rsp->reason = RSP_REASON_UNSUPPORTED;
 
-            rsp->is_static = 1;
-            return(finish_http_response(rsp));
+               return(finish_http_response(csp, rsp));
+            }
          }
       }
    }