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