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