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