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