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