Mark *csp as immutable for is_untrusted_url()
[privoxy.git] / filters.c
index 831fcb0..36eccf4 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1,4 +1,4 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.70 2006/12/09 13:33:15 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.76 2007/01/01 19:36:37 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
@@ -40,6 +40,32 @@ const char filters_rcs[] = "$Id: filters.c,v 1.70 2006/12/09 13:33:15 fabiankeil
  *
  * Revisions   :
  *    $Log: filters.c,v $
+ *    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
+ *    entries to the trustfile. Closes FR 1097611.
+ *
+ *    Revision 1.71  2006/12/22 14:24:52  fabiankeil
+ *    Skip empty filter files in pcrs_filter_response,
+ *    but don't ignore the ones that come afterwards.
+ *    Fixes parts of BR 1619208.
+ *
  *    Revision 1.70  2006/12/09 13:33:15  fabiankeil
  *    Added some sanity checks for get_last_url().
  *    Fixed possible segfault caused by my last commit.
@@ -916,7 +942,6 @@ struct http_response *block_url(struct client_state *csp)
             return cgi_error_memory();
          }
       }
-
    }
    else
 #endif /* def FEATURE_IMAGE_BLOCKING */
@@ -1071,7 +1096,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);
@@ -1093,7 +1118,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);
@@ -1404,7 +1429,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);
    }
@@ -1466,7 +1491,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;
@@ -1513,7 +1538,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;
@@ -1565,7 +1590,7 @@ int is_untrusted_url(struct client_state *csp)
       {
          /* if the URL's referrer is from a trusted referrer, then
           * add the target spec to the trustfile as an unblocked
-          * domain and return NULL (which means it's OK).
+          * domain and return 0 (which means it's OK).
           */
 
          FILE *fp;
@@ -1596,9 +1621,19 @@ int is_untrusted_url(struct client_state *csp)
                string_join(&new_entry, path);
             }
 
+            /*
+             * Give a reason for generating this entry.
+             */
+            string_append(&new_entry, " # Trusted referrer was: ");
+            string_append(&new_entry, referer);
+
             if (new_entry != NULL)
             {
-               fprintf(fp, "%s\n", new_entry);
+               if (-1 == fprintf(fp, "%s\n", new_entry))
+               {
+                  log_error(LOG_LEVEL_ERROR, "Failed to append \'%s\' to trustfile \'%s\': %E",
+                     new_entry, csp->config->trustfile);
+               }
                free(new_entry);
             }
             else
@@ -1609,6 +1644,11 @@ int is_untrusted_url(struct client_state *csp)
 
             fclose(fp);
          }
+         else
+         {
+            log_error(LOG_LEVEL_ERROR, "Failed to append new entry for \'%s\' to trustfile \'%s\': %E",
+               csp->http->hostport, csp->config->trustfile);
+         }
          return 0;
       }
    }
@@ -1627,6 +1667,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...)
  *
@@ -1655,7 +1700,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...
@@ -1676,7 +1721,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);
    }
 
    /*
@@ -1694,6 +1739,41 @@ 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 || csp->content_type & 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. This is
+          * slightly tricky because we need to remember not to
+          * modify the Content-Encoding header later; using
+          * CT_TABOO flag is a kludge for this purpose.
+          */
+          csp->content_type |= CT_TABOO;
+          return(NULL);
+      }
+      log_error(LOG_LEVEL_RE_FILTER, "Decompressing successful");
+
+      /*
+       * 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];
@@ -1722,7 +1802,9 @@ char *pcrs_filter_response(struct client_state *csp)
       {
          if (strcmp(b->name, filtername->str) == 0)
          {
-            int current_hits = 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 */
 
             if ( NULL == b->joblist )
             {
@@ -1734,13 +1816,50 @@ char *pcrs_filter_response(struct client_state *csp)
             /* Apply all jobs from the joblist */
             for (job = b->joblist; NULL != job; job = job->next)
             {
-               current_hits += pcrs_execute(job, old, size, &new, &size);
-               if (old != csp->iob->cur) free(old);
-               old=new;
+               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)
+                  {
+                     free(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;
+               }
             }
 
-            log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) with filter %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,
+               "re_filtering %s%s (size %d) with filter %s produced %d hits (new size %d).",
+               csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
 
             hits += current_hits;
          }
@@ -1786,7 +1905,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,
@@ -1856,9 +1977,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,
@@ -1931,7 +2055,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;