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