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