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