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