X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=cgiedit.c;h=4f73db2b943b73872c5425b408417be2fedd87bc;hp=bbc646e5bbcdd4d948153c910a49c5394f70210e;hb=960ae61d1bce3289d3a79290a3d0c583620c2414;hpb=63ccfc0cef6353f9a87af9ba5d49abfdae73779f diff --git a/cgiedit.c b/cgiedit.c index bbc646e5..4f73db2b 100644 --- a/cgiedit.c +++ b/cgiedit.c @@ -1,12 +1,9 @@ -const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.54 2007/05/14 10:33:51 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/cgiedit.c,v $ * * Purpose : CGI-based actionsfile editor. * - * Functions declared include: cgi_edit_* - * * NOTE: The CGIs in this file use parameter names * such as "f" and "s" which are really *BAD* choices. * However, I'm trying to save bytes in the @@ -15,8 +12,8 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.54 2007/05/14 10:33:51 fabiankeil * * Stick to the short names in this file for consistency. * - * Copyright : Written by and Copyright (C) 2001-2007 the SourceForge - * Privoxy team. http://www.privoxy.org/ + * Copyright : Written by and Copyright (C) 2001-2014 the + * Privoxy team. https://www.privoxy.org/ * * Based on the Internet Junkbuster originally written * by and Copyright (C) 1997 Anonymous Coders and @@ -40,316 +37,8 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.54 2007/05/14 10:33:51 fabiankeil * or write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Revisions : - * $Log: cgiedit.c,v $ - * Revision 1.54 2007/05/14 10:33:51 fabiankeil - * - Use strlcpy() and strlcat() instead of strcpy() and strcat(). - * - * Revision 1.53 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.52 2007/04/12 10:41:23 fabiankeil - * - Don't mistake VC++'s _snprintf() for a snprintf() replacement. - * - Move some cgi_edit_actions_for_url() variables into structs. - * - Remove bogus comment. - * - * Revision 1.51 2007/04/08 13:21:05 fabiankeil - * Reference action files in CGI URLs by id instead - * of using the first part of the file name. - * Fixes BR 1694250 and BR 1590556. - * - * Revision 1.50 2007/03/29 11:40:34 fabiankeil - * Divide @filter-params@ into @client-header-filter-params@ - * @content-filter-params@ and @server-header-filter-params@. - * - * Revision 1.49 2007/03/20 15:16:34 fabiankeil - * Use dedicated header filter actions instead of abusing "filter". - * Replace "filter-client-headers" and "filter-client-headers" - * with "server-header-filter" and "client-header-filter". - * - * Revision 1.48 2007/02/13 14:35:25 fabiankeil - * Replace hash escaping code to prevent - * crashes, memory and file corruption. - * - * Revision 1.47 2006/12/28 18:04:25 fabiankeil - * Fixed gcc43 conversion warnings. - * - * Revision 1.46 2006/12/27 18:44:52 fabiankeil - * Stop shadowing string.h's index(). - * - * Revision 1.45 2006/12/21 12:57:48 fabiankeil - * Add config option "split-large-forms" - * to work around the browser bug reported - * in BR #1570678. - * - * Revision 1.44 2006/12/09 13:49:16 fabiankeil - * Fix configure option --disable-toggle. - * Thanks to Peter Thoenen for reporting this. - * - * Revision 1.43 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.41.2.12 2006/01/30 15:16:25 david__schmidt - * Remove a little residual debugging info - * - * Revision 1.41.2.11 2006/01/29 23:10:56 david__schmidt - * Multiple filter file support - * - * Revision 1.41.2.10 2005/07/04 03:13:43 david__schmidt - * Undo some damaging memory leak patches - * - * Revision 1.41.2.9 2005/07/04 00:31:04 david__schmidt - * Removing a double free - * - * Revision 1.41.2.8 2005/05/07 21:50:54 david__schmidt - * A few memory leaks plugged (mostly on error paths) - * - * Revision 1.41.2.7 2004/02/17 13:30:23 oes - * Moved cgi_error_disabled() from cgiedit.c to - * cgi.c to re-enable build with --disable-editor. - * Fixes Bug #892744. Thanks to Matthew Fischer - * for spotting. - * - * Revision 1.41.2.6 2003/12/18 08:13:48 oes - * One line lost in last commit - * - * Revision 1.41.2.5 2003/12/17 16:33:47 oes - * - All edit functions that redirect back to the list page - * now use cgi_redirect - * - All redirects now contain useless parameter "foo", whose - * value are raw seconds since epoch, in order to force - * Opera and Konqueror to properly reload the list. Closes - * bug #859993 - * - * Revision 1.41.2.4 2003/03/11 11:53:59 oes - * Cosmetic: Renamed cryptic variable - * - * Revision 1.41.2.3 2002/11/12 15:01:41 oes - * Fix: Don't free uninitialized struct editable_file - * - * Revision 1.41.2.2 2002/08/05 20:02:59 oes - * Bugfix: "Insert new section at top" did not work properly if first non-comment line in file was of type FILE_LINE_ACTION - * - * Revision 1.41.2.1 2002/08/02 12:43:14 oes - * Fixed bug #588514: first_time now set on a per-string basis in actions_from_radio; javascriptify now called on copies - * - * Revision 1.41 2002/05/21 19:09:45 oes - * - Made Add/Edit/Remove URL Submit and Cancel - * buttons jump back to relevant section in eal - * - Bugfix: remove-url-form needs p export - * - * Revision 1.40 2002/05/19 11:34:35 jongfoster - * Handling read-only actions files better - report the actual - * error, not "Out of memory"! - * - * Bug report: - * http://sourceforge.net/tracker/index.php?func=detail - * &aid=557905&group_id=11118&atid=111118 - * - * Revision 1.39 2002/05/12 21:39:15 jongfoster - * - Adding Doxygen-style comments to structures and #defines. - * - Correcting function comments - * - * Revision 1.38 2002/05/03 23:00:38 jongfoster - * Support for templates for "standard actions" buttons. - * See bug #549871 - * - * Revision 1.37 2002/04/30 11:14:52 oes - * Made csp the first parameter in *action_to_html - * - * Revision 1.36 2002/04/26 21:53:30 jongfoster - * Fixing a memory leak. (Near, but not caused by, my earlier commit). - * - * Revision 1.35 2002/04/26 21:50:02 jongfoster - * Honouring default exports in edit-actions-for-url-filter template. - * - * Revision 1.34 2002/04/26 12:54:17 oes - * Adaptions to changes in actions.c - * - * Revision 1.33 2002/04/24 02:17:47 oes - * - Moved get_char_param, get_string_param and get_number_param to cgi.c - * - Comments - * - Activated Jon's code for editing multiple AFs - * - cgi_edit_list_actions now provides context-sensitive - * help, looks up all action sets from standard.action and - * makes buttons for them in the catchall section - * - cgi_edit_action_submit now honors a p parameter, looks up - * the corresponding action set, and sets the catchall pattern's - * actions accordingly. - * - * Revision 1.32 2002/04/19 16:55:31 jongfoster - * Fixing newline problems. If we do our own text file newline - * mangling, we don't want the library to do any, so we need to - * open the files in *binary* mode. - * - * Revision 1.31 2002/04/18 19:21:08 jongfoster - * Added code to detect "conventional" action files, that start - * with a set of actions for all URLs (the pattern "/"). - * These are special-cased in the "edit-actions-list" CGI, so - * that a special UI can be written for them. - * - * Revision 1.30 2002/04/10 13:38:35 oes - * load_template signature changed - * - * Revision 1.29 2002/04/08 16:59:08 oes - * Fixed comment - * - * Revision 1.28 2002/03/27 12:30:29 oes - * Deleted unsused variable - * - * Revision 1.27 2002/03/26 23:06:04 jongfoster - * Removing duplicate @ifs on the toggle page - * - * Revision 1.26 2002/03/26 22:59:17 jongfoster - * Fixing /toggle to display status consistently. - * - * Revision 1.25 2002/03/26 22:29:54 swa - * we have a new homepage! - * - * Revision 1.24 2002/03/24 15:23:33 jongfoster - * Name changes - * - * Revision 1.23 2002/03/24 13:32:41 swa - * name change related issues - * - * Revision 1.22 2002/03/24 13:25:43 swa - * name change related issues - * - * Revision 1.21 2002/03/22 18:02:48 jongfoster - * Fixing remote toggle - * - * Revision 1.20 2002/03/16 20:28:34 oes - * Added descriptions to the filters so users will know what they select in the cgi editor - * - * Revision 1.19 2002/03/16 18:38:14 jongfoster - * Stopping stupid or malicious users from breaking the actions - * file using the web-based editor. - * - * Revision 1.18 2002/03/16 14:57:44 jongfoster - * Full support for enabling/disabling modular filters. - * - * Revision 1.17 2002/03/16 14:26:42 jongfoster - * First version of modular filters support - READ ONLY! - * Fixing a double-free bug in the out-of-memory handling in map_radio(). - * - * Revision 1.16 2002/03/07 03:46:17 oes - * Fixed compiler warnings - * - * Revision 1.15 2002/03/06 22:54:35 jongfoster - * Automated function-comment nitpicking. - * - * Revision 1.14 2002/03/05 00:24:51 jongfoster - * Patch to always edit the current actions file. - * - * Revision 1.13 2002/03/04 02:07:59 david__schmidt - * Enable web editing of actions file on OS/2 (it had been broken all this time!) - * - * Revision 1.12 2002/03/03 09:18:03 joergs - * Made jumbjuster work on AmigaOS again. - * - * Revision 1.11 2002/01/23 01:03:31 jongfoster - * Fixing gcc [CygWin] compiler warnings - * - * Revision 1.10 2002/01/23 00:22:59 jongfoster - * Adding new function cgi_edit_actions_section_swap(), to reorder - * the actions file. - * - * Adding get_url_spec_param() to get a validated URL pattern. - * - * Moving edit_read_line() out of this file and into loaders.c. - * - * Adding missing html_encode() to many CGI functions. - * - * 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. - * - * Major optimizations in cgi_edit_actions_list() to reduce the size of - * the generated HTML (down 40% from 550k to 304k), with major side-effects - * throughout the editor and templates. In particular, the length of the - * URLs throughout the editor has been drastically reduced, by cutting - * paramater names down to 1 character and CGI names down to 3-4 - * characters, by removing all non-essential CGI paramaters even at the - * expense of having to re-read the actions file for the most trivial - * page, and by using relative rather than absolute URLs. This means - * that this (typical example): - * - * - * - * is now this: - * - * - * - * Revision 1.9 2002/01/17 20:56:22 jongfoster - * Replacing hard references to the URL of the config interface - * with #defines from project.h - * - * Revision 1.8 2001/11/30 23:35:51 jongfoster - * Renaming actionsfile to ijb.action - * - * Revision 1.7 2001/11/13 00:28:24 jongfoster - * - Renaming parameters from edit-actions-for-url so that they only - * contain legal JavaScript characters. If we wanted to write - * JavaScript that worked with Netscape 4, this is nessacery. - * (Note that at the moment the JavaScript doesn't actually work - * with Netscape 4, but now this is purely a template issue, not - * one affecting code). - * - Adding new CGIs for use by non-JavaScript browsers: - * edit-actions-url-form - * edit-actions-add-url-form - * edit-actions-remove-url-form - * - Fixing || bug. - * - * Revision 1.6 2001/10/29 03:48:09 david__schmidt - * OS/2 native needed a snprintf() routine. Added one to miscutil, brackedted - * by and __OS2__ ifdef. - * - * Revision 1.5 2001/10/25 03:40:48 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.4 2001/10/23 21:48:19 jongfoster - * Cleaning up error handling in CGI functions - they now send back - * a HTML error page and should never cause a FATAL error. (Fixes one - * potential source of "denial of service" attacks). - * - * CGI actions file editor that works and is actually useful. - * - * Ability to toggle JunkBuster remotely using a CGI call. - * - * You can turn off both the above features in the main configuration - * file, e.g. if you are running a multi-user proxy. - * - * Revision 1.3 2001/10/14 22:12:49 jongfoster - * New version of CGI-based actionsfile editor. - * Major changes, including: - * - Completely new file parser and file output routines - * - edit-actions CGI renamed edit-actions-for-url - * - All CGIs now need a filename parameter, except for... - * - New CGI edit-actions which doesn't need a filename, - * to allow you to start the editor up. - * - edit-actions-submit now works, and now automatically - * redirects you back to the main edit-actions-list handler. - * - * Revision 1.2 2001/09/16 17:05:14 jongfoster - * Removing unused #include showarg.h - * - * Revision 1.1 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. - * - * **********************************************************************/ - + #include "config.h" @@ -381,8 +70,6 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.54 2007/05/14 10:33:51 fabiankeil #endif /* def FEATURE_TOGGLE */ #include "urlmatch.h" -const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION; - #ifdef FEATURE_CGI_EDIT_ACTIONS @@ -393,10 +80,10 @@ struct file_line { /** Next entry in the linked list */ struct file_line * next; - + /** The raw data, to write out if this line is unmodified. */ char * raw; - + /** Comments and/or whitespace to put before this line if it's modified and then written out. */ char * prefix; @@ -405,7 +92,7 @@ struct file_line are performed on the data read from file before it's stored here, so it will be a single line of data. */ char * unprocessed; - + /** The type of data on this line. One of the FILE_LINE_xxx constants. */ int type; @@ -431,19 +118,6 @@ struct file_line } setting; - /* Add more data types here... e.g. - - - struct url_spec url[1]; - - struct - { - struct action_spec action[1]; - const char * name; - } alias; - - */ - } data; }; @@ -479,6 +153,11 @@ struct file_line /** This file_line is in a {{description}} block. */ #define FILE_LINE_DESCRIPTION_ENTRY 10 +/* + * Number of file modification time mismatches + * before the CGI editor gets turned off. + */ +#define ACCEPTABLE_TIMESTAMP_MISMATCHES 3 /** * A configuration file, in a format that can be edited and written back to @@ -517,6 +196,11 @@ struct filter_type_info For example "content-filter-params" */ const char *type; /**< Name of the filter type, for example "server-header-filter". */ + /* XXX: check if these two can be combined. */ + const char *disable_all_option; /**< Name of the catch-all radio option that has + to be checked or unchecked for this filter type. */ + const char *disable_all_param; /**< Name of the parameter that causes all filters of + this type to be disabled. */ const char *abbr_type; /**< Abbreviation of the filter type, usually the first or second character capitalized */ const char *anchor; /**< Anchor for the User Manual link, @@ -524,33 +208,58 @@ struct filter_type_info }; /* Accessed by index, keep the order in the way the FT_ macros are defined. */ -const static struct filter_type_info filter_type_info[] = +static const struct filter_type_info filter_type_info[] = { { ACTION_MULTI_FILTER, "content-filter-params", "filter", + "filter-all", "filter_all", "F", "FILTER" }, { ACTION_MULTI_CLIENT_HEADER_FILTER, "client-header-filter-params", "client-header-filter", + "client-header-filter-all", "client_header_filter_all", "C", "CLIENT-HEADER-FILTER" }, { ACTION_MULTI_SERVER_HEADER_FILTER, "server-header-filter-params", "server-header-filter", + "server-header-filter-all", "server_header_filter_all", "S", "SERVER-HEADER-FILTER" }, { ACTION_MULTI_CLIENT_HEADER_TAGGER, "client-header-tagger-params", "client-header-tagger", + "client-header-tagger-all", "client_header_tagger_all", "L", "CLIENT-HEADER-TAGGER" }, { ACTION_MULTI_SERVER_HEADER_TAGGER, "server-header-tagger-params", "server-header-tagger", + "server-header-tagger-all", "server_header_tagger_all", "E", "SERVER-HEADER-TAGGER" }, + { + ACTION_MULTI_SUPPRESS_TAG, + "suppress-tag-params", "suppress-tag", + "suppress-tag-all", "suppress_tag_all", + "U", "SUPPRESS-TAG" + }, + { + ACTION_MULTI_CLIENT_BODY_FILTER, + "client-body-filter-params", "client-body-filter", + "client-body-filter-all", "client_body_filter_all", + "P", "CLIENT-BODY-FILTER" + }, +#ifdef FEATURE_EXTERNAL_FILTERS + { + ACTION_MULTI_EXTERNAL_FILTER, + "external-content-filter-params", "external-filter", + "external-content-filter-all", "external_content_filter_all", + "E", "EXTERNAL-CONTENT-FILTER" + }, +#endif }; /* FIXME: Following non-static functions should be prototyped in .h or made static */ @@ -609,20 +318,19 @@ static jb_err actions_to_radio(struct map * exports, const struct action_spec *action); static jb_err actions_from_radio(const struct map * parameters, struct action_spec *action); +static jb_err action_render_string_filters_template(struct map * exports, + const struct action_spec *action, + const char* flter_template, + const struct filter_type_info *type); static jb_err map_copy_parameter_html(struct map *out, const struct map *in, const char *name); -#if 0 /* unused function */ -static jb_err map_copy_parameter_url(struct map *out, - const struct map *in, - const char *name); -#endif /* unused function */ - -static jb_err get_file_name_param(struct client_state *csp, - const struct map *parameters, - const char *param_name, + +static jb_err get_file_name_param(struct client_state *csp, + const struct map *parameters, + const char *param_name, const char **pfilename); /* Internal convenience functions */ @@ -651,7 +359,7 @@ static char *section_target(const unsigned sectionid) { char buf[30]; - snprintf(buf, 30, "#l%d", sectionid); + snprintf(buf, sizeof(buf), "#l%u", sectionid); return(strdup(buf)); } @@ -673,7 +381,7 @@ static char *stringify(const unsigned number) { char buf[6]; - snprintf(buf, sizeof(buf), "%i", number); + snprintf(buf, sizeof(buf), "%u", number); return strdup(buf); } @@ -726,56 +434,6 @@ static jb_err map_copy_parameter_html(struct map *out, } -#if 0 /* unused function */ -/********************************************************************* - * - * Function : map_copy_parameter_url - * - * Description : Copy a CGI parameter from one map to another, URL - * encoding it. - * - * Parameters : - * 1 : out = target map - * 2 : in = source map - * 3 : name = name of cgi parameter to copy - * - * Returns : JB_ERR_OK on success - * JB_ERR_MEMORY on out-of-memory - * JB_ERR_CGI_PARAMS if the parameter doesn't exist - * in the source map - * - *********************************************************************/ -static jb_err map_copy_parameter_url(struct map *out, - const struct map *in, - const char *name) -{ - const char * value; - jb_err err; - - assert(out); - assert(in); - assert(name); - - value = lookup(in, name); - err = map(out, name, 1, url_encode(value), 0); - - if (err) - { - /* Out of memory */ - return err; - } - else if (*value == '\0') - { - return JB_ERR_CGI_PARAMS; - } - else - { - return JB_ERR_OK; - } -} -#endif /* 0 - unused function */ - - /********************************************************************* * * Function : cgi_edit_actions_url_form @@ -846,7 +504,7 @@ jb_err cgi_edit_actions_url_form(struct client_state *csp, if ( (cur_line == NULL) || (line_number != patternid) - || (patternid < 1) + || (patternid < 1U) || (cur_line->type != FILE_LINE_URL)) { /* Invalid "patternid" parameter */ @@ -1000,13 +658,13 @@ jb_err cgi_edit_actions_remove_url_form(struct client_state *csp, if (cur_line->type == FILE_LINE_ACTION) { section_start_line_number = line_number; - } + } cur_line = cur_line->next; } if ( (cur_line == NULL) || (line_number != patternid) - || (patternid < 1) + || (patternid < 1U) || (cur_line->type != FILE_LINE_URL)) { /* Invalid "patternid" parameter */ @@ -1115,12 +773,7 @@ jb_err edit_write_file(struct editable_file * file) /* Allocate new memory for string */ len = strlen(cur_line->unprocessed) + (size_t)numhash; - if (NULL == (str = malloc(len + 1))) - { - /* Uh oh, just trashed file! */ - fclose(fp); - return JB_ERR_MEMORY; - } + str = malloc_or_die(len + 1); /* Copy string but quote hashes */ src = cur_line->unprocessed; @@ -1201,13 +854,9 @@ jb_err edit_write_file(struct editable_file * file) /* Correct file->version_str */ freez(file->version_str); - snprintf(version_buf, 22, "%u", file->version); - version_buf[21] = '\0'; - file->version_str = strdup(version_buf); - if (version_buf == NULL) - { - return JB_ERR_MEMORY; - } + snprintf(version_buf, sizeof(version_buf), "%u", file->version); + version_buf[sizeof(version_buf)-1] = '\0'; + file->version_str = strdup_or_die(version_buf); return JB_ERR_OK; } @@ -1328,7 +977,7 @@ static int match_actions_file_header_line(const char * line, const char * name) line += 2; /* Look for optional whitespace */ - while ( (*line == ' ') || (*line == '\t') ) + while ((*line == ' ') || (*line == '\t')) { line++; } @@ -1342,7 +991,7 @@ static int match_actions_file_header_line(const char * line, const char * name) line += len; /* Look for optional whitespace */ - while ( (*line == ' ') || (*line == '\t') ) + while ((*line == ' ') || (*line == '\t')) { line++; } @@ -1411,10 +1060,7 @@ static jb_err split_line_on_equals(const char * line, char ** pname, char ** pva } name_len = (size_t)(name_end - line) + 1; /* Length excluding \0 */ - if (NULL == (*pname = (char *) malloc(name_len + 1))) - { - return JB_ERR_MEMORY; - } + *pname = malloc_or_die(name_len + 1); strncpy(*pname, line, name_len); (*pname)[name_len] = '\0'; @@ -1470,7 +1116,7 @@ jb_err edit_parse_actions_file(struct editable_file * file) /* alias_list contains the aliases defined in this file. * It might be better to use the "file_line.data" fields - * in the relavent places instead. + * in the relevant places instead. */ cur_line = file->lines; @@ -1484,16 +1130,16 @@ jb_err edit_parse_actions_file(struct editable_file * file) /* Skip leading blanks. Should only happen if file is * empty (which is valid, but pointless). */ - while ( (cur_line != NULL) - && (cur_line->unprocessed[0] == '\0') ) + while ((cur_line != NULL) + && (cur_line->unprocessed[0] == '\0')) { /* Blank line */ cur_line->type = FILE_LINE_BLANK; cur_line = cur_line->next; } - if ( (cur_line != NULL) - && (cur_line->unprocessed[0] != '{') ) + if ((cur_line != NULL) + && (cur_line->unprocessed[0] != '{')) { /* File doesn't start with a header */ file->parse_error = cur_line; @@ -1501,8 +1147,8 @@ jb_err edit_parse_actions_file(struct editable_file * file) return JB_ERR_PARSE; } - if ( (cur_line != NULL) && (0 == - match_actions_file_header_line(cur_line->unprocessed, "settings") ) ) + if ((cur_line != NULL) && (0 == + match_actions_file_header_line(cur_line->unprocessed, "settings"))) { cur_line->type = FILE_LINE_SETTINGS_HEADER; @@ -1536,8 +1182,8 @@ jb_err edit_parse_actions_file(struct editable_file * file) } } - if ( (cur_line != NULL) && (0 == - match_actions_file_header_line(cur_line->unprocessed, "description") ) ) + if ((cur_line != NULL) && (0 == + match_actions_file_header_line(cur_line->unprocessed, "description"))) { cur_line->type = FILE_LINE_DESCRIPTION_HEADER; @@ -1556,8 +1202,8 @@ jb_err edit_parse_actions_file(struct editable_file * file) } } - if ( (cur_line != NULL) && (0 == - match_actions_file_header_line(cur_line->unprocessed, "alias") ) ) + if ((cur_line != NULL) && (0 == + match_actions_file_header_line(cur_line->unprocessed, "alias"))) { cur_line->type = FILE_LINE_ALIAS_HEADER; @@ -1574,6 +1220,7 @@ jb_err edit_parse_actions_file(struct editable_file * file) err = split_line_on_equals(cur_line->unprocessed, &name, &value); if (err == JB_ERR_MEMORY) { + free_alias_list(alias_list); return err; } else if (err != JB_ERR_OK) @@ -1581,17 +1228,11 @@ jb_err edit_parse_actions_file(struct editable_file * file) /* Line does not contain a name=value pair */ file->parse_error = cur_line; file->parse_error_text = "Expected a name=value pair on this {{alias}} line, but couldn't find one."; + free_alias_list(alias_list); return JB_ERR_PARSE; } - if ((new_alias = zalloc(sizeof(*new_alias))) == NULL) - { - /* Out of memory */ - free(name); - free(value); - free_alias_list(alias_list); - return JB_ERR_MEMORY; - } + new_alias = zalloc_or_die(sizeof(*new_alias)); err = get_actions(value, alias_list, new_alias->action); if (err) @@ -1661,14 +1302,14 @@ jb_err edit_parse_actions_file(struct editable_file * file) return JB_ERR_PARSE; } - while ( (*text == ' ') || (*text == '\t') ) + while ((*text == ' ') || (*text == '\t')) { text++; len--; } - while ( (len > 0) - && ( (text[len - 1] == ' ') - || (text[len - 1] == '\t') ) ) + while ((len > (size_t)0) + && ((text[len - 1] == ' ') + || (text[len - 1] == '\t'))) { len--; } @@ -1676,12 +1317,7 @@ jb_err edit_parse_actions_file(struct editable_file * file) cur_line->type = FILE_LINE_ACTION; /* Remove {} and make copy */ - if (NULL == (value = (char *) malloc(len + 1))) - { - /* Out of memory */ - free_alias_list(alias_list); - return JB_ERR_MEMORY; - } + value = malloc_or_die(len + 1); strncpy(value, text, len); value[len] = '\0'; @@ -1764,11 +1400,7 @@ jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline) *pfile = NULL; - cur_line = first_line = zalloc(sizeof(struct file_line)); - if (cur_line == NULL) - { - return JB_ERR_MEMORY; - } + cur_line = first_line = zalloc_or_die(sizeof(struct file_line)); cur_line->type = FILE_LINE_UNPROCESSED; @@ -1776,7 +1408,7 @@ jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline) if (rval) { /* Out of memory or empty file. */ - /* Note that empty file is not an error we propogate up */ + /* Note that empty file is not an error we propagate up */ free(cur_line); return ((rval == JB_ERR_FILE) ? JB_ERR_OK : rval); } @@ -1784,13 +1416,7 @@ jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline) do { prev_line = cur_line; - cur_line = prev_line->next = zalloc(sizeof(struct file_line)); - if (cur_line == NULL) - { - /* Out of memory */ - edit_free_file_lines(first_line); - return JB_ERR_MEMORY; - } + cur_line = prev_line->next = zalloc_or_die(sizeof(struct file_line)); cur_line->type = FILE_LINE_UNPROCESSED; @@ -1881,7 +1507,7 @@ jb_err edit_read_file(struct client_state *csp, * Probably an old-school URL like * http://config.privoxy.org/edit-actions-list?f=default */ - err = get_file_name_param(csp, parameters, "f", &filename); + get_file_name_param(csp, parameters, "f", &filename); } if (NULL == filename || stat(filename, statbuf) < 0) @@ -1920,12 +1546,7 @@ jb_err edit_read_file(struct client_state *csp, return err; } - file = (struct editable_file *) zalloc(sizeof(*file)); - if (err) - { - edit_free_file_lines(lines); - return err; - } + file = zalloc_or_die(sizeof(*file)); file->lines = lines; file->newline = newline; @@ -1935,14 +1556,9 @@ jb_err edit_read_file(struct client_state *csp, /* Correct file->version_str */ freez(file->version_str); - snprintf(version_buf, 22, "%u", file->version); - version_buf[21] = '\0'; - file->version_str = strdup(version_buf); - if (version_buf == NULL) - { - edit_free_file(file); - return JB_ERR_MEMORY; - } + snprintf(version_buf, sizeof(version_buf), "%u", file->version); + version_buf[sizeof(version_buf)-1] = '\0'; + file->version_str = strdup_or_die(version_buf); *pfile = file; return JB_ERR_OK; @@ -1994,6 +1610,7 @@ jb_err edit_read_actions_file(struct client_state *csp, { jb_err err; struct editable_file *file; + static int acceptable_failures = ACCEPTABLE_TIMESTAMP_MISMATCHES - 1; assert(csp); assert(parameters); @@ -2011,7 +1628,24 @@ jb_err edit_read_actions_file(struct client_state *csp, } else if (err == JB_ERR_MODIFIED) { + assert(require_version); err = cgi_error_modified(csp, rsp, lookup(parameters, "f")); + log_error(LOG_LEVEL_ERROR, + "Blocking CGI edit request due to modification time mismatch."); + if (acceptable_failures > 0) + { + log_error(LOG_LEVEL_INFO, + "The CGI editor will be turned off after another %d mismatche(s).", + acceptable_failures); + acceptable_failures--; + } + else + { + log_error(LOG_LEVEL_INFO, + "Timestamp mismatch limit reached, turning CGI editor off. " + "Reload the configuration file to re-enable it."); + csp->config->feature_flags &= ~RUNTIME_FEATURE_CGI_EDIT_ACTIONS; + } } if (err == JB_ERR_OK) { @@ -2115,7 +1749,7 @@ static jb_err get_file_name_param(struct client_state *csp, && ((ch < 'a') || (ch > 'z')) && ((ch < '0') || (ch > '9')) && (ch != '-') - && (ch != '_') ) + && (ch != '_')) { /* Probable hack attempt. */ return JB_ERR_CGI_PARAMS; @@ -2124,11 +1758,7 @@ static jb_err get_file_name_param(struct client_state *csp, /* Append extension */ name_size = len + strlen(suffix) + 1; - name = malloc(name_size); - if (name == NULL) - { - return JB_ERR_MEMORY; - } + name = malloc_or_die(name_size); strlcpy(name, param, name_size); strlcat(name, suffix, name_size); @@ -2189,7 +1819,7 @@ static jb_err get_url_spec_param(struct client_state *csp, const char *orig_param; char *param; char *s; - struct url_spec compiled[1]; + struct pattern_spec compiled[1]; jb_err err; assert(csp); @@ -2237,14 +1867,14 @@ static jb_err get_url_spec_param(struct client_state *csp, free(param); return JB_ERR_MEMORY; } - err = create_url_spec(compiled, s); + err = create_pattern_spec(compiled, s); free(s); if (err) { free(param); return (err == JB_ERR_MEMORY) ? JB_ERR_MEMORY : JB_ERR_CGI_PARAMS; } - free_url_spec(compiled); + free_pattern_spec(compiled); if (param[strlen(param) - 1] == '\\') { @@ -2273,14 +1903,14 @@ static jb_err get_url_spec_param(struct client_state *csp, free(param); return JB_ERR_MEMORY; } - err = create_url_spec(compiled, s); + err = create_pattern_spec(compiled, s); free(s); if (err) { free(param); return (err == JB_ERR_MEMORY) ? JB_ERR_MEMORY : JB_ERR_CGI_PARAMS; } - free_url_spec(compiled); + free_pattern_spec(compiled); } *pvalue = param; @@ -2328,11 +1958,7 @@ static jb_err map_radio(struct map * exports, assert(optionname); assert(values); - buf = malloc(buf_size); - if (buf == NULL) - { - return JB_ERR_MEMORY; - } + buf = malloc_or_die(buf_size); strlcpy(buf, optionname, buf_size); @@ -2569,6 +2195,7 @@ jb_err cgi_edit_actions(struct client_state *csp, struct http_response *rsp, const struct map *parameters) { + (void)parameters; if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) { @@ -2642,6 +2269,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp, if (err) { /* No filename specified, can't read file, or out of memory. */ + free_map(exports); return (err == JB_ERR_FILE ? JB_ERR_OK : err); } @@ -2705,7 +2333,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp, { if (!strncmp(b->url->spec, "standard.", 9) && *(b->url->spec + 9) != '\0') { - if (err || (NULL == (section_exports = new_map()))) + if (err) { freez(buttons); free(section_template); @@ -2714,6 +2342,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp, return JB_ERR_MEMORY; } + section_exports = new_map(); err = map(section_exports, "button-name", 1, b->url->spec + 9, 1); if (err || (NULL == (s = strdup(section_template)))) @@ -2742,9 +2371,9 @@ jb_err cgi_edit_actions_list(struct client_state *csp, */ if (!err) err = map_conditional(exports, "all-urls-present", 1); - snprintf(buf, 150, "%d", line_number); + snprintf(buf, sizeof(buf), "%u", line_number); if (!err) err = map(exports, "all-urls-s", 1, buf, 1); - snprintf(buf, 150, "%d", line_number + 2); + snprintf(buf, sizeof(buf), "%u", line_number + 2); if (!err) err = map(exports, "all-urls-s-next", 1, buf, 1); if (!err) err = map(exports, "all-urls-actions", 1, actions_to_html(csp, cur_line->data.action), 0); @@ -2822,7 +2451,6 @@ jb_err cgi_edit_actions_list(struct client_state *csp, free(url_template); edit_free_file(file); free_map(exports); - free(url_template); return err; } @@ -2846,22 +2474,14 @@ jb_err cgi_edit_actions_list(struct client_state *csp, while ((cur_line != NULL) && (cur_line->type == FILE_LINE_ACTION)) { - if (NULL == (section_exports = new_map())) - { - free(sections); - free(section_template); - free(url_template); - edit_free_file(file); - free_map(exports); - return JB_ERR_MEMORY; - } + section_exports = new_map(); - snprintf(buf, 150, "%d", line_number); + snprintf(buf, sizeof(buf), "%u", line_number); err = map(section_exports, "s", 1, buf, 1); if (!err) err = map(section_exports, "actions", 1, actions_to_html(csp, cur_line->data.action), 0); - if ( (!err) + if ((!err) && (cur_line->next != NULL) && (cur_line->next->type == FILE_LINE_URL)) { @@ -2876,7 +2496,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp, if (prev_section_line_number != ((unsigned)(-1))) { /* Not last section */ - snprintf(buf, 150, "%d", prev_section_line_number); + snprintf(buf, sizeof(buf), "%u", prev_section_line_number); if (!err) err = map(section_exports, "s-prev", 1, buf, 1); if (!err) err = map_block_keep(section_exports, "s-prev-exists"); } @@ -2918,22 +2538,12 @@ jb_err cgi_edit_actions_list(struct client_state *csp, while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL)) { - if (NULL == (url_exports = new_map())) - { - free(urls); - free(sections); - free(section_template); - free(url_template); - edit_free_file(file); - free_map(exports); - free_map(section_exports); - return JB_ERR_MEMORY; - } + url_exports = new_map(); - snprintf(buf, 150, "%d", line_number); + snprintf(buf, sizeof(buf), "%u", line_number); err = map(url_exports, "p", 1, buf, 1); - snprintf(buf, 150, "%d", url_1_2); + snprintf(buf, sizeof(buf), "%d", url_1_2); if (!err) err = map(url_exports, "url-1-2", 1, buf, 1); if (!err) err = map(url_exports, "url-html", 1, @@ -2996,11 +2606,11 @@ jb_err cgi_edit_actions_list(struct client_state *csp, /* Could also do section-specific exports here, but it wouldn't be as fast */ - snprintf(buf, 150, "%d", line_number); + snprintf(buf, sizeof(buf), "%u", line_number); if (!err) err = map(section_exports, "s-next", 1, buf, 1); - if ( (cur_line != NULL) - && (cur_line->type == FILE_LINE_ACTION)) + if ((cur_line != NULL) + && (cur_line->type == FILE_LINE_ACTION)) { /* Not last section */ if (!err) err = map_block_keep(section_exports, "s-next-exists"); @@ -3091,6 +2701,7 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, const struct map *parameters) { struct map * exports; + char *filter_template; unsigned sectionid; struct editable_file * file; struct file_line * cur_line; @@ -3140,14 +2751,26 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, return JB_ERR_MEMORY; } + err = template_load(csp, &filter_template, "edit-actions-for-url-string-filter", 0); + if (err) + { + edit_free_file(file); + free_map(exports); + return cgi_error_no_template(csp, rsp, "edit-actions-for-url-string-filter"); + } + err = map(exports, "f", 1, stringify(file->identifier), 0); if (!err) err = map(exports, "v", 1, file->version_str, 1); if (!err) err = map(exports, "s", 1, url_encode(lookup(parameters, "s")), 0); if (!err) err = actions_to_radio(exports, cur_line->data.action); + if (!err) err = action_render_string_filters_template(exports, cur_line->data.action, filter_template, + &filter_type_info[FT_SUPPRESS_TAG]); + freez(filter_template); + /* - * XXX: Some browsers (at least IE6 and IE7) have an artifical URL + * XXX: Some browsers (at least IE6 and IE7) have an artificial URL * length limitation and ignore clicks on the Submit buttons if * the resulting GET URL would be longer than their limit. * @@ -3156,7 +2779,7 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, * browsers (BR #1570678). * * The config option split-large-forms works around this browser - * bug (HTTP has no URL lenght limitation) by deviding the action + * bug (HTTP has no URL length limitation) by dividing the action * list form into multiple smaller ones. It means the URLs are shorter * and work in broken browsers as well, but the user can no longer change * all actions with one submit. @@ -3164,7 +2787,7 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, * A better solution would be to switch to POST requests, * but this will do for now. */ - if(!err && (csp->config->feature_flags & RUNTIME_FEATURE_SPLIT_LARGE_FORMS)) + if (!err && (csp->config->feature_flags & RUNTIME_FEATURE_SPLIT_LARGE_FORMS)) { /* Generate multiple smaller form by killing the big one. */ err = map_block_killer(exports, "one-form-only"); @@ -3185,6 +2808,13 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, } } +#ifndef FEATURE_EXTERNAL_FILTERS + if (!err) err = map_block_killer(exports, "external-content-filters"); +#endif +#ifndef FEATURE_HTTPS_INSPECTION + if (!err) err = map_block_killer(exports, "https-inspection"); +#endif + if (err) { edit_free_file(file); @@ -3201,7 +2831,6 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, /* * List available filters and their settings. */ - char *filter_template; int filter_identifier = 0; char *prepared_templates[MAX_FILTER_TYPES]; @@ -3235,7 +2864,7 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, char number[20]; struct list_entry *filter_name; struct map *line_exports; - const int type = filter_group->type; + const enum filter_type type = filter_group->type; const int multi_action_index = filter_type_info[type].multi_action_index; assert(type < MAX_FILTER_TYPES); @@ -3292,7 +2921,7 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, if (filter_line == NULL) err = JB_ERR_MEMORY; } if (!err) err = template_fill(&filter_line, line_exports); - string_join(&prepared_templates[type], filter_line); + if (!err) err = string_join(&prepared_templates[type], filter_line); free_map(line_exports); } @@ -3318,8 +2947,14 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, } } - if (!err) err = map_radio(exports, "filter-all", "nx", - (cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] ? 'n' : 'x')); + /* Check or uncheck the "disable all of this type" radio buttons. */ + for (i = 0; i < MAX_FILTER_TYPES; i++) + { + const int a = filter_type_info[i].multi_action_index; + const int disable_all = cur_line->data.action->multi_remove_all[a]; + if (err) break; + err = map_radio(exports, filter_type_info[i].disable_all_option, "nx", (disable_all ? 'n' : 'x')); + } edit_free_file(file); @@ -3333,6 +2968,51 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, } +/********************************************************************* + * + * Function : get_number_of_filters + * + * Description : Counts the number of filter available. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : Number of filters available. + * + *********************************************************************/ +static int get_number_of_filters(const struct client_state *csp) +{ + int i; + struct re_filterfile_spec *b; + struct file_list *fl; + int number_of_filters = 0; + + for (i = 0; i < MAX_AF_FILES; i++) + { + fl = csp->rlist[i]; + if ((NULL == fl) || (NULL == fl->f)) + { + /* + * Either there are no filter files left or this + * filter file just contains no valid filters. + * + * Continue to be sure we don't miss valid filter + * files that are chained after empty or invalid ones. + */ + continue; + } + + for (b = fl->f; b != NULL; b = b->next) + { + number_of_filters++; + } + } + + return number_of_filters; + +} + + /********************************************************************* * * Function : cgi_edit_actions_submit @@ -3367,10 +3047,11 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, char target[1024]; jb_err err; int filter_identifier; + int i; const char * action_set_name; - char ch; struct file_list * fl; struct url_actions * b; + int number_of_filters; if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) { @@ -3418,7 +3099,7 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, { if (!strncmp(b->url->spec, "standard.", 9) && !strcmp(b->url->spec + 9, action_set_name)) { - copy_action(cur_line->data.action, b->action); + copy_action(cur_line->data.action, b->action); goto found; } } @@ -3434,26 +3115,34 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, err = actions_from_radio(parameters, cur_line->data.action); } - if(err) + if (err) { /* Out of memory */ edit_free_file(file); return err; } - ch = get_char_param(parameters, "filter_all"); - if (ch == 'N') - { - list_remove_all(cur_line->data.action->multi_add[ACTION_MULTI_FILTER]); - list_remove_all(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER]); - cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] = 1; - } - else if (ch == 'X') + /* Check the "disable all of this type" parameters. */ + for (i = 0; i < MAX_FILTER_TYPES; i++) { - cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] = 0; + const int multi_action_index = filter_type_info[i].multi_action_index; + const char ch = get_char_param(parameters, filter_type_info[i].disable_all_param); + + if (ch == 'N') + { + list_remove_all(cur_line->data.action->multi_add[multi_action_index]); + list_remove_all(cur_line->data.action->multi_remove[multi_action_index]); + cur_line->data.action->multi_remove_all[multi_action_index] = 1; + } + else if (ch == 'X') + { + cur_line->data.action->multi_remove_all[multi_action_index] = 0; + } } - for (filter_identifier = 0; !err; filter_identifier++) + number_of_filters = get_number_of_filters(csp); + + for (filter_identifier = 0; filter_identifier < number_of_filters && !err; filter_identifier++) { char key_value[30]; char key_name[30]; @@ -3482,8 +3171,8 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, if (name == NULL) { - /* End of list */ - break; + /* The filter identifier isn't present. Try the next one. */ + continue; } type = get_char_param(parameters, key_type); @@ -3504,6 +3193,9 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, case 'E': multi_action_index = ACTION_MULTI_SERVER_HEADER_TAGGER; break; + case 'P': + multi_action_index = ACTION_MULTI_CLIENT_BODY_FILTER; + break; default: log_error(LOG_LEVEL_ERROR, "Unknown filter type: %c for filter %s. Filter ignored.", type, name); @@ -3534,11 +3226,138 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, } } - if(err) + /* process existing suppress tag */ + for (filter_identifier = 0; !err; filter_identifier++) { - /* Out of memory */ - edit_free_file(file); - return err; + char key_value[30]; + char key_name[30]; + char old_name[30]; + char key_type[30]; + const char *name, *new_name; + char value; /* + * Filter state. Valid states are: 'Y' (active), + * 'N' (inactive) and 'X' (no change). + * XXX: bad name. + */ + char type; /* + * Abbreviated filter type. Valid types are: 'U' (suppress tag). + */ + int multi_action_index = 0; + + /* Generate the keys */ + snprintf(key_value, sizeof(key_value), "string_filter_r%x", filter_identifier); + snprintf(key_name, sizeof(key_name), "string_filter_n%x", filter_identifier); + snprintf(old_name, sizeof(old_name), "string_filter_o%x", filter_identifier); + snprintf(key_type, sizeof(key_type), "string_filter_t%x", filter_identifier); + + err = get_string_param(parameters, old_name, &name); + if (err) break; + + if (name == NULL) + { + /* The filter identifier isn't present: we're done! */ + break; + } + + err = get_string_param(parameters, key_name, &new_name); + if (err) break; + if (new_name == NULL) new_name = name; + + type = get_char_param(parameters, key_type); + switch (type) + { + case 'U': + multi_action_index = ACTION_MULTI_SUPPRESS_TAG; + break; + default: + log_error(LOG_LEVEL_ERROR, + "Unknown filter type: %c for filter %s. Filter ignored.", type, name); + continue; + } + assert(multi_action_index); + + value = get_char_param(parameters, key_value); + if (value == 'X' || value == 'Y' || value == 'N') + { + list_remove_item(cur_line->data.action->multi_add[multi_action_index], name); + list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name); + } + + if (value == 'Y') + { + err = enlist(cur_line->data.action->multi_add[multi_action_index], new_name); + } + else if (value == 'N') + { + err = enlist(cur_line->data.action->multi_remove[multi_action_index], new_name); + } + } + + /* process new string filters */ + for (filter_identifier = 0; !err; filter_identifier++) + { + char key_value[30]; + char key_name[30]; + char key_type[30]; + const char *name; + char value; /* + * Filter state. Valid states are: 'Y' (active), + * 'N' (inactive) and 'X' (no change). + * XXX: bad name. + */ + char type; /* + * Abbreviated filter type. Valid types are: 'U' (suppress tag). + */ + int multi_action_index = 0; + + /* Generate the keys */ + snprintf(key_value, sizeof(key_value), "new_string_filter_r%x", filter_identifier); + snprintf(key_name, sizeof(key_name), "new_string_filter_n%x", filter_identifier); + snprintf(key_type, sizeof(key_type), "new_string_filter_t%x", filter_identifier); + + err = get_string_param(parameters, key_name, &name); + if (err) break; + + if (name == NULL) + { + /* The filter identifier isn't present: we've done! */ + break; + } + + type = get_char_param(parameters, key_type); + switch (type) + { + case 'U': + multi_action_index = ACTION_MULTI_SUPPRESS_TAG; + break; + default: + log_error(LOG_LEVEL_ERROR, + "Unknown filter type: %c for filter %s. Filter ignored.", type, name); + continue; + } + assert(multi_action_index); + + value = get_char_param(parameters, key_value); + if (value == 'Y') + { + list_remove_item(cur_line->data.action->multi_add[multi_action_index], name); + if (!err) err = enlist(cur_line->data.action->multi_add[multi_action_index], name); + list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name); + } + else if (value == 'N') + { + list_remove_item(cur_line->data.action->multi_add[multi_action_index], name); + list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name); + if (!err) err = enlist(cur_line->data.action->multi_remove[multi_action_index], name); + } + /* nothing to do if the value is 'X' */ + } + + if (err) + { + /* Out of memory */ + edit_free_file(file); + return err; } if (NULL == (actiontext = actions_to_text(cur_line->data.action))) @@ -3559,13 +3378,7 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, } newtext_size = len + 2; - if (NULL == (newtext = malloc(newtext_size))) - { - /* Out of memory */ - free(actiontext); - edit_free_file(file); - return JB_ERR_MEMORY; - } + newtext = malloc_or_die(newtext_size); strlcpy(newtext, actiontext, newtext_size); free(actiontext); newtext[0] = '{'; @@ -3589,8 +3402,8 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, return err; } - snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%i#l%d", - (long) time(NULL), file->identifier, sectionid); + snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u#l%u", + (unsigned long) time(NULL), file->identifier, sectionid); edit_free_file(file); @@ -3677,13 +3490,13 @@ jb_err cgi_edit_actions_url(struct client_state *csp, if (cur_line->type == FILE_LINE_ACTION) { section_start_line_number = line_number; - } + } cur_line = cur_line->next; line_number++; } - if ( (cur_line == NULL) - || (cur_line->type != FILE_LINE_URL)) + if ((cur_line == NULL) + || (cur_line->type != FILE_LINE_URL)) { /* Invalid "patternid" parameter */ free(new_pattern); @@ -3710,8 +3523,8 @@ jb_err cgi_edit_actions_url(struct client_state *csp, return err; } - snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%i#l%d", - (long) time(NULL), file->identifier, section_start_line_number); + snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u#l%u", + (unsigned long) time(NULL), file->identifier, section_start_line_number); edit_free_file(file); @@ -3794,8 +3607,8 @@ jb_err cgi_edit_actions_add_url(struct client_state *csp, line_number++; } - if ( (cur_line == NULL) - || (cur_line->type != FILE_LINE_ACTION)) + if ((cur_line == NULL) + || (cur_line->type != FILE_LINE_ACTION)) { /* Invalid "sectionid" parameter */ free(new_pattern); @@ -3806,13 +3619,7 @@ jb_err cgi_edit_actions_add_url(struct client_state *csp, /* At this point, the section header is in cur_line - add after this. */ /* Allocate the new line */ - new_line = (struct file_line *)zalloc(sizeof(*new_line)); - if (new_line == NULL) - { - free(new_pattern); - edit_free_file(file); - return JB_ERR_MEMORY; - } + new_line = zalloc_or_die(sizeof(*new_line)); /* Fill in the data members of the new line */ new_line->raw = NULL; @@ -3839,8 +3646,8 @@ jb_err cgi_edit_actions_add_url(struct client_state *csp, return err; } - snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%i#l%d", - (long) time(NULL), file->identifier, sectionid); + snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u#l%u", + (unsigned long) time(NULL), file->identifier, sectionid); edit_free_file(file); @@ -3950,8 +3757,8 @@ jb_err cgi_edit_actions_remove_url(struct client_state *csp, return err; } - snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%i#l%d", - (long) time(NULL), file->identifier, section_start_line_number); + snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u#l%u", + (unsigned long) time(NULL), file->identifier, section_start_line_number); edit_free_file(file); @@ -4024,16 +3831,16 @@ jb_err cgi_edit_actions_section_remove(struct client_state *csp, line_number++; } - if ( (cur_line == NULL) - || (cur_line->type != FILE_LINE_ACTION) ) + if ((cur_line == NULL) + || (cur_line->type != FILE_LINE_ACTION)) { /* Invalid "sectionid" parameter */ edit_free_file(file); return JB_ERR_CGI_PARAMS; } - if ( (cur_line->next != NULL) - && (cur_line->next->type == FILE_LINE_URL) ) + if ((cur_line->next != NULL) + && (cur_line->next->type == FILE_LINE_URL)) { /* Section not empty. */ edit_free_file(file); @@ -4072,8 +3879,8 @@ jb_err cgi_edit_actions_section_remove(struct client_state *csp, return err; } - snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%i", - (long) time(NULL), file->identifier); + snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u", + (unsigned long) time(NULL), file->identifier); edit_free_file(file); @@ -4147,8 +3954,8 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp, /* There's something in the file, find the line before the first * action. */ - while ( (cur_line->next != NULL) - && (cur_line->next->type != FILE_LINE_ACTION) ) + while ((cur_line->next != NULL) + && (cur_line->next->type != FILE_LINE_ACTION)) { cur_line = cur_line->next; line_number++; @@ -4169,8 +3976,8 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp, line_number++; } - if ( (cur_line == NULL) - || (cur_line->type != FILE_LINE_ACTION)) + if ((cur_line == NULL) + || (cur_line->type != FILE_LINE_ACTION)) { /* Invalid "sectionid" parameter */ edit_free_file(file); @@ -4178,8 +3985,8 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp, } /* Skip through the section to find the last line in it. */ - while ( (cur_line->next != NULL) - && (cur_line->next->type != FILE_LINE_ACTION) ) + while ((cur_line->next != NULL) + && (cur_line->next->type != FILE_LINE_ACTION)) { cur_line = cur_line->next; line_number++; @@ -4199,13 +4006,7 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp, } /* Allocate the new line */ - new_line = (struct file_line *)zalloc(sizeof(*new_line)); - if (new_line == NULL) - { - free(new_text); - edit_free_file(file); - return JB_ERR_MEMORY; - } + new_line = zalloc_or_die(sizeof(*new_line)); /* Fill in the data members of the new line */ new_line->raw = NULL; @@ -4241,8 +4042,8 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp, return err; } - snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%i", - (long) time(NULL), file->identifier); + snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u", + (unsigned long) time(NULL), file->identifier); edit_free_file(file); @@ -4337,8 +4138,8 @@ jb_err cgi_edit_actions_section_swap(struct client_state *csp, line_number++; } - if ( (cur_line == NULL) - || (cur_line->type != FILE_LINE_ACTION) ) + if ((cur_line == NULL) + || (cur_line->type != FILE_LINE_ACTION)) { /* Invalid "section1" parameter */ edit_free_file(file); @@ -4369,8 +4170,8 @@ jb_err cgi_edit_actions_section_swap(struct client_state *csp, line_number++; } - if ( (cur_line == NULL) - || (cur_line->type != FILE_LINE_ACTION) ) + if ((cur_line == NULL) + || (cur_line->type != FILE_LINE_ACTION)) { /* Invalid "section2" parameter */ edit_free_file(file); @@ -4430,83 +4231,14 @@ jb_err cgi_edit_actions_section_swap(struct client_state *csp, } } /* END if (section1 != section2) */ - snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%i", - (long) time(NULL), file->identifier); + snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u", + (unsigned long) time(NULL), file->identifier); edit_free_file(file); return cgi_redirect(rsp, target); } -#ifdef FEATURE_TOGGLE -/********************************************************************* - * - * Function : cgi_toggle - * - * Description : CGI function that adds a new empty section to - * an actions file. - * - * Parameters : - * 1 : csp = Current client state (buffers, headers, etc...) - * 2 : rsp = http_response data structure for output - * 3 : parameters = map of cgi parameters - * - * CGI Parameters : - * set : If present, how to change toggle setting: - * "enable", "disable", "toggle", or none (default). - * mini : If present, use mini reply template. - * - * Returns : JB_ERR_OK on success - * JB_ERR_MEMORY on out-of-memory - * - *********************************************************************/ -jb_err cgi_toggle(struct client_state *csp, - struct http_response *rsp, - const struct map *parameters) -{ - struct map *exports; - char mode; - const char *template_name; - - assert(csp); - assert(rsp); - assert(parameters); - - if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_TOGGLE)) - { - return cgi_error_disabled(csp, rsp); - } - - mode = get_char_param(parameters, "set"); - - if (mode == 'E') - { - /* Enable */ - global_toggle_state = 1; - } - else if (mode == 'D') - { - /* Disable */ - global_toggle_state = 0; - } - else if (mode == 'T') - { - /* Toggle */ - global_toggle_state = !global_toggle_state; - } - - if (NULL == (exports = default_exports(csp, "toggle"))) - { - return JB_ERR_MEMORY; - } - - template_name = (get_char_param(parameters, "mini") - ? "toggle-mini" - : "toggle"); - - return template_fill_for_cgi(csp, template_name, exports, rsp); -} -#endif /* def FEATURE_TOGGLE */ /********************************************************************* * @@ -4514,7 +4246,7 @@ jb_err cgi_toggle(struct client_state *csp, * * Description : Converts a string into a form JavaScript will like. * - * Netscape 4's JavaScript sucks - it doesn't use + * Netscape 4's JavaScript sucks - it doesn't use * "id" parameters, so you have to set the "name" * used to submit a form element to something JavaScript * will like. (Or access the elements by index in an @@ -4564,8 +4296,8 @@ static void javascriptify(char * identifier) static jb_err actions_to_radio(struct map * exports, const struct action_spec *action) { - unsigned mask = action->mask; - unsigned add = action->add; + unsigned long mask; + unsigned long add; int mapped_param; int checked; char current_mode; @@ -4674,6 +4406,85 @@ static jb_err actions_to_radio(struct map * exports, return JB_ERR_OK; } +/********************************************************************* + * + * Function : action_render_string_filters_template + * + * Description : Converts a actionsfile entry into HTML template for actions with string + * filters (currently SUPPRESS-TAG actions only) + * + * Parameters : + * 1 : exports = List of substitutions to add to. + * 2 : action = Action to read + * 3 : filter_template = template to fill + * 4 : type = filter type info for rendered values/macro name + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * + *********************************************************************/ +static jb_err action_render_string_filters_template(struct map * exports, + const struct action_spec *action, + const char* filter_template, + const struct filter_type_info *type) +{ + jb_err err = JB_ERR_OK; + int filter_identifier = 0; + int i; + char *prepared_template = strdup(""); + + struct action_multi { + char radio; + struct list_entry *list; + }; + + struct action_multi desc[] = { + { 'y', action->multi_add[type->multi_action_index][0].first }, + { 'n', action->multi_remove[type->multi_action_index][0].first } + }; + + for (i = 0; i < SZ(desc); ++i) + { + const char radio = desc[i].radio; + struct list_entry *entry = desc[i].list; + for (;(!err) && (entry != NULL); entry = entry->next) + { + char number[20]; + struct map *line_exports; + + /* Generate a unique serial number */ + snprintf(number, sizeof(number), "%x", filter_identifier++); + + line_exports = new_map(); + if (line_exports == NULL) + { + err = JB_ERR_MEMORY; + } + else + { + char *filter_line; + if (!err) err = map(line_exports, "index", 1, number, 1); + if (!err) err = map(line_exports, "name", 1, entry->str, 1); + if (!err) err = map_radio(line_exports, "this-filter", "ynx", radio); + if (!err) err = map(line_exports, "filter-type", 1, type->type, 1); + if (!err) err = map(line_exports, "abbr-filter-type", 1, type->abbr_type, 1); + if (!err) err = map(line_exports, "anchor", 1, type->anchor, 1); + if (!err) + { + filter_line = strdup(filter_template); + if (filter_line == NULL) err = JB_ERR_MEMORY; + } + if (!err) err = template_fill(&filter_line, line_exports); + if (!err) err = string_join(&prepared_template, filter_line); + + free_map(line_exports); + } + } + } + if (!err) map(exports, type->macro_name, 1, prepared_template, 1); + freez(prepared_template); + return err; +} /********************************************************************* * @@ -4821,9 +4632,80 @@ static jb_err actions_from_radio(const struct map * parameters, return err; } +#endif /* def FEATURE_CGI_EDIT_ACTIONS */ -#endif /* def FEATURE_CGI_EDIT_ACTIONS */ +#ifdef FEATURE_TOGGLE +/********************************************************************* + * + * Function : cgi_toggle + * + * Description : CGI function that adds a new empty section to + * an actions file. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : rsp = http_response data structure for output + * 3 : parameters = map of cgi parameters + * + * CGI Parameters : + * set : If present, how to change toggle setting: + * "enable", "disable", "toggle", or none (default). + * mini : If present, use mini reply template. + * + * Returns : JB_ERR_OK on success + * JB_ERR_MEMORY on out-of-memory + * + *********************************************************************/ +jb_err cgi_toggle(struct client_state *csp, + struct http_response *rsp, + const struct map *parameters) +{ + struct map *exports; + char mode; + const char *template_name; + + assert(csp); + assert(rsp); + assert(parameters); + + if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_TOGGLE)) + { + return cgi_error_disabled(csp, rsp); + } + + mode = get_char_param(parameters, "set"); + + if (mode == 'E') + { + /* Enable */ + global_toggle_state = 1; + } + else if (mode == 'D') + { + /* Disable */ + global_toggle_state = 0; + } + else if (mode == 'T') + { + /* Toggle */ + global_toggle_state = !global_toggle_state; + } + + log_error(LOG_LEVEL_INFO, "Now toggled %s.", global_toggle_state ? "ON" : "OFF"); + + if (NULL == (exports = default_exports(csp, "toggle"))) + { + return JB_ERR_MEMORY; + } + + template_name = (get_char_param(parameters, "mini") + ? "toggle-mini" + : "toggle"); + + return template_fill_for_cgi(csp, template_name, exports, rsp); +} +#endif /* def FEATURE_TOGGLE */ /*