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