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