1 const char actions_rcs[] = "$Id: actions.c,v 1.5 2001/06/03 19:11:48 oes 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.5 2001/06/03 19:11:48 oes
37 * adapted to new enlist_unique arg format
39 * Revision 1.5 2001/06/03 11:03:48 oes
46 * adapted to new enlist_unique arg format
50 * introduced confdir option
52 * filters.c filtrers.h
54 * extracted-CGI relevant stuff
62 * support for new cgi mechansim
66 * functions for new list type: "map"
67 * extended enlist_unique
74 * deleted const struct interceptors
82 * added struct http_response
83 * changes struct interceptors to struct cgi_dispatcher
84 * moved HTML stuff to cgi.h
93 * Revision 1.4 2001/06/01 20:03:42 jongfoster
94 * Better memory management - current_action->strings[] now
95 * contains copies of the strings, not the original.
97 * Revision 1.3 2001/06/01 18:49:17 jongfoster
98 * Replaced "list_share" with "list" - the tiny memory gain was not
99 * worth the extra complexity.
101 * Revision 1.2 2001/05/31 21:40:00 jongfoster
102 * Removing some commented out, obsolete blocks of code.
104 * Revision 1.1 2001/05/31 21:16:46 jongfoster
105 * Moved functions to process the action list into this new file.
108 *********************************************************************/
120 #include "miscutil.h"
124 const char actions_h_rcs[] = ACTIONS_H_VERSION;
127 /* Turn off everything except forwarding */
128 /* This structure is used to hold user-defined aliases */
132 struct action_spec action[1];
133 struct action_alias * next;
138 * Must declare this in this file for the above structure.
140 static int get_actions (char *line,
141 struct action_alias * alias_list,
142 struct action_spec *cur_action);
145 * We need the main list of options.
147 * First, we need a way to tell between boolean, string, and multi-string
148 * options. For string and multistring options, we also need to be
149 * able to tell the difference between a "+" and a "-". (For bools,
150 * the "+"/"-" information is encoded in "add" and "mask"). So we use
151 * an enumerated type (well, the preprocessor equivalent). Here are
154 #define AV_NONE 0 /* +opt -opt */
155 #define AV_ADD_STRING 1 /* +stropt{string} */
156 #define AV_REM_STRING 2 /* -stropt */
157 #define AV_ADD_MULTI 3 /* +multiopt{string} +multiopt{string2} */
158 #define AV_REM_MULTI 4 /* -multiopt{string} -multiopt{*} */
161 * We need a structure to hold the name, flag changes,
162 * type, and string index.
167 unsigned mask; /* a bit set to "0" = remove action */
168 unsigned add; /* a bit set to "1" = add action */
169 int takes_value; /* an AV_... constant */
170 int index; /* index into strings[] or multi[] */
174 * And with those building blocks in place, here's the array.
176 static const struct action_name action_names[] =
179 * Well actually there's no data here - it's in actionlist.h
180 * This keeps it together to make it easy to change.
182 * Here's the macros used to format it:
184 #define DEFINE_ACTION_MULTI(name,index) \
185 { "+" name, ACTION_MASK_ALL, 0, AV_ADD_MULTI, index }, \
186 { "-" name, ACTION_MASK_ALL, 0, AV_REM_MULTI, index },
187 #define DEFINE_ACTION_STRING(name,flag,index) \
188 { "+" name, ACTION_MASK_ALL, flag, AV_ADD_STRING, index }, \
189 { "-" name, ~flag, 0, AV_REM_STRING, index },
190 #define DEFINE_ACTION_BOOL(name,flag) \
191 { "+" name, ACTION_MASK_ALL, flag }, \
192 { "-" name, ~flag, 0 },
193 #define DEFINE_ACTION_ALIAS 1 /* Want aliases please */
195 #include "actionlist.h"
197 #undef DEFINE_ACTION_MULTI
198 #undef DEFINE_ACTION_STRING
199 #undef DEFINE_ACTION_BOOL
200 #undef DEFINE_ACTION_ALIAS
202 { NULL, 0, 0 } /* End marker */
206 /*********************************************************************
208 * Function : merge_actions
210 * Description : Merge two actions together.
211 * Similar to "cur_action += new_action".
214 * 1 : cur_action = Current actions, to modify.
215 * 2 : new_action = Action to add.
219 *********************************************************************/
220 void merge_actions (struct action_spec *dest,
221 const struct action_spec *src)
225 dest->mask &= src->mask;
226 dest->add &= src->mask;
227 dest->add |= src->add;
229 for (i = 0; i < ACTION_STRING_COUNT; i++)
231 char * str = src->string[i];
234 freez(dest->string[i]);
235 dest->string[i] = strdup(str);
239 for (i = 0; i < ACTION_MULTI_COUNT; i++)
241 if (src->multi_remove_all[i])
243 /* Remove everything from dest */
244 destroy_list(dest->multi_remove[i]);
245 destroy_list(dest->multi_add[i]);
246 dest->multi_remove_all[i] = 1;
247 list_duplicate(dest->multi_add[i], src->multi_add[i]);
249 else if (dest->multi_remove_all[i])
252 * dest already removes everything, so we only need to worry
255 list_remove_list(dest->multi_add[i], src->multi_remove[i]);
256 list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
260 /* No "remove all"s to worry about. */
261 list_remove_list(dest->multi_add[i], src->multi_remove[i]);
262 list_append_list_unique(dest->multi_remove[i], src->multi_remove[i]);
263 list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
269 /*********************************************************************
271 * Function : copy_action
273 * Description : Copy an action_specs.
274 * Similar to "cur_action = new_action".
277 * 1 : dest = Destination of copy.
278 * 2 : src = Source for copy.
282 *********************************************************************/
283 void copy_action (struct action_spec *dest,
284 const struct action_spec *src)
288 dest->mask = src->mask;
289 dest->add = src->add;
291 for (i = 0; i < ACTION_STRING_COUNT; i++)
293 char * str = src->string[i];
294 dest->string[i] = (str ? strdup(str) : NULL);
297 for (i = 0; i < ACTION_MULTI_COUNT; i++)
299 dest->multi_remove_all[i] = src->multi_remove_all[i];
300 list_duplicate(dest->multi_remove[i], src->multi_remove[i]);
301 list_duplicate(dest->multi_add[i], src->multi_add[i]);
306 /*********************************************************************
308 * Function : free_action
310 * Description : Free an action_specs.
313 * 1 : src = Source to free.
317 *********************************************************************/
318 void free_action (struct action_spec *src)
322 for (i = 0; i < ACTION_STRING_COUNT; i++)
324 freez(src->string[i]);
327 for (i = 0; i < ACTION_MULTI_COUNT; i++)
329 destroy_list(src->multi_remove[i]);
330 destroy_list(src->multi_add[i]);
333 memset(src, '\0', sizeof(*src));
337 /*********************************************************************
339 * Function : get_action_token
341 * Description : Parses a line for the first action.
342 * Modifies it's input array, doesn't allocate memory.
344 * *line=" +abc{def} -ghi "
351 * 1 : line = [in] The line containing the action.
352 * [out] Start of next action on line, or
353 * NULL if we reached the end of line before
354 * we found an action.
355 * 2 : name = [out] Start of action name, null
356 * terminated. NULL on EOL
357 * 3 : value = [out] Start of action value, null
358 * terminated. NULL if none or EOL.
361 * nonzero => Mismatched {} (line was trashed anyway)
363 *********************************************************************/
364 int get_action_token(char **line, char **name, char **value)
369 /* set default returns */
374 /* Eat any leading whitespace */
375 while ((*str == ' ') || (*str == '\t'))
387 /* null name, just value is prohibited */
394 while (((ch = *str) != '\0') &&
395 (ch != ' ') && (ch != '\t') && (ch != '{'))
411 /* EOL - be careful not to run off buffer */
416 /* More to parse next time. */
425 str = strchr(str, '}');
443 /*********************************************************************
445 * Function : get_actions
447 * Description : Parses a list of actions.
450 * 1 : line = The string containing the actions.
451 * Will be written to by this function.
452 * 2 : aliases = Custom alias list, or NULL for none.
453 * 3 : cur_action = Where to store the action. Caller
457 * nonzero => Error (line was trashed anyway)
459 *********************************************************************/
460 static int get_actions(char *line,
461 struct action_alias * alias_list,
462 struct action_spec *cur_action)
464 memset(cur_action, '\0', sizeof(*cur_action));
465 cur_action->mask = ACTION_MASK_ALL;
469 char * option = NULL;
472 if (get_action_token(&line, &option, &value))
479 /* handle option in 'option' */
481 /* Check for standard action name */
482 const struct action_name * action = action_names;
484 while ( (action->name != NULL) && (0 != strcmpic(action->name, option)) )
488 if (action->name != NULL)
491 cur_action->mask &= action->mask;
492 cur_action->add &= action->mask;
493 cur_action->add |= action->add;
495 switch (action->takes_value)
498 /* ignore any option. */
502 /* add single string. */
504 if ((value == NULL) || (*value == '\0'))
508 /* FIXME: should validate option string here */
509 freez (cur_action->string[action->index]);
510 cur_action->string[action->index] = strdup(value);
515 /* remove single string. */
517 freez (cur_action->string[action->index]);
522 /* append multi string. */
524 struct list * remove = cur_action->multi_remove[action->index];
525 struct list * add = cur_action->multi_add[action->index];
527 if ((value == NULL) || (*value == '\0'))
532 list_remove_item(remove, value);
533 enlist_unique(add, value, 0);
538 /* remove multi string. */
540 struct list * remove = cur_action->multi_remove[action->index];
541 struct list * add = cur_action->multi_add[action->index];
543 if ( (value == NULL) || (*value == '\0')
544 || ((*value == '*') && (value[1] == '\0')) )
547 * no option, or option == "*".
551 destroy_list(remove);
553 cur_action->multi_remove_all[action->index] = 1;
557 /* Valid option - remove only 1 option */
559 if ( !cur_action->multi_remove_all[action->index] )
561 /* there isn't a catch-all in the remove list already */
562 enlist_unique(remove, value, 0);
564 list_remove_item(add, value);
569 /* Shouldn't get here unless there's memory corruption. */
575 /* try user aliases. */
576 const struct action_alias * alias = alias_list;
578 while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) )
585 merge_actions(cur_action, alias->action);
589 /* Bad action name */
600 /*********************************************************************
602 * Function : actions_to_text
604 * Description : Converts a actionsfile entry from numeric form
605 * ("mask" and "add") to text.
608 * 1 : mask = As from struct url_actions
609 * 2 : add = As from struct url_actions
611 * Returns : A string. Caller must free it.
613 *********************************************************************/
614 char * actions_to_text(struct action_spec *action)
616 unsigned mask = action->mask;
617 unsigned add = action->add;
618 char * result = strdup("");
621 /* sanity - prevents "-feature +feature" */
625 #define DEFINE_ACTION_BOOL(__name, __bit) \
626 if (!(mask & __bit)) \
628 result = strsav(result, " -" __name); \
630 else if (add & __bit) \
632 result = strsav(result, " +" __name); \
635 #define DEFINE_ACTION_STRING(__name, __bit, __index) \
636 if (!(mask & __bit)) \
638 result = strsav(result, " -" __name); \
640 else if (add & __bit) \
642 result = strsav(result, " +" __name "{"); \
643 result = strsav(result, action->string[__index]); \
644 result = strsav(result, "}"); \
647 #define DEFINE_ACTION_MULTI(__name, __index) \
648 if (action->multi_remove_all[__index]) \
650 result = strsav(result, " -" __name "{*}"); \
654 lst = action->multi_remove[__index]->next; \
657 result = strsav(result, " -" __name "{"); \
658 result = strsav(result, lst->str); \
659 result = strsav(result, "}"); \
663 lst = action->multi_add[__index]->next; \
666 result = strsav(result, " +" __name "{"); \
667 result = strsav(result, lst->str); \
668 result = strsav(result, "}"); \
672 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
674 #include "actionlist.h"
676 #undef DEFINE_ACTION_MULTI
677 #undef DEFINE_ACTION_STRING
678 #undef DEFINE_ACTION_BOOL
679 #undef DEFINE_ACTION_ALIAS
685 /*********************************************************************
687 * Function : current_actions_to_text
689 * Description : Converts a actionsfile entry to text.
692 * 1 : action = Action
694 * Returns : A string. Caller must free it.
696 *********************************************************************/
697 char * current_action_to_text(struct current_action_spec *action)
699 unsigned flags = action->flags;
700 char * result = strdup("");
703 #define DEFINE_ACTION_BOOL(__name, __bit) \
706 result = strsav(result, " +" __name); \
710 result = strsav(result, " -" __name); \
713 #define DEFINE_ACTION_STRING(__name, __bit, __index) \
716 result = strsav(result, " +" __name "{"); \
717 result = strsav(result, action->string[__index]); \
718 result = strsav(result, "}"); \
722 result = strsav(result, " -" __name); \
725 #define DEFINE_ACTION_MULTI(__name, __index) \
726 lst = action->multi[__index]->next; \
729 result = strsav(result, " -" __name); \
735 result = strsav(result, " +" __name "{"); \
736 result = strsav(result, lst->str); \
737 result = strsav(result, "}"); \
742 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
744 #include "actionlist.h"
746 #undef DEFINE_ACTION_MULTI
747 #undef DEFINE_ACTION_STRING
748 #undef DEFINE_ACTION_BOOL
749 #undef DEFINE_ACTION_ALIAS
755 /*********************************************************************
757 * Function : init_current_action
759 * Description : Zero out an action.
762 * 1 : dest = An uninitialized current_action_spec.
766 *********************************************************************/
767 void init_current_action (struct current_action_spec *dest)
769 memset(dest, '\0', sizeof(*dest));
770 dest->flags = ACTION_MOST_COMPATIBLE;
774 /*********************************************************************
776 * Function : merge_current_action
778 * Description : Merge two actions together.
779 * Similar to "dest += src".
780 * Differences between this and merge_actions()
781 * is that this one doesn't allocate memory for
782 * strings (so "src" better be in memory for at least
783 * as long as "dest" is, and you'd better free
784 * "dest" using "current_free_action").
785 * Also, there is no mask or remove lists in dest.
786 * (If we're applying it to a URL, we don't need them)
789 * 1 : dest = Current actions, to modify.
790 * 2 : src = Action to add.
794 *********************************************************************/
795 void merge_current_action (struct current_action_spec *dest,
796 const struct action_spec *src)
800 dest->flags &= src->mask;
801 dest->flags |= src->add;
803 for (i = 0; i < ACTION_STRING_COUNT; i++)
805 char * str = src->string[i];
808 freez(dest->string[i]);
809 dest->string[i] = strdup(str);
813 for (i = 0; i < ACTION_MULTI_COUNT; i++)
815 if (src->multi_remove_all[i])
817 /* Remove everything from dest */
818 destroy_list(dest->multi[i]);
819 list_duplicate(dest->multi[i], src->multi_add[i]);
823 list_remove_list(dest->multi[i], src->multi_remove[i]);
824 list_append_list_unique(dest->multi[i], src->multi_add[i]);
830 /*********************************************************************
832 * Function : free_current_action
834 * Description : Free a current_action_spec.
837 * 1 : src = Source to free.
841 *********************************************************************/
842 void free_current_action (struct current_action_spec *src)
846 for (i = 0; i < ACTION_STRING_COUNT; i++)
848 freez(src->string[i]);
851 for (i = 0; i < ACTION_MULTI_COUNT; i++)
853 destroy_list(src->multi[i]);
856 memset(src, '\0', sizeof(*src));
860 /*********************************************************************
862 * Function : unload_actions_file
864 * Description : Unloads an actions module.
867 * 1 : file_data = the data structure associated with the
872 *********************************************************************/
873 void unload_actions_file(void *file_data)
875 struct url_actions * next;
876 struct url_actions * cur = (struct url_actions *)file_data;
888 /*********************************************************************
890 * Function : load_actions_file
892 * Description : Read and parse a action file and add to files
896 * 1 : csp = Current client state (buffers, headers, etc...)
898 * Returns : 0 => Ok, everything else is an error.
900 *********************************************************************/
901 int load_actions_file(struct client_state *csp)
903 static struct file_list *current_actions_file = NULL;
907 struct url_actions *last_perm;
908 struct url_actions *perm;
910 struct file_list *fs;
911 #define MODE_START_OF_FILE 1
912 #define MODE_ACTIONS 2
914 int mode = MODE_START_OF_FILE;
915 struct action_spec cur_action[1];
916 struct action_alias * alias_list = NULL;
918 memset(cur_action, '\0', sizeof(*cur_action));
920 if (!check_file_changed(current_actions_file, csp->config->actions_file, &fs))
922 /* No need to load */
925 csp->actions_list = current_actions_file;
931 log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error finding file: %E",
932 csp->config->actions_file);
933 return 1; /* never get here */
936 fs->f = last_perm = (struct url_actions *)zalloc(sizeof(*last_perm));
937 if (last_perm == NULL)
939 log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': out of memory!",
940 csp->config->actions_file);
941 return 1; /* never get here */
944 if ((fp = fopen(csp->config->actions_file, "r")) == NULL)
946 log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error opening file: %E",
947 csp->config->actions_file);
948 return 1; /* never get here */
951 while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
955 /* It's a header block */
958 /* It's {{settings}} or {{alias}} */
959 int len = strlen(buf);
960 char * start = buf + 2;
961 char * end = buf + len - 1;
962 if ((len < 5) || (*end-- != '}') || (*end-- != '}'))
966 log_error(LOG_LEVEL_FATAL,
967 "can't load actions file '%s': invalid line: %s",
968 csp->config->actions_file, buf);
969 return 1; /* never get here */
972 /* Trim leading and trailing whitespace. */
980 log_error(LOG_LEVEL_FATAL,
981 "can't load actions file '%s': invalid line: {{ }}",
982 csp->config->actions_file);
983 return 1; /* never get here */
986 if (0 == strcmpic(start, "alias"))
988 /* it's an {{alias}} block */
994 /* invalid {{something}} block */
996 log_error(LOG_LEVEL_FATAL,
997 "can't load actions file '%s': invalid line: {{%s}}",
998 csp->config->actions_file, start);
999 return 1; /* never get here */
1004 /* It's an actions block */
1006 char actions_buf[BUFSIZ];
1010 mode = MODE_ACTIONS;
1012 /* free old action */
1013 free_action(cur_action);
1016 strcpy(actions_buf, buf + 1);
1018 /* check we have a trailing } and then trim it */
1019 end = actions_buf + strlen(actions_buf) - 1;
1024 log_error(LOG_LEVEL_FATAL,
1025 "can't load actions file '%s': invalid line: %s",
1026 csp->config->actions_file, buf);
1027 return 1; /* never get here */
1031 /* trim any whitespace immediately inside {} */
1034 if (*actions_buf == '\0')
1038 log_error(LOG_LEVEL_FATAL,
1039 "can't load actions file '%s': invalid line: %s",
1040 csp->config->actions_file, buf);
1041 return 1; /* never get here */
1044 if (get_actions(actions_buf, alias_list, cur_action))
1048 log_error(LOG_LEVEL_FATAL,
1049 "can't load actions file '%s': invalid line: %s",
1050 csp->config->actions_file, buf);
1051 return 1; /* never get here */
1055 else if (mode == MODE_ALIAS)
1057 /* define an alias */
1058 char actions_buf[BUFSIZ];
1059 struct action_alias * new_alias;
1062 char * start = strchr(buf, '=');
1065 if ((start == NULL) || (start == buf))
1067 log_error(LOG_LEVEL_FATAL,
1068 "can't load actions file '%s': invalid alias line: %s",
1069 csp->config->actions_file, buf);
1070 return 1; /* never get here */
1073 if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1076 log_error(LOG_LEVEL_FATAL,
1077 "can't load actions file '%s': out of memory!",
1078 csp->config->actions_file);
1079 return 1; /* never get here */
1082 /* Eat any the whitespace before the '=' */
1084 while ((*end == ' ') || (*end == '\t'))
1087 * we already know we must have at least 1 non-ws char
1088 * at start of buf - no need to check
1094 /* Eat any the whitespace after the '=' */
1096 while ((*start == ' ') || (*start == '\t'))
1102 log_error(LOG_LEVEL_FATAL,
1103 "can't load actions file '%s': invalid alias line: %s",
1104 csp->config->actions_file, buf);
1105 return 1; /* never get here */
1108 new_alias->name = strdup(buf);
1110 strcpy(actions_buf, start);
1112 if (get_actions(actions_buf, alias_list, new_alias->action))
1116 log_error(LOG_LEVEL_FATAL,
1117 "can't load actions file '%s': invalid alias line: %s = %s",
1118 csp->config->actions_file, buf, start);
1119 return 1; /* never get here */
1123 new_alias->next = alias_list;
1124 alias_list = new_alias;
1126 else if (mode == MODE_ACTIONS)
1128 /* it's a URL pattern */
1130 /* allocate a new node */
1131 if ((perm = zalloc(sizeof(*perm))) == NULL)
1134 log_error(LOG_LEVEL_FATAL,
1135 "can't load actions file '%s': out of memory!",
1136 csp->config->actions_file);
1137 return 1; /* never get here */
1141 copy_action (perm->action, cur_action);
1143 /* Save the URL pattern */
1144 if (create_url_spec(perm->url, buf))
1147 log_error(LOG_LEVEL_FATAL,
1148 "can't load actions file '%s': cannot create URL pattern from: %s",
1149 csp->config->actions_file, buf);
1150 return 1; /* never get here */
1153 /* add it to the list */
1154 last_perm->next = perm;
1157 else if (mode == MODE_START_OF_FILE)
1159 /* oops - please have a {} line as 1st line in file. */
1161 log_error(LOG_LEVEL_FATAL,
1162 "can't load actions file '%s': first line is invalid: %s",
1163 csp->config->actions_file, buf);
1164 return 1; /* never get here */
1168 /* How did we get here? This is impossible! */
1170 log_error(LOG_LEVEL_FATAL,
1171 "can't load actions file '%s': INTERNAL ERROR - mode = %d",
1172 csp->config->actions_file, mode);
1173 return 1; /* never get here */
1179 free_action(cur_action);
1181 while (alias_list != NULL)
1183 struct action_alias * next = alias_list->next;
1184 freez((char *)alias_list->name);
1185 free_action(alias_list->action);
1190 #ifndef SPLIT_PROXY_ARGS
1191 if (!suppress_blocklists)
1193 fs->proxy_args = strsav(fs->proxy_args, "</pre>");
1195 #endif /* ndef SPLIT_PROXY_ARGS */
1197 /* the old one is now obsolete */
1198 if (current_actions_file)
1200 current_actions_file->unloader = unload_actions_file;
1203 fs->next = files->next;
1205 current_actions_file = fs;
1209 csp->actions_list = fs;