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