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