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