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