X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=cgiedit.c;h=e4f1d68fd2546befaeff2b974411e2f5b4d28a0d;hp=4e4927d0bd228b15de63eb212172cf6cee7811b2;hb=7771ace7e31aca3163179f61afb58ceea217945a;hpb=e37a59aca86ce1f33b5979f7456084b11919b3b7 diff --git a/cgiedit.c b/cgiedit.c index 4e4927d0..e4f1d68f 100644 --- a/cgiedit.c +++ b/cgiedit.c @@ -1,4 +1,4 @@ -const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.14 2002/03/05 00:24:51 jongfoster Exp $"; +const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.47 2006/12/28 18:04:25 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/cgiedit.c,v $ @@ -15,8 +15,8 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.14 2002/03/05 00:24:51 jongfoster * * Stick to the short names in this file for consistency. * - * Copyright : Written by and Copyright (C) 2001 the SourceForge - * IJBSWA team. http://ijbswa.sourceforge.net + * Copyright : Written by and Copyright (C) 2001-2007 the SourceForge + * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written * by and Copyright (C) 1997 Anonymous Coders and @@ -42,6 +42,174 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.14 2002/03/05 00:24:51 jongfoster * * Revisions : * $Log: cgiedit.c,v $ + * 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. * @@ -164,7 +332,6 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.14 2002/03/05 00:24:51 jongfoster #include #include #include -#include #include #ifdef _WIN32 @@ -181,8 +348,10 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.14 2002/03/05 00:24:51 jongfoster #include "miscutil.h" #include "errlog.h" #include "loaders.h" +#ifdef FEATURE_TOGGLE +/* loadcfg.h is for global_toggle_state only */ #include "loadcfg.h" -/* loadcfg.h is for g_bToggleIJB only */ +#endif /* def FEATURE_TOGGLE */ #include "urlmatch.h" const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION; @@ -190,23 +359,49 @@ const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION; #ifdef FEATURE_CGI_EDIT_ACTIONS +/** + * A line in an editable_file. + */ 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; + + /** The actual data, as a string. Line continuation and comment removal + 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; + /** The actual data, processed into some sensible data type. */ union { + + /** An action specification. */ struct action_spec action[1]; + /** A name=value pair. */ struct { + + /** The name in the name=value pair. */ char * name; + + /** The value in the name=value pair, as a string. */ char * svalue; + + /** The value in the name=value pair, as an integer. */ int ivalue; + } setting; /* Add more data types here... e.g. @@ -223,42 +418,65 @@ struct file_line */ } data; + }; +/** This file_line has not been processed yet. */ #define FILE_LINE_UNPROCESSED 1 + +/** This file_line is blank. Can only appear at the end of a file, due to + the way the parser works. */ #define FILE_LINE_BLANK 2 + +/** This file_line says {{alias}}. */ #define FILE_LINE_ALIAS_HEADER 3 + +/** This file_line defines an alias. */ #define FILE_LINE_ALIAS_ENTRY 4 + +/** This file_line defines an {action}. */ #define FILE_LINE_ACTION 5 + +/** This file_line specifies a URL pattern. */ #define FILE_LINE_URL 6 + +/** This file_line says {{settings}}. */ #define FILE_LINE_SETTINGS_HEADER 7 + +/** This file_line is in a {{settings}} block. */ #define FILE_LINE_SETTINGS_ENTRY 8 + +/** This file_line says {{description}}. */ #define FILE_LINE_DESCRIPTION_HEADER 9 + +/** This file_line is in a {{description}} block. */ #define FILE_LINE_DESCRIPTION_ENTRY 10 +/** + * A configuration file, in a format that can be edited and written back to + * disk. + */ struct editable_file { - struct file_line * lines; - const char * filename; /* Full pathname - e.g. "/etc/junkbuster/wibble.action" */ - const char * identifier; /* Filename stub - e.g. "wibble". Use for CGI param. */ - /* Pre-encoded with url_encode() for ease of use. */ - const char * version_str; /* Last modification time, as a string. For CGI param */ - /* Can be used in URL without using url_param(). */ - unsigned version; /* Last modification time - prevents chaos with - * the browser's "back" button. Note that this is a - * time_t cast to an unsigned. When comparing, always - * cast the time_t to an unsigned, and *NOT* vice-versa. - * This may lose the top few bits, but they're not - * significant anyway. - */ - int newline; /* Newline convention - one of the NEWLINE_xxx constants. - * Note that changing this after the file has been - * read in will cause a mess. - */ - struct file_line * parse_error; /* On parse error, this is the offending line. */ - const char * parse_error_text; /* On parse error, this is the problem. - * (Statically allocated) */ + struct file_line * lines; /**< The contents of the file. A linked list of lines. */ + const char * filename; /**< Full pathname - e.g. "/etc/privoxy/wibble.action". */ + const char * identifier; /**< Filename stub - e.g. "wibble". Use for CGI param. */ + /**< Pre-encoded with url_encode() for ease of use. */ + const char * version_str; /**< Last modification time, as a string. For CGI param. */ + /**< Can be used in URL without using url_param(). */ + unsigned version; /**< Last modification time - prevents chaos with + the browser's "back" button. Note that this is a + time_t cast to an unsigned. When comparing, always + cast the time_t to an unsigned, and *NOT* vice-versa. + This may lose the top few bits, but they're not + significant anyway. */ + int newline; /**< Newline convention - one of the NEWLINE_xxx constants. + Note that changing this after the file has been + read in will cause a mess. */ + struct file_line * parse_error; /**< On parse error, this is the offending line. */ + const char * parse_error_text; /**< On parse error, this is the problem. + (Statically allocated) */ }; /* FIXME: Following non-static functions should be prototyped in .h or made static */ @@ -290,8 +508,9 @@ jb_err cgi_error_parse(struct client_state *csp, jb_err cgi_error_file(struct client_state *csp, struct http_response *rsp, const char *filename); -jb_err cgi_error_disabled(struct client_state *csp, - struct http_response *rsp); +jb_err cgi_error_file_read_only(struct client_state *csp, + struct http_response *rsp, + const char *filename); /* Internal arbitrary config file support functions */ static jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline); @@ -308,20 +527,18 @@ static jb_err get_file_name_param(struct client_state *csp, const char *suffix, char **pfilename, const char **pparam); -static jb_err get_number_param(struct client_state *csp, - const struct map *parameters, - char *name, - unsigned *pvalue); + static jb_err get_url_spec_param(struct client_state *csp, const struct map *parameters, const char *name, char **pvalue); + /* Internal actionsfile <==> HTML conversion functions */ static jb_err map_radio(struct map * exports, const char * optionname, const char * values, - char value); + int value); static jb_err actions_to_radio(struct map * exports, const struct action_spec *action); static jb_err actions_from_radio(const struct map * parameters, @@ -337,6 +554,34 @@ static jb_err map_copy_parameter_url(struct map *out, const char *name); #endif /* unused function */ +/* Internal convenience functions */ +static char *section_target(const unsigned sectionid); + +/********************************************************************* + * + * Function : section_target + * + * Description : Given an unsigned (section id) n, produce a dynamically + * allocated string of the form #l, for use in link + * targets. + * + * Parameters : + * 1 : sectionid = start line number of section + * + * Returns : String with link target, or NULL if out of + * memory + * + *********************************************************************/ +static char *section_target(const unsigned sectionid) +{ + char buf[30]; + + snprintf(buf, 30, "#l%d", sectionid); + return(strdup(buf)); + +} + + /********************************************************************* * * Function : map_copy_parameter_html @@ -388,7 +633,7 @@ static jb_err map_copy_parameter_html(struct map *out, #if 0 /* unused function */ /********************************************************************* * - * Function : map_copy_parameter_html + * Function : map_copy_parameter_url * * Description : Copy a CGI parameter from one map to another, URL * encoding it. @@ -466,6 +711,7 @@ jb_err cgi_edit_actions_url_form(struct client_state *csp, struct editable_file * file; struct file_line * cur_line; unsigned line_number; + unsigned section_start_line_number = 0; jb_err err; assert(csp); @@ -494,6 +740,10 @@ jb_err cgi_edit_actions_url_form(struct client_state *csp, for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++) { + if (cur_line->type == FILE_LINE_ACTION) + { + section_start_line_number = line_number; + } cur_line = cur_line->next; } @@ -517,6 +767,7 @@ jb_err cgi_edit_actions_url_form(struct client_state *csp, if (!err) err = map(exports, "v", 1, file->version_str, 1); if (!err) err = map(exports, "p", 1, url_encode(lookup(parameters, "p")), 0); if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0); + if (!err) err = map(exports, "jumptarget", 1, section_target(section_start_line_number), 0); edit_free_file(file); @@ -620,6 +871,7 @@ jb_err cgi_edit_actions_remove_url_form(struct client_state *csp, struct editable_file * file; struct file_line * cur_line; unsigned line_number; + unsigned section_start_line_number = 0; jb_err err; assert(csp); @@ -648,6 +900,10 @@ jb_err cgi_edit_actions_remove_url_form(struct client_state *csp, for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++) { + if (cur_line->type == FILE_LINE_ACTION) + { + section_start_line_number = line_number; + } cur_line = cur_line->next; } @@ -669,8 +925,9 @@ jb_err cgi_edit_actions_remove_url_form(struct client_state *csp, err = map(exports, "f", 1, file->identifier, 1); 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 = map(exports, "p", 1, url_encode(lookup(parameters, "p")), 0); if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0); + if (!err) err = map(exports, "jumptarget", 1, section_target(section_start_line_number), 0); edit_free_file(file); @@ -691,8 +948,7 @@ jb_err cgi_edit_actions_remove_url_form(struct client_state *csp, * Description : Write a complete file to disk. * * Parameters : - * 1 : filename = File to write to. - * 2 : file = Data structure to write. + * 1 : file = File to write. * * Returns : JB_ERR_OK on success * JB_ERR_FILE on error writing to file. @@ -712,11 +968,7 @@ jb_err edit_write_file(struct editable_file * file) assert(file); assert(file->filename); -#if defined(AMIGA) || defined(__OS2__) - if (NULL == (fp = fopen(file->filename, "w"))) -#else - if (NULL == (fp = fopen(file->filename, "wt"))) -#endif /* def AMIGA */ + if (NULL == (fp = fopen(file->filename, "wb"))) { return JB_ERR_FILE; } @@ -744,15 +996,12 @@ jb_err edit_write_file(struct editable_file * file) } if (cur_line->unprocessed) { - /* This should be a single line - sanity check. */ - assert(NULL == strchr(cur_line->unprocessed, '\r')); - assert(NULL == strchr(cur_line->unprocessed, '\n')); if (NULL != strchr(cur_line->unprocessed, '#')) { /* Must quote '#' characters */ int numhash = 0; - int len; + size_t len; char * src; char * dest; char * str; @@ -767,29 +1016,44 @@ jb_err edit_write_file(struct editable_file * file) assert(numhash > 0); /* Allocate new memory for string */ - len = strlen(cur_line->unprocessed); - if (NULL == (str = malloc(len + 1 + numhash))) + 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; } - /* Loop through string from end */ - src = cur_line->unprocessed + len; - dest = str + len + numhash; - for ( ; len >= 0; len--) + /* Copy string but quote hashes */ + src = cur_line->unprocessed; + dest = str; + while (*src) { - if ((*dest-- = *src--) == '#') + if (*src == '#') { - *dest-- = '\\'; + *dest++ = '\\'; numhash--; assert(numhash >= 0); } + *dest++ = *src++; } + *dest = '\0'; + assert(numhash == 0); - assert(src + 1 == cur_line->unprocessed); - assert(dest + 1 == str); + assert(strlen(str) == len); + assert(str == dest - len); + assert(src - len <= cur_line->unprocessed); + + if ((strlen(str) != len) || (numhash != 0)) + { + /* + * Escaping didn't work as expected, go spread the news. + * Only reached in non-debugging builds. + */ + log_error(LOG_LEVEL_ERROR, + "Looks like hash escaping failed. %s might be corrupted now.", + file->filename); + } if (fputs(str, fp) < 0) { @@ -885,7 +1149,7 @@ void edit_free_file(struct editable_file * file) /********************************************************************* * - * Function : edit_free_file + * Function : edit_free_file_lines * * Description : Free an entire linked list of file lines. * @@ -955,7 +1219,7 @@ static void edit_free_file_lines(struct file_line * first_line) *********************************************************************/ static int match_actions_file_header_line(const char * line, const char * name) { - int len; + size_t len; assert(line); assert(name); @@ -1007,8 +1271,8 @@ static int match_actions_file_header_line(const char * line, const char * name) * Parameters : * 1 : line = String from file. Must not start with * whitespace (else infinite loop!) - * 2 : name = Destination for name - * 2 : name = Destination for value + * 2 : pname = Destination for name + * 2 : pvalue = Destination for value * * Returns : JB_ERR_OK on success * JB_ERR_MEMORY on out-of-memory @@ -1021,7 +1285,7 @@ static jb_err split_line_on_equals(const char * line, char ** pname, char ** pva { const char * name_end; const char * value_start; - int name_len; + size_t name_len; assert(line); assert(pname); @@ -1050,7 +1314,7 @@ static jb_err split_line_on_equals(const char * line, char ** pname, char ** pva name_end--; } - name_len = name_end - line + 1; /* Length excluding \0 */ + name_len = (size_t)(name_end - line) + 1; /* Length excluding \0 */ if (NULL == (*pname = (char *) malloc(name_len + 1))) { return JB_ERR_MEMORY; @@ -1101,7 +1365,7 @@ static jb_err split_line_on_equals(const char * line, char ** pname, char ** pva jb_err edit_parse_actions_file(struct editable_file * file) { struct file_line * cur_line; - int len; + size_t len; const char * text; /* Text from a line */ char * name; /* For lines of the form name=value */ char * value; /* For lines of the form name=value */ @@ -1386,6 +1650,7 @@ jb_err edit_parse_actions_file(struct editable_file * file) * at EOF but it will not have been closed. * 2 : pfile = Destination for a linked list of file_lines. * Will be set to NULL on error. + * 3 : newline = How to handle newlines. * * Returns : JB_ERR_OK on success * JB_ERR_MEMORY on out-of-memory @@ -1543,11 +1808,7 @@ jb_err edit_read_file(struct client_state *csp, } } -#if defined(AMIGA) || defined(__OS2__) - if (NULL == (fp = fopen(filename,"r"))) -#else - if (NULL == (fp = fopen(filename,"rt"))) -#endif /* def AMIGA */ + if (NULL == (fp = fopen(filename,"rb"))) { free(filename); return JB_ERR_FILE; @@ -1746,12 +2007,10 @@ static jb_err get_file_name_param(struct client_state *csp, { const char *param; const char *s; -#if 0 /* Patch to make 3.0.0 work properly. */ char *name; -#endif /* 0 - Patch to make 3.0.0 work properly. */ char *fullpath; char ch; - int len; + size_t len; assert(csp); assert(parameters); @@ -1792,13 +2051,6 @@ static jb_err get_file_name_param(struct client_state *csp, } } - /* - * FIXME Following is a hack to make 3.0.0 work properly. - * Change "#if 0" --> "#if 1" below when we have modular action - * files. - * -- Jon - */ -#if 0 /* Patch to make 3.0.0 work properly. */ /* Append extension */ name = malloc(len + strlen(suffix) + 1); if (name == NULL) @@ -1811,16 +2063,7 @@ static jb_err get_file_name_param(struct client_state *csp, /* Prepend path */ fullpath = make_path(csp->config->confdir, name); free(name); -#else /* 1 - Patch to make 3.0.0 work properly. */ - if ((csp->actions_list == NULL) - || (csp->actions_list->filename == NULL)) - { - return JB_ERR_CGI_PARAMS; - } - fullpath = ( (csp->actions_list && csp->actions_list->filename) - ? strdup(csp->actions_list->filename) : NULL); -#endif /* 1 - Patch to make 3.0.0 work properly. */ if (fullpath == NULL) { return JB_ERR_MEMORY; @@ -1833,82 +2076,6 @@ static jb_err get_file_name_param(struct client_state *csp, } -/********************************************************************* - * - * Function : get_number_param - * - * Description : Get a non-negative integer from the parameters - * passed to a CGI function. - * - * Parameters : - * 1 : csp = Current client state (buffers, headers, etc...) - * 2 : parameters = map of cgi parameters - * 3 : name = Name of CGI parameter to read - * 4 : pvalue = destination for value. - * Set to -1 on error. - * - * Returns : JB_ERR_OK on success - * JB_ERR_MEMORY on out-of-memory - * JB_ERR_CGI_PARAMS if the parameter was not specified - * or is not valid. - * - *********************************************************************/ -static jb_err get_number_param(struct client_state *csp, - const struct map *parameters, - char *name, - unsigned *pvalue) -{ - const char *param; - char ch; - unsigned value; - - assert(csp); - assert(parameters); - assert(name); - assert(pvalue); - - *pvalue = -1; - - param = lookup(parameters, name); - if (!*param) - { - return JB_ERR_CGI_PARAMS; - } - - /* We don't use atoi because I want to check this carefully... */ - - value = 0; - while ((ch = *param++) != '\0') - { - if ((ch < '0') || (ch > '9')) - { - return JB_ERR_CGI_PARAMS; - } - - ch -= '0'; - - /* Note: - * - * defines UINT_MAX - * - * (UINT_MAX - ch) / 10 is the largest number that - * can be safely multiplied by 10 then have ch added. - */ - if (value > ((UINT_MAX - (unsigned)ch) / 10U)) - { - return JB_ERR_CGI_PARAMS; - } - - value = value * 10 + ch; - } - - /* Success */ - *pvalue = value; - - return JB_ERR_OK; -} - - /********************************************************************* * * Function : get_url_spec_param @@ -2065,9 +2232,9 @@ static jb_err get_url_spec_param(struct client_state *csp, static jb_err map_radio(struct map * exports, const char * optionname, const char * values, - char value) + int value) { - int len; + size_t len; char * buf; char * p; char c; @@ -2095,20 +2262,13 @@ static jb_err map_radio(struct map * exports, *p = c; if (map(exports, buf, 1, "", 1)) { - free(buf); return JB_ERR_MEMORY; } } } - *p = value; - if (map(exports, buf, 0, "checked", 1)) - { - free(buf); - return JB_ERR_MEMORY; - } - - return JB_ERR_OK; + *p = (char)value; + return map(exports, buf, 0, "checked", 1); } @@ -2258,14 +2418,15 @@ jb_err cgi_error_file(struct client_state *csp, /********************************************************************* * - * Function : cgi_error_bad_param + * Function : cgi_error_file * - * Description : CGI function that is called if the parameters - * (query string) for a CGI were wrong. + * Description : CGI function that is called when a file cannot be + * opened for writing by the CGI editor. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) * 2 : rsp = http_response data structure for output + * 3 : filename = The file that we can't write to * * CGI Parameters : none * @@ -2273,20 +2434,30 @@ jb_err cgi_error_file(struct client_state *csp, * JB_ERR_MEMORY on out-of-memory error. * *********************************************************************/ -jb_err cgi_error_disabled(struct client_state *csp, - struct http_response *rsp) +jb_err cgi_error_file_read_only(struct client_state *csp, + struct http_response *rsp, + const char *filename) { struct map *exports; + jb_err err; assert(csp); assert(rsp); + assert(filename); if (NULL == (exports = default_exports(csp, NULL))) { return JB_ERR_MEMORY; } - return template_fill_for_cgi(csp, "cgi-error-disabled", exports, rsp); + err = map(exports, "f", 1, html_encode(filename), 0); + if (err) + { + free_map(exports); + return err; + } + + return template_fill_for_cgi(csp, "cgi-error-file-read-only", exports, rsp); } @@ -2319,20 +2490,9 @@ jb_err cgi_edit_actions(struct client_state *csp, } /* FIXME: Incomplete */ - rsp->status = strdup("302 Local Redirect from Junkbuster"); - if (rsp->status == NULL) - { - return JB_ERR_MEMORY; - } - if (enlist_unique_header(rsp->headers, "Location", - CGI_PREFIX "edit-actions-list?f=ijb")) - { - free(rsp->status); - rsp->status = NULL; - return JB_ERR_MEMORY; - } - return JB_ERR_OK; + return cgi_redirect(rsp, CGI_PREFIX "edit-actions-list?f=default"); + } @@ -2366,7 +2526,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp, char * url_template; char * sections; char * urls; - char buf[50]; + char buf[150]; char * s; struct map * exports; struct map * section_exports; @@ -2375,7 +2535,10 @@ jb_err cgi_edit_actions_list(struct client_state *csp, struct file_line * cur_line; unsigned line_number = 0; unsigned prev_section_line_number = ((unsigned) (-1)); - int url_1_2; + int i, url_1_2; + struct file_list * fl; + struct url_actions * b; + char * buttons = NULL; jb_err err; if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) @@ -2383,6 +2546,12 @@ jb_err cgi_edit_actions_list(struct client_state *csp, return cgi_error_disabled(csp, rsp); } + if (NULL == (exports = default_exports(csp, NULL))) + { + return JB_ERR_MEMORY; + } + + /* Load actions file */ err = edit_read_actions_file(csp, rsp, parameters, 0, &file); if (err) { @@ -2390,15 +2559,140 @@ jb_err cgi_edit_actions_list(struct client_state *csp, return (err == JB_ERR_FILE ? JB_ERR_OK : err); } - if (NULL == (exports = default_exports(csp, NULL))) + /* Find start of actions in file */ + cur_line = file->lines; + line_number = 1; + while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION)) { - edit_free_file(file); - return JB_ERR_MEMORY; + cur_line = cur_line->next; + line_number++; } - err = map(exports, "f", 1, file->identifier, 1); + /* + * Conventional actions files should have a match all block + * at the start: + * cur_line = {...global actions...} + * cur_line->next = / + * cur_line->next->next = {...actions...} or EOF + */ + if ( (cur_line != NULL) + && (cur_line->type == FILE_LINE_ACTION) + && (cur_line->next != NULL) + && (cur_line->next->type == FILE_LINE_URL) + && (0 == strcmp(cur_line->next->unprocessed, "/")) + && ( (cur_line->next->next == NULL) + || (cur_line->next->next->type != FILE_LINE_URL) + ) ) + { + /* + * Generate string with buttons to set actions for "/" to + * any predefined set of actions (named standard.*, probably + * residing in standard.action). + */ + + err = template_load(csp, §ion_template, "edit-actions-list-button", 0); + if (err) + { + edit_free_file(file); + free_map(exports); + if (err == JB_ERR_FILE) + { + return cgi_error_no_template(csp, rsp, "edit-actions-list-button"); + } + return err; + } + + err = template_fill(§ion_template, exports); + if (err) + { + edit_free_file(file); + free_map(exports); + return err; + } + + buttons = strdup(""); + for (i = 0; i < MAX_AF_FILES; i++) + { + if (((fl = csp->actions_list[i]) != NULL) && ((b = fl->f) != NULL)) + { + for (b = b->next; NULL != b; b = b->next) + { + if (!strncmp(b->url->spec, "standard.", 9) && *(b->url->spec + 9) != '\0') + { + if (err || (NULL == (section_exports = new_map()))) + { + freez(buttons); + free(section_template); + edit_free_file(file); + free_map(exports); + return JB_ERR_MEMORY; + } + + err = map(section_exports, "button-name", 1, b->url->spec + 9, 1); + + if (err || (NULL == (s = strdup(section_template)))) + { + free_map(section_exports); + freez(buttons); + free(section_template); + edit_free_file(file); + free_map(exports); + return JB_ERR_MEMORY; + } + + if (!err) err = template_fill(&s, section_exports); + free_map(section_exports); + if (!err) err = string_join(&buttons, s); + } + } + } + } + freez(section_template); + if (!err) err = map(exports, "all-urls-buttons", 1, buttons, 0); + + /* + * Conventional actions file, supply extra editing help. + * (e.g. don't allow them to make it an unconventional one). + */ + if (!err) err = map_conditional(exports, "all-urls-present", 1); + + snprintf(buf, 150, "%d", line_number); + if (!err) err = map(exports, "all-urls-s", 1, buf, 1); + snprintf(buf, 150, "%d", 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); + + /* Skip the 2 lines */ + cur_line = cur_line->next->next; + line_number += 2; + + /* + * Note that prev_section_line_number is NOT set here. + * This is deliberate and not a bug. It stops a "Move up" + * option appearing on the next section. Clicking "Move + * up" would make the actions file unconventional, which + * we don't want, so we hide this option. + */ + } + else + { + /* + * Non-standard actions file - does not begin with + * the "All URLs" section. + */ + if (!err) err = map_conditional(exports, "all-urls-present", 0); + } + + /* Set up global exports */ + + if (!err) err = map(exports, "f", 1, file->identifier, 1); if (!err) err = map(exports, "v", 1, file->version_str, 1); + /* Discourage private additions to default.action */ + + if (!err) err = map_conditional(exports, "default-action", + (strcmp("default", lookup(parameters, "f")) == 0)); if (err) { edit_free_file(file); @@ -2408,7 +2702,9 @@ jb_err cgi_edit_actions_list(struct client_state *csp, /* Should do all global exports above this point */ - err = template_load(csp, §ion_template, "edit-actions-list-section"); + /* Load templates */ + + err = template_load(csp, §ion_template, "edit-actions-list-section", 0); if (err) { edit_free_file(file); @@ -2420,7 +2716,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp, return err; } - err = template_load(csp, &url_template, "edit-actions-list-url"); + err = template_load(csp, &url_template, "edit-actions-list-url", 0); if (err) { free(section_template); @@ -2452,15 +2748,6 @@ jb_err cgi_edit_actions_list(struct client_state *csp, return err; } - /* Find start of actions in file */ - cur_line = file->lines; - line_number = 1; - while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION)) - { - cur_line = cur_line->next; - line_number++; - } - if (NULL == (sections = strdup(""))) { free(section_template); @@ -2482,10 +2769,10 @@ jb_err cgi_edit_actions_list(struct client_state *csp, return JB_ERR_MEMORY; } - snprintf(buf, 50, "%d", line_number); + snprintf(buf, 150, "%d", line_number); err = map(section_exports, "s", 1, buf, 1); if (!err) err = map(section_exports, "actions", 1, - actions_to_html(cur_line->data.action), 0); + actions_to_html(csp, cur_line->data.action), 0); if ( (!err) && (cur_line->next != NULL) @@ -2502,7 +2789,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp, if (prev_section_line_number != ((unsigned)(-1))) { /* Not last section */ - snprintf(buf, 50, "%d", prev_section_line_number); + snprintf(buf, 150, "%d", 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"); } @@ -2556,10 +2843,10 @@ jb_err cgi_edit_actions_list(struct client_state *csp, return JB_ERR_MEMORY; } - snprintf(buf, 50, "%d", line_number); + snprintf(buf, 150, "%d", line_number); err = map(url_exports, "p", 1, buf, 1); - snprintf(buf, 50, "%d", url_1_2); + snprintf(buf, 150, "%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, @@ -2622,12 +2909,13 @@ 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); + if (!err) err = map(section_exports, "s-next", 1, buf, 1); + if ( (cur_line != NULL) && (cur_line->type == FILE_LINE_ACTION)) { /* Not last section */ - snprintf(buf, 50, "%d", line_number); - if (!err) err = map(section_exports, "s-next", 1, buf, 1); if (!err) err = map_block_keep(section_exports, "s-next-exists"); } else @@ -2694,7 +2982,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp, /********************************************************************* * - * Function : cgi_edit_actions + * Function : cgi_edit_actions_for_url * * Description : CGI function that edits the Actions list. * @@ -2721,6 +3009,8 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, struct file_line * cur_line; unsigned line_number; jb_err err; + struct re_filterfile_spec *filter_group; + int i, have_filters = 0; if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) { @@ -2769,7 +3059,162 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, if (!err) err = actions_to_radio(exports, cur_line->data.action); - edit_free_file(file); + /* + * XXX: Some browsers (at least IE6 and IE7) have an artifical URL + * length limitation and ignore clicks on the Submit buttons if + * the resulting GET URL would be longer than their limit. + * + * In Privoxy 3.0.5 beta the standard edit-actions-for-url template + * reached this limit and action editing stopped working in these + * browsers (BR #1570678). + * + * The config option split-large-forms works around this browser + * bug (HTTP has no URL lenght limitation) by deviding 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. + * + * 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)) + { + /* Generate multiple smaller form by killing the big one. */ + err = map_block_killer(exports, "one-form-only"); + } + else + { + /* Default: Generate one large form by killing the smaller ones. */ + err = map_block_killer(exports, "multiple-forms"); + } + + for (i = 0; i < MAX_AF_FILES; i++) + { + if ((csp->rlist[i] != NULL) && (csp->rlist[i]->f != NULL)) + { + if (!err) err = map_conditional(exports, "any-filters-defined", 1); + have_filters = 1; + break; + } + } + + if (err) + { + edit_free_file(file); + free_map(exports); + return err; + } + + if (0 == have_filters) + err = map(exports, "filter-params", 1, "", 1); + else + { + /* We have some entries in the filter list */ + char * result; + int filter_identifier = 0; + char * filter_template; + + err = template_load(csp, &filter_template, "edit-actions-for-url-filter", 0); + if (err) + { + edit_free_file(file); + free_map(exports); + if (err == JB_ERR_FILE) + { + return cgi_error_no_template(csp, rsp, "edit-actions-for-url-filter"); + } + return err; + } + + err = template_fill(&filter_template, exports); + + result = strdup(""); + + for (i = 0; i < MAX_AF_FILES; i++) + { + if ((csp->rlist[i] != NULL) && (csp->rlist[i]->f != NULL)) + { + filter_group = csp->rlist[i]->f; + for (;(!err) && (filter_group != NULL); filter_group = filter_group->next) + { + char current_mode = 'x'; + struct list_entry *filter_name; + char * this_line; + struct map *line_exports; + char number[20]; + + filter_name = cur_line->data.action->multi_add[ACTION_MULTI_FILTER]->first; + while ((filter_name != NULL) + && (0 != strcmp(filter_group->name, filter_name->str))) + { + filter_name = filter_name->next; + } + + if (filter_name != NULL) + { + current_mode = 'y'; + } + else + { + filter_name = cur_line->data.action->multi_remove[ACTION_MULTI_FILTER]->first; + while ((filter_name != NULL) + && (0 != strcmp(filter_group->name, filter_name->str))) + { + filter_name = filter_name->next; + } + if (filter_name != NULL) + { + current_mode = 'n'; + } + } + + /* Generate a unique serial number */ + snprintf(number, sizeof(number), "%x", filter_identifier++); + number[sizeof(number) - 1] = '\0'; + + line_exports = new_map(); + if (line_exports == NULL) + { + err = JB_ERR_MEMORY; + freez(result); + } + else + { + if (!err) err = map(line_exports, "index", 1, number, 1); + if (!err) err = map(line_exports, "name", 1, filter_group->name, 1); + if (!err) err = map(line_exports, "description", 1, filter_group->description, 1); + if (!err) err = map_radio(line_exports, "this-filter", "ynx", current_mode); + + this_line = NULL; + if (!err) + { + this_line = strdup(filter_template); + if (this_line == NULL) err = JB_ERR_MEMORY; + } + if (!err) err = template_fill(&this_line, line_exports); + string_join(&result, this_line); + + free_map(line_exports); + } + } + } + } + freez(filter_template); + + if (!err) + { + err = map(exports, "filter-params", 1, result, 0); + } + else + { + freez(result); + } + } + + if (!err) err = map_radio(exports, "filter-all", "nx", + (cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] ? 'n' : 'x')); + + edit_free_file(file); if (err) { @@ -2807,12 +3252,17 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, unsigned sectionid; char * actiontext; char * newtext; - int len; + size_t len; struct editable_file * file; struct file_line * cur_line; unsigned line_number; - char * target; + char target[1024]; jb_err err; + int filter_identifier; + const char * action_set_name; + char ch; + struct file_list * fl; + struct url_actions * b; if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) { @@ -2849,7 +3299,97 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, return JB_ERR_CGI_PARAMS; } - err = actions_from_radio(parameters, cur_line->data.action); + get_string_param(parameters, "p", &action_set_name); + if (action_set_name != NULL) + { + for (filter_identifier = 0; filter_identifier < MAX_AF_FILES; filter_identifier++) + { + if (((fl = csp->actions_list[filter_identifier]) != NULL) && ((b = fl->f) != NULL)) + { + for (b = b->next; NULL != b; b = b->next) + { + if (!strncmp(b->url->spec, "standard.", 9) && !strcmp(b->url->spec + 9, action_set_name)) + { + copy_action(cur_line->data.action, b->action); + goto found; + } + } + } + } + edit_free_file(file); + return JB_ERR_CGI_PARAMS; + + found: ; + } + else + { + err = actions_from_radio(parameters, cur_line->data.action); + } + + 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') + { + cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] = 0; + } + + for (filter_identifier = 0; !err; filter_identifier++) + { + char key_value[30]; + char key_name[30]; + const char *name; + char value; + + /* Generate the keys */ + snprintf(key_value, sizeof(key_value), "filter_r%x", filter_identifier); + key_value[sizeof(key_value) - 1] = '\0'; + snprintf(key_name, sizeof(key_name), "filter_n%x", filter_identifier); + key_name[sizeof(key_name) - 1] = '\0'; + + err = get_string_param(parameters, key_name, &name); + if (err) break; + + if (name == NULL) + { + /* End of list */ + break; + } + + value = get_char_param(parameters, key_value); + if (value == 'Y') + { + list_remove_item(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name); + if (!err) err = enlist(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name); + list_remove_item(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name); + } + else if (value == 'N') + { + list_remove_item(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name); + if (!cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER]) + { + list_remove_item(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name); + if (!err) err = enlist(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name); + } + } + else if (value == 'X') + { + list_remove_item(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name); + list_remove_item(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name); + } + } + if(err) { /* Out of memory */ @@ -2895,31 +3435,21 @@ jb_err cgi_edit_actions_submit(struct client_state *csp, if (err) { /* Error writing file */ + if (err == JB_ERR_FILE) + { + /* Read-only file. */ + err = cgi_error_file_read_only(csp, rsp, file->identifier); + } edit_free_file(file); return err; } - target = strdup(CGI_PREFIX "edit-actions-list?f="); - string_append(&target, file->identifier); + snprintf(target, 1024, CGI_PREFIX "edit-actions-list?foo=%lu&f=%s#l%d", + (long) time(NULL), file->identifier, sectionid); edit_free_file(file); - if (target == NULL) - { - /* Out of memory */ - return JB_ERR_MEMORY; - } - - rsp->status = strdup("302 Local Redirect from Junkbuster"); - if (rsp->status == NULL) - { - free(target); - return JB_ERR_MEMORY; - } - err = enlist_unique_header(rsp->headers, "Location", target); - free(target); - - return err; + return cgi_redirect(rsp, target); } @@ -2957,9 +3487,14 @@ jb_err cgi_edit_actions_url(struct client_state *csp, struct editable_file * file; struct file_line * cur_line; unsigned line_number; - char * target; + unsigned section_start_line_number = 0; + char target[1024]; jb_err err; + assert(csp); + assert(rsp); + assert(parameters); + if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) { return cgi_error_disabled(csp, rsp); @@ -2994,6 +3529,10 @@ jb_err cgi_edit_actions_url(struct client_state *csp, while ((cur_line != NULL) && (line_number < patternid)) { + if (cur_line->type == FILE_LINE_ACTION) + { + section_start_line_number = line_number; + } cur_line = cur_line->next; line_number++; } @@ -3017,31 +3556,21 @@ jb_err cgi_edit_actions_url(struct client_state *csp, if (err) { /* Error writing file */ + if (err == JB_ERR_FILE) + { + /* Read-only file. */ + err = cgi_error_file_read_only(csp, rsp, file->identifier); + } edit_free_file(file); return err; } - target = strdup(CGI_PREFIX "edit-actions-list?f="); - string_append(&target, file->identifier); + snprintf(target, 1024, CGI_PREFIX "edit-actions-list?foo=%lu&f=%s#l%d", + (long) time(NULL), file->identifier, section_start_line_number); edit_free_file(file); - if (target == NULL) - { - /* Out of memory */ - return JB_ERR_MEMORY; - } - - rsp->status = strdup("302 Local Redirect from Junkbuster"); - if (rsp->status == NULL) - { - free(target); - return JB_ERR_MEMORY; - } - err = enlist_unique_header(rsp->headers, "Location", target); - free(target); - - return err; + return cgi_redirect(rsp, target); } @@ -3079,7 +3608,7 @@ jb_err cgi_edit_actions_add_url(struct client_state *csp, struct editable_file * file; struct file_line * cur_line; unsigned line_number; - char * target; + char target[1024]; jb_err err; if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) @@ -3156,31 +3685,21 @@ jb_err cgi_edit_actions_add_url(struct client_state *csp, if (err) { /* Error writing file */ + if (err == JB_ERR_FILE) + { + /* Read-only file. */ + err = cgi_error_file_read_only(csp, rsp, file->identifier); + } edit_free_file(file); return err; } - target = strdup(CGI_PREFIX "edit-actions-list?f="); - string_append(&target, file->identifier); + snprintf(target, 1024, CGI_PREFIX "edit-actions-list?foo=%lu&f=%s#l%d", + (long) time(NULL), file->identifier, sectionid); edit_free_file(file); - if (target == NULL) - { - /* Out of memory */ - return JB_ERR_MEMORY; - } - - rsp->status = strdup("302 Local Redirect from Junkbuster"); - if (rsp->status == NULL) - { - free(target); - return JB_ERR_MEMORY; - } - err = enlist_unique_header(rsp->headers, "Location", target); - free(target); - - return err; + return cgi_redirect(rsp, target); } @@ -3216,7 +3735,8 @@ jb_err cgi_edit_actions_remove_url(struct client_state *csp, struct file_line * cur_line; struct file_line * prev_line; unsigned line_number; - char * target; + unsigned section_start_line_number = 0; + char target[1024]; jb_err err; if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) @@ -3243,6 +3763,10 @@ jb_err cgi_edit_actions_remove_url(struct client_state *csp, while ((cur_line != NULL) && (line_number < patternid)) { + if (cur_line->type == FILE_LINE_ACTION) + { + section_start_line_number = line_number; + } prev_line = cur_line; cur_line = cur_line->next; line_number++; @@ -3272,31 +3796,21 @@ jb_err cgi_edit_actions_remove_url(struct client_state *csp, if (err) { /* Error writing file */ + if (err == JB_ERR_FILE) + { + /* Read-only file. */ + err = cgi_error_file_read_only(csp, rsp, file->identifier); + } edit_free_file(file); return err; } - target = strdup(CGI_PREFIX "edit-actions-list?f="); - string_append(&target, file->identifier); + snprintf(target, 1024, CGI_PREFIX "edit-actions-list?foo=%lu&f=%s#l%d", + (long) time(NULL), file->identifier, section_start_line_number); edit_free_file(file); - if (target == NULL) - { - /* Out of memory */ - return JB_ERR_MEMORY; - } - - rsp->status = strdup("302 Local Redirect from Junkbuster"); - if (rsp->status == NULL) - { - free(target); - return JB_ERR_MEMORY; - } - err = enlist_unique_header(rsp->headers, "Location", target); - free(target); - - return err; + return cgi_redirect(rsp, target); } @@ -3333,7 +3847,7 @@ jb_err cgi_edit_actions_section_remove(struct client_state *csp, struct file_line * cur_line; struct file_line * prev_line; unsigned line_number; - char * target; + char target[1024]; jb_err err; if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) @@ -3404,31 +3918,21 @@ jb_err cgi_edit_actions_section_remove(struct client_state *csp, if (err) { /* Error writing file */ + if (err == JB_ERR_FILE) + { + /* Read-only file. */ + err = cgi_error_file_read_only(csp, rsp, file->identifier); + } edit_free_file(file); return err; } - target = strdup(CGI_PREFIX "edit-actions-list?f="); - string_append(&target, file->identifier); + snprintf(target, 1024, CGI_PREFIX "edit-actions-list?foo=%lu&f=%s", + (long) time(NULL), file->identifier); edit_free_file(file); - if (target == NULL) - { - /* Out of memory */ - return JB_ERR_MEMORY; - } - - rsp->status = strdup("302 Local Redirect from Junkbuster"); - if (rsp->status == NULL) - { - free(target); - return JB_ERR_MEMORY; - } - err = enlist_unique_header(rsp->headers, "Location", target); - free(target); - - return err; + return cgi_redirect(rsp, target); } @@ -3466,7 +3970,7 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp, struct editable_file * file; struct file_line * cur_line; unsigned line_number; - char * target; + char target[1024]; jb_err err; if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) @@ -3490,10 +3994,10 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp, line_number = 1; cur_line = file->lines; - if (sectionid < 1U) + if (sectionid <= 1U) { /* Add to start of file */ - if (cur_line != NULL) + if (cur_line != NULL && cur_line->type != FILE_LINE_ACTION) { /* There's something in the file, find the line before the first * action. @@ -3505,6 +4009,11 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp, line_number++; } } + else + { + /* File starts with action line, so insert at top */ + cur_line = NULL; + } } else { @@ -3578,31 +4087,21 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp, if (err) { /* Error writing file */ + if (err == JB_ERR_FILE) + { + /* Read-only file. */ + err = cgi_error_file_read_only(csp, rsp, file->identifier); + } edit_free_file(file); return err; } - target = strdup(CGI_PREFIX "edit-actions-list?f="); - string_append(&target, file->identifier); + snprintf(target, 1024, CGI_PREFIX "edit-actions-list?foo=%lu&f=%s", + (long) time(NULL), file->identifier); edit_free_file(file); - if (target == NULL) - { - /* Out of memory */ - return JB_ERR_MEMORY; - } - - rsp->status = strdup("302 Local Redirect from Junkbuster"); - if (rsp->status == NULL) - { - free(target); - return JB_ERR_MEMORY; - } - err = enlist_unique_header(rsp->headers, "Location", target); - free(target); - - return err; + return cgi_redirect(rsp, target); } @@ -3651,7 +4150,7 @@ jb_err cgi_edit_actions_section_swap(struct client_state *csp, struct file_line * line_end_section2; struct file_line * line_after_section2; unsigned line_number; - char * target; + char target[1024]; jb_err err; if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS)) @@ -3776,35 +4275,25 @@ jb_err cgi_edit_actions_section_swap(struct client_state *csp, if (err) { /* Error writing file */ + if (err == JB_ERR_FILE) + { + /* Read-only file. */ + err = cgi_error_file_read_only(csp, rsp, file->identifier); + } edit_free_file(file); return err; } } /* END if (section1 != section2) */ - target = strdup(CGI_PREFIX "edit-actions-list?f="); - string_append(&target, file->identifier); + snprintf(target, 1024, CGI_PREFIX "edit-actions-list?foo=%lu&f=%s", + (long) time(NULL), file->identifier); edit_free_file(file); - if (target == NULL) - { - /* Out of memory */ - return JB_ERR_MEMORY; - } - - rsp->status = strdup("302 Local Redirect from Junkbuster"); - if (rsp->status == NULL) - { - free(target); - return JB_ERR_MEMORY; - } - err = enlist_unique_header(rsp->headers, "Location", target); - free(target); - - return err; + return cgi_redirect(rsp, target); } - +#ifdef FEATURE_TOGGLE /********************************************************************* * * Function : cgi_toggle @@ -3833,7 +4322,6 @@ jb_err cgi_toggle(struct client_state *csp, struct map *exports; char mode; const char *template_name; - jb_err err; assert(csp); assert(rsp); @@ -3844,42 +4332,73 @@ jb_err cgi_toggle(struct client_state *csp, return cgi_error_disabled(csp, rsp); } - if (NULL == (exports = default_exports(csp, "toggle"))) - { - return JB_ERR_MEMORY; - } - - mode = *(lookup(parameters, "set")); + mode = get_char_param(parameters, "set"); - if (mode == 'e') + if (mode == 'E') { /* Enable */ - g_bToggleIJB = 1; + global_toggle_state = 1; } - else if (mode == 'd') + else if (mode == 'D') { /* Disable */ - g_bToggleIJB = 0; + global_toggle_state = 0; } - else if (mode == 't') + else if (mode == 'T') { /* Toggle */ - g_bToggleIJB = !g_bToggleIJB; + global_toggle_state = !global_toggle_state; } - err = map_conditional(exports, "enabled", g_bToggleIJB); - if (err) + if (NULL == (exports = default_exports(csp, "toggle"))) { - free_map(exports); - return err; + return JB_ERR_MEMORY; } - template_name = (*(lookup(parameters, "mini")) + template_name = (get_char_param(parameters, "mini") ? "toggle-mini" : "toggle"); return template_fill_for_cgi(csp, template_name, exports, rsp); } +#endif /* def FEATURE_TOGGLE */ + +/********************************************************************* + * + * Function : javascriptify + * + * Description : Converts a string into a form JavaScript will like. + * + * 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 + * array. That array contains >60 elements and will + * be changed whenever we add a new action to the + * editor, so I'm NOT going to use indexes that have + * to be figured out by hand.) + * + * Currently the only thing we have to worry about + * is "-" ==> "_" conversion. + * + * This is a length-preserving operation so it is + * carried out in-place, no memory is allocated + * or freed. + * + * Parameters : + * 1 : identifier = String to make JavaScript-friendly. + * + * Returns : N/A + * + *********************************************************************/ +static void javascriptify(char * identifier) +{ + char * p = identifier; + while (NULL != (p = strchr(p, '-'))) + { + *p++ = '_'; + } +} /********************************************************************* @@ -4011,44 +4530,6 @@ static jb_err actions_to_radio(struct map * exports, } -/********************************************************************* - * - * Function : javascriptify - * - * Description : Converts a string into a form JavaScript will like. - * - * 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 - * array. That array contains >60 elements and will - * be changed whenever we add a new action to the - * editor, so I'm NOT going to use indexes that have - * to be figured out by hand.) - * - * Currently the only thing we have to worry about - * is "-" ==> "_" conversion. - * - * This is a length-preserving operation so it is - * carried out in-place, no memory is allocated - * or freed. - * - * Parameters : - * 1 : identifier = String to make JavaScript-friendly. - * - * Returns : N/A - * - *********************************************************************/ -static void javascriptify(char * identifier) -{ - char * p = identifier; - while (NULL != (p = strchr(p, '-'))) - { - *p++ = '_'; - } -} - - /********************************************************************* * * Function : actions_from_radio @@ -4067,13 +4548,13 @@ static void javascriptify(char * identifier) * *********************************************************************/ static jb_err actions_from_radio(const struct map * parameters, - struct action_spec *action) + struct action_spec *action) { - static int first_time = 1; const char * param; char * param_dup; char ch; const char * js_name; + jb_err err = JB_ERR_OK; assert(parameters); assert(action); @@ -4082,22 +4563,27 @@ static jb_err actions_from_radio(const struct map * parameters, * but in this case we're safe and don't need semaphores. * Be careful if you modify this function. * - Jon + * The js_name_arr's are never free()d, but this is no + * problem, since they will only be created once and + * used by all threads thereafter. -oes */ #define JAVASCRIPTIFY(dest_var, string) \ { \ - static char js_name_arr[] = string; \ + static int first_time = 1; \ + static char *js_name_arr; \ if (first_time) \ { \ + js_name_arr = strdup(string); \ javascriptify(js_name_arr); \ } \ dest_var = js_name_arr; \ + first_time = 0; \ } \ #define DEFINE_ACTION_BOOL(name, bit) \ JAVASCRIPTIFY(js_name, name); \ - param = lookup(parameters, js_name); \ - ch = ijb_toupper(param[0]); \ + ch = get_char_param(parameters, js_name); \ if (ch == 'Y') \ { \ action->add |= bit; \ @@ -4116,18 +4602,18 @@ static jb_err actions_from_radio(const struct map * parameters, #define DEFINE_ACTION_STRING(name, bit, index) \ JAVASCRIPTIFY(js_name, name); \ - param = lookup(parameters, js_name); \ - ch = ijb_toupper(param[0]); \ + ch = get_char_param(parameters, js_name); \ if (ch == 'Y') \ { \ + param = NULL; \ JAVASCRIPTIFY(js_name, name "-mode"); \ - param = lookup(parameters, js_name); \ - if ((*param == '\0') || (0 == strcmp(param, "CUSTOM"))) \ - { \ - JAVASCRIPTIFY(js_name, name "-param"); \ - param = lookup(parameters, js_name); \ + if (!err) err = get_string_param(parameters, js_name, ¶m); \ + if ((param == NULL) || (0 == strcmp(param, "CUSTOM"))) \ + { \ + JAVASCRIPTIFY(js_name, name "-param"); \ + if (!err) err = get_string_param(parameters, js_name, ¶m); \ } \ - if (*param != '\0') \ + if (param != NULL) \ { \ if (NULL == (param_dup = strdup(param))) \ { \ @@ -4160,8 +4646,7 @@ static jb_err actions_from_radio(const struct map * parameters, #define DEFINE_ACTION_MULTI(name, index) \ JAVASCRIPTIFY(js_name, name); \ - param = lookup(parameters, js_name); \ - ch = ijb_toupper((int)param[0]); \ + ch = get_char_param(parameters, js_name); \ if (ch == 'Y') \ { \ /* FIXME */ \ @@ -4189,9 +4674,7 @@ static jb_err actions_from_radio(const struct map * parameters, #undef DEFINE_ACTION_ALIAS #undef JAVASCRIPTIFY - first_time = 0; - - return JB_ERR_OK; + return err; }