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