Add unload_forward_spec() in preparation for forward-override{}.
[privoxy.git] / filters.c
index 1c9b254..f6665bf 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1,4 +1,4 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.81 2007/03/05 14:40:53 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.86 2007/04/30 15:03:28 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
@@ -40,6 +40,28 @@ const char filters_rcs[] = "$Id: filters.c,v 1.81 2007/03/05 14:40:53 fabiankeil
  *
  * Revisions   :
  *    $Log: filters.c,v $
+ *    Revision 1.86  2007/04/30 15:03:28  fabiankeil
+ *    - Introduce dynamic pcrs jobs that can resolve variables.
+ *    - Don't run redirect functions more than once,
+ *      unless they are activated more than once.
+ *
+ *    Revision 1.85  2007/03/21 12:24:47  fabiankeil
+ *    - Log the content size after decompression in decompress_iob()
+ *      instead of pcrs_filter_response().
+ *
+ *    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
@@ -1030,7 +1052,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 || 0 == strcmpic(csp->http->gpc, "connect"))
+      /*
+       * 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");
@@ -1179,12 +1209,17 @@ 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
-   if (0 == strcmpic(csp->http->gpc, "connect"))
+   if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
+    || (0 == strcmpic(csp->http->gpc, "connect")))
    {
-       err = map_block_killer(exports, "force-support");
+      err = map_block_killer(exports, "force-support");
    }
    else
    {
@@ -1216,58 +1251,76 @@ struct http_response *trust_url(struct client_state *csp)
 }
 #endif /* def FEATURE_TRUST */
 
+
 /*********************************************************************
  *
- * Function    :  execute_single_pcrs_command
+ * Function    :  compile_dynamic_pcrs_job_list
  *
- * Description :  Apply single pcrs command to the subject.
- *                The subject itself is left untouched, memory for the result
- *                is malloc()ed and it is the caller's responsibility to free
- *                the result when it's no longer needed.
+ * Description :  Compiles a dynamic pcrs job list (one with variables
+ *                resolved at request time)
  *
  * Parameters  :
- *          1  :  subject = the subject (== original) string
- *          2  :  pcrs_command = the pcrs command as string (s@foo@bar@) 
- *          3  :  hits = int* for returning  the number of modifications 
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  b = The filter list to compile
  *
  * Returns     :  NULL in case of errors, otherwise the
- *                result of the pcrs command.  
+ *                pcrs job list.  
  *
  *********************************************************************/
-char *execute_single_pcrs_command(char *subject, const char *pcrs_command, int *hits)
+pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const struct re_filterfile_spec *b)
 {
-   int error;
-   size_t size;
-   char *result = NULL;
-   pcrs_job *job;
+   struct list_entry *pattern;
+   pcrs_job *job_list = NULL;
+   pcrs_job *dummy = NULL;
+   pcrs_job *lastjob = NULL;
+   int error = 0;
 
-   assert(subject);
-   assert(pcrs_command);
-
-   *hits = 0;
-   size = strlen(subject);
-
-   if (NULL == (job = pcrs_compile_command(pcrs_command, &error)))
-   {
-      log_error(LOG_LEVEL_ERROR, "Failed to compile pcrs command \"%s\". Error: %d.",
-         pcrs_command, error);
-   }
-   else if ((*hits = pcrs_execute(job, subject, size, &result, &size)) < 0)
+   const struct pcrs_variable variables[] =
    {
-      log_error(LOG_LEVEL_ERROR, "Failed to execute pcrs command: %s", pcrs_strerror(*hits));
-      *hits = 0;
-      freez(result);
-   }
+      {"url",    csp->http->url,   1},
+      {"path",   csp->http->path,  1},
+      {"host",   csp->http->host,  1},
+      {"origin", csp->ip_addr_str, 1},
+      {NULL,     NULL,             1}
+   };
 
-   if (job)
+   for (pattern = b->patterns->first; pattern != NULL; pattern = pattern->next)
    {
-      job = pcrs_free_job(job);
-   }
+      assert(pattern->str != NULL);
 
-   return result;
+      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));
+         continue;
+      }
+      else
+      {
+         if (error == PCRS_WARN_TRUNCATION)
+         {
+            log_error(LOG_LEVEL_ERROR,
+               "At least one of the variables in \'%s\' had to "
+               "be truncated before compilation", pattern->str);
+         }
+         if (job_list == NULL)
+         {
+            job_list = dummy;
+         }
+         else
+         {
+            lastjob->next = dummy;
+         }
+         lastjob = dummy;
+      }
+   }
 
+   return job_list;
 }
 
+
 /*********************************************************************
  *
  * Function    :  rewrite_url
@@ -1293,13 +1346,20 @@ char *rewrite_url(char *old_url, const char *pcrs_command)
    assert(old_url);
    assert(pcrs_command);
 
-   new_url = execute_single_pcrs_command(old_url, pcrs_command, &hits);
+   new_url = pcrs_execute_single_command(old_url, pcrs_command, &hits);
 
    if (hits == 0)
    {
       log_error(LOG_LEVEL_REDIRECTS,
          "pcrs command \"%s\" didn't change \"%s\".",
-         pcrs_command, old_url, new_url);
+         pcrs_command, old_url);
+      freez(new_url);
+   }
+   else if (hits < 0)
+   {
+      log_error(LOG_LEVEL_REDIRECTS,
+         "executing pcrs command \"%s\" to rewrite %s failed: %s",
+         pcrs_command, old_url, pcrs_strerror(hits));
       freez(new_url);
    }
    else if (strncmpic(new_url, "http://", 7) && strncmpic(new_url, "https://", 8))
@@ -1484,7 +1544,19 @@ struct http_response *redirect_url(struct client_state *csp)
       new_url = get_last_url(old_url, redirect_mode);
       freez(old_url);
    }
+
+   /*
+    * Disable redirect checkers, so that they
+    * will be only run more than once if the user
+    * also enables them through tags.
+    *
+    * From a performance point of view
+    * it doesn't matter, but the duplicated
+    * log messages are annoying.
+    */
+   csp->action->flags &= ~ACTION_FAST_REDIRECTS;
 #endif /* def FEATURE_FAST_REDIRECTS */
+   csp->action->flags &= ~ACTION_REDIRECT;
 
    /* Did any redirect action trigger? */   
    if (new_url)
@@ -1819,9 +1891,6 @@ char *pcrs_filter_response(struct client_state *csp)
           csp->content_type &= ~CT_DEFLATE;
           return(NULL);
       }
-      log_error(LOG_LEVEL_RE_FILTER,
-         "Decompression successful. Old size: %d, new size: %d.",
-         size, csp->iob->eod - csp->iob->cur);
 
       /*
        * Decompression gives us a completely new iob,
@@ -1858,6 +1927,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)
       {
@@ -1866,8 +1941,11 @@ char *pcrs_filter_response(struct client_state *csp)
             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 */
+            pcrs_job *joblist = b->joblist;
 
-            if ( NULL == b->joblist )
+            if (b->dynamic) joblist = compile_dynamic_pcrs_job_list(csp, b);
+
+            if (NULL == joblist)
             {
                log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name);
                continue;
@@ -1875,7 +1953,7 @@ char *pcrs_filter_response(struct client_state *csp)
 
             prev_size = size;
             /* Apply all jobs from the joblist */
-            for (job = b->joblist; NULL != job; job = job->next)
+            for (job = joblist; NULL != job; job = job->next)
             {
                job_number++;
                job_hits = pcrs_execute(job, old, size, &new, &size);
@@ -1918,6 +1996,8 @@ char *pcrs_filter_response(struct client_state *csp)
                }
             }
 
+            if (b->dynamic) pcrs_free_joblist(joblist);
+
             log_error(LOG_LEVEL_RE_FILTER,
                "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);