Add the new action suppress-tag{}
authorMaxim Antonov <mantonov@gmail.com>
Tue, 13 Oct 2020 10:28:52 +0000 (17:28 +0700)
committerFabian Keil <fk@fabiankeil.de>
Mon, 14 Dec 2020 09:10:28 +0000 (10:10 +0100)
Usage:
in user.filters:
--begin--
CLIENT-HEADER-TAGGER: maximum-url-length Tag for URLS longer than 600 characters.

s@(^GET\s+\/.{600,}\s+HTTP\/\d\.\d\s*$)@MAXIMUM-URL-LENGTH@i
--end--

in user.actions:
--begin--
{+client-header-tagger{maximum-url-length}}
/

{+block{Maximum URL length of 600 bytes reached.}}
TAG:^MAXIMUM-URL-LENGTH

{+suppress-tag{MAXIMUM-URL-LENGTH}}
.google.*
 --end--

will block all URLs with length > 600 bytes except for google.

Currently the online action editor supports modification/removal of any
number of existing tags and the creation of a single suppress tag per
one submit. The submit scheme that is used is similar to the existing
filter one but:

1. It uses 'string_filter[_r|_n|_o|_t][hex_index]' keys for existing
string filter values (id/name(value)/old_name(old value)/filter type)
and 'new_string_filter[_r|_n|_t][hex_index]' for new string filter
values. 'String filter values' here are parameters of the suppress-tag
action that are simple strings rather than parameters of, for example,
the client-header-tagger action that must be described in filters file.

2. String filter values are accessed by the value rather by the
index. Indexes must start from 0 and when there is no key with index+1
in parameters - we've done with existing or new string filters
processing.

Possible further improvements:

1. Extend suppress-tag action edit scheme to add-header action
   edit that is not supported now.
2. If needed, multiple suppress-tag addition can be added with
   some browser JS code.

Sponsored by: Robert Klemme

actionlist.h
actions.c
cgiedit.c
doc/source/user-manual.sgml
parsers.c
project.h
templates/edit-actions-for-url
templates/edit-actions-for-url-string-filter [new file with mode: 0644]

index 5f94ea4..6129bb0 100644 (file)
@@ -129,6 +129,7 @@ DEFINE_ACTION_STRING     ("set-image-blocker",          ACTION_IMAGE_BLOCKER,
 DEFINE_CGI_PARAM_RADIO   ("set-image-blocker",          ACTION_IMAGE_BLOCKER,   ACTION_STRING_IMAGE_BLOCKER, "pattern", 1)
 DEFINE_CGI_PARAM_RADIO   ("set-image-blocker",          ACTION_IMAGE_BLOCKER,   ACTION_STRING_IMAGE_BLOCKER, "blank", 0)
 DEFINE_CGI_PARAM_CUSTOM  ("set-image-blocker",          ACTION_IMAGE_BLOCKER,   ACTION_STRING_IMAGE_BLOCKER,  CGI_PREFIX "send-banner?type=pattern")
+DEFINE_ACTION_MULTI      ("suppress-tag",               ACTION_MULTI_SUPPRESS_TAG)
 
 #if DEFINE_ACTION_ALIAS
 
index 775350a..7905231 100644 (file)
--- a/actions.c
+++ b/actions.c
@@ -1115,6 +1115,8 @@ static const char *filter_type_to_string(enum filter_type filter_type)
    case FT_EXTERNAL_CONTENT_FILTER:
       return "external content filter";
 #endif
+   case FT_SUPPRESS_TAG:
+      return "suppress tag filter";
    case FT_INVALID_FILTER:
       return "invalid filter type";
    }
index 64c8dc1..af22d07 100644 (file)
--- a/cgiedit.c
+++ b/cgiedit.c
@@ -248,6 +248,12 @@ static const struct filter_type_info filter_type_info[] =
       "E", "EXTERNAL-CONTENT-FILTER"
    },
 #endif
+   {
+      ACTION_MULTI_SUPPRESS_TAG,
+      "suppress-tag-params", "suppress-tag",
+      "suppress-tag-all", "suppress_tag_all",
+      "U", "SUPPRESS-TAG"
+   },
 };
 
 /* FIXME: Following non-static functions should be prototyped in .h or made static */
@@ -306,6 +312,10 @@ static jb_err actions_to_radio(struct map * exports,
                                const struct action_spec *action);
 static jb_err actions_from_radio(const struct map * parameters,
                                  struct action_spec *action);
+static jb_err action_render_string_filters_template(struct map * exports,
+                                       const struct action_spec *action,
+                                       const char* flter_template,
+                                       const struct filter_type_info *type);
 
 
 static jb_err map_copy_parameter_html(struct map *out,
@@ -2685,6 +2695,7 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp,
                                 const struct map *parameters)
 {
    struct map * exports;
+   char *filter_template;
    unsigned sectionid;
    struct editable_file * file;
    struct file_line * cur_line;
@@ -2734,12 +2745,24 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp,
       return JB_ERR_MEMORY;
    }
 
+   err = template_load(csp, &filter_template, "edit-actions-for-url-string-filter", 0);
+   if (err)
+   {
+       edit_free_file(file);
+       free_map(exports);
+       return cgi_error_no_template(csp, rsp, "edit-actions-for-url-string-filter");
+   }
+
    err = map(exports, "f", 1, stringify(file->identifier), 0);
    if (!err) err = map(exports, "v", 1, file->version_str, 1);
    if (!err) err = map(exports, "s", 1, url_encode(lookup(parameters, "s")), 0);
 
    if (!err) err = actions_to_radio(exports, cur_line->data.action);
 
+   if (!err) err = action_render_string_filters_template(exports, cur_line->data.action, filter_template,
+                                               &filter_type_info[FT_SUPPRESS_TAG]);
+   freez(filter_template);
+
    /*
     * XXX: Some browsers (at least IE6 and IE7) have an artificial URL
     * length limitation and ignore clicks on the Submit buttons if
@@ -2802,7 +2825,6 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp,
       /*
        * List available filters and their settings.
        */
-      char *filter_template;
       int filter_identifier = 0;
       char *prepared_templates[MAX_FILTER_TYPES];
 
@@ -2893,7 +2915,7 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp,
                      if (filter_line == NULL) err = JB_ERR_MEMORY;
                   }
                   if (!err) err = template_fill(&filter_line, line_exports);
-                  string_join(&prepared_templates[type], filter_line);
+                  if (!err) err = string_join(&prepared_templates[type], filter_line);
 
                   free_map(line_exports);
                }
@@ -3195,6 +3217,133 @@ jb_err cgi_edit_actions_submit(struct client_state *csp,
       }
    }
 
+   /* process existing suppress tag */
+   for (filter_identifier = 0; !err; filter_identifier++)
+   {
+      char key_value[30];
+      char key_name[30];
+      char old_name[30];
+      char key_type[30];
+      const char *name, *new_name;
+      char value; /*
+                   * Filter state. Valid states are: 'Y' (active),
+                   * 'N' (inactive) and 'X' (no change).
+                   * XXX: bad name.
+                   */
+      char type;  /*
+                   * Abbreviated filter type. Valid types are: 'U' (suppress tag).
+                   */
+      int multi_action_index = 0;
+
+      /* Generate the keys */
+      snprintf(key_value, sizeof(key_value), "string_filter_r%x", filter_identifier);
+      snprintf(key_name, sizeof(key_name), "string_filter_n%x", filter_identifier);
+      snprintf(old_name, sizeof(old_name), "string_filter_o%x", filter_identifier);
+      snprintf(key_type, sizeof(key_type), "string_filter_t%x", filter_identifier);
+
+      err = get_string_param(parameters, old_name, &name);
+      if (err) break;
+
+      if (name == NULL)
+      {
+         /* The filter identifier isn't present: we're done! */
+         break;
+      }
+
+      err = get_string_param(parameters, key_name, &new_name);
+      if (err) break;
+      if (new_name == NULL) new_name = name;
+
+      type = get_char_param(parameters, key_type);
+      switch (type)
+      {
+         case 'U':
+            multi_action_index = ACTION_MULTI_SUPPRESS_TAG;
+            break;
+         default:
+            log_error(LOG_LEVEL_ERROR,
+               "Unknown filter type: %c for filter %s. Filter ignored.", type, name);
+            continue;
+      }
+      assert(multi_action_index);
+
+      value = get_char_param(parameters, key_value);
+      if (value == 'X' || value == 'Y' || value == 'N')
+      {
+         list_remove_item(cur_line->data.action->multi_add[multi_action_index], name);
+         list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name);
+      }
+
+      if (value == 'Y')
+      {
+         err = enlist(cur_line->data.action->multi_add[multi_action_index], new_name);
+      }
+      else if (value == 'N')
+      {
+         err = enlist(cur_line->data.action->multi_remove[multi_action_index], new_name);
+      }
+   }
+
+   /* process new string filters */
+   for (filter_identifier = 0; !err; filter_identifier++)
+   {
+      char key_value[30];
+      char key_name[30];
+      char key_type[30];
+      const char *name;
+      char value; /*
+                   * Filter state. Valid states are: 'Y' (active),
+                   * 'N' (inactive) and 'X' (no change).
+                   * XXX: bad name.
+                   */
+      char type;  /*
+                   * Abbreviated filter type. Valid types are: 'U' (suppress tag).
+                   */
+      int multi_action_index = 0;
+
+      /* Generate the keys */
+      snprintf(key_value, sizeof(key_value), "new_string_filter_r%x", filter_identifier);
+      snprintf(key_name, sizeof(key_name), "new_string_filter_n%x", filter_identifier);
+      snprintf(key_type, sizeof(key_type), "new_string_filter_t%x", filter_identifier);
+
+      err = get_string_param(parameters, key_name, &name);
+      if (err) break;
+
+      if (name == NULL)
+      {
+         /* The filter identifier isn't present: we've done! */
+         break;
+      }
+
+      type = get_char_param(parameters, key_type);
+      switch (type)
+      {
+         case 'U':
+            multi_action_index = ACTION_MULTI_SUPPRESS_TAG;
+            break;
+         default:
+            log_error(LOG_LEVEL_ERROR,
+               "Unknown filter type: %c for filter %s. Filter ignored.", type, name);
+            continue;
+      }
+      assert(multi_action_index);
+
+      value = get_char_param(parameters, key_value);
+      if (value == 'Y')
+      {
+         list_remove_item(cur_line->data.action->multi_add[multi_action_index], name);
+         if (!err) err = enlist(cur_line->data.action->multi_add[multi_action_index], name);
+         list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name);
+      }
+      else if (value == 'N')
+      {
+         list_remove_item(cur_line->data.action->multi_add[multi_action_index], name);
+         list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name);
+         if (!err) err = enlist(cur_line->data.action->multi_remove[multi_action_index], name);
+      }
+      /* nothing to do if the value is 'X' */
+   }
+
    if (err)
    {
       /* Out of memory */
@@ -4248,6 +4397,84 @@ static jb_err actions_to_radio(struct map * exports,
    return JB_ERR_OK;
 }
 
+/*********************************************************************
+ *
+ * Function    :  action_render_string_filters_template
+ *
+ * Description :  Converts a actionsfile entry into HTML template for actions with string
+ *                filters (currently SUPPRESS-TAG actions only)
+ *
+ * Parameters  :
+ *          1  :  exports = List of substitutions to add to.
+ *          2  :  action  = Action to read
+ *          3  :  filter_template  = template to fill
+ *          4  :  type  = filter type info for rendered values/macro name
+ *
+ * Returns     :  JB_ERR_OK     on success
+ *                JB_ERR_MEMORY on out-of-memory
+ *
+ *********************************************************************/
+static jb_err action_render_string_filters_template(struct map * exports,
+                                       const struct action_spec *action,
+                                       const char* filter_template,
+                                       const struct filter_type_info *type)
+{
+   jb_err err = JB_ERR_OK;
+   int filter_identifier = 0;
+   char *prepared_template = strdup("");
+
+   struct action_multi {
+       char radio;
+       struct list_entry *list;
+   };
+
+   struct action_multi desc[] = {
+       { 'y', action->multi_add[type->multi_action_index][0].first },
+       { 'n', action->multi_remove[type->multi_action_index][0].first }
+   };
+
+   for (int i=0; i < SZ(desc); ++i)
+   {
+      const char radio = desc[i].radio;
+      struct list_entry *entry = desc[i].list;
+      for (;(!err) && (entry != NULL); entry = entry->next)
+      {
+         char number[20];
+         struct map *line_exports;
+
+         /* Generate a unique serial number */
+         snprintf(number, sizeof(number), "%x", filter_identifier++);
+
+         line_exports = new_map();
+         if (line_exports == NULL)
+         {
+            err = JB_ERR_MEMORY;
+         }
+         else
+         {
+            char *filter_line;
+            if (!err) err = map(line_exports, "index", 1, number, 1);
+            if (!err) err = map(line_exports, "name",  1, entry->str, 1);
+            if (!err) err = map_radio(line_exports, "this-filter", "ynx", radio);
+            if (!err) err = map(line_exports, "filter-type", 1, type->type, 1);
+            if (!err) err = map(line_exports, "abbr-filter-type", 1, type->abbr_type, 1);
+            if (!err) err = map(line_exports, "anchor", 1, type->anchor, 1);
+            if (!err)
+            {
+               filter_line = strdup(filter_template);
+               if (filter_line == NULL) err = JB_ERR_MEMORY;
+            }
+            if (!err) err = template_fill(&filter_line, line_exports);
+            if (!err) err = string_join(&prepared_template, filter_line);
+
+            free_map(line_exports);
+        }
+      }
+   }
+   if (!err) map(exports, type->macro_name, 1, prepared_template, 1);
+   freez(prepared_template);
+   return err;
+}
 
 /*********************************************************************
  *
index 60c437f..cb1243d 100644 (file)
@@ -5936,6 +5936,63 @@ TAG:^image/
 </sect3>
 
 
+<!--   ~~~~~       New section      ~~~~~     -->
+<sect3 renderas="sect4" id="suppress-tag">
+<title>suppress-tag</title>
+
+<variablelist>
+ <varlistentry>
+  <term>Typical use:</term>
+  <listitem>
+   <para>
+   Suppress client or server tag.
+   </para>
+  </listitem>
+ </varlistentry>
+
+ <varlistentry>
+  <term>Effect:</term>
+  <listitem>
+   <para>
+    Server or client tags to which this action applies are not added to the request,
+    thus making all actions that are specific to these request tags inactive.
+   </para>
+  </listitem>
+ </varlistentry>
+
+ <varlistentry>
+  <term>Type:</term>
+  <!-- boolean, parameterized, Multi-value -->
+  <listitem>
+   <para>Multi-value.</para>
+  </listitem>
+ </varlistentry>
+
+ <varlistentry>
+  <term>Parameter:</term>
+  <listitem>
+   <para>
+    The result tag of a server-header or client-header tagger, as defined in one of the
+    <link linkend="filter-file">filter files</link>.
+   </para>
+  </listitem>
+ </varlistentry>
+
+ <varlistentry>
+  <term>Example usage (section):</term>
+  <listitem>
+     <screen>
+# Suppress tag produced by range-requests client-header tagger for requests coming from address 10.0.0.1
+{+suppress-tag{RANGE-REQUEST}}
+TAG:^IP-ADDRESS: 10\.0\.0\.1$
+</screen>
+  </listitem>
+ </varlistentry>
+
+</variablelist>
+</sect3>
+
+
 <!--   ~~~~~       New section      ~~~~~     -->
 <sect3 renderas="sect4" id="session-cookies-only">
 <title>session-cookies-only</title>
index eab5e76..d2ef046 100644 (file)
--- a/parsers.c
+++ b/parsers.c
@@ -1540,6 +1540,15 @@ static jb_err header_tagger(struct client_state *csp, char *header)
             continue;
          }
 
+         if (list_contains_item(csp->action->multi[ACTION_MULTI_SUPPRESS_TAG], tag))
+         {
+            log_error(LOG_LEVEL_HEADER,
+               "Tagger \'%s\' didn't add tag \'%s\': suppressed",
+               b->name, tag);
+            freez(tag);
+            continue;
+         }
+
          if (!list_contains_item(csp->tags, tag))
          {
             if (JB_ERR_OK != enlist(csp->tags, tag))
index e07c761..43792dd 100644 (file)
--- a/project.h
+++ b/project.h
@@ -649,8 +649,10 @@ struct iob
 #define ACTION_MULTI_SERVER_HEADER_TAGGER    5
 /** Number of multi-string actions. */
 #define ACTION_MULTI_EXTERNAL_FILTER         6
+/** Index into current_action_spec::multi[] for tags to suppress. */
+#define ACTION_MULTI_SUPPRESS_TAG            7
 /** Number of multi-string actions. */
-#define ACTION_MULTI_COUNT                   7
+#define ACTION_MULTI_COUNT                   8
 
 
 /**
@@ -1303,13 +1305,14 @@ enum filter_type
 #ifdef FEATURE_EXTERNAL_FILTERS
    FT_EXTERNAL_CONTENT_FILTER = 5,
 #endif
+   FT_SUPPRESS_TAG = 6,
    FT_INVALID_FILTER       = 42,
 };
 
 #ifdef FEATURE_EXTERNAL_FILTERS
-#define MAX_FILTER_TYPES        6
+#define MAX_FILTER_TYPES        7
 #else
-#define MAX_FILTER_TYPES        5
+#define MAX_FILTER_TYPES        6
 #endif
 
 /**
index e3a240c..1b9ed4f 100644 (file)
@@ -1142,6 +1142,30 @@ function show_limit_connect_opts(tf)
         you can enable or disable the taggers individually below.</td>
     </tr>
 @server-header-tagger-params@
+@suppress-tag-params@
+    <tr class="bg1" align="left" valign="top">
+      <td class="en1" align="center" valign="middle"><input type="radio"
+        name="new_string_filter_r0" value="Y"
+        ></td>
+      <td class="dis1" align="center" valign="middle"><input type="radio"
+        name="new_string_filter_r0" value="N"
+        ></td>
+      <td class="noc1" align="center" valign="middle"><input type="radio"
+        name="new_string_filter_r0" value="X" checked
+        ></td>
+      <td class="action"><a href="@user-manual@@actions-help-prefix@SUPPRESS-TAG">suppress-tag</a></td>
+      <td>Suppress tag.</td>
+    </tr>
+    <tr class="bg1" align="left" valign="top" id="suppress-tag_opts">
+      <td class="en1">&nbsp;</td>
+      <td class="dis1">&nbsp;</td>
+      <td class="noc1">&nbsp;</td>
+      <td>&nbsp;</td>
+      <td>Tag to suppress:<br>
+        <input type="hidden" name="new_string_filter_t0" value="U">
+        <input type="text" name="new_string_filter_n0" size="40" value="">
+      </td>
+    </tr>
     <tr class="bg1" align="left" valign="top">
       <td class="en1" align="center" valign="middle"><input type="radio"
         name="session_cookies_only" value="Y" @session-cookies-only-y@
diff --git a/templates/edit-actions-for-url-string-filter b/templates/edit-actions-for-url-string-filter
new file mode 100644 (file)
index 0000000..b1f9b6c
--- /dev/null
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# File        :  $Source: /cvsroot/ijbswa/current/templates/edit-actions-for-url-filter,v $
+#
+# Purpose     :  Template that is included from most of Privoxy's CGI pages
+#                to show the user how to get help or report problems.
+#
+#
+# Copyright   :  Written by and Copyright (C) 2002-2007 members of
+#                members of the Privoxy team. https://www.privoxy.org/
+#
+#                This template is free software; you can redistribute it
+#                and/or modify it under the terms of the GNU General
+#                Public License as published by the Free Software
+#                Foundation; either version 2 of the License, or (at
+#                your option) any later version.
+#
+##############################################################################
+
+<tr class="bg1" align="left" valign="top">
+  <td class="en1" align="center" valign="middle"><input type="radio" name="string_filter_r@index@" value="Y" @this-filter-y@></td>
+  <td class="dis1" align="center" valign="middle"><input type="radio" name="string_filter_r@index@" value="N" @this-filter-n@></td>
+  <td class="noc1" align="center" valign="middle"><input type="radio" name="string_filter_r@index@" value="X"></td>
+  <td class="action">
+    <input type="hidden" name="string_filter_t@index@" value="@abbr-filter-type@">
+    <input type="hidden" name="string_filter_c@index@" value="@code@">
+    <input type="hidden" name="string_filter_o@index@" value="@name@">
+    <a href="@user-manual@@actions-help-prefix@@anchor@">@filter-type@</a> @name@
+  </td>
+  <td>Suppress tag</td>
+</tr>
+<tr class="bg1" align="left" valign="top" id="string_filter@index@_opts">
+  <td class="en1">&nbsp;</td>
+  <td class="dis1">&nbsp;</td>
+  <td class="noc1">&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>Tag to suppress:<br>
+  <input type="text" name="string_filter_n@index@" size="40" value="@name@">
+  </td>
+</tr>