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