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