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