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