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