X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=actions.c;h=6a30577caeb4355c387bfaeb922fb1cfeea24e71;hp=b4904c92300bc594f316182fdce625f93900bf69;hb=873efe14859c0fb3f53a905eb346c36cf5fe7eda;hpb=c4f97e9c6a51667f8b68c4aae1816fa4faf7ee8b diff --git a/actions.c b/actions.c index b4904c92..6a30577c 100644 --- a/actions.c +++ b/actions.c @@ -1,13 +1,11 @@ -const char actions_rcs[] = "$Id: actions.c,v 1.44 2008/03/04 18:30:34 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/actions.c,v $ * * Purpose : Declares functions to work with actions files - * Functions declared include: FIXME * - * Copyright : Written by and Copyright (C) 2001-2008 the SourceForge - * Privoxy team. http://www.privoxy.org/ + * Copyright : Written by and Copyright (C) 2001-2016 the + * Privoxy team. https://www.privoxy.org/ * * Based on the Internet Junkbuster originally written * by and Copyright (C) 1997 Anonymous Coders and @@ -31,217 +29,8 @@ const char actions_rcs[] = "$Id: actions.c,v 1.44 2008/03/04 18:30:34 fabiankeil * or write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Revisions : - * $Log: actions.c,v $ - * Revision 1.44 2008/03/04 18:30:34 fabiankeil - * Remove the treat-forbidden-connects-like-blocks action. We now - * use the "blocked" page for forbidden CONNECT requests by default. - * - * Revision 1.43 2008/03/01 14:00:43 fabiankeil - * Let the block action take the reason for the block - * as argument and show it on the "blocked" page. - * - * Revision 1.42 2008/02/09 15:15:38 fabiankeil - * List active and inactive actions in the show-url-info's - * "Final results" section separately. Patch submitted by Lee - * in #1830056, modified to list active actions first. - * - * Revision 1.41 2008/01/28 20:17:40 fabiankeil - * - Mark some parameters as immutable. - * - Hide update_action_bits_for_all_tags() while it's unused. - * - * Revision 1.40 2007/05/21 10:26:50 fabiankeil - * - Use strlcpy() instead of strcpy(). - * - Provide a reason why loading the actions - * file might have failed. - * - * Revision 1.39 2007/04/17 18:21:45 fabiankeil - * Split update_action_bits() into - * update_action_bits_for_all_tags() - * and update_action_bits_for_tag(). - * - * Revision 1.38 2007/04/15 16:39:20 fabiankeil - * Introduce tags as alternative way to specify which - * actions apply to a request. At the moment tags can be - * created based on client and server headers. - * - * Revision 1.37 2007/03/11 15:56:12 fabiankeil - * Add kludge to log unknown aliases and actions before exiting. - * - * Revision 1.36 2006/12/28 17:15:42 fabiankeil - * Fix gcc43 conversion warning. - * - * Revision 1.35 2006/07/18 14:48:45 david__schmidt - * Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch) - * with what was really the latest development (the v_3_0_branch branch) - * - * Revision 1.32.2.6 2006/01/29 23:10:56 david__schmidt - * Multiple filter file support - * - * Revision 1.32.2.5 2005/06/09 01:18:41 david__schmidt - * Tweaks to conditionally include pthread.h if FEATURE_PTHREAD is enabled - - * this becomes important when jcc.h gets included later down the line. - * - * Revision 1.32.2.4 2003/12/03 10:33:11 oes - * - Implemented Privoxy version requirement through - * for-privoxy-version= statement in {{settings}} - * block - * - Fix for unchecked out-of-memory condition - * - * Revision 1.32.2.3 2003/02/28 12:52:10 oes - * Fixed memory leak reported by Dan Price in Bug #694713 - * - * Revision 1.32.2.2 2002/11/20 14:36:55 oes - * Extended unload_current_actions_file() to multiple AFs. - * Thanks to Oliver Stoeneberg for the hint. - * - * Revision 1.32.2.1 2002/05/26 12:13:16 roro - * Change unsigned to unsigned long in actions_name struct. This closes - * SourceForge Bug #539284. - * - * Revision 1.32 2002/05/12 21:36:29 jongfoster - * Correcting function comments - * - * Revision 1.31 2002/05/06 07:56:50 oes - * Made actions_to_html independent of FEATURE_CGI_EDIT_ACTIONS - * - * Revision 1.30 2002/04/30 11:14:52 oes - * Made csp the first parameter in *action_to_html - * - * Revision 1.29 2002/04/26 19:30:54 jongfoster - * - current_action_to_html(): Adding help link for the "-" form of - * one-string actions. - * - Some actions had "
-", some "
-" (note the space). - * Standardizing on no space. - * - Greatly simplifying some of the code by using string_join() - * where appropriate. - * - * Revision 1.28 2002/04/26 12:53:15 oes - * - CGI AF editor now writes action lines split into - * single lines with line continuation - * - actions_to_html now embeds each action name in - * link to chapter - * - current_action_to_text is now called current_action_to_html - * and acts like actions_to_html - * - * Revision 1.27 2002/04/24 02:10:31 oes - * - Jon's patch for multiple AFs: - * - split load_actions_file, add load_one_actions_file - * - make csp->actions_list files an array - * - remember file id with each action - * - Copy_action now frees dest action before copying - * - * Revision 1.26 2002/03/26 22:29:54 swa - * we have a new homepage! - * - * Revision 1.25 2002/03/24 13:25:43 swa - * name change related issues - * - * Revision 1.24 2002/03/16 23:54:06 jongfoster - * Adding graceful termination feature, to help look for memory leaks. - * If you enable this (which, by design, has to be done by hand - * editing config.h) and then go to http://i.j.b/die, then the program - * will exit cleanly after the *next* request. It should free all the - * memory that was used. - * - * Revision 1.23 2002/03/07 03:46:16 oes - * Fixed compiler warnings - * - * Revision 1.22 2002/01/21 00:27:02 jongfoster - * Allowing free_action(NULL). - * Moving the functions that #include actionlist.h to the end of the file, - * because the Visual C++ 97 debugger gets extremely confused if you try - * to debug any code that comes after them in the file. - * - * Revision 1.21 2002/01/17 20:54:44 jongfoster - * Renaming free_url to free_url_spec, since it frees a struct url_spec. - * - * Revision 1.20 2001/11/22 21:56:49 jongfoster - * Making action_spec->flags into an unsigned long rather than just an - * unsigned int. - * Fixing a bug in the display of -add-header and -wafer - * - * Revision 1.19 2001/11/13 00:14:07 jongfoster - * Fixing stupid bug now I've figured out what || means. - * (It always returns 0 or 1, not one of it's paramaters.) - * - * Revision 1.18 2001/11/07 00:06:06 steudten - * Add line number in error output for lineparsing for - * actionsfile. - * - * Revision 1.17 2001/10/25 03:40:47 david__schmidt - * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple - * threads to call select() simultaneously. So, it's time to do a real, live, - * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__ - * (native). Both versions will work, but using __OS2__ offers multi-threading. - * - * Revision 1.16 2001/10/23 21:30:30 jongfoster - * Adding error-checking to selected functions. - * - * Revision 1.15 2001/10/14 21:58:22 jongfoster - * Adding support for the CGI-based editor: - * - Exported get_actions() - * - Added new function free_alias_list() - * - Added support for {{settings}} and {{description}} blocks - * in the actions file. They are currently ignored. - * - Added restriction to only one {{alias}} block which must appear - * first in the file, to simplify the editor's rewriting rules. - * - Note that load_actions_file() is no longer used by the CGI-based - * editor, but some of the other routines in this file are. - * - * Revision 1.14 2001/09/22 16:36:59 jongfoster - * Removing unused parameter fs from read_config_line() - * - * Revision 1.13 2001/09/16 15:47:37 jongfoster - * First version of CGI-based edit interface. This is very much a - * work-in-progress, and you can't actually use it to edit anything - * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes - * to have any effect. - * - * Revision 1.12 2001/09/16 13:21:27 jongfoster - * Changes to use new list functions. - * - * Revision 1.11 2001/09/14 00:17:32 jongfoster - * Tidying up memory allocation. New function init_action(). - * - * Revision 1.10 2001/09/10 10:14:34 oes - * Removing unused variable - * - * Revision 1.9 2001/07/30 22:08:36 jongfoster - * Tidying up #defines: - * - All feature #defines are now of the form FEATURE_xxx - * - Permanently turned off WIN_GUI_EDIT - * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS - * - * Revision 1.8 2001/06/29 13:19:52 oes - * Removed logentry from cancelled commit - * - * Revision 1.7 2001/06/09 10:55:28 jongfoster - * Changing BUFSIZ ==> BUFFER_SIZE - * - * Revision 1.6 2001/06/07 23:04:34 jongfoster - * Made get_actions() static. - * - * Revision 1.5 2001/06/03 19:11:48 oes - * adapted to new enlist_unique arg format - * - * Revision 1.4 2001/06/01 20:03:42 jongfoster - * Better memory management - current_action->strings[] now - * contains copies of the strings, not the original. - * - * Revision 1.3 2001/06/01 18:49:17 jongfoster - * Replaced "list_share" with "list" - the tiny memory gain was not - * worth the extra complexity. - * - * Revision 1.2 2001/05/31 21:40:00 jongfoster - * Removing some commented out, obsolete blocks of code. - * - * Revision 1.1 2001/05/31 21:16:46 jongfoster - * Moved functions to process the action list into this new file. - * - * *********************************************************************/ - + #include "config.h" @@ -265,9 +54,7 @@ const char actions_rcs[] = "$Id: actions.c,v 1.44 2008/03/04 18:30:34 fabiankeil #include "urlmatch.h" #include "cgi.h" #include "ssplit.h" - -const char actions_h_rcs[] = ACTIONS_H_VERSION; - +#include "filters.h" /* * We need the main list of options. @@ -279,11 +66,13 @@ const char actions_h_rcs[] = ACTIONS_H_VERSION; * an enumerated type (well, the preprocessor equivalent). Here are * the values: */ -#define AV_NONE 0 /* +opt -opt */ -#define AV_ADD_STRING 1 /* +stropt{string} */ -#define AV_REM_STRING 2 /* -stropt */ -#define AV_ADD_MULTI 3 /* +multiopt{string} +multiopt{string2} */ -#define AV_REM_MULTI 4 /* -multiopt{string} -multiopt */ +enum action_value_type { + AV_NONE = 0, /* +opt -opt */ + AV_ADD_STRING = 1, /* +stropt{string} */ + AV_REM_STRING = 2, /* -stropt */ + AV_ADD_MULTI = 3, /* +multiopt{string} +multiopt{string2} */ + AV_REM_MULTI = 4 /* -multiopt{string} -multiopt */ +}; /* * We need a structure to hold the name, flag changes, @@ -292,10 +81,10 @@ const char actions_h_rcs[] = ACTIONS_H_VERSION; struct action_name { const char * name; - unsigned long mask; /* a bit set to "0" = remove action */ - unsigned long add; /* a bit set to "1" = add action */ - int takes_value; /* an AV_... constant */ - int index; /* index into strings[] or multi[] */ + unsigned long mask; /* a bit set to "0" = remove action */ + unsigned long add; /* a bit set to "1" = add action */ + enum action_value_type value_type; /* an AV_... constant */ + int index; /* index into strings[] or multi[] */ }; /* @@ -331,7 +120,10 @@ static const struct action_name action_names[] = }; -static int load_one_actions_file(struct client_state *csp, int fileid); +#ifndef FUZZ +static +#endif +int load_one_actions_file(struct client_state *csp, int fileid); /********************************************************************* @@ -364,11 +156,7 @@ jb_err merge_actions (struct action_spec *dest, if (str) { freez(dest->string[i]); - dest->string[i] = strdup(str); - if (NULL == dest->string[i]) - { - return JB_ERR_MEMORY; - } + dest->string[i] = strdup_or_die(str); } } @@ -420,7 +208,7 @@ jb_err merge_actions (struct action_spec *dest, * 1 : dest = Destination of copy. * 2 : src = Source for copy. * - * Returns : N/A + * Returns : JB_ERR_OK or JB_ERR_MEMORY * *********************************************************************/ jb_err copy_action (struct action_spec *dest, @@ -440,11 +228,7 @@ jb_err copy_action (struct action_spec *dest, char * str = src->string[i]; if (str) { - str = strdup(str); - if (!str) - { - return JB_ERR_MEMORY; - } + str = strdup_or_die(str); dest->string[i] = str; } } @@ -528,7 +312,7 @@ void free_action (struct action_spec *src) * Function : get_action_token * * Description : Parses a line for the first action. - * Modifies it's input array, doesn't allocate memory. + * Modifies its input array, doesn't allocate memory. * e.g. given: * *line=" +abc{def} -ghi " * Returns: @@ -611,7 +395,17 @@ jb_err get_action_token(char **line, char **name, char **value) str++; *value = str; - str = strchr(str, '}'); + /* The value ends with the first non-escaped closing curly brace */ + while ((str = strchr(str, '}')) != NULL) + { + if (str[-1] == '\\') + { + /* Overwrite the '\' so the action doesn't see it. */ + string_move(str-1, str); + continue; + } + break; + } if (str == NULL) { /* error */ @@ -630,7 +424,7 @@ jb_err get_action_token(char **line, char **name, char **value) /********************************************************************* * - * Function : action_used_to_valid_ + * Function : action_used_to_be_valid * * Description : Checks if unrecognized actions were valid in earlier * releases. @@ -643,7 +437,26 @@ jb_err get_action_token(char **line, char **name, char **value) *********************************************************************/ static int action_used_to_be_valid(const char *action) { - return (0 == strcmpic(action, "treat-forbidden-connects-like-blocks")); + static const char * const formerly_valid_actions[] = { + "inspect-jpegs", + "kill-popups", + "send-vanilla-wafer", + "send-wafer", + "treat-forbidden-connects-like-blocks", + "vanilla-wafer", + "wafer" + }; + unsigned int i; + + for (i = 0; i < SZ(formerly_valid_actions); i++) + { + if (0 == strcmpic(action, formerly_valid_actions[i])) + { + return TRUE; + } + } + + return FALSE; } /********************************************************************* @@ -690,7 +503,7 @@ jb_err get_actions(char *line, /* Check for standard action name */ const struct action_name * action = action_names; - while ( (action->name != NULL) && (0 != strcmpic(action->name, option)) ) + while ((action->name != NULL) && (0 != strcmpic(action->name, option))) { action++; } @@ -701,10 +514,16 @@ jb_err get_actions(char *line, cur_action->add &= action->mask; cur_action->add |= action->add; - switch (action->takes_value) + switch (action->value_type) { case AV_NONE: - /* ignore any option. */ + if (value != NULL) + { + log_error(LOG_LEVEL_ERROR, + "Action %s does not take parameters but %s was given.", + action->name, value); + return JB_ERR_PARSE; + } break; case AV_ADD_STRING: { @@ -712,26 +531,28 @@ jb_err get_actions(char *line, if ((value == NULL) || (*value == '\0')) { - if (0 != strcmpic(action->name, "block")) + if (0 == strcmpic(action->name, "+block")) { /* * XXX: Temporary backwards compatibility hack. + * XXX: should include line number. */ - static int complaint_shown = 0; value = "No reason specified."; - if (!complaint_shown) - { - log_error(LOG_LEVEL_ERROR, "At least one block " - "without reason found. This may become a fatal " - "error in future versions."); - complaint_shown = 1; - } + log_error(LOG_LEVEL_ERROR, + "block action without reason found. This may " + "become a fatal error in future versions."); } else { return JB_ERR_PARSE; } } +#ifdef FEATURE_EXTENDED_STATISTICS + if (0 == strcmpic(action->name, "+block")) + { + register_block_reason_for_statistics(value); + } +#endif /* FIXME: should validate option string here */ freez (cur_action->string[action->index]); cur_action->string[action->index] = strdup(value); @@ -775,8 +596,8 @@ jb_err get_actions(char *line, struct list * remove_p = cur_action->multi_remove[action->index]; struct list * add_p = cur_action->multi_add[action->index]; - if ( (value == NULL) || (*value == '\0') - || ((*value == '*') && (value[1] == '\0')) ) + if ((value == NULL) || (*value == '\0') + || ((*value == '*') && (value[1] == '\0'))) { /* * no option, or option == "*". @@ -791,7 +612,7 @@ jb_err get_actions(char *line, { /* Valid option - remove only 1 option */ - if ( !cur_action->multi_remove_all[action->index] ) + if (!cur_action->multi_remove_all[action->index]) { /* there isn't a catch-all in the remove list already */ err = enlist_unique(remove_p, value, 0); @@ -815,7 +636,7 @@ jb_err get_actions(char *line, /* try user aliases. */ const struct action_alias * alias = alias_list; - while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) ) + while ((alias != NULL) && (0 != strcmpic(alias->name, option))) { alias = alias->next; } @@ -824,11 +645,17 @@ jb_err get_actions(char *line, /* Found it */ merge_actions(cur_action, alias->action); } - else if ((2 < strlen(option)) && action_used_to_be_valid(option+1)) + else if (((size_t)2 < strlen(option)) && action_used_to_be_valid(option+1)) { log_error(LOG_LEVEL_ERROR, "Action '%s' is no longer valid " "in this Privoxy release. Ignored.", option+1); } + else if (((size_t)2 < strlen(option)) && 0 == strcmpic(option+1, "hide-forwarded-for-headers")) + { + log_error(LOG_LEVEL_FATAL, "The action 'hide-forwarded-for-headers' " + "is no longer valid in this Privoxy release. " + "Use 'change-x-forwarded-for' instead."); + } else { /* Bad action name */ @@ -926,11 +753,7 @@ jb_err merge_current_action (struct current_action_spec *dest, char * str = src->string[i]; if (str) { - str = strdup(str); - if (!str) - { - return JB_ERR_MEMORY; - } + str = strdup_or_die(str); freez(dest->string[i]); dest->string[i] = str; } @@ -960,36 +783,6 @@ jb_err merge_current_action (struct current_action_spec *dest, return err; } -#if 0 -/********************************************************************* - * - * Function : update_action_bits_for_all_tags - * - * Description : Updates the action bits based on all matching tags. - * - * Parameters : - * 1 : csp = Current client state (buffers, headers, etc...) - * - * Returns : 0 if no tag matched, or - * 1 otherwise - * - *********************************************************************/ -int update_action_bits_for_all_tags(struct client_state *csp) -{ - struct list_entry *tag; - int updated = 0; - - for (tag = csp->tags->first; tag != NULL; tag = tag->next) - { - if (update_action_bits_for_tag(csp, tag->str)) - { - updated = 1; - } - } - - return updated; -} -#endif /********************************************************************* * @@ -1029,20 +822,20 @@ int update_action_bits_for_tag(struct client_state *csp, const char *tag) /* and through all the action patterns, */ for (b = b->next; NULL != b; b = b->next) { - /* skip the URL patterns, */ - if (NULL == b->url->tag_regex) + /* skip everything but TAG patterns, */ + if (!(b->url->flags & PATTERN_SPEC_TAG_PATTERN)) { continue; } /* and check if one of the tag patterns matches the tag, */ - if (0 == regexec(b->url->tag_regex, tag, 0, NULL, 0)) + if (0 == regexec(b->url->pattern.tag_regex, tag, 0, NULL, 0)) { /* if it does, update the action bit map, */ if (merge_current_action(csp->action, b->action)) { log_error(LOG_LEVEL_ERROR, - "Out of memorey while changing action bits"); + "Out of memory while changing action bits"); } /* and signal the change. */ updated = 1; @@ -1054,6 +847,76 @@ int update_action_bits_for_tag(struct client_state *csp, const char *tag) } +/********************************************************************* + * + * Function : check_negative_tag_patterns + * + * Description : Updates the action bits based on NO-*-TAG patterns. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : flag = The tag pattern type + * + * Returns : JB_ERR_OK in case off success, or + * JB_ERR_MEMORY on out-of-memory error. + * + *********************************************************************/ +jb_err check_negative_tag_patterns(struct client_state *csp, unsigned int flag) +{ + struct list_entry *tag; + struct file_list *fl; + struct url_actions *b = NULL; + int i; + + for (i = 0; i < MAX_AF_FILES; i++) + { + fl = csp->actions_list[i]; + if ((fl == NULL) || ((b = fl->f) == NULL)) + { + continue; + } + for (b = b->next; NULL != b; b = b->next) + { + int tag_found = 0; + if (0 == (b->url->flags & flag)) + { + continue; + } + for (tag = csp->tags->first; NULL != tag; tag = tag->next) + { + if (0 == regexec(b->url->pattern.tag_regex, tag->str, 0, NULL, 0)) + { + /* + * The pattern matches at least one tag, thus the action + * section doesn't apply and we don't need to look at the + * other tags. + */ + tag_found = 1; + break; + } + } + if (!tag_found) + { + /* + * The pattern doesn't match any tags, + * thus the action section applies. + */ + if (merge_current_action(csp->action, b->action)) + { + log_error(LOG_LEVEL_ERROR, + "Out of memory while changing action bits"); + return JB_ERR_MEMORY; + } + log_error(LOG_LEVEL_HEADER, "Updated action bits based on: %s", + b->url->spec); + } + } + } + + return JB_ERR_OK; +} + + /********************************************************************* * * Function : free_current_action @@ -1067,7 +930,7 @@ int update_action_bits_for_tag(struct client_state *csp, const char *tag) * Returns : N/A * *********************************************************************/ -void free_current_action (struct current_action_spec *src) +void free_current_action(struct current_action_spec *src) { int i; @@ -1140,7 +1003,7 @@ void unload_actions_file(void *file_data) while (cur != NULL) { next = cur->next; - free_url_spec(cur->url); + free_pattern_spec(cur->url); if ((next == NULL) || (next->action != cur->action)) { /* @@ -1185,7 +1048,7 @@ void free_alias_list(struct action_alias *alias_list) /********************************************************************* * - * Function : load_actions_file + * Function : load_action_files * * Description : Read and parse all the action files and add to files * list. @@ -1196,7 +1059,7 @@ void free_alias_list(struct action_alias *alias_list) * Returns : 0 => Ok, everything else is an error. * *********************************************************************/ -int load_actions_file(struct client_state *csp) +int load_action_files(struct client_state *csp) { int i; int result; @@ -1221,6 +1084,128 @@ int load_actions_file(struct client_state *csp) return 0; } + +/********************************************************************* + * + * Function : filter_type_to_string + * + * Description : Converts a filter type enum into a string. + * + * Parameters : + * 1 : filter_type = filter_type as enum + * + * Returns : Pointer to static string. + * + *********************************************************************/ +static const char *filter_type_to_string(enum filter_type filter_type) +{ + switch (filter_type) + { + case FT_CONTENT_FILTER: + return "content filter"; + case FT_CLIENT_HEADER_FILTER: + return "client-header filter"; + case FT_SERVER_HEADER_FILTER: + return "server-header filter"; + case FT_CLIENT_HEADER_TAGGER: + return "client-header tagger"; + case FT_SERVER_HEADER_TAGGER: + return "server-header tagger"; +#ifdef FEATURE_EXTERNAL_FILTERS + case FT_EXTERNAL_CONTENT_FILTER: + return "external content filter"; +#endif + case FT_SUPPRESS_TAG: + return "suppress tag filter"; + case FT_CLIENT_BODY_FILTER: + return "client body filter"; + case FT_INVALID_FILTER: + return "invalid filter type"; + } + + return "unknown filter type"; + +} + +/********************************************************************* + * + * Function : referenced_filters_are_missing + * + * Description : Checks if any filters of a certain type referenced + * in an action spec are missing. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : cur_action = The action spec to check. + * 3 : multi_index = The index where to look for the filter. + * 4 : filter_type = The filter type the caller is interested in. + * + * Returns : 0 => All referenced filters exist, everything else is an error. + * + *********************************************************************/ +static int referenced_filters_are_missing(const struct client_state *csp, + const struct action_spec *cur_action, int multi_index, enum filter_type filter_type) +{ + struct list_entry *filtername; + + for (filtername = cur_action->multi_add[multi_index]->first; + filtername; filtername = filtername->next) + { + if (NULL == get_filter(csp, filtername->str, filter_type)) + { + log_error(LOG_LEVEL_ERROR, "Missing %s '%s'", + filter_type_to_string(filter_type), filtername->str); + return 1; + } + } + + return 0; + +} + + +/********************************************************************* + * + * Function : action_spec_is_valid + * + * Description : Should eventually figure out if an action spec + * is valid, but currently only checks that the + * referenced filters are accounted for. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : cur_action = The action spec to check. + * + * Returns : 0 => No problems detected, everything else is an error. + * + *********************************************************************/ +static int action_spec_is_valid(struct client_state *csp, const struct action_spec *cur_action) +{ + struct { + int multi_index; + enum filter_type filter_type; + } filter_map[] = { + {ACTION_MULTI_FILTER, FT_CONTENT_FILTER}, + {ACTION_MULTI_CLIENT_HEADER_FILTER, FT_CLIENT_HEADER_FILTER}, + {ACTION_MULTI_SERVER_HEADER_FILTER, FT_SERVER_HEADER_FILTER}, + {ACTION_MULTI_CLIENT_HEADER_TAGGER, FT_CLIENT_HEADER_TAGGER}, + {ACTION_MULTI_SERVER_HEADER_TAGGER, FT_SERVER_HEADER_TAGGER}, + {ACTION_MULTI_CLIENT_BODY_FILTER, FT_CLIENT_BODY_FILTER} + }; + int errors = 0; + int i; + + for (i = 0; i < SZ(filter_map); i++) + { + errors += referenced_filters_are_missing(csp, cur_action, + filter_map[i].multi_index, filter_map[i].filter_type); + } + + return errors; + +} + + /********************************************************************* * * Function : load_one_actions_file @@ -1235,7 +1220,10 @@ int load_actions_file(struct client_state *csp) * Returns : 0 => Ok, everything else is an error. * *********************************************************************/ -static int load_one_actions_file(struct client_state *csp, int fileid) +#ifndef FUZZ +static +#endif +int load_one_actions_file(struct client_state *csp, int fileid) { /* @@ -1243,23 +1231,24 @@ static int load_one_actions_file(struct client_state *csp, int fileid) * Note: Keep these in the order they occur in the file, they are * sometimes tested with <= */ -#define MODE_START_OF_FILE 1 -#define MODE_SETTINGS 2 -#define MODE_DESCRIPTION 3 -#define MODE_ALIAS 4 -#define MODE_ACTIONS 5 - - int mode = MODE_START_OF_FILE; + enum { + MODE_START_OF_FILE = 1, + MODE_SETTINGS = 2, + MODE_DESCRIPTION = 3, + MODE_ALIAS = 4, + MODE_ACTIONS = 5 + } mode; FILE *fp; struct url_actions *last_perm; struct url_actions *perm; - char buf[BUFFER_SIZE]; + char *buf; struct file_list *fs; struct action_spec * cur_action = NULL; int cur_action_used = 0; struct action_alias * alias_list = NULL; unsigned long linenum = 0; + mode = MODE_START_OF_FILE; if (!check_file_changed(current_actions_file[fileid], csp->config->actions_file[fileid], &fs)) { @@ -1275,13 +1264,7 @@ static int load_one_actions_file(struct client_state *csp, int fileid) return 1; /* never get here */ } - fs->f = last_perm = (struct url_actions *)zalloc(sizeof(*last_perm)); - if (last_perm == NULL) - { - log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': out of memory!", - csp->config->actions_file[fileid]); - return 1; /* never get here */ - } + fs->f = last_perm = zalloc_or_die(sizeof(*last_perm)); if ((fp = fopen(csp->config->actions_file[fileid], "r")) == NULL) { @@ -1290,7 +1273,9 @@ static int load_one_actions_file(struct client_state *csp, int fileid) return 1; /* never get here */ } - while (read_config_line(buf, sizeof(buf), fp, &linenum) != NULL) + log_error(LOG_LEVEL_INFO, "Loading actions file: %s", csp->config->actions_file[fileid]); + + while (read_config_line(fp, &linenum, &buf) != NULL) { if (*buf == '{') { @@ -1301,12 +1286,12 @@ static int load_one_actions_file(struct client_state *csp, int fileid) size_t len = strlen(buf); char * start = buf + 2; char * end = buf + len - 1; - if ((len < 5) || (*end-- != '}') || (*end-- != '}')) + if ((len < (size_t)5) || (*end-- != '}') || (*end-- != '}')) { /* too short */ fclose(fp); log_error(LOG_LEVEL_FATAL, - "can't load actions file '%s': invalid line (%lu): %s", + "can't load actions file '%s': invalid line (%lu): %s", csp->config->actions_file[fileid], linenum, buf); return 1; /* never get here */ } @@ -1407,11 +1392,11 @@ static int load_one_actions_file(struct client_state *csp, int fileid) { /* It's an actions block */ - char actions_buf[BUFFER_SIZE]; + char *actions_buf; char * end; /* set mode */ - mode = MODE_ACTIONS; + mode = MODE_ACTIONS; /* free old action */ if (cur_action) @@ -1423,28 +1408,31 @@ static int load_one_actions_file(struct client_state *csp, int fileid) cur_action = NULL; } cur_action_used = 0; - cur_action = (struct action_spec *)zalloc(sizeof(*cur_action)); - if (cur_action == NULL) - { - fclose(fp); - log_error(LOG_LEVEL_FATAL, - "can't load actions file '%s': out of memory", - csp->config->actions_file[fileid]); - return 1; /* never get here */ - } + cur_action = zalloc_or_die(sizeof(*cur_action)); init_action(cur_action); - /* trim { */ - strlcpy(actions_buf, buf + 1, sizeof(actions_buf)); + /* + * Copy the buffer before messing with it as we may need the + * unmodified version in for the fatal error messages. Given + * that this is not a common event, we could instead simply + * read the line again. + * + * buf + 1 to skip the leading '{' + */ + actions_buf = end = strdup_or_die(buf + 1); /* check we have a trailing } and then trim it */ - end = actions_buf + strlen(actions_buf) - 1; + if (strlen(actions_buf)) + { + end += strlen(actions_buf) - 1; + } if (*end != '}') { /* No closing } */ fclose(fp); - log_error(LOG_LEVEL_FATAL, - "can't load actions file '%s': invalid line (%lu): %s", + freez(actions_buf); + log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': " + "Missing trailing '}' in action section starting at line (%lu): %s", csp->config->actions_file[fileid], linenum, buf); return 1; /* never get here */ } @@ -1457,11 +1445,21 @@ static int load_one_actions_file(struct client_state *csp, int fileid) { /* error */ fclose(fp); - log_error(LOG_LEVEL_FATAL, - "can't load actions file '%s': invalid line (%lu): %s", + freez(actions_buf); + log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': " + "can't completely parse the action section starting at line (%lu): %s", csp->config->actions_file[fileid], linenum, buf); return 1; /* never get here */ } + + if (action_spec_is_valid(csp, cur_action)) + { + log_error(LOG_LEVEL_ERROR, "Invalid action section in file '%s', " + "starting at line %lu: %s", + csp->config->actions_file[fileid], linenum, buf); + } + + freez(actions_buf); } } else if (mode == MODE_SETTINGS) @@ -1477,16 +1475,9 @@ static int load_one_actions_file(struct client_state *csp, int fileid) char *version_string, *fields[3]; int num_fields; - if ((version_string = strdup(buf + 20)) == NULL) - { - fclose(fp); - log_error(LOG_LEVEL_FATAL, - "can't load actions file '%s': out of memory!", - csp->config->actions_file[fileid]); - return 1; /* never get here */ - } - - num_fields = ssplit(version_string, ".", fields, 3, TRUE, FALSE); + version_string = strdup_or_die(buf + 20); + + num_fields = ssplit(version_string, ".", fields, SZ(fields)); if (num_fields < 1 || atoi(fields[0]) == 0) { @@ -1494,14 +1485,14 @@ static int load_one_actions_file(struct client_state *csp, int fileid) "While loading actions file '%s': invalid line (%lu): %s", csp->config->actions_file[fileid], linenum, buf); } - else if ( atoi(fields[0]) > VERSION_MAJOR - || (num_fields > 1 && atoi(fields[1]) > VERSION_MINOR) - || (num_fields > 2 && atoi(fields[2]) > VERSION_POINT)) + else if ( (atoi(fields[0]) > VERSION_MAJOR) + || ((num_fields > 1) && (atoi(fields[1]) > VERSION_MINOR)) + || ((num_fields > 2) && (atoi(fields[2]) > VERSION_POINT))) { fclose(fp); log_error(LOG_LEVEL_FATAL, "Actions file '%s', line %lu requires newer Privoxy version: %s", - csp->config->actions_file[fileid], linenum, buf ); + csp->config->actions_file[fileid], linenum, buf); return 1; /* never get here */ } free(version_string); @@ -1533,14 +1524,7 @@ static int load_one_actions_file(struct client_state *csp, int fileid) return 1; /* never get here */ } - if ((new_alias = zalloc(sizeof(*new_alias))) == NULL) - { - fclose(fp); - log_error(LOG_LEVEL_FATAL, - "can't load actions file '%s': out of memory!", - csp->config->actions_file[fileid]); - return 1; /* never get here */ - } + new_alias = zalloc_or_die(sizeof(*new_alias)); /* Eat any the whitespace before the '=' */ end--; @@ -1568,14 +1552,7 @@ static int load_one_actions_file(struct client_state *csp, int fileid) return 1; /* never get here */ } - if ((new_alias->name = strdup(buf)) == NULL) - { - fclose(fp); - log_error(LOG_LEVEL_FATAL, - "can't load actions file '%s': out of memory!", - csp->config->actions_file[fileid]); - return 1; /* never get here */ - } + new_alias->name = strdup_or_die(buf); strlcpy(actions_buf, start, sizeof(actions_buf)); @@ -1595,27 +1572,20 @@ static int load_one_actions_file(struct client_state *csp, int fileid) } else if (mode == MODE_ACTIONS) { - /* it's a URL pattern */ + /* it's an URL pattern */ /* allocate a new node */ - if ((perm = zalloc(sizeof(*perm))) == NULL) - { - fclose(fp); - log_error(LOG_LEVEL_FATAL, - "can't load actions file '%s': out of memory!", - csp->config->actions_file[fileid]); - return 1; /* never get here */ - } + perm = zalloc_or_die(sizeof(*perm)); perm->action = cur_action; cur_action_used = 1; /* Save the URL pattern */ - if (create_url_spec(perm->url, buf)) + if (create_pattern_spec(perm->url, buf)) { fclose(fp); log_error(LOG_LEVEL_FATAL, - "can't load actions file '%s': line %lu: cannot create URL pattern from: %s", + "can't load actions file '%s': line %lu: cannot create URL or TAG pattern from: %s", csp->config->actions_file[fileid], linenum, buf); return 1; /* never get here */ } @@ -1629,7 +1599,7 @@ static int load_one_actions_file(struct client_state *csp, int fileid) /* oops - please have a {} line as 1st line in file. */ fclose(fp); log_error(LOG_LEVEL_FATAL, - "can't load actions file '%s': first needed line (%lu) is invalid: %s", + "can't load actions file '%s': line %lu should begin with a '{': %s", csp->config->actions_file[fileid], linenum, buf); return 1; /* never get here */ } @@ -1642,6 +1612,7 @@ static int load_one_actions_file(struct client_state *csp, int fileid) csp->config->actions_file[fileid], mode); return 1; /* never get here */ } + freez(buf); } fclose(fp); @@ -1675,7 +1646,7 @@ static int load_one_actions_file(struct client_state *csp, int fileid) * * Description : Converts a actionsfile entry from the internal * structure into a text line. The output is split - * into one line for each action with line continuation. + * into one line for each action with line continuation. * * Parameters : * 1 : action = The action to format. @@ -1686,9 +1657,9 @@ static int load_one_actions_file(struct client_state *csp, int fileid) *********************************************************************/ char * actions_to_text(const struct action_spec *action) { - unsigned mask = action->mask; - unsigned add = action->add; - char * result = strdup(""); + unsigned long mask = action->mask; + unsigned long add = action->add; + char *result = strdup_or_die(""); struct list_entry * lst; /* sanity - prevents "-feature +feature" */ @@ -1760,7 +1731,7 @@ char * actions_to_text(const struct action_spec *action) * Function : actions_to_html * * Description : Converts a actionsfile entry from numeric form - * ("mask" and "add") to a
-seperated HTML string + * ("mask" and "add") to a
-separated HTML string * in which each action is linked to its chapter in * the user manual. * @@ -1775,9 +1746,9 @@ char * actions_to_text(const struct action_spec *action) char * actions_to_html(const struct client_state *csp, const struct action_spec *action) { - unsigned mask = action->mask; - unsigned add = action->add; - char * result = strdup(""); + unsigned long mask = action->mask; + unsigned long add = action->add; + char *result = strdup_or_die(""); struct list_entry * lst; /* sanity - prevents "-feature +feature" */ @@ -1866,12 +1837,12 @@ char * actions_to_html(const struct client_state *csp, * * Function : current_actions_to_html * - * Description : Converts a curren action spec to a
seperated HTML + * Description : Converts a current action spec to a
separated HTML * text in which each action is linked to its chapter in * the user manual. * * Parameters : - * 1 : csp = Client state (for config) + * 1 : csp = Client state (for config) * 2 : action = Current action spec to be converted * * Returns : A string. Caller must free it. @@ -1883,9 +1854,9 @@ char *current_action_to_html(const struct client_state *csp, { unsigned long flags = action->flags; struct list_entry * lst; - char *result = strdup(""); - char *active = strdup(""); - char *inactive = strdup(""); + char *result = strdup_or_die(""); + char *active = strdup_or_die(""); + char *inactive = strdup_or_die(""); #define DEFINE_ACTION_BOOL(__name, __bit) \ if (flags & __bit) \ @@ -1956,3 +1927,69 @@ char *current_action_to_html(const struct client_state *csp, } return result; } + + +/********************************************************************* + * + * Function : action_to_line_of_text + * + * Description : Converts a action spec to a single text line + * listing the enabled actions. + * + * Parameters : + * 1 : action = Current action spec to be converted + * + * Returns : A string. Caller must free it. + * Out-of-memory errors are fatal. + * + *********************************************************************/ +char *actions_to_line_of_text(const struct current_action_spec *action) +{ + char buffer[200]; + struct list_entry *lst; + char *active; + const unsigned long flags = action->flags; + + active = strdup_or_die(""); + +#define DEFINE_ACTION_BOOL(__name, __bit) \ + if (flags & __bit) \ + { \ + snprintf(buffer, sizeof(buffer), "+%s ", __name); \ + string_append(&active, buffer); \ + } \ + +#define DEFINE_ACTION_STRING(__name, __bit, __index) \ + if (flags & __bit) \ + { \ + snprintf(buffer, sizeof(buffer), "+%s{%s} ", \ + __name, action->string[__index]); \ + string_append(&active, buffer); \ + } \ + +#define DEFINE_ACTION_MULTI(__name, __index) \ + lst = action->multi[__index]->first; \ + while (lst != NULL) \ + { \ + snprintf(buffer, sizeof(buffer), "+%s{%s} ", \ + __name, lst->str); \ + string_append(&active, buffer); \ + lst = lst->next; \ + } \ + +#define DEFINE_ACTION_ALIAS 0 /* No aliases for output */ + +#include "actionlist.h" + +#undef DEFINE_ACTION_MULTI +#undef DEFINE_ACTION_STRING +#undef DEFINE_ACTION_BOOL +#undef DEFINE_ACTION_ALIAS + + if (active == NULL) + { + log_error(LOG_LEVEL_FATAL, "Out of memory in action_to_line_of_text()"); + } + + return active; +}