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