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