909cd68cb9a52633ae6ae20e8de9a09c16cc742d
[privoxy.git] / actions.c
1 const char actions_rcs[] = "$Id: actions.c,v 1.10 2001/09/10 10:14:34 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/actions.c,v $
5  *
6  * Purpose     :  Declares functions to work with actions files
7  *                Functions declared include: FIXME
8  *
9  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
10  *                IJBSWA team.  http://ijbswa.sourceforge.net
11  *
12  *                Based on the Internet Junkbuster originally written
13  *                by and Copyright (C) 1997 Anonymous Coders and 
14  *                Junkbusters Corporation.  http://www.junkbusters.com
15  *
16  *                This program is free software; you can redistribute it 
17  *                and/or modify it under the terms of the GNU General
18  *                Public License as published by the Free Software
19  *                Foundation; either version 2 of the License, or (at
20  *                your option) any later version.
21  *
22  *                This program is distributed in the hope that it will
23  *                be useful, but WITHOUT ANY WARRANTY; without even the
24  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
25  *                PARTICULAR PURPOSE.  See the GNU General Public
26  *                License for more details.
27  *
28  *                The GNU General Public License should be included with
29  *                this file.  If not, you can view it at
30  *                http://www.gnu.org/copyleft/gpl.html
31  *                or write to the Free Software Foundation, Inc., 59
32  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
33  *
34  * Revisions   :
35  *    $Log: actions.c,v $
36  *    Revision 1.10  2001/09/10 10:14:34  oes
37  *    Removing unused variable
38  *
39  *    Revision 1.9  2001/07/30 22:08:36  jongfoster
40  *    Tidying up #defines:
41  *    - All feature #defines are now of the form FEATURE_xxx
42  *    - Permanently turned off WIN_GUI_EDIT
43  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
44  *
45  *    Revision 1.8  2001/06/29 13:19:52  oes
46  *    Removed logentry from cancelled commit
47  *
48  *    Revision 1.7  2001/06/09 10:55:28  jongfoster
49  *    Changing BUFSIZ ==> BUFFER_SIZE
50  *
51  *    Revision 1.6  2001/06/07 23:04:34  jongfoster
52  *    Made get_actions() static.
53  *
54  *    Revision 1.5  2001/06/03 19:11:48  oes
55  *    adapted to new enlist_unique arg format
56  *
57  *    Revision 1.4  2001/06/01 20:03:42  jongfoster
58  *    Better memory management - current_action->strings[] now
59  *    contains copies of the strings, not the original.
60  *
61  *    Revision 1.3  2001/06/01 18:49:17  jongfoster
62  *    Replaced "list_share" with "list" - the tiny memory gain was not
63  *    worth the extra complexity.
64  *
65  *    Revision 1.2  2001/05/31 21:40:00  jongfoster
66  *    Removing some commented out, obsolete blocks of code.
67  *
68  *    Revision 1.1  2001/05/31 21:16:46  jongfoster
69  *    Moved functions to process the action list into this new file.
70  *
71  *
72  *********************************************************************/
73 \f
74
75 #include "config.h"
76
77 #include <stdio.h>
78 #include <string.h>
79
80 #include "project.h"
81 #include "jcc.h"
82 #include "list.h"
83 #include "actions.h"
84 #include "miscutil.h"
85 #include "errlog.h"
86 #include "loaders.h"
87
88 const char actions_h_rcs[] = ACTIONS_H_VERSION;
89
90
91 /* Turn off everything except forwarding */
92 /* This structure is used to hold user-defined aliases */
93 struct action_alias
94 {
95    const char * name;
96    struct action_spec action[1];
97    struct action_alias * next;
98 };
99
100
101 /*
102  * Must declare this in this file for the above structure.
103  */
104 static int get_actions (char *line, 
105                         struct action_alias * alias_list,
106                         struct action_spec *cur_action);
107
108 /*
109  * We need the main list of options.
110  *
111  * First, we need a way to tell between boolean, string, and multi-string
112  * options.  For string and multistring options, we also need to be
113  * able to tell the difference between a "+" and a "-".  (For bools,
114  * the "+"/"-" information is encoded in "add" and "mask").  So we use
115  * an enumerated type (well, the preprocessor equivalent).  Here are
116  * the values:
117  */
118 #define AV_NONE       0 /* +opt -opt */
119 #define AV_ADD_STRING 1 /* +stropt{string} */
120 #define AV_REM_STRING 2 /* -stropt */
121 #define AV_ADD_MULTI  3 /* +multiopt{string} +multiopt{string2} */
122 #define AV_REM_MULTI  4 /* -multiopt{string} -multiopt{*}       */
123
124 /*
125  * We need a structure to hold the name, flag changes, 
126  * type, and string index.
127  */
128 struct action_name
129 {
130    const char * name;
131    unsigned mask;   /* a bit set to "0" = remove action */
132    unsigned add;    /* a bit set to "1" = add action */
133    int takes_value; /* an AV_... constant */
134    int index;       /* index into strings[] or multi[] */
135 };
136
137 /*
138  * And with those building blocks in place, here's the array.
139  */
140 static const struct action_name action_names[] =
141 {
142    /*
143     * Well actually there's no data here - it's in actionlist.h
144     * This keeps it together to make it easy to change.
145     *
146     * Here's the macros used to format it:
147     */
148 #define DEFINE_ACTION_MULTI(name,index)                   \
149    { "+" name, ACTION_MASK_ALL, 0, AV_ADD_MULTI, index }, \
150    { "-" name, ACTION_MASK_ALL, 0, AV_REM_MULTI, index },
151 #define DEFINE_ACTION_STRING(name,flag,index)                 \
152    { "+" name, ACTION_MASK_ALL, flag, AV_ADD_STRING, index }, \
153    { "-" name, ~flag, 0, AV_REM_STRING, index },
154 #define DEFINE_ACTION_BOOL(name,flag)   \
155    { "+" name, ACTION_MASK_ALL, flag }, \
156    { "-" name, ~flag, 0 },
157 #define DEFINE_ACTION_ALIAS 1 /* Want aliases please */
158
159 #include "actionlist.h"
160
161 #undef DEFINE_ACTION_MULTI
162 #undef DEFINE_ACTION_STRING
163 #undef DEFINE_ACTION_BOOL
164 #undef DEFINE_ACTION_ALIAS
165
166    { NULL, 0, 0 } /* End marker */
167 };
168
169
170 /*********************************************************************
171  *
172  * Function    :  merge_actions
173  *
174  * Description :  Merge two actions together.
175  *                Similar to "cur_action += new_action".
176  *
177  * Parameters  :
178  *          1  :  cur_action = Current actions, to modify.
179  *          2  :  new_action = Action to add.
180  *
181  * Returns     :  N/A
182  *
183  *********************************************************************/
184 void merge_actions (struct action_spec *dest, 
185                     const struct action_spec *src)
186 {
187    int i;
188
189    dest->mask &= src->mask;
190    dest->add  &= src->mask;
191    dest->add  |= src->add;
192
193    for (i = 0; i < ACTION_STRING_COUNT; i++)
194    {
195       char * str = src->string[i];
196       if (str)
197       {
198          freez(dest->string[i]);
199          dest->string[i] = strdup(str);
200       }
201    }
202
203    for (i = 0; i < ACTION_MULTI_COUNT; i++)
204    {
205       if (src->multi_remove_all[i])
206       {
207          /* Remove everything from dest */
208          destroy_list(dest->multi_remove[i]);
209          destroy_list(dest->multi_add[i]);
210          dest->multi_remove_all[i] = 1;
211          list_duplicate(dest->multi_add[i], src->multi_add[i]);
212       }
213       else if (dest->multi_remove_all[i])
214       {
215          /*
216           * dest already removes everything, so we only need to worry
217           * about what we add.
218           */
219          list_remove_list(dest->multi_add[i], src->multi_remove[i]);
220          list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
221       }
222       else
223       {
224          /* No "remove all"s to worry about. */
225          list_remove_list(dest->multi_add[i], src->multi_remove[i]);
226          list_append_list_unique(dest->multi_remove[i], src->multi_remove[i]);
227          list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
228       }
229    }
230 }
231
232
233 /*********************************************************************
234  *
235  * Function    :  copy_action
236  *
237  * Description :  Copy an action_specs.
238  *                Similar to "cur_action = new_action".
239  *                Note that dest better not contain valid data
240  *                - it's overwritten, not freed.
241  *
242  * Parameters  :
243  *          1  :  dest = Destination of copy.
244  *          2  :  src = Source for copy.
245  *
246  * Returns     :  N/A
247  *
248  *********************************************************************/
249 void copy_action (struct action_spec *dest, 
250                   const struct action_spec *src)
251 {
252    int i;
253
254    memset(dest, '\0', sizeof(*dest));
255
256    dest->mask = src->mask;
257    dest->add  = src->add;
258
259    for (i = 0; i < ACTION_STRING_COUNT; i++)
260    {
261       char * str = src->string[i];
262       dest->string[i] = (str ? strdup(str) : NULL);
263    }
264
265    for (i = 0; i < ACTION_MULTI_COUNT; i++)
266    {
267       dest->multi_remove_all[i] = src->multi_remove_all[i];
268       list_duplicate(dest->multi_remove[i], src->multi_remove[i]);
269       list_duplicate(dest->multi_add[i],    src->multi_add[i]);
270    }
271 }
272
273
274 /*********************************************************************
275  *
276  * Function    :  free_action
277  *
278  * Description :  Destroy an action_spec.  Frees memory used by it,
279  *                except for the memory used by the struct action_spec
280  *                itself.
281  *
282  * Parameters  :
283  *          1  :  src = Source to free.
284  *
285  * Returns     :  N/A
286  *
287  *********************************************************************/
288 void free_action (struct action_spec *src)
289 {
290    int i;
291
292    for (i = 0; i < ACTION_STRING_COUNT; i++)
293    {
294       freez(src->string[i]);
295    }
296
297    for (i = 0; i < ACTION_MULTI_COUNT; i++)
298    {
299       destroy_list(src->multi_remove[i]);
300       destroy_list(src->multi_add[i]);
301    }
302
303    memset(src, '\0', sizeof(*src));
304 }
305
306
307 /*********************************************************************
308  *
309  * Function    :  get_action_token
310  *
311  * Description :  Parses a line for the first action.
312  *                Modifies it's input array, doesn't allocate memory.
313  *                e.g. given:
314  *                *line="  +abc{def}  -ghi "
315  *                Returns:
316  *                *line="  -ghi "
317  *                *name="+abc"
318  *                *value="def"
319  *
320  * Parameters  :
321  *          1  :  line = [in] The line containing the action.
322  *                       [out] Start of next action on line, or
323  *                       NULL if we reached the end of line before
324  *                       we found an action.
325  *          2  :  name = [out] Start of action name, null
326  *                       terminated.  NULL on EOL
327  *          3  :  value = [out] Start of action value, null 
328  *                        terminated.  NULL if none or EOL.
329  *
330  * Returns     :  0 => Ok
331  *                nonzero => Mismatched {} (line was trashed anyway)
332  *
333  *********************************************************************/
334 int get_action_token(char **line, char **name, char **value)
335 {
336    char * str = *line;
337    char ch;
338
339    /* set default returns */
340    *line = NULL;
341    *name = NULL;
342    *value = NULL;
343
344    /* Eat any leading whitespace */
345    while ((*str == ' ') || (*str == '\t'))
346    {
347       str++;
348    }
349
350    if (*str == '\0')
351    {
352       return 0;
353    }
354
355    if (*str == '{')
356    {
357       /* null name, just value is prohibited */
358       return 1;
359    }
360
361    *name = str;
362
363    /* parse option */
364    while (((ch = *str) != '\0') && 
365           (ch != ' ') && (ch != '\t') && (ch != '{'))
366    {
367       if (ch == '}')
368       {
369          /* error */
370          return 1;
371       }
372       str++;
373    }
374    *str = '\0';
375
376    if (ch != '{')
377    {
378       /* no value */
379       if (ch == '\0')
380       {
381          /* EOL - be careful not to run off buffer */
382          *line = str;
383       }
384       else
385       {
386          /* More to parse next time. */
387          *line = str + 1;
388       }
389       return 0;
390    }
391
392    str++;
393    *value = str;
394
395    str = strchr(str, '}');
396    if (str == NULL)
397    {
398       /* error */
399       *value = NULL;
400       return 1;
401    }
402
403    /* got value */
404    *str = '\0';
405    *line = str + 1;
406
407    chomp(*value);
408
409    return 0;
410 }
411
412
413 /*********************************************************************
414  *
415  * Function    :  get_actions
416  *
417  * Description :  Parses a list of actions.
418  *
419  * Parameters  :
420  *          1  :  line = The string containing the actions.
421  *                       Will be written to by this function.
422  *          2  :  aliases = Custom alias list, or NULL for none.
423  *          3  :  cur_action = Where to store the action.  Caller
424  *                             allocates memory.
425  *
426  * Returns     :  0 => Ok
427  *                nonzero => Error (line was trashed anyway)
428  *
429  *********************************************************************/
430 static int get_actions(char *line,
431                        struct action_alias * alias_list,
432                        struct action_spec *cur_action)
433 {
434    init_action(cur_action);
435    cur_action->mask = ACTION_MASK_ALL;
436
437    while (line)
438    {
439       char * option = NULL;
440       char * value = NULL;
441
442       if (get_action_token(&line, &option, &value))
443       {
444          return 1;
445       }
446
447       if (option)
448       {
449          /* handle option in 'option' */
450       
451          /* Check for standard action name */
452          const struct action_name * action = action_names;
453
454          while ( (action->name != NULL) && (0 != strcmpic(action->name, option)) )
455          {
456             action++;
457          }
458          if (action->name != NULL)
459          {
460             /* Found it */
461             cur_action->mask &= action->mask;
462             cur_action->add  &= action->mask;
463             cur_action->add  |= action->add;
464
465             switch (action->takes_value)
466             {
467             case AV_NONE:
468                /* ignore any option. */
469                break;
470             case AV_ADD_STRING:
471                {
472                   /* add single string. */
473
474                   if ((value == NULL) || (*value == '\0'))
475                   {
476                      return 1;
477                   }
478                   /* FIXME: should validate option string here */
479                   freez (cur_action->string[action->index]);
480                   cur_action->string[action->index] = strdup(value);
481                   break;
482                }
483             case AV_REM_STRING:
484                {
485                   /* remove single string. */
486
487                   freez (cur_action->string[action->index]);
488                   break;
489                }
490             case AV_ADD_MULTI:
491                {
492                   /* append multi string. */
493
494                   struct list * remove = cur_action->multi_remove[action->index];
495                   struct list * add    = cur_action->multi_add[action->index];
496
497                   if ((value == NULL) || (*value == '\0'))
498                   {
499                      return 1;
500                   }
501
502                   list_remove_item(remove, value);
503                   enlist_unique(add, value, 0);
504                   break;
505                }
506             case AV_REM_MULTI:
507                {
508                   /* remove multi string. */
509
510                   struct list * remove = cur_action->multi_remove[action->index];
511                   struct list * add    = cur_action->multi_add[action->index];
512
513                   if ( (value == NULL) || (*value == '\0')
514                      || ((*value == '*') && (value[1] == '\0')) )
515                   {
516                      /*
517                       * no option, or option == "*".
518                       *
519                       * Remove *ALL*.
520                       */
521                      destroy_list(remove);
522                      destroy_list(add);
523                      cur_action->multi_remove_all[action->index] = 1;
524                   }
525                   else
526                   {
527                      /* Valid option - remove only 1 option */
528
529                      if ( !cur_action->multi_remove_all[action->index] )
530                      {
531                         /* there isn't a catch-all in the remove list already */
532                         enlist_unique(remove, value, 0);
533                      }
534                      list_remove_item(add, value);
535                   }
536                   break;
537                }
538             default:
539                /* Shouldn't get here unless there's memory corruption. */
540                return 1;
541             }
542          }
543          else
544          {
545             /* try user aliases. */
546             const struct action_alias * alias = alias_list;
547             
548             while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) )
549             {
550                alias = alias->next;
551             }
552             if (alias != NULL)
553             {
554                /* Found it */
555                merge_actions(cur_action, alias->action);
556             }
557             else
558             {
559                /* Bad action name */
560                return 1;
561             }
562          }
563       }
564    }
565
566    return 0;
567 }
568
569
570 /*********************************************************************
571  *
572  * Function    :  actions_to_text
573  *
574  * Description :  Converts a actionsfile entry from numeric form
575  *                ("mask" and "add") to text.
576  *
577  * Parameters  :
578  *          1  :  mask = As from struct url_actions
579  *          2  :  add  = As from struct url_actions
580  *
581  * Returns     :  A string.  Caller must free it.
582  *
583  *********************************************************************/
584 char * actions_to_text(struct action_spec *action)
585 {
586    unsigned mask = action->mask;
587    unsigned add  = action->add;
588    char * result = strdup("");
589    struct list * lst;
590
591    /* sanity - prevents "-feature +feature" */
592    mask |= add;
593
594
595 #define DEFINE_ACTION_BOOL(__name, __bit)   \
596    if (!(mask & __bit))                     \
597    {                                        \
598       result = strsav(result, " -" __name); \
599    }                                        \
600    else if (add & __bit)                    \
601    {                                        \
602       result = strsav(result, " +" __name); \
603    }
604
605 #define DEFINE_ACTION_STRING(__name, __bit, __index) \
606    if (!(mask & __bit))                     \
607    {                                        \
608       result = strsav(result, " -" __name); \
609    }                                        \
610    else if (add & __bit)                    \
611    {                                        \
612       result = strsav(result, " +" __name "{"); \
613       result = strsav(result, action->string[__index]); \
614       result = strsav(result, "}"); \
615    }
616
617 #define DEFINE_ACTION_MULTI(__name, __index)         \
618    if (action->multi_remove_all[__index])            \
619    {                                                 \
620       result = strsav(result, " -" __name "{*}");    \
621    }                                                 \
622    else                                              \
623    {                                                 \
624       lst = action->multi_remove[__index]->next;     \
625       while (lst)                                    \
626       {                                              \
627          result = strsav(result, " -" __name "{");   \
628          result = strsav(result, lst->str);          \
629          result = strsav(result, "}");               \
630          lst = lst->next;                            \
631       }                                              \
632    }                                                 \
633    lst = action->multi_add[__index]->next;           \
634    while (lst)                                       \
635    {                                                 \
636       result = strsav(result, " +" __name "{");      \
637       result = strsav(result, lst->str);             \
638       result = strsav(result, "}");                  \
639       lst = lst->next;                               \
640    }
641
642 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
643
644 #include "actionlist.h"
645
646 #undef DEFINE_ACTION_MULTI
647 #undef DEFINE_ACTION_STRING
648 #undef DEFINE_ACTION_BOOL
649 #undef DEFINE_ACTION_ALIAS
650
651    return result;
652 }
653
654
655 /*********************************************************************
656  *
657  * Function    :  current_actions_to_text
658  *
659  * Description :  Converts a actionsfile entry to text.
660  *
661  * Parameters  :
662  *          1  :  action = Action
663  *
664  * Returns     :  A string.  Caller must free it.
665  *
666  *********************************************************************/
667 char * current_action_to_text(struct current_action_spec *action)
668 {
669    unsigned flags  = action->flags;
670    char * result = strdup("");
671    struct list * lst;
672
673 #define DEFINE_ACTION_BOOL(__name, __bit)   \
674    if (flags & __bit)                       \
675    {                                        \
676       result = strsav(result, " +" __name); \
677    }                                        \
678    else                                     \
679    {                                        \
680       result = strsav(result, " -" __name); \
681    }
682
683 #define DEFINE_ACTION_STRING(__name, __bit, __index)    \
684    if (flags & __bit)                                   \
685    {                                                    \
686       result = strsav(result, " +" __name "{");         \
687       result = strsav(result, action->string[__index]); \
688       result = strsav(result, "}");                     \
689    }                                                    \
690    else                                                 \
691    {                                                    \
692       result = strsav(result, " -" __name);             \
693    }
694
695 #define DEFINE_ACTION_MULTI(__name, __index)            \
696    lst = action->multi[__index]->next;                  \
697    if (lst == NULL)                                     \
698    {                                                    \
699       result = strsav(result, " -" __name);             \
700    }                                                    \
701    else                                                 \
702    {                                                    \
703       while (lst)                                       \
704       {                                                 \
705          result = strsav(result, " +" __name "{");      \
706          result = strsav(result, lst->str);             \
707          result = strsav(result, "}");                  \
708          lst = lst->next;                               \
709       }                                                 \
710    }
711
712 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
713
714 #include "actionlist.h"
715
716 #undef DEFINE_ACTION_MULTI
717 #undef DEFINE_ACTION_STRING
718 #undef DEFINE_ACTION_BOOL
719 #undef DEFINE_ACTION_ALIAS
720
721    return result;
722 }
723
724
725 /*********************************************************************
726  *
727  * Function    :  init_current_action
728  *
729  * Description :  Zero out an action.
730  *
731  * Parameters  :
732  *          1  :  dest = An uninitialized current_action_spec.
733  *
734  * Returns     :  N/A
735  *
736  *********************************************************************/
737 void init_current_action (struct current_action_spec *dest)
738 {
739    memset(dest, '\0', sizeof(*dest));
740    dest->flags = ACTION_MOST_COMPATIBLE;
741 }
742
743
744 /*********************************************************************
745  *
746  * Function    :  init_action
747  *
748  * Description :  Zero out an action.
749  *
750  * Parameters  :
751  *          1  :  dest = An uninitialized action_spec.
752  *
753  * Returns     :  N/A
754  *
755  *********************************************************************/
756 void init_action (struct action_spec *dest)
757 {
758    memset(dest, '\0', sizeof(*dest));
759 }
760
761
762 /*********************************************************************
763  *
764  * Function    :  merge_current_action
765  *
766  * Description :  Merge two actions together.
767  *                Similar to "dest += src".
768  *                Differences between this and merge_actions()
769  *                is that this one doesn't allocate memory for
770  *                strings (so "src" better be in memory for at least
771  *                as long as "dest" is, and you'd better free
772  *                "dest" using "free_current_action").
773  *                Also, there is no  mask or remove lists in dest.
774  *                (If we're applying it to a URL, we don't need them)
775  *
776  * Parameters  :
777  *          1  :  dest = Current actions, to modify.
778  *          2  :  src = Action to add.
779  *
780  * Returns     :  N/A
781  *
782  *********************************************************************/
783 void merge_current_action (struct current_action_spec *dest, 
784                            const struct action_spec *src)
785 {
786    int i;
787
788    dest->flags  &= src->mask;
789    dest->flags  |= src->add;
790
791    for (i = 0; i < ACTION_STRING_COUNT; i++)
792    {
793       char * str = src->string[i];
794       if (str)
795       {
796          freez(dest->string[i]);
797          dest->string[i] = strdup(str);
798       }
799    }
800
801    for (i = 0; i < ACTION_MULTI_COUNT; i++)
802    {
803       if (src->multi_remove_all[i])
804       {
805          /* Remove everything from dest */
806          destroy_list(dest->multi[i]);
807          list_duplicate(dest->multi[i], src->multi_add[i]);
808       }
809       else
810       {
811          list_remove_list(dest->multi[i], src->multi_remove[i]);
812          list_append_list_unique(dest->multi[i], src->multi_add[i]);
813       }
814    }
815 }
816
817
818 /*********************************************************************
819  *
820  * Function    :  free_current_action
821  *
822  * Description :  Free memory used by a current_action_spec.
823  *                Does not free the current_action_spec itself.
824  *
825  * Parameters  :
826  *          1  :  src = Source to free.
827  *
828  * Returns     :  N/A
829  *
830  *********************************************************************/
831 void free_current_action (struct current_action_spec *src)
832 {
833    int i;
834
835    for (i = 0; i < ACTION_STRING_COUNT; i++)
836    {
837       freez(src->string[i]);
838    }
839
840    for (i = 0; i < ACTION_MULTI_COUNT; i++)
841    {
842       destroy_list(src->multi[i]);
843    }
844
845    memset(src, '\0', sizeof(*src));
846 }
847
848
849 /*********************************************************************
850  *
851  * Function    :  unload_actions_file
852  *
853  * Description :  Unloads an actions module.
854  *
855  * Parameters  :
856  *          1  :  file_data = the data structure associated with the
857  *                            actions file.
858  *
859  * Returns     :  N/A
860  *
861  *********************************************************************/
862 void unload_actions_file(void *file_data)
863 {
864    struct url_actions * next;
865    struct url_actions * cur = (struct url_actions *)file_data;
866    while (cur != NULL)
867    {
868       next = cur->next;
869       free_url(cur->url);
870       free_action(cur->action);
871       freez(cur);
872       cur = next;
873    }
874
875 }
876
877
878 /*********************************************************************
879  *
880  * Function    :  load_actions_file
881  *
882  * Description :  Read and parse a action file and add to files
883  *                list.
884  *
885  * Parameters  :
886  *          1  :  csp = Current client state (buffers, headers, etc...)
887  *
888  * Returns     :  0 => Ok, everything else is an error.
889  *
890  *********************************************************************/
891 int load_actions_file(struct client_state *csp)
892 {
893    static struct file_list *current_actions_file  = NULL;
894
895    FILE *fp;
896
897    struct url_actions *last_perm;
898    struct url_actions *perm;
899    char  buf[BUFFER_SIZE];
900    struct file_list *fs;
901 #define MODE_START_OF_FILE 1
902 #define MODE_ACTIONS       2
903 #define MODE_ALIAS         3
904    int mode = MODE_START_OF_FILE;
905    struct action_spec cur_action[1];
906    struct action_alias * alias_list = NULL;
907
908    init_action(cur_action);
909
910    if (!check_file_changed(current_actions_file, csp->config->actions_file, &fs))
911    {
912       /* No need to load */
913       if (csp)
914       {
915          csp->actions_list = current_actions_file;
916       }
917       return 0;
918    }
919    if (!fs)
920    {
921       log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error finding file: %E",
922                 csp->config->actions_file);
923       return 1; /* never get here */
924    }
925
926    fs->f = last_perm = (struct url_actions *)zalloc(sizeof(*last_perm));
927    if (last_perm == NULL)
928    {
929       log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': out of memory!",
930                 csp->config->actions_file);
931       return 1; /* never get here */
932    }
933
934    if ((fp = fopen(csp->config->actions_file, "r")) == NULL)
935    {
936       log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error opening file: %E",
937                 csp->config->actions_file);
938       return 1; /* never get here */
939    }
940
941    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
942    {
943       if (*buf == '{')
944       {
945          /* It's a header block */
946          if (buf[1] == '{')
947          {
948             /* It's {{settings}} or {{alias}} */
949             int len = strlen(buf);
950             char * start = buf + 2;
951             char * end = buf + len - 1;
952             if ((len < 5) || (*end-- != '}') || (*end-- != '}'))
953             {
954                /* too short */
955                fclose(fp);
956                log_error(LOG_LEVEL_FATAL, 
957                   "can't load actions file '%s': invalid line: %s",
958                   csp->config->actions_file, buf);
959                return 1; /* never get here */
960             }
961
962             /* Trim leading and trailing whitespace. */
963             end[1] = '\0';
964             chomp(start);
965
966             if (*start == '\0')
967             {
968                /* too short */
969                fclose(fp);
970                log_error(LOG_LEVEL_FATAL, 
971                   "can't load actions file '%s': invalid line: {{ }}",
972                   csp->config->actions_file);
973                return 1; /* never get here */
974             }
975
976             if (0 == strcmpic(start, "alias"))
977             {
978                /* it's an {{alias}} block */
979
980                mode = MODE_ALIAS;
981             }
982             else
983             {
984                /* invalid {{something}} block */
985                fclose(fp);
986                log_error(LOG_LEVEL_FATAL, 
987                   "can't load actions file '%s': invalid line: {{%s}}",
988                   csp->config->actions_file, start);
989                return 1; /* never get here */
990             }
991          }
992          else
993          {
994             /* It's an actions block */
995
996             char  actions_buf[BUFFER_SIZE];
997             char * end;
998
999             /* set mode */
1000             mode    = MODE_ACTIONS;
1001
1002             /* free old action */
1003             free_action(cur_action);
1004
1005             /* trim { */
1006             strcpy(actions_buf, buf + 1);
1007
1008             /* check we have a trailing } and then trim it */
1009             end = actions_buf + strlen(actions_buf) - 1;
1010             if (*end != '}')
1011             {
1012                /* too short */
1013                fclose(fp);
1014                log_error(LOG_LEVEL_FATAL, 
1015                   "can't load actions file '%s': invalid line: %s",
1016                   csp->config->actions_file, buf);
1017                return 1; /* never get here */
1018             }
1019             *end = '\0';
1020
1021             /* trim any whitespace immediately inside {} */
1022             chomp(actions_buf);
1023
1024             if (*actions_buf == '\0')
1025             {
1026                /* too short */
1027                fclose(fp);
1028                log_error(LOG_LEVEL_FATAL, 
1029                   "can't load actions file '%s': invalid line: %s",
1030                   csp->config->actions_file, buf);
1031                return 1; /* never get here */
1032             }
1033
1034             if (get_actions(actions_buf, alias_list, cur_action))
1035             {
1036                /* error */
1037                fclose(fp);
1038                log_error(LOG_LEVEL_FATAL, 
1039                   "can't load actions file '%s': invalid line: %s",
1040                   csp->config->actions_file, buf);
1041                return 1; /* never get here */
1042             }
1043          }
1044       }
1045       else if (mode == MODE_ALIAS)
1046       {
1047          /* define an alias */
1048          char  actions_buf[BUFFER_SIZE];
1049          struct action_alias * new_alias;
1050
1051          char * start = strchr(buf, '=');
1052          char * end = start;
1053
1054          if ((start == NULL) || (start == buf))
1055          {
1056             log_error(LOG_LEVEL_FATAL, 
1057                "can't load actions file '%s': invalid alias line: %s",
1058                csp->config->actions_file, buf);
1059             return 1; /* never get here */
1060          }
1061
1062          if ((new_alias = zalloc(sizeof(*new_alias))) == 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
1071          /* Eat any the whitespace before the '=' */
1072          end--;
1073          while ((*end == ' ') || (*end == '\t'))
1074          {
1075             /*
1076              * we already know we must have at least 1 non-ws char
1077              * at start of buf - no need to check
1078              */
1079             end--;
1080          }
1081          end[1] = '\0';
1082
1083          /* Eat any the whitespace after the '=' */
1084          start++;
1085          while ((*start == ' ') || (*start == '\t'))
1086          {
1087             start++;
1088          }
1089          if (*start == '\0')
1090          {
1091             log_error(LOG_LEVEL_FATAL, 
1092                "can't load actions file '%s': invalid alias line: %s",
1093                csp->config->actions_file, buf);
1094             return 1; /* never get here */
1095          }
1096
1097          new_alias->name = strdup(buf);
1098
1099          strcpy(actions_buf, start);
1100
1101          if (get_actions(actions_buf, alias_list, new_alias->action))
1102          {
1103             /* error */
1104             fclose(fp);
1105             log_error(LOG_LEVEL_FATAL, 
1106                "can't load actions file '%s': invalid alias line: %s = %s",
1107                csp->config->actions_file, buf, start);
1108             return 1; /* never get here */
1109          }
1110          
1111          /* add to list */
1112          new_alias->next = alias_list;
1113          alias_list = new_alias;
1114       }
1115       else if (mode == MODE_ACTIONS)
1116       {
1117          /* it's a URL pattern */
1118
1119          /* allocate a new node */
1120          if ((perm = zalloc(sizeof(*perm))) == NULL)
1121          {
1122             fclose(fp);
1123             log_error(LOG_LEVEL_FATAL,
1124                "can't load actions file '%s': out of memory!",
1125                csp->config->actions_file);
1126             return 1; /* never get here */
1127          }
1128
1129          /* Save flags */
1130          copy_action (perm->action, cur_action);
1131
1132          /* Save the URL pattern */
1133          if (create_url_spec(perm->url, buf))
1134          {
1135             fclose(fp);
1136             log_error(LOG_LEVEL_FATAL, 
1137                "can't load actions file '%s': cannot create URL pattern from: %s",
1138                csp->config->actions_file, buf);
1139             return 1; /* never get here */
1140          }
1141
1142          /* add it to the list */
1143          last_perm->next = perm;
1144          last_perm = perm;
1145       }
1146       else if (mode == MODE_START_OF_FILE)
1147       {
1148          /* oops - please have a {} line as 1st line in file. */
1149          fclose(fp);
1150          log_error(LOG_LEVEL_FATAL, 
1151             "can't load actions file '%s': first line is invalid: %s",
1152             csp->config->actions_file, buf);
1153          return 1; /* never get here */
1154       }
1155       else
1156       {
1157          /* How did we get here? This is impossible! */
1158          fclose(fp);
1159          log_error(LOG_LEVEL_FATAL, 
1160             "can't load actions file '%s': INTERNAL ERROR - mode = %d",
1161             csp->config->actions_file, mode);
1162          return 1; /* never get here */
1163       }
1164    }
1165
1166    fclose(fp);
1167    
1168    free_action(cur_action);
1169
1170    while (alias_list != NULL)
1171    {
1172       struct action_alias * next = alias_list->next;
1173       freez((char *)alias_list->name);
1174       free_action(alias_list->action);
1175       free(alias_list);
1176       alias_list = next;
1177    }
1178
1179    /* the old one is now obsolete */
1180    if (current_actions_file)
1181    {
1182       current_actions_file->unloader = unload_actions_file;
1183    }
1184
1185    fs->next    = files->next;
1186    files->next = fs;
1187    current_actions_file = fs;
1188
1189    if (csp)
1190    {
1191       csp->actions_list = fs;
1192    }
1193
1194    return(0);
1195
1196 }