1 /*********************************************************************
3 * File : $Source: /cvsroot/ijbswa/current/cgiedit.c,v $
5 * Purpose : CGI-based actionsfile editor.
7 * NOTE: The CGIs in this file use parameter names
8 * such as "f" and "s" which are really *BAD* choices.
9 * However, I'm trying to save bytes in the
10 * edit-actions-list HTML page - the standard actions
11 * file generated a 550kbyte page, which is ridiculous.
13 * Stick to the short names in this file for consistency.
15 * Copyright : Written by and Copyright (C) 2001-2014 the
16 * Privoxy team. https://www.privoxy.org/
18 * Based on the Internet Junkbuster originally written
19 * by and Copyright (C) 1997 Anonymous Coders and
20 * Junkbusters Corporation. http://www.junkbusters.com
22 * This program is free software; you can redistribute it
23 * and/or modify it under the terms of the GNU General
24 * Public License as published by the Free Software
25 * Foundation; either version 2 of the License, or (at
26 * your option) any later version.
28 * This program is distributed in the hope that it will
29 * be useful, but WITHOUT ANY WARRANTY; without even the
30 * implied warranty of MERCHANTABILITY or FITNESS FOR A
31 * PARTICULAR PURPOSE. See the GNU General Public
32 * License for more details.
34 * The GNU General Public License should be included with
35 * this file. If not, you can view it at
36 * http://www.gnu.org/copyleft/gpl.html
37 * or write to the Free Software Foundation, Inc., 59
38 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
40 **********************************************************************/
46 * FIXME: Following includes copied from cgi.c - which are actually needed?
51 #include <sys/types.h>
60 #include "cgisimple.h"
68 /* loadcfg.h is for global_toggle_state only */
70 #endif /* def FEATURE_TOGGLE */
74 #ifdef FEATURE_CGI_EDIT_ACTIONS
77 * A line in an editable_file.
81 /** Next entry in the linked list */
82 struct file_line * next;
84 /** The raw data, to write out if this line is unmodified. */
87 /** Comments and/or whitespace to put before this line if it's modified
88 and then written out. */
91 /** The actual data, as a string. Line continuation and comment removal
92 are performed on the data read from file before it's stored here, so
93 it will be a single line of data. */
96 /** The type of data on this line. One of the FILE_LINE_xxx constants. */
99 /** The actual data, processed into some sensible data type. */
103 /** An action specification. */
104 struct action_spec action[1];
106 /** A name=value pair. */
110 /** The name in the name=value pair. */
113 /** The value in the name=value pair, as a string. */
116 /** The value in the name=value pair, as an integer. */
125 /** This file_line has not been processed yet. */
126 #define FILE_LINE_UNPROCESSED 1
128 /** This file_line is blank. Can only appear at the end of a file, due to
129 the way the parser works. */
130 #define FILE_LINE_BLANK 2
132 /** This file_line says {{alias}}. */
133 #define FILE_LINE_ALIAS_HEADER 3
135 /** This file_line defines an alias. */
136 #define FILE_LINE_ALIAS_ENTRY 4
138 /** This file_line defines an {action}. */
139 #define FILE_LINE_ACTION 5
141 /** This file_line specifies a URL pattern. */
142 #define FILE_LINE_URL 6
144 /** This file_line says {{settings}}. */
145 #define FILE_LINE_SETTINGS_HEADER 7
147 /** This file_line is in a {{settings}} block. */
148 #define FILE_LINE_SETTINGS_ENTRY 8
150 /** This file_line says {{description}}. */
151 #define FILE_LINE_DESCRIPTION_HEADER 9
153 /** This file_line is in a {{description}} block. */
154 #define FILE_LINE_DESCRIPTION_ENTRY 10
157 * Number of file modification time mismatches
158 * before the CGI editor gets turned off.
160 #define ACCEPTABLE_TIMESTAMP_MISMATCHES 3
163 * A configuration file, in a format that can be edited and written back to
168 struct file_line * lines; /**< The contents of the file. A linked list of lines. */
169 const char * filename; /**< Full pathname - e.g. "/etc/privoxy/wibble.action". */
170 unsigned identifier; /**< The file name's position in csp->config->actions_file[]. */
171 const char * version_str; /**< Last modification time, as a string. For CGI param. */
172 /**< Can be used in URL without using url_param(). */
173 unsigned version; /**< Last modification time - prevents chaos with
174 the browser's "back" button. Note that this is a
175 time_t cast to an unsigned. When comparing, always
176 cast the time_t to an unsigned, and *NOT* vice-versa.
177 This may lose the top few bits, but they're not
178 significant anyway. */
179 int newline; /**< Newline convention - one of the NEWLINE_xxx constants.
180 Note that changing this after the file has been
181 read in will cause a mess. */
182 struct file_line * parse_error; /**< On parse error, this is the offending line. */
183 const char * parse_error_text; /**< On parse error, this is the problem.
184 (Statically allocated) */
188 * Information about the filter types.
189 * Used for macro replacement in cgi_edit_actions_for_url.
191 struct filter_type_info
193 const int multi_action_index; /**< The multi action index as defined in project.h */
194 const char *macro_name; /**< Name of the macro that has to be replaced
195 with the prepared templates.
196 For example "content-filter-params" */
197 const char *type; /**< Name of the filter type,
198 for example "server-header-filter". */
199 /* XXX: check if these two can be combined. */
200 const char *disable_all_option; /**< Name of the catch-all radio option that has
201 to be checked or unchecked for this filter type. */
202 const char *disable_all_param; /**< Name of the parameter that causes all filters of
203 this type to be disabled. */
204 const char *abbr_type; /**< Abbreviation of the filter type, usually the
205 first or second character capitalized */
206 const char *anchor; /**< Anchor for the User Manual link,
207 for example "SERVER-HEADER-FILTER" */
210 /* Accessed by index, keep the order in the way the FT_ macros are defined. */
211 static const struct filter_type_info filter_type_info[] =
215 "content-filter-params", "filter",
216 "filter-all", "filter_all",
220 ACTION_MULTI_CLIENT_HEADER_FILTER,
221 "client-header-filter-params", "client-header-filter",
222 "client-header-filter-all", "client_header_filter_all",
223 "C", "CLIENT-HEADER-FILTER"
226 ACTION_MULTI_SERVER_HEADER_FILTER,
227 "server-header-filter-params", "server-header-filter",
228 "server-header-filter-all", "server_header_filter_all",
229 "S", "SERVER-HEADER-FILTER"
232 ACTION_MULTI_CLIENT_HEADER_TAGGER,
233 "client-header-tagger-params", "client-header-tagger",
234 "client-header-tagger-all", "client_header_tagger_all",
235 "L", "CLIENT-HEADER-TAGGER"
238 ACTION_MULTI_SERVER_HEADER_TAGGER,
239 "server-header-tagger-params", "server-header-tagger",
240 "server-header-tagger-all", "server_header_tagger_all",
241 "E", "SERVER-HEADER-TAGGER"
244 ACTION_MULTI_SUPPRESS_TAG,
245 "suppress-tag-params", "suppress-tag",
246 "suppress-tag-all", "suppress_tag_all",
250 ACTION_MULTI_CLIENT_BODY_FILTER,
251 "client-body-filter-params", "client-body-filter",
252 "client-body-filter-all", "client_body_filter_all",
253 "P", "CLIENT-BODY-FILTER"
255 #ifdef FEATURE_EXTERNAL_FILTERS
257 ACTION_MULTI_EXTERNAL_FILTER,
258 "external-content-filter-params", "external-filter",
259 "external-content-filter-all", "external_content_filter_all",
260 "E", "EXTERNAL-CONTENT-FILTER"
265 /* FIXME: Following non-static functions should be prototyped in .h or made static */
267 /* Functions to read and write arbitrary config files */
268 jb_err edit_read_file(struct client_state *csp,
269 const struct map *parameters,
271 struct editable_file **pfile);
272 jb_err edit_write_file(struct editable_file * file);
273 void edit_free_file(struct editable_file * file);
275 /* Functions to read and write actions files */
276 jb_err edit_parse_actions_file(struct editable_file * file);
277 jb_err edit_read_actions_file(struct client_state *csp,
278 struct http_response *rsp,
279 const struct map *parameters,
281 struct editable_file **pfile);
284 jb_err cgi_error_modified(struct client_state *csp,
285 struct http_response *rsp,
286 const char *filename);
287 jb_err cgi_error_parse(struct client_state *csp,
288 struct http_response *rsp,
289 struct editable_file *file);
290 jb_err cgi_error_file(struct client_state *csp,
291 struct http_response *rsp,
292 const char *filename);
293 jb_err cgi_error_file_read_only(struct client_state *csp,
294 struct http_response *rsp,
295 const char *filename);
297 /* Internal arbitrary config file support functions */
298 static jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline);
299 static void edit_free_file_lines(struct file_line * first_line);
301 /* Internal actions file support functions */
302 static int match_actions_file_header_line(const char * line, const char * name);
303 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue);
305 /* Internal parameter parsing functions */
306 static jb_err get_url_spec_param(struct client_state *csp,
307 const struct map *parameters,
312 /* Internal actionsfile <==> HTML conversion functions */
313 static jb_err map_radio(struct map * exports,
314 const char * optionname,
317 static jb_err actions_to_radio(struct map * exports,
318 const struct action_spec *action);
319 static jb_err actions_from_radio(const struct map * parameters,
320 struct action_spec *action);
321 static jb_err action_render_string_filters_template(struct map * exports,
322 const struct action_spec *action,
323 const char* flter_template,
324 const struct filter_type_info *type);
327 static jb_err map_copy_parameter_html(struct map *out,
328 const struct map *in,
331 static jb_err get_file_name_param(struct client_state *csp,
332 const struct map *parameters,
333 const char *param_name,
334 const char **pfilename);
336 /* Internal convenience functions */
337 static char *section_target(const unsigned sectionid);
339 /*********************************************************************
341 * Function : section_target
343 * Description : Given an unsigned (section id) n, produce a dynamically
344 * allocated string of the form #l<n>, for use in link
347 * XXX: The hash should be moved into the templates
348 * to make this function more generic and render
349 * stringify() obsolete.
352 * 1 : sectionid = start line number of section
354 * Returns : String with link target, or NULL if out of
357 *********************************************************************/
358 static char *section_target(const unsigned sectionid)
362 snprintf(buf, sizeof(buf), "#l%u", sectionid);
368 /*********************************************************************
370 * Function : stringify
372 * Description : Convert a number into a dynamically allocated string.
375 * 1 : number = The number to convert.
377 * Returns : String with link target, or NULL if out of memory
379 *********************************************************************/
380 static char *stringify(const unsigned number)
384 snprintf(buf, sizeof(buf), "%u", number);
389 /*********************************************************************
391 * Function : map_copy_parameter_html
393 * Description : Copy a CGI parameter from one map to another, HTML
397 * 1 : out = target map
398 * 2 : in = source map
399 * 3 : name = name of cgi parameter to copy
401 * Returns : JB_ERR_OK on success
402 * JB_ERR_MEMORY on out-of-memory
403 * JB_ERR_CGI_PARAMS if the parameter doesn't exist
406 *********************************************************************/
407 static jb_err map_copy_parameter_html(struct map *out,
408 const struct map *in,
418 value = lookup(in, name);
419 err = map(out, name, 1, html_encode(value), 0);
426 else if (*value == '\0')
428 return JB_ERR_CGI_PARAMS;
437 /*********************************************************************
439 * Function : cgi_edit_actions_url_form
441 * Description : CGI function that displays a form for
445 * 1 : csp = Current client state (buffers, headers, etc...)
446 * 2 : rsp = http_response data structure for output
447 * 3 : parameters = map of cgi parameters
450 * i : (action index) Identifies the file to edit
451 * v : (version) File's last-modified time
452 * p : (pattern) Line number of pattern to edit
454 * Returns : JB_ERR_OK on success
455 * JB_ERR_MEMORY on out-of-memory
456 * JB_ERR_CGI_PARAMS if the CGI parameters are not
457 * specified or not valid.
459 *********************************************************************/
460 jb_err cgi_edit_actions_url_form(struct client_state *csp,
461 struct http_response *rsp,
462 const struct map *parameters)
464 struct map * exports;
466 struct editable_file * file;
467 struct file_line * cur_line;
468 unsigned line_number;
469 unsigned section_start_line_number = 0;
476 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
478 return cgi_error_disabled(csp, rsp);
481 err = get_number_param(csp, parameters, "p", &patternid);
487 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
490 /* No filename specified, can't read file, modified, or out of memory. */
491 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
494 cur_line = file->lines;
496 for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++)
498 if (cur_line->type == FILE_LINE_ACTION)
500 section_start_line_number = line_number;
502 cur_line = cur_line->next;
505 if ( (cur_line == NULL)
506 || (line_number != patternid)
508 || (cur_line->type != FILE_LINE_URL))
510 /* Invalid "patternid" parameter */
511 edit_free_file(file);
512 return JB_ERR_CGI_PARAMS;
515 if (NULL == (exports = default_exports(csp, NULL)))
517 edit_free_file(file);
518 return JB_ERR_MEMORY;
521 err = map(exports, "f", 1, stringify(file->identifier), 0);
522 if (!err) err = map(exports, "v", 1, file->version_str, 1);
523 if (!err) err = map(exports, "p", 1, url_encode(lookup(parameters, "p")), 0);
524 if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0);
525 if (!err) err = map(exports, "jumptarget", 1, section_target(section_start_line_number), 0);
527 edit_free_file(file);
535 return template_fill_for_cgi(csp, "edit-actions-url-form", exports, rsp);
539 /*********************************************************************
541 * Function : cgi_edit_actions_add_url_form
543 * Description : CGI function that displays a form for
547 * 1 : csp = Current client state (buffers, headers, etc...)
548 * 2 : rsp = http_response data structure for output
549 * 3 : parameters = map of cgi parameters
552 * f : (filename) Identifies the file to edit
553 * v : (version) File's last-modified time
554 * s : (section) Line number of section to edit
556 * Returns : JB_ERR_OK on success
557 * JB_ERR_MEMORY on out-of-memory
558 * JB_ERR_CGI_PARAMS if the CGI parameters are not
559 * specified or not valid.
561 *********************************************************************/
562 jb_err cgi_edit_actions_add_url_form(struct client_state *csp,
563 struct http_response *rsp,
564 const struct map *parameters)
573 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
575 return cgi_error_disabled(csp, rsp);
578 if (NULL == (exports = default_exports(csp, NULL)))
580 return JB_ERR_MEMORY;
583 err = map_copy_parameter_html(exports, parameters, "f");
584 if (!err) err = map_copy_parameter_html(exports, parameters, "v");
585 if (!err) err = map_copy_parameter_html(exports, parameters, "s");
593 return template_fill_for_cgi(csp, "edit-actions-add-url-form", exports, rsp);
597 /*********************************************************************
599 * Function : cgi_edit_actions_remove_url_form
601 * Description : CGI function that displays a form for
605 * 1 : csp = Current client state (buffers, headers, etc...)
606 * 2 : rsp = http_response data structure for output
607 * 3 : parameters = map of cgi parameters
610 * f : (number) The action file identifier.
611 * v : (version) File's last-modified time
612 * p : (pattern) Line number of pattern to edit
614 * Returns : JB_ERR_OK on success
615 * JB_ERR_MEMORY on out-of-memory
616 * JB_ERR_CGI_PARAMS if the CGI parameters are not
617 * specified or not valid.
619 *********************************************************************/
620 jb_err cgi_edit_actions_remove_url_form(struct client_state *csp,
621 struct http_response *rsp,
622 const struct map *parameters)
624 struct map * exports;
626 struct editable_file * file;
627 struct file_line * cur_line;
628 unsigned line_number;
629 unsigned section_start_line_number = 0;
636 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
638 return cgi_error_disabled(csp, rsp);
641 err = get_number_param(csp, parameters, "p", &patternid);
647 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
650 /* No filename specified, can't read file, modified, or out of memory. */
651 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
654 cur_line = file->lines;
656 for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++)
658 if (cur_line->type == FILE_LINE_ACTION)
660 section_start_line_number = line_number;
662 cur_line = cur_line->next;
665 if ( (cur_line == NULL)
666 || (line_number != patternid)
668 || (cur_line->type != FILE_LINE_URL))
670 /* Invalid "patternid" parameter */
671 edit_free_file(file);
672 return JB_ERR_CGI_PARAMS;
675 if (NULL == (exports = default_exports(csp, NULL)))
677 edit_free_file(file);
678 return JB_ERR_MEMORY;
681 err = map(exports, "f", 1, stringify(file->identifier), 0);
682 if (!err) err = map(exports, "v", 1, file->version_str, 1);
683 if (!err) err = map(exports, "p", 1, url_encode(lookup(parameters, "p")), 0);
684 if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0);
685 if (!err) err = map(exports, "jumptarget", 1, section_target(section_start_line_number), 0);
686 if (!err) err = map(exports, "actions-file", 1, html_encode(file->filename), 0);
688 edit_free_file(file);
696 return template_fill_for_cgi(csp, "edit-actions-remove-url-form", exports, rsp);
700 /*********************************************************************
702 * Function : edit_write_file
704 * Description : Write a complete file to disk.
707 * 1 : file = File to write.
709 * Returns : JB_ERR_OK on success
710 * JB_ERR_FILE on error writing to file.
711 * JB_ERR_MEMORY on out of memory
713 *********************************************************************/
714 jb_err edit_write_file(struct editable_file * file)
717 struct file_line * cur_line;
718 struct stat statbuf[1];
719 char version_buf[22]; /* 22 = ceil(log10(2^64)) + 2 = max number of
720 digits in time_t, assuming this is a 64-bit
721 machine, plus null terminator, plus one
725 assert(file->filename);
727 if (NULL == (fp = fopen(file->filename, "wb")))
732 cur_line = file->lines;
733 while (cur_line != NULL)
737 if (fputs(cur_line->raw, fp) < 0)
745 if (cur_line->prefix)
747 if (fputs(cur_line->prefix, fp) < 0)
753 if (cur_line->unprocessed)
756 if (NULL != strchr(cur_line->unprocessed, '#'))
758 /* Must quote '#' characters */
765 /* Count number of # characters, so we know length of output string */
766 src = cur_line->unprocessed;
767 while (NULL != (src = strchr(src, '#')))
774 /* Allocate new memory for string */
775 len = strlen(cur_line->unprocessed) + (size_t)numhash;
776 str = malloc_or_die(len + 1);
778 /* Copy string but quote hashes */
779 src = cur_line->unprocessed;
787 assert(numhash >= 0);
793 assert(numhash == 0);
794 assert(strlen(str) == len);
795 assert(str == dest - len);
796 assert(src - len <= cur_line->unprocessed);
798 if ((strlen(str) != len) || (numhash != 0))
801 * Escaping didn't work as expected, go spread the news.
802 * Only reached in non-debugging builds.
804 log_error(LOG_LEVEL_ERROR,
805 "Looks like hash escaping failed. %s might be corrupted now.",
809 if (fputs(str, fp) < 0)
820 /* Can write without quoting '#' characters. */
821 if (fputs(cur_line->unprocessed, fp) < 0)
827 if (fputs(NEWLINE(file->newline), fp) < 0)
835 /* FIXME: Write data from file->data->whatever */
839 cur_line = cur_line->next;
845 /* Update the version stamp in the file structure, since we just
846 * wrote to the file & changed it's date.
848 if (stat(file->filename, statbuf) < 0)
850 /* Error, probably file not found. */
853 file->version = (unsigned)statbuf->st_mtime;
855 /* Correct file->version_str */
856 freez(file->version_str);
857 snprintf(version_buf, sizeof(version_buf), "%u", file->version);
858 version_buf[sizeof(version_buf)-1] = '\0';
859 file->version_str = strdup_or_die(version_buf);
865 /*********************************************************************
867 * Function : edit_free_file
869 * Description : Free a complete file in memory.
872 * 1 : file = Data structure to free.
876 *********************************************************************/
877 void edit_free_file(struct editable_file * file)
881 /* Silently ignore NULL pointer */
885 edit_free_file_lines(file->lines);
886 freez(file->version_str);
888 file->parse_error_text = NULL; /* Statically allocated */
889 file->parse_error = NULL;
895 /*********************************************************************
897 * Function : edit_free_file_lines
899 * Description : Free an entire linked list of file lines.
902 * 1 : first_line = Data structure to free.
906 *********************************************************************/
907 static void edit_free_file_lines(struct file_line * first_line)
909 struct file_line * next_line;
911 while (first_line != NULL)
913 next_line = first_line->next;
914 first_line->next = NULL;
915 freez(first_line->raw);
916 freez(first_line->prefix);
917 freez(first_line->unprocessed);
918 switch(first_line->type)
920 case 0: /* special case if memory zeroed */
921 case FILE_LINE_UNPROCESSED:
922 case FILE_LINE_BLANK:
923 case FILE_LINE_ALIAS_HEADER:
924 case FILE_LINE_SETTINGS_HEADER:
925 case FILE_LINE_DESCRIPTION_HEADER:
926 case FILE_LINE_DESCRIPTION_ENTRY:
927 case FILE_LINE_ALIAS_ENTRY:
929 /* No data is stored for these */
932 case FILE_LINE_ACTION:
933 free_action(first_line->data.action);
936 case FILE_LINE_SETTINGS_ENTRY:
937 freez(first_line->data.setting.name);
938 freez(first_line->data.setting.svalue);
941 /* Should never happen */
945 first_line->type = 0; /* paranoia */
947 first_line = next_line;
952 /*********************************************************************
954 * Function : match_actions_file_header_line
956 * Description : Match an actions file {{header}} line
959 * 1 : line = String from file
960 * 2 : name = Header to match against
962 * Returns : 0 iff they match.
964 *********************************************************************/
965 static int match_actions_file_header_line(const char * line, const char * name)
973 if ((line[0] != '{') || (line[1] != '{'))
979 /* Look for optional whitespace */
980 while ((*line == ' ') || (*line == '\t'))
985 /* Look for the specified name (case-insensitive) */
987 if (0 != strncmpic(line, name, len))
993 /* Look for optional whitespace */
994 while ((*line == ' ') || (*line == '\t'))
999 /* Look for "}}" and end of string*/
1000 if ((line[0] != '}') || (line[1] != '}') || (line[2] != '\0'))
1010 /*********************************************************************
1012 * Function : match_actions_file_header_line
1014 * Description : Match an actions file {{header}} line
1017 * 1 : line = String from file. Must not start with
1018 * whitespace (else infinite loop!)
1019 * 2 : pname = Destination for name
1020 * 2 : pvalue = Destination for value
1022 * Returns : JB_ERR_OK on success
1023 * JB_ERR_MEMORY on out-of-memory
1024 * JB_ERR_PARSE if there's no "=" sign, or if there's
1025 * nothing before the "=" sign (but empty
1026 * values *after* the "=" sign are legal).
1028 *********************************************************************/
1029 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue)
1031 const char * name_end;
1032 const char * value_start;
1038 assert(*line != ' ');
1039 assert(*line != '\t');
1044 value_start = strchr(line, '=');
1045 if ((value_start == NULL) || (value_start == line))
1047 return JB_ERR_PARSE;
1050 name_end = value_start - 1;
1052 /* Eat any whitespace before the '=' */
1053 while ((*name_end == ' ') || (*name_end == '\t'))
1056 * we already know we must have at least 1 non-ws char
1057 * at start of buf - no need to check
1062 name_len = (size_t)(name_end - line) + 1; /* Length excluding \0 */
1063 *pname = malloc_or_die(name_len + 1);
1064 strncpy(*pname, line, name_len);
1065 (*pname)[name_len] = '\0';
1067 /* Eat any the whitespace after the '=' */
1069 while ((*value_start == ' ') || (*value_start == '\t'))
1074 if (NULL == (*pvalue = strdup(value_start)))
1078 return JB_ERR_MEMORY;
1085 /*********************************************************************
1087 * Function : edit_parse_actions_file
1089 * Description : Parse an actions file in memory.
1091 * Passed linked list must have the "data" member
1092 * zeroed, and must contain valid "next" and
1093 * "unprocessed" fields. The "raw" and "prefix"
1094 * fields are ignored, and "type" is just overwritten.
1096 * Note that on error the file may have been
1100 * 1 : file = Actions file to be parsed in-place.
1102 * Returns : JB_ERR_OK on success
1103 * JB_ERR_MEMORY on out-of-memory
1104 * JB_ERR_PARSE on error
1106 *********************************************************************/
1107 jb_err edit_parse_actions_file(struct editable_file * file)
1109 struct file_line * cur_line;
1111 const char * text; /* Text from a line */
1112 char * name; /* For lines of the form name=value */
1113 char * value; /* For lines of the form name=value */
1114 struct action_alias * alias_list = NULL;
1115 jb_err err = JB_ERR_OK;
1117 /* alias_list contains the aliases defined in this file.
1118 * It might be better to use the "file_line.data" fields
1119 * in the relevant places instead.
1122 cur_line = file->lines;
1124 /* A note about blank line support: Blank lines should only
1125 * ever occur as the last line in the file. This function
1126 * is more forgiving than that - FILE_LINE_BLANK can occur
1130 /* Skip leading blanks. Should only happen if file is
1131 * empty (which is valid, but pointless).
1133 while ((cur_line != NULL)
1134 && (cur_line->unprocessed[0] == '\0'))
1137 cur_line->type = FILE_LINE_BLANK;
1138 cur_line = cur_line->next;
1141 if ((cur_line != NULL)
1142 && (cur_line->unprocessed[0] != '{'))
1144 /* File doesn't start with a header */
1145 file->parse_error = cur_line;
1146 file->parse_error_text = "First (non-comment) line of the file must contain a header.";
1147 return JB_ERR_PARSE;
1150 if ((cur_line != NULL) && (0 ==
1151 match_actions_file_header_line(cur_line->unprocessed, "settings")))
1153 cur_line->type = FILE_LINE_SETTINGS_HEADER;
1155 cur_line = cur_line->next;
1156 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1158 if (cur_line->unprocessed[0])
1160 cur_line->type = FILE_LINE_SETTINGS_ENTRY;
1162 err = split_line_on_equals(cur_line->unprocessed,
1163 &cur_line->data.setting.name,
1164 &cur_line->data.setting.svalue);
1165 if (err == JB_ERR_MEMORY)
1169 else if (err != JB_ERR_OK)
1171 /* Line does not contain a name=value pair */
1172 file->parse_error = cur_line;
1173 file->parse_error_text = "Expected a name=value pair on this {{description}} line, but couldn't find one.";
1174 return JB_ERR_PARSE;
1179 cur_line->type = FILE_LINE_BLANK;
1181 cur_line = cur_line->next;
1185 if ((cur_line != NULL) && (0 ==
1186 match_actions_file_header_line(cur_line->unprocessed, "description")))
1188 cur_line->type = FILE_LINE_DESCRIPTION_HEADER;
1190 cur_line = cur_line->next;
1191 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1193 if (cur_line->unprocessed[0])
1195 cur_line->type = FILE_LINE_DESCRIPTION_ENTRY;
1199 cur_line->type = FILE_LINE_BLANK;
1201 cur_line = cur_line->next;
1205 if ((cur_line != NULL) && (0 ==
1206 match_actions_file_header_line(cur_line->unprocessed, "alias")))
1208 cur_line->type = FILE_LINE_ALIAS_HEADER;
1210 cur_line = cur_line->next;
1211 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1213 if (cur_line->unprocessed[0])
1215 /* define an alias */
1216 struct action_alias * new_alias;
1218 cur_line->type = FILE_LINE_ALIAS_ENTRY;
1220 err = split_line_on_equals(cur_line->unprocessed, &name, &value);
1221 if (err == JB_ERR_MEMORY)
1223 free_alias_list(alias_list);
1226 else if (err != JB_ERR_OK)
1228 /* Line does not contain a name=value pair */
1229 file->parse_error = cur_line;
1230 file->parse_error_text = "Expected a name=value pair on this {{alias}} line, but couldn't find one.";
1231 free_alias_list(alias_list);
1232 return JB_ERR_PARSE;
1235 new_alias = zalloc_or_die(sizeof(*new_alias));
1237 err = get_actions(value, alias_list, new_alias->action);
1240 /* Invalid action or out of memory */
1244 free_alias_list(alias_list);
1245 if (err == JB_ERR_MEMORY)
1251 /* Line does not contain a name=value pair */
1252 file->parse_error = cur_line;
1253 file->parse_error_text = "This alias does not specify a valid set of actions.";
1254 return JB_ERR_PARSE;
1260 new_alias->name = name;
1263 new_alias->next = alias_list;
1264 alias_list = new_alias;
1268 cur_line->type = FILE_LINE_BLANK;
1270 cur_line = cur_line->next;
1274 /* Header done, process the main part of the file */
1275 while (cur_line != NULL)
1277 /* At this point, (cur_line->unprocessed[0] == '{') */
1278 assert(cur_line->unprocessed[0] == '{');
1279 text = cur_line->unprocessed + 1;
1280 len = strlen(text) - 1;
1281 if (text[len] != '}')
1283 /* No closing } on header */
1284 free_alias_list(alias_list);
1285 file->parse_error = cur_line;
1286 file->parse_error_text = "Headers starting with '{' must have a "
1287 "closing bracket ('}'). Headers starting with two brackets ('{{') "
1288 "must close with two brackets ('}}').";
1289 return JB_ERR_PARSE;
1294 /* An invalid {{ header. */
1295 free_alias_list(alias_list);
1296 file->parse_error = cur_line;
1297 file->parse_error_text = "Unknown or unexpected two-bracket header. "
1298 "Please remember that the system (two-bracket) headers must "
1299 "appear in the order {{settings}}, {{description}}, {{alias}}, "
1300 "and must appear before any actions (one-bracket) headers. "
1301 "Also note that system headers may not be repeated.";
1302 return JB_ERR_PARSE;
1305 while ((*text == ' ') || (*text == '\t'))
1310 while ((len > (size_t)0)
1311 && ((text[len - 1] == ' ')
1312 || (text[len - 1] == '\t')))
1317 cur_line->type = FILE_LINE_ACTION;
1319 /* Remove {} and make copy */
1320 value = malloc_or_die(len + 1);
1321 strncpy(value, text, len);
1325 err = get_actions(value, alias_list, cur_line->data.action);
1328 /* Invalid action or out of memory */
1330 free_alias_list(alias_list);
1331 if (err == JB_ERR_MEMORY)
1337 /* Line does not contain a name=value pair */
1338 file->parse_error = cur_line;
1339 file->parse_error_text = "This header does not specify a valid set of actions.";
1340 return JB_ERR_PARSE;
1344 /* Done with string - it was clobbered anyway */
1347 /* Process next line */
1348 cur_line = cur_line->next;
1350 /* Loop processing URL patterns */
1351 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1353 if (cur_line->unprocessed[0])
1355 /* Could parse URL here, but this isn't currently needed */
1357 cur_line->type = FILE_LINE_URL;
1361 cur_line->type = FILE_LINE_BLANK;
1363 cur_line = cur_line->next;
1365 } /* End main while(cur_line != NULL) loop */
1367 free_alias_list(alias_list);
1373 /*********************************************************************
1375 * Function : edit_read_file_lines
1377 * Description : Read all the lines of a file into memory.
1378 * Handles whitespace, comments and line continuation.
1381 * 1 : fp = File to read from. On return, this will be
1382 * at EOF but it will not have been closed.
1383 * 2 : pfile = Destination for a linked list of file_lines.
1384 * Will be set to NULL on error.
1385 * 3 : newline = How to handle newlines.
1387 * Returns : JB_ERR_OK on success
1388 * JB_ERR_MEMORY on out-of-memory
1390 *********************************************************************/
1391 jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline)
1393 struct file_line * first_line; /* Keep for return value or to free */
1394 struct file_line * cur_line; /* Current line */
1395 struct file_line * prev_line; /* Entry with prev_line->next = cur_line */
1403 cur_line = first_line = zalloc_or_die(sizeof(struct file_line));
1405 cur_line->type = FILE_LINE_UNPROCESSED;
1407 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed, newline, NULL);
1410 /* Out of memory or empty file. */
1411 /* Note that empty file is not an error we propagate up */
1413 return ((rval == JB_ERR_FILE) ? JB_ERR_OK : rval);
1418 prev_line = cur_line;
1419 cur_line = prev_line->next = zalloc_or_die(sizeof(struct file_line));
1421 cur_line->type = FILE_LINE_UNPROCESSED;
1423 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed, newline, NULL);
1424 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
1427 edit_free_file_lines(first_line);
1428 return JB_ERR_MEMORY;
1432 while (rval != JB_ERR_FILE);
1436 /* We allocated one too many - free it */
1437 prev_line->next = NULL;
1440 *pfile = first_line;
1445 /*********************************************************************
1447 * Function : edit_read_file
1449 * Description : Read a complete file into memory.
1450 * Handles CGI parameter parsing. If requested, also
1451 * checks the file's modification timestamp.
1454 * 1 : csp = Current client state (buffers, headers, etc...)
1455 * 2 : parameters = map of cgi parameters.
1456 * 3 : require_version = true to check "ver" parameter.
1457 * 4 : pfile = Destination for the file. Will be set
1461 * f : The action file identifier.
1462 * ver : (Only if require_version is nonzero)
1463 * Timestamp of the actions file. If wrong, this
1464 * function fails with JB_ERR_MODIFIED.
1466 * Returns : JB_ERR_OK on success
1467 * JB_ERR_MEMORY on out-of-memory
1468 * JB_ERR_CGI_PARAMS if "filename" was not specified
1470 * JB_ERR_FILE if the file cannot be opened or
1472 * JB_ERR_MODIFIED if version checking was requested and
1473 * failed - the file was modified outside
1474 * of this CGI editor instance.
1476 *********************************************************************/
1477 jb_err edit_read_file(struct client_state *csp,
1478 const struct map *parameters,
1479 int require_version,
1480 struct editable_file **pfile)
1482 struct file_line * lines;
1485 const char *filename = NULL;
1486 struct editable_file * file;
1487 unsigned version = 0;
1488 struct stat statbuf[1];
1489 char version_buf[22];
1490 int newline = NEWLINE_UNKNOWN;
1499 err = get_number_param(csp, parameters, "f", &i);
1500 if ((JB_ERR_OK == err) && (i < MAX_AF_FILES) && (NULL != csp->config->actions_file[i]))
1502 filename = csp->config->actions_file[i];
1504 else if (JB_ERR_CGI_PARAMS == err)
1507 * Probably an old-school URL like
1508 * http://config.privoxy.org/edit-actions-list?f=default
1510 get_file_name_param(csp, parameters, "f", &filename);
1513 if (NULL == filename || stat(filename, statbuf) < 0)
1515 /* Error, probably file not found. */
1518 version = (unsigned) statbuf->st_mtime;
1520 if (require_version)
1522 unsigned specified_version;
1523 err = get_number_param(csp, parameters, "v", &specified_version);
1529 if (version != specified_version)
1531 return JB_ERR_MODIFIED;
1535 if (NULL == (fp = fopen(filename,"rb")))
1540 err = edit_read_file_lines(fp, &lines, &newline);
1549 file = zalloc_or_die(sizeof(*file));
1551 file->lines = lines;
1552 file->newline = newline;
1553 file->filename = filename;
1554 file->version = version;
1555 file->identifier = i;
1557 /* Correct file->version_str */
1558 freez(file->version_str);
1559 snprintf(version_buf, sizeof(version_buf), "%u", file->version);
1560 version_buf[sizeof(version_buf)-1] = '\0';
1561 file->version_str = strdup_or_die(version_buf);
1568 /*********************************************************************
1570 * Function : edit_read_actions_file
1572 * Description : Read a complete actions file into memory.
1573 * Handles CGI parameter parsing. If requested, also
1574 * checks the file's modification timestamp.
1576 * If this function detects an error in the categories
1577 * JB_ERR_FILE, JB_ERR_MODIFIED, or JB_ERR_PARSE,
1578 * then it handles it by filling in the specified
1579 * response structure and returning JB_ERR_FILE.
1582 * 1 : csp = Current client state (buffers, headers, etc...)
1583 * 2 : rsp = HTTP response. Only filled in on error.
1584 * 2 : parameters = map of cgi parameters.
1585 * 3 : require_version = true to check "ver" parameter.
1586 * 4 : pfile = Destination for the file. Will be set
1590 * f : The actions file identifier.
1591 * ver : (Only if require_version is nonzero)
1592 * Timestamp of the actions file. If wrong, this
1593 * function fails with JB_ERR_MODIFIED.
1595 * Returns : JB_ERR_OK on success
1596 * JB_ERR_MEMORY on out-of-memory
1597 * JB_ERR_CGI_PARAMS if "filename" was not specified
1599 * JB_ERR_FILE if the file does not contain valid data,
1600 * or if file cannot be opened or
1601 * contains no data, or if version
1602 * checking was requested and failed.
1604 *********************************************************************/
1605 jb_err edit_read_actions_file(struct client_state *csp,
1606 struct http_response *rsp,
1607 const struct map *parameters,
1608 int require_version,
1609 struct editable_file **pfile)
1612 struct editable_file *file;
1613 static int acceptable_failures = ACCEPTABLE_TIMESTAMP_MISMATCHES - 1;
1621 err = edit_read_file(csp, parameters, require_version, &file);
1624 /* Try to handle if possible */
1625 if (err == JB_ERR_FILE)
1627 err = cgi_error_file(csp, rsp, lookup(parameters, "f"));
1629 else if (err == JB_ERR_MODIFIED)
1631 assert(require_version);
1632 err = cgi_error_modified(csp, rsp, lookup(parameters, "f"));
1633 log_error(LOG_LEVEL_ERROR,
1634 "Blocking CGI edit request due to modification time mismatch.");
1635 if (acceptable_failures > 0)
1637 log_error(LOG_LEVEL_INFO,
1638 "The CGI editor will be turned off after another %d mismatche(s).",
1639 acceptable_failures);
1640 acceptable_failures--;
1644 log_error(LOG_LEVEL_INFO,
1645 "Timestamp mismatch limit reached, turning CGI editor off. "
1646 "Reload the configuration file to re-enable it.");
1647 csp->config->feature_flags &= ~RUNTIME_FEATURE_CGI_EDIT_ACTIONS;
1650 if (err == JB_ERR_OK)
1653 * Signal to higher-level CGI code that there was a problem but we
1654 * handled it, they should just return JB_ERR_OK.
1661 err = edit_parse_actions_file(file);
1664 if (err == JB_ERR_PARSE)
1666 err = cgi_error_parse(csp, rsp, file);
1667 if (err == JB_ERR_OK)
1670 * Signal to higher-level CGI code that there was a problem but we
1671 * handled it, they should just return JB_ERR_OK.
1676 edit_free_file(file);
1685 /*********************************************************************
1687 * Function : get_file_name_param
1689 * Description : Get the name of the file to edit from the parameters
1690 * passed to a CGI function using the old syntax.
1691 * This function handles security checks and only
1692 * accepts files that Privoxy already knows.
1695 * 1 : csp = Current client state (buffers, headers, etc...)
1696 * 2 : parameters = map of cgi parameters
1697 * 3 : param_name = The name of the parameter to read
1698 * 4 : pfilename = pointer to the filename in
1699 * csp->config->actions_file[] if found. Set to NULL on error.
1701 * Returns : JB_ERR_OK on success
1702 * JB_ERR_MEMORY on out-of-memory
1703 * JB_ERR_CGI_PARAMS if "filename" was not specified
1706 *********************************************************************/
1707 static jb_err get_file_name_param(struct client_state *csp,
1708 const struct map *parameters,
1709 const char *param_name,
1710 const char **pfilename)
1713 const char suffix[] = ".action";
1728 param = lookup(parameters, param_name);
1731 return JB_ERR_CGI_PARAMS;
1734 len = strlen(param);
1735 if (len >= FILENAME_MAX)
1738 return JB_ERR_CGI_PARAMS;
1742 * Check every character to see if it's legal.
1743 * Totally unnecessary but we do it anyway.
1746 while ((ch = *s++) != '\0')
1748 if ( ((ch < 'A') || (ch > 'Z'))
1749 && ((ch < 'a') || (ch > 'z'))
1750 && ((ch < '0') || (ch > '9'))
1754 /* Probable hack attempt. */
1755 return JB_ERR_CGI_PARAMS;
1759 /* Append extension */
1760 name_size = len + strlen(suffix) + 1;
1761 name = malloc_or_die(name_size);
1762 strlcpy(name, param, name_size);
1763 strlcat(name, suffix, name_size);
1766 fullpath = make_path(csp->config->confdir, name);
1769 if (fullpath == NULL)
1771 return JB_ERR_MEMORY;
1774 /* Check if the file is known */
1775 for (i = 0; i < MAX_AF_FILES; i++)
1777 if (NULL != csp->config->actions_file[i] &&
1778 !strcmp(fullpath, csp->config->actions_file[i]))
1781 *pfilename = csp->config->actions_file[i];
1789 return JB_ERR_CGI_PARAMS;
1793 /*********************************************************************
1795 * Function : get_url_spec_param
1797 * Description : Get a URL pattern from the parameters
1798 * passed to a CGI function. Removes leading/trailing
1799 * spaces and validates it.
1802 * 1 : csp = Current client state (buffers, headers, etc...)
1803 * 2 : parameters = map of cgi parameters
1804 * 3 : name = Name of CGI parameter to read
1805 * 4 : pvalue = destination for value. Will be malloc()'d.
1806 * Set to NULL on error.
1808 * Returns : JB_ERR_OK on success
1809 * JB_ERR_MEMORY on out-of-memory
1810 * JB_ERR_CGI_PARAMS if the parameter was not specified
1813 *********************************************************************/
1814 static jb_err get_url_spec_param(struct client_state *csp,
1815 const struct map *parameters,
1819 const char *orig_param;
1822 struct pattern_spec compiled[1];
1832 orig_param = lookup(parameters, name);
1835 return JB_ERR_CGI_PARAMS;
1838 /* Copy and trim whitespace */
1839 param = strdup(orig_param);
1842 return JB_ERR_MEMORY;
1846 /* Must be non-empty, and can't allow 1st character to be '{' */
1847 if (param[0] == '\0' || param[0] == '{')
1850 return JB_ERR_CGI_PARAMS;
1853 /* Check for embedded newlines */
1854 for (s = param; *s != '\0'; s++)
1856 if ((*s == '\r') || (*s == '\n'))
1859 return JB_ERR_CGI_PARAMS;
1863 /* Check that regex is valid */
1868 return JB_ERR_MEMORY;
1870 err = create_pattern_spec(compiled, s);
1875 return (err == JB_ERR_MEMORY) ? JB_ERR_MEMORY : JB_ERR_CGI_PARAMS;
1877 free_pattern_spec(compiled);
1879 if (param[strlen(param) - 1] == '\\')
1882 * Must protect trailing '\\' from becoming line continuation character.
1883 * Two methods: 1) If it's a domain only, add a trailing '/'.
1884 * 2) For path, add the do-nothing PCRE expression (?:) to the end
1886 if (strchr(param, '/') == NULL)
1888 err = string_append(¶m, "/");
1892 err = string_append(¶m, "(?:)");
1899 /* Check that the modified regex is valid */
1904 return JB_ERR_MEMORY;
1906 err = create_pattern_spec(compiled, s);
1911 return (err == JB_ERR_MEMORY) ? JB_ERR_MEMORY : JB_ERR_CGI_PARAMS;
1913 free_pattern_spec(compiled);
1920 /*********************************************************************
1922 * Function : map_radio
1924 * Description : Map a set of radio button values. E.g. if you have
1925 * 3 radio buttons, declare them as:
1926 * <option type="radio" name="xyz" @xyz-a@>
1927 * <option type="radio" name="xyz" @xyz-b@>
1928 * <option type="radio" name="xyz" @xyz-c@>
1929 * Then map one of the @xyz-?@ variables to "checked"
1930 * and all the others to empty by calling:
1931 * map_radio(exports, "xyz", "abc", sel)
1932 * Where 'sel' is 'a', 'b', or 'c'.
1935 * 1 : exports = Exports map to modify.
1936 * 2 : optionname = name for map
1937 * 3 : values = null-terminated list of values;
1938 * 4 : value = Selected value.
1940 * CGI Parameters : None
1942 * Returns : JB_ERR_OK on success
1943 * JB_ERR_MEMORY on out-of-memory
1945 *********************************************************************/
1946 static jb_err map_radio(struct map * exports,
1947 const char * optionname,
1948 const char * values,
1954 const size_t len = strlen(optionname);
1955 const size_t buf_size = len + 3;
1961 buf = malloc_or_die(buf_size);
1963 strlcpy(buf, optionname, buf_size);
1965 /* XXX: this looks ... interesting */
1970 while ((c = *values++) != '\0')
1975 if (map(exports, buf, 1, "", 1))
1977 return JB_ERR_MEMORY;
1983 return map(exports, buf, 0, "checked", 1);
1987 /*********************************************************************
1989 * Function : cgi_error_modified
1991 * Description : CGI function that is called when a file is modified
1992 * outside the CGI editor.
1995 * 1 : csp = Current client state (buffers, headers, etc...)
1996 * 2 : rsp = http_response data structure for output
1997 * 3 : filename = The file that was modified.
1999 * CGI Parameters : none
2001 * Returns : JB_ERR_OK on success
2002 * JB_ERR_MEMORY on out-of-memory error.
2004 *********************************************************************/
2005 jb_err cgi_error_modified(struct client_state *csp,
2006 struct http_response *rsp,
2007 const char *filename)
2009 struct map *exports;
2016 if (NULL == (exports = default_exports(csp, NULL)))
2018 return JB_ERR_MEMORY;
2021 err = map(exports, "f", 1, html_encode(filename), 0);
2028 return template_fill_for_cgi(csp, "cgi-error-modified", exports, rsp);
2032 /*********************************************************************
2034 * Function : cgi_error_parse
2036 * Description : CGI function that is called when a file cannot
2037 * be parsed by the CGI editor.
2040 * 1 : csp = Current client state (buffers, headers, etc...)
2041 * 2 : rsp = http_response data structure for output
2042 * 3 : file = The file that was modified.
2044 * CGI Parameters : none
2046 * Returns : JB_ERR_OK on success
2047 * JB_ERR_MEMORY on out-of-memory error.
2049 *********************************************************************/
2050 jb_err cgi_error_parse(struct client_state *csp,
2051 struct http_response *rsp,
2052 struct editable_file *file)
2054 struct map *exports;
2056 struct file_line *cur_line;
2062 if (NULL == (exports = default_exports(csp, NULL)))
2064 return JB_ERR_MEMORY;
2067 err = map(exports, "f", 1, stringify(file->identifier), 0);
2068 if (!err) err = map(exports, "parse-error", 1, html_encode(file->parse_error_text), 0);
2070 cur_line = file->parse_error;
2073 if (!err) err = map(exports, "line-raw", 1, html_encode(cur_line->raw), 0);
2074 if (!err) err = map(exports, "line-data", 1, html_encode(cur_line->unprocessed), 0);
2082 return template_fill_for_cgi(csp, "cgi-error-parse", exports, rsp);
2086 /*********************************************************************
2088 * Function : cgi_error_file
2090 * Description : CGI function that is called when a file cannot be
2091 * opened by the CGI editor.
2094 * 1 : csp = Current client state (buffers, headers, etc...)
2095 * 2 : rsp = http_response data structure for output
2096 * 3 : filename = The file that was modified.
2098 * CGI Parameters : none
2100 * Returns : JB_ERR_OK on success
2101 * JB_ERR_MEMORY on out-of-memory error.
2103 *********************************************************************/
2104 jb_err cgi_error_file(struct client_state *csp,
2105 struct http_response *rsp,
2106 const char *filename)
2108 struct map *exports;
2115 if (NULL == (exports = default_exports(csp, NULL)))
2117 return JB_ERR_MEMORY;
2120 err = map(exports, "f", 1, html_encode(filename), 0);
2127 return template_fill_for_cgi(csp, "cgi-error-file", exports, rsp);
2131 /*********************************************************************
2133 * Function : cgi_error_file_read_only
2135 * Description : CGI function that is called when a file cannot be
2136 * opened for writing by the CGI editor.
2139 * 1 : csp = Current client state (buffers, headers, etc...)
2140 * 2 : rsp = http_response data structure for output
2141 * 3 : filename = The file that we can't write to
2143 * CGI Parameters : none
2145 * Returns : JB_ERR_OK on success
2146 * JB_ERR_MEMORY on out-of-memory error.
2148 *********************************************************************/
2149 jb_err cgi_error_file_read_only(struct client_state *csp,
2150 struct http_response *rsp,
2151 const char *filename)
2153 struct map *exports;
2160 if (NULL == (exports = default_exports(csp, NULL)))
2162 return JB_ERR_MEMORY;
2165 err = map(exports, "f", 1, html_encode(filename), 0);
2172 return template_fill_for_cgi(csp, "cgi-error-file-read-only", exports, rsp);
2176 /*********************************************************************
2178 * Function : cgi_edit_actions
2180 * Description : CGI function that allows the user to choose which
2181 * actions file to edit.
2184 * 1 : csp = Current client state (buffers, headers, etc...)
2185 * 2 : rsp = http_response data structure for output
2186 * 3 : parameters = map of cgi parameters
2188 * CGI Parameters : None
2190 * Returns : JB_ERR_OK on success
2191 * JB_ERR_MEMORY on out-of-memory error
2193 *********************************************************************/
2194 jb_err cgi_edit_actions(struct client_state *csp,
2195 struct http_response *rsp,
2196 const struct map *parameters)
2200 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2202 return cgi_error_disabled(csp, rsp);
2205 /* FIXME: Incomplete */
2207 return cgi_redirect(rsp, CGI_PREFIX "edit-actions-list?f=default");
2212 /*********************************************************************
2214 * Function : cgi_edit_actions_list
2216 * Description : CGI function that edits the actions list.
2217 * FIXME: This function shouldn't FATAL ever.
2218 * FIXME: This function doesn't check the retval of map()
2220 * 1 : csp = Current client state (buffers, headers, etc...)
2221 * 2 : rsp = http_response data structure for output
2222 * 3 : parameters = map of cgi parameters
2224 * CGI Parameters : filename
2226 * Returns : JB_ERR_OK on success
2227 * JB_ERR_MEMORY on out-of-memory
2228 * JB_ERR_FILE if the file cannot be opened or
2230 * JB_ERR_CGI_PARAMS if "filename" was not specified
2233 *********************************************************************/
2234 jb_err cgi_edit_actions_list(struct client_state *csp,
2235 struct http_response *rsp,
2236 const struct map *parameters)
2238 char * section_template;
2239 char * url_template;
2244 struct map * exports;
2245 struct map * section_exports;
2246 struct map * url_exports;
2247 struct editable_file * file;
2248 struct file_line * cur_line;
2249 unsigned line_number = 0;
2250 unsigned prev_section_line_number = ((unsigned) (-1));
2252 struct file_list * fl;
2253 struct url_actions * b;
2254 char * buttons = NULL;
2257 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2259 return cgi_error_disabled(csp, rsp);
2262 if (NULL == (exports = default_exports(csp, NULL)))
2264 return JB_ERR_MEMORY;
2267 /* Load actions file */
2268 err = edit_read_actions_file(csp, rsp, parameters, 0, &file);
2271 /* No filename specified, can't read file, or out of memory. */
2273 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2276 /* Find start of actions in file */
2277 cur_line = file->lines;
2279 while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION))
2281 cur_line = cur_line->next;
2286 * Conventional actions files should have a match all block
2288 * cur_line = {...global actions...}
2289 * cur_line->next = /
2290 * cur_line->next->next = {...actions...} or EOF
2292 if ( (cur_line != NULL)
2293 && (cur_line->type == FILE_LINE_ACTION)
2294 && (cur_line->next != NULL)
2295 && (cur_line->next->type == FILE_LINE_URL)
2296 && (0 == strcmp(cur_line->next->unprocessed, "/"))
2297 && ( (cur_line->next->next == NULL)
2298 || (cur_line->next->next->type != FILE_LINE_URL)
2302 * Generate string with buttons to set actions for "/" to
2303 * any predefined set of actions (named standard.*, probably
2304 * residing in standard.action).
2307 err = template_load(csp, §ion_template, "edit-actions-list-button", 0);
2310 edit_free_file(file);
2312 if (err == JB_ERR_FILE)
2314 return cgi_error_no_template(csp, rsp, "edit-actions-list-button");
2319 err = template_fill(§ion_template, exports);
2322 edit_free_file(file);
2327 buttons = strdup("");
2328 for (i = 0; i < MAX_AF_FILES; i++)
2330 if (((fl = csp->actions_list[i]) != NULL) && ((b = fl->f) != NULL))
2332 for (b = b->next; NULL != b; b = b->next)
2334 if (!strncmp(b->url->spec, "standard.", 9) && *(b->url->spec + 9) != '\0')
2339 free(section_template);
2340 edit_free_file(file);
2342 return JB_ERR_MEMORY;
2345 section_exports = new_map();
2346 err = map(section_exports, "button-name", 1, b->url->spec + 9, 1);
2348 if (err || (NULL == (s = strdup(section_template))))
2350 free_map(section_exports);
2352 free(section_template);
2353 edit_free_file(file);
2355 return JB_ERR_MEMORY;
2358 if (!err) err = template_fill(&s, section_exports);
2359 free_map(section_exports);
2360 if (!err) err = string_join(&buttons, s);
2365 freez(section_template);
2366 if (!err) err = map(exports, "all-urls-buttons", 1, buttons, 0);
2369 * Conventional actions file, supply extra editing help.
2370 * (e.g. don't allow them to make it an unconventional one).
2372 if (!err) err = map_conditional(exports, "all-urls-present", 1);
2374 snprintf(buf, sizeof(buf), "%u", line_number);
2375 if (!err) err = map(exports, "all-urls-s", 1, buf, 1);
2376 snprintf(buf, sizeof(buf), "%u", line_number + 2);
2377 if (!err) err = map(exports, "all-urls-s-next", 1, buf, 1);
2378 if (!err) err = map(exports, "all-urls-actions", 1,
2379 actions_to_html(csp, cur_line->data.action), 0);
2381 /* Skip the 2 lines */
2382 cur_line = cur_line->next->next;
2386 * Note that prev_section_line_number is NOT set here.
2387 * This is deliberate and not a bug. It stops a "Move up"
2388 * option appearing on the next section. Clicking "Move
2389 * up" would make the actions file unconventional, which
2390 * we don't want, so we hide this option.
2396 * Non-standard actions file - does not begin with
2397 * the "All URLs" section.
2399 if (!err) err = map_conditional(exports, "all-urls-present", 0);
2402 /* Set up global exports */
2404 if (!err) err = map(exports, "actions-file", 1, html_encode(file->filename), 0);
2405 if (!err) err = map(exports, "f", 1, stringify(file->identifier), 0);
2406 if (!err) err = map(exports, "v", 1, file->version_str, 1);
2408 /* Discourage private additions to default.action */
2410 if (!err) err = map_conditional(exports, "default-action",
2411 (strstr("default.action", file->filename) != NULL));
2414 edit_free_file(file);
2419 /* Should do all global exports above this point */
2421 /* Load templates */
2423 err = template_load(csp, §ion_template, "edit-actions-list-section", 0);
2426 edit_free_file(file);
2428 if (err == JB_ERR_FILE)
2430 return cgi_error_no_template(csp, rsp, "edit-actions-list-section");
2435 err = template_load(csp, &url_template, "edit-actions-list-url", 0);
2438 free(section_template);
2439 edit_free_file(file);
2441 if (err == JB_ERR_FILE)
2443 return cgi_error_no_template(csp, rsp, "edit-actions-list-url");
2448 err = template_fill(§ion_template, exports);
2452 edit_free_file(file);
2457 err = template_fill(&url_template, exports);
2460 free(section_template);
2461 edit_free_file(file);
2466 if (NULL == (sections = strdup("")))
2468 free(section_template);
2470 edit_free_file(file);
2472 return JB_ERR_MEMORY;
2475 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_ACTION))
2477 section_exports = new_map();
2479 snprintf(buf, sizeof(buf), "%u", line_number);
2480 err = map(section_exports, "s", 1, buf, 1);
2481 if (!err) err = map(section_exports, "actions", 1,
2482 actions_to_html(csp, cur_line->data.action), 0);
2485 && (cur_line->next != NULL)
2486 && (cur_line->next->type == FILE_LINE_URL))
2488 /* This section contains at least one URL, don't allow delete */
2489 err = map_block_killer(section_exports, "empty-section");
2493 if (!err) err = map_block_keep(section_exports, "empty-section");
2496 if (prev_section_line_number != ((unsigned)(-1)))
2498 /* Not last section */
2499 snprintf(buf, sizeof(buf), "%u", prev_section_line_number);
2500 if (!err) err = map(section_exports, "s-prev", 1, buf, 1);
2501 if (!err) err = map_block_keep(section_exports, "s-prev-exists");
2506 if (!err) err = map_block_killer(section_exports, "s-prev-exists");
2508 prev_section_line_number = line_number;
2513 free(section_template);
2515 edit_free_file(file);
2517 free_map(section_exports);
2521 /* Should do all section-specific exports above this point */
2523 if (NULL == (urls = strdup("")))
2526 free(section_template);
2528 edit_free_file(file);
2530 free_map(section_exports);
2531 return JB_ERR_MEMORY;
2536 cur_line = cur_line->next;
2539 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL))
2541 url_exports = new_map();
2543 snprintf(buf, sizeof(buf), "%u", line_number);
2544 err = map(url_exports, "p", 1, buf, 1);
2546 snprintf(buf, sizeof(buf), "%d", url_1_2);
2547 if (!err) err = map(url_exports, "url-1-2", 1, buf, 1);
2549 if (!err) err = map(url_exports, "url-html", 1,
2550 html_encode(cur_line->unprocessed), 0);
2551 if (!err) err = map(url_exports, "url", 1,
2552 url_encode(cur_line->unprocessed), 0);
2558 free(section_template);
2560 edit_free_file(file);
2562 free_map(section_exports);
2563 free_map(url_exports);
2567 if (NULL == (s = strdup(url_template)))
2571 free(section_template);
2573 edit_free_file(file);
2575 free_map(section_exports);
2576 free_map(url_exports);
2577 return JB_ERR_MEMORY;
2580 err = template_fill(&s, section_exports);
2581 if (!err) err = template_fill(&s, url_exports);
2582 if (!err) err = string_append(&urls, s);
2584 free_map(url_exports);
2591 free(section_template);
2593 edit_free_file(file);
2595 free_map(section_exports);
2599 url_1_2 = 3 - url_1_2;
2601 cur_line = cur_line->next;
2605 err = map(section_exports, "urls", 1, urls, 0);
2607 /* Could also do section-specific exports here, but it wouldn't be as fast */
2609 snprintf(buf, sizeof(buf), "%u", line_number);
2610 if (!err) err = map(section_exports, "s-next", 1, buf, 1);
2612 if ((cur_line != NULL)
2613 && (cur_line->type == FILE_LINE_ACTION))
2615 /* Not last section */
2616 if (!err) err = map_block_keep(section_exports, "s-next-exists");
2621 if (!err) err = map_block_killer(section_exports, "s-next-exists");
2627 free(section_template);
2629 edit_free_file(file);
2631 free_map(section_exports);
2635 if (NULL == (s = strdup(section_template)))
2638 free(section_template);
2640 edit_free_file(file);
2642 free_map(section_exports);
2643 return JB_ERR_MEMORY;
2646 err = template_fill(&s, section_exports);
2647 if (!err) err = string_append(§ions, s);
2650 free_map(section_exports);
2655 free(section_template);
2657 edit_free_file(file);
2663 edit_free_file(file);
2664 free(section_template);
2667 err = map(exports, "sections", 1, sections, 0);
2674 /* Could also do global exports here, but it wouldn't be as fast */
2676 return template_fill_for_cgi(csp, "edit-actions-list", exports, rsp);
2680 /*********************************************************************
2682 * Function : cgi_edit_actions_for_url
2684 * Description : CGI function that edits the Actions list.
2687 * 1 : csp = Current client state (buffers, headers, etc...)
2688 * 2 : rsp = http_response data structure for output
2689 * 3 : parameters = map of cgi parameters
2691 * CGI Parameters : None
2693 * Returns : JB_ERR_OK on success
2694 * JB_ERR_MEMORY on out-of-memory
2695 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2696 * specified or not valid.
2698 *********************************************************************/
2699 jb_err cgi_edit_actions_for_url(struct client_state *csp,
2700 struct http_response *rsp,
2701 const struct map *parameters)
2703 struct map * exports;
2704 char *filter_template;
2706 struct editable_file * file;
2707 struct file_line * cur_line;
2708 unsigned line_number;
2710 struct re_filterfile_spec *filter_group;
2711 int i, have_filters = 0;
2713 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2715 return cgi_error_disabled(csp, rsp);
2718 err = get_number_param(csp, parameters, "s", §ionid);
2724 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2727 /* No filename specified, can't read file, modified, or out of memory. */
2728 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2731 cur_line = file->lines;
2733 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
2735 cur_line = cur_line->next;
2738 if ( (cur_line == NULL)
2739 || (line_number != sectionid)
2741 || (cur_line->type != FILE_LINE_ACTION))
2743 /* Invalid "sectionid" parameter */
2744 edit_free_file(file);
2745 return JB_ERR_CGI_PARAMS;
2748 if (NULL == (exports = default_exports(csp, NULL)))
2750 edit_free_file(file);
2751 return JB_ERR_MEMORY;
2754 err = template_load(csp, &filter_template, "edit-actions-for-url-string-filter", 0);
2757 edit_free_file(file);
2759 return cgi_error_no_template(csp, rsp, "edit-actions-for-url-string-filter");
2762 err = map(exports, "f", 1, stringify(file->identifier), 0);
2763 if (!err) err = map(exports, "v", 1, file->version_str, 1);
2764 if (!err) err = map(exports, "s", 1, url_encode(lookup(parameters, "s")), 0);
2766 if (!err) err = actions_to_radio(exports, cur_line->data.action);
2768 if (!err) err = action_render_string_filters_template(exports, cur_line->data.action, filter_template,
2769 &filter_type_info[FT_SUPPRESS_TAG]);
2770 freez(filter_template);
2773 * XXX: Some browsers (at least IE6 and IE7) have an artificial URL
2774 * length limitation and ignore clicks on the Submit buttons if
2775 * the resulting GET URL would be longer than their limit.
2777 * In Privoxy 3.0.5 beta the standard edit-actions-for-url template
2778 * reached this limit and action editing stopped working in these
2779 * browsers (BR #1570678).
2781 * The config option split-large-forms works around this browser
2782 * bug (HTTP has no URL length limitation) by dividing the action
2783 * list form into multiple smaller ones. It means the URLs are shorter
2784 * and work in broken browsers as well, but the user can no longer change
2785 * all actions with one submit.
2787 * A better solution would be to switch to POST requests,
2788 * but this will do for now.
2790 if (!err && (csp->config->feature_flags & RUNTIME_FEATURE_SPLIT_LARGE_FORMS))
2792 /* Generate multiple smaller form by killing the big one. */
2793 err = map_block_killer(exports, "one-form-only");
2797 /* Default: Generate one large form by killing the smaller ones. */
2798 err = map_block_killer(exports, "multiple-forms");
2801 for (i = 0; i < MAX_AF_FILES; i++)
2803 if ((csp->rlist[i] != NULL) && (csp->rlist[i]->f != NULL))
2805 if (!err) err = map_conditional(exports, "any-filters-defined", 1);
2811 #ifndef FEATURE_EXTERNAL_FILTERS
2812 if (!err) err = map_block_killer(exports, "external-content-filters");
2814 #ifndef FEATURE_HTTPS_INSPECTION
2815 if (!err) err = map_block_killer(exports, "https-inspection");
2820 edit_free_file(file);
2825 if (0 == have_filters)
2827 err = map(exports, "filter-params", 1, "", 1);
2832 * List available filters and their settings.
2834 int filter_identifier = 0;
2835 char *prepared_templates[MAX_FILTER_TYPES];
2837 for (i = 0; i < MAX_FILTER_TYPES; i++)
2839 prepared_templates[i] = strdup("");
2842 err = template_load(csp, &filter_template, "edit-actions-for-url-filter", 0);
2845 edit_free_file(file);
2847 if (err == JB_ERR_FILE)
2849 return cgi_error_no_template(csp, rsp, "edit-actions-for-url-filter");
2854 err = template_fill(&filter_template, exports);
2856 for (i = 0; i < MAX_AF_FILES; i++)
2858 if ((csp->rlist[i] != NULL) && (csp->rlist[i]->f != NULL))
2860 filter_group = csp->rlist[i]->f;
2861 for (;(!err) && (filter_group != NULL); filter_group = filter_group->next)
2863 char current_mode = 'x';
2865 struct list_entry *filter_name;
2866 struct map *line_exports;
2867 const enum filter_type type = filter_group->type;
2868 const int multi_action_index = filter_type_info[type].multi_action_index;
2870 assert(type < MAX_FILTER_TYPES);
2872 filter_name = cur_line->data.action->multi_add[multi_action_index]->first;
2873 while ((filter_name != NULL)
2874 && (0 != strcmp(filter_group->name, filter_name->str)))
2876 filter_name = filter_name->next;
2879 if (filter_name != NULL)
2885 filter_name = cur_line->data.action->multi_remove[multi_action_index]->first;
2886 while ((filter_name != NULL)
2887 && (0 != strcmp(filter_group->name, filter_name->str)))
2889 filter_name = filter_name->next;
2891 if (filter_name != NULL)
2897 /* Generate a unique serial number */
2898 snprintf(number, sizeof(number), "%x", filter_identifier++);
2899 number[sizeof(number) - 1] = '\0';
2901 line_exports = new_map();
2902 if (line_exports == NULL)
2904 err = JB_ERR_MEMORY;
2910 if (!err) err = map(line_exports, "index", 1, number, 1);
2911 if (!err) err = map(line_exports, "name", 1, filter_group->name, 1);
2912 if (!err) err = map(line_exports, "description", 1, filter_group->description, 1);
2913 if (!err) err = map_radio(line_exports, "this-filter", "ynx", current_mode);
2914 if (!err) err = map(line_exports, "filter-type", 1, filter_type_info[type].type, 1);
2915 if (!err) err = map(line_exports, "abbr-filter-type", 1, filter_type_info[type].abbr_type, 1);
2916 if (!err) err = map(line_exports, "anchor", 1, filter_type_info[type].anchor, 1);
2920 filter_line = strdup(filter_template);
2921 if (filter_line == NULL) err = JB_ERR_MEMORY;
2923 if (!err) err = template_fill(&filter_line, line_exports);
2924 if (!err) err = string_join(&prepared_templates[type], filter_line);
2926 free_map(line_exports);
2931 freez(filter_template);
2933 /* Replace all filter macros with the aggregated templates */
2934 for (i = 0; i < MAX_FILTER_TYPES; i++)
2937 err = map(exports, filter_type_info[i].macro_name, 1, prepared_templates[i], 0);
2942 /* Free aggregated templates */
2943 for (i = 0; i < MAX_FILTER_TYPES; i++)
2945 freez(prepared_templates[i]);
2950 /* Check or uncheck the "disable all of this type" radio buttons. */
2951 for (i = 0; i < MAX_FILTER_TYPES; i++)
2953 const int a = filter_type_info[i].multi_action_index;
2954 const int disable_all = cur_line->data.action->multi_remove_all[a];
2956 err = map_radio(exports, filter_type_info[i].disable_all_option, "nx", (disable_all ? 'n' : 'x'));
2959 edit_free_file(file);
2967 return template_fill_for_cgi(csp, "edit-actions-for-url", exports, rsp);
2971 /*********************************************************************
2973 * Function : get_number_of_filters
2975 * Description : Counts the number of filter available.
2978 * 1 : csp = Current client state (buffers, headers, etc...)
2980 * Returns : Number of filters available.
2982 *********************************************************************/
2983 static int get_number_of_filters(const struct client_state *csp)
2986 struct re_filterfile_spec *b;
2987 struct file_list *fl;
2988 int number_of_filters = 0;
2990 for (i = 0; i < MAX_AF_FILES; i++)
2993 if ((NULL == fl) || (NULL == fl->f))
2996 * Either there are no filter files left or this
2997 * filter file just contains no valid filters.
2999 * Continue to be sure we don't miss valid filter
3000 * files that are chained after empty or invalid ones.
3005 for (b = fl->f; b != NULL; b = b->next)
3007 number_of_filters++;
3011 return number_of_filters;
3016 /*********************************************************************
3018 * Function : cgi_edit_actions_submit
3020 * Description : CGI function that actually edits the Actions list.
3023 * 1 : csp = Current client state (buffers, headers, etc...)
3024 * 2 : rsp = http_response data structure for output
3025 * 3 : parameters = map of cgi parameters
3027 * CGI Parameters : None
3029 * Returns : JB_ERR_OK on success
3030 * JB_ERR_MEMORY on out-of-memory
3031 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3032 * specified or not valid.
3034 *********************************************************************/
3035 jb_err cgi_edit_actions_submit(struct client_state *csp,
3036 struct http_response *rsp,
3037 const struct map *parameters)
3042 size_t newtext_size;
3044 struct editable_file * file;
3045 struct file_line * cur_line;
3046 unsigned line_number;
3049 int filter_identifier;
3051 const char * action_set_name;
3052 struct file_list * fl;
3053 struct url_actions * b;
3054 int number_of_filters;
3056 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3058 return cgi_error_disabled(csp, rsp);
3061 err = get_number_param(csp, parameters, "s", §ionid);
3067 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3070 /* No filename specified, can't read file, modified, or out of memory. */
3071 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3074 cur_line = file->lines;
3076 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
3078 cur_line = cur_line->next;
3081 if ( (cur_line == NULL)
3082 || (line_number != sectionid)
3084 || (cur_line->type != FILE_LINE_ACTION))
3086 /* Invalid "sectionid" parameter */
3087 edit_free_file(file);
3088 return JB_ERR_CGI_PARAMS;
3091 get_string_param(parameters, "p", &action_set_name);
3092 if (action_set_name != NULL)
3094 for (filter_identifier = 0; filter_identifier < MAX_AF_FILES; filter_identifier++)
3096 if (((fl = csp->actions_list[filter_identifier]) != NULL) && ((b = fl->f) != NULL))
3098 for (b = b->next; NULL != b; b = b->next)
3100 if (!strncmp(b->url->spec, "standard.", 9) && !strcmp(b->url->spec + 9, action_set_name))
3102 copy_action(cur_line->data.action, b->action);
3108 edit_free_file(file);
3109 return JB_ERR_CGI_PARAMS;
3115 err = actions_from_radio(parameters, cur_line->data.action);
3121 edit_free_file(file);
3125 /* Check the "disable all of this type" parameters. */
3126 for (i = 0; i < MAX_FILTER_TYPES; i++)
3128 const int multi_action_index = filter_type_info[i].multi_action_index;
3129 const char ch = get_char_param(parameters, filter_type_info[i].disable_all_param);
3133 list_remove_all(cur_line->data.action->multi_add[multi_action_index]);
3134 list_remove_all(cur_line->data.action->multi_remove[multi_action_index]);
3135 cur_line->data.action->multi_remove_all[multi_action_index] = 1;
3139 cur_line->data.action->multi_remove_all[multi_action_index] = 0;
3143 number_of_filters = get_number_of_filters(csp);
3145 for (filter_identifier = 0; filter_identifier < number_of_filters && !err; filter_identifier++)
3152 * Filter state. Valid states are: 'Y' (active),
3153 * 'N' (inactive) and 'X' (no change).
3157 * Abbreviated filter type. Valid types are: 'F' (content filter),
3158 * 'S' (server-header filter) and 'C' (client-header filter).
3160 int multi_action_index = 0;
3162 /* Generate the keys */
3163 snprintf(key_value, sizeof(key_value), "filter_r%x", filter_identifier);
3164 key_value[sizeof(key_value) - 1] = '\0'; /* XXX: Why? */
3165 snprintf(key_name, sizeof(key_name), "filter_n%x", filter_identifier);
3166 key_name[sizeof(key_name) - 1] = '\0'; /* XXX: Why? */
3167 snprintf(key_type, sizeof(key_type), "filter_t%x", filter_identifier);
3169 err = get_string_param(parameters, key_name, &name);
3174 /* The filter identifier isn't present. Try the next one. */
3178 type = get_char_param(parameters, key_type);
3182 multi_action_index = ACTION_MULTI_FILTER;
3185 multi_action_index = ACTION_MULTI_SERVER_HEADER_FILTER;
3188 multi_action_index = ACTION_MULTI_CLIENT_HEADER_FILTER;
3191 multi_action_index = ACTION_MULTI_CLIENT_HEADER_TAGGER;
3194 multi_action_index = ACTION_MULTI_SERVER_HEADER_TAGGER;
3197 multi_action_index = ACTION_MULTI_CLIENT_BODY_FILTER;
3200 log_error(LOG_LEVEL_ERROR,
3201 "Unknown filter type: %c for filter %s. Filter ignored.", type, name);
3204 assert(multi_action_index);
3206 value = get_char_param(parameters, key_value);
3209 list_remove_item(cur_line->data.action->multi_add[multi_action_index], name);
3210 if (!err) err = enlist(cur_line->data.action->multi_add[multi_action_index], name);
3211 list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name);
3213 else if (value == 'N')
3215 list_remove_item(cur_line->data.action->multi_add[multi_action_index], name);
3216 if (!cur_line->data.action->multi_remove_all[multi_action_index])
3218 list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name);
3219 if (!err) err = enlist(cur_line->data.action->multi_remove[multi_action_index], name);
3222 else if (value == 'X')
3224 list_remove_item(cur_line->data.action->multi_add[multi_action_index], name);
3225 list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name);
3229 /* process existing suppress tag */
3230 for (filter_identifier = 0; !err; filter_identifier++)
3236 const char *name, *new_name;
3238 * Filter state. Valid states are: 'Y' (active),
3239 * 'N' (inactive) and 'X' (no change).
3243 * Abbreviated filter type. Valid types are: 'U' (suppress tag).
3245 int multi_action_index = 0;
3247 /* Generate the keys */
3248 snprintf(key_value, sizeof(key_value), "string_filter_r%x", filter_identifier);
3249 snprintf(key_name, sizeof(key_name), "string_filter_n%x", filter_identifier);
3250 snprintf(old_name, sizeof(old_name), "string_filter_o%x", filter_identifier);
3251 snprintf(key_type, sizeof(key_type), "string_filter_t%x", filter_identifier);
3253 err = get_string_param(parameters, old_name, &name);
3258 /* The filter identifier isn't present: we're done! */
3262 err = get_string_param(parameters, key_name, &new_name);
3264 if (new_name == NULL) new_name = name;
3266 type = get_char_param(parameters, key_type);
3270 multi_action_index = ACTION_MULTI_SUPPRESS_TAG;
3273 log_error(LOG_LEVEL_ERROR,
3274 "Unknown filter type: %c for filter %s. Filter ignored.", type, name);
3277 assert(multi_action_index);
3279 value = get_char_param(parameters, key_value);
3280 if (value == 'X' || value == 'Y' || value == 'N')
3282 list_remove_item(cur_line->data.action->multi_add[multi_action_index], name);
3283 list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name);
3288 err = enlist(cur_line->data.action->multi_add[multi_action_index], new_name);
3290 else if (value == 'N')
3292 err = enlist(cur_line->data.action->multi_remove[multi_action_index], new_name);
3296 /* process new string filters */
3297 for (filter_identifier = 0; !err; filter_identifier++)
3304 * Filter state. Valid states are: 'Y' (active),
3305 * 'N' (inactive) and 'X' (no change).
3309 * Abbreviated filter type. Valid types are: 'U' (suppress tag).
3311 int multi_action_index = 0;
3313 /* Generate the keys */
3314 snprintf(key_value, sizeof(key_value), "new_string_filter_r%x", filter_identifier);
3315 snprintf(key_name, sizeof(key_name), "new_string_filter_n%x", filter_identifier);
3316 snprintf(key_type, sizeof(key_type), "new_string_filter_t%x", filter_identifier);
3318 err = get_string_param(parameters, key_name, &name);
3323 /* The filter identifier isn't present: we've done! */
3327 type = get_char_param(parameters, key_type);
3331 multi_action_index = ACTION_MULTI_SUPPRESS_TAG;
3334 log_error(LOG_LEVEL_ERROR,
3335 "Unknown filter type: %c for filter %s. Filter ignored.", type, name);
3338 assert(multi_action_index);
3340 value = get_char_param(parameters, key_value);
3343 list_remove_item(cur_line->data.action->multi_add[multi_action_index], name);
3344 if (!err) err = enlist(cur_line->data.action->multi_add[multi_action_index], name);
3345 list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name);
3347 else if (value == 'N')
3349 list_remove_item(cur_line->data.action->multi_add[multi_action_index], name);
3350 list_remove_item(cur_line->data.action->multi_remove[multi_action_index], name);
3351 if (!err) err = enlist(cur_line->data.action->multi_remove[multi_action_index], name);
3353 /* nothing to do if the value is 'X' */
3359 edit_free_file(file);
3363 if (NULL == (actiontext = actions_to_text(cur_line->data.action)))
3366 edit_free_file(file);
3367 return JB_ERR_MEMORY;
3370 len = strlen(actiontext);
3374 * Empty action - must special-case this.
3375 * Simply setting len to 1 is sufficient...
3380 newtext_size = len + 2;
3381 newtext = malloc_or_die(newtext_size);
3382 strlcpy(newtext, actiontext, newtext_size);
3386 newtext[len + 1] = '\0';
3388 freez(cur_line->raw);
3389 freez(cur_line->unprocessed);
3390 cur_line->unprocessed = newtext;
3392 err = edit_write_file(file);
3395 /* Error writing file */
3396 if (err == JB_ERR_FILE)
3398 /* Read-only file. */
3399 err = cgi_error_file_read_only(csp, rsp, file->filename);
3401 edit_free_file(file);
3405 snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u#l%u",
3406 (unsigned long) time(NULL), file->identifier, sectionid);
3408 edit_free_file(file);
3410 return cgi_redirect(rsp, target);
3414 /*********************************************************************
3416 * Function : cgi_edit_actions_url
3418 * Description : CGI function that actually edits a URL pattern in
3422 * 1 : csp = Current client state (buffers, headers, etc...)
3423 * 2 : rsp = http_response data structure for output
3424 * 3 : parameters = map of cgi parameters
3427 * filename : Identifies the file to edit
3428 * ver : File's last-modified time
3429 * section : Line number of section to edit
3430 * pattern : Line number of pattern to edit
3431 * newval : New value for pattern
3433 * Returns : JB_ERR_OK on success
3434 * JB_ERR_MEMORY on out-of-memory
3435 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3436 * specified or not valid.
3438 *********************************************************************/
3439 jb_err cgi_edit_actions_url(struct client_state *csp,
3440 struct http_response *rsp,
3441 const struct map *parameters)
3445 struct editable_file * file;
3446 struct file_line * cur_line;
3447 unsigned line_number;
3448 unsigned section_start_line_number = 0;
3456 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3458 return cgi_error_disabled(csp, rsp);
3461 err = get_number_param(csp, parameters, "p", &patternid);
3468 return JB_ERR_CGI_PARAMS;
3471 err = get_url_spec_param(csp, parameters, "u", &new_pattern);
3477 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3480 /* No filename specified, can't read file, modified, or out of memory. */
3482 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3486 cur_line = file->lines;
3488 while ((cur_line != NULL) && (line_number < patternid))
3490 if (cur_line->type == FILE_LINE_ACTION)
3492 section_start_line_number = line_number;
3494 cur_line = cur_line->next;
3498 if ((cur_line == NULL)
3499 || (cur_line->type != FILE_LINE_URL))
3501 /* Invalid "patternid" parameter */
3503 edit_free_file(file);
3504 return JB_ERR_CGI_PARAMS;
3507 /* At this point, the line to edit is in cur_line */
3509 freez(cur_line->raw);
3510 freez(cur_line->unprocessed);
3511 cur_line->unprocessed = new_pattern;
3513 err = edit_write_file(file);
3516 /* Error writing file */
3517 if (err == JB_ERR_FILE)
3519 /* Read-only file. */
3520 err = cgi_error_file_read_only(csp, rsp, file->filename);
3522 edit_free_file(file);
3526 snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u#l%u",
3527 (unsigned long) time(NULL), file->identifier, section_start_line_number);
3529 edit_free_file(file);
3531 return cgi_redirect(rsp, target);
3535 /*********************************************************************
3537 * Function : cgi_edit_actions_add_url
3539 * Description : CGI function that actually adds a URL pattern to
3543 * 1 : csp = Current client state (buffers, headers, etc...)
3544 * 2 : rsp = http_response data structure for output
3545 * 3 : parameters = map of cgi parameters
3548 * filename : Identifies the file to edit
3549 * ver : File's last-modified time
3550 * section : Line number of section to edit
3551 * newval : New pattern
3553 * Returns : JB_ERR_OK on success
3554 * JB_ERR_MEMORY on out-of-memory
3555 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3556 * specified or not valid.
3558 *********************************************************************/
3559 jb_err cgi_edit_actions_add_url(struct client_state *csp,
3560 struct http_response *rsp,
3561 const struct map *parameters)
3565 struct file_line * new_line;
3566 struct editable_file * file;
3567 struct file_line * cur_line;
3568 unsigned line_number;
3572 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3574 return cgi_error_disabled(csp, rsp);
3577 err = get_number_param(csp, parameters, "s", §ionid);
3584 return JB_ERR_CGI_PARAMS;
3587 err = get_url_spec_param(csp, parameters, "u", &new_pattern);
3593 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3596 /* No filename specified, can't read file, modified, or out of memory. */
3598 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3602 cur_line = file->lines;
3604 while ((cur_line != NULL) && (line_number < sectionid))
3606 cur_line = cur_line->next;
3610 if ((cur_line == NULL)
3611 || (cur_line->type != FILE_LINE_ACTION))
3613 /* Invalid "sectionid" parameter */
3615 edit_free_file(file);
3616 return JB_ERR_CGI_PARAMS;
3619 /* At this point, the section header is in cur_line - add after this. */
3621 /* Allocate the new line */
3622 new_line = zalloc_or_die(sizeof(*new_line));
3624 /* Fill in the data members of the new line */
3625 new_line->raw = NULL;
3626 new_line->prefix = NULL;
3627 new_line->unprocessed = new_pattern;
3628 new_line->type = FILE_LINE_URL;
3630 /* Link new_line into the list, after cur_line */
3631 new_line->next = cur_line->next;
3632 cur_line->next = new_line;
3634 /* Done making changes, now commit */
3636 err = edit_write_file(file);
3639 /* Error writing file */
3640 if (err == JB_ERR_FILE)
3642 /* Read-only file. */
3643 err = cgi_error_file_read_only(csp, rsp, file->filename);
3645 edit_free_file(file);
3649 snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u#l%u",
3650 (unsigned long) time(NULL), file->identifier, sectionid);
3652 edit_free_file(file);
3654 return cgi_redirect(rsp, target);
3658 /*********************************************************************
3660 * Function : cgi_edit_actions_remove_url
3662 * Description : CGI function that actually removes a URL pattern from
3666 * 1 : csp = Current client state (buffers, headers, etc...)
3667 * 2 : rsp = http_response data structure for output
3668 * 3 : parameters = map of cgi parameters
3671 * f : (filename) Identifies the file to edit
3672 * v : (version) File's last-modified time
3673 * p : (pattern) Line number of pattern to remove
3675 * Returns : JB_ERR_OK on success
3676 * JB_ERR_MEMORY on out-of-memory
3677 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3678 * specified or not valid.
3680 *********************************************************************/
3681 jb_err cgi_edit_actions_remove_url(struct client_state *csp,
3682 struct http_response *rsp,
3683 const struct map *parameters)
3686 struct editable_file * file;
3687 struct file_line * cur_line;
3688 struct file_line * prev_line;
3689 unsigned line_number;
3690 unsigned section_start_line_number = 0;
3694 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3696 return cgi_error_disabled(csp, rsp);
3699 err = get_number_param(csp, parameters, "p", &patternid);
3705 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3708 /* No filename specified, can't read file, modified, or out of memory. */
3709 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3714 cur_line = file->lines;
3716 while ((cur_line != NULL) && (line_number < patternid))
3718 if (cur_line->type == FILE_LINE_ACTION)
3720 section_start_line_number = line_number;
3722 prev_line = cur_line;
3723 cur_line = cur_line->next;
3727 if ( (cur_line == NULL)
3728 || (prev_line == NULL)
3729 || (cur_line->type != FILE_LINE_URL))
3731 /* Invalid "patternid" parameter */
3732 edit_free_file(file);
3733 return JB_ERR_CGI_PARAMS;
3736 /* At this point, the line to remove is in cur_line, and the previous
3737 * one is in prev_line
3740 /* Unlink cur_line */
3741 prev_line->next = cur_line->next;
3742 cur_line->next = NULL;
3745 edit_free_file_lines(cur_line);
3747 err = edit_write_file(file);
3750 /* Error writing file */
3751 if (err == JB_ERR_FILE)
3753 /* Read-only file. */
3754 err = cgi_error_file_read_only(csp, rsp, file->filename);
3756 edit_free_file(file);
3760 snprintf(target, sizeof(target), CGI_PREFIX "edit-actions-list?foo=%lu&f=%u#l%u",
3761 (unsigned long) time(NULL), file->identifier, section_start_line_number);
3763 edit_free_file(file);
3765 return cgi_redirect(rsp, target);
3769 /*********************************************************************
3771 * Function : cgi_edit_actions_section_remove
3773 * Description : CGI function that actually removes a whole section from
3774 * the actions file. The section must be empty first
3775 * (else JB_ERR_CGI_PARAMS).
3778 * 1 : csp = Current client state (buffers, headers, etc...)
3779 * 2 : rsp = http_response data structure for output