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