create-package-feed.pl: Only generate checksums for files that make it into the feed
[privoxy.git] / actions.c
1 const char actions_rcs[] = "$Id: actions.c,v 1.96 2016/02/26 12:29:38 fabiankeil Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/actions.c,v $
5  *
6  * Purpose     :  Declares functions to work with actions files
7  *
8  * Copyright   :  Written by and Copyright (C) 2001-2016 the
9  *                Privoxy team. http://www.privoxy.org/
10  *
11  *                Based on the Internet Junkbuster originally written
12  *                by and Copyright (C) 1997 Anonymous Coders and
13  *                Junkbusters Corporation.  http://www.junkbusters.com
14  *
15  *                This program is free software; you can redistribute it
16  *                and/or modify it under the terms of the GNU General
17  *                Public License as published by the Free Software
18  *                Foundation; either version 2 of the License, or (at
19  *                your option) any later version.
20  *
21  *                This program is distributed in the hope that it will
22  *                be useful, but WITHOUT ANY WARRANTY; without even the
23  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
24  *                PARTICULAR PURPOSE.  See the GNU General Public
25  *                License for more details.
26  *
27  *                The GNU General Public License should be included with
28  *                this file.  If not, you can view it at
29  *                http://www.gnu.org/copyleft/gpl.html
30  *                or write to the Free Software Foundation, Inc., 59
31  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
32  *
33  *********************************************************************/
34
35
36 #include "config.h"
37
38 #include <stdio.h>
39 #include <string.h>
40 #include <assert.h>
41 #include <stdlib.h>
42
43 #ifdef FEATURE_PTHREAD
44 #include <pthread.h>
45 #endif
46
47 #include "project.h"
48 #include "jcc.h"
49 #include "list.h"
50 #include "actions.h"
51 #include "miscutil.h"
52 #include "errlog.h"
53 #include "loaders.h"
54 #include "encode.h"
55 #include "urlmatch.h"
56 #include "cgi.h"
57 #include "ssplit.h"
58 #include "filters.h"
59
60 const char actions_h_rcs[] = ACTIONS_H_VERSION;
61
62
63 /*
64  * We need the main list of options.
65  *
66  * First, we need a way to tell between boolean, string, and multi-string
67  * options.  For string and multistring options, we also need to be
68  * able to tell the difference between a "+" and a "-".  (For bools,
69  * the "+"/"-" information is encoded in "add" and "mask").  So we use
70  * an enumerated type (well, the preprocessor equivalent).  Here are
71  * the values:
72  */
73 enum action_value_type {
74    AV_NONE       = 0, /* +opt -opt */
75    AV_ADD_STRING = 1, /* +stropt{string} */
76    AV_REM_STRING = 2, /* -stropt */
77    AV_ADD_MULTI  = 3, /* +multiopt{string} +multiopt{string2} */
78    AV_REM_MULTI  = 4  /* -multiopt{string} -multiopt          */
79 };
80
81 /*
82  * We need a structure to hold the name, flag changes,
83  * type, and string index.
84  */
85 struct action_name
86 {
87    const char * name;
88    unsigned long mask;                /* a bit set to "0" = remove action */
89    unsigned long add;                 /* a bit set to "1" = add action */
90    enum action_value_type value_type; /* an AV_... constant */
91    int index;                         /* index into strings[] or multi[] */
92 };
93
94 /*
95  * And with those building blocks in place, here's the array.
96  */
97 static const struct action_name action_names[] =
98 {
99    /*
100     * Well actually there's no data here - it's in actionlist.h
101     * This keeps it together to make it easy to change.
102     *
103     * Here's the macros used to format it:
104     */
105 #define DEFINE_ACTION_MULTI(name,index)                   \
106    { "+" name, ACTION_MASK_ALL, 0, AV_ADD_MULTI, index }, \
107    { "-" name, ACTION_MASK_ALL, 0, AV_REM_MULTI, index },
108 #define DEFINE_ACTION_STRING(name,flag,index)                 \
109    { "+" name, ACTION_MASK_ALL, flag, AV_ADD_STRING, index }, \
110    { "-" name, ~flag, 0, AV_REM_STRING, index },
111 #define DEFINE_ACTION_BOOL(name,flag)   \
112    { "+" name, ACTION_MASK_ALL, flag }, \
113    { "-" name, ~flag, 0 },
114 #define DEFINE_ACTION_ALIAS 1 /* Want aliases please */
115
116 #include "actionlist.h"
117
118 #undef DEFINE_ACTION_MULTI
119 #undef DEFINE_ACTION_STRING
120 #undef DEFINE_ACTION_BOOL
121 #undef DEFINE_ACTION_ALIAS
122
123    { NULL, 0, 0 } /* End marker */
124 };
125
126
127 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    :  filter_type_to_string
1086  *
1087  * Description :  Converts a filter type enum into a string.
1088  *
1089  * Parameters  :
1090  *          1  :  filter_type = filter_type as enum
1091  *
1092  * Returns     :  Pointer to static string.
1093  *
1094  *********************************************************************/
1095 static const char *filter_type_to_string(enum filter_type filter_type)
1096 {
1097    switch (filter_type)
1098    {
1099    case FT_CONTENT_FILTER:
1100       return "content filter";
1101    case FT_CLIENT_HEADER_FILTER:
1102       return "client-header filter";
1103    case FT_SERVER_HEADER_FILTER:
1104       return "server-header filter";
1105    case FT_CLIENT_HEADER_TAGGER:
1106       return "client-header tagger";
1107    case FT_SERVER_HEADER_TAGGER:
1108       return "server-header tagger";
1109 #ifdef FEATURE_EXTERNAL_FILTERS
1110    case FT_EXTERNAL_CONTENT_FILTER:
1111       return "external content filter";
1112 #endif
1113    case FT_INVALID_FILTER:
1114       return "invalid filter type";
1115    }
1116
1117    return "unknown filter type";
1118
1119 }
1120
1121 /*********************************************************************
1122  *
1123  * Function    :  referenced_filters_are_missing
1124  *
1125  * Description :  Checks if any filters of a certain type referenced
1126  *                in an action spec are missing.
1127  *
1128  * Parameters  :
1129  *          1  :  csp = Current client state (buffers, headers, etc...)
1130  *          2  :  cur_action = The action spec to check.
1131  *          3  :  multi_index = The index where to look for the filter.
1132  *          4  :  filter_type = The filter type the caller is interested in.
1133  *
1134  * Returns     :  0 => All referenced filters exist, everything else is an error.
1135  *
1136  *********************************************************************/
1137 static int referenced_filters_are_missing(const struct client_state *csp,
1138    const struct action_spec *cur_action, int multi_index, enum filter_type filter_type)
1139 {
1140    struct list_entry *filtername;
1141
1142    for (filtername = cur_action->multi_add[multi_index]->first;
1143         filtername; filtername = filtername->next)
1144    {
1145       if (NULL == get_filter(csp, filtername->str, filter_type))
1146       {
1147          log_error(LOG_LEVEL_ERROR, "Missing %s '%s'",
1148             filter_type_to_string(filter_type), filtername->str);
1149          return 1;
1150       }
1151    }
1152
1153    return 0;
1154
1155 }
1156
1157
1158 /*********************************************************************
1159  *
1160  * Function    :  action_spec_is_valid
1161  *
1162  * Description :  Should eventually figure out if an action spec
1163  *                is valid, but currently only checks that the
1164  *                referenced filters are accounted for.
1165  *
1166  * Parameters  :
1167  *          1  :  csp = Current client state (buffers, headers, etc...)
1168  *          2  :  cur_action = The action spec to check.
1169  *
1170  * Returns     :  0 => No problems detected, everything else is an error.
1171  *
1172  *********************************************************************/
1173 static int action_spec_is_valid(struct client_state *csp, const struct action_spec *cur_action)
1174 {
1175    struct {
1176       int multi_index;
1177       enum filter_type filter_type;
1178    } filter_map[] = {
1179       {ACTION_MULTI_FILTER, FT_CONTENT_FILTER},
1180       {ACTION_MULTI_CLIENT_HEADER_FILTER, FT_CLIENT_HEADER_FILTER},
1181       {ACTION_MULTI_SERVER_HEADER_FILTER, FT_SERVER_HEADER_FILTER},
1182       {ACTION_MULTI_CLIENT_HEADER_TAGGER, FT_CLIENT_HEADER_TAGGER},
1183       {ACTION_MULTI_SERVER_HEADER_TAGGER, FT_SERVER_HEADER_TAGGER}
1184    };
1185    int errors = 0;
1186    int i;
1187
1188    for (i = 0; i < SZ(filter_map); i++)
1189    {
1190       errors += referenced_filters_are_missing(csp, cur_action,
1191          filter_map[i].multi_index, filter_map[i].filter_type);
1192    }
1193
1194    return errors;
1195
1196 }
1197
1198
1199 /*********************************************************************
1200  *
1201  * Function    :  load_one_actions_file
1202  *
1203  * Description :  Read and parse a action file and add to files
1204  *                list.
1205  *
1206  * Parameters  :
1207  *          1  :  csp = Current client state (buffers, headers, etc...)
1208  *          2  :  fileid = File index to load.
1209  *
1210  * Returns     :  0 => Ok, everything else is an error.
1211  *
1212  *********************************************************************/
1213 static int load_one_actions_file(struct client_state *csp, int fileid)
1214 {
1215
1216    /*
1217     * Parser mode.
1218     * Note: Keep these in the order they occur in the file, they are
1219     * sometimes tested with <=
1220     */
1221    enum {
1222       MODE_START_OF_FILE = 1,
1223       MODE_SETTINGS      = 2,
1224       MODE_DESCRIPTION   = 3,
1225       MODE_ALIAS         = 4,
1226       MODE_ACTIONS       = 5
1227    } mode;
1228
1229    FILE *fp;
1230    struct url_actions *last_perm;
1231    struct url_actions *perm;
1232    char  *buf;
1233    struct file_list *fs;
1234    struct action_spec * cur_action = NULL;
1235    int cur_action_used = 0;
1236    struct action_alias * alias_list = NULL;
1237    unsigned long linenum = 0;
1238    mode = MODE_START_OF_FILE;
1239
1240    if (!check_file_changed(current_actions_file[fileid], csp->config->actions_file[fileid], &fs))
1241    {
1242       /* No need to load */
1243       csp->actions_list[fileid] = current_actions_file[fileid];
1244       return 0;
1245    }
1246    if (!fs)
1247    {
1248       log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': %E. "
1249          "Note that beginning with Privoxy 3.0.7, actions files have to be specified "
1250          "with their complete file names.", csp->config->actions_file[fileid]);
1251       return 1; /* never get here */
1252    }
1253
1254    fs->f = last_perm = zalloc_or_die(sizeof(*last_perm));
1255
1256    if ((fp = fopen(csp->config->actions_file[fileid], "r")) == NULL)
1257    {
1258       log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error opening file: %E",
1259                 csp->config->actions_file[fileid]);
1260       return 1; /* never get here */
1261    }
1262
1263    log_error(LOG_LEVEL_INFO, "Loading actions file: %s", csp->config->actions_file[fileid]);
1264
1265    while (read_config_line(fp, &linenum, &buf) != NULL)
1266    {
1267       if (*buf == '{')
1268       {
1269          /* It's a header block */
1270          if (buf[1] == '{')
1271          {
1272             /* It's {{settings}} or {{alias}} */
1273             size_t len = strlen(buf);
1274             char * start = buf + 2;
1275             char * end = buf + len - 1;
1276             if ((len < (size_t)5) || (*end-- != '}') || (*end-- != '}'))
1277             {
1278                /* too short */
1279                fclose(fp);
1280                log_error(LOG_LEVEL_FATAL,
1281                   "can't load actions file '%s': invalid line (%lu): %s",
1282                   csp->config->actions_file[fileid], linenum, buf);
1283                return 1; /* never get here */
1284             }
1285
1286             /* Trim leading and trailing whitespace. */
1287             end[1] = '\0';
1288             chomp(start);
1289
1290             if (*start == '\0')
1291             {
1292                /* too short */
1293                fclose(fp);
1294                log_error(LOG_LEVEL_FATAL,
1295                   "can't load actions file '%s': invalid line (%lu): {{ }}",
1296                   csp->config->actions_file[fileid], linenum);
1297                return 1; /* never get here */
1298             }
1299
1300             /*
1301              * An actionsfile can optionally contain the following blocks.
1302              * They *MUST* be in this order, to simplify processing:
1303              *
1304              * {{settings}}
1305              * name=value...
1306              *
1307              * {{description}}
1308              * ...free text, format TBD, but no line may start with a '{'...
1309              *
1310              * {{alias}}
1311              * name=actions...
1312              *
1313              * The actual actions must be *after* these special blocks.
1314              * None of these special blocks may be repeated.
1315              *
1316              */
1317             if (0 == strcmpic(start, "settings"))
1318             {
1319                /* it's a {{settings}} block */
1320                if (mode >= MODE_SETTINGS)
1321                {
1322                   /* {{settings}} must be first thing in file and must only
1323                    * appear once.
1324                    */
1325                   fclose(fp);
1326                   log_error(LOG_LEVEL_FATAL,
1327                      "can't load actions file '%s': line %lu: {{settings}} must only appear once, and it must be before anything else.",
1328                      csp->config->actions_file[fileid], linenum);
1329                }
1330                mode = MODE_SETTINGS;
1331             }
1332             else if (0 == strcmpic(start, "description"))
1333             {
1334                /* it's a {{description}} block */
1335                if (mode >= MODE_DESCRIPTION)
1336                {
1337                   /* {{description}} is a singleton and only {{settings}} may proceed it
1338                    */
1339                   fclose(fp);
1340                   log_error(LOG_LEVEL_FATAL,
1341                      "can't load actions file '%s': line %lu: {{description}} must only appear once, and only a {{settings}} block may be above it.",
1342                      csp->config->actions_file[fileid], linenum);
1343                }
1344                mode = MODE_DESCRIPTION;
1345             }
1346             else if (0 == strcmpic(start, "alias"))
1347             {
1348                /* it's an {{alias}} block */
1349                if (mode >= MODE_ALIAS)
1350                {
1351                   /* {{alias}} must be first thing in file, possibly after
1352                    * {{settings}} and {{description}}
1353                    *
1354                    * {{alias}} must only appear once.
1355                    *
1356                    * Note that these are new restrictions introduced in
1357                    * v2.9.10 in order to make actionsfile editing simpler.
1358                    * (Otherwise, reordering actionsfile entries without
1359                    * completely rewriting the file becomes non-trivial)
1360                    */
1361                   fclose(fp);
1362                   log_error(LOG_LEVEL_FATAL,
1363                      "can't load actions file '%s': line %lu: {{alias}} must only appear once, and it must be before all actions.",
1364                      csp->config->actions_file[fileid], linenum);
1365                }
1366                mode = MODE_ALIAS;
1367             }
1368             else
1369             {
1370                /* invalid {{something}} block */
1371                fclose(fp);
1372                log_error(LOG_LEVEL_FATAL,
1373                   "can't load actions file '%s': invalid line (%lu): {{%s}}",
1374                   csp->config->actions_file[fileid], linenum, start);
1375                return 1; /* never get here */
1376             }
1377          }
1378          else
1379          {
1380             /* It's an actions block */
1381
1382             char *actions_buf;
1383             char * end;
1384
1385             /* set mode */
1386             mode = MODE_ACTIONS;
1387
1388             /* free old action */
1389             if (cur_action)
1390             {
1391                if (!cur_action_used)
1392                {
1393                   free_action_spec(cur_action);
1394                }
1395                cur_action = NULL;
1396             }
1397             cur_action_used = 0;
1398             cur_action = zalloc_or_die(sizeof(*cur_action));
1399             init_action(cur_action);
1400
1401             /*
1402              * Copy the buffer before messing with it as we may need the
1403              * unmodified version in for the fatal error messages. Given
1404              * that this is not a common event, we could instead simply
1405              * read the line again.
1406              *
1407              * buf + 1 to skip the leading '{'
1408              */
1409             actions_buf = end = strdup_or_die(buf + 1);
1410
1411             /* check we have a trailing } and then trim it */
1412             if (strlen(actions_buf))
1413             {
1414                end += strlen(actions_buf) - 1;
1415             }
1416             if (*end != '}')
1417             {
1418                /* No closing } */
1419                fclose(fp);
1420                freez(actions_buf);
1421                log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': "
1422                   "Missing trailing '}' in action section starting at line (%lu): %s",
1423                   csp->config->actions_file[fileid], linenum, buf);
1424                return 1; /* never get here */
1425             }
1426             *end = '\0';
1427
1428             /* trim any whitespace immediately inside {} */
1429             chomp(actions_buf);
1430
1431             if (get_actions(actions_buf, alias_list, cur_action))
1432             {
1433                /* error */
1434                fclose(fp);
1435                freez(actions_buf);
1436                log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': "
1437                   "can't completely parse the action section starting at line (%lu): %s",
1438                   csp->config->actions_file[fileid], linenum, buf);
1439                return 1; /* never get here */
1440             }
1441
1442             if (action_spec_is_valid(csp, cur_action))
1443             {
1444                log_error(LOG_LEVEL_ERROR, "Invalid action section in file '%s', "
1445                   "starting at line %lu: %s",
1446                   csp->config->actions_file[fileid], linenum, buf);
1447             }
1448
1449             freez(actions_buf);
1450          }
1451       }
1452       else if (mode == MODE_SETTINGS)
1453       {
1454          /*
1455           * Part of the {{settings}} block.
1456           * For now only serves to check if the file's minimum Privoxy
1457           * version requirement is met, but we may want to read & check
1458           * permissions when we go multi-user.
1459           */
1460          if (!strncmp(buf, "for-privoxy-version=", 20))
1461          {
1462             char *version_string, *fields[3];
1463             int num_fields;
1464
1465             version_string = strdup_or_die(buf + 20);
1466
1467             num_fields = ssplit(version_string, ".", fields, SZ(fields));
1468
1469             if (num_fields < 1 || atoi(fields[0]) == 0)
1470             {
1471                log_error(LOG_LEVEL_ERROR,
1472                  "While loading actions file '%s': invalid line (%lu): %s",
1473                   csp->config->actions_file[fileid], linenum, buf);
1474             }
1475             else if (                  (atoi(fields[0]) > VERSION_MAJOR)
1476                || ((num_fields > 1) && (atoi(fields[1]) > VERSION_MINOR))
1477                || ((num_fields > 2) && (atoi(fields[2]) > VERSION_POINT)))
1478             {
1479                fclose(fp);
1480                log_error(LOG_LEVEL_FATAL,
1481                          "Actions file '%s', line %lu requires newer Privoxy version: %s",
1482                          csp->config->actions_file[fileid], linenum, buf);
1483                return 1; /* never get here */
1484             }
1485             free(version_string);
1486          }
1487       }
1488       else if (mode == MODE_DESCRIPTION)
1489       {
1490          /*
1491           * Part of the {{description}} block.
1492           * Ignore for now.
1493           */
1494       }
1495       else if (mode == MODE_ALIAS)
1496       {
1497          /*
1498           * define an alias
1499           */
1500          char  actions_buf[BUFFER_SIZE];
1501          struct action_alias * new_alias;
1502
1503          char * start = strchr(buf, '=');
1504          char * end = start;
1505
1506          if ((start == NULL) || (start == buf))
1507          {
1508             log_error(LOG_LEVEL_FATAL,
1509                "can't load actions file '%s': invalid alias line (%lu): %s",
1510                csp->config->actions_file[fileid], linenum, buf);
1511             return 1; /* never get here */
1512          }
1513
1514          new_alias = zalloc_or_die(sizeof(*new_alias));
1515
1516          /* Eat any the whitespace before the '=' */
1517          end--;
1518          while ((*end == ' ') || (*end == '\t'))
1519          {
1520             /*
1521              * we already know we must have at least 1 non-ws char
1522              * at start of buf - no need to check
1523              */
1524             end--;
1525          }
1526          end[1] = '\0';
1527
1528          /* Eat any the whitespace after the '=' */
1529          start++;
1530          while ((*start == ' ') || (*start == '\t'))
1531          {
1532             start++;
1533          }
1534          if (*start == '\0')
1535          {
1536             log_error(LOG_LEVEL_FATAL,
1537                "can't load actions file '%s': invalid alias line (%lu): %s",
1538                csp->config->actions_file[fileid], linenum, buf);
1539             return 1; /* never get here */
1540          }
1541
1542          new_alias->name = strdup_or_die(buf);
1543
1544          strlcpy(actions_buf, start, sizeof(actions_buf));
1545
1546          if (get_actions(actions_buf, alias_list, new_alias->action))
1547          {
1548             /* error */
1549             fclose(fp);
1550             log_error(LOG_LEVEL_FATAL,
1551                "can't load actions file '%s': invalid alias line (%lu): %s = %s",
1552                csp->config->actions_file[fileid], linenum, buf, start);
1553             return 1; /* never get here */
1554          }
1555
1556          /* add to list */
1557          new_alias->next = alias_list;
1558          alias_list = new_alias;
1559       }
1560       else if (mode == MODE_ACTIONS)
1561       {
1562          /* it's an URL pattern */
1563
1564          /* allocate a new node */
1565          perm = zalloc_or_die(sizeof(*perm));
1566
1567          perm->action = cur_action;
1568          cur_action_used = 1;
1569
1570          /* Save the URL pattern */
1571          if (create_pattern_spec(perm->url, buf))
1572          {
1573             fclose(fp);
1574             log_error(LOG_LEVEL_FATAL,
1575                "can't load actions file '%s': line %lu: cannot create URL or TAG pattern from: %s",
1576                csp->config->actions_file[fileid], linenum, buf);
1577             return 1; /* never get here */
1578          }
1579
1580          /* add it to the list */
1581          last_perm->next = perm;
1582          last_perm = perm;
1583       }
1584       else if (mode == MODE_START_OF_FILE)
1585       {
1586          /* oops - please have a {} line as 1st line in file. */
1587          fclose(fp);
1588          log_error(LOG_LEVEL_FATAL,
1589             "can't load actions file '%s': line %lu should begin with a '{': %s",
1590             csp->config->actions_file[fileid], linenum, buf);
1591          return 1; /* never get here */
1592       }
1593       else
1594       {
1595          /* How did we get here? This is impossible! */
1596          fclose(fp);
1597          log_error(LOG_LEVEL_FATAL,
1598             "can't load actions file '%s': INTERNAL ERROR - mode = %d",
1599             csp->config->actions_file[fileid], mode);
1600          return 1; /* never get here */
1601       }
1602       freez(buf);
1603    }
1604
1605    fclose(fp);
1606
1607    if (!cur_action_used)
1608    {
1609       free_action_spec(cur_action);
1610    }
1611    free_alias_list(alias_list);
1612
1613    /* the old one is now obsolete */
1614    if (current_actions_file[fileid])
1615    {
1616       current_actions_file[fileid]->unloader = unload_actions_file;
1617    }
1618
1619    fs->next    = files->next;
1620    files->next = fs;
1621    current_actions_file[fileid] = fs;
1622
1623    csp->actions_list[fileid] = fs;
1624
1625    return(0);
1626
1627 }
1628
1629
1630 /*********************************************************************
1631  *
1632  * Function    :  actions_to_text
1633  *
1634  * Description :  Converts a actionsfile entry from the internal
1635  *                structure into a text line.  The output is split
1636  *                into one line for each action with line continuation.
1637  *
1638  * Parameters  :
1639  *          1  :  action = The action to format.
1640  *
1641  * Returns     :  A string.  Caller must free it.
1642  *                NULL on out-of-memory error.
1643  *
1644  *********************************************************************/
1645 char * actions_to_text(const struct action_spec *action)
1646 {
1647    unsigned long mask = action->mask;
1648    unsigned long add  = action->add;
1649    char *result = strdup_or_die("");
1650    struct list_entry * lst;
1651
1652    /* sanity - prevents "-feature +feature" */
1653    mask |= add;
1654
1655
1656 #define DEFINE_ACTION_BOOL(__name, __bit)          \
1657    if (!(mask & __bit))                            \
1658    {                                               \
1659       string_append(&result, " -" __name " \\\n"); \
1660    }                                               \
1661    else if (add & __bit)                           \
1662    {                                               \
1663       string_append(&result, " +" __name " \\\n"); \
1664    }
1665
1666 #define DEFINE_ACTION_STRING(__name, __bit, __index)   \
1667    if (!(mask & __bit))                                \
1668    {                                                   \
1669       string_append(&result, " -" __name " \\\n");     \
1670    }                                                   \
1671    else if (add & __bit)                               \
1672    {                                                   \
1673       string_append(&result, " +" __name "{");         \
1674       string_append(&result, action->string[__index]); \
1675       string_append(&result, "} \\\n");                \
1676    }
1677
1678 #define DEFINE_ACTION_MULTI(__name, __index)         \
1679    if (action->multi_remove_all[__index])            \
1680    {                                                 \
1681       string_append(&result, " -" __name " \\\n");   \
1682    }                                                 \
1683    else                                              \
1684    {                                                 \
1685       lst = action->multi_remove[__index]->first;    \
1686       while (lst)                                    \
1687       {                                              \
1688          string_append(&result, " -" __name "{");    \
1689          string_append(&result, lst->str);           \
1690          string_append(&result, "} \\\n");           \
1691          lst = lst->next;                            \
1692       }                                              \
1693    }                                                 \
1694    lst = action->multi_add[__index]->first;          \
1695    while (lst)                                       \
1696    {                                                 \
1697       string_append(&result, " +" __name "{");       \
1698       string_append(&result, lst->str);              \
1699       string_append(&result, "} \\\n");              \
1700       lst = lst->next;                               \
1701    }
1702
1703 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1704
1705 #include "actionlist.h"
1706
1707 #undef DEFINE_ACTION_MULTI
1708 #undef DEFINE_ACTION_STRING
1709 #undef DEFINE_ACTION_BOOL
1710 #undef DEFINE_ACTION_ALIAS
1711
1712    return result;
1713 }
1714
1715
1716 /*********************************************************************
1717  *
1718  * Function    :  actions_to_html
1719  *
1720  * Description :  Converts a actionsfile entry from numeric form
1721  *                ("mask" and "add") to a <br>-separated HTML string
1722  *                in which each action is linked to its chapter in
1723  *                the user manual.
1724  *
1725  * Parameters  :
1726  *          1  :  csp    = Client state (for config)
1727  *          2  :  action = Action spec to be converted
1728  *
1729  * Returns     :  A string.  Caller must free it.
1730  *                NULL on out-of-memory error.
1731  *
1732  *********************************************************************/
1733 char * actions_to_html(const struct client_state *csp,
1734                        const struct action_spec *action)
1735 {
1736    unsigned long mask = action->mask;
1737    unsigned long add  = action->add;
1738    char *result = strdup_or_die("");
1739    struct list_entry * lst;
1740
1741    /* sanity - prevents "-feature +feature" */
1742    mask |= add;
1743
1744
1745 #define DEFINE_ACTION_BOOL(__name, __bit)       \
1746    if (!(mask & __bit))                         \
1747    {                                            \
1748       string_append(&result, "\n<br>-");        \
1749       string_join(&result, add_help_link(__name, csp->config)); \
1750    }                                            \
1751    else if (add & __bit)                        \
1752    {                                            \
1753       string_append(&result, "\n<br>+");        \
1754       string_join(&result, add_help_link(__name, csp->config)); \
1755    }
1756
1757 #define DEFINE_ACTION_STRING(__name, __bit, __index) \
1758    if (!(mask & __bit))                              \
1759    {                                                 \
1760       string_append(&result, "\n<br>-");             \
1761       string_join(&result, add_help_link(__name, csp->config)); \
1762    }                                                 \
1763    else if (add & __bit)                             \
1764    {                                                 \
1765       string_append(&result, "\n<br>+");             \
1766       string_join(&result, add_help_link(__name, csp->config)); \
1767       string_append(&result, "{");                   \
1768       string_join(&result, html_encode(action->string[__index])); \
1769       string_append(&result, "}");                   \
1770    }
1771
1772 #define DEFINE_ACTION_MULTI(__name, __index)          \
1773    if (action->multi_remove_all[__index])             \
1774    {                                                  \
1775       string_append(&result, "\n<br>-");              \
1776       string_join(&result, add_help_link(__name, csp->config)); \
1777    }                                                  \
1778    else                                               \
1779    {                                                  \
1780       lst = action->multi_remove[__index]->first;     \
1781       while (lst)                                     \
1782       {                                               \
1783          string_append(&result, "\n<br>-");           \
1784          string_join(&result, add_help_link(__name, csp->config)); \
1785          string_append(&result, "{");                 \
1786          string_join(&result, html_encode(lst->str)); \
1787          string_append(&result, "}");                 \
1788          lst = lst->next;                             \
1789       }                                               \
1790    }                                                  \
1791    lst = action->multi_add[__index]->first;           \
1792    while (lst)                                        \
1793    {                                                  \
1794       string_append(&result, "\n<br>+");              \
1795       string_join(&result, add_help_link(__name, csp->config)); \
1796       string_append(&result, "{");                    \
1797       string_join(&result, html_encode(lst->str));    \
1798       string_append(&result, "}");                    \
1799       lst = lst->next;                                \
1800    }
1801
1802 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1803
1804 #include "actionlist.h"
1805
1806 #undef DEFINE_ACTION_MULTI
1807 #undef DEFINE_ACTION_STRING
1808 #undef DEFINE_ACTION_BOOL
1809 #undef DEFINE_ACTION_ALIAS
1810
1811    /* trim leading <br> */
1812    if (result && *result)
1813    {
1814       char * s = result;
1815       result = strdup(result + 5);
1816       free(s);
1817    }
1818
1819    return result;
1820 }
1821
1822
1823 /*********************************************************************
1824  *
1825  * Function    :  current_actions_to_html
1826  *
1827  * Description :  Converts a curren action spec to a <br> separated HTML
1828  *                text in which each action is linked to its chapter in
1829  *                the user manual.
1830  *
1831  * Parameters  :
1832  *          1  :  csp    = Client state (for config)
1833  *          2  :  action = Current action spec to be converted
1834  *
1835  * Returns     :  A string.  Caller must free it.
1836  *                NULL on out-of-memory error.
1837  *
1838  *********************************************************************/
1839 char *current_action_to_html(const struct client_state *csp,
1840                              const struct current_action_spec *action)
1841 {
1842    unsigned long flags  = action->flags;
1843    struct list_entry * lst;
1844    char *result   = strdup_or_die("");
1845    char *active   = strdup_or_die("");
1846    char *inactive = strdup_or_die("");
1847
1848 #define DEFINE_ACTION_BOOL(__name, __bit)  \
1849    if (flags & __bit)                      \
1850    {                                       \
1851       string_append(&active, "\n<br>+");   \
1852       string_join(&active, add_help_link(__name, csp->config)); \
1853    }                                       \
1854    else                                    \
1855    {                                       \
1856       string_append(&inactive, "\n<br>-"); \
1857       string_join(&inactive, add_help_link(__name, csp->config)); \
1858    }
1859
1860 #define DEFINE_ACTION_STRING(__name, __bit, __index)   \
1861    if (flags & __bit)                                  \
1862    {                                                   \
1863       string_append(&active, "\n<br>+");               \
1864       string_join(&active, add_help_link(__name, csp->config)); \
1865       string_append(&active, "{");                     \
1866       string_join(&active, html_encode(action->string[__index])); \
1867       string_append(&active, "}");                     \
1868    }                                                   \
1869    else                                                \
1870    {                                                   \
1871       string_append(&inactive, "\n<br>-");             \
1872       string_join(&inactive, add_help_link(__name, csp->config)); \
1873    }
1874
1875 #define DEFINE_ACTION_MULTI(__name, __index)           \
1876    lst = action->multi[__index]->first;                \
1877    if (lst == NULL)                                    \
1878    {                                                   \
1879       string_append(&inactive, "\n<br>-");             \
1880       string_join(&inactive, add_help_link(__name, csp->config)); \
1881    }                                                   \
1882    else                                                \
1883    {                                                   \
1884       while (lst)                                      \
1885       {                                                \
1886          string_append(&active, "\n<br>+");            \
1887          string_join(&active, add_help_link(__name, csp->config)); \
1888          string_append(&active, "{");                  \
1889          string_join(&active, html_encode(lst->str));  \
1890          string_append(&active, "}");                  \
1891          lst = lst->next;                              \
1892       }                                                \
1893    }
1894
1895 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1896
1897 #include "actionlist.h"
1898
1899 #undef DEFINE_ACTION_MULTI
1900 #undef DEFINE_ACTION_STRING
1901 #undef DEFINE_ACTION_BOOL
1902 #undef DEFINE_ACTION_ALIAS
1903
1904    if (active != NULL)
1905    {
1906       string_append(&result, active);
1907       freez(active);
1908    }
1909    string_append(&result, "\n<br>");
1910    if (inactive != NULL)
1911    {
1912       string_append(&result, inactive);
1913       freez(inactive);
1914    }
1915    return result;
1916 }
1917
1918
1919 /*********************************************************************
1920  *
1921  * Function    :  action_to_line_of_text
1922  *
1923  * Description :  Converts a action spec to a single text line
1924  *                listing the enabled actions.
1925  *
1926  * Parameters  :
1927  *          1  :  action = Current action spec to be converted
1928  *
1929  * Returns     :  A string. Caller must free it.
1930  *                Out-of-memory errors are fatal.
1931  *
1932  *********************************************************************/
1933 char *actions_to_line_of_text(const struct current_action_spec *action)
1934 {
1935    char buffer[200];
1936    struct list_entry *lst;
1937    char *active;
1938    const unsigned long flags = action->flags;
1939
1940    active = strdup_or_die("");
1941
1942 #define DEFINE_ACTION_BOOL(__name, __bit)               \
1943    if (flags & __bit)                                   \
1944    {                                                    \
1945       snprintf(buffer, sizeof(buffer), "+%s ", __name); \
1946       string_append(&active, buffer);                   \
1947    }                                                    \
1948
1949 #define DEFINE_ACTION_STRING(__name, __bit, __index)    \
1950    if (flags & __bit)                                   \
1951    {                                                    \
1952       snprintf(buffer, sizeof(buffer), "+%s{%s} ",      \
1953          __name, action->string[__index]);              \
1954       string_append(&active, buffer);                   \
1955    }                                                    \
1956
1957 #define DEFINE_ACTION_MULTI(__name, __index)            \
1958    lst = action->multi[__index]->first;                 \
1959    while (lst != NULL)                                  \
1960    {                                                    \
1961       snprintf(buffer, sizeof(buffer), "+%s{%s} ",      \
1962          __name, lst->str);                             \
1963       string_append(&active, buffer);                   \
1964       lst = lst->next;                                  \
1965    }                                                    \
1966
1967 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1968
1969 #include "actionlist.h"
1970
1971 #undef DEFINE_ACTION_MULTI
1972 #undef DEFINE_ACTION_STRING
1973 #undef DEFINE_ACTION_BOOL
1974 #undef DEFINE_ACTION_ALIAS
1975
1976    if (active == NULL)
1977    {
1978       log_error(LOG_LEVEL_FATAL, "Out of memory in action_to_line_of_text()");
1979    }
1980
1981    return active;
1982 }