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