c33ba81578da7972e7ae19dda7091051cb2bb28d
[privoxy.git] / actions.c
1 const char actions_rcs[] = "$Id: actions.c,v ???? $";
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  *
37  *********************************************************************/
38 \f
39
40 #include "config.h"
41
42 #include <stdio.h>
43 #include <string.h>
44
45 #include "project.h"
46 #include "jcc.h"
47 #include "list.h"
48 #include "actions.h"
49 #include "miscutil.h"
50 #include "errlog.h"
51 #include "loaders.h"
52
53 const char actions_h_rcs[] = ACTIONS_H_VERSION;
54
55 /* This structure is used to hold user-defined aliases */
56 struct action_alias
57 {
58    const char * name;
59    struct action_spec action[1];
60    struct action_alias * next;
61 };
62
63
64 /*
65  * Must declare this in this file for the above structure.
66  * FIXME: Make this static or put structure in header.
67  */
68 extern int get_actions (char *line, struct action_alias * alias_list,
69                         struct action_spec *cur_action);
70
71 /*
72  * We need the main list of options.
73  *
74  * First, we need a way to tell between boolean, string, and multi-string
75  * options.  For string and multistring options, we also need to be
76  * able to tell the difference between a "+" and a "-".  (For bools,
77  * the "+"/"-" information is encoded in "add" and "mask").  So we use
78  * an enumerated type (well, the preprocessor equivalent).  Here are
79  * the values:
80  */
81 #define AV_NONE       0 /* +opt -opt */
82 #define AV_ADD_STRING 1 /* +stropt{string} */
83 #define AV_REM_STRING 2 /* -stropt */
84 #define AV_ADD_MULTI  3 /* +multiopt{string} +multiopt{string2} */
85 #define AV_REM_MULTI  4 /* -multiopt{string} -multiopt{*}       */
86
87 /*
88  * We need a structure to hold the name, flag changes, 
89  * type, and string index.
90  */
91 struct action_name
92 {
93    const char * name;
94    unsigned mask;   /* a bit set to "0" = remove action */
95    unsigned add;    /* a bit set to "1" = add action */
96    int takes_value; /* an AV_... constant */
97    int index;       /* index into strings[] or multi[] */
98 };
99
100 /*
101  * And with those building blocks in place, here's the array.
102  */
103 static const struct action_name action_names[] =
104 {
105    /*
106     * Well actually there's no data here - it's in actionlist.h
107     * This keeps it together to make it easy to change.
108     *
109     * Here's the macros used to format it:
110     */
111 #define DEFINE_ACTION_MULTI(name,index)                   \
112    { "+" name, ACTION_MASK_ALL, 0, AV_ADD_MULTI, index }, \
113    { "-" name, ACTION_MASK_ALL, 0, AV_REM_MULTI, index },
114 #define DEFINE_ACTION_STRING(name,flag,index)                 \
115    { "+" name, ACTION_MASK_ALL, flag, AV_ADD_STRING, index }, \
116    { "-" name, ~flag, 0, AV_REM_STRING, index },
117 #define DEFINE_ACTION_BOOL(name,flag)   \
118    { "+" name, ACTION_MASK_ALL, flag }, \
119    { "-" name, ~flag, 0 },
120 #define DEFINE_ACTION_ALIAS 1 /* Want aliases please */
121
122 #include "actionlist.h"
123
124 #undef DEFINE_ACTION_MULTI
125 #undef DEFINE_ACTION_STRING
126 #undef DEFINE_ACTION_BOOL
127 #undef DEFINE_ACTION_ALIAS
128
129  /*
130    { "+add-header",        ACTION_MASK_ALL, 0,                      AV_ADD_MULTI,  ACTION_MULTI_ADD_HEADER },
131    { "+block",             ACTION_MASK_ALL, ACTION_BLOCK           },
132    { "+fast-redirects",    ACTION_MASK_ALL, ACTION_FAST_REDIRECTS  },
133    { "+filter",            ACTION_MASK_ALL, ACTION_FILTER          },
134    { "+hide-forwarded",    ACTION_MASK_ALL, ACTION_HIDE_FORWARDED  },
135    { "+hide-from",         ACTION_MASK_ALL, ACTION_HIDE_FROM,       AV_ADD_STRING, ACTION_STRING_FROM       },
136    { "+hide-referer",      ACTION_MASK_ALL, ACTION_HIDE_REFERER,    AV_ADD_STRING, ACTION_STRING_REFERER    },
137    { "+hide-referrer",     ACTION_MASK_ALL, ACTION_HIDE_REFERER,    AV_ADD_STRING, ACTION_STRING_REFERER    },
138    { "+hide-user-agent",   ACTION_MASK_ALL, ACTION_HIDE_USER_AGENT, AV_ADD_STRING, ACTION_STRING_USER_AGENT },
139    { "+image",             ACTION_MASK_ALL, ACTION_IMAGE           },
140    { "+no-cookies-read",   ACTION_MASK_ALL, ACTION_NO_COOKIE_READ  },
141    { "+no-cookies-set",    ACTION_MASK_ALL, ACTION_NO_COOKIE_SET   },
142    { "+no-popup",          ACTION_MASK_ALL, ACTION_NO_POPUPS       },
143    { "+no-popups",         ACTION_MASK_ALL, ACTION_NO_POPUPS       },
144    { "+vanilla-wafer",     ACTION_MASK_ALL, ACTION_VANILLA_WAFER   },
145    { "+wafer",             ACTION_MASK_ALL, 0,                      AV_ADD_MULTI,  ACTION_MULTI_WAFER },
146    { "-add-header",        ACTION_MASK_ALL,         0, AV_REM_MULTI,  ACTION_MULTI_ADD_HEADER },
147    { "-block",             ~ACTION_BLOCK,           0 },
148    { "-fast-redirects",    ~ACTION_FAST_REDIRECTS,  0 },
149    { "-filter",            ~ACTION_FILTER,          0 },
150    { "-hide-forwarded",    ~ACTION_HIDE_FORWARDED,  0 },
151    { "-hide-from",         ~ACTION_HIDE_FROM,       0, AV_REM_STRING, ACTION_STRING_FROM       },
152    { "-hide-referer",      ~ACTION_HIDE_REFERER,    0, AV_REM_STRING, ACTION_STRING_REFERER    },
153    { "-hide-referrer",     ~ACTION_HIDE_REFERER,    0, AV_REM_STRING, ACTION_STRING_REFERER    },
154    { "-hide-user-agent",   ~ACTION_HIDE_USER_AGENT, 0, AV_REM_STRING, ACTION_STRING_USER_AGENT },
155    { "-image",             ~ACTION_IMAGE,           0 },
156    { "-no-cookies-read",   ~ACTION_NO_COOKIE_READ,  0 },
157    { "-no-cookies-set",    ~ACTION_NO_COOKIE_SET,   0 },
158    { "-no-popup",          ~ACTION_NO_POPUPS,       0 },
159    { "-no-popups",         ~ACTION_NO_POPUPS,       0 },
160    { "-vanilla-wafer",     ~ACTION_VANILLA_WAFER,   0 },
161    { "-wafer",             ACTION_MASK_ALL,         0, AV_REM_MULTI,  ACTION_MULTI_WAFER },
162 */
163    { NULL, 0, 0 } /* End marker */
164 };
165
166
167 /*********************************************************************
168  *
169  * Function    :  merge_actions
170  *
171  * Description :  Merge two actions together.
172  *                Similar to "cur_action += new_action".
173  *
174  * Parameters  :
175  *          1  :  cur_action = Current actions, to modify.
176  *          2  :  new_action = Action to add.
177  *
178  * Returns     :  N/A
179  *
180  *********************************************************************/
181 void merge_actions (struct action_spec *dest, 
182                     const struct action_spec *src)
183 {
184    int i;
185
186    dest->mask &= src->mask;
187    dest->add  &= src->mask;
188    dest->add  |= src->add;
189
190    for (i = 0; i < ACTION_STRING_COUNT; i++)
191    {
192       char * str = src->string[i];
193       if (str)
194       {
195          freez(dest->string[i]);
196          dest->string[i] = strdup(str);
197       }
198    }
199
200    for (i = 0; i < ACTION_MULTI_COUNT; i++)
201    {
202       if (src->multi_remove_all[i])
203       {
204          /* Remove everything from dest */
205          destroy_list(dest->multi_remove[i]);
206          destroy_list(dest->multi_add[i]);
207          dest->multi_remove_all[i] = 1;
208          list_duplicate(dest->multi_add[i], src->multi_add[i]);
209       }
210       else if (dest->multi_remove_all[i])
211       {
212          /*
213           * dest already removes everything, so we only need to worry
214           * about what we add.
215           */
216          list_remove_list(dest->multi_add[i], src->multi_remove[i]);
217          list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
218       }
219       else
220       {
221          /* No "remove all"s to worry about. */
222          list_remove_list(dest->multi_add[i], src->multi_remove[i]);
223          list_append_list_unique(dest->multi_remove[i], src->multi_remove[i]);
224          list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
225       }
226    }
227 }
228
229
230 /*********************************************************************
231  *
232  * Function    :  copy_action
233  *
234  * Description :  Copy an action_specs.
235  *                Similar to "cur_action = new_action".
236  *
237  * Parameters  :
238  *          1  :  dest = Destination of copy.
239  *          2  :  src = Source for copy.
240  *
241  * Returns     :  N/A
242  *
243  *********************************************************************/
244 void copy_action (struct action_spec *dest, 
245                   const struct action_spec *src)
246 {
247    int i;
248
249    dest->mask = src->mask;
250    dest->add  = src->add;
251
252    for (i = 0; i < ACTION_STRING_COUNT; i++)
253    {
254       char * str = src->string[i];
255       dest->string[i] = (str ? strdup(str) : NULL);
256    }
257
258    for (i = 0; i < ACTION_MULTI_COUNT; i++)
259    {
260       dest->multi_remove_all[i] = src->multi_remove_all[i];
261       list_duplicate(dest->multi_remove[i], src->multi_remove[i]);
262       list_duplicate(dest->multi_add[i],    src->multi_add[i]);
263    }
264 }
265
266
267 /*********************************************************************
268  *
269  * Function    :  free_action
270  *
271  * Description :  Free an action_specs.
272  *
273  * Parameters  :
274  *          1  :  src = Source to free.
275  *
276  * Returns     :  N/A
277  *
278  *********************************************************************/
279 void free_action (struct action_spec *src)
280 {
281    int i;
282
283    for (i = 0; i < ACTION_STRING_COUNT; i++)
284    {
285       freez(src->string[i]);
286    }
287
288    for (i = 0; i < ACTION_MULTI_COUNT; i++)
289    {
290       destroy_list(src->multi_remove[i]);
291       destroy_list(src->multi_add[i]);
292    }
293
294    memset(src, '\0', sizeof(*src));
295 }
296
297
298 /*********************************************************************
299  *
300  * Function    :  get_action_token
301  *
302  * Description :  Parses a line for the first action.
303  *                Modifies it's input array, doesn't allocate memory.
304  *                e.g. given:
305  *                *line="  +abc{def}  -ghi "
306  *                Returns:
307  *                *line="  -ghi "
308  *                *name="+abc"
309  *                *value="def"
310  *
311  * Parameters  :
312  *          1  :  line = [in] The line containing the action.
313  *                       [out] Start of next action on line, or
314  *                       NULL if we reached the end of line before
315  *                       we found an action.
316  *          2  :  name = [out] Start of action name, null
317  *                       terminated.  NULL on EOL
318  *          3  :  value = [out] Start of action value, null 
319  *                        terminated.  NULL if none or EOL.
320  *
321  * Returns     :  0 => Ok
322  *                nonzero => Mismatched {} (line was trashed anyway)
323  *
324  *********************************************************************/
325 int get_action_token(char **line, char **name, char **value)
326 {
327    char * str = *line;
328    char ch;
329
330    /* set default returns */
331    *line = NULL;
332    *name = NULL;
333    *value = NULL;
334
335    /* Eat any leading whitespace */
336    while ((*str == ' ') || (*str == '\t'))
337    {
338       str++;
339    }
340
341    if (*str == '\0')
342    {
343       return 0;
344    }
345
346    if (*str == '{')
347    {
348       /* null name, just value is prohibited */
349       return 1;
350    }
351
352    *name = str;
353
354    /* parse option */
355    while (((ch = *str) != '\0') && 
356           (ch != ' ') && (ch != '\t') && (ch != '{'))
357    {
358       if (ch == '}')
359       {
360          /* error */
361          return 1;
362       }
363       str++;
364    }
365    *str = '\0';
366
367    if (ch != '{')
368    {
369       /* no value */
370       if (ch == '\0')
371       {
372          /* EOL - be careful not to run off buffer */
373          *line = str;
374       }
375       else
376       {
377          /* More to parse next time. */
378          *line = str + 1;
379       }
380       return 0;
381    }
382
383    str++;
384    *value = str;
385
386    str = strchr(str, '}');
387    if (str == NULL)
388    {
389       /* error */
390       *value = NULL;
391       return 1;
392    }
393
394    /* got value */
395    *str = '\0';
396    *line = str + 1;
397
398    chomp(*value);
399
400    return 0;
401 }
402
403
404 /*********************************************************************
405  *
406  * Function    :  get_actions
407  *
408  * Description :  Parses a list of actions.
409  *
410  * Parameters  :
411  *          1  :  line = The string containing the actions.
412  *                       Will be written to by this function.
413  *          2  :  aliases = Custom alias list, or NULL for none.
414  *          3  :  cur_action = Where to store the action.  Caller
415  *                             allocates memory.
416  *
417  * Returns     :  0 => Ok
418  *                nonzero => Error (line was trashed anyway)
419  *
420  *********************************************************************/
421 int get_actions(char *line, struct action_alias * alias_list,
422                 struct action_spec *cur_action)
423 {
424    memset(cur_action, '\0', sizeof(*cur_action));
425    cur_action->mask = ACTION_MASK_ALL;
426
427    while (line)
428    {
429       char * option = NULL;
430       char * value = NULL;
431
432       if (get_action_token(&line, &option, &value))
433       {
434          return 1;
435       }
436
437       if (option)
438       {
439          /* handle option in 'option' */
440       
441          /* Check for standard action name */
442          const struct action_name * action = action_names;
443
444          while ( (action->name != NULL) && (0 != strcmpic(action->name, option)) )
445          {
446             action++;
447          }
448          if (action->name != NULL)
449          {
450             /* Found it */
451             cur_action->mask &= action->mask;
452             cur_action->add  &= action->mask;
453             cur_action->add  |= action->add;
454
455             switch (action->takes_value)
456             {
457             case AV_NONE:
458                /* ignore any option. */
459                break;
460             case AV_ADD_STRING:
461                {
462                   /* add single string. */
463
464                   if ((value == NULL) || (*value == '\0'))
465                   {
466                      return 1;
467                   }
468                   /* FIXME: should validate option string here */
469                   freez (cur_action->string[action->index]);
470                   cur_action->string[action->index] = strdup(value);
471                   break;
472                }
473             case AV_REM_STRING:
474                {
475                   /* remove single string. */
476
477                   freez (cur_action->string[action->index]);
478                   break;
479                }
480             case AV_ADD_MULTI:
481                {
482                   /* append multi string. */
483
484                   struct list * remove = cur_action->multi_remove[action->index];
485                   struct list * add    = cur_action->multi_add[action->index];
486
487                   if ((value == NULL) || (*value == '\0'))
488                   {
489                      return 1;
490                   }
491
492                   list_remove_item(remove, value);
493                   enlist_unique(add, value);
494                   break;
495                }
496             case AV_REM_MULTI:
497                {
498                   /* remove multi string. */
499
500                   struct list * remove = cur_action->multi_remove[action->index];
501                   struct list * add    = cur_action->multi_add[action->index];
502
503                   if ( (value == NULL) || (*value == '\0')
504                      || ((*value == '*') && (value[1] == '\0')) )
505                   {
506                      /*
507                       * no option, or option == "*".
508                       *
509                       * Remove *ALL*.
510                       */
511                      destroy_list(remove);
512                      destroy_list(add);
513                      cur_action->multi_remove_all[action->index] = 1;
514                   }
515                   else
516                   {
517                      /* Valid option - remove only 1 option */
518
519                      if ( !cur_action->multi_remove_all[action->index] )
520                      {
521                         /* there isn't a catch-all in the remove list already */
522                         enlist_unique(remove, value);
523                      }
524                      list_remove_item(add, value);
525                   }
526                   break;
527                }
528             default:
529                /* Shouldn't get here unless there's memory corruption. */
530                return 1;
531             }
532          }
533          else
534          {
535             /* try user aliases. */
536             const struct action_alias * alias = alias_list;
537             
538             while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) )
539             {
540                alias = alias->next;
541             }
542             if (alias != NULL)
543             {
544                /* Found it */
545                merge_actions(cur_action, alias->action);
546             }
547             else
548             {
549                /* Bad action name */
550                return 1;
551             }
552          }
553       }
554    }
555
556    return 0;
557 }
558
559
560 /*********************************************************************
561  *
562  * Function    :  actions_to_text
563  *
564  * Description :  Converts a actionsfile entry from numeric form
565  *                ("mask" and "add") to text.
566  *
567  * Parameters  :
568  *          1  :  mask = As from struct url_actions
569  *          2  :  add  = As from struct url_actions
570  *
571  * Returns     :  A string.  Caller must free it.
572  *
573  *********************************************************************/
574 char * actions_to_text(struct action_spec *action)
575 {
576    unsigned mask = action->mask;
577    unsigned add  = action->add;
578    char * result = strdup("");
579    struct list * lst;
580
581    /* sanity - prevents "-feature +feature" */
582    mask |= add;
583
584
585 #define DEFINE_ACTION_BOOL(__name, __bit)   \
586    if (!(mask & __bit))                     \
587    {                                        \
588       result = strsav(result, " -" __name); \
589    }                                        \
590    else if (add & __bit)                    \
591    {                                        \
592       result = strsav(result, " +" __name); \
593    }
594
595 #define DEFINE_ACTION_STRING(__name, __bit, __index) \
596    if (!(mask & __bit))                     \
597    {                                        \
598       result = strsav(result, " -" __name); \
599    }                                        \
600    else if (add & __bit)                    \
601    {                                        \
602       result = strsav(result, " +" __name "{"); \
603       result = strsav(result, action->string[__index]); \
604       result = strsav(result, "}"); \
605    }
606
607 #define DEFINE_ACTION_MULTI(__name, __index)         \
608    if (action->multi_remove_all[__index])            \
609    {                                                 \
610       result = strsav(result, " -" __name "{*}");    \
611    }                                                 \
612    else                                              \
613    {                                                 \
614       lst = action->multi_remove[__index]->next;     \
615       while (lst)                                    \
616       {                                              \
617          result = strsav(result, " -" __name "{");   \
618          result = strsav(result, lst->str);          \
619          result = strsav(result, "}");               \
620          lst = lst->next;                            \
621       }                                              \
622    }                                                 \
623    lst = action->multi_add[__index]->next;           \
624    while (lst)                                       \
625    {                                                 \
626       result = strsav(result, " +" __name "{");      \
627       result = strsav(result, lst->str);             \
628       result = strsav(result, "}");                  \
629       lst = lst->next;                               \
630    }
631
632 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
633
634 #include "actionlist.h"
635
636 #undef DEFINE_ACTION_MULTI
637 #undef DEFINE_ACTION_STRING
638 #undef DEFINE_ACTION_BOOL
639 #undef DEFINE_ACTION_ALIAS
640 /*   
641    ACTION_MULTI_TO_TEXT (                        "add-header",      ACTION_MULTI_ADD_HEADER)
642    ACTION_TO_TEXT       (ACTION_BLOCK,           "block");
643    ACTION_TO_TEXT       (ACTION_FAST_REDIRECTS,  "fast-redirects");
644    ACTION_TO_TEXT       (ACTION_FILTER,          "filter");
645    ACTION_TO_TEXT       (ACTION_HIDE_FORWARDED,  "hide-forwarded");
646    ACTION_STRING_TO_TEXT(ACTION_HIDE_FROM,       "hide-from",       ACTION_STRING_FROM);
647    ACTION_STRING_TO_TEXT(ACTION_HIDE_REFERER,    "hide-referer",    ACTION_STRING_REFERER);
648    ACTION_STRING_TO_TEXT(ACTION_HIDE_USER_AGENT, "hide-user-agent", ACTION_STRING_USER_AGENT);
649    ACTION_TO_TEXT       (ACTION_IMAGE,           "image");
650    ACTION_TO_TEXT       (ACTION_NO_COOKIE_READ,  "no-cookies-read");
651    ACTION_TO_TEXT       (ACTION_NO_COOKIE_SET,   "no-cookies-set");
652    ACTION_TO_TEXT       (ACTION_NO_POPUPS,       "no-popups");
653    ACTION_TO_TEXT       (ACTION_VANILLA_WAFER,   "vanilla-wafer");
654    ACTION_MULTI_TO_TEXT (                        "wafer",           ACTION_MULTI_WAFER);
655
656 #undef ACTION_TO_TEXT
657 #undef ACTION_STRING_TO_TEXT
658 #undef ACTION_MULTI_TO_TEXT
659 */
660    return result;
661 }
662
663
664 /*********************************************************************
665  *
666  * Function    :  current_actions_to_text
667  *
668  * Description :  Converts a actionsfile entry to text.
669  *
670  * Parameters  :
671  *          1  :  action = Action
672  *
673  * Returns     :  A string.  Caller must free it.
674  *
675  *********************************************************************/
676 char * current_action_to_text(struct current_action_spec *action)
677 {
678    unsigned flags  = action->flags;
679    char * result = strdup("");
680    struct list_share * lst;
681
682 #define DEFINE_ACTION_BOOL(__name, __bit)   \
683    if (flags & __bit)                       \
684    {                                        \
685       result = strsav(result, " +" __name); \
686    }                                        \
687    else                                     \
688    {                                        \
689       result = strsav(result, " -" __name); \
690    }
691
692 #define DEFINE_ACTION_STRING(__name, __bit, __index)    \
693    if (flags & __bit)                                   \
694    {                                                    \
695       result = strsav(result, " +" __name "{");         \
696       result = strsav(result, action->string[__index]); \
697       result = strsav(result, "}");                     \
698    }                                                    \
699    else                                                 \
700    {                                                    \
701       result = strsav(result, " -" __name);             \
702    }
703
704 #define DEFINE_ACTION_MULTI(__name, __index)            \
705    lst = action->multi[__index]->next;                  \
706    if (lst == NULL)                                     \
707    {                                                    \
708       result = strsav(result, " -" __name);             \
709    }                                                    \
710    else                                                 \
711    {                                                    \
712       while (lst)                                       \
713       {                                                 \
714          result = strsav(result, " +" __name "{");      \
715          result = strsav(result, lst->str);             \
716          result = strsav(result, "}");                  \
717          lst = lst->next;                               \
718       }                                                 \
719    }
720
721 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
722
723 #include "actionlist.h"
724
725 #undef DEFINE_ACTION_MULTI
726 #undef DEFINE_ACTION_STRING
727 #undef DEFINE_ACTION_BOOL
728 #undef DEFINE_ACTION_ALIAS
729 /*   
730
731    ACTION_MULTI_TO_TEXT (                        "add-header",      ACTION_MULTI_ADD_HEADER)
732    ACTION_TO_TEXT       (ACTION_BLOCK,           "block");
733    ACTION_TO_TEXT       (ACTION_FAST_REDIRECTS,  "fast-redirects");
734    ACTION_TO_TEXT       (ACTION_FILTER,          "filter");
735    ACTION_TO_TEXT       (ACTION_HIDE_FORWARDED,  "hide-forwarded");
736    ACTION_STRING_TO_TEXT(ACTION_HIDE_FROM,       "hide-from",       ACTION_STRING_FROM);
737    ACTION_STRING_TO_TEXT(ACTION_HIDE_REFERER,    "hide-referer",    ACTION_STRING_REFERER);
738    ACTION_STRING_TO_TEXT(ACTION_HIDE_USER_AGENT, "hide-user-agent", ACTION_STRING_USER_AGENT);
739    ACTION_TO_TEXT       (ACTION_IMAGE,           "image");
740    ACTION_TO_TEXT       (ACTION_NO_COOKIE_READ,  "no-cookies-read");
741    ACTION_TO_TEXT       (ACTION_NO_COOKIE_SET,   "no-cookies-set");
742    ACTION_TO_TEXT       (ACTION_NO_POPUPS,       "no-popups");
743    ACTION_TO_TEXT       (ACTION_VANILLA_WAFER,   "vanilla-wafer");
744    ACTION_MULTI_TO_TEXT (                        "wafer",           ACTION_MULTI_WAFER);
745
746 #undef ACTION_TO_TEXT
747 #undef ACTION_STRING_TO_TEXT
748 #undef ACTION_MULTI_TO_TEXT
749 */
750    return result;
751 }
752
753
754 /*********************************************************************
755  *
756  * Function    :  init_current_action
757  *
758  * Description :  Zero out an action.
759  *
760  * Parameters  :
761  *          1  :  dest = An uninitialized current_action_spec.
762  *
763  * Returns     :  N/A
764  *
765  *********************************************************************/
766 void init_current_action (struct current_action_spec *dest)
767 {
768    memset(dest, '\0', sizeof(*dest));
769    dest->flags = ACTION_MOST_COMPATIBLE;
770 }
771
772
773 /*********************************************************************
774  *
775  * Function    :  merge_current_action
776  *
777  * Description :  Merge two actions together.
778  *                Similar to "dest += src".
779  *                Differences between this and merge_actions()
780  *                is that this one doesn't allocate memory for
781  *                strings (so "src" better be in memory for at least
782  *                as long as "dest" is, and you'd better free
783  *                "dest" using "current_free_action").
784  *                Also, there is no  mask or remove lists in dest.
785  *                (If we're applying it to a URL, we don't need them)
786  *
787  * Parameters  :
788  *          1  :  dest = Current actions, to modify.
789  *          2  :  src = Action to add.
790  *
791  * Returns     :  N/A
792  *
793  *********************************************************************/
794 void merge_current_action (struct current_action_spec *dest, 
795                            const struct action_spec *src)
796 {
797    int i;
798
799    dest->flags  &= src->mask;
800    dest->flags  |= src->add;
801
802    for (i = 0; i < ACTION_STRING_COUNT; i++)
803    {
804       char * str = src->string[i];
805       if (str)
806       {
807          dest->string[i] = str;
808       }
809    }
810
811    for (i = 0; i < ACTION_MULTI_COUNT; i++)
812    {
813       if (src->multi_remove_all[i])
814       {
815          /* Remove everything from dest */
816          destroy_list_share(dest->multi[i]);
817          list_duplicate_share(dest->multi[i], src->multi_add[i]);
818       }
819       else
820       {
821          list_remove_list_share(dest->multi[i], src->multi_remove[i]);
822          list_append_list_unique_share(dest->multi[i], src->multi_add[i]);
823       }
824    }
825 }
826
827
828 /*********************************************************************
829  *
830  * Function    :  free_current_action
831  *
832  * Description :  Free a current_action_spec.
833  *
834  * Parameters  :
835  *          1  :  src = Source to free.
836  *
837  * Returns     :  N/A
838  *
839  *********************************************************************/
840 void free_current_action (struct current_action_spec *src)
841 {
842    int i;
843
844    for (i = 0; i < ACTION_MULTI_COUNT; i++)
845    {
846       destroy_list_share(src->multi[i]);
847    }
848
849    memset(src, '\0', sizeof(*src));
850 }
851
852
853 /*********************************************************************
854  *
855  * Function    :  unload_actions_file
856  *
857  * Description :  Unloads an actions module.
858  *
859  * Parameters  :
860  *          1  :  file_data = the data structure associated with the
861  *                            actions file.
862  *
863  * Returns     :  N/A
864  *
865  *********************************************************************/
866 void unload_actions_file(void *file_data)
867 {
868    struct url_actions * next;
869    struct url_actions * cur = (struct url_actions *)file_data;
870    while (cur != NULL)
871    {
872       next = cur->next;
873       free_url(cur->url);
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[BUFSIZ];
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    memset(cur_action, '\0', sizeof(*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[BUFSIZ];
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[BUFSIZ];
1052          struct action_alias * new_alias;
1053          int more = 1;
1054
1055          char * start = strchr(buf, '=');
1056          char * end = start;
1057
1058          if ((start == NULL) || (start == buf))
1059          {
1060             log_error(LOG_LEVEL_FATAL, 
1061                "can't load actions file '%s': invalid alias line: %s",
1062                csp->config->actions_file, buf);
1063             return 1; /* never get here */
1064          }
1065
1066          if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1067          {
1068             fclose(fp);
1069             log_error(LOG_LEVEL_FATAL,
1070                "can't load actions file '%s': out of memory!",
1071                csp->config->actions_file);
1072             return 1; /* never get here */
1073          }
1074
1075          /* Eat any the whitespace before the '=' */
1076          end--;
1077          while ((*end == ' ') || (*end == '\t'))
1078          {
1079             /*
1080              * we already know we must have at least 1 non-ws char
1081              * at start of buf - no need to check
1082              */
1083             end--;
1084          }
1085          end[1] = '\0';
1086
1087          /* Eat any the whitespace after the '=' */
1088          start++;
1089          while ((*start == ' ') || (*start == '\t'))
1090          {
1091             start++;
1092          }
1093          if (*start == '\0')
1094          {
1095             log_error(LOG_LEVEL_FATAL, 
1096                "can't load actions file '%s': invalid alias line: %s",
1097                csp->config->actions_file, buf);
1098             return 1; /* never get here */
1099          }
1100
1101          new_alias->name = strdup(buf);
1102
1103          strcpy(actions_buf, start);
1104
1105          if (get_actions(actions_buf, alias_list, new_alias->action))
1106          {
1107             /* error */
1108             fclose(fp);
1109             log_error(LOG_LEVEL_FATAL, 
1110                "can't load actions file '%s': invalid alias line: %s = %s",
1111                csp->config->actions_file, buf, start);
1112             return 1; /* never get here */
1113          }
1114          
1115          /* add to list */
1116          new_alias->next = alias_list;
1117          alias_list = new_alias;
1118       }
1119       else if (mode == MODE_ACTIONS)
1120       {
1121          /* it's a URL pattern */
1122
1123          /* allocate a new node */
1124          if ((perm = zalloc(sizeof(*perm))) == NULL)
1125          {
1126             fclose(fp);
1127             log_error(LOG_LEVEL_FATAL,
1128                "can't load actions file '%s': out of memory!",
1129                csp->config->actions_file);
1130             return 1; /* never get here */
1131          }
1132
1133          /* Save flags */
1134          copy_action (perm->action, cur_action);
1135
1136          /* Save the URL pattern */
1137          if (create_url_spec(perm->url, buf))
1138          {
1139             fclose(fp);
1140             log_error(LOG_LEVEL_FATAL, 
1141                "can't load actions file '%s': cannot create URL pattern from: %s",
1142                csp->config->actions_file, buf);
1143             return 1; /* never get here */
1144          }
1145
1146          /* add it to the list */
1147          last_perm->next = perm;
1148          last_perm = perm;
1149       }
1150       else if (mode == MODE_START_OF_FILE)
1151       {
1152          /* oops - please have a {} line as 1st line in file. */
1153          fclose(fp);
1154          log_error(LOG_LEVEL_FATAL, 
1155             "can't load actions file '%s': first line is invalid: %s",
1156             csp->config->actions_file, buf);
1157          return 1; /* never get here */
1158       }
1159       else
1160       {
1161          /* How did we get here? This is impossible! */
1162          fclose(fp);
1163          log_error(LOG_LEVEL_FATAL, 
1164             "can't load actions file '%s': INTERNAL ERROR - mode = %d",
1165             csp->config->actions_file, mode);
1166          return 1; /* never get here */
1167       }
1168    }
1169
1170    fclose(fp);
1171    
1172    free_action(cur_action);
1173
1174    while (alias_list != NULL)
1175    {
1176       struct action_alias * next = alias_list->next;
1177       freez((char *)alias_list->name);
1178       free_action(alias_list->action);
1179       free(alias_list);
1180       alias_list = next;
1181    }
1182
1183 #ifndef SPLIT_PROXY_ARGS
1184    if (!suppress_blocklists)
1185    {
1186       fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1187    }
1188 #endif /* ndef SPLIT_PROXY_ARGS */
1189
1190    /* the old one is now obsolete */
1191    if (current_actions_file)
1192    {
1193       current_actions_file->unloader = unload_actions_file;
1194    }
1195
1196    fs->next    = files->next;
1197    files->next = fs;
1198    current_actions_file = fs;
1199
1200    if (csp)
1201    {
1202       csp->actions_list = fs;
1203    }
1204
1205    return(0);
1206
1207 }