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