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