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