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