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