1 const char actions_rcs[] = "$Id: actions.c,v 1.6 2001/06/07 23:04:34 jongfoster Exp $";
2 /*********************************************************************
4 * File : $Source: /cvsroot/ijbswa/current/actions.c,v $
6 * Purpose : Declares functions to work with actions files
7 * Functions declared include: FIXME
9 * Copyright : Written by and Copyright (C) 2001 the SourceForge
10 * IJBSWA team. http://ijbswa.sourceforge.net
12 * Based on the Internet Junkbuster originally written
13 * by and Copyright (C) 1997 Anonymous Coders and
14 * Junkbusters Corporation. http://www.junkbusters.com
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.
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.
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.
36 * Revision 1.6 2001/06/07 23:04:34 jongfoster
37 * Made get_actions() static.
39 * Revision 1.5 2001/06/03 19:11:48 oes
40 * adapted to new enlist_unique arg format
42 * Revision 1.5 2001/06/03 11:03:48 oes
49 * adapted to new enlist_unique arg format
53 * introduced confdir option
55 * filters.c filtrers.h
57 * extracted-CGI relevant stuff
65 * support for new cgi mechansim
69 * functions for new list type: "map"
70 * extended enlist_unique
77 * deleted const struct interceptors
85 * added struct http_response
86 * changes struct interceptors to struct cgi_dispatcher
87 * moved HTML stuff to cgi.h
96 * Revision 1.4 2001/06/01 20:03:42 jongfoster
97 * Better memory management - current_action->strings[] now
98 * contains copies of the strings, not the original.
100 * Revision 1.3 2001/06/01 18:49:17 jongfoster
101 * Replaced "list_share" with "list" - the tiny memory gain was not
102 * worth the extra complexity.
104 * Revision 1.2 2001/05/31 21:40:00 jongfoster
105 * Removing some commented out, obsolete blocks of code.
107 * Revision 1.1 2001/05/31 21:16:46 jongfoster
108 * Moved functions to process the action list into this new file.
111 *********************************************************************/
123 #include "miscutil.h"
127 const char actions_h_rcs[] = ACTIONS_H_VERSION;
130 /* Turn off everything except forwarding */
131 /* This structure is used to hold user-defined aliases */
135 struct action_spec action[1];
136 struct action_alias * next;
141 * Must declare this in this file for the above structure.
143 static int get_actions (char *line,
144 struct action_alias * alias_list,
145 struct action_spec *cur_action);
148 * We need the main list of options.
150 * First, we need a way to tell between boolean, string, and multi-string
151 * options. For string and multistring options, we also need to be
152 * able to tell the difference between a "+" and a "-". (For bools,
153 * the "+"/"-" information is encoded in "add" and "mask"). So we use
154 * an enumerated type (well, the preprocessor equivalent). Here are
157 #define AV_NONE 0 /* +opt -opt */
158 #define AV_ADD_STRING 1 /* +stropt{string} */
159 #define AV_REM_STRING 2 /* -stropt */
160 #define AV_ADD_MULTI 3 /* +multiopt{string} +multiopt{string2} */
161 #define AV_REM_MULTI 4 /* -multiopt{string} -multiopt{*} */
164 * We need a structure to hold the name, flag changes,
165 * type, and string index.
170 unsigned mask; /* a bit set to "0" = remove action */
171 unsigned add; /* a bit set to "1" = add action */
172 int takes_value; /* an AV_... constant */
173 int index; /* index into strings[] or multi[] */
177 * And with those building blocks in place, here's the array.
179 static const struct action_name action_names[] =
182 * Well actually there's no data here - it's in actionlist.h
183 * This keeps it together to make it easy to change.
185 * Here's the macros used to format it:
187 #define DEFINE_ACTION_MULTI(name,index) \
188 { "+" name, ACTION_MASK_ALL, 0, AV_ADD_MULTI, index }, \
189 { "-" name, ACTION_MASK_ALL, 0, AV_REM_MULTI, index },
190 #define DEFINE_ACTION_STRING(name,flag,index) \
191 { "+" name, ACTION_MASK_ALL, flag, AV_ADD_STRING, index }, \
192 { "-" name, ~flag, 0, AV_REM_STRING, index },
193 #define DEFINE_ACTION_BOOL(name,flag) \
194 { "+" name, ACTION_MASK_ALL, flag }, \
195 { "-" name, ~flag, 0 },
196 #define DEFINE_ACTION_ALIAS 1 /* Want aliases please */
198 #include "actionlist.h"
200 #undef DEFINE_ACTION_MULTI
201 #undef DEFINE_ACTION_STRING
202 #undef DEFINE_ACTION_BOOL
203 #undef DEFINE_ACTION_ALIAS
205 { NULL, 0, 0 } /* End marker */
209 /*********************************************************************
211 * Function : merge_actions
213 * Description : Merge two actions together.
214 * Similar to "cur_action += new_action".
217 * 1 : cur_action = Current actions, to modify.
218 * 2 : new_action = Action to add.
222 *********************************************************************/
223 void merge_actions (struct action_spec *dest,
224 const struct action_spec *src)
228 dest->mask &= src->mask;
229 dest->add &= src->mask;
230 dest->add |= src->add;
232 for (i = 0; i < ACTION_STRING_COUNT; i++)
234 char * str = src->string[i];
237 freez(dest->string[i]);
238 dest->string[i] = strdup(str);
242 for (i = 0; i < ACTION_MULTI_COUNT; i++)
244 if (src->multi_remove_all[i])
246 /* Remove everything from dest */
247 destroy_list(dest->multi_remove[i]);
248 destroy_list(dest->multi_add[i]);
249 dest->multi_remove_all[i] = 1;
250 list_duplicate(dest->multi_add[i], src->multi_add[i]);
252 else if (dest->multi_remove_all[i])
255 * dest already removes everything, so we only need to worry
258 list_remove_list(dest->multi_add[i], src->multi_remove[i]);
259 list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
263 /* No "remove all"s to worry about. */
264 list_remove_list(dest->multi_add[i], src->multi_remove[i]);
265 list_append_list_unique(dest->multi_remove[i], src->multi_remove[i]);
266 list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
272 /*********************************************************************
274 * Function : copy_action
276 * Description : Copy an action_specs.
277 * Similar to "cur_action = new_action".
280 * 1 : dest = Destination of copy.
281 * 2 : src = Source for copy.
285 *********************************************************************/
286 void copy_action (struct action_spec *dest,
287 const struct action_spec *src)
291 dest->mask = src->mask;
292 dest->add = src->add;
294 for (i = 0; i < ACTION_STRING_COUNT; i++)
296 char * str = src->string[i];
297 dest->string[i] = (str ? strdup(str) : NULL);
300 for (i = 0; i < ACTION_MULTI_COUNT; i++)
302 dest->multi_remove_all[i] = src->multi_remove_all[i];
303 list_duplicate(dest->multi_remove[i], src->multi_remove[i]);
304 list_duplicate(dest->multi_add[i], src->multi_add[i]);
309 /*********************************************************************
311 * Function : free_action
313 * Description : Free an action_specs.
316 * 1 : src = Source to free.
320 *********************************************************************/
321 void free_action (struct action_spec *src)
325 for (i = 0; i < ACTION_STRING_COUNT; i++)
327 freez(src->string[i]);
330 for (i = 0; i < ACTION_MULTI_COUNT; i++)
332 destroy_list(src->multi_remove[i]);
333 destroy_list(src->multi_add[i]);
336 memset(src, '\0', sizeof(*src));
340 /*********************************************************************
342 * Function : get_action_token
344 * Description : Parses a line for the first action.
345 * Modifies it's input array, doesn't allocate memory.
347 * *line=" +abc{def} -ghi "
354 * 1 : line = [in] The line containing the action.
355 * [out] Start of next action on line, or
356 * NULL if we reached the end of line before
357 * we found an action.
358 * 2 : name = [out] Start of action name, null
359 * terminated. NULL on EOL
360 * 3 : value = [out] Start of action value, null
361 * terminated. NULL if none or EOL.
364 * nonzero => Mismatched {} (line was trashed anyway)
366 *********************************************************************/
367 int get_action_token(char **line, char **name, char **value)
372 /* set default returns */
377 /* Eat any leading whitespace */
378 while ((*str == ' ') || (*str == '\t'))
390 /* null name, just value is prohibited */
397 while (((ch = *str) != '\0') &&
398 (ch != ' ') && (ch != '\t') && (ch != '{'))
414 /* EOL - be careful not to run off buffer */
419 /* More to parse next time. */
428 str = strchr(str, '}');
446 /*********************************************************************
448 * Function : get_actions
450 * Description : Parses a list of actions.
453 * 1 : line = The string containing the actions.
454 * Will be written to by this function.
455 * 2 : aliases = Custom alias list, or NULL for none.
456 * 3 : cur_action = Where to store the action. Caller
460 * nonzero => Error (line was trashed anyway)
462 *********************************************************************/
463 static int get_actions(char *line,
464 struct action_alias * alias_list,
465 struct action_spec *cur_action)
467 memset(cur_action, '\0', sizeof(*cur_action));
468 cur_action->mask = ACTION_MASK_ALL;
472 char * option = NULL;
475 if (get_action_token(&line, &option, &value))
482 /* handle option in 'option' */
484 /* Check for standard action name */
485 const struct action_name * action = action_names;
487 while ( (action->name != NULL) && (0 != strcmpic(action->name, option)) )
491 if (action->name != NULL)
494 cur_action->mask &= action->mask;
495 cur_action->add &= action->mask;
496 cur_action->add |= action->add;
498 switch (action->takes_value)
501 /* ignore any option. */
505 /* add single string. */
507 if ((value == NULL) || (*value == '\0'))
511 /* FIXME: should validate option string here */
512 freez (cur_action->string[action->index]);
513 cur_action->string[action->index] = strdup(value);
518 /* remove single string. */
520 freez (cur_action->string[action->index]);
525 /* append multi string. */
527 struct list * remove = cur_action->multi_remove[action->index];
528 struct list * add = cur_action->multi_add[action->index];
530 if ((value == NULL) || (*value == '\0'))
535 list_remove_item(remove, value);
536 enlist_unique(add, value, 0);
541 /* remove multi string. */
543 struct list * remove = cur_action->multi_remove[action->index];
544 struct list * add = cur_action->multi_add[action->index];
546 if ( (value == NULL) || (*value == '\0')
547 || ((*value == '*') && (value[1] == '\0')) )
550 * no option, or option == "*".
554 destroy_list(remove);
556 cur_action->multi_remove_all[action->index] = 1;
560 /* Valid option - remove only 1 option */
562 if ( !cur_action->multi_remove_all[action->index] )
564 /* there isn't a catch-all in the remove list already */
565 enlist_unique(remove, value, 0);
567 list_remove_item(add, value);
572 /* Shouldn't get here unless there's memory corruption. */
578 /* try user aliases. */
579 const struct action_alias * alias = alias_list;
581 while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) )
588 merge_actions(cur_action, alias->action);
592 /* Bad action name */
603 /*********************************************************************
605 * Function : actions_to_text
607 * Description : Converts a actionsfile entry from numeric form
608 * ("mask" and "add") to text.
611 * 1 : mask = As from struct url_actions
612 * 2 : add = As from struct url_actions
614 * Returns : A string. Caller must free it.
616 *********************************************************************/
617 char * actions_to_text(struct action_spec *action)
619 unsigned mask = action->mask;
620 unsigned add = action->add;
621 char * result = strdup("");
624 /* sanity - prevents "-feature +feature" */
628 #define DEFINE_ACTION_BOOL(__name, __bit) \
629 if (!(mask & __bit)) \
631 result = strsav(result, " -" __name); \
633 else if (add & __bit) \
635 result = strsav(result, " +" __name); \
638 #define DEFINE_ACTION_STRING(__name, __bit, __index) \
639 if (!(mask & __bit)) \
641 result = strsav(result, " -" __name); \
643 else if (add & __bit) \
645 result = strsav(result, " +" __name "{"); \
646 result = strsav(result, action->string[__index]); \
647 result = strsav(result, "}"); \
650 #define DEFINE_ACTION_MULTI(__name, __index) \
651 if (action->multi_remove_all[__index]) \
653 result = strsav(result, " -" __name "{*}"); \
657 lst = action->multi_remove[__index]->next; \
660 result = strsav(result, " -" __name "{"); \
661 result = strsav(result, lst->str); \
662 result = strsav(result, "}"); \
666 lst = action->multi_add[__index]->next; \
669 result = strsav(result, " +" __name "{"); \
670 result = strsav(result, lst->str); \
671 result = strsav(result, "}"); \
675 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
677 #include "actionlist.h"
679 #undef DEFINE_ACTION_MULTI
680 #undef DEFINE_ACTION_STRING
681 #undef DEFINE_ACTION_BOOL
682 #undef DEFINE_ACTION_ALIAS
688 /*********************************************************************
690 * Function : current_actions_to_text
692 * Description : Converts a actionsfile entry to text.
695 * 1 : action = Action
697 * Returns : A string. Caller must free it.
699 *********************************************************************/
700 char * current_action_to_text(struct current_action_spec *action)
702 unsigned flags = action->flags;
703 char * result = strdup("");
706 #define DEFINE_ACTION_BOOL(__name, __bit) \
709 result = strsav(result, " +" __name); \
713 result = strsav(result, " -" __name); \
716 #define DEFINE_ACTION_STRING(__name, __bit, __index) \
719 result = strsav(result, " +" __name "{"); \
720 result = strsav(result, action->string[__index]); \
721 result = strsav(result, "}"); \
725 result = strsav(result, " -" __name); \
728 #define DEFINE_ACTION_MULTI(__name, __index) \
729 lst = action->multi[__index]->next; \
732 result = strsav(result, " -" __name); \
738 result = strsav(result, " +" __name "{"); \
739 result = strsav(result, lst->str); \
740 result = strsav(result, "}"); \
745 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
747 #include "actionlist.h"
749 #undef DEFINE_ACTION_MULTI
750 #undef DEFINE_ACTION_STRING
751 #undef DEFINE_ACTION_BOOL
752 #undef DEFINE_ACTION_ALIAS
758 /*********************************************************************
760 * Function : init_current_action
762 * Description : Zero out an action.
765 * 1 : dest = An uninitialized current_action_spec.
769 *********************************************************************/
770 void init_current_action (struct current_action_spec *dest)
772 memset(dest, '\0', sizeof(*dest));
773 dest->flags = ACTION_MOST_COMPATIBLE;
777 /*********************************************************************
779 * Function : merge_current_action
781 * Description : Merge two actions together.
782 * Similar to "dest += src".
783 * Differences between this and merge_actions()
784 * is that this one doesn't allocate memory for
785 * strings (so "src" better be in memory for at least
786 * as long as "dest" is, and you'd better free
787 * "dest" using "current_free_action").
788 * Also, there is no mask or remove lists in dest.
789 * (If we're applying it to a URL, we don't need them)
792 * 1 : dest = Current actions, to modify.
793 * 2 : src = Action to add.
797 *********************************************************************/
798 void merge_current_action (struct current_action_spec *dest,
799 const struct action_spec *src)
803 dest->flags &= src->mask;
804 dest->flags |= src->add;
806 for (i = 0; i < ACTION_STRING_COUNT; i++)
808 char * str = src->string[i];
811 freez(dest->string[i]);
812 dest->string[i] = strdup(str);
816 for (i = 0; i < ACTION_MULTI_COUNT; i++)
818 if (src->multi_remove_all[i])
820 /* Remove everything from dest */
821 destroy_list(dest->multi[i]);
822 list_duplicate(dest->multi[i], src->multi_add[i]);
826 list_remove_list(dest->multi[i], src->multi_remove[i]);
827 list_append_list_unique(dest->multi[i], src->multi_add[i]);
833 /*********************************************************************
835 * Function : free_current_action
837 * Description : Free a current_action_spec.
840 * 1 : src = Source to free.
844 *********************************************************************/
845 void free_current_action (struct current_action_spec *src)
849 for (i = 0; i < ACTION_STRING_COUNT; i++)
851 freez(src->string[i]);
854 for (i = 0; i < ACTION_MULTI_COUNT; i++)
856 destroy_list(src->multi[i]);
859 memset(src, '\0', sizeof(*src));
863 /*********************************************************************
865 * Function : unload_actions_file
867 * Description : Unloads an actions module.
870 * 1 : file_data = the data structure associated with the
875 *********************************************************************/
876 void unload_actions_file(void *file_data)
878 struct url_actions * next;
879 struct url_actions * cur = (struct url_actions *)file_data;
891 /*********************************************************************
893 * Function : load_actions_file
895 * Description : Read and parse a action file and add to files
899 * 1 : csp = Current client state (buffers, headers, etc...)
901 * Returns : 0 => Ok, everything else is an error.
903 *********************************************************************/
904 int load_actions_file(struct client_state *csp)
906 static struct file_list *current_actions_file = NULL;
910 struct url_actions *last_perm;
911 struct url_actions *perm;
912 char buf[BUFFER_SIZE];
913 struct file_list *fs;
914 #define MODE_START_OF_FILE 1
915 #define MODE_ACTIONS 2
917 int mode = MODE_START_OF_FILE;
918 struct action_spec cur_action[1];
919 struct action_alias * alias_list = NULL;
921 memset(cur_action, '\0', sizeof(*cur_action));
923 if (!check_file_changed(current_actions_file, csp->config->actions_file, &fs))
925 /* No need to load */
928 csp->actions_list = current_actions_file;
934 log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error finding file: %E",
935 csp->config->actions_file);
936 return 1; /* never get here */
939 fs->f = last_perm = (struct url_actions *)zalloc(sizeof(*last_perm));
940 if (last_perm == NULL)
942 log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': out of memory!",
943 csp->config->actions_file);
944 return 1; /* never get here */
947 if ((fp = fopen(csp->config->actions_file, "r")) == NULL)
949 log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error opening file: %E",
950 csp->config->actions_file);
951 return 1; /* never get here */
954 while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
958 /* It's a header block */
961 /* It's {{settings}} or {{alias}} */
962 int len = strlen(buf);
963 char * start = buf + 2;
964 char * end = buf + len - 1;
965 if ((len < 5) || (*end-- != '}') || (*end-- != '}'))
969 log_error(LOG_LEVEL_FATAL,
970 "can't load actions file '%s': invalid line: %s",
971 csp->config->actions_file, buf);
972 return 1; /* never get here */
975 /* Trim leading and trailing whitespace. */
983 log_error(LOG_LEVEL_FATAL,
984 "can't load actions file '%s': invalid line: {{ }}",
985 csp->config->actions_file);
986 return 1; /* never get here */
989 if (0 == strcmpic(start, "alias"))
991 /* it's an {{alias}} block */
997 /* invalid {{something}} block */
999 log_error(LOG_LEVEL_FATAL,
1000 "can't load actions file '%s': invalid line: {{%s}}",
1001 csp->config->actions_file, start);
1002 return 1; /* never get here */
1007 /* It's an actions block */
1009 char actions_buf[BUFFER_SIZE];
1013 mode = MODE_ACTIONS;
1015 /* free old action */
1016 free_action(cur_action);
1019 strcpy(actions_buf, buf + 1);
1021 /* check we have a trailing } and then trim it */
1022 end = actions_buf + strlen(actions_buf) - 1;
1027 log_error(LOG_LEVEL_FATAL,
1028 "can't load actions file '%s': invalid line: %s",
1029 csp->config->actions_file, buf);
1030 return 1; /* never get here */
1034 /* trim any whitespace immediately inside {} */
1037 if (*actions_buf == '\0')
1041 log_error(LOG_LEVEL_FATAL,
1042 "can't load actions file '%s': invalid line: %s",
1043 csp->config->actions_file, buf);
1044 return 1; /* never get here */
1047 if (get_actions(actions_buf, alias_list, cur_action))
1051 log_error(LOG_LEVEL_FATAL,
1052 "can't load actions file '%s': invalid line: %s",
1053 csp->config->actions_file, buf);
1054 return 1; /* never get here */
1058 else if (mode == MODE_ALIAS)
1060 /* define an alias */
1061 char actions_buf[BUFFER_SIZE];
1062 struct action_alias * new_alias;
1065 char * start = strchr(buf, '=');
1068 if ((start == NULL) || (start == buf))
1070 log_error(LOG_LEVEL_FATAL,
1071 "can't load actions file '%s': invalid alias line: %s",
1072 csp->config->actions_file, buf);
1073 return 1; /* never get here */
1076 if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1079 log_error(LOG_LEVEL_FATAL,
1080 "can't load actions file '%s': out of memory!",
1081 csp->config->actions_file);
1082 return 1; /* never get here */
1085 /* Eat any the whitespace before the '=' */
1087 while ((*end == ' ') || (*end == '\t'))
1090 * we already know we must have at least 1 non-ws char
1091 * at start of buf - no need to check
1097 /* Eat any the whitespace after the '=' */
1099 while ((*start == ' ') || (*start == '\t'))
1105 log_error(LOG_LEVEL_FATAL,
1106 "can't load actions file '%s': invalid alias line: %s",
1107 csp->config->actions_file, buf);
1108 return 1; /* never get here */
1111 new_alias->name = strdup(buf);
1113 strcpy(actions_buf, start);
1115 if (get_actions(actions_buf, alias_list, new_alias->action))
1119 log_error(LOG_LEVEL_FATAL,
1120 "can't load actions file '%s': invalid alias line: %s = %s",
1121 csp->config->actions_file, buf, start);
1122 return 1; /* never get here */
1126 new_alias->next = alias_list;
1127 alias_list = new_alias;
1129 else if (mode == MODE_ACTIONS)
1131 /* it's a URL pattern */
1133 /* allocate a new node */
1134 if ((perm = zalloc(sizeof(*perm))) == NULL)
1137 log_error(LOG_LEVEL_FATAL,
1138 "can't load actions file '%s': out of memory!",
1139 csp->config->actions_file);
1140 return 1; /* never get here */
1144 copy_action (perm->action, cur_action);
1146 /* Save the URL pattern */
1147 if (create_url_spec(perm->url, buf))
1150 log_error(LOG_LEVEL_FATAL,
1151 "can't load actions file '%s': cannot create URL pattern from: %s",
1152 csp->config->actions_file, buf);
1153 return 1; /* never get here */
1156 /* add it to the list */
1157 last_perm->next = perm;
1160 else if (mode == MODE_START_OF_FILE)
1162 /* oops - please have a {} line as 1st line in file. */
1164 log_error(LOG_LEVEL_FATAL,
1165 "can't load actions file '%s': first line is invalid: %s",
1166 csp->config->actions_file, buf);
1167 return 1; /* never get here */
1171 /* How did we get here? This is impossible! */
1173 log_error(LOG_LEVEL_FATAL,
1174 "can't load actions file '%s': INTERNAL ERROR - mode = %d",
1175 csp->config->actions_file, mode);
1176 return 1; /* never get here */
1182 free_action(cur_action);
1184 while (alias_list != NULL)
1186 struct action_alias * next = alias_list->next;
1187 freez((char *)alias_list->name);
1188 free_action(alias_list->action);
1193 #ifndef SPLIT_PROXY_ARGS
1194 if (!suppress_blocklists)
1196 fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1198 #endif /* ndef SPLIT_PROXY_ARGS */
1200 /* the old one is now obsolete */
1201 if (current_actions_file)
1203 current_actions_file->unloader = unload_actions_file;
1206 fs->next = files->next;
1208 current_actions_file = fs;
1212 csp->actions_list = fs;