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