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