Incorporate Fabian Keil's patch work:\rhttp://www.fabiankeil.de/sourcecode/privoxy/
authorDavid Schmidt <david__schmidt@users.sourceforge.net>
Thu, 3 Aug 2006 02:46:42 +0000 (02:46 +0000)
committerDavid Schmidt <david__schmidt@users.sourceforge.net>
Thu, 3 Aug 2006 02:46:42 +0000 (02:46 +0000)
13 files changed:
actionlist.h
cgi.c
config
default.action.master
errlog.c
filters.c
jbsockets.c
jcc.c
parsers.c
parsers.h
project.h
standard.action
templates/edit-actions-for-url

index 1e06364..d8dddd6 100644 (file)
@@ -1,6 +1,6 @@
 /*********************************************************************
  *
 /*********************************************************************
  *
- * File        :  $Source: /cvsroot/ijbswa/current/Attic/actionlist.h,v $
+ * File        :  $Source: /cvsroot/ijbswa/current/actionlist.h,v $
  *
  * Purpose     :  Master list of supported actions.
  *                Not really a header, since it generates code.
  *
  * Purpose     :  Master list of supported actions.
  *                Not really a header, since it generates code.
  *
  * Revisions   :
  *    $Log: actionlist.h,v $
  *
  * Revisions   :
  *    $Log: actionlist.h,v $
+ *    Revision 1.19  2006/07/18 14:48:45  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
  *    Revision 1.17.2.3  2004/10/03 12:53:32  david__schmidt
  *    Add the ability to check jpeg images for invalid
  *    lengths of comment blocks.  Defensive strategy
  *    Revision 1.17.2.3  2004/10/03 12:53:32  david__schmidt
  *    Add the ability to check jpeg images for invalid
  *    lengths of comment blocks.  Defensive strategy
@@ -126,14 +130,43 @@ DEFINE_ACTION_STRING     ("deanimate-gifs",             ACTION_DEANIMATE,
 DEFINE_CGI_PARAM_RADIO   ("deanimate-gifs",             ACTION_DEANIMATE,       ACTION_STRING_DEANIMATE,     "first", 0)
 DEFINE_CGI_PARAM_RADIO   ("deanimate-gifs",             ACTION_DEANIMATE,       ACTION_STRING_DEANIMATE,     "last",  1)
 DEFINE_ACTION_BOOL       ("downgrade-http-version",     ACTION_DOWNGRADE)
 DEFINE_CGI_PARAM_RADIO   ("deanimate-gifs",             ACTION_DEANIMATE,       ACTION_STRING_DEANIMATE,     "first", 0)
 DEFINE_CGI_PARAM_RADIO   ("deanimate-gifs",             ACTION_DEANIMATE,       ACTION_STRING_DEANIMATE,     "last",  1)
 DEFINE_ACTION_BOOL       ("downgrade-http-version",     ACTION_DOWNGRADE)
-DEFINE_ACTION_BOOL       ("fast-redirects",             ACTION_FAST_REDIRECTS)
+DEFINE_ACTION_STRING     ("fast-redirects",             ACTION_FAST_REDIRECTS,  ACTION_STRING_FAST_REDIRECTS)
+DEFINE_CGI_PARAM_RADIO   ("fast-redirects",             ACTION_FAST_REDIRECTS,  ACTION_STRING_FAST_REDIRECTS, "simple-check",  0)
+DEFINE_CGI_PARAM_RADIO   ("fast-redirects",             ACTION_FAST_REDIRECTS,  ACTION_STRING_FAST_REDIRECTS, "check-decoded-url",  1)
 DEFINE_ACTION_MULTI      ("filter",                     ACTION_MULTI_FILTER)
 DEFINE_ACTION_BOOL       ("handle-as-image",            ACTION_IMAGE)
 DEFINE_ACTION_BOOL       ("hide-forwarded-for-headers", ACTION_HIDE_FORWARDED)
 DEFINE_ACTION_STRING     ("hide-from-header",           ACTION_HIDE_FROM,       ACTION_STRING_FROM)
 DEFINE_CGI_PARAM_RADIO   ("hide-from-header",           ACTION_HIDE_FROM,       ACTION_STRING_FROM,          "block", 1)
 DEFINE_CGI_PARAM_CUSTOM  ("hide-from-header",           ACTION_HIDE_FROM,       ACTION_STRING_FROM,          "spam_me_senseless@sittingduck.xyz")
 DEFINE_ACTION_MULTI      ("filter",                     ACTION_MULTI_FILTER)
 DEFINE_ACTION_BOOL       ("handle-as-image",            ACTION_IMAGE)
 DEFINE_ACTION_BOOL       ("hide-forwarded-for-headers", ACTION_HIDE_FORWARDED)
 DEFINE_ACTION_STRING     ("hide-from-header",           ACTION_HIDE_FROM,       ACTION_STRING_FROM)
 DEFINE_CGI_PARAM_RADIO   ("hide-from-header",           ACTION_HIDE_FROM,       ACTION_STRING_FROM,          "block", 1)
 DEFINE_CGI_PARAM_CUSTOM  ("hide-from-header",           ACTION_HIDE_FROM,       ACTION_STRING_FROM,          "spam_me_senseless@sittingduck.xyz")
+DEFINE_ACTION_BOOL       ("filter-headers",             ACTION_FILTER_HEADERS)
+DEFINE_ACTION_STRING     ("crunch-server-header",       ACTION_CRUNCH_SERVER_HEADER, ACTION_STRING_SERVER_HEADER)
+DEFINE_CGI_PARAM_NO_RADIO("crunch-server-header",       ACTION_CRUNCH_SERVER_HEADER, ACTION_STRING_SERVER_HEADER,          "X-Whatever:")
+DEFINE_ACTION_STRING     ("crunch-client-header",       ACTION_CRUNCH_CLIENT_HEADER, ACTION_STRING_CLIENT_HEADER)
+DEFINE_CGI_PARAM_NO_RADIO("crunch-client-header",       ACTION_CRUNCH_CLIENT_HEADER, ACTION_STRING_CLIENT_HEADER,          "X-Whatever:")
+DEFINE_ACTION_STRING     ("hide-accept-language",       ACTION_HIDE_ACCEPT_LANGUAGE, ACTION_STRING_LANGUAGE)
+DEFINE_CGI_PARAM_RADIO   ("hide-accept-language",       ACTION_HIDE_ACCEPT_LANGUAGE, ACTION_STRING_LANGUAGE, "block", 0)
+DEFINE_CGI_PARAM_CUSTOM  ("hide-accept-language",       ACTION_HIDE_ACCEPT_LANGUAGE, ACTION_STRING_LANGUAGE, "de-de")
+DEFINE_ACTION_STRING     ("content-type-overwrite",     ACTION_CONTENT_TYPE_OVERWRITE, ACTION_STRING_CONTENT_TYPE)
+DEFINE_CGI_PARAM_NO_RADIO("content-type-overwrite",     ACTION_CONTENT_TYPE_OVERWRITE, ACTION_STRING_CONTENT_TYPE,    "text/html")
+DEFINE_ACTION_STRING     ("hide-content-disposition",   ACTION_HIDE_CONTENT_DISPOSITION, ACTION_STRING_CONTENT_DISPOSITION)
+DEFINE_CGI_PARAM_RADIO   ("hide-content-disposition",   ACTION_HIDE_CONTENT_DISPOSITION, ACTION_STRING_CONTENT_DISPOSITION,    "block", 0)
+DEFINE_CGI_PARAM_CUSTOM  ("hide-content-disposition",   ACTION_HIDE_CONTENT_DISPOSITION, ACTION_STRING_CONTENT_DISPOSITION,    "attachment; filename=WHATEVER.txt")
+DEFINE_ACTION_STRING     ("hide-if-modified-since",     ACTION_HIDE_IF_MODIFIED_SINCE, ACTION_STRING_IF_MODIFIED_SINCE)
+DEFINE_CGI_PARAM_RADIO   ("hide-if-modified-since",     ACTION_HIDE_IF_MODIFIED_SINCE, ACTION_STRING_IF_MODIFIED_SINCE, "block", 0)
+DEFINE_CGI_PARAM_CUSTOM  ("hide-if-modified-since",     ACTION_HIDE_IF_MODIFIED_SINCE, ACTION_STRING_IF_MODIFIED_SINCE, "-1")
+DEFINE_ACTION_STRING     ("overwrite-last-modified",    ACTION_OVERWRITE_LAST_MODIFIED, ACTION_STRING_LAST_MODIFIED)
+DEFINE_CGI_PARAM_RADIO   ("overwrite-last-modified",    ACTION_OVERWRITE_LAST_MODIFIED, ACTION_STRING_LAST_MODIFIED, "block", 0)
+DEFINE_CGI_PARAM_RADIO   ("overwrite-last-modified",    ACTION_OVERWRITE_LAST_MODIFIED, ACTION_STRING_LAST_MODIFIED, "reset-to-request-time", 1)
+DEFINE_CGI_PARAM_RADIO   ("overwrite-last-modified",    ACTION_OVERWRITE_LAST_MODIFIED, ACTION_STRING_LAST_MODIFIED, "randomize", 2)
+DEFINE_ACTION_BOOL       ("crunch-if-none-match",       ACTION_CRUNCH_IF_NONE_MATCH)
+DEFINE_ACTION_BOOL       ("force-text-mode",            ACTION_FORCE_TEXT_MODE)
+DEFINE_ACTION_BOOL       ("handle-as-empty-document",   ACTION_HANDLE_AS_EMPTY_DOCUMENT)
+DEFINE_ACTION_BOOL       ("treat-forbidden-connects-like-blocks",   ACTION_TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS)
+DEFINE_ACTION_STRING     ("redirect",                   ACTION_REDIRECT,        ACTION_STRING_REDIRECT)
+DEFINE_CGI_PARAM_NO_RADIO("redirect",                   ACTION_REDIRECT,        ACTION_STRING_REDIRECT,  "http://localhost/")
 DEFINE_ACTION_STRING     ("hide-referrer",              ACTION_HIDE_REFERER,    ACTION_STRING_REFERER)
 DEFINE_ACTION_STRING     ("hide-referrer",              ACTION_HIDE_REFERER,    ACTION_STRING_REFERER)
+DEFINE_CGI_PARAM_RADIO   ("hide-referrer",              ACTION_HIDE_REFERER,    ACTION_STRING_REFERER,       "conditional-block", 2)
 DEFINE_CGI_PARAM_RADIO   ("hide-referrer",              ACTION_HIDE_REFERER,    ACTION_STRING_REFERER,       "forge", 1)
 DEFINE_CGI_PARAM_RADIO   ("hide-referrer",              ACTION_HIDE_REFERER,    ACTION_STRING_REFERER,       "block", 0)
 DEFINE_CGI_PARAM_CUSTOM  ("hide-referrer",              ACTION_HIDE_REFERER,    ACTION_STRING_REFERER,       "http://www.google.com/")
 DEFINE_CGI_PARAM_RADIO   ("hide-referrer",              ACTION_HIDE_REFERER,    ACTION_STRING_REFERER,       "forge", 1)
 DEFINE_CGI_PARAM_RADIO   ("hide-referrer",              ACTION_HIDE_REFERER,    ACTION_STRING_REFERER,       "block", 0)
 DEFINE_CGI_PARAM_CUSTOM  ("hide-referrer",              ACTION_HIDE_REFERER,    ACTION_STRING_REFERER,       "http://www.google.com/")
diff --git a/cgi.c b/cgi.c
index 1db93f8..0c7c004 100644 (file)
--- a/cgi.c
+++ b/cgi.c
@@ -1,7 +1,7 @@
-const char cgi_rcs[] = "$Id: cgi.c,v 1.70.2.13 2004/02/17 13:30:23 oes Exp $";
+const char cgi_rcs[] = "$Id: cgi.c,v 1.72 2006/07/18 14:48:45 david__schmidt Exp $";
 /*********************************************************************
  *
 /*********************************************************************
  *
- * File        :  $Source: /cvsroot/ijbswa/current/Attic/cgi.c,v $
+ * File        :  $Source: /cvsroot/ijbswa/current/cgi.c,v $
  *
  * Purpose     :  Declares functions to intercept request, generate
  *                html or gif answers, and to compose HTTP resonses.
  *
  * Purpose     :  Declares functions to intercept request, generate
  *                html or gif answers, and to compose HTTP resonses.
@@ -38,6 +38,10 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.70.2.13 2004/02/17 13:30:23 oes Exp $";
  *
  * Revisions   :
  *    $Log: cgi.c,v $
  *
  * Revisions   :
  *    $Log: cgi.c,v $
+ *    Revision 1.72  2006/07/18 14:48:45  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
  *    Revision 1.70.2.13  2004/02/17 13:30:23  oes
  *    Moved cgi_error_disabled() from cgiedit.c to
  *    cgi.c to re-enable build with --disable-editor.
  *    Revision 1.70.2.13  2004/02/17 13:30:23  oes
  *    Moved cgi_error_disabled() from cgiedit.c to
  *    cgi.c to re-enable build with --disable-editor.
@@ -1763,16 +1767,31 @@ struct http_response *finish_http_response(struct http_response *rsp)
    else
    {
       /*
    else
    {
       /*
-       * Compliant browsers should not cache this due to the "Cache-Control"
-       * setting.  However, to be certain, we also set both "Last-Modified"
-       * and "Expires" to the current time.
+       * Setting "Cache-Control" to "no-cache" and  "Expires" to
+       * the current time doesn't exactly forbid caching, it just
+       * requires the client to revalidate the cached copy.
+       *
+       * If a temporary problem occurres and the user tries again after
+       * getting Privoxy's error message, a compliant browser may set the
+       * If-Modified-Since header with the content of the error page's
+       * Last-Modified header. More often than not, the document on the server
+       * is older than Privoxy's error message, the server would send status code
+       * 304 and the browser would display the outdated error message again and again.
+       *
+       * As a last resort we set "Last-Modified" to Tim Berners-Lee's birthday,
+       * which predates the age of any page on the web and can be safely used to
+       * "revalidate" without getting a status code 304.
+       *
+       * There is no need to let the useless If-Modified-Since header reach the
+       * server, it is therefore stripped by client_if_modified_since in parsers.c.
        */
       if (!err) err = enlist_unique_header(rsp->headers, "Cache-Control", "no-cache");
 
       get_http_time(0, buf);
       if (!err) err = enlist_unique_header(rsp->headers, "Date", buf);
        */
       if (!err) err = enlist_unique_header(rsp->headers, "Cache-Control", "no-cache");
 
       get_http_time(0, buf);
       if (!err) err = enlist_unique_header(rsp->headers, "Date", buf);
-      if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", buf);
+      if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", "Wed, 08 Jun 1955 12:00:00 GMT");
       if (!err) err = enlist_unique_header(rsp->headers, "Expires", "Sat, 17 Jun 2000 12:00:00 GMT");
       if (!err) err = enlist_unique_header(rsp->headers, "Expires", "Sat, 17 Jun 2000 12:00:00 GMT");
+      if (!err) err = enlist_unique_header(rsp->headers, "Pragma", "no-cache");
    }
 
 
    }
 
 
diff --git a/config b/config
index daee979..d2fa15d 100644 (file)
--- a/config
+++ b/config
@@ -2,7 +2,7 @@
 #  
 #  Copyright (C) 2001-2004 Privoxy Developers http://privoxy.org
 #  
 #  
 #  Copyright (C) 2001-2004 Privoxy Developers http://privoxy.org
 #  
-#  $Id: config,v 1.39.2.17 2006/01/31 11:52:32 david__schmidt Exp $
+#  $Id: config,v 1.47 2006/07/18 14:48:45 david__schmidt Exp $
 #  
 ####################################################################
 #                                                                  #
 #  
 ####################################################################
 #                                                                  #
@@ -660,7 +660,7 @@ debug   8192 # Errors - *we highly recommended enabling this*
 #  
 #        listen-address  192.168.0.1:8118
 #  
 #  
 #        listen-address  192.168.0.1:8118
 #  
-listen-address  :8118
+listen-address  :8119
 
 #  
 #  4.2. toggle
 
 #  
 #  4.2. toggle
index 52c15ac..471fa7f 100644 (file)
@@ -2,7 +2,7 @@
 # 
 #  File        :  $Source: /cvsroot/ijbswa/current/default.action.master,v $
 # 
 # 
 #  File        :  $Source: /cvsroot/ijbswa/current/default.action.master,v $
 # 
-#  $Id: default.action.master,v 1.10 2006/07/18 14:48:45 david__schmidt Exp $
+#  $Id: default.action.master,v 1.11 2006/08/02 17:36:19 david__schmidt Exp $
 #
 #  Purpose     :  Default actions file, see
 #                 http://www.privoxy.org/user-manual/actions-file.html
 #
 #  Purpose     :  Default actions file, see
 #                 http://www.privoxy.org/user-manual/actions-file.html
 #    responses as well. Use this action for servers that use HTTP/1.1
 #    protocol features that Privoxy currently can't handle yet.
 #
 #    responses as well. Use this action for servers that use HTTP/1.1
 #    protocol features that Privoxy currently can't handle yet.
 #
-# +fast-redirects
+# +fast-redirects{check-decoded-url}
 #    Many sites, like yahoo.com, don't just link to other sites.
 #    Instead, they will link to some script on their own server,
 #    giving the destination as a parameter, which will then redirect
 #    Many sites, like yahoo.com, don't just link to other sites.
 #    Instead, they will link to some script on their own server,
 #    giving the destination as a parameter, which will then redirect
 #    time is wasted, while your browser aks the server for one redirect
 #    after the other. Plus, it feeds the advertisers.
 #
 #    time is wasted, while your browser aks the server for one redirect
 #    after the other. Plus, it feeds the advertisers.
 #
-#    The +fast-redirects option enables interception of these requests
+#    The +fast-redirects{check-decoded-url} option enables interception of these requests
 #    by Privoxy, who will cut off all but the last valid URL in the
 #    request and send a local redirect back to your browser without
 #    contacting the intermediate sites.
 #    by Privoxy, who will cut off all but the last valid URL in the
 #    request and send a local redirect back to your browser without
 #    contacting the intermediate sites.
@@ -1609,7 +1609,7 @@ cf.nbc4.com/
 #MASTER# PROBLEM-URL: http://www.hh.schule.de/ak/nt/
 www.hh.schule.de/ak/nt/
 
 #MASTER# PROBLEM-URL: http://www.hh.schule.de/ak/nt/
 www.hh.schule.de/ak/nt/
 
-{+fast-redirects -block}
+{+fast-redirects{check-decoded-url} -block}
 #MASTER# PROBLEM-URL: http://isbn.nu/0596001088/price/2.html
 www.commission-junction.com/track/
 #MASTER# PROBLEM-URL: http://uk.rd.yahoo.com/M=200059723.200849546.202365062.200414073/D=ukhmpg/S=15426100:TEAR/A=200396897/R=1119/id=img1_nocap_dial/*http://ad.uk.doubleclick.net/clk;5982435;8261020;g?http://www.lunnpoly.com
 #MASTER# PROBLEM-URL: http://isbn.nu/0596001088/price/2.html
 www.commission-junction.com/track/
 #MASTER# PROBLEM-URL: http://uk.rd.yahoo.com/M=200059723.200849546.202365062.200414073/D=ukhmpg/S=15426100:TEAR/A=200396897/R=1119/id=img1_nocap_dial/*http://ad.uk.doubleclick.net/clk;5982435;8261020;g?http://www.lunnpoly.com
index 48290a9..1d0cb80 100644 (file)
--- a/errlog.c
+++ b/errlog.c
@@ -1,7 +1,7 @@
-const char errlog_rcs[] = "$Id: errlog.c,v 1.40.2.4 2005/04/03 20:10:50 david__schmidt Exp $";
+const char errlog_rcs[] = "$Id: errlog.c,v 1.42 2006/07/18 14:48:46 david__schmidt Exp $";
 /*********************************************************************
  *
 /*********************************************************************
  *
- * File        :  $Source: /cvsroot/ijbswa/current/Attic/errlog.c,v $
+ * File        :  $Source: /cvsroot/ijbswa/current/errlog.c,v $
  *
  * Purpose     :  Log errors to a designated destination in an elegant,
  *                printf-like fashion.
  *
  * Purpose     :  Log errors to a designated destination in an elegant,
  *                printf-like fashion.
@@ -33,6 +33,10 @@ const char errlog_rcs[] = "$Id: errlog.c,v 1.40.2.4 2005/04/03 20:10:50 david__s
  *
  * Revisions   :
  *    $Log: errlog.c,v $
  *
  * Revisions   :
  *    $Log: errlog.c,v $
+ *    Revision 1.42  2006/07/18 14:48:46  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
  *    Revision 1.40.2.4  2005/04/03 20:10:50  david__schmidt
  *    Thanks to Jindrich Makovicka for a race condition fix for the log
  *    file.  The race condition remains for non-pthread implementations.
  *    Revision 1.40.2.4  2005/04/03 20:10:50  david__schmidt
  *    Thanks to Jindrich Makovicka for a race condition fix for the log
  *    file.  The race condition remains for non-pthread implementations.
@@ -288,7 +292,7 @@ const char errlog_h_rcs[] = ERRLOG_H_VERSION;
 static FILE *logfp = NULL;
 
 /* logging detail level.  */
 static FILE *logfp = NULL;
 
 /* logging detail level.  */
-static int debug = (LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO);  
+int debug = (LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO);  
 
 /* static functions */
 static void fatal_error(const char * error_message);
 
 /* static functions */
 static void fatal_error(const char * error_message);
index 744af6a..de38b56 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1,7 +1,7 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.58.2.9 2006/01/29 23:10:56 david__schmidt Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.60 2006/07/18 14:48:46 david__schmidt Exp $";
 /*********************************************************************
  *
 /*********************************************************************
  *
- * File        :  $Source: /cvsroot/ijbswa/current/Attic/filters.c,v $
+ * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
  *
  * Purpose     :  Declares functions to parse/crunch headers and pages.
  *                Functions declared include:
  *
  * Purpose     :  Declares functions to parse/crunch headers and pages.
  *                Functions declared include:
@@ -39,6 +39,10 @@ const char filters_rcs[] = "$Id: filters.c,v 1.58.2.9 2006/01/29 23:10:56 david_
  *
  * Revisions   :
  *    $Log: filters.c,v $
  *
  * Revisions   :
  *    $Log: filters.c,v $
+ *    Revision 1.60  2006/07/18 14:48:46  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
  *    Revision 1.58.2.9  2006/01/29 23:10:56  david__schmidt
  *    Multiple filter file support
  *
  *    Revision 1.58.2.9  2006/01/29 23:10:56  david__schmidt
  *    Multiple filter file support
  *
@@ -721,6 +725,7 @@ int match_portlist(const char *portlist, int port)
 struct http_response *block_url(struct client_state *csp)
 {
    struct http_response *rsp;
 struct http_response *block_url(struct client_state *csp)
 {
    struct http_response *rsp;
+   const char *new_content_type = NULL;
 
    /*
     * If it's not blocked, don't block it ;-)
 
    /*
     * If it's not blocked, don't block it ;-)
@@ -729,7 +734,10 @@ struct http_response *block_url(struct client_state *csp)
    {
       return NULL;
    }
    {
       return NULL;
    }
-
+   if (csp->action->flags & ACTION_REDIRECT)
+   {
+      log_error(LOG_LEVEL_ERROR, "redirect{} overruled by block.");     
+   }
    /*
     * Else, prepare a response
     */
    /*
     * Else, prepare a response
     */
@@ -750,11 +758,21 @@ struct http_response *block_url(struct client_state *csp)
       /* determine HOW images should be blocked */
       p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
 
       /* determine HOW images should be blocked */
       p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
 
+      if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
+      {
+         log_error(LOG_LEVEL_ERROR, "handle-as-empty-document overruled by handle-as-image.");
+      }
 #if 1 /* Two alternative strategies, use this one for now: */
 
       /* and handle accordingly: */
       if ((p == NULL) || (0 == strcmpic(p, "pattern")))
       {
 #if 1 /* Two alternative strategies, use this one for now: */
 
       /* and handle accordingly: */
       if ((p == NULL) || (0 == strcmpic(p, "pattern")))
       {
+         rsp->status = strdup("403 Request blocked by Privoxy");
+         if (rsp->status == NULL)
+         {
+            free_http_response(rsp);
+            return cgi_error_memory();
+         }
          rsp->body = bindup(image_pattern_data, image_pattern_length);
          if (rsp->body == NULL)
          {
          rsp->body = bindup(image_pattern_data, image_pattern_length);
          if (rsp->body == NULL)
          {
@@ -772,6 +790,12 @@ struct http_response *block_url(struct client_state *csp)
 
       else if (0 == strcmpic(p, "blank"))
       {
 
       else if (0 == strcmpic(p, "blank"))
       {
+         rsp->status = strdup("403 Request blocked by Privoxy");
+         if (rsp->status == NULL)
+         {
+            free_http_response(rsp);
+            return cgi_error_memory();
+         }
          rsp->body = bindup(image_blank_data, image_blank_length);
          if (rsp->body == NULL)
          {
          rsp->body = bindup(image_blank_data, image_blank_length);
          if (rsp->body == NULL)
          {
@@ -827,6 +851,34 @@ struct http_response *block_url(struct client_state *csp)
          return cgi_error_memory();
       }
 #endif /* Preceeding code is disabled for now */
          return cgi_error_memory();
       }
 #endif /* Preceeding code is disabled for now */
+   }
+   else if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
+   {
+     /*
+      *  Send empty document.               
+      */
+      new_content_type = csp->action->string[ACTION_STRING_CONTENT_TYPE];
+
+      freez(rsp->body);
+      rsp->body = strdup(" ");
+      rsp->content_length = 1;
+
+      rsp->status = strdup("403 Request blocked by Privoxy");
+      if (rsp->status == NULL)
+      {
+         free_http_response(rsp);
+         return cgi_error_memory();
+      }
+      if (new_content_type != 0)
+      {
+         log_error(LOG_LEVEL_HEADER, "Overwriting Content-Type with %s", new_content_type);
+         if (enlist_unique_header(rsp->headers, "Content-Type", new_content_type))
+         {
+            free_http_response(rsp);
+            return cgi_error_memory();
+         }
+      }
+
    }
    else
 #endif /* def FEATURE_IMAGE_BLOCKING */
    }
    else
 #endif /* def FEATURE_IMAGE_BLOCKING */
@@ -1070,18 +1122,55 @@ struct http_response *redirect_url(struct client_state *csp)
 {
    char *p, *q;
    struct http_response *rsp;
 {
    char *p, *q;
    struct http_response *rsp;
+   char *redirect_mode = NULL;
+   int x, y;
 
 
-   p = q = csp->http->path;
-   log_error(LOG_LEVEL_REDIRECTS, "checking path for redirects: %s", p);
-
-   /*
-    * find the last URL encoded in the request
-    */
-   while ((p = strstr(p, "http://")) != NULL)
+   if ((csp->action->flags & ACTION_REDIRECT))
    {
    {
-      q = p++;
+      q = csp->action->string[ACTION_STRING_REDIRECT];
    }
    }
+   else
+   {
+      redirect_mode = csp->action->string[ACTION_STRING_FAST_REDIRECTS];
+      if (0 == strcmpic(redirect_mode, "check-decoded-url"))
+      {  
+         p = q = csp->http->path; 
+         log_error(LOG_LEVEL_REDIRECTS, "Decoding path: %s if necessary.", p);
+         while (*p)
+         {
+            if (*p == '%') /* Escape sequence? */
+            {
+               /* Yes, translate from hexadecimal to decimal */
+               p++;
+               /* First byte */
+               x=((int)*p++)-48;
+               if (x>9) x-=7;
+               x<<=4;
+               /* Second byte */
+               y=((int)*p++)-48;
+               if (y>9)y-=7;
+               /* Merge */
+               *q++=(char)(x|y);
+            }
+            else
+            {
+               /* No, forward character. */
+               *q++=*p++;
+            }
+         }
+         *q='\0';
+      }
+      p = q = csp->http->path;
+      log_error(LOG_LEVEL_REDIRECTS, "Checking path for redirects: %s", p);
 
 
+      /*
+       * find the last URL encoded in the request
+       */
+      while ((p = strstr(p, "http://")) != NULL)
+      {
+         q = p++;
+      }
+   }
    /*
     * if there was any, generate and return a HTTP redirect
     */
    /*
     * if there was any, generate and return a HTTP redirect
     */
index 2cf89e9..ed5d597 100644 (file)
@@ -1,7 +1,7 @@
-const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.35.2.8 2006/01/21 16:16:08 david__schmidt Exp $";
+const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.38 2006/07/18 14:48:46 david__schmidt Exp $";
 /*********************************************************************
  *
 /*********************************************************************
  *
- * File        :  $Source: /cvsroot/ijbswa/current/Attic/jbsockets.c,v $
+ * File        :  $Source: /cvsroot/ijbswa/current/jbsockets.c,v $
  *
  * Purpose     :  Contains wrappers for system-specific sockets code,
  *                so that the rest of Junkbuster can be more
  *
  * Purpose     :  Contains wrappers for system-specific sockets code,
  *                so that the rest of Junkbuster can be more
@@ -35,6 +35,10 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.35.2.8 2006/01/21 16:16:08 da
  *
  * Revisions   :
  *    $Log: jbsockets.c,v $
  *
  * Revisions   :
  *    $Log: jbsockets.c,v $
+ *    Revision 1.38  2006/07/18 14:48:46  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
  *    Revision 1.35.2.8  2006/01/21 16:16:08  david__schmidt
  *    Thanks to  Edward Carrel for his patch to modernize OSX's\rpthreads support.  See bug #1409623.
  *
  *    Revision 1.35.2.8  2006/01/21 16:16:08  david__schmidt
  *    Thanks to  Edward Carrel for his patch to modernize OSX's\rpthreads support.  See bug #1409623.
  *
@@ -787,6 +791,7 @@ unsigned long resolve_hostname_to_ip(const char *host)
 {
    struct sockaddr_in inaddr;
    struct hostent *hostp;
 {
    struct sockaddr_in inaddr;
    struct hostent *hostp;
+   unsigned int dns_retries = 0;
 #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS) || defined(HAVE_GETHOSTBYNAME_R_3_ARGS)
    struct hostent result;
 #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS)
 #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS) || defined(HAVE_GETHOSTBYNAME_R_3_ARGS)
    struct hostent result;
 #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS) || defined(HAVE_GETHOSTBYNAME_R_5_ARGS)
@@ -807,8 +812,13 @@ unsigned long resolve_hostname_to_ip(const char *host)
    if ((inaddr.sin_addr.s_addr = inet_addr(host)) == -1)
    {
 #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS)
    if ((inaddr.sin_addr.s_addr = inet_addr(host)) == -1)
    {
 #if defined(HAVE_GETHOSTBYNAME_R_6_ARGS)
-      gethostbyname_r(host, &result, hbuf,
-                      HOSTENT_BUFFER_SIZE, &hostp, &thd_err);
+       while ( gethostbyname_r(host, &result, hbuf,
+                      HOSTENT_BUFFER_SIZE, &hostp, &thd_err)
+               && (thd_err == TRY_AGAIN) && (dns_retries++ < 10) )
+      {   
+         log_error(LOG_LEVEL_ERROR, "Timeout #%u while trying to resolve %s. Trying again.",
+                                                dns_retries, host);
+      }
 #elif defined(HAVE_GETHOSTBYNAME_R_5_ARGS)
       hostp = gethostbyname_r(host, &result, hbuf,
                       HOSTENT_BUFFER_SIZE, &thd_err);
 #elif defined(HAVE_GETHOSTBYNAME_R_5_ARGS)
       hostp = gethostbyname_r(host, &result, hbuf,
                       HOSTENT_BUFFER_SIZE, &thd_err);
@@ -823,10 +833,20 @@ unsigned long resolve_hostname_to_ip(const char *host)
       }
 #elif OSX_DARWIN
       pthread_mutex_lock(&gethostbyname_mutex);
       }
 #elif OSX_DARWIN
       pthread_mutex_lock(&gethostbyname_mutex);
-      hostp = gethostbyname(host);
+      while ( NULL == (hostp = gethostbyname(host))
+            && (h_errno == TRY_AGAIN) && (dns_retries++ < 10) )
+      {   
+         log_error(LOG_LEVEL_ERROR, "Timeout #%u while trying to resolve %s. Trying again.",
+                                                dns_retries, host);
+      }
       pthread_mutex_unlock(&gethostbyname_mutex);
 #else
       pthread_mutex_unlock(&gethostbyname_mutex);
 #else
-      hostp = gethostbyname(host);
+      while ( NULL == (hostp = gethostbyname(host))
+            && (h_errno == TRY_AGAIN) && (dns_retries++ < 10) )
+      {
+         log_error(LOG_LEVEL_ERROR, "Timeout #%u while trying to resolve %s. Trying again.",
+                                                dns_retries, host);
+      }
 #endif /* def HAVE_GETHOSTBYNAME_R_(6|5|3)_ARGS */
       /*
        * On Mac OSX, if a domain exists but doesn't have a type A
 #endif /* def HAVE_GETHOSTBYNAME_R_(6|5|3)_ARGS */
       /*
        * On Mac OSX, if a domain exists but doesn't have a type A
diff --git a/jcc.c b/jcc.c
index 9010076..90802a3 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,7 +1,7 @@
-const char jcc_rcs[] = "$Id: jcc.c,v 1.92.2.16 2005/04/03 20:10:50 david__schmidt Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.94 2006/07/18 14:48:46 david__schmidt Exp $";
 /*********************************************************************
  *
 /*********************************************************************
  *
- * File        :  $Source: /cvsroot/ijbswa/current/Attic/jcc.c,v $
+ * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
  *
  * Purpose     :  Main file.  Contains main() method, main loop, and
  *                the main connection-handling function.
  *
  * Purpose     :  Main file.  Contains main() method, main loop, and
  *                the main connection-handling function.
@@ -33,6 +33,10 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.92.2.16 2005/04/03 20:10:50 david__schmid
  *
  * Revisions   :
  *    $Log: jcc.c,v $
  *
  * Revisions   :
  *    $Log: jcc.c,v $
+ *    Revision 1.94  2006/07/18 14:48:46  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
  *    Revision 1.92.2.16  2005/04/03 20:10:50  david__schmidt
  *    Thanks to Jindrich Makovicka for a race condition fix for the log
  *    file.  The race condition remains for non-pthread implementations.
  *    Revision 1.92.2.16  2005/04/03 20:10:50  david__schmidt
  *    Thanks to Jindrich Makovicka for a race condition fix for the log
  *    file.  The race condition remains for non-pthread implementations.
@@ -860,6 +864,7 @@ static void chat(struct client_state *csp)
    int server_body;
    int ms_iis5_hack = 0;
    int byte_count = 0;
    int server_body;
    int ms_iis5_hack = 0;
    int byte_count = 0;
+   unsigned int socks_retries = 0;
    const struct forward_spec * fwd;
    struct http_request *http;
    int len; /* for buffer sizes */
    const struct forward_spec * fwd;
    struct http_request *http;
    int len; /* for buffer sizes */
@@ -1013,13 +1018,22 @@ static void chat(struct client_state *csp)
            || (csp->action->flags & ACTION_LIMIT_CONNECT
               && !match_portlist(csp->action->string[ACTION_STRING_LIMIT_CONNECT], csp->http->port)) )
       {
            || (csp->action->flags & ACTION_LIMIT_CONNECT
               && !match_portlist(csp->action->string[ACTION_STRING_LIMIT_CONNECT], csp->http->port)) )
       {
-         strcpy(buf, CFORBIDDEN);
-         write_socket(csp->cfd, buf, strlen(buf));
-
-         log_error(LOG_LEVEL_CONNECT, "Denying suspicious CONNECT request from %s", csp->ip_addr_str);
-         log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 403 0", csp->ip_addr_str);
-
-         return;
+         if (csp->action->flags & ACTION_TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS)
+         {
+            /* The response will violate the specs, but makes unblocking easier. */
+            log_error(LOG_LEVEL_ERROR, "Marking suspicious CONNECT request from %s for blocking.",
+               csp->ip_addr_str);
+            csp->action->flags |= ACTION_BLOCK;
+            http->ssl = 0;
+         }
+         else
+         {
+            strcpy(buf, CFORBIDDEN);
+            write_socket(csp->cfd, buf, strlen(buf));
+            log_error(LOG_LEVEL_CONNECT, "Denying suspicious CONNECT request from %s", csp->ip_addr_str);
+            log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 403 0", csp->ip_addr_str);
+            return;
+         }
       }
    }
 
       }
    }
 
@@ -1177,7 +1191,7 @@ static void chat(struct client_state *csp)
       csp->flags |= CSP_FLAG_REJECTED;
 #endif /* def FEATURE_STATISTICS */
 
       csp->flags |= CSP_FLAG_REJECTED;
 #endif /* def FEATURE_STATISTICS */
 
-      /* Log (FIXME: All intercept reasons apprear as "crunch" with Status 200) */
+      /* Log (FIXME: All intercept reasons appear as "crunch" with Status 200) */
       log_error(LOG_LEVEL_GPC, "%s%s crunch!", http->hostport, http->path);
       log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", csp->ip_addr_str, http->ocmd);
 
       log_error(LOG_LEVEL_GPC, "%s%s crunch!", http->hostport, http->path);
       log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", csp->ip_addr_str, http->ocmd);
 
@@ -1209,7 +1223,12 @@ static void chat(struct client_state *csp)
 
    /* here we connect to the server, gateway, or the forwarder */
 
 
    /* here we connect to the server, gateway, or the forwarder */
 
-   csp->sfd = forwarded_connect(fwd, http, csp);
+   while ( (csp->sfd = forwarded_connect(fwd, http, csp))
+         && (errno == EINVAL) && (socks_retries++ < 3))
+   {
+               log_error(LOG_LEVEL_ERROR, "failed request #%u to connect to %s. Trying again.",
+                socks_retries, http->hostport);
+   }
 
    if (csp->sfd == JB_INVALID_SOCKET)
    {
 
    if (csp->sfd == JB_INVALID_SOCKET)
    {
@@ -1439,7 +1458,8 @@ static void chat(struct client_state *csp)
                      csp->content_length = csp->iob->eod - csp->iob->cur;
                   }
 
                      csp->content_length = csp->iob->eod - csp->iob->cur;
                   }
 
-                  hdr = sed(server_patterns, add_server_headers, csp);
+                  hdr = sed(server_patterns_light, NULL, csp);
+
                   if (hdr == NULL)
                   {
                      /* FIXME Should handle error properly */
                   if (hdr == NULL)
                   {
                      /* FIXME Should handle error properly */
index dd8929b..ac5bfef 100644 (file)
--- a/parsers.c
+++ b/parsers.c
@@ -1,7 +1,7 @@
-const char parsers_rcs[] = "$Id: parsers.c,v 1.56.2.10 2006/01/21 16:16:08 david__schmidt Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.58 2006/07/18 14:48:47 david__schmidt Exp $";
 /*********************************************************************
  *
 /*********************************************************************
  *
- * File        :  $Source: /cvsroot/ijbswa/current/Attic/parsers.c,v $
+ * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
  *
  * Purpose     :  Declares functions to parse/crunch headers and pages.
  *                Functions declared include:
  *
  * Purpose     :  Declares functions to parse/crunch headers and pages.
  *                Functions declared include:
@@ -40,6 +40,10 @@ const char parsers_rcs[] = "$Id: parsers.c,v 1.56.2.10 2006/01/21 16:16:08 david
  *
  * Revisions   :
  *    $Log: parsers.c,v $
  *
  * Revisions   :
  *    $Log: parsers.c,v $
+ *    Revision 1.58  2006/07/18 14:48:47  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
  *    Revision 1.56.2.10  2006/01/21 16:16:08  david__schmidt
  *    Thanks to  Edward Carrel for his patch to modernize OSX's\rpthreads support.  See bug #1409623.
  *
  *    Revision 1.56.2.10  2006/01/21 16:16:08  david__schmidt
  *    Thanks to  Edward Carrel for his patch to modernize OSX's\rpthreads support.  See bug #1409623.
  *
@@ -444,6 +448,7 @@ const char parsers_rcs[] = "$Id: parsers.c,v 1.56.2.10 2006/01/21 16:16:08 david
 #include <ctype.h>
 #include <assert.h>
 #include <string.h>
 #include <ctype.h>
 #include <assert.h>
 #include <string.h>
+#include <time.h>
 
 #if !defined(_WIN32) && !defined(__OS2__)
 #include <unistd.h>
 
 #if !defined(_WIN32) && !defined(__OS2__)
 #include <unistd.h>
@@ -482,37 +487,50 @@ const char parsers_h_rcs[] = PARSERS_H_VERSION;
 
 
 const struct parsers client_patterns[] = {
 
 
 const struct parsers client_patterns[] = {
-   { "referer:",                 8,    client_referrer },
+   { "referer:",                  8,   client_referrer },
    { "user-agent:",              11,   client_uagent },
    { "user-agent:",              11,   client_uagent },
-   { "ua-",                      3,    client_ua },
-   { "from:",                    5,    client_from },
-   { "cookie:",                  7,    client_send_cookie },
+   { "ua-",                       3,   client_ua },
+   { "from:",                     5,   client_from },
+   { "cookie:",                   7,   client_send_cookie },
    { "x-forwarded-for:",         16,   client_x_forwarded },
    { "Accept-Encoding:",         16,   client_accept_encoding },
    { "TE:",                       3,   client_te },
    { "Host:",                     5,   client_host },
    { "x-forwarded-for:",         16,   client_x_forwarded },
    { "Accept-Encoding:",         16,   client_accept_encoding },
    { "TE:",                       3,   client_te },
    { "Host:",                     5,   client_host },
-/* { "if-modified-since:",       18,   crumble }, */
+   { "if-modified-since:",       18,   client_if_modified_since },
    { "Keep-Alive:",              11,   crumble },
    { "connection:",              11,   crumble },
    { "proxy-connection:",        17,   crumble },
    { "max-forwards:",            13,   client_max_forwards },
    { "Keep-Alive:",              11,   crumble },
    { "connection:",              11,   crumble },
    { "proxy-connection:",        17,   crumble },
    { "max-forwards:",            13,   client_max_forwards },
-   { NULL,                       0,    NULL }
+   { "Accept-Language:",         16,   client_accept_language },
+   { "if-none-match:",           14,   client_if_none_match },
+   { "X-Filter:",                 9,   client_x_filter },
+   { "*",                         0,   crunch_client_header },
+   { "*",                         0,   filter_header },
+   { NULL,                        0,   NULL }
 };
 
 };
 
-
 const struct parsers server_patterns[] = {
 const struct parsers server_patterns[] = {
-   { "HTTP",                4, server_http },
-   { "set-cookie:",        11, server_set_cookie },
-   { "connection:",        11, crumble },
-   { "Content-Type:",      13, server_content_type },
-   { "Content-Length:",    15, server_content_length },
-   { "Content-MD5:",       12, server_content_md5 },
-   { "Content-Encoding:",  17, server_content_encoding },
-   { "Transfer-Encoding:", 18, server_transfer_coding },
-   { "Keep-Alive:",        11, crumble },
+   { "HTTP",                      4, server_http },
+   { "set-cookie:",              11, server_set_cookie },
+   { "connection:",              11, crumble },
+   { "Content-Type:",            13, server_content_type },
+   { "Content-Length:",          15, server_content_length },
+   { "Content-MD5:",             12, server_content_md5 },
+   { "Content-Encoding:",        17, server_content_encoding },
+   { "Transfer-Encoding:",       18, server_transfer_coding },
+   { "Keep-Alive:",              11, crumble },
+   { "content-disposition:",     20, server_content_disposition },
+   { "Last-Modified:",           14, server_last_modified },
+   { "*",                         0, crunch_server_header },
+   { "*",                         0, filter_header },
    { NULL, 0, NULL }
 };
 
    { NULL, 0, NULL }
 };
 
+const struct parsers server_patterns_light[] = {
+   { "Content-Length:",          15, server_content_length },
+   { "Transfer-Encoding:",       18, server_transfer_coding },
+   { NULL, 0, NULL }
+};
 
 const add_header_func_ptr add_client_headers[] = {
    client_host_adder,
 
 const add_header_func_ptr add_client_headers[] = {
    client_host_adder,
@@ -530,7 +548,6 @@ const add_header_func_ptr add_server_headers[] = {
    NULL
 };
 
    NULL
 };
 
-
 /*********************************************************************
  *
  * Function    :  flush_socket
 /*********************************************************************
  *
  * Function    :  flush_socket
@@ -779,27 +796,62 @@ char *sed(const struct parsers pats[],
    const struct parsers *v;
    const add_header_func_ptr *f;
    jb_err err = JB_ERR_OK;
    const struct parsers *v;
    const add_header_func_ptr *f;
    jb_err err = JB_ERR_OK;
+   int first_run;
+
+   /*
+    * If filtering is enabled, sed is run twice,
+    * but most of the work needs to be done only once.
+    */
+   first_run = (more_headers != NULL ) ? 1 : 0;
 
 
-   for (v = pats; (err == JB_ERR_OK) && (v->str != NULL) ; v++)
+   if (first_run) /* Parse and print */
    {
    {
-      for (p = csp->headers->first; (err == JB_ERR_OK) && (p != NULL) ; p = p->next)
+      for (v = pats; (err == JB_ERR_OK) && (v->str != NULL) ; v++)
       {
       {
-         /* Header crunch()ed in previous run? -> ignore */
-         if (p->str == NULL) continue;
+         for (p = csp->headers->first; (err == JB_ERR_OK) && (p != NULL) ; p = p->next)
+         {
+            /* Header crunch()ed in previous run? -> ignore */
+            if (p->str == NULL) continue;
 
 
-         if (v == pats) log_error(LOG_LEVEL_HEADER, "scan: %s", p->str);
+            if (v == pats) log_error(LOG_LEVEL_HEADER, "scan: %s", p->str);
 
 
-         if (strncmpic(p->str, v->str, v->len) == 0)
-         {
-            err = v->parser(csp, (char **)&(p->str));
+            /* Does the current parser handle this header? */
+            if ((strncmpic(p->str, v->str, v->len) == 0) || (v->len == CHECK_EVERY_HEADER_REMAINING))
+            {
+               err = v->parser(csp, (char **)&(p->str));
+            }
          }
       }
          }
       }
+      /* place any additional headers on the csp->headers list */
+      for (f = more_headers; (err == JB_ERR_OK) && (*f) ; f++)
+      {
+         err = (*f)(csp);
+      }
    }
    }
-
-   /* place any additional headers on the csp->headers list */
-   for (f = more_headers; (err == JB_ERR_OK) && (*f) ; f++)
+   else /* Parse only */
    {
    {
-      err = (*f)(csp);
+      /*
+       * The second run is only needed if the body was modified
+       * and the content-lenght has changed.
+       */
+      if (strncmpic(csp->http->cmd, "HEAD", 4))
+      {
+         /*XXX: Code duplication*/
+         for (v = pats; (err == JB_ERR_OK) && (v->str != NULL) ; v++)
+         {
+            for (p = csp->headers->first; (err == JB_ERR_OK) && (p != NULL) ; p = p->next)
+            {
+               /* Header crunch()ed in previous run? -> ignore */
+               if (p->str == NULL) continue;
+
+               /* Does the current parser handle this header? */
+               if (strncmpic(p->str, v->str, v->len) == 0)
+               {
+                  err = v->parser(csp, (char **)&(p->str));
+               }
+            }
+         }
+      }
    }
 
    if (err != JB_ERR_OK)
    }
 
    if (err != JB_ERR_OK)
@@ -814,6 +866,148 @@ char *sed(const struct parsers pats[],
 /* here begins the family of parser functions that reformat header lines */
 
 
 /* here begins the family of parser functions that reformat header lines */
 
 
+/*********************************************************************
+ *
+ * Function    :  filter_header
+ *
+ * Description :  Executes all text substitutions from all applying
+ *                +filter actions on the header.
+ *                Most of the code was copied from pcrs_filter_response,
+ *                including the rather short variable names
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  header = On input, pointer to header to modify.
+ *                On output, pointer to the modified header, or NULL
+ *                to remove the header.  This function frees the
+ *                original string if necessary.
+ *
+ * Returns     :  JB_ERR_OK on success and always succeeds
+ *
+ *********************************************************************/
+jb_err filter_header(struct client_state *csp, char **header)
+{
+   int hits=0;
+   int matches;
+   size_t size = strlen(*header);
+
+   char *newheader = NULL;
+   pcrs_job *job;
+
+   struct file_list *fl;
+   struct re_filterfile_spec *b;
+   struct list_entry *filtername;
+
+   int i, found_filters = 0;
+
+#ifndef  MAX_AF_FILES
+# define MAX_AF_FILES 1
+# define INDEX_OR_NOT
+#else
+# define INDEX_OR_NOT [i] 
+#endif
+
+   if (!(csp->action->flags & ACTION_FILTER_HEADERS))
+   {
+      return(JB_ERR_OK);
+   }
+   log_error(LOG_LEVEL_RE_FILTER, "Entered filter_headers");
+   /*
+    * Need to check the set of re_filterfiles...
+    */
+   for (i = 0; i < MAX_AF_FILES; i++)
+   {
+      fl = csp->rlist INDEX_OR_NOT;
+      if (NULL != fl)
+      {
+         if (NULL != fl->f)
+         {
+           found_filters = 1;
+           break;
+         }
+      }
+   }
+
+   if (0 == found_filters)
+   {
+      log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
+         return(JB_ERR_OK);
+   }
+
+   for (i = 0; i < MAX_AF_FILES; i++)
+   {
+      fl = csp->rlist INDEX_OR_NOT;
+      if ((NULL == fl) || (NULL == fl->f))
+         break;
+      /*
+       * For all applying +filter actions, look if a filter by that
+       * name exists and if yes, execute it's pcrs_joblist on the
+       * buffer.
+       */
+      for (b = fl->f; b; b = b->next)
+      {
+         for (filtername = csp->action->multi[ACTION_MULTI_FILTER]->first;
+              filtername ; filtername = filtername->next)
+         {
+            if (strcmp(b->name, filtername->str) == 0)
+            {
+               int current_hits = 0;
+
+               if ( NULL == b->joblist )
+               {
+                  log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name);
+                  continue;
+               }
+
+               log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s (size %d) with filter %s...",
+                         *header, size, b->name);
+
+               /* Apply all jobs from the joblist */
+               for (job = b->joblist; NULL != job; job = job->next)
+               {
+                  matches = pcrs_execute(job, *header, size, &newheader, &size);
+                  if ( 0 < matches )
+                  {
+                     current_hits += matches; 
+                     log_error(LOG_LEVEL_HEADER, "Transforming \"%s\" to \"%s\"", *header, newheader);
+                     freez(*header);
+                     *header = newheader;
+                  }
+                  else if ( 0 == matches )
+                  {
+                     /* Filter doesn't change header */
+                     freez(newheader);
+                  }
+                  else
+                  {
+                     /* RegEx failure */
+                     log_error(LOG_LEVEL_ERROR, "Filtering \'%s\' with \'%s\' didn't work out: %s",
+                        *header, b->name, pcrs_strerror(matches));
+                     if( newheader != NULL)
+                     {
+                        log_error(LOG_LEVEL_ERROR, "Freeing what's left: %s", newheader);
+                        freez(newheader);
+                     }
+                  }
+               }
+               log_error(LOG_LEVEL_RE_FILTER, " ...produced %d hits (new size %d).", current_hits, size);
+               hits += current_hits;
+            }
+         }
+      }
+   }
+
+   if ( 0 == size )
+   {
+          log_error(LOG_LEVEL_HEADER, "Removing empty header %s", *header);
+      freez(*header);
+   }
+   log_error(LOG_LEVEL_RE_FILTER, "Leaving filter headers");
+   return(JB_ERR_OK);
+
+}
+
+
 /*********************************************************************
  *
  * Function    :  crumble
 /*********************************************************************
  *
  * Function    :  crumble
@@ -833,12 +1027,47 @@ char *sed(const struct parsers pats[],
  *********************************************************************/
 jb_err crumble(struct client_state *csp, char **header)
 {
  *********************************************************************/
 jb_err crumble(struct client_state *csp, char **header)
 {
-   log_error(LOG_LEVEL_HEADER, "crunch!");
+   log_error(LOG_LEVEL_HEADER, "crumble crunched: %s!", *header);
    freez(*header);
    return JB_ERR_OK;
 }
 
    freez(*header);
    return JB_ERR_OK;
 }
 
+/*********************************************************************
+ *
+ * Function    :  crunch_server_header
+ *
+ * Description :  Crunch server header if it matches a string supplied by the
+ *                user. Called from `sed'.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  header = On input, pointer to header to modify.
+ *                On output, pointer to the modified header, or NULL
+ *                to remove the header.  This function frees the
+ *                original string if necessary.
+ *
+ * Returns     :  JB_ERR_OK on success and always succeeds
+ *
+ *********************************************************************/
+jb_err crunch_server_header(struct client_state *csp, char **header)
+{
+   const char *crunch_pattern;
+   /*Is there a header to crunch*/
+
+   if ((csp->action->flags & ACTION_CRUNCH_SERVER_HEADER))
+   {
+      crunch_pattern = csp->action->string[ACTION_STRING_SERVER_HEADER];
 
 
+      /*Is the current header the lucky one?*/
+      if (strstr(*header, crunch_pattern))
+      {
+         log_error(LOG_LEVEL_HEADER, "Crunching server header: %s (contains: %s)", *header, crunch_pattern);  
+         freez(*header);
+      }
+   }
+
+   return JB_ERR_OK;
+}
 /*********************************************************************
  *
  * Function    :  server_content_type
 /*********************************************************************
  *
  * Function    :  server_content_type
@@ -863,9 +1092,14 @@ jb_err crumble(struct client_state *csp, char **header)
  *********************************************************************/
 jb_err server_content_type(struct client_state *csp, char **header)
 {
  *********************************************************************/
 jb_err server_content_type(struct client_state *csp, char **header)
 {
+   const char *newval;
+   
+   newval = csp->action->string[ACTION_STRING_CONTENT_TYPE]; 
+
    if (csp->content_type != CT_TABOO)
    {
       if ((strstr(*header, " text/") && !strstr(*header, "plain"))
    if (csp->content_type != CT_TABOO)
    {
       if ((strstr(*header, " text/") && !strstr(*header, "plain"))
+       || strstr(*header, "xml")
        || strstr(*header, "application/x-javascript"))
          csp->content_type = CT_TEXT;
       else if (strstr(*header, " image/gif"))
        || strstr(*header, "application/x-javascript"))
          csp->content_type = CT_TEXT;
       else if (strstr(*header, " image/gif"))
@@ -875,7 +1109,52 @@ jb_err server_content_type(struct client_state *csp, char **header)
       else
          csp->content_type = 0;
    }
       else
          csp->content_type = 0;
    }
-
+   /*
+    * Are we enabling text mode by force?
+    */
+   if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
+   {
+     /*
+      * Do we really have to?
+      */
+      if (csp->content_type == CT_TEXT)
+      {
+         log_error(LOG_LEVEL_HEADER, "Text mode is already enabled.");   
+      }
+      else
+      {
+         csp->content_type = CT_TEXT;
+         log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!");   
+      }
+   }
+   /*
+    * Are we messing with the content type?
+    */ 
+   if (csp->action->flags & ACTION_CONTENT_TYPE_OVERWRITE)
+   { 
+     /*
+      * Make sure the user doesn't accidently
+      * change the content type of binary documents. 
+      */ 
+     if (csp->content_type == CT_TEXT)
+     { 
+        freez(*header);
+        *header = strdup("Content-Type: ");
+        string_append(header, newval);
+        
+        if (header == NULL)
+        { 
+           log_error(LOG_LEVEL_HEADER, "Insufficient memory. Conten-Type crunched without replacement!");
+           return JB_ERR_MEMORY;
+        }
+        log_error(LOG_LEVEL_HEADER, "Modified: %s!", *header);
+     }
+     else
+     {
+        log_error(LOG_LEVEL_HEADER, "%s not replaced. It doesn't look like text. "
+                 "Enable force-text-mode if you know what you're doing.", *header);   
+     }
+   }  
    return JB_ERR_OK;
 }
 
    return JB_ERR_OK;
 }
 
@@ -925,6 +1204,7 @@ jb_err server_transfer_coding(struct client_state *csp, char **header)
       {
          freez(*header);
          *header = strdup("Transfer-Encoding: identity");
       {
          freez(*header);
          *header = strdup("Transfer-Encoding: identity");
+         log_error(LOG_LEVEL_HEADER, "Set: %s", *header);
          return (header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
       }
    }
          return (header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
       }
    }
@@ -985,8 +1265,12 @@ jb_err server_content_encoding(struct client_state *csp, char **header)
  *********************************************************************/
 jb_err server_content_length(struct client_state *csp, char **header)
 {
  *********************************************************************/
 jb_err server_content_length(struct client_state *csp, char **header)
 {
-   if (csp->content_length != 0) /* Content length has been modified */
+   if (csp->content_length != 0) /* Content length could have been modified */
    {
    {
+      /*
+       * XXX: Shouldn't we check if csp->content_length
+       * is different than the original value?
+       */
       freez(*header);
       *header = (char *) zalloc(100);
       if (*header == NULL)
       freez(*header);
       *header = (char *) zalloc(100);
       if (*header == NULL)
@@ -1032,6 +1316,189 @@ jb_err server_content_md5(struct client_state *csp, char **header)
    return JB_ERR_OK;
 }
 
    return JB_ERR_OK;
 }
 
+/*********************************************************************
+ *
+ * Function    :  server_content_disposition
+ *
+ * Description :  If enabled, blocks or modifies the "content-disposition" header.
+ *                Called from `sed'.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  header = On input, pointer to header to modify.
+ *                On output, pointer to the modified header, or NULL
+ *                to remove the header.  This function frees the
+ *                original string if necessary.
+ *
+ * Returns     :  JB_ERR_OK on success, or
+ *                JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err server_content_disposition(struct client_state *csp, char **header)
+{
+   const char *newval;
+
+   /*
+    * Are we messing with the content-disposition header?
+    */
+   if ((csp->action->flags & ACTION_HIDE_CONTENT_DISPOSITION) == 0)
+   {
+      /*Me tinks not*/
+      return JB_ERR_OK;
+   }
+
+   newval = csp->action->string[ACTION_STRING_CONTENT_DISPOSITION];
+
+   if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
+   {
+      /*
+       * Blocking content-disposition header
+       */
+      log_error(LOG_LEVEL_HEADER, "Crunching %s!", *header);
+      freez(*header);
+      return JB_ERR_OK;
+   }
+   else
+   {  
+      /*
+       * Replacing content-disposition header
+       */
+      freez(*header);
+      *header = strdup("content-disposition: ");
+      string_append(header, newval);   
+
+      if (*header == NULL)
+      {
+         log_error(LOG_LEVEL_HEADER, "Insufficent memory. content-disposition header not fully replaced.");  
+      }
+      else
+      {
+         log_error(LOG_LEVEL_HEADER, "content-disposition header crunched and replaced with: %s", *header);
+      }
+   }
+   return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
+}
+
+/*********************************************************************
+ *
+ * Function    :  server_last_modified
+ *
+ * Description :  Changes Last-Modified header to the actual date
+ *                to help hide-if-modified-since.
+ *                Called from `sed'.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  header = On input, pointer to header to modify.
+ *                On output, pointer to the modified header, or NULL
+ *                to remove the header.  This function frees the
+ *                original string if necessary.
+ *
+ * Returns     :  JB_ERR_OK on success, or
+ *                JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err server_last_modified(struct client_state *csp, char **header)
+{
+   const char *newval;
+   char buf[BUFFER_SIZE];
+
+   char newheader[50];
+   struct tm *timeptr;
+   time_t now, last_modified;                  
+   long int rtime;
+   long int days, hours, minutes, seconds;
+   
+   /*
+    * Are we messing with the Last-Modified header?
+    */
+   if ((csp->action->flags & ACTION_OVERWRITE_LAST_MODIFIED) == 0)
+   {
+      /*Nope*/
+      return JB_ERR_OK;
+   }
+
+   newval = csp->action->string[ACTION_STRING_LAST_MODIFIED];
+
+   if (0 == strcmpic(newval, "block") )
+   {
+      /*
+       * Blocking Last-Modified header. Useless but why not.
+       */
+      log_error(LOG_LEVEL_HEADER, "Crunching %s!", *header);
+      freez(*header);
+      return JB_ERR_OK;
+   }
+   else if (0 == strcmpic(newval, "reset-to-request-time"))
+   {  
+      /*
+       * Setting Last-Modified Header to now.
+       */
+      get_http_time(0, buf);
+      freez(*header);
+      *header = strdup("Last-Modified: ");
+      string_append(header, buf);   
+
+      if (*header == NULL)
+      {
+         log_error(LOG_LEVEL_HEADER, "Insufficent memory. Last-Modified header got lost, boohoo.");  
+      }
+      else
+      {
+         log_error(LOG_LEVEL_HEADER, "Reset to present time: %s", *header);
+      }
+   }
+   else if (0 == strcmpic(newval, "randomize"))
+   {
+      log_error(LOG_LEVEL_HEADER, "Randomizing: %s", *header);
+      now = time(NULL);
+      timeptr = gmtime(&now);
+      if (strptime(*header, "Last-Modified: %a, %d %b %Y %T", timeptr) == NULL)
+      {
+          log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header);
+          freez(*header);
+      }
+      else
+      {
+         last_modified = timegm(timeptr);
+         rtime = difftime(now, last_modified);
+         if (rtime)
+         {
+            rtime = random() % rtime + 1; 
+            last_modified += rtime;
+            timeptr = gmtime(&last_modified);
+            strftime(newheader, sizeof(newheader), "%a, %d %b %Y %T GMT", timeptr);
+            freez(*header);
+            *header = strdup("Last-Modified: ");
+            string_append(header, newheader);
+
+            if (*header == NULL)
+            {
+               log_error(LOG_LEVEL_ERROR, " Insufficent memory, header crunched without replacement.");
+               return JB_ERR_MEMORY;  
+            }
+
+            if(LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */
+            {
+               days    = rtime / (3600 * 24);
+               hours   = rtime / 3600 % 24;
+               minutes = rtime / 60 % 60;
+               seconds = rtime % 60;            
+
+               log_error(LOG_LEVEL_HEADER, "Randomized:  %s (added %d da%s %d hou%s %d minut%s %d second%s",
+                  *header, days, (days == 1) ? "y" : "ys", hours, (hours == 1) ? "r" : "rs",
+                  minutes, (minutes == 1) ? "e" : "es", seconds, (seconds == 1) ? ")" : "s)");
+            }
+         }
+         else
+         {
+            log_error(LOG_LEVEL_HEADER, "Randomized ... or not. No time difference to work with.");
+         }
+      }
+   }
+
+   return JB_ERR_OK;
+}
 
 /*********************************************************************
  *
 
 /*********************************************************************
  *
@@ -1129,10 +1596,13 @@ jb_err client_te(struct client_state *csp, char **header)
 jb_err client_referrer(struct client_state *csp, char **header)
 {
    const char *newval;
 jb_err client_referrer(struct client_state *csp, char **header)
 {
    const char *newval;
-
+   const char *host;
+   char *referer;
+   int hostlenght;
 #ifdef FEATURE_FORCE_LOAD
    /* Since the referrer can include the prefix even
 #ifdef FEATURE_FORCE_LOAD
    /* Since the referrer can include the prefix even
-    * even if the request itself is non-forced, we must
+    * if the request itself is non-forced, we must
     * clean it unconditionally
     */
    strclean(*header, FORCE_PREFIX);
     * clean it unconditionally
     */
    strclean(*header, FORCE_PREFIX);
@@ -1146,27 +1616,73 @@ jb_err client_referrer(struct client_state *csp, char **header)
       return JB_ERR_OK;
    }
 
       return JB_ERR_OK;
    }
 
-   freez(*header);
-
    newval = csp->action->string[ACTION_STRING_REFERER];
 
    newval = csp->action->string[ACTION_STRING_REFERER];
 
+   if ((0 != strcmpic(newval, "conditional-block")))
+   {  
+      freez(*header);
+   }
    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
    {
       /*
        * Blocking referer
        */
    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
    {
       /*
        * Blocking referer
        */
-      log_error(LOG_LEVEL_HEADER, "crunch!");
+      log_error(LOG_LEVEL_HEADER, "Referer crunched!");
       return JB_ERR_OK;
    }
       return JB_ERR_OK;
    }
-   else if (0 == strncmpic(newval, "http://", 7))
+   else if (0 == strcmpic(newval, "conditional-block"))
    {
       /*
    {
       /*
-       * We have a specific (fixed) referer we want to send.
+       * Block referer if host has changed.
        */
        */
-      log_error(LOG_LEVEL_HEADER, "modified");
 
 
+      if (NULL == (host = strdup(csp->http->hostport)))
+      {
+         freez(*header);
+         log_error(LOG_LEVEL_HEADER, "Referer crunched! Couldn't allocate memory for temporary host copy.");
+         return JB_ERR_MEMORY;
+      }
+      if (NULL == (referer = strdup(*header)))
+      {
+         freez(*header);
+         freez(host);
+         log_error(LOG_LEVEL_HEADER, "Referer crunched! Couldn't allocate memory for temporary referer copy.");
+         return JB_ERR_MEMORY;
+      }
+      hostlenght = strlen(host);
+      if ( hostlenght < (strlen(referer)-17) ) /*referer begins with 'Referer: http[s]://'*/
+      {
+         /*Shorten referer to make sure the referer is blocked
+          *if www.example.org/www.example.com-shall-see-the-referer/
+          *links to www.example.com/
+          */
+         referer[hostlenght+17] = '\n';
+      }
+      if ( 0 == strstr(referer, host)) /*Host has changed*/
+      {
+         log_error(LOG_LEVEL_HEADER, "New host is: %s. Crunching %s!", host, *header);
+         freez(*header);
+      }
+      else
+      {
+         log_error(LOG_LEVEL_HEADER, "%s (not modified, still on %s)", *header, host);
+      }
+      freez(referer);
+      freez(host);
+      return JB_ERR_OK;    
+   }
+   else if (0 != strcmpic(newval, "forge"))
+   {
+      /*
+       * We have a specific (fixed) referer we want to send.
+       */
+      if ((0 != strncmpic(newval, "http://", 7)) && (0 != strncmpic(newval, "https://", 8)))
+      {
+         log_error(LOG_LEVEL_HEADER, "Parameter: +referrer{%s} is a bad idea, but I don't care.", newval);
+      }
       *header = strdup("Referer: ");
       string_append(header, newval);
       *header = strdup("Referer: ");
       string_append(header, newval);
+      log_error(LOG_LEVEL_HEADER, "Referer overwritten with: %s", *header);
 
       return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
    }
 
       return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
    }
@@ -1176,23 +1692,115 @@ jb_err client_referrer(struct client_state *csp, char **header)
        * Forge a referer as http://[hostname:port of REQUEST]/
        * to fool stupid checks for in-site links
        */
        * Forge a referer as http://[hostname:port of REQUEST]/
        * to fool stupid checks for in-site links
        */
-      if (0 != strcmpic(newval, "forge"))
-      {
-         /*
-          * Invalid choice - but forge is probably the best default.
-          */
-         log_error(LOG_LEVEL_ERROR, "Bad parameter: +referer{%s}", newval);
-      }
 
       *header = strdup("Referer: http://");
       string_append(header, csp->http->hostport);
       string_append(header, "/");
 
       *header = strdup("Referer: http://");
       string_append(header, csp->http->hostport);
       string_append(header, "/");
-      log_error(LOG_LEVEL_HEADER, "crunch+forge to %s", *header);
+      log_error(LOG_LEVEL_HEADER, "Referer forged to: %s", *header);
       
       return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
    }
 }
 
       
       return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
    }
 }
 
+/*********************************************************************
+ *
+ * Function    :  client_accept_language
+ *
+ * Description :  Handle the "Accept-Language" config setting properly.
+ *                Called from `sed'.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  header = On input, pointer to header to modify.
+ *                On output, pointer to the modified header, or NULL
+ *                to remove the header.  This function frees the
+ *                original string if necessary.
+ *
+ * Returns     :  JB_ERR_OK on success, or
+ *                JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err client_accept_language(struct client_state *csp, char **header)
+{
+   const char *newval;
+
+   /*
+    * Are we messing with the Accept-Language?
+    */
+   if ((csp->action->flags & ACTION_HIDE_ACCEPT_LANGUAGE) == 0)
+   {
+      /*I don't think so*/
+      return JB_ERR_OK;
+   }
+
+   newval = csp->action->string[ACTION_STRING_LANGUAGE];
+
+   if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
+   {
+      /*
+       * Blocking Accept-Language header
+       */
+      log_error(LOG_LEVEL_HEADER, "Crunching Accept-Language!");
+      freez(*header);
+      return JB_ERR_OK;
+   }
+   else
+   {  
+      /*
+       * Replacing Accept-Language header
+       */
+      freez(*header);
+      *header = strdup("Accept-Language: ");
+      string_append(header, newval);   
+
+      if (*header == NULL)
+      {
+         log_error(LOG_LEVEL_ERROR, " Insufficent memory. Accept-Language header crunched without replacement.");  
+      }
+      else
+      {
+         log_error(LOG_LEVEL_HEADER, "Accept-Language header crunched and replaced with: %s", *header);
+      }
+   }
+   return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
+}
+
+/*********************************************************************
+ *
+ * Function    :  crunch_client_header
+ *
+ * Description :  Crunch client header if it matches a string supplied by the
+ *                user. Called from `sed'.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  header = On input, pointer to header to modify.
+ *                On output, pointer to the modified header, or NULL
+ *                to remove the header.  This function frees the
+ *                original string if necessary.
+ *
+ * Returns     :  JB_ERR_OK on success and always succeeds
+ *
+ *********************************************************************/
+jb_err crunch_client_header(struct client_state *csp, char **header)
+{
+   const char *crunch_pattern;
+   /*Is there a header to crunch*/
+   
+   if ((csp->action->flags & ACTION_CRUNCH_CLIENT_HEADER))
+   {
+      crunch_pattern = csp->action->string[ACTION_STRING_CLIENT_HEADER];
+
+      /*Is the current header the lucky one?*/
+      if (strstr(*header, crunch_pattern))
+      {
+         log_error(LOG_LEVEL_HEADER, "Crunching client header: %s (contains: %s)", *header, crunch_pattern);  
+         freez(*header);
+      }
+   }
+   return JB_ERR_OK;
+}
+
 
 /*********************************************************************
  *
 
 /*********************************************************************
  *
@@ -1228,16 +1836,15 @@ jb_err client_uagent(struct client_state *csp, char **header)
       return JB_ERR_OK;
    }
 
       return JB_ERR_OK;
    }
 
-   log_error(LOG_LEVEL_HEADER, "modified");
-
    freez(*header);
    *header = strdup("User-Agent: ");
    string_append(header, newval);
 
    freez(*header);
    *header = strdup("User-Agent: ");
    string_append(header, newval);
 
+   log_error(LOG_LEVEL_HEADER, "Modified: %s", *header);
+
    return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
 }
 
    return (*header == NULL) ? JB_ERR_MEMORY : JB_ERR_OK;
 }
 
-
 /*********************************************************************
  *
  * Function    :  client_ua
 /*********************************************************************
  *
  * Function    :  client_ua
@@ -1259,7 +1866,7 @@ jb_err client_ua(struct client_state *csp, char **header)
 {
    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) != 0)
    {
 {
    if ((csp->action->flags & ACTION_HIDE_USER_AGENT) != 0)
    {
-      log_error(LOG_LEVEL_HEADER, "crunch!");
+      log_error(LOG_LEVEL_HEADER, "crunched User-Agent!");
       freez(*header);
    }
 
       freez(*header);
    }
 
@@ -1303,7 +1910,7 @@ jb_err client_from(struct client_state *csp, char **header)
     */
    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
    {
     */
    if ((newval == NULL) || (0 == strcmpic(newval, "block")) )
    {
-      log_error(LOG_LEVEL_HEADER, "crunch!");
+      log_error(LOG_LEVEL_HEADER, "crunched From!");
       return JB_ERR_OK;
    }
 
       return JB_ERR_OK;
    }
 
@@ -1394,7 +2001,7 @@ jb_err client_x_forwarded(struct client_state *csp, char **header)
    else
    {
       freez(*header);
    else
    {
       freez(*header);
-      log_error(LOG_LEVEL_HEADER, " crunch!");
+      log_error(LOG_LEVEL_HEADER, "crunched x-forwarded-for!");
    }
 
    return JB_ERR_OK;
    }
 
    return JB_ERR_OK;
@@ -1520,6 +2127,179 @@ jb_err client_host(struct client_state *csp, char **header)
    return JB_ERR_OK;
 }
 
    return JB_ERR_OK;
 }
 
+/*********************************************************************
+ *
+ * Function    :  client_if_modified_since
+ *
+ * Description :  Remove or modify the If-Modified-Since header.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  header = On input, pointer to header to modify.
+ *                On output, pointer to the modified header, or NULL
+ *                to remove the header.  This function frees the
+ *                original string if necessary.
+ *
+ * Returns     :  JB_ERR_OK on success, or
+ *                JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err client_if_modified_since(struct client_state *csp, char **header)
+{
+   char newheader[50];
+   struct tm *timeptr;
+   time_t tm = 0;                  
+   const char *newval;
+   time_t rtime;
+   time_t hours, minutes, seconds;
+   int negative = 0;
+   char * endptr;
+   
+   if ( 0 == strcmpic(*header, "If-Modified-Since: Wed, 08 Jun 1955 12:00:00 GMT"))
+   {
+      /* 
+       * The client got an error message because of a temporary problem,
+       * the problem is gone and the client now tries to revalidate our
+       * error message on the real server. The revalidation would always
+       * end with the transmission of the whole document and there is
+       * no need to expose the bogus If-Modified-Since header.
+       */
+      log_error(LOG_LEVEL_HEADER, "Crunching useless If-Modified-Since header.");
+      freez(*header);
+   }
+   else if (csp->action->flags & ACTION_HIDE_IF_MODIFIED_SINCE)
+   {
+      newval = csp->action->string[ACTION_STRING_IF_MODIFIED_SINCE];
+
+      if ((0 == strcmpic(newval, "block")))
+      {
+         log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
+         freez(*header);
+      }
+      else /* add random value */
+      {
+         /*
+          * tm must be initinalized to prevent segmentation faults.
+          */
+         timeptr = gmtime(&tm);
+         if (strptime(*header, "If-Modified-Since: %a, %d %b %Y %T", timeptr) == NULL)
+         {
+            log_error(LOG_LEVEL_HEADER, "Couldn't parse: %s (crunching!)", *header);
+            freez(*header);
+         }
+         else
+         {
+            rtime = strtol(newval, &endptr, 0);
+
+            log_error(LOG_LEVEL_HEADER, "Randomizing: %s (random range: %d hou%s)",
+               *header, rtime, (rtime == 1 || rtime == -1) ? "r": "rs");
+
+            rtime *= 3600;
+            rtime = random() % rtime; 
+
+            if(newval[0] == '-')
+            {
+               rtime *= -1;      
+            }
+            tm = timegm(timeptr) + rtime;
+            timeptr = gmtime(&tm);
+            strftime(newheader, sizeof(newheader), "%a, %d %b %Y %T GMT", timeptr);
+
+            freez(*header);
+            *header = strdup("If-Modified-Since: ");
+            string_append(header, newheader);
+
+            if (*header == NULL)
+            {
+               log_error(LOG_LEVEL_HEADER, " Insufficent memory, header crunched without replacement.");
+               return JB_ERR_MEMORY;  
+            }
+
+            if(LOG_LEVEL_HEADER & debug) /* Save cycles if the user isn't interested. */
+            {
+               if(rtime < 0)
+               {
+                  rtime *= -1; 
+                  negative = 1;
+               }
+               hours   = rtime / 3600 % 24;
+               minutes = rtime / 60 % 60;
+               seconds = rtime % 60;            
+
+               log_error(LOG_LEVEL_HEADER, "Randomized:  %s (%s %d hou%s %d minut%s %d second%s",
+                  *header, (negative) ? "subtracted" : "added", hours, (hours == 1) ? "r" : "rs",
+                  minutes, (minutes == 1) ? "e" : "es", seconds, (seconds == 1) ? ")" : "s)");
+            }
+         }
+      }
+   }
+
+   return JB_ERR_OK;
+}
+
+/*********************************************************************
+ *
+ * Function    :  client_if_none_match
+ *
+ * Description :  Remove the If-None-Match header.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  header = On input, pointer to header to modify.
+ *                On output, pointer to the modified header, or NULL
+ *                to remove the header.  This function frees the
+ *                original string if necessary.
+ *
+ * Returns     :  JB_ERR_OK on success, or
+ *                JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err client_if_none_match(struct client_state *csp, char **header)
+{
+   if (csp->action->flags & ACTION_CRUNCH_IF_NONE_MATCH)
+   {  
+      log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
+      freez(*header);
+   }
+
+   return JB_ERR_OK;
+}
+
+/*********************************************************************
+ *
+ * Function    :  client_x_filter
+ *
+ * Description :  Disables filtering if the client set "X-Filter: No".
+ *                Called from `sed'.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  header = On input, pointer to header to modify.
+ *                On output, pointer to the modified header, or NULL
+ *                to remove the header.  This function frees the
+ *                original string if necessary.
+ *
+ * Returns     :  JB_ERR_OK on success
+ *
+ *********************************************************************/
+jb_err client_x_filter(struct client_state *csp, char **header)
+{
+   if ( 0 == strcmpic(*header, "X-Filter: No"))
+   {
+      if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
+      {
+         log_error(LOG_LEVEL_HEADER, "force-text-mode overruled the client's request to disable filtering!");
+      }
+      else
+      {  
+         csp->content_type = CT_TABOO;
+         log_error(LOG_LEVEL_HEADER, "Disabled filter mode on behalf of the client.");
+      }
+      log_error(LOG_LEVEL_HEADER, "Crunching %s", *header);
+      freez(*header);
+   }
+   return JB_ERR_OK; 
+}
 
 /* the following functions add headers directly to the header list */
 
 
 /* the following functions add headers directly to the header list */
 
@@ -1761,6 +2541,7 @@ jb_err client_x_forwarded_adder(struct client_state *csp)
  *********************************************************************/
 jb_err connection_close_adder(struct client_state *csp)
 {
  *********************************************************************/
 jb_err connection_close_adder(struct client_state *csp)
 {
+   log_error(LOG_LEVEL_HEADER, "Adding: Connection: close");
    return enlist(csp->headers, "Connection: close");
 }
 
    return enlist(csp->headers, "Connection: close");
 }
 
index 144c222..74f60e6 100644 (file)
--- a/parsers.h
+++ b/parsers.h
@@ -1,9 +1,9 @@
 #ifndef PARSERS_H_INCLUDED
 #define PARSERS_H_INCLUDED
 #ifndef PARSERS_H_INCLUDED
 #define PARSERS_H_INCLUDED
-#define PARSERS_H_VERSION "$Id: parsers.h,v 1.26.2.1 2002/09/25 14:52:46 oes Exp $"
+#define PARSERS_H_VERSION "$Id: parsers.h,v 1.28 2006/07/18 14:48:47 david__schmidt Exp $"
 /*********************************************************************
  *
 /*********************************************************************
  *
- * File        :  $Source: /cvsroot/ijbswa/current/Attic/parsers.h,v $
+ * File        :  $Source: /cvsroot/ijbswa/current/parsers.h,v $
  *
  * Purpose     :  Declares functions to parse/crunch headers and pages.
  *                Functions declared include:
  *
  * Purpose     :  Declares functions to parse/crunch headers and pages.
  *                Functions declared include:
  *
  * Revisions   :
  *    $Log: parsers.h,v $
  *
  * Revisions   :
  *    $Log: parsers.h,v $
+ *    Revision 1.28  2006/07/18 14:48:47  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
  *    Revision 1.26.2.1  2002/09/25 14:52:46  oes
  *    Added basic support for OPTIONS and TRACE HTTP methods:
  *     - New parser function client_max_forwards which decrements
  *    Revision 1.26.2.1  2002/09/25 14:52:46  oes
  *    Added basic support for OPTIONS and TRACE HTTP methods:
  *     - New parser function client_max_forwards which decrements
@@ -188,6 +192,7 @@ extern "C" {
 
 extern const struct parsers client_patterns[];
 extern const struct parsers server_patterns[];
 
 extern const struct parsers client_patterns[];
 extern const struct parsers server_patterns[];
+extern const struct parsers server_patterns_light[];
 
 extern const add_header_func_ptr add_client_headers[];
 extern const add_header_func_ptr add_server_headers[];
 
 extern const add_header_func_ptr add_client_headers[];
 extern const add_header_func_ptr add_server_headers[];
@@ -197,6 +202,7 @@ extern jb_err add_to_iob(struct client_state *csp, char *buf, int n);
 extern char *get_header(struct client_state *csp);
 extern char *get_header_value(const struct list *header_list, const char *header_name);
 extern char *sed(const struct parsers pats[], const add_header_func_ptr more_headers[], struct client_state *csp);
 extern char *get_header(struct client_state *csp);
 extern char *get_header_value(const struct list *header_list, const char *header_name);
 extern char *sed(const struct parsers pats[], const add_header_func_ptr more_headers[], struct client_state *csp);
+extern void get_http_time(int time_offset, char *buf);
 
 extern jb_err crumble                (struct client_state *csp, char **header);
 extern jb_err client_referrer        (struct client_state *csp, char **header);
 
 extern jb_err crumble                (struct client_state *csp, char **header);
 extern jb_err client_referrer        (struct client_state *csp, char **header);
@@ -209,6 +215,12 @@ extern jb_err client_accept_encoding (struct client_state *csp, char **header);
 extern jb_err client_te              (struct client_state *csp, char **header);
 extern jb_err client_max_forwards    (struct client_state *csp, char **header);
 extern jb_err client_host(struct client_state *csp, char **header);
 extern jb_err client_te              (struct client_state *csp, char **header);
 extern jb_err client_max_forwards    (struct client_state *csp, char **header);
 extern jb_err client_host(struct client_state *csp, char **header);
+extern jb_err client_if_modified_since(struct client_state *csp, char **header);
+extern jb_err client_accept_language  (struct client_state *csp, char **header);
+extern jb_err client_if_none_match    (struct client_state *csp, char **header);
+extern jb_err crunch_client_header    (struct client_state *csp, char **header);
+extern jb_err filter_header           (struct client_state *csp, char **header);
+extern jb_err client_x_filter         (struct client_state *csp, char **header);
 
 
 extern jb_err client_host_adder           (struct client_state *csp);
 
 
 extern jb_err client_host_adder           (struct client_state *csp);
@@ -226,6 +238,9 @@ extern jb_err server_content_md5     (struct client_state *csp, char **header);
 extern jb_err server_content_encoding(struct client_state *csp, char **header);
 extern jb_err server_transfer_coding (struct client_state *csp, char **header);
 extern jb_err server_http            (struct client_state *csp, char **header);
 extern jb_err server_content_encoding(struct client_state *csp, char **header);
 extern jb_err server_transfer_coding (struct client_state *csp, char **header);
 extern jb_err server_http            (struct client_state *csp, char **header);
+extern jb_err crunch_server_header   (struct client_state *csp, char **header);
+extern jb_err server_last_modified   (struct client_state *csp, char **header);
+extern jb_err server_content_disposition(struct client_state *csp, char **header);
 
 #ifdef FEATURE_FORCE_LOAD
 extern int strclean(const char *string, const char *substring);
 
 #ifdef FEATURE_FORCE_LOAD
 extern int strclean(const char *string, const char *substring);
@@ -235,6 +250,8 @@ extern int strclean(const char *string, const char *substring);
 extern const char parsers_rcs[];
 extern const char parsers_h_rcs[];
 
 extern const char parsers_rcs[];
 extern const char parsers_h_rcs[];
 
+extern int debug;
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
index e957048..aaaeed2 100644 (file)
--- a/project.h
+++ b/project.h
@@ -1,10 +1,10 @@
 #ifndef PROJECT_H_INCLUDED
 #define PROJECT_H_INCLUDED
 /** Version string. */
 #ifndef PROJECT_H_INCLUDED
 #define PROJECT_H_INCLUDED
 /** Version string. */
-#define PROJECT_H_VERSION "$Id: project.h,v 1.72.2.7 2006/01/29 23:10:56 david__schmidt Exp $"
+#define PROJECT_H_VERSION "$Id: project.h,v 1.74 2006/07/18 14:48:47 david__schmidt Exp $"
 /*********************************************************************
  *
 /*********************************************************************
  *
- * File        :  $Source: /cvsroot/ijbswa/current/Attic/project.h,v $
+ * File        :  $Source: /cvsroot/ijbswa/current/project.h,v $
  *
  * Purpose     :  Defines data structures which are widely used in the
  *                project.  Does not define any variables or functions
  *
  * Purpose     :  Defines data structures which are widely used in the
  *                project.  Does not define any variables or functions
  *
  * Revisions   :
  *    $Log: project.h,v $
  *
  * Revisions   :
  *    $Log: project.h,v $
+ *    Revision 1.74  2006/07/18 14:48:47  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
  *    Revision 1.72.2.7  2006/01/29 23:10:56  david__schmidt
  *    Multiple filter file support
  *
  *    Revision 1.72.2.7  2006/01/29 23:10:56  david__schmidt
  *    Multiple filter file support
  *
@@ -880,19 +884,74 @@ struct iob
 #define ACTION_JPEG_INSPECT    0x00020000UL
 
 /** Action string index: How to deanimate GIFs */
 #define ACTION_JPEG_INSPECT    0x00020000UL
 
 /** Action string index: How to deanimate GIFs */
-#define ACTION_STRING_DEANIMATE     0
+#define ACTION_STRING_DEANIMATE             0
 /** Action string index: Replacement for "From:" header */
 /** Action string index: Replacement for "From:" header */
-#define ACTION_STRING_FROM          1
+#define ACTION_STRING_FROM                  1
 /** Action string index: How to block images */
 /** Action string index: How to block images */
-#define ACTION_STRING_IMAGE_BLOCKER 2
+#define ACTION_STRING_IMAGE_BLOCKER         2
 /** Action string index: Replacement for "Referer:" header */
 /** Action string index: Replacement for "Referer:" header */
-#define ACTION_STRING_REFERER       3
+#define ACTION_STRING_REFERER               3
 /** Action string index: Replacement for "User-Agent:" header */
 /** Action string index: Replacement for "User-Agent:" header */
-#define ACTION_STRING_USER_AGENT    4
+#define ACTION_STRING_USER_AGENT            4
 /** Action string index: Legal CONNECT ports. */
 /** Action string index: Legal CONNECT ports. */
-#define ACTION_STRING_LIMIT_CONNECT 5
+#define ACTION_STRING_LIMIT_CONNECT         5
+/** Action string index: Server headers containing this pattern are crunched*/
+#define ACTION_STRING_SERVER_HEADER         6
+/** Action string index: Client headers containing this pattern are crunched*/
+#define ACTION_STRING_CLIENT_HEADER         7
+/** Action string index: Replacement for the "Accept-Language:" header*/
+#define ACTION_STRING_LANGUAGE              8
+/** Action string index: Replacement for the "Content-Type:" header*/
+#define ACTION_STRING_CONTENT_TYPE          9
+/** Action string index: Replacement for the "content-dispostion:" header*/
+#define ACTION_STRING_CONTENT_DISPOSITION  10
+/** Action string index: Replacement for the "If-Modified-Since:" header*/
+#define ACTION_STRING_IF_MODIFIED_SINCE    11
+/** Action string index: Replacement for the "Last-Modified:" header. */
+#define ACTION_STRING_LAST_MODIFIED        12
+/** Action string index: Redirect URL */
+#define ACTION_STRING_REDIRECT             13
+/** Action string index: Decode before redirect? */
+#define ACTION_STRING_FAST_REDIRECTS       14
 /** Number of string actions. */
 /** Number of string actions. */
-#define ACTION_STRING_COUNT         6
+#define ACTION_STRING_COUNT                15
+
+/*
+ * These defines really belong a few lines higher,
+ * but moving them down here makes this patch apply
+ * against Privoxy 3.0.3 as well.
+ */
+/** Action bitmap: Crunch or modify "if-modified-since" header. */
+#define ACTION_HIDE_IF_MODIFIED_SINCE                0x00040000UL
+/** Action bitmap: Overwrite Content-Type header. */
+#define ACTION_CONTENT_TYPE_OVERWRITE                0x00080000UL
+/** Action bitmap: Crunch specified server header. */
+#define ACTION_CRUNCH_SERVER_HEADER                  0x00100000UL
+/** Action bitmap: Crunch specified client header */
+#define ACTION_CRUNCH_CLIENT_HEADER                  0x00200000UL
+/** Action bitmap: Enable text mode by force */
+#define ACTION_FORCE_TEXT_MODE                       0x00400000UL
+/** Action bitmap: Enable text mode by force */
+#define ACTION_CRUNCH_IF_NONE_MATCH                  0x00800000UL
+/** Action bitmap: Enable content-dispostion crunching */
+#define ACTION_HIDE_CONTENT_DISPOSITION              0x01000000UL
+/** Action bitmap: Replace or block Last-Modified header */
+#define ACTION_OVERWRITE_LAST_MODIFIED               0x02000000UL
+/** Action bitmap: Replace or block Accept-Language header */
+#define ACTION_HIDE_ACCEPT_LANGUAGE                  0x04000000UL
+/** Action bitmap: Block as empty document */
+#define  ACTION_HANDLE_AS_EMPTY_DOCUMENT             0x08000000UL
+/** Action bitmap: Redirect request. */
+#define  ACTION_REDIRECT                             0x10000000UL
+/** Action bitmap: Answer blocked Connects verbosely */
+#define ACTION_TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS  0x20000000UL
+/** Action bitmap: Filter headers with pcre */
+#define ACTION_FILTER_HEADERS                        0x40000000UL
+
+
+/*To make the ugly hack in sed easier to understand*/
+#define CHECK_EVERY_HEADER_REMAINING 0
+
 
 /** Index into current_action_spec::multi[] for headers to add. */
 #define ACTION_MULTI_ADD_HEADER     0
 
 /** Index into current_action_spec::multi[] for headers to add. */
 #define ACTION_MULTI_ADD_HEADER     0
index cbb1672..c17dbe8 100644 (file)
@@ -2,7 +2,7 @@
 # 
 #  File        :  $Source: /cvsroot/ijbswa/current/standard.action,v $
 # 
 # 
 #  File        :  $Source: /cvsroot/ijbswa/current/standard.action,v $
 # 
-#  $Id: standard.action,v 1.2.2.9 2003/04/01 09:49:50 oes Exp $
+#  $Id: standard.action,v 1.7 2006/07/18 14:48:47 david__schmidt Exp $
 #
 #  Purpose     :  Provide prefedined sets of actions, see
 #                 http://www.privoxy.org/user-manual/actions-file.html
 #
 #  Purpose     :  Provide prefedined sets of actions, see
 #                 http://www.privoxy.org/user-manual/actions-file.html
@@ -129,7 +129,7 @@ standard.Medium
 +crunch-incoming-cookies \
 +deanimate-gifs{last} \
 -downgrade-http-version \
 +crunch-incoming-cookies \
 +deanimate-gifs{last} \
 -downgrade-http-version \
-+fast-redirects \
++fast-redirects{check-decoded-url} \
 +filter{js-annoyances} \
 -filter{js-events} \
 +filter{html-annoyances} \
 +filter{js-annoyances} \
 -filter{js-events} \
 +filter{html-annoyances} \
index f97c33a..6bc760c 100644 (file)
 #
 # Revisions   :
 #    $Log: edit-actions-for-url,v $
 #
 # Revisions   :
 #    $Log: edit-actions-for-url,v $
+#    Revision 1.34  2006/07/18 14:49:13  david__schmidt
+#    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+#    with what was really the latest development (the v_3_0_branch branch)
+#
 #    Revision 1.29.2.5  2004/10/05 03:17:26  david__schmidt
 #    Typo: inspect_jpegs, not inspect-jpegs in form
 #
 #    Revision 1.29.2.5  2004/10/05 03:17:26  david__schmidt
 #    Typo: inspect_jpegs, not inspect-jpegs in form
 #
@@ -536,7 +540,8 @@ function show_send_wafer_opts(tf)
         name="downgrade_http_version" value="N" @downgrade-http-version-n@></td>
       <td class="noc1" align="center" valign="middle"><input type="radio"
         name="downgrade_http_version" value="X" @downgrade-http-version-x@></td>
         name="downgrade_http_version" value="N" @downgrade-http-version-n@></td>
       <td class="noc1" align="center" valign="middle"><input type="radio"
         name="downgrade_http_version" value="X" @downgrade-http-version-x@></td>
-      <td class="action"><a href="@user-manual@@actions-help-prefix@DOWNGRADE-HTTP-VERSION">downgrade-http-version</td>
+      <td class="action"><a href="@user-manual@@actions-help-prefix@DOWNGRADE-HTTP-VERSION"
+       >downgrade-http-version</a></td>
       <td>Change HTTP/1.1 requests to HTTP/1.0.  Only change if you know
         what you're doing!</td>
     </tr>
       <td>Change HTTP/1.1 requests to HTTP/1.0.  Only change if you know
         what you're doing!</td>
     </tr>
@@ -553,7 +558,22 @@ function show_send_wafer_opts(tf)
       <td class="action"><a href="@user-manual@@actions-help-prefix@FAST-REDIRECTS">fast-redirects</a></td>
       <td>Bypass some click-tracking URLs.</td>
     </tr>
       <td class="action"><a href="@user-manual@@actions-help-prefix@FAST-REDIRECTS">fast-redirects</a></td>
       <td>Bypass some click-tracking URLs.</td>
     </tr>
-
+<!--TODO: Document-->
+    <tr class="bg1" align="left" valign="top" id="fast-redirects_opts">
+      <td class="en1">&nbsp;</td>
+      <td class="dis1">&nbsp;</td>
+      <td class="noc1">&nbsp;</td>
+      <td>&nbsp;</td>
+      <td>
+       <input type="radio" name="fast_redirects_mode"
+        value="simple-check" id="fast_redirects_simple_check" @fast-redirects-param-simple-check@><label
+        for="fast_redirects_simple_check">Simple check.</label>
+       <br>
+       <input type="radio" name="fast_redirects_mode" value="check-decoded-url" 
+        id="fast_redirects_check_decoded_url" @fast-redirects-param-check-decoded-url@><label
+        for="fast_redirects_check_decoded_url">Decode URL before checking.</label></td>
+    </tr>
+<!--TODO: Document-->
     <tr class="bg1" align="left" valign="top">
       <td class="en1">&nbsp;</td>
       <td class="dis1" align="center" valign="middle"><input type="radio"
     <tr class="bg1" align="left" valign="top">
       <td class="en1">&nbsp;</td>
       <td class="dis1" align="center" valign="middle"><input type="radio"
@@ -594,6 +614,340 @@ function show_send_wafer_opts(tf)
       <td class="action"><a href="@user-manual@@actions-help-prefix@HIDE-FORWARDED-FOR-HEADERS">hide-forwarded-for-headers</a></td>
       <td>Block any existing X-Forwarded-for header, and do not add a new one.</td>
     </tr>
       <td class="action"><a href="@user-manual@@actions-help-prefix@HIDE-FORWARDED-FOR-HEADERS">hide-forwarded-for-headers</a></td>
       <td>Block any existing X-Forwarded-for header, and do not add a new one.</td>
     </tr>
+<!--TODO: Document-->
+   <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="hide_if_modified_since" value="Y" @hide-if-modified-since-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="hide_if_modified_since" value="N" @hide-if-modified-since-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="hide_if_modified_since" value="X" @hide-if-modified-since-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@HIDE_IF_MODIFIED_SINCE">-->hide-if-modified-since<!--</a>--></td>
+      <td>Remove or randomize If-Modified-Since header.</td>
+    </tr>
+    <tr class="bg1" align="left" valign="top" id="hide_if-modifed-since_opts">
+      <td class="en1">&nbsp;</td>
+      <td class="dis1">&nbsp;</td>
+      <td class="noc1">&nbsp;</td>
+      <td>&nbsp;</td>
+      <td><input type="radio" name="hide_if_modified_since_mode" value="block"
+        onclick="hide_if_modified_since_param_disable(true)"
+        @hide-if-modified-since-param-block@ id="hide_if_modified_since_mode_block"><label
+        for="hide_if_modified_since_mode_block">Remove without replacement.</label>
+        Useful for filter testing.<br>
+        <input type="radio" name="hide_if_modified_since_mode" value="CUSTOM"
+        onclick="hide_if_modified_since_param_disable(true)"
+        @hide-if-modified-since-param-custom@ id="hide_if_modified_since_mode_set"><label
+        for="hide_if_modified_since_mode_set">Add random amount of seconds in the range of</label>
+        <input type="text" name="hide_if_modified_since_param" 
+        id="hide_if_modified_since_param" size="4"
+        value="@hide-if-modified-since-param@"> hour(s).
+        To appreciate this option a small amount of paranoia is required,
+        but at least in theory the If-Modified-Since header could be used
+       to keep track of your visits.
+     </td>
+    </tr>
+   <!--   -->
+    <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="overwrite_last_modified" value="Y" @overwrite-last-modified-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="overwrite_last_modified" value="N" @overwrite-last-modified-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="overwrite_last_modified" value="X" @overwrite-last-modified-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@OVERWRITE_LAST_MODIFIED">-->overwrite-last-modified<!--</a>--></td>
+      <td>Remove or randomize If-Modified-Since header.</td>
+    </tr>
+    <tr class="bg1" align="left" valign="top" id="overwrite_last_modified_opts">
+      <td class="en1">&nbsp;</td>
+      <td class="dis1">&nbsp;</td>
+      <td class="noc1">&nbsp;</td>
+      <td>&nbsp;</td>
+      <td><input type="radio" name="overwrite_last_modified_mode" value="block"
+        onclick="overwrite_last_modified_param_disable(true)"
+        @overwrite-last-modified-param-block@ id="overwrite_last_modified_mode_block"><label
+        for="overwrite_last_modified_mode_block">Remove without replacement.</label>
+        <strong>Useless</strong>.<br>
+        <input type="radio" name="overwrite_last_modified_mode" value="reset-to-request-time"
+        onclick="overwrite_last_modified_param_disable(true)"
+        @overwrite-last-modified-param-reset-to-request-time@
+         id="overwrite_last_modified_mode_reset-to-request-time"><label
+        for="overwrite_last_modified_mode_reset-to-request-time">Reset to the time of the request.</label>
+        <br>
+        <input type="radio" name="overwrite_last_modified_mode" value="randomize"
+        onclick="overwrite_last_modified_param_disable(true)"
+        @overwrite-last-modified-param-randomize@
+         id="overwrite_last_modified_mode_randomize"><label
+        for="overwrite_last_modified_mode_randomize">Overwrite with random value
+        between original header value and the time of the request.</label>
+        <br>
+        <!--
+        <input type="radio" name="overwrite_last_modified_mode" value="CUSTOM"
+        onclick="overwrite_last_modified_param_disable(true)"
+        @overwrite-last-modified-param-custom@ id="overwrite_last_modified_mode_set"><label
+        for="overwrite_last_modified_mode_set">Add random amount of seconds in the range of</label>
+        <input type="text" name="overwrite_last_modified_param" 
+        id="overwrite_last_modified_param" size="4"
+        value="@overwrite-last-modified-param@"> hour(s).
+        To appreciate this option a small amount of paranoia is required,
+        but at least in theory the If-Modified-Since header could be used
+       to keep track of your visits.-->
+     </td>
+    </tr>
+    <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="crunch_if_none_match" value="Y" @crunch-if-none-match-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="crunch_if_none_match" value="N" @crunch-if-none-match-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="crunch_if_none_match" value="X" @crunch-if-none-match-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@CRUNCH_IF_NONE_MATCH">-->crunch-if-none-match<!--</a>--></td>
+      <td>Remove If-None-Match header to circumvent browser cache. Useful for filter testing,
+         but hurts performance.</td>
+    </tr>
+   <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="content_type_overwrite" value="Y" @content-type-overwrite-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="content_type_overwrite" value="N" @content-type-overwrite-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="content_type_overwrite" value="X" @content-type-overwrite-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@CONTENT_TYPE_OVERWRITE">-->content-type-overwrite<!--</a>--></td>
+      <td>Replace Content-Type header. Useful to let the browser render broken
+         <abbr title="EXtensible HyperText Markup Language">XHTML</abbr> as broken
+         <abbr title="HyperText Markup Language">HTML</abbr>. By default it only applies to
+        text documents, if you know what you're doing you
+        can enable force-text-mode to modify binary content types as well.</td>
+    </tr>
+    <tr class="bg1" align="left" valign="top" id="content_type_overwrite_opts">
+      <td class="en1">&nbsp;</td>
+      <td class="dis1">&nbsp;</td>
+      <td class="noc1">&nbsp;</td>
+      <td>&nbsp;</td>
+      <td>New Content-Type:<br>
+        <input type="text" name="content_type_overwrite_mode" size="40"
+        value="@content-type-overwrite-param@"></td>
+    </tr>
+
+   <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="force_text_mode" value="Y" @force-text-mode-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="force_text_mode" value="N" @force-text-mode-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="force_text_mode" value="X" @force-text-mode-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@FORCE_TEXT_MODE">-->force-text-mode<!--</a>--></td>
+      <td>
+       Enable filtering on documents whose Content-Type wasn't recognized as text.
+       Do think twice, nothing is alright.
+     </td>
+    </tr>
+   <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="handle_as_empty_document" value="Y" @handle-as-empty-document-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="handle_as_empty_document" value="N" @handle-as-empty-document-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="handle_as_empty_document" value="X" @handle-as-empty-document-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@HANDLE_AS_EMPTY_DOCUMENT">-->handle-as-empty-document<!--</a>--></td>
+      <td>
+       Block with an <em>empty</em> document instead of an Image or HTML message.
+       The <em>empty</em> document contains only a space and can safely be parsed
+       as JavaScript or Style Sheet. Use content-type-overwrite to specify the
+       Content-Type, default is <q>test/html</q>.
+     </td>
+    </tr>
+   <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="treat_forbidden_connects_like_blocks" value="Y" @treat-forbidden-connects-like-blocks-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="treat_forbidden_connects_like_blocks" value="N" @treat-forbidden-connects-like-blocks-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="treat_forbidden_connects_like_blocks" value="X" @treat-forbidden-connects-like-blocks-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@TREAT_FORBIDDEN_CONNECTS_LIKE_BLOCKS">-->treat-forbidden-connects-like-blocks<!--</a>--></td>
+      <td>
+       Answer forbidden Connect requests with the usual block message.
+       May be a protocol violation, but makes unblocking easier.
+     </td>
+    </tr>
+<!--crunch-client-header-->
+    <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="crunch_client_header" id="crunch_client_header_y" value="Y" @crunch-client-header-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="crunch_client_header" value="N" @crunch-client-header-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="crunch_client_header" value="X" @crunch-client-header-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@CRUNCH-CLIENT-HEADER">-->crunch-client-header<!--</a>--></td>
+      <td>Remove header(s) matching the supplied pattern. Together with add-header you can
+          build a custom header replacement action.</td>
+    </tr>
+    <tr class="bg1" align="left" valign="top" id="crunch_client_header_opts">
+      <td class="en1">&nbsp;</td>
+      <td class="dis1">&nbsp;</td>
+      <td class="noc1">&nbsp;</td>
+      <td>&nbsp;</td>
+      <td>Header string to suppress:<br>
+        <input type="text" name="crunch_client_header_mode" size="40"
+        value="@crunch-client-header-param@"></td>
+    </tr>
+<!--/crunch-client-header-->
+<!--crunch-server-header-->
+    <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="crunch_server_header" id="crunch_server_header_y" value="Y" @crunch-server-header-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="crunch_server_header" value="N" @crunch-server-header-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="crunch_server_header" value="X" @crunch-server-header-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@CRUNCH-SERVER-HEADER">-->crunch-server-header<!--</a>--></td>
+      <td>Remove server header(s) matching the supplied pattern.</td>
+    </tr>
+    <tr class="bg1" align="left" valign="top" id="crunch_server_header_opts">
+      <td class="en1">&nbsp;</td>
+      <td class="dis1">&nbsp;</td>
+      <td class="noc1">&nbsp;</td>
+      <td>&nbsp;</td>
+      <td>Header string to suppress:<br>
+        <input type="text" name="crunch_server_header_mode" size="40"
+        value="@crunch-server-header-param@"></td>
+    </tr>
+<!--/crunch-server-header-->
+    <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="hide_accept_language" id="hide_accept_language_y" value="Y" @hide-accept-language-y@
+        onclick="show_hide_accept_language_opts(true)"></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="hide_accept_language" id="hide_accept_language_n" value="N" @hide-accept-language-n@
+        onclick="show_hide_accept_language_opts(false)"></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="hide_accept_language" id="hide_accept_language_x" value="X" @hide-accept-language-x@
+        onclick="show_hide_accept_language_opts(false)"
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@HIDE-ACCEPT-LANGUAGE">-->hide-accept-language<!--</a>--></td>
+      <td>Pretend to have different language settings. (Makes a fake User-Agent more believable,
+          but can lead to foreign content.)</td>
+    </tr>
+    <tr class="bg1" align="left" valign="top" id="hide_accept-language_opts">
+      <td class="en1">&nbsp;</td>
+      <td class="dis1">&nbsp;</td>
+      <td class="noc1">&nbsp;</td>
+      <td>&nbsp;</td>
+      <td><input type="radio" name="hide_accept_language_mode" value="block"
+        onclick="hide_accept_language_param_disable(true)"
+        @hide-accept-language-param-block@ id="hide_accept_language_mode_block"><label
+        for="hide_accept_language_mode_block">Remove Accept-Language header without replacement.</label>
+        This isn't the option you're looking for.<br>
+        <input type="radio" name="hide_accept_language_mode" value="CUSTOM"
+        onclick="hide_accept_language_param_disable(false)"
+        @hide-accept-language-param-custom@ id="hide_accept_language_mode_set"><label
+        for="hide_accept_language_mode_set">Fake these language settings:</label><br>
+        <input type="text" name="hide_accept_language_param" 
+        id="hide_accept_language_param" size="40"
+        value="@hide-accept-language-param@"><br>
+     </td>
+<!--content-dispostion-->
+    </tr>    <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="hide_content_disposition" id="hide_content_disposition_y" value="Y" @hide-content-disposition-y@
+        onclick="show_hide_content_disposition_opts(true)"></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="hide_content_disposition" id="hide_content_disposition_n" value="N" @hide-content-disposition-n@
+        onclick="show_hide_content_disposition_opts(false)"></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="hide_content_disposition" id="hide_content_disposition_x" value="X" @hide-content-disposition-x@
+        onclick="show_hide_content_disposition_opts(false)"
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@HIDE-CONTENT-DISPOSITION">-->hide-content-disposition<!--</a>--></td>
+      <td>Block or overwrite the content-disposition header. Useful to view a document inside the browser,
+       even if you were supposed to save it first, or to change the suggested file name.</td>
+    </tr>
+    <tr class="bg1" align="left" valign="top" id="hide_content_disposition_opts">
+      <td class="en1">&nbsp;</td>
+      <td class="dis1">&nbsp;</td>
+      <td class="noc1">&nbsp;</td>
+      <td>&nbsp;</td>
+      <td><input type="radio" name="hide_content_disposition_mode" value="block"
+        onclick="hide_content_disposition_param_disable(true)"
+        @hide-content-disposition-param-block@ id="hide_content_disposition_mode_block"><label
+        for="hide_content_disposition_mode_block">Remove content-disposition header without replacement.</label>
+        <br>
+        <input type="radio" name="hide_content_disposition_mode" value="CUSTOM"
+        onclick="hide_content_disposition_param_disable(false)"
+        @hide-content-disposition-param-custom@ id="hide_content_disposition_mode_set"><label
+        for="hide_content_disposition_mode_set">Overwrite content-dispostion header with:</label><br>
+        <input type="text" name="hide_content_disposition_param" 
+        id="hide_content_disposition_param" size="40"
+        value="@hide-content-disposition-param@"><br>
+     </td>
+    </tr>
+   <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="redirect" value="Y" @redirect-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="redirect" value="N" @redirect-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="redirect" value="X" @redirect-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@REDIRECT">-->redirect<!--</a>--></td>
+      <td>Redirect to another address.
+    </tr>
+    <tr class="bg1" align="left" valign="top" id="redirect_opts">
+      <td class="en1">&nbsp;</td>
+      <td class="dis1">&nbsp;</td>
+      <td class="noc1">&nbsp;</td>
+      <td>&nbsp;</td>
+      <td>Address to redirect to:<br>
+        <input type="text" name="redirect_mode" size="40"
+        value="@redirect-param@"></td>
+    </tr>
+
+    <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="filter_headers" value="Y" @filter-headers-y@
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="filter_headers" value="N" @filter-headers-n@
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="filter_headers" value="X" @filter-headers-x@
+        ></td>
+      <td class="action"><!--<a href="@user-manual@@actions-help-prefix@FILTER-HEADERS">-->filter-headers<!--</a>--></td>
+      <td>Apply filters to the headers as well. <strong>Only enable this if you know what you're doing.</strong></td>
+    </tr>
+
+
+
+<!--TODO: Document-->
     <tr class="bg1" align="left" valign="top">
       <td class="en1" align="center" valign="middle"><input type="radio"
         name="hide_from_header" id="hide_from_header_y" value="Y" @hide-from-header-y@
     <tr class="bg1" align="left" valign="top">
       <td class="en1" align="center" valign="middle"><input type="radio"
         name="hide_from_header" id="hide_from_header_y" value="Y" @hide-from-header-y@
@@ -660,7 +1014,15 @@ function show_send_wafer_opts(tf)
         for="hide_referrer_mode_set">Fake as this web address:</label><br>
         <input type="text" name="hide_referrer_param" 
         id="hide_referrer_param" size="40"
         for="hide_referrer_mode_set">Fake as this web address:</label><br>
         <input type="text" name="hide_referrer_param" 
         id="hide_referrer_param" size="40"
-        value="@hide-referrer-param@"></td>
+        value="@hide-referrer-param@"><br>
+<!--TODO: Document-->
+        <input type="radio" name="hide_referrer_mode" value="conditional-block"
+        onclick="hide_referrer_param_disable(true)"
+        @hide-referrer-param-conditional-block@ id="hide_referrer_mode_conditional-block"><label
+        for="hide_referrer_mode_conditional-block">Remove completely if host has changed,
+        but don't touch in-site referrers.
+        Less suspicious than the options above.</label><br></td>
+<!--TODO: Document-->
     </tr>
     <tr class="bg1" align="left" valign="top">
       <td class="en1" align="center" valign="middle"><input type="radio"
     </tr>
     <tr class="bg1" align="left" valign="top">
       <td class="en1" align="center" valign="middle"><input type="radio"
@@ -695,7 +1057,7 @@ function show_send_wafer_opts(tf)
       <td class="noc1" align="center" valign="middle"><input type="radio"
         name="kill_popups" value="X" @kill-popups-x@
         ></td>
       <td class="noc1" align="center" valign="middle"><input type="radio"
         name="kill_popups" value="X" @kill-popups-x@
         ></td>
-      <td class="action"><a href="@user-manual@@actions-help-prefix@KILL-POPUPS">kill-popups</td>
+      <td class="action"><a href="@user-manual@@actions-help-prefix@KILL-POPUPS">kill-popups</a></td>
       <td>Filter the website through a built-in filter to disable many JavaScript
         pop-up windows.</td>
     </tr>
       <td>Filter the website through a built-in filter to disable many JavaScript
         pop-up windows.</td>
     </tr>