LCOV - code coverage report
Current view: top level - fuzz - actions.c (source / functions) Hit Total Coverage
Test: trace.lcov_info_final Lines: 360 546 65.9 %
Date: 2021-02-22 04:51:02 Functions: 19 24 79.2 %

          Line data    Source code
       1             : /*********************************************************************
       2             :  *
       3             :  * File        :  $Source: /cvsroot/ijbswa/current/actions.c,v $
       4             :  *
       5             :  * Purpose     :  Declares functions to work with actions files
       6             :  *
       7             :  * Copyright   :  Written by and Copyright (C) 2001-2016 the
       8             :  *                Privoxy team. https://www.privoxy.org/
       9             :  *
      10             :  *                Based on the Internet Junkbuster originally written
      11             :  *                by and Copyright (C) 1997 Anonymous Coders and
      12             :  *                Junkbusters Corporation.  http://www.junkbusters.com
      13             :  *
      14             :  *                This program is free software; you can redistribute it
      15             :  *                and/or modify it under the terms of the GNU General
      16             :  *                Public License as published by the Free Software
      17             :  *                Foundation; either version 2 of the License, or (at
      18             :  *                your option) any later version.
      19             :  *
      20             :  *                This program is distributed in the hope that it will
      21             :  *                be useful, but WITHOUT ANY WARRANTY; without even the
      22             :  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
      23             :  *                PARTICULAR PURPOSE.  See the GNU General Public
      24             :  *                License for more details.
      25             :  *
      26             :  *                The GNU General Public License should be included with
      27             :  *                this file.  If not, you can view it at
      28             :  *                http://www.gnu.org/copyleft/gpl.html
      29             :  *                or write to the Free Software Foundation, Inc., 59
      30             :  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
      31             :  *
      32             :  *********************************************************************/
      33             : 
      34             : 
      35             : #include "config.h"
      36             : 
      37             : #include <stdio.h>
      38             : #include <string.h>
      39             : #include <assert.h>
      40             : #include <stdlib.h>
      41             : 
      42             : #ifdef FEATURE_PTHREAD
      43             : #include <pthread.h>
      44             : #endif
      45             : 
      46             : #include "project.h"
      47             : #include "jcc.h"
      48             : #include "list.h"
      49             : #include "actions.h"
      50             : #include "miscutil.h"
      51             : #include "errlog.h"
      52             : #include "loaders.h"
      53       71410 : #include "encode.h"
      54       71410 : #include "urlmatch.h"
      55             : #include "cgi.h"
      56       71410 : #include "ssplit.h"
      57             : #include "filters.h"
      58             : 
      59      108868 : /*
      60      221223 :  * We need the main list of options.
      61      108869 :  *
      62       71410 :  * First, we need a way to tell between boolean, string, and multi-string
      63             :  * options.  For string and multistring options, we also need to be
      64       71410 :  * able to tell the difference between a "+" and a "-".  (For bools,
      65             :  * the "+"/"-" information is encoded in "add" and "mask").  So we use
      66       71410 :  * an enumerated type (well, the preprocessor equivalent).  Here are
      67       71410 :  * the values:
      68       71410 :  */
      69       71410 : enum action_value_type {
      70             :    AV_NONE       = 0, /* +opt -opt */
      71       71410 :    AV_ADD_STRING = 1, /* +stropt{string} */
      72             :    AV_REM_STRING = 2, /* -stropt */
      73       71410 :    AV_ADD_MULTI  = 3, /* +multiopt{string} +multiopt{string2} */
      74             :    AV_REM_MULTI  = 4  /* -multiopt{string} -multiopt          */
      75             : };
      76       71410 : 
      77             : /*
      78       71410 :  * We need a structure to hold the name, flag changes,
      79             :  * type, and string index.
      80             :  */
      81       71410 : struct action_name
      82             : {
      83             :    const char * name;
      84             :    unsigned long mask;                /* a bit set to "0" = remove action */
      85      141034 :    unsigned long add;                 /* a bit set to "1" = add action */
      86       71410 :    enum action_value_type value_type; /* an AV_... constant */
      87       71410 :    int index;                         /* index into strings[] or multi[] */
      88             : };
      89       71410 : 
      90       71410 : /*
      91       71410 :  * And with those building blocks in place, here's the array.
      92             :  */
      93             : static const struct action_name action_names[] =
      94       71410 : {
      95             :    /*
      96             :     * Well actually there's no data here - it's in actionlist.h
      97       71410 :     * This keeps it together to make it easy to change.
      98             :     *
      99             :     * Here's the macros used to format it:
     100       71410 :     */
     101             : #define DEFINE_ACTION_MULTI(name,index)                   \
     102             :    { "+" name, ACTION_MASK_ALL, 0, AV_ADD_MULTI, index }, \
     103       71410 :    { "-" name, ACTION_MASK_ALL, 0, AV_REM_MULTI, index },
     104             : #define DEFINE_ACTION_STRING(name,flag,index)                 \
     105             :    { "+" name, ACTION_MASK_ALL, flag, AV_ADD_STRING, index }, \
     106             :    { "-" name, ~flag, 0, AV_REM_STRING, index },
     107             : #define DEFINE_ACTION_BOOL(name,flag)   \
     108             :    { "+" name, ACTION_MASK_ALL, flag }, \
     109       71410 :    { "-" name, ~flag, 0 },
     110             : #define DEFINE_ACTION_ALIAS 1 /* Want aliases please */
     111             : 
     112       71410 : #include "actionlist.h"
     113       71410 : 
     114             : #undef DEFINE_ACTION_MULTI
     115       71410 : #undef DEFINE_ACTION_STRING
     116             : #undef DEFINE_ACTION_BOOL
     117       71410 : #undef DEFINE_ACTION_ALIAS
     118             : 
     119       71410 :    { NULL, 0, 0 } /* End marker */
     120             : };
     121             : 
     122             : 
     123       71410 : #ifndef FUZZ
     124       71410 : static
     125             : #endif
     126      108874 : int load_one_actions_file(struct client_state *csp, int fileid);
     127      108867 : 
     128       71410 : 
     129       71410 : /*********************************************************************
     130             :  *
     131             :  * Function    :  merge_actions
     132             :  *
     133       71410 :  * Description :  Merge two actions together.
     134             :  *                Similar to "dest += src".
     135             :  *
     136             :  * Parameters  :
     137             :  *          1  :  dest = Actions to modify.
     138             :  *          2  :  src = Action to add.
     139             :  *
     140             :  * Returns     :  JB_ERR_OK or JB_ERR_MEMORY
     141             :  *
     142             :  *********************************************************************/
     143        9632 : jb_err merge_actions (struct action_spec *dest,
     144             :                       const struct action_spec *src)
     145             : {
     146             :    int i;
     147             :    jb_err err;
     148             : 
     149        9632 :    dest->mask &= src->mask;
     150        9632 :    dest->add  &= src->mask;
     151        9632 :    dest->add  |= src->add;
     152             : 
     153      202272 :    for (i = 0; i < ACTION_STRING_COUNT; i++)
     154             :    {
     155      192640 :       char * str = src->string[i];
     156      192640 :       if (str)
     157             :       {
     158        4816 :          freez(dest->string[i]);
     159        4816 :          dest->string[i] = strdup_or_die(str);
     160             :       }
     161             :    }
     162             : 
     163       96320 :    for (i = 0; i < ACTION_MULTI_COUNT; i++)
     164             :    {
     165       86688 :       if (src->multi_remove_all[i])
     166             :       {
     167             :          /* Remove everything from dest */
     168           0 :          list_remove_all(dest->multi_remove[i]);
     169           0 :          dest->multi_remove_all[i] = 1;
     170             : 
     171           0 :          err = list_duplicate(dest->multi_add[i], src->multi_add[i]);
     172             :       }
     173       86688 :       else if (dest->multi_remove_all[i])
     174             :       {
     175             :          /*
     176             :           * dest already removes everything, so we only need to worry
     177             :           * about what we add.
     178             :           */
     179           0 :          list_remove_list(dest->multi_add[i], src->multi_remove[i]);
     180           0 :          err = list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
     181             :       }
     182             :       else
     183             :       {
     184             :          /* No "remove all"s to worry about. */
     185       86688 :          list_remove_list(dest->multi_add[i], src->multi_remove[i]);
     186       86688 :          err = list_append_list_unique(dest->multi_remove[i], src->multi_remove[i]);
     187       86688 :          if (!err) err = list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
     188             :       }
     189             : 
     190       86688 :       if (err)
     191             :       {
     192           0 :          return err;
     193             :       }
     194             :    }
     195             : 
     196        9632 :    return JB_ERR_OK;
     197             : }
     198             : 
     199             : 
     200             : /*********************************************************************
     201             :  *
     202             :  * Function    :  copy_action
     203             :  *
     204             :  * Description :  Copy an action_specs.
     205             :  *                Similar to "dest = src".
     206             :  *
     207             :  * Parameters  :
     208             :  *          1  :  dest = Destination of copy.
     209             :  *          2  :  src = Source for copy.
     210             :  *
     211             :  * Returns     :  JB_ERR_OK or JB_ERR_MEMORY
     212             :  *
     213             :  *********************************************************************/
     214           0 : jb_err copy_action (struct action_spec *dest,
     215             :                     const struct action_spec *src)
     216             : {
     217             :    int i;
     218           0 :    jb_err err = JB_ERR_OK;
     219             : 
     220           0 :    free_action(dest);
     221           0 :    memset(dest, '\0', sizeof(*dest));
     222             : 
     223           0 :    dest->mask = src->mask;
     224           0 :    dest->add  = src->add;
     225             : 
     226           0 :    for (i = 0; i < ACTION_STRING_COUNT; i++)
     227             :    {
     228           0 :       char * str = src->string[i];
     229           0 :       if (str)
     230             :       {
     231           0 :          str = strdup_or_die(str);
     232           0 :          dest->string[i] = str;
     233             :       }
     234             :    }
     235             : 
     236           0 :    for (i = 0; i < ACTION_MULTI_COUNT; i++)
     237             :    {
     238           0 :       dest->multi_remove_all[i] = src->multi_remove_all[i];
     239           0 :       err = list_duplicate(dest->multi_remove[i], src->multi_remove[i]);
     240           0 :       if (err)
     241             :       {
     242           0 :          return err;
     243             :       }
     244           0 :       err = list_duplicate(dest->multi_add[i],    src->multi_add[i]);
     245           0 :       if (err)
     246             :       {
     247           0 :          return err;
     248             :       }
     249             :    }
     250           0 :    return err;
     251             : }
     252             : 
     253             : /*********************************************************************
     254             :  *
     255             :  * Function    :  free_action_spec
     256             :  *
     257             :  * Description :  Frees an action_spec and the memory used by it.
     258             :  *
     259             :  * Parameters  :
     260             :  *          1  :  src = Source to free.
     261             :  *
     262             :  * Returns     :  N/A
     263             :  *
     264             :  *********************************************************************/
     265           0 : void free_action_spec(struct action_spec *src)
     266             : {
     267           0 :    free_action(src);
     268           0 :    freez(src);
     269           0 : }
     270             : 
     271             : 
     272             : /*********************************************************************
     273             :  *
     274             :  * Function    :  free_action
     275             :  *
     276             :  * Description :  Destroy an action_spec.  Frees memory used by it,
     277             :  *                except for the memory used by the struct action_spec
     278             :  *                itself.
     279             :  *
     280             :  * Parameters  :
     281             :  *          1  :  src = Source to free.
     282             :  *
     283             :  * Returns     :  N/A
     284             :  *
     285             :  *********************************************************************/
     286       69882 : void free_action (struct action_spec *src)
     287             : {
     288             :    int i;
     289             : 
     290       69882 :    if (src == NULL)
     291             :    {
     292           0 :       return;
     293             :    }
     294             : 
     295     1467522 :    for (i = 0; i < ACTION_STRING_COUNT; i++)
     296             :    {
     297     1397640 :       freez(src->string[i]);
     298             :    }
     299             : 
     300      698820 :    for (i = 0; i < ACTION_MULTI_COUNT; i++)
     301             :    {
     302      628938 :       destroy_list(src->multi_remove[i]);
     303      628938 :       destroy_list(src->multi_add[i]);
     304             :    }
     305             : 
     306       69882 :    memset(src, '\0', sizeof(*src));
     307             : }
     308             : 
     309             : 
     310             : /*********************************************************************
     311             :  *
     312             :  * Function    :  get_action_token
     313             :  *
     314             :  * Description :  Parses a line for the first action.
     315             :  *                Modifies its input array, doesn't allocate memory.
     316             :  *                e.g. given:
     317             :  *                *line="  +abc{def}  -ghi "
     318             :  *                Returns:
     319             :  *                *line="  -ghi "
     320             :  *                *name="+abc"
     321             :  *                *value="def"
     322             :  *
     323             :  * Parameters  :
     324             :  *          1  :  line = [in] The line containing the action.
     325             :  *                       [out] Start of next action on line, or
     326             :  *                       NULL if we reached the end of line before
     327             :  *                       we found an action.
     328             :  *          2  :  name = [out] Start of action name, null
     329             :  *                       terminated.  NULL on EOL
     330             :  *          3  :  value = [out] Start of action value, null
     331             :  *                        terminated.  NULL if none or EOL.
     332             :  *
     333             :  * Returns     :  JB_ERR_OK => Ok
     334             :  *                JB_ERR_PARSE => Mismatched {} (line was trashed anyway)
     335             :  *
     336             :  *********************************************************************/
     337      492534 : jb_err get_action_token(char **line, char **name, char **value)
     338             : {
     339      492534 :    char * str = *line;
     340             :    char ch;
     341             : 
     342             :    /* set default returns */
     343      492534 :    *line = NULL;
     344      492534 :    *name = NULL;
     345      492534 :    *value = NULL;
     346             : 
     347             :    /* Eat any leading whitespace */
     348      724420 :    while ((*str == ' ') || (*str == '\t'))
     349             :    {
     350      231886 :       str++;
     351             :    }
     352             : 
     353      492534 :    if (*str == '\0')
     354             :    {
     355      131832 :       return 0;
     356             :    }
     357             : 
     358      360702 :    if (*str == '{')
     359             :    {
     360             :       /* null name, just value is prohibited */
     361           0 :       return JB_ERR_PARSE;
     362             :    }
     363             : 
     364      360702 :    *name = str;
     365             : 
     366             :    /* parse option */
     367     5972412 :    while (((ch = *str) != '\0') &&
     368     5825682 :           (ch != ' ') && (ch != '\t') && (ch != '{'))
     369             :    {
     370     5611710 :       if (ch == '}')
     371             :       {
     372             :          /* error, '}' without '{' */
     373           0 :          return JB_ERR_PARSE;
     374             :       }
     375     5611710 :       str++;
     376             :    }
     377      360702 :    *str = '\0';
     378             : 
     379      360702 :    if (ch != '{')
     380             :    {
     381             :       /* no value */
     382      146730 :       if (ch == '\0')
     383             :       {
     384             :          /* EOL - be careful not to run off buffer */
     385       87138 :          *line = str;
     386             :       }
     387             :       else
     388             :       {
     389             :          /* More to parse next time. */
     390       59592 :          *line = str + 1;
     391             :       }
     392      146730 :       return JB_ERR_OK;
     393             :    }
     394             : 
     395      213972 :    str++;
     396      213972 :    *value = str;
     397             : 
     398             :    /* The value ends with the first non-escaped closing curly brace */
     399      213972 :    while ((str = strchr(str, '}')) != NULL)
     400             :    {
     401      213972 :       if (str[-1] == '\\')
     402             :       {
     403             :          /* Overwrite the '\' so the action doesn't see it. */
     404           0 :          string_move(str-1, str);
     405           0 :          continue;
     406             :       }
     407      213972 :       break;
     408             :    }
     409      213972 :    if (str == NULL)
     410             :    {
     411             :       /* error */
     412           0 :       *value = NULL;
     413           0 :       return JB_ERR_PARSE;
     414             :    }
     415             : 
     416             :    /* got value */
     417      213972 :    *str = '\0';
     418      213972 :    *line = str + 1;
     419             : 
     420      213972 :    chomp(*value);
     421             : 
     422      213972 :    return JB_ERR_OK;
     423             : }
     424             : 
     425             : /*********************************************************************
     426             :  *
     427             :  * Function    :  action_used_to_be_valid
     428             :  *
     429             :  * Description :  Checks if unrecognized actions were valid in earlier
     430             :  *                releases.
     431             :  *
     432             :  * Parameters  :
     433             :  *          1  :  action = The string containing the action to check.
     434             :  *
     435             :  * Returns     :  True if yes, otherwise false.
     436             :  *
     437             :  *********************************************************************/
     438           0 : static int action_used_to_be_valid(const char *action)
     439             : {
     440             :    static const char * const formerly_valid_actions[] = {
     441             :       "inspect-jpegs",
     442             :       "kill-popups",
     443             :       "send-vanilla-wafer",
     444             :       "send-wafer",
     445             :       "treat-forbidden-connects-like-blocks",
     446             :       "vanilla-wafer",
     447             :       "wafer"
     448             :    };
     449             :    unsigned int i;
     450             : 
     451           0 :    for (i = 0; i < SZ(formerly_valid_actions); i++)
     452             :    {
     453           0 :       if (0 == strcmpic(action, formerly_valid_actions[i]))
     454             :       {
     455           0 :          return TRUE;
     456             :       }
     457             :    }
     458             : 
     459           0 :    return FALSE;
     460             : }
     461             : 
     462             : /*********************************************************************
     463             :  *
     464             :  * Function    :  get_actions
     465             :  *
     466             :  * Description :  Parses a list of actions.
     467             :  *
     468             :  * Parameters  :
     469             :  *          1  :  line = The string containing the actions.
     470             :  *                       Will be written to by this function.
     471             :  *          2  :  alias_list = Custom alias list, or NULL for none.
     472             :  *          3  :  cur_action = Where to store the action.  Caller
     473             :  *                             allocates memory.
     474             :  *
     475             :  * Returns     :  JB_ERR_OK => Ok
     476             :  *                JB_ERR_PARSE => Parse error (line was trashed anyway)
     477             :  *                nonzero => Out of memory (line was trashed anyway)
     478             :  *
     479             :  *********************************************************************/
     480      131832 : jb_err get_actions(char *line,
     481             :                    struct action_alias * alias_list,
     482             :                    struct action_spec *cur_action)
     483             : {
     484             :    jb_err err;
     485      131832 :    init_action(cur_action);
     486      131832 :    cur_action->mask = ACTION_MASK_ALL;
     487             : 
     488      624366 :    while (line)
     489             :    {
     490      492534 :       char * option = NULL;
     491      492534 :       char * value = NULL;
     492             : 
     493      492534 :       err = get_action_token(&line, &option, &value);
     494      492534 :       if (err)
     495             :       {
     496           0 :          return err;
     497             :       }
     498             : 
     499      492534 :       if (option)
     500             :       {
     501             :          /* handle option in 'option' */
     502             : 
     503             :          /* Check for standard action name */
     504      360702 :          const struct action_name * action = action_names;
     505             : 
     506    12745994 :          while ((action->name != NULL) && (0 != strcmpic(action->name, option)))
     507             :          {
     508    12385292 :             action++;
     509             :          }
     510      360702 :          if (action->name != NULL)
     511             :          {
     512             :             /* Found it */
     513      351070 :             cur_action->mask &= action->mask;
     514      351070 :             cur_action->add  &= action->mask;
     515      351070 :             cur_action->add  |= action->add;
     516             : 
     517      351070 :             switch (action->value_type)
     518             :             {
     519       84122 :             case AV_NONE:
     520       84122 :                if (value != NULL)
     521             :                {
     522           0 :                   log_error(LOG_LEVEL_ERROR,
     523             :                      "Action %s does not take parameters but %s was given.",
     524             :                      action->name, value);
     525           0 :                   return JB_ERR_PARSE;
     526             :                }
     527       84122 :                break;
     528      117068 :             case AV_ADD_STRING:
     529             :                {
     530             :                   /* add single string. */
     531             : 
     532      117068 :                   if ((value == NULL) || (*value == '\0'))
     533             :                   {
     534           0 :                      if (0 == strcmpic(action->name, "+block"))
     535             :                      {
     536             :                         /*
     537             :                          * XXX: Temporary backwards compatibility hack.
     538             :                          * XXX: should include line number.
     539             :                          */
     540           0 :                         value = "No reason specified.";
     541           0 :                         log_error(LOG_LEVEL_ERROR,
     542             :                            "block action without reason found. This may "
     543             :                            "become a fatal error in future versions.");
     544             :                      }
     545             :                      else
     546             :                      {
     547           0 :                         return JB_ERR_PARSE;
     548             :                      }
     549             :                   }
     550             : #ifdef FEATURE_EXTENDED_STATISTICS
     551      117068 :                   if (0 == strcmpic(action->name, "+block"))
     552             :                   {
     553       14448 :                      register_block_reason_for_statistics(value);
     554             :                   }
     555             : #endif
     556             :                   /* FIXME: should validate option string here */
     557      117068 :                   freez (cur_action->string[action->index]);
     558      117068 :                   cur_action->string[action->index] = strdup(value);
     559      117068 :                   if (NULL == cur_action->string[action->index])
     560             :                   {
     561           0 :                      return JB_ERR_MEMORY;
     562             :                   }
     563      117068 :                   break;
     564             :                }
     565       58692 :             case AV_REM_STRING:
     566             :                {
     567             :                   /* remove single string. */
     568             : 
     569       58692 :                   freez (cur_action->string[action->index]);
     570       58692 :                   break;
     571             :                }
     572       76740 :             case AV_ADD_MULTI:
     573             :                {
     574             :                   /* append multi string. */
     575             : 
     576       76740 :                   struct list * remove_p = cur_action->multi_remove[action->index];
     577       76740 :                   struct list * add_p    = cur_action->multi_add[action->index];
     578             : 
     579       76740 :                   if ((value == NULL) || (*value == '\0'))
     580             :                   {
     581           0 :                      return JB_ERR_PARSE;
     582             :                   }
     583             : 
     584       76740 :                   list_remove_item(remove_p, value);
     585       76740 :                   err = enlist_unique(add_p, value, 0);
     586       76740 :                   if (err)
     587             :                   {
     588           0 :                      return err;
     589             :                   }
     590       76740 :                   break;
     591             :                }
     592       14448 :             case AV_REM_MULTI:
     593             :                {
     594             :                   /* remove multi string. */
     595             : 
     596       14448 :                   struct list * remove_p = cur_action->multi_remove[action->index];
     597       14448 :                   struct list * add_p    = cur_action->multi_add[action->index];
     598             : 
     599       14448 :                   if ((value == NULL) || (*value == '\0')
     600        9632 :                      || ((*value == '*') && (value[1] == '\0')))
     601             :                   {
     602             :                      /*
     603             :                       * no option, or option == "*".
     604             :                       *
     605             :                       * Remove *ALL*.
     606             :                       */
     607        4816 :                      list_remove_all(remove_p);
     608        4816 :                      list_remove_all(add_p);
     609        4816 :                      cur_action->multi_remove_all[action->index] = 1;
     610             :                   }
     611             :                   else
     612             :                   {
     613             :                      /* Valid option - remove only 1 option */
     614             : 
     615        9632 :                      if (!cur_action->multi_remove_all[action->index])
     616             :                      {
     617             :                         /* there isn't a catch-all in the remove list already */
     618        9632 :                         err = enlist_unique(remove_p, value, 0);
     619        9632 :                         if (err)
     620             :                         {
     621           0 :                            return err;
     622             :                         }
     623             :                      }
     624        9632 :                      list_remove_item(add_p, value);
     625             :                   }
     626       14448 :                   break;
     627             :                }
     628           0 :             default:
     629             :                /* Shouldn't get here unless there's memory corruption. */
     630           0 :                assert(0);
     631             :                return JB_ERR_PARSE;
     632             :             }
     633             :          }
     634             :          else
     635             :          {
     636             :             /* try user aliases. */
     637        9632 :             const struct action_alias * alias = alias_list;
     638             : 
     639       19264 :             while ((alias != NULL) && (0 != strcmpic(alias->name, option)))
     640             :             {
     641        9632 :                alias = alias->next;
     642             :             }
     643        9632 :             if (alias != NULL)
     644             :             {
     645             :                /* Found it */
     646        9632 :                merge_actions(cur_action, alias->action);
     647             :             }
     648           0 :             else if (((size_t)2 < strlen(option)) && action_used_to_be_valid(option+1))
     649             :             {
     650           0 :                log_error(LOG_LEVEL_ERROR, "Action '%s' is no longer valid "
     651             :                   "in this Privoxy release. Ignored.", option+1);
     652             :             }
     653           0 :             else if (((size_t)2 < strlen(option)) && 0 == strcmpic(option+1, "hide-forwarded-for-headers"))
     654             :             {
     655           0 :                log_error(LOG_LEVEL_FATAL, "The action 'hide-forwarded-for-headers' "
     656             :                   "is no longer valid in this Privoxy release. "
     657             :                   "Use 'change-x-forwarded-for' instead.");
     658             :             }
     659             :             else
     660             :             {
     661             :                /* Bad action name */
     662             :                /*
     663             :                 * XXX: This is a fatal error and Privoxy will later on exit
     664             :                 * in load_one_actions_file() because of an "invalid line".
     665             :                 *
     666             :                 * It would be preferable to name the offending option in that
     667             :                 * error message, but currently there is no way to do that and
     668             :                 * we have to live with two error messages for basically the
     669             :                 * same reason.
     670             :                 */
     671           0 :                log_error(LOG_LEVEL_ERROR, "Unknown action or alias: %s", option);
     672           0 :                return JB_ERR_PARSE;
     673             :             }
     674             :          }
     675             :       }
     676             :    }
     677             : 
     678      131832 :    return JB_ERR_OK;
     679             : }
     680             : 
     681             : 
     682             : /*********************************************************************
     683             :  *
     684             :  * Function    :  init_current_action
     685             :  *
     686             :  * Description :  Zero out an action.
     687             :  *
     688             :  * Parameters  :
     689             :  *          1  :  dest = An uninitialized current_action_spec.
     690             :  *
     691             :  * Returns     :  N/A
     692             :  *
     693             :  *********************************************************************/
     694       35897 : void init_current_action (struct current_action_spec *dest)
     695             : {
     696       35897 :    memset(dest, '\0', sizeof(*dest));
     697             : 
     698       35897 :    dest->flags = ACTION_MOST_COMPATIBLE;
     699       35897 : }
     700             : 
     701             : 
     702             : /*********************************************************************
     703             :  *
     704             :  * Function    :  init_action
     705             :  *
     706             :  * Description :  Zero out an action.
     707             :  *
     708             :  * Parameters  :
     709             :  *          1  :  dest = An uninitialized action_spec.
     710             :  *
     711             :  * Returns     :  N/A
     712             :  *
     713             :  *********************************************************************/
     714      193792 : void init_action (struct action_spec *dest)
     715             : {
     716      193792 :    memset(dest, '\0', sizeof(*dest));
     717      193792 : }
     718             : 
     719             : 
     720             : /*********************************************************************
     721             :  *
     722             :  * Function    :  merge_current_action
     723             :  *
     724             :  * Description :  Merge two actions together.
     725             :  *                Similar to "dest += src".
     726             :  *                Differences between this and merge_actions()
     727             :  *                is that this one doesn't allocate memory for
     728             :  *                strings (so "src" better be in memory for at least
     729             :  *                as long as "dest" is, and you'd better free
     730             :  *                "dest" using "free_current_action").
     731             :  *                Also, there is no  mask or remove lists in dest.
     732             :  *                (If we're applying it to a URL, we don't need them)
     733             :  *
     734             :  * Parameters  :
     735             :  *          1  :  dest = Current actions, to modify.
     736             :  *          2  :  src = Action to add.
     737             :  *
     738             :  * Returns  0  :  no error
     739             :  *        !=0  :  error, probably JB_ERR_MEMORY.
     740             :  *
     741             :  *********************************************************************/
     742       79400 : jb_err merge_current_action (struct current_action_spec *dest,
     743             :                              const struct action_spec *src)
     744             : {
     745             :    int i;
     746       79400 :    jb_err err = JB_ERR_OK;
     747             : 
     748       79400 :    dest->flags  &= src->mask;
     749       79400 :    dest->flags  |= src->add;
     750             : 
     751     1667400 :    for (i = 0; i < ACTION_STRING_COUNT; i++)
     752             :    {
     753     1588000 :       char * str = src->string[i];
     754     1588000 :       if (str)
     755             :       {
     756      418929 :          str = strdup_or_die(str);
     757      418929 :          freez(dest->string[i]);
     758      418929 :          dest->string[i] = str;
     759             :       }
     760             :    }
     761             : 
     762      794000 :    for (i = 0; i < ACTION_MULTI_COUNT; i++)
     763             :    {
     764      714600 :       if (src->multi_remove_all[i])
     765             :       {
     766             :          /* Remove everything from dest, then add src->multi_add */
     767        2489 :          err = list_duplicate(dest->multi[i], src->multi_add[i]);
     768        2489 :          if (err)
     769             :          {
     770           0 :             return err;
     771             :          }
     772             :       }
     773             :       else
     774             :       {
     775      712111 :          list_remove_list(dest->multi[i], src->multi_remove[i]);
     776      712111 :          err = list_append_list_unique(dest->multi[i], src->multi_add[i]);
     777      712111 :          if (err)
     778             :          {
     779           0 :             return err;
     780             :          }
     781             :       }
     782             :    }
     783       79400 :    return err;
     784             : }
     785             : 
     786             : 
     787             : /*********************************************************************
     788             :  *
     789             :  * Function    :  update_action_bits_for_tag
     790             :  *
     791             :  * Description :  Updates the action bits based on the action sections
     792             :  *                whose tag patterns match a provided tag.
     793             :  *
     794             :  * Parameters  :
     795             :  *          1  :  csp = Current client state (buffers, headers, etc...)
     796             :  *          2  :  tag = The tag on which the update should be based on
     797             :  *
     798             :  * Returns     :  0 if no tag matched, or
     799             :  *                1 otherwise
     800             :  *
     801             :  *********************************************************************/
     802       10729 : int update_action_bits_for_tag(struct client_state *csp, const char *tag)
     803             : {
     804             :    struct file_list *fl;
     805             :    struct url_actions *b;
     806             : 
     807       10729 :    int updated = 0;
     808             :    int i;
     809             : 
     810       10729 :    assert(tag);
     811       10729 :    assert(list_contains_item(csp->tags, tag));
     812             : 
     813             :    /* Run through all action files, */
     814     1083629 :    for (i = 0; i < MAX_AF_FILES; i++)
     815             :    {
     816     1072900 :       if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL))
     817             :       {
     818             :          /* Skip empty files */
     819     1051442 :          continue;
     820             :       }
     821             : 
     822             :       /* and through all the action patterns, */
     823      246767 :       for (b = b->next; NULL != b; b = b->next)
     824             :       {
     825             :          /* skip everything but TAG patterns, */
     826      225309 :          if (!(b->url->flags & PATTERN_SPEC_TAG_PATTERN))
     827             :          {
     828      225309 :             continue;
     829             :          }
     830             : 
     831             :          /* and check if one of the tag patterns matches the tag, */
     832           0 :          if (0 == regexec(b->url->pattern.tag_regex, tag, 0, NULL, 0))
     833             :          {
     834             :             /* if it does, update the action bit map, */
     835           0 :             if (merge_current_action(csp->action, b->action))
     836             :             {
     837           0 :                log_error(LOG_LEVEL_ERROR,
     838             :                   "Out of memory while changing action bits");
     839             :             }
     840             :             /* and signal the change. */
     841           0 :             updated = 1;
     842             :          }
     843             :       }
     844             :    }
     845             : 
     846       10729 :    return updated;
     847             : }
     848             : 
     849             : 
     850             : /*********************************************************************
     851             :  *
     852             :  * Function    :  check_negative_tag_patterns
     853             :  *
     854             :  * Description :  Updates the action bits based on NO-*-TAG patterns.
     855             :  *
     856             :  * Parameters  :
     857             :  *          1  :  csp = Current client state (buffers, headers, etc...)
     858             :  *          2  :  flag = The tag pattern type
     859             :  *
     860             :  * Returns     :  JB_ERR_OK in case off success, or
     861             :  *                JB_ERR_MEMORY on out-of-memory error.
     862             :  *
     863             :  *********************************************************************/
     864       39281 : jb_err check_negative_tag_patterns(struct client_state *csp, unsigned int flag)
     865             : {
     866             :    struct list_entry *tag;
     867             :    struct file_list *fl;
     868       39281 :    struct url_actions *b = NULL;
     869             :    int i;
     870             : 
     871     3967381 :    for (i = 0; i < MAX_AF_FILES; i++)
     872             :    {
     873     3928100 :       fl = csp->actions_list[i];
     874     3928100 :       if ((fl == NULL) || ((b = fl->f) == NULL))
     875             :       {
     876     3849538 :          continue;
     877             :       }
     878      903463 :       for (b = b->next; NULL != b; b = b->next)
     879             :       {
     880      824901 :          int tag_found = 0;
     881      824901 :          if (0 == (b->url->flags & flag))
     882             :          {
     883      824901 :             continue;
     884             :          }
     885           0 :          for (tag = csp->tags->first; NULL != tag; tag = tag->next)
     886             :          {
     887           0 :             if (0 == regexec(b->url->pattern.tag_regex, tag->str, 0, NULL, 0))
     888             :             {
     889             :                /*
     890             :                 * The pattern matches at least one tag, thus the action
     891             :                 * section doesn't apply and we don't need to look at the
     892             :                 * other tags.
     893             :                 */
     894           0 :                tag_found = 1;
     895           0 :                break;
     896             :             }
     897             :          }
     898           0 :          if (!tag_found)
     899             :          {
     900             :             /*
     901             :              * The pattern doesn't match any tags,
     902             :              * thus the action section applies.
     903             :              */
     904           0 :             if (merge_current_action(csp->action, b->action))
     905             :             {
     906           0 :                log_error(LOG_LEVEL_ERROR,
     907             :                   "Out of memory while changing action bits");
     908           0 :                return JB_ERR_MEMORY;
     909             :             }
     910           0 :             log_error(LOG_LEVEL_HEADER, "Updated action bits based on: %s",
     911             :                b->url->spec);
     912             :          }
     913             :       }
     914             :    }
     915             : 
     916       39281 :    return JB_ERR_OK;
     917             : }
     918             : 
     919             : 
     920             : /*********************************************************************
     921             :  *
     922             :  * Function    :  free_current_action
     923             :  *
     924             :  * Description :  Free memory used by a current_action_spec.
     925             :  *                Does not free the current_action_spec itself.
     926             :  *
     927             :  * Parameters  :
     928             :  *          1  :  src = Source to free.
     929             :  *
     930             :  * Returns     :  N/A
     931             :  *
     932             :  *********************************************************************/
     933       35760 : void free_current_action(struct current_action_spec *src)
     934             : {
     935             :    int i;
     936             : 
     937      750960 :    for (i = 0; i < ACTION_STRING_COUNT; i++)
     938             :    {
     939      715200 :       freez(src->string[i]);
     940             :    }
     941             : 
     942      357600 :    for (i = 0; i < ACTION_MULTI_COUNT; i++)
     943             :    {
     944      321840 :       destroy_list(src->multi[i]);
     945             :    }
     946             : 
     947       35760 :    memset(src, '\0', sizeof(*src));
     948       35760 : }
     949             : 
     950             : 
     951             : static struct file_list *current_actions_file[MAX_AF_FILES]  = {
     952             :    NULL, NULL, NULL, NULL, NULL,
     953             :    NULL, NULL, NULL, NULL, NULL
     954             : };
     955             : 
     956             : 
     957             : #ifdef FEATURE_GRACEFUL_TERMINATION
     958             : /*********************************************************************
     959             :  *
     960             :  * Function    :  unload_current_actions_file
     961             :  *
     962             :  * Description :  Unloads current actions file - reset to state at
     963             :  *                beginning of program.
     964             :  *
     965             :  * Parameters  :  None
     966             :  *
     967             :  * Returns     :  N/A
     968             :  *
     969             :  *********************************************************************/
     970             : void unload_current_actions_file(void)
     971             : {
     972             :    int i;
     973             : 
     974             :    for (i = 0; i < MAX_AF_FILES; i++)
     975             :    {
     976             :       if (current_actions_file[i])
     977             :       {
     978             :          current_actions_file[i]->unloader = unload_actions_file;
     979             :          current_actions_file[i] = NULL;
     980             :       }
     981             :    }
     982             : }
     983             : #endif /* FEATURE_GRACEFUL_TERMINATION */
     984             : 
     985             : 
     986             : /*********************************************************************
     987             :  *
     988             :  * Function    :  unload_actions_file
     989             :  *
     990             :  * Description :  Unloads an actions module.
     991             :  *
     992             :  * Parameters  :
     993             :  *          1  :  file_data = the data structure associated with the
     994             :  *                            actions file.
     995             :  *
     996             :  * Returns     :  N/A
     997             :  *
     998             :  *********************************************************************/
     999           0 : void unload_actions_file(void *file_data)
    1000             : {
    1001             :    struct url_actions * next;
    1002           0 :    struct url_actions * cur = (struct url_actions *)file_data;
    1003           0 :    while (cur != NULL)
    1004             :    {
    1005           0 :       next = cur->next;
    1006           0 :       free_pattern_spec(cur->url);
    1007           0 :       if ((next == NULL) || (next->action != cur->action))
    1008             :       {
    1009             :          /*
    1010             :           * As the action settings might be shared,
    1011             :           * we can only free them if the current
    1012             :           * url pattern is the last one, or if the
    1013             :           * next one is using different settings.
    1014             :           */
    1015           0 :          free_action_spec(cur->action);
    1016             :       }
    1017           0 :       freez(cur);
    1018           0 :       cur = next;
    1019             :    }
    1020           0 : }
    1021             : 
    1022             : 
    1023             : /*********************************************************************
    1024             :  *
    1025             :  * Function    :  free_alias_list
    1026             :  *
    1027             :  * Description :  Free memory used by a list of aliases.
    1028             :  *
    1029             :  * Parameters  :
    1030             :  *          1  :  alias_list = Linked list to free.
    1031             :  *
    1032             :  * Returns     :  N/A
    1033             :  *
    1034             :  *********************************************************************/
    1035       10082 : void free_alias_list(struct action_alias *alias_list)
    1036             : {
    1037       43794 :    while (alias_list != NULL)
    1038             :    {
    1039       33712 :       struct action_alias * next = alias_list->next;
    1040       33712 :       alias_list->next = NULL;
    1041       33712 :       freez(alias_list->name);
    1042       33712 :       free_action(alias_list->action);
    1043       33712 :       free(alias_list);
    1044       33712 :       alias_list = next;
    1045             :    }
    1046       10082 : }
    1047             : 
    1048             : 
    1049             : /*********************************************************************
    1050             :  *
    1051             :  * Function    :  load_action_files
    1052             :  *
    1053             :  * Description :  Read and parse all the action files and add to files
    1054             :  *                list.
    1055             :  *
    1056             :  * Parameters  :
    1057             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    1058             :  *
    1059             :  * Returns     :  0 => Ok, everything else is an error.
    1060             :  *
    1061             :  *********************************************************************/
    1062        6196 : int load_action_files(struct client_state *csp)
    1063             : {
    1064             :    int i;
    1065             :    int result;
    1066             : 
    1067      625796 :    for (i = 0; i < MAX_AF_FILES; i++)
    1068             :    {
    1069      619600 :       if (csp->config->actions_file[i])
    1070             :       {
    1071       12392 :          result = load_one_actions_file(csp, i);
    1072       12392 :          if (result)
    1073             :          {
    1074           0 :             return result;
    1075             :          }
    1076             :       }
    1077      607208 :       else if (current_actions_file[i])
    1078             :       {
    1079           0 :          current_actions_file[i]->unloader = unload_actions_file;
    1080           0 :          current_actions_file[i] = NULL;
    1081             :       }
    1082             :    }
    1083             : 
    1084        6196 :    return 0;
    1085             : }
    1086             : 
    1087             : 
    1088             : /*********************************************************************
    1089             :  *
    1090             :  * Function    :  filter_type_to_string
    1091             :  *
    1092             :  * Description :  Converts a filter type enum into a string.
    1093             :  *
    1094             :  * Parameters  :
    1095             :  *          1  :  filter_type = filter_type as enum
    1096             :  *
    1097             :  * Returns     :  Pointer to static string.
    1098             :  *
    1099             :  *********************************************************************/
    1100           0 : static const char *filter_type_to_string(enum filter_type filter_type)
    1101             : {
    1102           0 :    switch (filter_type)
    1103             :    {
    1104           0 :    case FT_CONTENT_FILTER:
    1105           0 :       return "content filter";
    1106           0 :    case FT_CLIENT_HEADER_FILTER:
    1107           0 :       return "client-header filter";
    1108           0 :    case FT_SERVER_HEADER_FILTER:
    1109           0 :       return "server-header filter";
    1110           0 :    case FT_CLIENT_HEADER_TAGGER:
    1111           0 :       return "client-header tagger";
    1112           0 :    case FT_SERVER_HEADER_TAGGER:
    1113           0 :       return "server-header tagger";
    1114             : #ifdef FEATURE_EXTERNAL_FILTERS
    1115           0 :    case FT_EXTERNAL_CONTENT_FILTER:
    1116           0 :       return "external content filter";
    1117             : #endif
    1118           0 :    case FT_SUPPRESS_TAG:
    1119           0 :       return "suppress tag filter";
    1120           0 :    case FT_CLIENT_BODY_FILTER:
    1121           0 :       return "client body filter";
    1122           0 :    case FT_INVALID_FILTER:
    1123           0 :       return "invalid filter type";
    1124             :    }
    1125             : 
    1126           0 :    return "unknown filter type";
    1127             : 
    1128             : }
    1129             : 
    1130             : /*********************************************************************
    1131             :  *
    1132             :  * Function    :  referenced_filters_are_missing
    1133             :  *
    1134             :  * Description :  Checks if any filters of a certain type referenced
    1135             :  *                in an action spec are missing.
    1136             :  *
    1137             :  * Parameters  :
    1138             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    1139             :  *          2  :  cur_action = The action spec to check.
    1140             :  *          3  :  multi_index = The index where to look for the filter.
    1141             :  *          4  :  filter_type = The filter type the caller is interested in.
    1142             :  *
    1143             :  * Returns     :  0 => All referenced filters exist, everything else is an error.
    1144             :  *
    1145             :  *********************************************************************/
    1146      371760 : static int referenced_filters_are_missing(const struct client_state *csp,
    1147             :    const struct action_spec *cur_action, int multi_index, enum filter_type filter_type)
    1148             : {
    1149             :    struct list_entry *filtername;
    1150             : 
    1151      402740 :    for (filtername = cur_action->multi_add[multi_index]->first;
    1152       30980 :         filtername; filtername = filtername->next)
    1153             :    {
    1154       30980 :       if (NULL == get_filter(csp, filtername->str, filter_type))
    1155             :       {
    1156           0 :          log_error(LOG_LEVEL_ERROR, "Missing %s '%s'",
    1157             :             filter_type_to_string(filter_type), filtername->str);
    1158           0 :          return 1;
    1159             :       }
    1160             :    }
    1161             : 
    1162      371760 :    return 0;
    1163             : 
    1164             : }
    1165             : 
    1166             : 
    1167             : /*********************************************************************
    1168             :  *
    1169             :  * Function    :  action_spec_is_valid
    1170             :  *
    1171             :  * Description :  Should eventually figure out if an action spec
    1172             :  *                is valid, but currently only checks that the
    1173             :  *                referenced filters are accounted for.
    1174             :  *
    1175             :  * Parameters  :
    1176             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    1177             :  *          2  :  cur_action = The action spec to check.
    1178             :  *
    1179             :  * Returns     :  0 => No problems detected, everything else is an error.
    1180             :  *
    1181             :  *********************************************************************/
    1182       61960 : static int action_spec_is_valid(struct client_state *csp, const struct action_spec *cur_action)
    1183             : {
    1184             :    struct {
    1185             :       int multi_index;
    1186             :       enum filter_type filter_type;
    1187       61960 :    } filter_map[] = {
    1188             :       {ACTION_MULTI_FILTER, FT_CONTENT_FILTER},
    1189             :       {ACTION_MULTI_CLIENT_HEADER_FILTER, FT_CLIENT_HEADER_FILTER},
    1190             :       {ACTION_MULTI_SERVER_HEADER_FILTER, FT_SERVER_HEADER_FILTER},
    1191             :       {ACTION_MULTI_CLIENT_HEADER_TAGGER, FT_CLIENT_HEADER_TAGGER},
    1192             :       {ACTION_MULTI_SERVER_HEADER_TAGGER, FT_SERVER_HEADER_TAGGER},
    1193             :       {ACTION_MULTI_CLIENT_BODY_FILTER, FT_CLIENT_BODY_FILTER}
    1194             :    };
    1195       61960 :    int errors = 0;
    1196             :    int i;
    1197             : 
    1198      433720 :    for (i = 0; i < SZ(filter_map); i++)
    1199             :    {
    1200      371760 :       errors += referenced_filters_are_missing(csp, cur_action,
    1201             :          filter_map[i].multi_index, filter_map[i].filter_type);
    1202             :    }
    1203             : 
    1204       61960 :    return errors;
    1205             : 
    1206             : }
    1207             : 
    1208             : 
    1209             : /*********************************************************************
    1210             :  *
    1211             :  * Function    :  load_one_actions_file
    1212             :  *
    1213             :  * Description :  Read and parse a action file and add to files
    1214             :  *                list.
    1215             :  *
    1216             :  * Parameters  :
    1217             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    1218             :  *          2  :  fileid = File index to load.
    1219             :  *
    1220             :  * Returns     :  0 => Ok, everything else is an error.
    1221             :  *
    1222             :  *********************************************************************/
    1223             : #ifndef FUZZ
    1224             : static
    1225             : #endif
    1226       12392 : int load_one_actions_file(struct client_state *csp, int fileid)
    1227             : {
    1228             : 
    1229             :    /*
    1230             :     * Parser mode.
    1231             :     * Note: Keep these in the order they occur in the file, they are
    1232             :     * sometimes tested with <=
    1233             :     */
    1234             :    enum {
    1235             :       MODE_START_OF_FILE = 1,
    1236             :       MODE_SETTINGS      = 2,
    1237             :       MODE_DESCRIPTION   = 3,
    1238             :       MODE_ALIAS         = 4,
    1239             :       MODE_ACTIONS       = 5
    1240             :    } mode;
    1241             : 
    1242             :    FILE *fp;
    1243             :    struct url_actions *last_perm;
    1244             :    struct url_actions *perm;
    1245             :    char  *buf;
    1246             :    struct file_list *fs;
    1247       12392 :    struct action_spec * cur_action = NULL;
    1248       12392 :    int cur_action_used = 0;
    1249       12392 :    struct action_alias * alias_list = NULL;
    1250       12392 :    unsigned long linenum = 0;
    1251       12392 :    mode = MODE_START_OF_FILE;
    1252             : 
    1253       12392 :    if (!check_file_changed(current_actions_file[fileid], csp->config->actions_file[fileid], &fs))
    1254             :    {
    1255             :       /* No need to load */
    1256        6196 :       csp->actions_list[fileid] = current_actions_file[fileid];
    1257        6196 :       return 0;
    1258             :    }
    1259        6196 :    if (!fs)
    1260             :    {
    1261           0 :       log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': %E. "
    1262             :          "Note that beginning with Privoxy 3.0.7, actions files have to be specified "
    1263           0 :          "with their complete file names.", csp->config->actions_file[fileid]);
    1264           0 :       return 1; /* never get here */
    1265             :    }
    1266             : 
    1267        6196 :    fs->f = last_perm = zalloc_or_die(sizeof(*last_perm));
    1268             : 
    1269        6196 :    if ((fp = fopen(csp->config->actions_file[fileid], "r")) == NULL)
    1270             :    {
    1271           0 :       log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error opening file: %E",
    1272           0 :                 csp->config->actions_file[fileid]);
    1273           0 :       return 1; /* never get here */
    1274             :    }
    1275             : 
    1276        6196 :    log_error(LOG_LEVEL_INFO, "Loading actions file: %s", csp->config->actions_file[fileid]);
    1277             : 
    1278      164194 :    while (read_config_line(fp, &linenum, &buf) != NULL)
    1279             :    {
    1280      157998 :       if (*buf == '{')
    1281             :       {
    1282             :          /* It's a header block */
    1283       68156 :          if (buf[1] == '{')
    1284             :          {
    1285             :             /* It's {{settings}} or {{alias}} */
    1286        6196 :             size_t len = strlen(buf);
    1287        6196 :             char * start = buf + 2;
    1288        6196 :             char * end = buf + len - 1;
    1289        6196 :             if ((len < (size_t)5) || (*end-- != '}') || (*end-- != '}'))
    1290             :             {
    1291             :                /* too short */
    1292           0 :                fclose(fp);
    1293           0 :                log_error(LOG_LEVEL_FATAL,
    1294             :                   "can't load actions file '%s': invalid line (%lu): %s",
    1295           0 :                   csp->config->actions_file[fileid], linenum, buf);
    1296           0 :                return 1; /* never get here */
    1297             :             }
    1298             : 
    1299             :             /* Trim leading and trailing whitespace. */
    1300        6196 :             end[1] = '\0';
    1301        6196 :             chomp(start);
    1302             : 
    1303        6196 :             if (*start == '\0')
    1304             :             {
    1305             :                /* too short */
    1306           0 :                fclose(fp);
    1307           0 :                log_error(LOG_LEVEL_FATAL,
    1308             :                   "can't load actions file '%s': invalid line (%lu): {{ }}",
    1309           0 :                   csp->config->actions_file[fileid], linenum);
    1310           0 :                return 1; /* never get here */
    1311             :             }
    1312             : 
    1313             :             /*
    1314             :              * An actionsfile can optionally contain the following blocks.
    1315             :              * They *MUST* be in this order, to simplify processing:
    1316             :              *
    1317             :              * {{settings}}
    1318             :              * name=value...
    1319             :              *
    1320             :              * {{description}}
    1321             :              * ...free text, format TBD, but no line may start with a '{'...
    1322             :              *
    1323             :              * {{alias}}
    1324             :              * name=actions...
    1325             :              *
    1326             :              * The actual actions must be *after* these special blocks.
    1327             :              * None of these special blocks may be repeated.
    1328             :              *
    1329             :              */
    1330        6196 :             if (0 == strcmpic(start, "settings"))
    1331             :             {
    1332             :                /* it's a {{settings}} block */
    1333        3098 :                if (mode >= MODE_SETTINGS)
    1334             :                {
    1335             :                   /* {{settings}} must be first thing in file and must only
    1336             :                    * appear once.
    1337             :                    */
    1338           0 :                   fclose(fp);
    1339           0 :                   log_error(LOG_LEVEL_FATAL,
    1340             :                      "can't load actions file '%s': line %lu: {{settings}} must only appear once, and it must be before anything else.",
    1341           0 :                      csp->config->actions_file[fileid], linenum);
    1342             :                }
    1343        3098 :                mode = MODE_SETTINGS;
    1344             :             }
    1345        3098 :             else if (0 == strcmpic(start, "description"))
    1346             :             {
    1347             :                /* it's a {{description}} block */
    1348           0 :                if (mode >= MODE_DESCRIPTION)
    1349             :                {
    1350             :                   /* {{description}} is a singleton and only {{settings}} may proceed it
    1351             :                    */
    1352           0 :                   fclose(fp);
    1353           0 :                   log_error(LOG_LEVEL_FATAL,
    1354             :                      "can't load actions file '%s': line %lu: {{description}} must only appear once, and only a {{settings}} block may be above it.",
    1355           0 :                      csp->config->actions_file[fileid], linenum);
    1356             :                }
    1357           0 :                mode = MODE_DESCRIPTION;
    1358             :             }
    1359        3098 :             else if (0 == strcmpic(start, "alias"))
    1360             :             {
    1361             :                /* it's an {{alias}} block */
    1362        3098 :                if (mode >= MODE_ALIAS)
    1363             :                {
    1364             :                   /* {{alias}} must be first thing in file, possibly after
    1365             :                    * {{settings}} and {{description}}
    1366             :                    *
    1367             :                    * {{alias}} must only appear once.
    1368             :                    *
    1369             :                    * Note that these are new restrictions introduced in
    1370             :                    * v2.9.10 in order to make actionsfile editing simpler.
    1371             :                    * (Otherwise, reordering actionsfile entries without
    1372             :                    * completely rewriting the file becomes non-trivial)
    1373             :                    */
    1374           0 :                   fclose(fp);
    1375           0 :                   log_error(LOG_LEVEL_FATAL,
    1376             :                      "can't load actions file '%s': line %lu: {{alias}} must only appear once, and it must be before all actions.",
    1377           0 :                      csp->config->actions_file[fileid], linenum);
    1378             :                }
    1379        3098 :                mode = MODE_ALIAS;
    1380             :             }
    1381             :             else
    1382             :             {
    1383             :                /* invalid {{something}} block */
    1384           0 :                fclose(fp);
    1385           0 :                log_error(LOG_LEVEL_FATAL,
    1386             :                   "can't load actions file '%s': invalid line (%lu): {{%s}}",
    1387           0 :                   csp->config->actions_file[fileid], linenum, start);
    1388           0 :                return 1; /* never get here */
    1389             :             }
    1390             :          }
    1391             :          else
    1392             :          {
    1393             :             /* It's an actions block */
    1394             : 
    1395             :             char *actions_buf;
    1396             :             char * end;
    1397             : 
    1398             :             /* set mode */
    1399       61960 :             mode = MODE_ACTIONS;
    1400             : 
    1401             :             /* free old action */
    1402       61960 :             if (cur_action)
    1403             :             {
    1404       55764 :                if (!cur_action_used)
    1405             :                {
    1406           0 :                   free_action_spec(cur_action);
    1407             :                }
    1408       55764 :                cur_action = NULL;
    1409             :             }
    1410       61960 :             cur_action_used = 0;
    1411       61960 :             cur_action = zalloc_or_die(sizeof(*cur_action));
    1412       61960 :             init_action(cur_action);
    1413             : 
    1414             :             /*
    1415             :              * Copy the buffer before messing with it as we may need the
    1416             :              * unmodified version in for the fatal error messages. Given
    1417             :              * that this is not a common event, we could instead simply
    1418             :              * read the line again.
    1419             :              *
    1420             :              * buf + 1 to skip the leading '{'
    1421             :              */
    1422       61960 :             actions_buf = end = strdup_or_die(buf + 1);
    1423             : 
    1424             :             /* check we have a trailing } and then trim it */
    1425       61960 :             if (strlen(actions_buf))
    1426             :             {
    1427       61960 :                end += strlen(actions_buf) - 1;
    1428             :             }
    1429       61960 :             if (*end != '}')
    1430             :             {
    1431             :                /* No closing } */
    1432           0 :                fclose(fp);
    1433           0 :                freez(actions_buf);
    1434           0 :                log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': "
    1435             :                   "Missing trailing '}' in action section starting at line (%lu): %s",
    1436           0 :                   csp->config->actions_file[fileid], linenum, buf);
    1437           0 :                return 1; /* never get here */
    1438             :             }
    1439       61960 :             *end = '\0';
    1440             : 
    1441             :             /* trim any whitespace immediately inside {} */
    1442       61960 :             chomp(actions_buf);
    1443             : 
    1444       61960 :             if (get_actions(actions_buf, alias_list, cur_action))
    1445             :             {
    1446             :                /* error */
    1447           0 :                fclose(fp);
    1448           0 :                freez(actions_buf);
    1449           0 :                log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': "
    1450             :                   "can't completely parse the action section starting at line (%lu): %s",
    1451           0 :                   csp->config->actions_file[fileid], linenum, buf);
    1452           0 :                return 1; /* never get here */
    1453             :             }
    1454             : 
    1455       61960 :             if (action_spec_is_valid(csp, cur_action))
    1456             :             {
    1457           0 :                log_error(LOG_LEVEL_ERROR, "Invalid action section in file '%s', "
    1458             :                   "starting at line %lu: %s",
    1459           0 :                   csp->config->actions_file[fileid], linenum, buf);
    1460             :             }
    1461             : 
    1462       61960 :             freez(actions_buf);
    1463             :          }
    1464             :       }
    1465       89842 :       else if (mode == MODE_SETTINGS)
    1466             :       {
    1467             :          /*
    1468             :           * Part of the {{settings}} block.
    1469             :           * For now only serves to check if the file's minimum Privoxy
    1470             :           * version requirement is met, but we may want to read & check
    1471             :           * permissions when we go multi-user.
    1472             :           */
    1473        3098 :          if (!strncmp(buf, "for-privoxy-version=", 20))
    1474             :          {
    1475             :             char *version_string, *fields[3];
    1476             :             int num_fields;
    1477             : 
    1478        3098 :             version_string = strdup_or_die(buf + 20);
    1479             : 
    1480        3098 :             num_fields = ssplit(version_string, ".", fields, SZ(fields));
    1481             : 
    1482        3098 :             if (num_fields < 1 || atoi(fields[0]) == 0)
    1483             :             {
    1484           0 :                log_error(LOG_LEVEL_ERROR,
    1485             :                  "While loading actions file '%s': invalid line (%lu): %s",
    1486           0 :                   csp->config->actions_file[fileid], linenum, buf);
    1487             :             }
    1488        3098 :             else if (                  (atoi(fields[0]) > VERSION_MAJOR)
    1489        3098 :                || ((num_fields > 1) && (atoi(fields[1]) > VERSION_MINOR))
    1490        3098 :                || ((num_fields > 2) && (atoi(fields[2]) > VERSION_POINT)))
    1491             :             {
    1492           0 :                fclose(fp);
    1493           0 :                log_error(LOG_LEVEL_FATAL,
    1494             :                          "Actions file '%s', line %lu requires newer Privoxy version: %s",
    1495           0 :                          csp->config->actions_file[fileid], linenum, buf);
    1496           0 :                return 1; /* never get here */
    1497             :             }
    1498        3098 :             free(version_string);
    1499             :          }
    1500             :       }
    1501       86744 :       else if (mode == MODE_DESCRIPTION)
    1502             :       {
    1503             :          /*
    1504             :           * Part of the {{description}} block.
    1505             :           * Ignore for now.
    1506             :           */
    1507             :       }
    1508       86744 :       else if (mode == MODE_ALIAS)
    1509             :       {
    1510             :          /*
    1511             :           * define an alias
    1512             :           */
    1513             :          char  actions_buf[BUFFER_SIZE];
    1514             :          struct action_alias * new_alias;
    1515             : 
    1516       21686 :          char * start = strchr(buf, '=');
    1517       21686 :          char * end = start;
    1518             : 
    1519       21686 :          if ((start == NULL) || (start == buf))
    1520             :          {
    1521           0 :             log_error(LOG_LEVEL_FATAL,
    1522             :                "can't load actions file '%s': invalid alias line (%lu): %s",
    1523           0 :                csp->config->actions_file[fileid], linenum, buf);
    1524           0 :             return 1; /* never get here */
    1525             :          }
    1526             : 
    1527       21686 :          new_alias = zalloc_or_die(sizeof(*new_alias));
    1528             : 
    1529             :          /* Eat any the whitespace before the '=' */
    1530       21686 :          end--;
    1531       96038 :          while ((*end == ' ') || (*end == '\t'))
    1532             :          {
    1533             :             /*
    1534             :              * we already know we must have at least 1 non-ws char
    1535             :              * at start of buf - no need to check
    1536             :              */
    1537       74352 :             end--;
    1538             :          }
    1539       21686 :          end[1] = '\0';
    1540             : 
    1541             :          /* Eat any the whitespace after the '=' */
    1542       21686 :          start++;
    1543       43372 :          while ((*start == ' ') || (*start == '\t'))
    1544             :          {
    1545       21686 :             start++;
    1546             :          }
    1547       21686 :          if (*start == '\0')
    1548             :          {
    1549           0 :             log_error(LOG_LEVEL_FATAL,
    1550             :                "can't load actions file '%s': invalid alias line (%lu): %s",
    1551           0 :                csp->config->actions_file[fileid], linenum, buf);
    1552           0 :             return 1; /* never get here */
    1553             :          }
    1554             : 
    1555       21686 :          new_alias->name = strdup_or_die(buf);
    1556             : 
    1557       21686 :          strlcpy(actions_buf, start, sizeof(actions_buf));
    1558             : 
    1559       21686 :          if (get_actions(actions_buf, alias_list, new_alias->action))
    1560             :          {
    1561             :             /* error */
    1562           0 :             fclose(fp);
    1563           0 :             log_error(LOG_LEVEL_FATAL,
    1564             :                "can't load actions file '%s': invalid alias line (%lu): %s = %s",
    1565           0 :                csp->config->actions_file[fileid], linenum, buf, start);
    1566           0 :             return 1; /* never get here */
    1567             :          }
    1568             : 
    1569             :          /* add to list */
    1570       21686 :          new_alias->next = alias_list;
    1571       21686 :          alias_list = new_alias;
    1572             :       }
    1573       65058 :       else if (mode == MODE_ACTIONS)
    1574             :       {
    1575             :          /* it's an URL pattern */
    1576             : 
    1577             :          /* allocate a new node */
    1578       65058 :          perm = zalloc_or_die(sizeof(*perm));
    1579             : 
    1580       65058 :          perm->action = cur_action;
    1581       65058 :          cur_action_used = 1;
    1582             : 
    1583             :          /* Save the URL pattern */
    1584       65058 :          if (create_pattern_spec(perm->url, buf))
    1585             :          {
    1586           0 :             fclose(fp);
    1587           0 :             log_error(LOG_LEVEL_FATAL,
    1588             :                "can't load actions file '%s': line %lu: cannot create URL or TAG pattern from: %s",
    1589           0 :                csp->config->actions_file[fileid], linenum, buf);
    1590           0 :             return 1; /* never get here */
    1591             :          }
    1592             : 
    1593             :          /* add it to the list */
    1594       65058 :          last_perm->next = perm;
    1595       65058 :          last_perm = perm;
    1596             :       }
    1597           0 :       else if (mode == MODE_START_OF_FILE)
    1598             :       {
    1599             :          /* oops - please have a {} line as 1st line in file. */
    1600           0 :          fclose(fp);
    1601           0 :          log_error(LOG_LEVEL_FATAL,
    1602             :             "can't load actions file '%s': line %lu should begin with a '{': %s",
    1603           0 :             csp->config->actions_file[fileid], linenum, buf);
    1604           0 :          return 1; /* never get here */
    1605             :       }
    1606             :       else
    1607             :       {
    1608             :          /* How did we get here? This is impossible! */
    1609           0 :          fclose(fp);
    1610           0 :          log_error(LOG_LEVEL_FATAL,
    1611             :             "can't load actions file '%s': INTERNAL ERROR - mode = %d",
    1612           0 :             csp->config->actions_file[fileid], mode);
    1613           0 :          return 1; /* never get here */
    1614             :       }
    1615      157998 :       freez(buf);
    1616             :    }
    1617             : 
    1618        6196 :    fclose(fp);
    1619             : 
    1620        6196 :    if (!cur_action_used)
    1621             :    {
    1622           0 :       free_action_spec(cur_action);
    1623             :    }
    1624        6196 :    free_alias_list(alias_list);
    1625             : 
    1626             :    /* the old one is now obsolete */
    1627        6196 :    if (current_actions_file[fileid])
    1628             :    {
    1629           0 :       current_actions_file[fileid]->unloader = unload_actions_file;
    1630             :    }
    1631             : 
    1632        6196 :    fs->next    = files->next;
    1633        6196 :    files->next = fs;
    1634        6196 :    current_actions_file[fileid] = fs;
    1635             : 
    1636        6196 :    csp->actions_list[fileid] = fs;
    1637             : 
    1638        6196 :    return(0);
    1639             : 
    1640             : }
    1641             : 
    1642             : 
    1643             : /*********************************************************************
    1644             :  *
    1645             :  * Function    :  actions_to_text
    1646             :  *
    1647             :  * Description :  Converts a actionsfile entry from the internal
    1648             :  *                structure into a text line.  The output is split
    1649             :  *                into one line for each action with line continuation.
    1650             :  *
    1651             :  * Parameters  :
    1652             :  *          1  :  action = The action to format.
    1653             :  *
    1654             :  * Returns     :  A string.  Caller must free it.
    1655             :  *                NULL on out-of-memory error.
    1656             :  *
    1657             :  *********************************************************************/
    1658         542 : char * actions_to_text(const struct action_spec *action)
    1659             : {
    1660         542 :    unsigned long mask = action->mask;
    1661         542 :    unsigned long add  = action->add;
    1662         542 :    char *result = strdup_or_die("");
    1663             :    struct list_entry * lst;
    1664             : 
    1665             :    /* sanity - prevents "-feature +feature" */
    1666         542 :    mask |= add;
    1667             : 
    1668             : 
    1669             : #define DEFINE_ACTION_BOOL(__name, __bit)          \
    1670             :    if (!(mask & __bit))                            \
    1671             :    {                                               \
    1672             :       string_append(&result, " -" __name " \\\n"); \
    1673             :    }                                               \
    1674             :    else if (add & __bit)                           \
    1675             :    {                                               \
    1676             :       string_append(&result, " +" __name " \\\n"); \
    1677             :    }
    1678             : 
    1679             : #define DEFINE_ACTION_STRING(__name, __bit, __index)   \
    1680             :    if (!(mask & __bit))                                \
    1681             :    {                                                   \
    1682             :       string_append(&result, " -" __name " \\\n");     \
    1683             :    }                                                   \
    1684             :    else if (add & __bit)                               \
    1685             :    {                                                   \
    1686             :       string_append(&result, " +" __name "{");         \
    1687             :       string_append(&result, action->string[__index]); \
    1688             :       string_append(&result, "} \\\n");                \
    1689             :    }
    1690             : 
    1691             : #define DEFINE_ACTION_MULTI(__name, __index)         \
    1692             :    if (action->multi_remove_all[__index])            \
    1693             :    {                                                 \
    1694             :       string_append(&result, " -" __name " \\\n");   \
    1695             :    }                                                 \
    1696             :    else                                              \
    1697             :    {                                                 \
    1698             :       lst = action->multi_remove[__index]->first;    \
    1699             :       while (lst)                                    \
    1700             :       {                                              \
    1701             :          string_append(&result, " -" __name "{");    \
    1702             :          string_append(&result, lst->str);           \
    1703             :          string_append(&result, "} \\\n");           \
    1704             :          lst = lst->next;                            \
    1705             :       }                                              \
    1706             :    }                                                 \
    1707             :    lst = action->multi_add[__index]->first;          \
    1708             :    while (lst)                                       \
    1709             :    {                                                 \
    1710             :       string_append(&result, " +" __name "{");       \
    1711             :       string_append(&result, lst->str);              \
    1712             :       string_append(&result, "} \\\n");              \
    1713             :       lst = lst->next;                               \
    1714             :    }
    1715             : 
    1716             : #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
    1717             : 
    1718             : #include "actionlist.h"
    1719             : 
    1720             : #undef DEFINE_ACTION_MULTI
    1721             : #undef DEFINE_ACTION_STRING
    1722             : #undef DEFINE_ACTION_BOOL
    1723             : #undef DEFINE_ACTION_ALIAS
    1724             : 
    1725         542 :    return result;
    1726             : }
    1727             : 
    1728             : 
    1729             : /*********************************************************************
    1730             :  *
    1731             :  * Function    :  actions_to_html
    1732             :  *
    1733             :  * Description :  Converts a actionsfile entry from numeric form
    1734             :  *                ("mask" and "add") to a <br>-separated HTML string
    1735             :  *                in which each action is linked to its chapter in
    1736             :  *                the user manual.
    1737             :  *
    1738             :  * Parameters  :
    1739             :  *          1  :  csp    = Client state (for config)
    1740             :  *          2  :  action = Action spec to be converted
    1741             :  *
    1742             :  * Returns     :  A string.  Caller must free it.
    1743             :  *                NULL on out-of-memory error.
    1744             :  *
    1745             :  *********************************************************************/
    1746       32964 : char * actions_to_html(const struct client_state *csp,
    1747             :                        const struct action_spec *action)
    1748             : {
    1749       32964 :    unsigned long mask = action->mask;
    1750       32964 :    unsigned long add  = action->add;
    1751       32964 :    char *result = strdup_or_die("");
    1752             :    struct list_entry * lst;
    1753             : 
    1754             :    /* sanity - prevents "-feature +feature" */
    1755       32964 :    mask |= add;
    1756             : 
    1757             : 
    1758             : #define DEFINE_ACTION_BOOL(__name, __bit)       \
    1759             :    if (!(mask & __bit))                         \
    1760             :    {                                            \
    1761             :       string_append(&result, "\n<br>-");        \
    1762             :       string_join(&result, add_help_link(__name, csp->config)); \
    1763             :    }                                            \
    1764             :    else if (add & __bit)                        \
    1765             :    {                                            \
    1766             :       string_append(&result, "\n<br>+");        \
    1767             :       string_join(&result, add_help_link(__name, csp->config)); \
    1768             :    }
    1769             : 
    1770             : #define DEFINE_ACTION_STRING(__name, __bit, __index) \
    1771             :    if (!(mask & __bit))                              \
    1772             :    {                                                 \
    1773             :       string_append(&result, "\n<br>-");             \
    1774             :       string_join(&result, add_help_link(__name, csp->config)); \
    1775             :    }                                                 \
    1776             :    else if (add & __bit)                             \
    1777             :    {                                                 \
    1778             :       string_append(&result, "\n<br>+");             \
    1779             :       string_join(&result, add_help_link(__name, csp->config)); \
    1780             :       string_append(&result, "{");                   \
    1781             :       string_join(&result, html_encode(action->string[__index])); \
    1782             :       string_append(&result, "}");                   \
    1783             :    }
    1784             : 
    1785             : #define DEFINE_ACTION_MULTI(__name, __index)          \
    1786             :    if (action->multi_remove_all[__index])             \
    1787             :    {                                                  \
    1788             :       string_append(&result, "\n<br>-");              \
    1789             :       string_join(&result, add_help_link(__name, csp->config)); \
    1790             :    }                                                  \
    1791             :    else                                               \
    1792             :    {                                                  \
    1793             :       lst = action->multi_remove[__index]->first;     \
    1794             :       while (lst)                                     \
    1795             :       {                                               \
    1796             :          string_append(&result, "\n<br>-");           \
    1797             :          string_join(&result, add_help_link(__name, csp->config)); \
    1798             :          string_append(&result, "{");                 \
    1799             :          string_join(&result, html_encode(lst->str)); \
    1800             :          string_append(&result, "}");                 \
    1801             :          lst = lst->next;                             \
    1802             :       }                                               \
    1803             :    }                                                  \
    1804             :    lst = action->multi_add[__index]->first;           \
    1805             :    while (lst)                                        \
    1806             :    {                                                  \
    1807             :       string_append(&result, "\n<br>+");              \
    1808             :       string_join(&result, add_help_link(__name, csp->config)); \
    1809             :       string_append(&result, "{");                    \
    1810             :       string_join(&result, html_encode(lst->str));    \
    1811             :       string_append(&result, "}");                    \
    1812             :       lst = lst->next;                                \
    1813             :    }
    1814             : 
    1815             : #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
    1816             : 
    1817             : #include "actionlist.h"
    1818             : 
    1819             : #undef DEFINE_ACTION_MULTI
    1820             : #undef DEFINE_ACTION_STRING
    1821             : #undef DEFINE_ACTION_BOOL
    1822             : #undef DEFINE_ACTION_ALIAS
    1823             : 
    1824             :    /* trim leading <br> */
    1825       32964 :    if (result && *result)
    1826             :    {
    1827       32964 :       char * s = result;
    1828       32964 :       result = strdup(result + 5);
    1829       32964 :       free(s);
    1830             :    }
    1831             : 
    1832       32964 :    return result;
    1833             : }
    1834             : 
    1835             : 
    1836             : /*********************************************************************
    1837             :  *
    1838             :  * Function    :  current_actions_to_html
    1839             :  *
    1840             :  * Description :  Converts a current action spec to a <br> separated HTML
    1841             :  *                text in which each action is linked to its chapter in
    1842             :  *                the user manual.
    1843             :  *
    1844             :  * Parameters  :
    1845             :  *          1  :  csp    = Client state (for config)
    1846             :  *          2  :  action = Current action spec to be converted
    1847             :  *
    1848             :  * Returns     :  A string.  Caller must free it.
    1849             :  *                NULL on out-of-memory error.
    1850             :  *
    1851             :  *********************************************************************/
    1852        2678 : char *current_action_to_html(const struct client_state *csp,
    1853             :                              const struct current_action_spec *action)
    1854             : {
    1855        2678 :    unsigned long flags  = action->flags;
    1856             :    struct list_entry * lst;
    1857        2678 :    char *result   = strdup_or_die("");
    1858        2678 :    char *active   = strdup_or_die("");
    1859        2678 :    char *inactive = strdup_or_die("");
    1860             : 
    1861             : #define DEFINE_ACTION_BOOL(__name, __bit)  \
    1862             :    if (flags & __bit)                      \
    1863             :    {                                       \
    1864             :       string_append(&active, "\n<br>+");   \
    1865             :       string_join(&active, add_help_link(__name, csp->config)); \
    1866             :    }                                       \
    1867             :    else                                    \
    1868             :    {                                       \
    1869             :       string_append(&inactive, "\n<br>-"); \
    1870             :       string_join(&inactive, add_help_link(__name, csp->config)); \
    1871             :    }
    1872             : 
    1873             : #define DEFINE_ACTION_STRING(__name, __bit, __index)   \
    1874             :    if (flags & __bit)                                  \
    1875             :    {                                                   \
    1876             :       string_append(&active, "\n<br>+");               \
    1877             :       string_join(&active, add_help_link(__name, csp->config)); \
    1878             :       string_append(&active, "{");                     \
    1879             :       string_join(&active, html_encode(action->string[__index])); \
    1880             :       string_append(&active, "}");                     \
    1881             :    }                                                   \
    1882             :    else                                                \
    1883             :    {                                                   \
    1884             :       string_append(&inactive, "\n<br>-");             \
    1885             :       string_join(&inactive, add_help_link(__name, csp->config)); \
    1886             :    }
    1887             : 
    1888             : #define DEFINE_ACTION_MULTI(__name, __index)           \
    1889             :    lst = action->multi[__index]->first;                \
    1890             :    if (lst == NULL)                                    \
    1891             :    {                                                   \
    1892             :       string_append(&inactive, "\n<br>-");             \
    1893             :       string_join(&inactive, add_help_link(__name, csp->config)); \
    1894             :    }                                                   \
    1895             :    else                                                \
    1896             :    {                                                   \
    1897             :       while (lst)                                      \
    1898             :       {                                                \
    1899             :          string_append(&active, "\n<br>+");            \
    1900             :          string_join(&active, add_help_link(__name, csp->config)); \
    1901             :          string_append(&active, "{");                  \
    1902             :          string_join(&active, html_encode(lst->str));  \
    1903             :          string_append(&active, "}");                  \
    1904             :          lst = lst->next;                              \
    1905             :       }                                                \
    1906             :    }
    1907             : 
    1908             : #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
    1909             : 
    1910             : #include "actionlist.h"
    1911             : 
    1912             : #undef DEFINE_ACTION_MULTI
    1913             : #undef DEFINE_ACTION_STRING
    1914             : #undef DEFINE_ACTION_BOOL
    1915             : #undef DEFINE_ACTION_ALIAS
    1916             : 
    1917        2678 :    if (active != NULL)
    1918             :    {
    1919        2678 :       string_append(&result, active);
    1920        2678 :       freez(active);
    1921             :    }
    1922        2678 :    string_append(&result, "\n<br>");
    1923        2678 :    if (inactive != NULL)
    1924             :    {
    1925        2678 :       string_append(&result, inactive);
    1926        2678 :       freez(inactive);
    1927             :    }
    1928        2678 :    return result;
    1929             : }
    1930             : 
    1931             : 
    1932             : /*********************************************************************
    1933             :  *
    1934             :  * Function    :  action_to_line_of_text
    1935             :  *
    1936             :  * Description :  Converts a action spec to a single text line
    1937             :  *                listing the enabled actions.
    1938             :  *
    1939             :  * Parameters  :
    1940             :  *          1  :  action = Current action spec to be converted
    1941             :  *
    1942             :  * Returns     :  A string. Caller must free it.
    1943             :  *                Out-of-memory errors are fatal.
    1944             :  *
    1945             :  *********************************************************************/
    1946       35226 : char *actions_to_line_of_text(const struct current_action_spec *action)
    1947             : {
    1948             :    char buffer[200];
    1949             :    struct list_entry *lst;
    1950             :    char *active;
    1951       35226 :    const unsigned long flags = action->flags;
    1952             : 
    1953       35226 :    active = strdup_or_die("");
    1954             : 
    1955             : #define DEFINE_ACTION_BOOL(__name, __bit)               \
    1956             :    if (flags & __bit)                                   \
    1957             :    {                                                    \
    1958             :       snprintf(buffer, sizeof(buffer), "+%s ", __name); \
    1959             :       string_append(&active, buffer);                   \
    1960             :    }                                                    \
    1961             : 
    1962             : #define DEFINE_ACTION_STRING(__name, __bit, __index)    \
    1963             :    if (flags & __bit)                                   \
    1964             :    {                                                    \
    1965             :       snprintf(buffer, sizeof(buffer), "+%s{%s} ",      \
    1966             :          __name, action->string[__index]);              \
    1967             :       string_append(&active, buffer);                   \
    1968             :    }                                                    \
    1969             : 
    1970             : #define DEFINE_ACTION_MULTI(__name, __index)            \
    1971             :    lst = action->multi[__index]->first;                 \
    1972             :    while (lst != NULL)                                  \
    1973             :    {                                                    \
    1974             :       snprintf(buffer, sizeof(buffer), "+%s{%s} ",      \
    1975             :          __name, lst->str);                             \
    1976             :       string_append(&active, buffer);                   \
    1977             :       lst = lst->next;                                  \
    1978             :    }                                                    \
    1979             : 
    1980             : #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
    1981             : 
    1982             : #include "actionlist.h"
    1983             : 
    1984             : #undef DEFINE_ACTION_MULTI
    1985             : #undef DEFINE_ACTION_STRING
    1986             : #undef DEFINE_ACTION_BOOL
    1987             : #undef DEFINE_ACTION_ALIAS
    1988             : 
    1989       35226 :    if (active == NULL)
    1990             :    {
    1991           0 :       log_error(LOG_LEVEL_FATAL, "Out of memory in action_to_line_of_text()");
    1992             :    }
    1993             : 
    1994       35226 :    return active;
    1995             : }

Generated by: LCOV version 1.14