Bringing back the "useless" localtime() call
[privoxy.git] / filters.c
index edead9d..f29803b 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1,4 +1,4 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.68 2006/12/05 14:45:48 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.73 2006/12/23 16:01:02 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
@@ -40,6 +40,27 @@ const char filters_rcs[] = "$Id: filters.c,v 1.68 2006/12/05 14:45:48 fabiankeil
  *
  * Revisions   :
  *    $Log: filters.c,v $
+ *    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.
+ *
+ *    Revision 1.69  2006/12/08 12:39:13  fabiankeil
+ *    Let get_last_url() catch https URLs as well.
+ *
  *    Revision 1.68  2006/12/05 14:45:48  fabiankeil
  *    Make sure get_last_url() behaves like advertised
  *    and fast-redirects{} can be combined with redirect{}.
@@ -1258,20 +1279,32 @@ char *rewrite_url(char *old_url, const char *pcrs_command)
  *********************************************************************/
 char *get_last_url(char *subject, const char *redirect_mode)
 {
-   char *new_url;
+   char *new_url = NULL;
    char *tmp;
 
    assert(subject);
    assert(redirect_mode);
 
    subject = strdup(subject);
+   if (subject == NULL)
+   {
+      log_error(LOG_LEVEL_ERROR, "Out of memory while searching for redirects.");
+      return NULL;
+   }
 
    if (0 == strcmpic(redirect_mode, "check-decoded-url"))
    {  
       log_error(LOG_LEVEL_REDIRECTS, "Decoding \"%s\" if necessary.", subject);
       new_url = url_decode(subject);
-      freez(subject);
-      subject = new_url;
+      if (new_url != NULL)
+      {
+         freez(subject);
+         subject = new_url;
+      }
+      else
+      {
+         log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", subject);
+      }
    }
 
    log_error(LOG_LEVEL_REDIRECTS, "Checking \"%s\" for redirects.", subject);
@@ -1284,15 +1317,17 @@ char *get_last_url(char *subject, const char *redirect_mode)
    {
       new_url = tmp++;
    }
-   tmp = new_url;
+   tmp = (new_url != NULL) ? new_url : subject;
    while ((tmp = strstr(tmp, "https://")) != NULL)
    {
       new_url = tmp++;
    }
 
-   if ((new_url != subject)
-      || (0 == strncmpic(subject, "http://", 7))
-      || (0 == strncmpic(subject, "https://", 8)))
+   if ((new_url != NULL)
+      && (  (new_url != subject)
+         || (0 == strncmpic(subject, "http://", 7))
+         || (0 == strncmpic(subject, "https://", 8))
+         ))
    {
       /*
        * Return new URL if we found a redirect 
@@ -1544,7 +1579,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;
@@ -1575,9 +1610,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
@@ -1588,6 +1633,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;
       }
    }
@@ -1677,7 +1727,18 @@ char *pcrs_filter_response(struct client_state *csp)
    {
      fl = csp->rlist[i];
      if ((NULL == fl) || (NULL == fl->f))
-       break;
+     {
+        /*
+         * 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
@@ -1690,7 +1751,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 )
             {
@@ -1702,13 +1765,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;
          }