1 const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.24 2002/03/24 15:23:33 jongfoster Exp $";
2 /*********************************************************************
4 * File : $Source: /cvsroot/ijbswa/current/cgiedit.c,v $
6 * Purpose : CGI-based actionsfile editor.
8 * Functions declared include: cgi_edit_*
10 * NOTE: The CGIs in this file use parameter names
11 * such as "f" and "s" which are really *BAD* choices.
12 * However, I'm trying to save bytes in the
13 * edit-actions-list HTML page - the standard actions
14 * file generated a 550kbyte page, which is ridiculous.
16 * Stick to the short names in this file for consistency.
18 * Copyright : Written by and Copyright (C) 2001 the SourceForge
19 * Privoxy team. http://www.privoxy.org/
21 * Based on the Internet Junkbuster originally written
22 * by and Copyright (C) 1997 Anonymous Coders and
23 * Junkbusters Corporation. http://www.junkbusters.com
25 * This program is free software; you can redistribute it
26 * and/or modify it under the terms of the GNU General
27 * Public License as published by the Free Software
28 * Foundation; either version 2 of the License, or (at
29 * your option) any later version.
31 * This program is distributed in the hope that it will
32 * be useful, but WITHOUT ANY WARRANTY; without even the
33 * implied warranty of MERCHANTABILITY or FITNESS FOR A
34 * PARTICULAR PURPOSE. See the GNU General Public
35 * License for more details.
37 * The GNU General Public License should be included with
38 * this file. If not, you can view it at
39 * http://www.gnu.org/copyleft/gpl.html
40 * or write to the Free Software Foundation, Inc., 59
41 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
45 * Revision 1.24 2002/03/24 15:23:33 jongfoster
48 * Revision 1.23 2002/03/24 13:32:41 swa
49 * name change related issues
51 * Revision 1.22 2002/03/24 13:25:43 swa
52 * name change related issues
54 * Revision 1.21 2002/03/22 18:02:48 jongfoster
55 * Fixing remote toggle
57 * Revision 1.20 2002/03/16 20:28:34 oes
58 * Added descriptions to the filters so users will know what they select in the cgi editor
60 * Revision 1.19 2002/03/16 18:38:14 jongfoster
61 * Stopping stupid or malicious users from breaking the actions
62 * file using the web-based editor.
64 * Revision 1.18 2002/03/16 14:57:44 jongfoster
65 * Full support for enabling/disabling modular filters.
67 * Revision 1.17 2002/03/16 14:26:42 jongfoster
68 * First version of modular filters support - READ ONLY!
69 * Fixing a double-free bug in the out-of-memory handling in map_radio().
71 * Revision 1.16 2002/03/07 03:46:17 oes
72 * Fixed compiler warnings
74 * Revision 1.15 2002/03/06 22:54:35 jongfoster
75 * Automated function-comment nitpicking.
77 * Revision 1.14 2002/03/05 00:24:51 jongfoster
78 * Patch to always edit the current actions file.
80 * Revision 1.13 2002/03/04 02:07:59 david__schmidt
81 * Enable web editing of actions file on OS/2 (it had been broken all this time!)
83 * Revision 1.12 2002/03/03 09:18:03 joergs
84 * Made jumbjuster work on AmigaOS again.
86 * Revision 1.11 2002/01/23 01:03:31 jongfoster
87 * Fixing gcc [CygWin] compiler warnings
89 * Revision 1.10 2002/01/23 00:22:59 jongfoster
90 * Adding new function cgi_edit_actions_section_swap(), to reorder
93 * Adding get_url_spec_param() to get a validated URL pattern.
95 * Moving edit_read_line() out of this file and into loaders.c.
97 * Adding missing html_encode() to many CGI functions.
99 * Moving the functions that #include actionlist.h to the end of the file,
100 * because the Visual C++ 97 debugger gets extremely confused if you try
101 * to debug any code that comes after them in the file.
103 * Major optimizations in cgi_edit_actions_list() to reduce the size of
104 * the generated HTML (down 40% from 550k to 304k), with major side-effects
105 * throughout the editor and templates. In particular, the length of the
106 * URLs throughout the editor has been drastically reduced, by cutting
107 * paramater names down to 1 character and CGI names down to 3-4
108 * characters, by removing all non-essential CGI paramaters even at the
109 * expense of having to re-read the actions file for the most trivial
110 * page, and by using relative rather than absolute URLs. This means
111 * that this (typical example):
113 * <a href="http://ijbswa.sourceforge.net/config/edit-actions-url-form?
114 * filename=ijb&ver=1011487572&section=12&pattern=13
115 * &oldval=www.oesterhelt.org%2Fdeanimate-demo">
119 * <a href="eau?f=ijb&v=1011487572&p=13">
121 * Revision 1.9 2002/01/17 20:56:22 jongfoster
122 * Replacing hard references to the URL of the config interface
123 * with #defines from project.h
125 * Revision 1.8 2001/11/30 23:35:51 jongfoster
126 * Renaming actionsfile to ijb.action
128 * Revision 1.7 2001/11/13 00:28:24 jongfoster
129 * - Renaming parameters from edit-actions-for-url so that they only
130 * contain legal JavaScript characters. If we wanted to write
131 * JavaScript that worked with Netscape 4, this is nessacery.
132 * (Note that at the moment the JavaScript doesn't actually work
133 * with Netscape 4, but now this is purely a template issue, not
134 * one affecting code).
135 * - Adding new CGIs for use by non-JavaScript browsers:
136 * edit-actions-url-form
137 * edit-actions-add-url-form
138 * edit-actions-remove-url-form
141 * Revision 1.6 2001/10/29 03:48:09 david__schmidt
142 * OS/2 native needed a snprintf() routine. Added one to miscutil, brackedted
143 * by and __OS2__ ifdef.
145 * Revision 1.5 2001/10/25 03:40:48 david__schmidt
146 * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
147 * threads to call select() simultaneously. So, it's time to do a real, live,
148 * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__
149 * (native). Both versions will work, but using __OS2__ offers multi-threading.
151 * Revision 1.4 2001/10/23 21:48:19 jongfoster
152 * Cleaning up error handling in CGI functions - they now send back
153 * a HTML error page and should never cause a FATAL error. (Fixes one
154 * potential source of "denial of service" attacks).
156 * CGI actions file editor that works and is actually useful.
158 * Ability to toggle JunkBuster remotely using a CGI call.
160 * You can turn off both the above features in the main configuration
161 * file, e.g. if you are running a multi-user proxy.
163 * Revision 1.3 2001/10/14 22:12:49 jongfoster
164 * New version of CGI-based actionsfile editor.
165 * Major changes, including:
166 * - Completely new file parser and file output routines
167 * - edit-actions CGI renamed edit-actions-for-url
168 * - All CGIs now need a filename parameter, except for...
169 * - New CGI edit-actions which doesn't need a filename,
170 * to allow you to start the editor up.
171 * - edit-actions-submit now works, and now automatically
172 * redirects you back to the main edit-actions-list handler.
174 * Revision 1.2 2001/09/16 17:05:14 jongfoster
175 * Removing unused #include showarg.h
177 * Revision 1.1 2001/09/16 15:47:37 jongfoster
178 * First version of CGI-based edit interface. This is very much a
179 * work-in-progress, and you can't actually use it to edit anything
180 * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes
181 * to have any effect.
184 **********************************************************************/
190 * FIXME: Following includes copied from cgi.c - which are actually needed?
195 #include <sys/types.h>
200 #include <sys/stat.h>
203 #define snprintf _snprintf
204 #endif /* def _WIN32 */
209 #include "cgisimple.h"
213 #include "miscutil.h"
217 /* loadcfg.h is for g_bToggleIJB only */
218 #include "urlmatch.h"
220 const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION;
223 #ifdef FEATURE_CGI_EDIT_ACTIONS
227 struct file_line * next;
235 struct action_spec action[1];
244 /* Add more data types here... e.g.
247 struct url_spec url[1];
251 struct action_spec action[1];
260 #define FILE_LINE_UNPROCESSED 1
261 #define FILE_LINE_BLANK 2
262 #define FILE_LINE_ALIAS_HEADER 3
263 #define FILE_LINE_ALIAS_ENTRY 4
264 #define FILE_LINE_ACTION 5
265 #define FILE_LINE_URL 6
266 #define FILE_LINE_SETTINGS_HEADER 7
267 #define FILE_LINE_SETTINGS_ENTRY 8
268 #define FILE_LINE_DESCRIPTION_HEADER 9
269 #define FILE_LINE_DESCRIPTION_ENTRY 10
274 struct file_line * lines;
275 const char * filename; /* Full pathname - e.g. "/etc/privoxy/wibble.action" */
276 const char * identifier; /* Filename stub - e.g. "wibble". Use for CGI param. */
277 /* Pre-encoded with url_encode() for ease of use. */
278 const char * version_str; /* Last modification time, as a string. For CGI param */
279 /* Can be used in URL without using url_param(). */
280 unsigned version; /* Last modification time - prevents chaos with
281 * the browser's "back" button. Note that this is a
282 * time_t cast to an unsigned. When comparing, always
283 * cast the time_t to an unsigned, and *NOT* vice-versa.
284 * This may lose the top few bits, but they're not
285 * significant anyway.
287 int newline; /* Newline convention - one of the NEWLINE_xxx constants.
288 * Note that changing this after the file has been
289 * read in will cause a mess.
291 struct file_line * parse_error; /* On parse error, this is the offending line. */
292 const char * parse_error_text; /* On parse error, this is the problem.
293 * (Statically allocated) */
296 #define CGI_ACTION_PARAM_LEN_MAX 500
298 /* FIXME: Following non-static functions should be prototyped in .h or made static */
300 /* Functions to read and write arbitrary config files */
301 jb_err edit_read_file(struct client_state *csp,
302 const struct map *parameters,
305 struct editable_file **pfile);
306 jb_err edit_write_file(struct editable_file * file);
307 void edit_free_file(struct editable_file * file);
309 /* Functions to read and write actions files */
310 jb_err edit_parse_actions_file(struct editable_file * file);
311 jb_err edit_read_actions_file(struct client_state *csp,
312 struct http_response *rsp,
313 const struct map *parameters,
315 struct editable_file **pfile);
318 jb_err cgi_error_modified(struct client_state *csp,
319 struct http_response *rsp,
320 const char *filename);
321 jb_err cgi_error_parse(struct client_state *csp,
322 struct http_response *rsp,
323 struct editable_file *file);
324 jb_err cgi_error_file(struct client_state *csp,
325 struct http_response *rsp,
326 const char *filename);
327 jb_err cgi_error_disabled(struct client_state *csp,
328 struct http_response *rsp);
330 /* Internal arbitrary config file support functions */
331 static jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline);
332 static void edit_free_file_lines(struct file_line * first_line);
334 /* Internal actions file support functions */
335 static int match_actions_file_header_line(const char * line, const char * name);
336 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue);
338 /* Internal parameter parsing functions */
339 static jb_err get_file_name_param(struct client_state *csp,
340 const struct map *parameters,
341 const char *param_name,
344 const char **pparam);
345 static jb_err get_number_param(struct client_state *csp,
346 const struct map *parameters,
349 static jb_err get_url_spec_param(struct client_state *csp,
350 const struct map *parameters,
353 static jb_err get_string_param(const struct map *parameters,
354 const char *param_name,
355 const char **pparam);
357 /* Internal actionsfile <==> HTML conversion functions */
358 static jb_err map_radio(struct map * exports,
359 const char * optionname,
362 static jb_err actions_to_radio(struct map * exports,
363 const struct action_spec *action);
364 static jb_err actions_from_radio(const struct map * parameters,
365 struct action_spec *action);
368 static jb_err map_copy_parameter_html(struct map *out,
369 const struct map *in,
371 #if 0 /* unused function */
372 static jb_err map_copy_parameter_url(struct map *out,
373 const struct map *in,
375 #endif /* unused function */
377 /*********************************************************************
379 * Function : map_copy_parameter_html
381 * Description : Copy a CGI parameter from one map to another, HTML
385 * 1 : out = target map
386 * 2 : in = source map
387 * 3 : name = name of cgi parameter to copy
389 * Returns : JB_ERR_OK on success
390 * JB_ERR_MEMORY on out-of-memory
391 * JB_ERR_CGI_PARAMS if the parameter doesn't exist
394 *********************************************************************/
395 static jb_err map_copy_parameter_html(struct map *out,
396 const struct map *in,
406 value = lookup(in, name);
407 err = map(out, name, 1, html_encode(value), 0);
414 else if (*value == '\0')
416 return JB_ERR_CGI_PARAMS;
425 #if 0 /* unused function */
426 /*********************************************************************
428 * Function : map_copy_parameter_html
430 * Description : Copy a CGI parameter from one map to another, URL
434 * 1 : out = target map
435 * 2 : in = source map
436 * 3 : name = name of cgi parameter to copy
438 * Returns : JB_ERR_OK on success
439 * JB_ERR_MEMORY on out-of-memory
440 * JB_ERR_CGI_PARAMS if the parameter doesn't exist
443 *********************************************************************/
444 static jb_err map_copy_parameter_url(struct map *out,
445 const struct map *in,
455 value = lookup(in, name);
456 err = map(out, name, 1, url_encode(value), 0);
463 else if (*value == '\0')
465 return JB_ERR_CGI_PARAMS;
472 #endif /* 0 - unused function */
474 /*********************************************************************
476 * Function : cgi_edit_actions_url_form
478 * Description : CGI function that displays a form for
482 * 1 : csp = Current client state (buffers, headers, etc...)
483 * 2 : rsp = http_response data structure for output
484 * 3 : parameters = map of cgi parameters
487 * f : (filename) Identifies the file to edit
488 * v : (version) File's last-modified time
489 * p : (pattern) Line number of pattern to edit
491 * Returns : JB_ERR_OK on success
492 * JB_ERR_MEMORY on out-of-memory
493 * JB_ERR_CGI_PARAMS if the CGI parameters are not
494 * specified or not valid.
496 *********************************************************************/
497 jb_err cgi_edit_actions_url_form(struct client_state *csp,
498 struct http_response *rsp,
499 const struct map *parameters)
501 struct map * exports;
503 struct editable_file * file;
504 struct file_line * cur_line;
505 unsigned line_number;
512 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
514 return cgi_error_disabled(csp, rsp);
517 err = get_number_param(csp, parameters, "p", &patternid);
523 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
526 /* No filename specified, can't read file, modified, or out of memory. */
527 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
530 cur_line = file->lines;
532 for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++)
534 cur_line = cur_line->next;
537 if ( (cur_line == NULL)
538 || (line_number != patternid)
540 || (cur_line->type != FILE_LINE_URL))
542 /* Invalid "patternid" parameter */
543 edit_free_file(file);
544 return JB_ERR_CGI_PARAMS;
547 if (NULL == (exports = default_exports(csp, NULL)))
549 edit_free_file(file);
550 return JB_ERR_MEMORY;
553 err = map(exports, "f", 1, file->identifier, 1);
554 if (!err) err = map(exports, "v", 1, file->version_str, 1);
555 if (!err) err = map(exports, "p", 1, url_encode(lookup(parameters, "p")), 0);
556 if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0);
558 edit_free_file(file);
566 return template_fill_for_cgi(csp, "edit-actions-url-form", exports, rsp);
570 /*********************************************************************
572 * Function : cgi_edit_actions_add_url_form
574 * Description : CGI function that displays a form for
578 * 1 : csp = Current client state (buffers, headers, etc...)
579 * 2 : rsp = http_response data structure for output
580 * 3 : parameters = map of cgi parameters
583 * f : (filename) Identifies the file to edit
584 * v : (version) File's last-modified time
585 * s : (section) Line number of section to edit
587 * Returns : JB_ERR_OK on success
588 * JB_ERR_MEMORY on out-of-memory
589 * JB_ERR_CGI_PARAMS if the CGI parameters are not
590 * specified or not valid.
592 *********************************************************************/
593 jb_err cgi_edit_actions_add_url_form(struct client_state *csp,
594 struct http_response *rsp,
595 const struct map *parameters)
604 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
606 return cgi_error_disabled(csp, rsp);
609 if (NULL == (exports = default_exports(csp, NULL)))
611 return JB_ERR_MEMORY;
614 err = map_copy_parameter_html(exports, parameters, "f");
615 if (!err) err = map_copy_parameter_html(exports, parameters, "v");
616 if (!err) err = map_copy_parameter_html(exports, parameters, "s");
624 return template_fill_for_cgi(csp, "edit-actions-add-url-form", exports, rsp);
628 /*********************************************************************
630 * Function : cgi_edit_actions_remove_url_form
632 * Description : CGI function that displays a form for
636 * 1 : csp = Current client state (buffers, headers, etc...)
637 * 2 : rsp = http_response data structure for output
638 * 3 : parameters = map of cgi parameters
641 * f : (filename) Identifies the file to edit
642 * v : (version) File's last-modified time
643 * p : (pattern) Line number of pattern to edit
645 * Returns : JB_ERR_OK on success
646 * JB_ERR_MEMORY on out-of-memory
647 * JB_ERR_CGI_PARAMS if the CGI parameters are not
648 * specified or not valid.
650 *********************************************************************/
651 jb_err cgi_edit_actions_remove_url_form(struct client_state *csp,
652 struct http_response *rsp,
653 const struct map *parameters)
655 struct map * exports;
657 struct editable_file * file;
658 struct file_line * cur_line;
659 unsigned line_number;
666 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
668 return cgi_error_disabled(csp, rsp);
671 err = get_number_param(csp, parameters, "p", &patternid);
677 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
680 /* No filename specified, can't read file, modified, or out of memory. */
681 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
684 cur_line = file->lines;
686 for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++)
688 cur_line = cur_line->next;
691 if ( (cur_line == NULL)
692 || (line_number != patternid)
694 || (cur_line->type != FILE_LINE_URL))
696 /* Invalid "patternid" parameter */
697 edit_free_file(file);
698 return JB_ERR_CGI_PARAMS;
701 if (NULL == (exports = default_exports(csp, NULL)))
703 edit_free_file(file);
704 return JB_ERR_MEMORY;
707 err = map(exports, "f", 1, file->identifier, 1);
708 if (!err) err = map(exports, "v", 1, file->version_str, 1);
709 if (!err) err = map(exports, "s", 1, url_encode(lookup(parameters, "s")), 0);
710 if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0);
712 edit_free_file(file);
720 return template_fill_for_cgi(csp, "edit-actions-remove-url-form", exports, rsp);
724 /*********************************************************************
726 * Function : edit_write_file
728 * Description : Write a complete file to disk.
731 * 1 : filename = File to write to.
732 * 2 : file = Data structure to write.
734 * Returns : JB_ERR_OK on success
735 * JB_ERR_FILE on error writing to file.
736 * JB_ERR_MEMORY on out of memory
738 *********************************************************************/
739 jb_err edit_write_file(struct editable_file * file)
742 struct file_line * cur_line;
743 struct stat statbuf[1];
744 char version_buf[22]; /* 22 = ceil(log10(2^64)) + 2 = max number of
745 digits in time_t, assuming this is a 64-bit
746 machine, plus null terminator, plus one
750 assert(file->filename);
752 #if defined(AMIGA) || defined(__OS2__)
753 if (NULL == (fp = fopen(file->filename, "w")))
755 if (NULL == (fp = fopen(file->filename, "wt")))
756 #endif /* def AMIGA */
761 cur_line = file->lines;
762 while (cur_line != NULL)
766 if (fputs(cur_line->raw, fp) < 0)
774 if (cur_line->prefix)
776 if (fputs(cur_line->prefix, fp) < 0)
782 if (cur_line->unprocessed)
784 /* This should be a single line - sanity check. */
785 assert(NULL == strchr(cur_line->unprocessed, '\r'));
786 assert(NULL == strchr(cur_line->unprocessed, '\n'));
788 if (NULL != strchr(cur_line->unprocessed, '#'))
790 /* Must quote '#' characters */
797 /* Count number of # characters, so we know length of output string */
798 src = cur_line->unprocessed;
799 while (NULL != (src = strchr(src, '#')))
806 /* Allocate new memory for string */
807 len = strlen(cur_line->unprocessed);
808 if (NULL == (str = malloc((size_t) len + 1 + numhash)))
810 /* Uh oh, just trashed file! */
812 return JB_ERR_MEMORY;
815 /* Loop through string from end */
816 src = cur_line->unprocessed + len;
817 dest = str + len + numhash;
818 for ( ; len >= 0; len--)
820 if ((*dest-- = *src--) == '#')
824 assert(numhash >= 0);
827 assert(numhash == 0);
828 assert(src + 1 == cur_line->unprocessed);
829 assert(dest + 1 == str);
831 if (fputs(str, fp) < 0)
842 /* Can write without quoting '#' characters. */
843 if (fputs(cur_line->unprocessed, fp) < 0)
849 if (fputs(NEWLINE(file->newline), fp) < 0)
857 /* FIXME: Write data from file->data->whatever */
861 cur_line = cur_line->next;
867 /* Update the version stamp in the file structure, since we just
868 * wrote to the file & changed it's date.
870 if (stat(file->filename, statbuf) < 0)
872 /* Error, probably file not found. */
875 file->version = (unsigned)statbuf->st_mtime;
877 /* Correct file->version_str */
878 freez(file->version_str);
879 snprintf(version_buf, 22, "%u", file->version);
880 version_buf[21] = '\0';
881 file->version_str = strdup(version_buf);
882 if (version_buf == NULL)
884 return JB_ERR_MEMORY;
891 /*********************************************************************
893 * Function : edit_free_file
895 * Description : Free a complete file in memory.
898 * 1 : file = Data structure to free.
902 *********************************************************************/
903 void edit_free_file(struct editable_file * file)
907 /* Silently ignore NULL pointer */
911 edit_free_file_lines(file->lines);
912 freez(file->filename);
913 freez(file->identifier);
914 freez(file->version_str);
916 file->parse_error_text = NULL; /* Statically allocated */
917 file->parse_error = NULL;
923 /*********************************************************************
925 * Function : edit_free_file
927 * Description : Free an entire linked list of file lines.
930 * 1 : first_line = Data structure to free.
934 *********************************************************************/
935 static void edit_free_file_lines(struct file_line * first_line)
937 struct file_line * next_line;
939 while (first_line != NULL)
941 next_line = first_line->next;
942 first_line->next = NULL;
943 freez(first_line->raw);
944 freez(first_line->prefix);
945 freez(first_line->unprocessed);
946 switch(first_line->type)
948 case 0: /* special case if memory zeroed */
949 case FILE_LINE_UNPROCESSED:
950 case FILE_LINE_BLANK:
951 case FILE_LINE_ALIAS_HEADER:
952 case FILE_LINE_SETTINGS_HEADER:
953 case FILE_LINE_DESCRIPTION_HEADER:
954 case FILE_LINE_DESCRIPTION_ENTRY:
955 case FILE_LINE_ALIAS_ENTRY:
957 /* No data is stored for these */
960 case FILE_LINE_ACTION:
961 free_action(first_line->data.action);
964 case FILE_LINE_SETTINGS_ENTRY:
965 freez(first_line->data.setting.name);
966 freez(first_line->data.setting.svalue);
969 /* Should never happen */
973 first_line->type = 0; /* paranoia */
975 first_line = next_line;
980 /*********************************************************************
982 * Function : match_actions_file_header_line
984 * Description : Match an actions file {{header}} line
987 * 1 : line = String from file
988 * 2 : name = Header to match against
990 * Returns : 0 iff they match.
992 *********************************************************************/
993 static int match_actions_file_header_line(const char * line, const char * name)
1001 if ((line[0] != '{') || (line[1] != '{'))
1007 /* Look for optional whitespace */
1008 while ( (*line == ' ') || (*line == '\t') )
1013 /* Look for the specified name (case-insensitive) */
1015 if (0 != strncmpic(line, name, len))
1021 /* Look for optional whitespace */
1022 while ( (*line == ' ') || (*line == '\t') )
1027 /* Look for "}}" and end of string*/
1028 if ((line[0] != '}') || (line[1] != '}') || (line[2] != '\0'))
1038 /*********************************************************************
1040 * Function : match_actions_file_header_line
1042 * Description : Match an actions file {{header}} line
1045 * 1 : line = String from file. Must not start with
1046 * whitespace (else infinite loop!)
1047 * 2 : name = Destination for name
1048 * 2 : name = Destination for value
1050 * Returns : JB_ERR_OK on success
1051 * JB_ERR_MEMORY on out-of-memory
1052 * JB_ERR_PARSE if there's no "=" sign, or if there's
1053 * nothing before the "=" sign (but empty
1054 * values *after* the "=" sign are legal).
1056 *********************************************************************/
1057 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue)
1059 const char * name_end;
1060 const char * value_start;
1066 assert(*line != ' ');
1067 assert(*line != '\t');
1072 value_start = strchr(line, '=');
1073 if ((value_start == NULL) || (value_start == line))
1075 return JB_ERR_PARSE;
1078 name_end = value_start - 1;
1080 /* Eat any whitespace before the '=' */
1081 while ((*name_end == ' ') || (*name_end == '\t'))
1084 * we already know we must have at least 1 non-ws char
1085 * at start of buf - no need to check
1090 name_len = name_end - line + 1; /* Length excluding \0 */
1091 if (NULL == (*pname = (char *) malloc(name_len + 1)))
1093 return JB_ERR_MEMORY;
1095 strncpy(*pname, line, name_len);
1096 (*pname)[name_len] = '\0';
1098 /* Eat any the whitespace after the '=' */
1100 while ((*value_start == ' ') || (*value_start == '\t'))
1105 if (NULL == (*pvalue = strdup(value_start)))
1109 return JB_ERR_MEMORY;
1116 /*********************************************************************
1118 * Function : edit_parse_actions_file
1120 * Description : Parse an actions file in memory.
1122 * Passed linked list must have the "data" member
1123 * zeroed, and must contain valid "next" and
1124 * "unprocessed" fields. The "raw" and "prefix"
1125 * fields are ignored, and "type" is just overwritten.
1127 * Note that on error the file may have been
1131 * 1 : file = Actions file to be parsed in-place.
1133 * Returns : JB_ERR_OK on success
1134 * JB_ERR_MEMORY on out-of-memory
1135 * JB_ERR_PARSE on error
1137 *********************************************************************/
1138 jb_err edit_parse_actions_file(struct editable_file * file)
1140 struct file_line * cur_line;
1142 const char * text; /* Text from a line */
1143 char * name; /* For lines of the form name=value */
1144 char * value; /* For lines of the form name=value */
1145 struct action_alias * alias_list = NULL;
1146 jb_err err = JB_ERR_OK;
1148 /* alias_list contains the aliases defined in this file.
1149 * It might be better to use the "file_line.data" fields
1150 * in the relavent places instead.
1153 cur_line = file->lines;
1155 /* A note about blank line support: Blank lines should only
1156 * ever occur as the last line in the file. This function
1157 * is more forgiving than that - FILE_LINE_BLANK can occur
1161 /* Skip leading blanks. Should only happen if file is
1162 * empty (which is valid, but pointless).
1164 while ( (cur_line != NULL)
1165 && (cur_line->unprocessed[0] == '\0') )
1168 cur_line->type = FILE_LINE_BLANK;
1169 cur_line = cur_line->next;
1172 if ( (cur_line != NULL)
1173 && (cur_line->unprocessed[0] != '{') )
1175 /* File doesn't start with a header */
1176 file->parse_error = cur_line;
1177 file->parse_error_text = "First (non-comment) line of the file must contain a header.";
1178 return JB_ERR_PARSE;
1181 if ( (cur_line != NULL) && (0 ==
1182 match_actions_file_header_line(cur_line->unprocessed, "settings") ) )
1184 cur_line->type = FILE_LINE_SETTINGS_HEADER;
1186 cur_line = cur_line->next;
1187 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1189 if (cur_line->unprocessed[0])
1191 cur_line->type = FILE_LINE_SETTINGS_ENTRY;
1193 err = split_line_on_equals(cur_line->unprocessed,
1194 &cur_line->data.setting.name,
1195 &cur_line->data.setting.svalue);
1196 if (err == JB_ERR_MEMORY)
1200 else if (err != JB_ERR_OK)
1202 /* Line does not contain a name=value pair */
1203 file->parse_error = cur_line;
1204 file->parse_error_text = "Expected a name=value pair on this {{description}} line, but couldn't find one.";
1205 return JB_ERR_PARSE;
1210 cur_line->type = FILE_LINE_BLANK;
1212 cur_line = cur_line->next;
1216 if ( (cur_line != NULL) && (0 ==
1217 match_actions_file_header_line(cur_line->unprocessed, "description") ) )
1219 cur_line->type = FILE_LINE_DESCRIPTION_HEADER;
1221 cur_line = cur_line->next;
1222 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1224 if (cur_line->unprocessed[0])
1226 cur_line->type = FILE_LINE_DESCRIPTION_ENTRY;
1230 cur_line->type = FILE_LINE_BLANK;
1232 cur_line = cur_line->next;
1236 if ( (cur_line != NULL) && (0 ==
1237 match_actions_file_header_line(cur_line->unprocessed, "alias") ) )
1239 cur_line->type = FILE_LINE_ALIAS_HEADER;
1241 cur_line = cur_line->next;
1242 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1244 if (cur_line->unprocessed[0])
1246 /* define an alias */
1247 struct action_alias * new_alias;
1249 cur_line->type = FILE_LINE_ALIAS_ENTRY;
1251 err = split_line_on_equals(cur_line->unprocessed, &name, &value);
1252 if (err == JB_ERR_MEMORY)
1256 else if (err != JB_ERR_OK)
1258 /* Line does not contain a name=value pair */
1259 file->parse_error = cur_line;
1260 file->parse_error_text = "Expected a name=value pair on this {{alias}} line, but couldn't find one.";
1261 return JB_ERR_PARSE;
1264 if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1269 free_alias_list(alias_list);
1270 return JB_ERR_MEMORY;
1273 err = get_actions(value, alias_list, new_alias->action);
1276 /* Invalid action or out of memory */
1280 free_alias_list(alias_list);
1281 if (err == JB_ERR_MEMORY)
1287 /* Line does not contain a name=value pair */
1288 file->parse_error = cur_line;
1289 file->parse_error_text = "This alias does not specify a valid set of actions.";
1290 return JB_ERR_PARSE;
1296 new_alias->name = name;
1299 new_alias->next = alias_list;
1300 alias_list = new_alias;
1304 cur_line->type = FILE_LINE_BLANK;
1306 cur_line = cur_line->next;
1310 /* Header done, process the main part of the file */
1311 while (cur_line != NULL)
1313 /* At this point, (cur_line->unprocessed[0] == '{') */
1314 assert(cur_line->unprocessed[0] == '{');
1315 text = cur_line->unprocessed + 1;
1316 len = strlen(text) - 1;
1317 if (text[len] != '}')
1319 /* No closing } on header */
1320 free_alias_list(alias_list);
1321 file->parse_error = cur_line;
1322 file->parse_error_text = "Headers starting with '{' must have a "
1323 "closing bracket ('}'). Headers starting with two brackets ('{{') "
1324 "must close with two brackets ('}}').";
1325 return JB_ERR_PARSE;
1330 /* An invalid {{ header. */
1331 free_alias_list(alias_list);
1332 file->parse_error = cur_line;
1333 file->parse_error_text = "Unknown or unexpected two-bracket header. "
1334 "Please remember that the system (two-bracket) headers must "
1335 "appear in the order {{settings}}, {{description}}, {{alias}}, "
1336 "and must appear before any actions (one-bracket) headers. "
1337 "Also note that system headers may not be repeated.";
1338 return JB_ERR_PARSE;
1341 while ( (*text == ' ') || (*text == '\t') )
1347 && ( (text[len - 1] == ' ')
1348 || (text[len - 1] == '\t') ) )
1353 cur_line->type = FILE_LINE_ACTION;
1355 /* Remove {} and make copy */
1356 if (NULL == (value = (char *) malloc(len + 1)))
1359 free_alias_list(alias_list);
1360 return JB_ERR_MEMORY;
1362 strncpy(value, text, len);
1366 err = get_actions(value, alias_list, cur_line->data.action);
1369 /* Invalid action or out of memory */
1371 free_alias_list(alias_list);
1372 if (err == JB_ERR_MEMORY)
1378 /* Line does not contain a name=value pair */
1379 file->parse_error = cur_line;
1380 file->parse_error_text = "This header does not specify a valid set of actions.";
1381 return JB_ERR_PARSE;
1385 /* Done with string - it was clobbered anyway */
1388 /* Process next line */
1389 cur_line = cur_line->next;
1391 /* Loop processing URL patterns */
1392 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1394 if (cur_line->unprocessed[0])
1396 /* Could parse URL here, but this isn't currently needed */
1398 cur_line->type = FILE_LINE_URL;
1402 cur_line->type = FILE_LINE_BLANK;
1404 cur_line = cur_line->next;
1406 } /* End main while(cur_line != NULL) loop */
1408 free_alias_list(alias_list);
1414 /*********************************************************************
1416 * Function : edit_read_file_lines
1418 * Description : Read all the lines of a file into memory.
1419 * Handles whitespace, comments and line continuation.
1422 * 1 : fp = File to read from. On return, this will be
1423 * at EOF but it will not have been closed.
1424 * 2 : pfile = Destination for a linked list of file_lines.
1425 * Will be set to NULL on error.
1427 * Returns : JB_ERR_OK on success
1428 * JB_ERR_MEMORY on out-of-memory
1430 *********************************************************************/
1431 jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline)
1433 struct file_line * first_line; /* Keep for return value or to free */
1434 struct file_line * cur_line; /* Current line */
1435 struct file_line * prev_line; /* Entry with prev_line->next = cur_line */
1443 cur_line = first_line = zalloc(sizeof(struct file_line));
1444 if (cur_line == NULL)
1446 return JB_ERR_MEMORY;
1449 cur_line->type = FILE_LINE_UNPROCESSED;
1451 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed, newline, NULL);
1454 /* Out of memory or empty file. */
1455 /* Note that empty file is not an error we propogate up */
1457 return ((rval == JB_ERR_FILE) ? JB_ERR_OK : rval);
1462 prev_line = cur_line;
1463 cur_line = prev_line->next = zalloc(sizeof(struct file_line));
1464 if (cur_line == NULL)
1467 edit_free_file_lines(first_line);
1468 return JB_ERR_MEMORY;
1471 cur_line->type = FILE_LINE_UNPROCESSED;
1473 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed, newline, NULL);
1474 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
1477 edit_free_file_lines(first_line);
1478 return JB_ERR_MEMORY;
1482 while (rval != JB_ERR_FILE);
1486 /* We allocated one too many - free it */
1487 prev_line->next = NULL;
1490 *pfile = first_line;
1495 /*********************************************************************
1497 * Function : edit_read_file
1499 * Description : Read a complete file into memory.
1500 * Handles CGI parameter parsing. If requested, also
1501 * checks the file's modification timestamp.
1504 * 1 : csp = Current client state (buffers, headers, etc...)
1505 * 2 : parameters = map of cgi parameters.
1506 * 3 : require_version = true to check "ver" parameter.
1507 * 4 : suffix = File extension, e.g. ".action".
1508 * 5 : pfile = Destination for the file. Will be set
1512 * filename : The name of the file to read, without the
1513 * path or ".action" extension.
1514 * ver : (Only if require_version is nonzero)
1515 * Timestamp of the actions file. If wrong, this
1516 * function fails with JB_ERR_MODIFIED.
1518 * Returns : JB_ERR_OK on success
1519 * JB_ERR_MEMORY on out-of-memory
1520 * JB_ERR_CGI_PARAMS if "filename" was not specified
1522 * JB_ERR_FILE if the file cannot be opened or
1524 * JB_ERR_MODIFIED if version checking was requested and
1525 * failed - the file was modified outside
1526 * of this CGI editor instance.
1528 *********************************************************************/
1529 jb_err edit_read_file(struct client_state *csp,
1530 const struct map *parameters,
1531 int require_version,
1533 struct editable_file **pfile)
1535 struct file_line * lines;
1539 const char * identifier;
1540 struct editable_file * file;
1541 unsigned version = 0;
1542 struct stat statbuf[1];
1543 char version_buf[22];
1544 int newline = NEWLINE_UNKNOWN;
1552 err = get_file_name_param(csp, parameters, "f", suffix,
1553 &filename, &identifier);
1559 if (stat(filename, statbuf) < 0)
1561 /* Error, probably file not found. */
1565 version = (unsigned) statbuf->st_mtime;
1567 if (require_version)
1569 unsigned specified_version;
1570 err = get_number_param(csp, parameters, "v", &specified_version);
1577 if (version != specified_version)
1579 return JB_ERR_MODIFIED;
1583 #if defined(AMIGA) || defined(__OS2__)
1584 if (NULL == (fp = fopen(filename,"r")))
1586 if (NULL == (fp = fopen(filename,"rt")))
1587 #endif /* def AMIGA */
1593 err = edit_read_file_lines(fp, &lines, &newline);
1603 file = (struct editable_file *) zalloc(sizeof(*file));
1607 edit_free_file_lines(lines);
1611 file->lines = lines;
1612 file->newline = newline;
1613 file->filename = filename;
1614 file->version = version;
1615 file->identifier = url_encode(identifier);
1617 if (file->identifier == NULL)
1619 edit_free_file(file);
1620 return JB_ERR_MEMORY;
1623 /* Correct file->version_str */
1624 freez(file->version_str);
1625 snprintf(version_buf, 22, "%u", file->version);
1626 version_buf[21] = '\0';
1627 file->version_str = strdup(version_buf);
1628 if (version_buf == NULL)
1630 edit_free_file(file);
1631 return JB_ERR_MEMORY;
1639 /*********************************************************************
1641 * Function : edit_read_actions_file
1643 * Description : Read a complete actions file into memory.
1644 * Handles CGI parameter parsing. If requested, also
1645 * checks the file's modification timestamp.
1647 * If this function detects an error in the categories
1648 * JB_ERR_FILE, JB_ERR_MODIFIED, or JB_ERR_PARSE,
1649 * then it handles it by filling in the specified
1650 * response structure and returning JB_ERR_FILE.
1653 * 1 : csp = Current client state (buffers, headers, etc...)
1654 * 2 : rsp = HTTP response. Only filled in on error.
1655 * 2 : parameters = map of cgi parameters.
1656 * 3 : require_version = true to check "ver" parameter.
1657 * 4 : pfile = Destination for the file. Will be set
1661 * filename : The name of the actions file to read, without the
1662 * path or ".action" extension.
1663 * ver : (Only if require_version is nonzero)
1664 * Timestamp of the actions file. If wrong, this
1665 * function fails with JB_ERR_MODIFIED.
1667 * Returns : JB_ERR_OK on success
1668 * JB_ERR_MEMORY on out-of-memory
1669 * JB_ERR_CGI_PARAMS if "filename" was not specified
1671 * JB_ERR_FILE if the file does not contain valid data,
1672 * or if file cannot be opened or
1673 * contains no data, or if version
1674 * checking was requested and failed.
1676 *********************************************************************/
1677 jb_err edit_read_actions_file(struct client_state *csp,
1678 struct http_response *rsp,
1679 const struct map *parameters,
1680 int require_version,
1681 struct editable_file **pfile)
1684 struct editable_file *file;
1692 err = edit_read_file(csp, parameters, require_version, ".action", &file);
1695 /* Try to handle if possible */
1696 if (err == JB_ERR_FILE)
1698 err = cgi_error_file(csp, rsp, lookup(parameters, "f"));
1700 else if (err == JB_ERR_MODIFIED)
1702 err = cgi_error_modified(csp, rsp, lookup(parameters, "f"));
1704 if (err == JB_ERR_OK)
1707 * Signal to higher-level CGI code that there was a problem but we
1708 * handled it, they should just return JB_ERR_OK.
1715 err = edit_parse_actions_file(file);
1718 if (err == JB_ERR_PARSE)
1720 err = cgi_error_parse(csp, rsp, file);
1721 if (err == JB_ERR_OK)
1724 * Signal to higher-level CGI code that there was a problem but we
1725 * handled it, they should just return JB_ERR_OK.
1730 edit_free_file(file);
1739 /*********************************************************************
1741 * Function : get_file_name_param
1743 * Description : Get the name of the file to edit from the parameters
1744 * passed to a CGI function. This function handles
1745 * security checks such as blocking urls containing
1746 * "/" or ".", prepending the config file directory,
1747 * and adding the specified suffix.
1749 * (This is an essential security check, otherwise
1750 * users may be able to pass "../../../etc/passwd"
1751 * and overwrite the password file [linux], "prn:"
1752 * and print random data [Windows], etc...)
1754 * This function only allows filenames contining the
1755 * characters '-', '_', 'A'-'Z', 'a'-'z', and '0'-'9'.
1756 * That's probably too restrictive but at least it's
1760 * 1 : csp = Current client state (buffers, headers, etc...)
1761 * 2 : parameters = map of cgi parameters
1762 * 3 : param_name = The name of the parameter to read
1763 * 4 : suffix = File extension, e.g. ".actions"
1764 * 5 : pfilename = destination for full filename. Caller
1765 * free()s. Set to NULL on error.
1766 * 6 : pparam = destination for partial filename,
1767 * suitable for use in another URL. Allocated as part
1768 * of the map "parameters", so don't free it.
1769 * Set to NULL if not specified.
1771 * Returns : JB_ERR_OK on success
1772 * JB_ERR_MEMORY on out-of-memory
1773 * JB_ERR_CGI_PARAMS if "filename" was not specified
1776 *********************************************************************/
1777 static jb_err get_file_name_param(struct client_state *csp,
1778 const struct map *parameters,
1779 const char *param_name,
1782 const char **pparam)
1786 #if 0 /* Patch to make 3.0.0 work properly. */
1788 #endif /* 0 - Patch to make 3.0.0 work properly. */
1802 param = lookup(parameters, param_name);
1805 return JB_ERR_CGI_PARAMS;
1810 len = strlen(param);
1811 if (len >= FILENAME_MAX)
1814 return JB_ERR_CGI_PARAMS;
1817 /* Check every character to see if it's legal */
1819 while ((ch = *s++) != '\0')
1821 if ( ((ch < 'A') || (ch > 'Z'))
1822 && ((ch < 'a') || (ch > 'z'))
1823 && ((ch < '0') || (ch > '9'))
1827 /* Probable hack attempt. */
1828 return JB_ERR_CGI_PARAMS;
1833 * FIXME Following is a hack to make 3.0.0 work properly.
1834 * Change "#if 0" --> "#if 1" below when we have modular action
1838 #if 0 /* Patch to make 3.0.0 work properly. */
1839 /* Append extension */
1840 name = malloc(len + strlen(suffix) + 1);
1843 return JB_ERR_MEMORY;
1845 strcpy(name, param);
1846 strcpy(name + len, suffix);
1849 fullpath = make_path(csp->config->confdir, name);
1851 #else /* 1 - Patch to make 3.0.0 work properly. */
1852 if ((csp->actions_list == NULL)
1853 || (csp->actions_list->filename == NULL))
1855 return JB_ERR_CGI_PARAMS;
1858 fullpath = ( (csp->actions_list && csp->actions_list->filename)
1859 ? strdup(csp->actions_list->filename) : NULL);
1860 #endif /* 1 - Patch to make 3.0.0 work properly. */
1861 if (fullpath == NULL)
1863 return JB_ERR_MEMORY;
1867 *pfilename = fullpath;
1873 /*********************************************************************
1875 * Function : get_char_param
1877 * Description : Get a single-character parameter passed to a CGI
1881 * 1 : parameters = map of cgi parameters
1882 * 2 : param_name = The name of the parameter to read
1884 * Returns : Uppercase character on success, '\0' on error.
1886 *********************************************************************/
1887 static char get_char_param(const struct map *parameters,
1888 const char *param_name)
1895 ch = *(lookup(parameters, param_name));
1896 if ((ch >= 'a') && (ch <= 'z'))
1898 ch = ch - 'a' + 'A';
1905 /*********************************************************************
1907 * Function : get_string_param
1909 * Description : Get a string paramater, to be used as an
1910 * ACTION_STRING or ACTION_MULTI paramater.
1911 * Validates the input to prevent stupid/malicious
1912 * users from corrupting their action file.
1915 * 1 : parameters = map of cgi parameters
1916 * 2 : param_name = The name of the parameter to read
1917 * 3 : pparam = destination for paramater. Allocated as
1918 * part of the map "parameters", so don't free it.
1919 * Set to NULL if not specified.
1921 * Returns : JB_ERR_OK on success, or if the paramater
1922 * was not specified.
1923 * JB_ERR_MEMORY on out-of-memory.
1924 * JB_ERR_CGI_PARAMS if the paramater is not valid.
1926 *********************************************************************/
1927 static jb_err get_string_param(const struct map *parameters,
1928 const char *param_name,
1929 const char **pparam)
1941 param = lookup(parameters, param_name);
1947 if (strlen(param) >= CGI_ACTION_PARAM_LEN_MAX)
1952 * Note that the length limit is arbitrary, it just seems
1953 * sensible to limit it to *something*. There's no
1954 * technical reason for any limit at all.
1956 return JB_ERR_CGI_PARAMS;
1959 /* Check every character to see if it's legal */
1961 while ((ch = *s++) != '\0')
1963 if ( ((unsigned char)ch < (unsigned char)' ')
1966 /* Probable hack attempt, or user accidentally used '}'. */
1967 return JB_ERR_CGI_PARAMS;
1978 /*********************************************************************
1980 * Function : get_number_param
1982 * Description : Get a non-negative integer from the parameters
1983 * passed to a CGI function.
1986 * 1 : csp = Current client state (buffers, headers, etc...)
1987 * 2 : parameters = map of cgi parameters
1988 * 3 : name = Name of CGI parameter to read
1989 * 4 : pvalue = destination for value.
1990 * Set to -1 on error.
1992 * Returns : JB_ERR_OK on success
1993 * JB_ERR_MEMORY on out-of-memory
1994 * JB_ERR_CGI_PARAMS if the parameter was not specified
1997 *********************************************************************/
1998 static jb_err get_number_param(struct client_state *csp,
1999 const struct map *parameters,
2014 param = lookup(parameters, name);
2017 return JB_ERR_CGI_PARAMS;
2020 /* We don't use atoi because I want to check this carefully... */
2023 while ((ch = *param++) != '\0')
2025 if ((ch < '0') || (ch > '9'))
2027 return JB_ERR_CGI_PARAMS;
2034 * <limits.h> defines UINT_MAX
2036 * (UINT_MAX - ch) / 10 is the largest number that
2037 * can be safely multiplied by 10 then have ch added.
2039 if (value > ((UINT_MAX - (unsigned)ch) / 10U))
2041 return JB_ERR_CGI_PARAMS;
2044 value = value * 10 + ch;
2055 /*********************************************************************
2057 * Function : get_url_spec_param
2059 * Description : Get a URL pattern from the parameters
2060 * passed to a CGI function. Removes leading/trailing
2061 * spaces and validates it.
2064 * 1 : csp = Current client state (buffers, headers, etc...)
2065 * 2 : parameters = map of cgi parameters
2066 * 3 : name = Name of CGI parameter to read
2067 * 4 : pvalue = destination for value. Will be malloc()'d.
2068 * Set to NULL on error.
2070 * Returns : JB_ERR_OK on success
2071 * JB_ERR_MEMORY on out-of-memory
2072 * JB_ERR_CGI_PARAMS if the parameter was not specified
2075 *********************************************************************/
2076 static jb_err get_url_spec_param(struct client_state *csp,
2077 const struct map *parameters,
2081 const char *orig_param;
2084 struct url_spec compiled[1];
2094 orig_param = lookup(parameters, name);
2097 return JB_ERR_CGI_PARAMS;
2100 /* Copy and trim whitespace */
2101 param = strdup(orig_param);
2104 return JB_ERR_MEMORY;
2108 /* Must be non-empty, and can't allow 1st character to be '{' */
2109 if (param[0] == '\0' || param[0] == '{')
2112 return JB_ERR_CGI_PARAMS;
2115 /* Check for embedded newlines */
2116 for (s = param; *s != '\0'; s++)
2118 if ((*s == '\r') || (*s == '\n'))
2121 return JB_ERR_CGI_PARAMS;
2125 /* Check that regex is valid */
2130 return JB_ERR_MEMORY;
2132 err = create_url_spec(compiled, s);
2137 return (err == JB_ERR_MEMORY) ? JB_ERR_MEMORY : JB_ERR_CGI_PARAMS;
2139 free_url_spec(compiled);
2141 if (param[strlen(param) - 1] == '\\')
2144 * Must protect trailing '\\' from becoming line continuation character.
2145 * Two methods: 1) If it's a domain only, add a trailing '/'.
2146 * 2) For path, add the do-nothing PCRE expression (?:) to the end
2148 if (strchr(param, '/') == NULL)
2150 err = string_append(¶m, "/");
2154 err = string_append(¶m, "(?:)");
2161 /* Check that the modified regex is valid */
2166 return JB_ERR_MEMORY;
2168 err = create_url_spec(compiled, s);
2173 return (err == JB_ERR_MEMORY) ? JB_ERR_MEMORY : JB_ERR_CGI_PARAMS;
2175 free_url_spec(compiled);
2182 /*********************************************************************
2184 * Function : map_radio
2186 * Description : Map a set of radio button values. E.g. if you have
2187 * 3 radio buttons, declare them as:
2188 * <option type="radio" name="xyz" @xyz-a@>
2189 * <option type="radio" name="xyz" @xyz-b@>
2190 * <option type="radio" name="xyz" @xyz-c@>
2191 * Then map one of the @xyz-?@ variables to "checked"
2192 * and all the others to empty by calling:
2193 * map_radio(exports, "xyz", "abc", sel)
2194 * Where 'sel' is 'a', 'b', or 'c'.
2197 * 1 : exports = Exports map to modify.
2198 * 2 : optionname = name for map
2199 * 3 : values = null-terminated list of values;
2200 * 4 : value = Selected value.
2202 * CGI Parameters : None
2204 * Returns : JB_ERR_OK on success
2205 * JB_ERR_MEMORY on out-of-memory
2207 *********************************************************************/
2208 static jb_err map_radio(struct map * exports,
2209 const char * optionname,
2210 const char * values,
2222 len = strlen(optionname);
2223 buf = malloc(len + 3);
2226 return JB_ERR_MEMORY;
2229 strcpy(buf, optionname);
2234 while ((c = *values++) != '\0')
2239 if (map(exports, buf, 1, "", 1))
2241 return JB_ERR_MEMORY;
2247 return map(exports, buf, 0, "checked", 1);
2251 /*********************************************************************
2253 * Function : cgi_error_modified
2255 * Description : CGI function that is called when a file is modified
2256 * outside the CGI editor.
2259 * 1 : csp = Current client state (buffers, headers, etc...)
2260 * 2 : rsp = http_response data structure for output
2261 * 3 : filename = The file that was modified.
2263 * CGI Parameters : none
2265 * Returns : JB_ERR_OK on success
2266 * JB_ERR_MEMORY on out-of-memory error.
2268 *********************************************************************/
2269 jb_err cgi_error_modified(struct client_state *csp,
2270 struct http_response *rsp,
2271 const char *filename)
2273 struct map *exports;
2280 if (NULL == (exports = default_exports(csp, NULL)))
2282 return JB_ERR_MEMORY;
2285 err = map(exports, "f", 1, html_encode(filename), 0);
2292 return template_fill_for_cgi(csp, "cgi-error-modified", exports, rsp);
2296 /*********************************************************************
2298 * Function : cgi_error_parse
2300 * Description : CGI function that is called when a file cannot
2301 * be parsed by the CGI editor.
2304 * 1 : csp = Current client state (buffers, headers, etc...)
2305 * 2 : rsp = http_response data structure for output
2306 * 3 : file = The file that was modified.
2308 * CGI Parameters : none
2310 * Returns : JB_ERR_OK on success
2311 * JB_ERR_MEMORY on out-of-memory error.
2313 *********************************************************************/
2314 jb_err cgi_error_parse(struct client_state *csp,
2315 struct http_response *rsp,
2316 struct editable_file *file)
2318 struct map *exports;
2320 struct file_line *cur_line;
2326 if (NULL == (exports = default_exports(csp, NULL)))
2328 return JB_ERR_MEMORY;
2331 err = map(exports, "f", 1, file->identifier, 1);
2332 if (!err) err = map(exports, "parse-error", 1, html_encode(file->parse_error_text), 0);
2334 cur_line = file->parse_error;
2337 if (!err) err = map(exports, "line-raw", 1, html_encode(cur_line->raw), 0);
2338 if (!err) err = map(exports, "line-data", 1, html_encode(cur_line->unprocessed), 0);
2346 return template_fill_for_cgi(csp, "cgi-error-parse", exports, rsp);
2350 /*********************************************************************
2352 * Function : cgi_error_file
2354 * Description : CGI function that is called when a file cannot be
2355 * opened by the CGI editor.
2358 * 1 : csp = Current client state (buffers, headers, etc...)
2359 * 2 : rsp = http_response data structure for output
2360 * 3 : filename = The file that was modified.
2362 * CGI Parameters : none
2364 * Returns : JB_ERR_OK on success
2365 * JB_ERR_MEMORY on out-of-memory error.
2367 *********************************************************************/
2368 jb_err cgi_error_file(struct client_state *csp,
2369 struct http_response *rsp,
2370 const char *filename)
2372 struct map *exports;
2379 if (NULL == (exports = default_exports(csp, NULL)))
2381 return JB_ERR_MEMORY;
2384 err = map(exports, "f", 1, html_encode(filename), 0);
2391 return template_fill_for_cgi(csp, "cgi-error-file", exports, rsp);
2395 /*********************************************************************
2397 * Function : cgi_error_bad_param
2399 * Description : CGI function that is called if the parameters
2400 * (query string) for a CGI were wrong.
2403 * 1 : csp = Current client state (buffers, headers, etc...)
2404 * 2 : rsp = http_response data structure for output
2406 * CGI Parameters : none
2408 * Returns : JB_ERR_OK on success
2409 * JB_ERR_MEMORY on out-of-memory error.
2411 *********************************************************************/
2412 jb_err cgi_error_disabled(struct client_state *csp,
2413 struct http_response *rsp)
2415 struct map *exports;
2420 if (NULL == (exports = default_exports(csp, NULL)))
2422 return JB_ERR_MEMORY;
2425 return template_fill_for_cgi(csp, "cgi-error-disabled", exports, rsp);
2429 /*********************************************************************
2431 * Function : cgi_edit_actions
2433 * Description : CGI function that allows the user to choose which
2434 * actions file to edit.
2437 * 1 : csp = Current client state (buffers, headers, etc...)
2438 * 2 : rsp = http_response data structure for output
2439 * 3 : parameters = map of cgi parameters
2441 * CGI Parameters : None
2443 * Returns : JB_ERR_OK on success
2444 * JB_ERR_MEMORY on out-of-memory error
2446 *********************************************************************/
2447 jb_err cgi_edit_actions(struct client_state *csp,
2448 struct http_response *rsp,
2449 const struct map *parameters)
2452 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2454 return cgi_error_disabled(csp, rsp);
2457 /* FIXME: Incomplete */
2458 rsp->status = strdup("302 Local Redirect from Privoxy");
2459 if (rsp->status == NULL)
2461 return JB_ERR_MEMORY;
2463 if (enlist_unique_header(rsp->headers, "Location",
2464 CGI_PREFIX "edit-actions-list?f=default"))
2468 return JB_ERR_MEMORY;
2475 /*********************************************************************
2477 * Function : cgi_edit_actions_list
2479 * Description : CGI function that edits the actions list.
2480 * FIXME: This function shouldn't FATAL ever.
2481 * FIXME: This function doesn't check the retval of map()
2483 * 1 : csp = Current client state (buffers, headers, etc...)
2484 * 2 : rsp = http_response data structure for output
2485 * 3 : parameters = map of cgi parameters
2487 * CGI Parameters : filename
2489 * Returns : JB_ERR_OK on success
2490 * JB_ERR_MEMORY on out-of-memory
2491 * JB_ERR_FILE if the file cannot be opened or
2493 * JB_ERR_CGI_PARAMS if "filename" was not specified
2496 *********************************************************************/
2497 jb_err cgi_edit_actions_list(struct client_state *csp,
2498 struct http_response *rsp,
2499 const struct map *parameters)
2501 char * section_template;
2502 char * url_template;
2507 struct map * exports;
2508 struct map * section_exports;
2509 struct map * url_exports;
2510 struct editable_file * file;
2511 struct file_line * cur_line;
2512 unsigned line_number = 0;
2513 unsigned prev_section_line_number = ((unsigned) (-1));
2517 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2519 return cgi_error_disabled(csp, rsp);
2522 err = edit_read_actions_file(csp, rsp, parameters, 0, &file);
2525 /* No filename specified, can't read file, or out of memory. */
2526 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2529 if (NULL == (exports = default_exports(csp, NULL)))
2531 edit_free_file(file);
2532 return JB_ERR_MEMORY;
2535 err = map(exports, "f", 1, file->identifier, 1);
2536 if (!err) err = map(exports, "v", 1, file->version_str, 1);
2540 edit_free_file(file);
2545 /* Should do all global exports above this point */
2547 err = template_load(csp, §ion_template, "edit-actions-list-section");
2550 edit_free_file(file);
2552 if (err == JB_ERR_FILE)
2554 return cgi_error_no_template(csp, rsp, "edit-actions-list-section");
2559 err = template_load(csp, &url_template, "edit-actions-list-url");
2562 free(section_template);
2563 edit_free_file(file);
2565 if (err == JB_ERR_FILE)
2567 return cgi_error_no_template(csp, rsp, "edit-actions-list-url");
2572 err = template_fill(§ion_template, exports);
2576 edit_free_file(file);
2582 err = template_fill(&url_template, exports);
2585 free(section_template);
2586 edit_free_file(file);
2591 /* Find start of actions in file */
2592 cur_line = file->lines;
2594 while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION))
2596 cur_line = cur_line->next;
2600 if (NULL == (sections = strdup("")))
2602 free(section_template);
2604 edit_free_file(file);
2606 return JB_ERR_MEMORY;
2609 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_ACTION))
2611 if (NULL == (section_exports = new_map()))
2614 free(section_template);
2616 edit_free_file(file);
2618 return JB_ERR_MEMORY;
2621 snprintf(buf, 50, "%d", line_number);
2622 err = map(section_exports, "s", 1, buf, 1);
2623 if (!err) err = map(section_exports, "actions", 1,
2624 actions_to_html(cur_line->data.action), 0);
2627 && (cur_line->next != NULL)
2628 && (cur_line->next->type == FILE_LINE_URL))
2630 /* This section contains at least one URL, don't allow delete */
2631 err = map_block_killer(section_exports, "empty-section");
2635 if (!err) err = map_block_keep(section_exports, "empty-section");
2638 if (prev_section_line_number != ((unsigned)(-1)))
2640 /* Not last section */
2641 snprintf(buf, 50, "%d", prev_section_line_number);
2642 if (!err) err = map(section_exports, "s-prev", 1, buf, 1);
2643 if (!err) err = map_block_keep(section_exports, "s-prev-exists");
2648 if (!err) err = map_block_killer(section_exports, "s-prev-exists");
2650 prev_section_line_number = line_number;
2655 free(section_template);
2657 edit_free_file(file);
2659 free_map(section_exports);
2663 /* Should do all section-specific exports above this point */
2665 if (NULL == (urls = strdup("")))
2668 free(section_template);
2670 edit_free_file(file);
2672 free_map(section_exports);
2673 return JB_ERR_MEMORY;
2678 cur_line = cur_line->next;
2681 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL))
2683 if (NULL == (url_exports = new_map()))
2687 free(section_template);
2689 edit_free_file(file);
2691 free_map(section_exports);
2692 return JB_ERR_MEMORY;
2695 snprintf(buf, 50, "%d", line_number);
2696 err = map(url_exports, "p", 1, buf, 1);
2698 snprintf(buf, 50, "%d", url_1_2);
2699 if (!err) err = map(url_exports, "url-1-2", 1, buf, 1);
2701 if (!err) err = map(url_exports, "url-html", 1,
2702 html_encode(cur_line->unprocessed), 0);
2703 if (!err) err = map(url_exports, "url", 1,
2704 url_encode(cur_line->unprocessed), 0);
2710 free(section_template);
2712 edit_free_file(file);
2714 free_map(section_exports);
2715 free_map(url_exports);
2719 if (NULL == (s = strdup(url_template)))
2723 free(section_template);
2725 edit_free_file(file);
2727 free_map(section_exports);
2728 free_map(url_exports);
2729 return JB_ERR_MEMORY;
2732 err = template_fill(&s, section_exports);
2733 if (!err) err = template_fill(&s, url_exports);
2734 if (!err) err = string_append(&urls, s);
2736 free_map(url_exports);
2743 free(section_template);
2745 edit_free_file(file);
2747 free_map(section_exports);
2751 url_1_2 = 3 - url_1_2;
2753 cur_line = cur_line->next;
2757 err = map(section_exports, "urls", 1, urls, 0);
2759 /* Could also do section-specific exports here, but it wouldn't be as fast */
2761 if ( (cur_line != NULL)
2762 && (cur_line->type == FILE_LINE_ACTION))
2764 /* Not last section */
2765 snprintf(buf, 50, "%d", line_number);
2766 if (!err) err = map(section_exports, "s-next", 1, buf, 1);
2767 if (!err) err = map_block_keep(section_exports, "s-next-exists");
2772 if (!err) err = map_block_killer(section_exports, "s-next-exists");
2778 free(section_template);
2780 edit_free_file(file);
2782 free_map(section_exports);
2786 if (NULL == (s = strdup(section_template)))
2789 free(section_template);
2791 edit_free_file(file);
2793 free_map(section_exports);
2794 return JB_ERR_MEMORY;
2797 err = template_fill(&s, section_exports);
2798 if (!err) err = string_append(§ions, s);
2801 free_map(section_exports);
2806 free(section_template);
2808 edit_free_file(file);
2814 edit_free_file(file);
2815 free(section_template);
2818 err = map(exports, "sections", 1, sections, 0);
2825 /* Could also do global exports here, but it wouldn't be as fast */
2827 return template_fill_for_cgi(csp, "edit-actions-list", exports, rsp);
2831 /*********************************************************************
2833 * Function : cgi_edit_actions
2835 * Description : CGI function that edits the Actions list.
2838 * 1 : csp = Current client state (buffers, headers, etc...)
2839 * 2 : rsp = http_response data structure for output
2840 * 3 : parameters = map of cgi parameters
2842 * CGI Parameters : None
2844 * Returns : JB_ERR_OK on success
2845 * JB_ERR_MEMORY on out-of-memory
2846 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2847 * specified or not valid.
2849 *********************************************************************/
2850 jb_err cgi_edit_actions_for_url(struct client_state *csp,
2851 struct http_response *rsp,
2852 const struct map *parameters)
2854 struct map * exports;
2856 struct editable_file * file;
2857 struct file_line * cur_line;
2858 unsigned line_number;
2860 struct file_list *filter_file;
2861 struct re_filterfile_spec *filter_group;
2863 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2865 return cgi_error_disabled(csp, rsp);
2868 err = get_number_param(csp, parameters, "s", §ionid);
2874 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2877 /* No filename specified, can't read file, modified, or out of memory. */
2878 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2881 cur_line = file->lines;
2883 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
2885 cur_line = cur_line->next;
2888 if ( (cur_line == NULL)
2889 || (line_number != sectionid)
2891 || (cur_line->type != FILE_LINE_ACTION))
2893 /* Invalid "sectionid" parameter */
2894 edit_free_file(file);
2895 return JB_ERR_CGI_PARAMS;
2898 if (NULL == (exports = default_exports(csp, NULL)))
2900 edit_free_file(file);
2901 return JB_ERR_MEMORY;
2904 err = map(exports, "f", 1, file->identifier, 1);
2905 if (!err) err = map(exports, "v", 1, file->version_str, 1);
2906 if (!err) err = map(exports, "s", 1, url_encode(lookup(parameters, "s")), 0);
2908 if (!err) err = actions_to_radio(exports, cur_line->data.action);
2910 filter_file = csp->rlist;
2911 filter_group = ((filter_file != NULL) ? filter_file->f : NULL);
2913 if (!err) err = map_conditional(exports, "any-filters-defined", (filter_group != NULL));
2917 edit_free_file(file);
2922 if (filter_group == NULL)
2924 err = map(exports, "filter-params", 1, "", 1);
2928 /* We have some entries in the filter list */
2931 char * filter_template;
2933 err = template_load(csp, &filter_template, "edit-actions-for-url-filter");
2936 edit_free_file(file);
2938 if (err == JB_ERR_FILE)
2940 return cgi_error_no_template(csp, rsp, "edit-actions-for-url-filter");
2945 result = strdup("");
2947 for (;(!err) && (filter_group != NULL); filter_group = filter_group->next)
2949 char current_mode = 'x';
2950 struct list_entry *filter_name;
2952 struct map *line_exports;
2955 filter_name = cur_line->data.action->multi_add[ACTION_MULTI_FILTER]->first;
2956 while ((filter_name != NULL)
2957 && (0 != strcmp(filter_group->name, filter_name->str)))
2959 filter_name = filter_name->next;
2962 if (filter_name != NULL)
2968 filter_name = cur_line->data.action->multi_remove[ACTION_MULTI_FILTER]->first;
2969 while ((filter_name != NULL)
2970 && (0 != strcmp(filter_group->name, filter_name->str)))
2972 filter_name = filter_name->next;
2974 if (filter_name != NULL)
2980 /* Generate a unique serial number */
2981 snprintf(number, sizeof(number), "%x", index++);
2982 number[sizeof(number) - 1] = '\0';
2984 line_exports = new_map();
2985 if (line_exports == NULL)
2987 err = JB_ERR_MEMORY;
2992 if (!err) err = map(line_exports, "index", 1, number, 1);
2993 if (!err) err = map(line_exports, "name", 1, filter_group->name, 1);
2994 if (!err) err = map(line_exports, "description", 1, filter_group->description, 1);
2995 if (!err) err = map_radio(line_exports, "this-filter", "ynx", current_mode);
3000 this_line = strdup(filter_template);
3001 if (this_line == NULL) err = JB_ERR_MEMORY;
3003 if (!err) err = template_fill(&this_line, line_exports);
3004 string_join(&result, this_line);
3006 free_map(line_exports);
3011 err = map(exports, "filter-params", 1, result, 0);
3019 if (!err) err = map_radio(exports, "filter-all", "nx",
3020 (cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] ? 'n' : 'x'));
3022 edit_free_file(file);
3030 return template_fill_for_cgi(csp, "edit-actions-for-url", exports, rsp);
3034 /*********************************************************************
3036 * Function : cgi_edit_actions_submit
3038 * Description : CGI function that actually edits the Actions list.
3041 * 1 : csp = Current client state (buffers, headers, etc...)
3042 * 2 : rsp = http_response data structure for output
3043 * 3 : parameters = map of cgi parameters
3045 * CGI Parameters : None
3047 * Returns : JB_ERR_OK on success
3048 * JB_ERR_MEMORY on out-of-memory
3049 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3050 * specified or not valid.
3052 *********************************************************************/
3053 jb_err cgi_edit_actions_submit(struct client_state *csp,
3054 struct http_response *rsp,
3055 const struct map *parameters)
3061 struct editable_file * file;
3062 struct file_line * cur_line;
3063 unsigned line_number;
3069 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3071 return cgi_error_disabled(csp, rsp);
3074 err = get_number_param(csp, parameters, "s", §ionid);
3080 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3083 /* No filename specified, can't read file, modified, or out of memory. */
3084 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3087 cur_line = file->lines;
3089 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
3091 cur_line = cur_line->next;
3094 if ( (cur_line == NULL)
3095 || (line_number != sectionid)
3097 || (cur_line->type != FILE_LINE_ACTION))
3099 /* Invalid "sectionid" parameter */
3100 edit_free_file(file);
3101 return JB_ERR_CGI_PARAMS;
3104 err = actions_from_radio(parameters, cur_line->data.action);
3108 edit_free_file(file);
3112 ch = get_char_param(parameters, "filter_all");
3115 list_remove_all(cur_line->data.action->multi_add[ACTION_MULTI_FILTER]);
3116 list_remove_all(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER]);
3117 cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] = 1;
3121 cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] = 0;
3124 for (index = 0; !err; index++)
3131 /* Generate the keys */
3132 snprintf(key_value, sizeof(key_value), "filter_r%x", index);
3133 key_value[sizeof(key_value) - 1] = '\0';
3134 snprintf(key_name, sizeof(key_name), "filter_n%x", index);
3135 key_name[sizeof(key_name) - 1] = '\0';
3137 err = get_string_param(parameters, key_name, &name);
3147 value = get_char_param(parameters, key_value);
3150 list_remove_item(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name);
3151 if (!err) err = enlist(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name);
3152 list_remove_item(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name);
3154 else if (value == 'N')
3156 list_remove_item(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name);
3157 if (!cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER])
3159 list_remove_item(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name);
3160 if (!err) err = enlist(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name);
3163 else if (value == 'X')
3165 list_remove_item(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name);
3166 list_remove_item(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name);
3173 edit_free_file(file);
3177 if (NULL == (actiontext = actions_to_text(cur_line->data.action)))
3180 edit_free_file(file);
3181 return JB_ERR_MEMORY;
3184 len = strlen(actiontext);
3188 * Empty action - must special-case this.
3189 * Simply setting len to 1 is sufficient...
3194 if (NULL == (newtext = malloc(len + 2)))
3198 edit_free_file(file);
3199 return JB_ERR_MEMORY;
3201 strcpy(newtext, actiontext);
3205 newtext[len + 1] = '\0';
3207 freez(cur_line->raw);
3208 freez(cur_line->unprocessed);
3209 cur_line->unprocessed = newtext;
3211 err = edit_write_file(file);
3214 /* Error writing file */
3215 edit_free_file(file);
3219 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3220 string_append(&target, file->identifier);
3222 edit_free_file(file);
3227 return JB_ERR_MEMORY;
3230 rsp->status = strdup("302 Local Redirect from Privoxy");
3231 if (rsp->status == NULL)
3234 return JB_ERR_MEMORY;
3236 err = enlist_unique_header(rsp->headers, "Location", target);
3243 /*********************************************************************
3245 * Function : cgi_edit_actions_url
3247 * Description : CGI function that actually edits a URL pattern in
3251 * 1 : csp = Current client state (buffers, headers, etc...)
3252 * 2 : rsp = http_response data structure for output
3253 * 3 : parameters = map of cgi parameters
3256 * filename : Identifies the file to edit
3257 * ver : File's last-modified time
3258 * section : Line number of section to edit
3259 * pattern : Line number of pattern to edit
3260 * newval : New value for pattern
3262 * Returns : JB_ERR_OK on success
3263 * JB_ERR_MEMORY on out-of-memory
3264 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3265 * specified or not valid.
3267 *********************************************************************/
3268 jb_err cgi_edit_actions_url(struct client_state *csp,
3269 struct http_response *rsp,
3270 const struct map *parameters)
3274 struct editable_file * file;
3275 struct file_line * cur_line;
3276 unsigned line_number;
3280 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3282 return cgi_error_disabled(csp, rsp);
3285 err = get_number_param(csp, parameters, "p", &patternid);
3292 return JB_ERR_CGI_PARAMS;
3295 err = get_url_spec_param(csp, parameters, "u", &new_pattern);
3301 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3304 /* No filename specified, can't read file, modified, or out of memory. */
3306 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3310 cur_line = file->lines;
3312 while ((cur_line != NULL) && (line_number < patternid))
3314 cur_line = cur_line->next;
3318 if ( (cur_line == NULL)
3319 || (cur_line->type != FILE_LINE_URL))
3321 /* Invalid "patternid" parameter */
3323 edit_free_file(file);
3324 return JB_ERR_CGI_PARAMS;
3327 /* At this point, the line to edit is in cur_line */
3329 freez(cur_line->raw);
3330 freez(cur_line->unprocessed);
3331 cur_line->unprocessed = new_pattern;
3333 err = edit_write_file(file);
3336 /* Error writing file */
3337 edit_free_file(file);
3341 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3342 string_append(&target, file->identifier);
3344 edit_free_file(file);
3349 return JB_ERR_MEMORY;
3352 rsp->status = strdup("302 Local Redirect from Privoxy");
3353 if (rsp->status == NULL)
3356 return JB_ERR_MEMORY;
3358 err = enlist_unique_header(rsp->headers, "Location", target);
3365 /*********************************************************************
3367 * Function : cgi_edit_actions_add_url
3369 * Description : CGI function that actually adds a URL pattern to
3373 * 1 : csp = Current client state (buffers, headers, etc...)
3374 * 2 : rsp = http_response data structure for output
3375 * 3 : parameters = map of cgi parameters
3378 * filename : Identifies the file to edit
3379 * ver : File's last-modified time
3380 * section : Line number of section to edit
3381 * newval : New pattern
3383 * Returns : JB_ERR_OK on success
3384 * JB_ERR_MEMORY on out-of-memory
3385 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3386 * specified or not valid.
3388 *********************************************************************/
3389 jb_err cgi_edit_actions_add_url(struct client_state *csp,
3390 struct http_response *rsp,
3391 const struct map *parameters)
3395 struct file_line * new_line;
3396 struct editable_file * file;
3397 struct file_line * cur_line;
3398 unsigned line_number;
3402 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3404 return cgi_error_disabled(csp, rsp);
3407 err = get_number_param(csp, parameters, "s", §ionid);
3414 return JB_ERR_CGI_PARAMS;
3417 err = get_url_spec_param(csp, parameters, "u", &new_pattern);
3423 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3426 /* No filename specified, can't read file, modified, or out of memory. */
3428 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3432 cur_line = file->lines;
3434 while ((cur_line != NULL) && (line_number < sectionid))
3436 cur_line = cur_line->next;
3440 if ( (cur_line == NULL)
3441 || (cur_line->type != FILE_LINE_ACTION))
3443 /* Invalid "sectionid" parameter */
3445 edit_free_file(file);
3446 return JB_ERR_CGI_PARAMS;
3449 /* At this point, the section header is in cur_line - add after this. */
3451 /* Allocate the new line */
3452 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3453 if (new_line == NULL)
3456 edit_free_file(file);
3457 return JB_ERR_MEMORY;
3460 /* Fill in the data members of the new line */
3461 new_line->raw = NULL;
3462 new_line->prefix = NULL;
3463 new_line->unprocessed = new_pattern;
3464 new_line->type = FILE_LINE_URL;
3466 /* Link new_line into the list, after cur_line */
3467 new_line->next = cur_line->next;
3468 cur_line->next = new_line;
3470 /* Done making changes, now commit */
3472 err = edit_write_file(file);
3475 /* Error writing file */
3476 edit_free_file(file);
3480 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3481 string_append(&target, file->identifier);
3483 edit_free_file(file);
3488 return JB_ERR_MEMORY;
3491 rsp->status = strdup("302 Local Redirect from Privoxy");
3492 if (rsp->status == NULL)
3495 return JB_ERR_MEMORY;
3497 err = enlist_unique_header(rsp->headers, "Location", target);
3504 /*********************************************************************
3506 * Function : cgi_edit_actions_remove_url
3508 * Description : CGI function that actually removes a URL pattern from
3512 * 1 : csp = Current client state (buffers, headers, etc...)
3513 * 2 : rsp = http_response data structure for output
3514 * 3 : parameters = map of cgi parameters
3517 * f : (filename) Identifies the file to edit
3518 * v : (version) File's last-modified time
3519 * p : (pattern) Line number of pattern to remove
3521 * Returns : JB_ERR_OK on success
3522 * JB_ERR_MEMORY on out-of-memory
3523 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3524 * specified or not valid.
3526 *********************************************************************/
3527 jb_err cgi_edit_actions_remove_url(struct client_state *csp,
3528 struct http_response *rsp,
3529 const struct map *parameters)
3532 struct editable_file * file;
3533 struct file_line * cur_line;
3534 struct file_line * prev_line;
3535 unsigned line_number;
3539 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3541 return cgi_error_disabled(csp, rsp);
3544 err = get_number_param(csp, parameters, "p", &patternid);
3550 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3553 /* No filename specified, can't read file, modified, or out of memory. */
3554 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3559 cur_line = file->lines;
3561 while ((cur_line != NULL) && (line_number < patternid))
3563 prev_line = cur_line;
3564 cur_line = cur_line->next;
3568 if ( (cur_line == NULL)
3569 || (prev_line == NULL)
3570 || (cur_line->type != FILE_LINE_URL))
3572 /* Invalid "patternid" parameter */
3573 edit_free_file(file);
3574 return JB_ERR_CGI_PARAMS;
3577 /* At this point, the line to remove is in cur_line, and the previous
3578 * one is in prev_line
3581 /* Unlink cur_line */
3582 prev_line->next = cur_line->next;
3583 cur_line->next = NULL;
3586 edit_free_file_lines(cur_line);
3588 err = edit_write_file(file);
3591 /* Error writing file */
3592 edit_free_file(file);
3596 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3597 string_append(&target, file->identifier);
3599 edit_free_file(file);
3604 return JB_ERR_MEMORY;
3607 rsp->status = strdup("302 Local Redirect from Privoxy");
3608 if (rsp->status == NULL)
3611 return JB_ERR_MEMORY;
3613 err = enlist_unique_header(rsp->headers, "Location", target);
3620 /*********************************************************************
3622 * Function : cgi_edit_actions_section_remove
3624 * Description : CGI function that actually removes a whole section from
3625 * the actions file. The section must be empty first
3626 * (else JB_ERR_CGI_PARAMS).
3629 * 1 : csp = Current client state (buffers, headers, etc...)
3630 * 2 : rsp = http_response data structure for output
3631 * 3 : parameters = map of cgi parameters
3634 * f : (filename) Identifies the file to edit
3635 * v : (version) File's last-modified time
3636 * s : (section) Line number of section to edit
3638 * Returns : JB_ERR_OK on success
3639 * JB_ERR_MEMORY on out-of-memory
3640 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3641 * specified or not valid.
3643 *********************************************************************/
3644 jb_err cgi_edit_actions_section_remove(struct client_state *csp,
3645 struct http_response *rsp,
3646 const struct map *parameters)
3649 struct editable_file * file;
3650 struct file_line * cur_line;
3651 struct file_line * prev_line;
3652 unsigned line_number;
3656 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3658 return cgi_error_disabled(csp, rsp);
3661 err = get_number_param(csp, parameters, "s", §ionid);
3667 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3670 /* No filename specified, can't read file, modified, or out of memory. */
3671 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3675 cur_line = file->lines;
3678 while ((cur_line != NULL) && (line_number < sectionid))
3680 prev_line = cur_line;
3681 cur_line = cur_line->next;
3685 if ( (cur_line == NULL)
3686 || (cur_line->type != FILE_LINE_ACTION) )
3688 /* Invalid "sectionid" parameter */
3689 edit_free_file(file);
3690 return JB_ERR_CGI_PARAMS;
3693 if ( (cur_line->next != NULL)
3694 && (cur_line->next->type == FILE_LINE_URL) )
3696 /* Section not empty. */
3697 edit_free_file(file);
3698 return JB_ERR_CGI_PARAMS;
3701 /* At this point, the line to remove is in cur_line, and the previous
3702 * one is in prev_line
3705 /* Unlink cur_line */
3706 if (prev_line == NULL)
3708 /* Removing the first line from the file */
3709 file->lines = cur_line->next;
3713 prev_line->next = cur_line->next;
3715 cur_line->next = NULL;
3718 edit_free_file_lines(cur_line);
3720 err = edit_write_file(file);
3723 /* Error writing file */
3724 edit_free_file(file);
3728 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3729 string_append(&target, file->identifier);
3731 edit_free_file(file);
3736 return JB_ERR_MEMORY;
3739 rsp->status = strdup("302 Local Redirect from Privoxy");
3740 if (rsp->status == NULL)
3743 return JB_ERR_MEMORY;
3745 err = enlist_unique_header(rsp->headers, "Location", target);
3752 /*********************************************************************
3754 * Function : cgi_edit_actions_section_add
3756 * Description : CGI function that adds a new empty section to
3760 * 1 : csp = Current client state (buffers, headers, etc...)
3761 * 2 : rsp = http_response data structure for output
3762 * 3 : parameters = map of cgi parameters
3765 * f : (filename) Identifies the file to edit
3766 * v : (version) File's last-modified time
3767 * s : (section) Line number of section to add after, 0 for
3770 * Returns : JB_ERR_OK on success
3771 * JB_ERR_MEMORY on out-of-memory
3772 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3773 * specified or not valid.
3775 *********************************************************************/
3776 jb_err cgi_edit_actions_section_add(struct client_state *csp,
3777 struct http_response *rsp,
3778 const struct map *parameters)
3781 struct file_line * new_line;
3783 struct editable_file * file;
3784 struct file_line * cur_line;
3785 unsigned line_number;
3789 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3791 return cgi_error_disabled(csp, rsp);
3794 err = get_number_param(csp, parameters, "s", §ionid);
3800 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3803 /* No filename specified, can't read file, modified, or out of memory. */
3804 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3808 cur_line = file->lines;
3812 /* Add to start of file */
3813 if (cur_line != NULL)
3815 /* There's something in the file, find the line before the first
3818 while ( (cur_line->next != NULL)
3819 && (cur_line->next->type != FILE_LINE_ACTION) )
3821 cur_line = cur_line->next;
3828 /* Add after stated section. */
3829 while ((cur_line != NULL) && (line_number < sectionid))
3831 cur_line = cur_line->next;
3835 if ( (cur_line == NULL)
3836 || (cur_line->type != FILE_LINE_ACTION))
3838 /* Invalid "sectionid" parameter */
3839 edit_free_file(file);
3840 return JB_ERR_CGI_PARAMS;
3843 /* Skip through the section to find the last line in it. */
3844 while ( (cur_line->next != NULL)
3845 && (cur_line->next->type != FILE_LINE_ACTION) )
3847 cur_line = cur_line->next;
3852 /* At this point, the last line in the previous section is in cur_line
3853 * - add after this. (Or if we need to add as the first line, cur_line
3857 new_text = strdup("{}");
3858 if (NULL == new_text)
3860 edit_free_file(file);
3861 return JB_ERR_MEMORY;
3864 /* Allocate the new line */
3865 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3866 if (new_line == NULL)
3869 edit_free_file(file);
3870 return JB_ERR_MEMORY;
3873 /* Fill in the data members of the new line */
3874 new_line->raw = NULL;
3875 new_line->prefix = NULL;
3876 new_line->unprocessed = new_text;
3877 new_line->type = FILE_LINE_ACTION;
3879 if (cur_line != NULL)
3881 /* Link new_line into the list, after cur_line */
3882 new_line->next = cur_line->next;
3883 cur_line->next = new_line;
3887 /* Link new_line into the list, as first line */
3888 new_line->next = file->lines;
3889 file->lines = new_line;
3892 /* Done making changes, now commit */
3894 err = edit_write_file(file);
3897 /* Error writing file */
3898 edit_free_file(file);
3902 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3903 string_append(&target, file->identifier);
3905 edit_free_file(file);
3910 return JB_ERR_MEMORY;
3913 rsp->status = strdup("302 Local Redirect from Privoxy");
3914 if (rsp->status == NULL)
3917 return JB_ERR_MEMORY;
3919 err = enlist_unique_header(rsp->headers, "Location", target);
3926 /*********************************************************************
3928 * Function : cgi_edit_actions_section_swap
3930 * Description : CGI function that swaps the order of two sections
3931 * in the actions file. Note that this CGI can actually
3932 * swap any two arbitrary sections, but the GUI interface
3933 * currently only allows consecutive sections to be
3937 * 1 : csp = Current client state (buffers, headers, etc...)
3938 * 2 : rsp = http_response data structure for output
3939 * 3 : parameters = map of cgi parameters
3942 * f : (filename) Identifies the file to edit
3943 * v : (version) File's last-modified time
3944 * s1 : (section1) Line number of first section to swap
3945 * s2 : (section2) Line number of second section to swap
3947 * Returns : JB_ERR_OK on success
3948 * JB_ERR_MEMORY on out-of-memory
3949 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3950 * specified or not valid.
3952 *********************************************************************/
3953 jb_err cgi_edit_actions_section_swap(struct client_state *csp,
3954 struct http_response *rsp,
3955 const struct map *parameters)
3959 struct editable_file * file;
3960 struct file_line * cur_line;
3961 struct file_line * prev_line;
3962 struct file_line * line_before_section1;
3963 struct file_line * line_start_section1;
3964 struct file_line * line_end_section1;
3965 struct file_line * line_after_section1;
3966 struct file_line * line_before_section2;
3967 struct file_line * line_start_section2;
3968 struct file_line * line_end_section2;
3969 struct file_line * line_after_section2;
3970 unsigned line_number;
3974 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3976 return cgi_error_disabled(csp, rsp);
3979 err = get_number_param(csp, parameters, "s1", §ion1);
3980 if (!err) err = get_number_param(csp, parameters, "s2", §ion2);
3986 if (section1 > section2)
3988 unsigned temp = section2;
3989 section2 = section1;
3993 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3996 /* No filename specified, can't read file, modified, or out of memory. */
3997 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
4000 /* Start at the beginning... */
4002 cur_line = file->lines;
4005 /* ... find section1 ... */
4006 while ((cur_line != NULL) && (line_number < section1))
4008 prev_line = cur_line;
4009 cur_line = cur_line->next;
4013 if ( (cur_line == NULL)
4014 || (cur_line->type != FILE_LINE_ACTION) )
4016 /* Invalid "section1" parameter */
4017 edit_free_file(file);
4018 return JB_ERR_CGI_PARAMS;
4021 /* If no-op, we've validated params and can skip the rest. */
4022 if (section1 != section2)
4024 /* ... find the end of section1 ... */
4025 line_before_section1 = prev_line;
4026 line_start_section1 = cur_line;
4029 prev_line = cur_line;
4030 cur_line = cur_line->next;
4033 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL));
4034 line_end_section1 = prev_line;
4035 line_after_section1 = cur_line;
4037 /* ... find section2 ... */
4038 while ((cur_line != NULL) && (line_number < section2))
4040 prev_line = cur_line;
4041 cur_line = cur_line->next;
4045 if ( (cur_line == NULL)
4046 || (cur_line->type != FILE_LINE_ACTION) )
4048 /* Invalid "section2" parameter */
4049 edit_free_file(file);
4050 return JB_ERR_CGI_PARAMS;
4053 /* ... find the end of section2 ... */
4054 line_before_section2 = prev_line;
4055 line_start_section2 = cur_line;
4058 prev_line = cur_line;
4059 cur_line = cur_line->next;
4062 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL));
4063 line_end_section2 = prev_line;
4064 line_after_section2 = cur_line;
4066 /* Now have all the pointers we need. Do the swap. */
4068 /* Change the pointer to section1 to point to section2 instead */
4069 if (line_before_section1 == NULL)
4071 file->lines = line_start_section2;
4075 line_before_section1->next = line_start_section2;
4078 if (line_before_section2 == line_end_section1)
4080 /* Consecutive sections */
4081 line_end_section2->next = line_start_section1;
4085 line_end_section2->next = line_after_section1;
4086 line_before_section2->next = line_start_section1;
4089 /* Set the pointer from the end of section1 to the rest of the file */
4090 line_end_section1->next = line_after_section2;
4092 err = edit_write_file(file);
4095 /* Error writing file */
4096 edit_free_file(file);
4099 } /* END if (section1 != section2) */
4101 target = strdup(CGI_PREFIX "edit-actions-list?f=");
4102 string_append(&target, file->identifier);
4104 edit_free_file(file);
4109 return JB_ERR_MEMORY;
4112 rsp->status = strdup("302 Local Redirect from Privoxy");
4113 if (rsp->status == NULL)
4116 return JB_ERR_MEMORY;
4118 err = enlist_unique_header(rsp->headers, "Location", target);
4125 /*********************************************************************
4127 * Function : cgi_toggle
4129 * Description : CGI function that adds a new empty section to
4133 * 1 : csp = Current client state (buffers, headers, etc...)
4134 * 2 : rsp = http_response data structure for output
4135 * 3 : parameters = map of cgi parameters
4138 * set : If present, how to change toggle setting:
4139 * "enable", "disable", "toggle", or none (default).
4140 * mini : If present, use mini reply template.
4142 * Returns : JB_ERR_OK on success
4143 * JB_ERR_MEMORY on out-of-memory
4145 *********************************************************************/
4146 jb_err cgi_toggle(struct client_state *csp,
4147 struct http_response *rsp,
4148 const struct map *parameters)
4150 struct map *exports;
4152 const char *template_name;
4159 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_TOGGLE))
4161 return cgi_error_disabled(csp, rsp);
4164 if (NULL == (exports = default_exports(csp, "toggle")))
4166 return JB_ERR_MEMORY;
4169 mode = get_char_param(parameters, "set");
4176 else if (mode == 'D')
4181 else if (mode == 'T')
4184 g_bToggleIJB = !g_bToggleIJB;
4187 err = map_conditional(exports, "enabled", g_bToggleIJB);
4194 template_name = (get_char_param(parameters, "mini")
4198 return template_fill_for_cgi(csp, template_name, exports, rsp);
4202 /*********************************************************************
4204 * Function : javascriptify
4206 * Description : Converts a string into a form JavaScript will like.
4208 * Netscape 4's JavaScript sucks - it doesn't use
4209 * "id" parameters, so you have to set the "name"
4210 * used to submit a form element to something JavaScript
4211 * will like. (Or access the elements by index in an
4212 * array. That array contains >60 elements and will
4213 * be changed whenever we add a new action to the
4214 * editor, so I'm NOT going to use indexes that have
4215 * to be figured out by hand.)
4217 * Currently the only thing we have to worry about
4218 * is "-" ==> "_" conversion.
4220 * This is a length-preserving operation so it is
4221 * carried out in-place, no memory is allocated
4225 * 1 : identifier = String to make JavaScript-friendly.
4229 *********************************************************************/
4230 static void javascriptify(char * identifier)
4232 char * p = identifier;
4233 while (NULL != (p = strchr(p, '-')))
4240 /*********************************************************************
4242 * Function : actions_to_radio
4244 * Description : Converts a actionsfile entry into settings for
4245 * radio buttons and edit boxes on a HTML form.
4248 * 1 : exports = List of substitutions to add to.
4249 * 2 : action = Action to read
4251 * Returns : JB_ERR_OK on success
4252 * JB_ERR_MEMORY on out-of-memory
4254 *********************************************************************/
4255 static jb_err actions_to_radio(struct map * exports,
4256 const struct action_spec *action)
4258 unsigned mask = action->mask;
4259 unsigned add = action->add;
4267 mask = action->mask;
4270 /* sanity - prevents "-feature +feature" */
4274 #define DEFINE_ACTION_BOOL(name, bit) \
4275 if (!(mask & bit)) \
4277 current_mode = 'n'; \
4279 else if (add & bit) \
4281 current_mode = 'y'; \
4285 current_mode = 'x'; \
4287 if (map_radio(exports, name, "ynx", current_mode)) \
4289 return JB_ERR_MEMORY; \
4292 #define DEFINE_ACTION_STRING(name, bit, index) \
4293 DEFINE_ACTION_BOOL(name, bit); \
4296 #define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default) \
4299 checked = !strcmp(action->string[index], value); \
4303 checked = is_default; \
4305 mapped_param |= checked; \
4306 if (map(exports, name "-param-" value, 1, (checked ? "checked" : ""), 1)) \
4308 return JB_ERR_MEMORY; \
4311 #define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val) \
4312 if (map(exports, name "-param-custom", 1, \
4313 ((!mapped_param) ? "checked" : ""), 1)) \
4315 return JB_ERR_MEMORY; \
4317 if (map(exports, name "-param", 1, \
4318 (((add & bit) && !mapped_param) ? \
4319 action->string[index] : default_val), 1)) \
4321 return JB_ERR_MEMORY; \
4324 #define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val) \
4325 if (map(exports, name "-param", 1, \
4326 ((add & bit) ? action->string[index] : default_val), 1)) \
4328 return JB_ERR_MEMORY; \
4331 #define DEFINE_ACTION_MULTI(name, index) \
4332 if (action->multi_add[index]->first) \
4334 current_mode = 'y'; \
4336 else if (action->multi_remove_all[index]) \
4338 current_mode = 'n'; \
4340 else if (action->multi_remove[index]->first) \
4342 current_mode = 'y'; \
4346 current_mode = 'x'; \
4348 if (map_radio(exports, name, "ynx", current_mode)) \
4350 return JB_ERR_MEMORY; \
4353 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
4355 #include "actionlist.h"
4357 #undef DEFINE_ACTION_MULTI
4358 #undef DEFINE_ACTION_STRING
4359 #undef DEFINE_ACTION_BOOL
4360 #undef DEFINE_ACTION_ALIAS
4361 #undef DEFINE_CGI_PARAM_CUSTOM
4362 #undef DEFINE_CGI_PARAM_RADIO
4363 #undef DEFINE_CGI_PARAM_NO_RADIO
4369 /*********************************************************************
4371 * Function : actions_from_radio
4373 * Description : Converts a map of parameters passed to a CGI function
4374 * into an actionsfile entry.
4377 * 1 : parameters = parameters to the CGI call
4378 * 2 : action = Action to change. Must be valid before
4379 * the call, actions not specified will be
4382 * Returns : JB_ERR_OK on success
4383 * JB_ERR_MEMORY on out-of-memory
4385 *********************************************************************/
4386 static jb_err actions_from_radio(const struct map * parameters,
4387 struct action_spec *action)
4389 static int first_time = 1;
4393 const char * js_name;
4394 jb_err err = JB_ERR_OK;
4399 /* Statics are generally a potential race condition,
4400 * but in this case we're safe and don't need semaphores.
4401 * Be careful if you modify this function.
4405 #define JAVASCRIPTIFY(dest_var, string) \
4407 static char js_name_arr[] = string; \
4410 javascriptify(js_name_arr); \
4412 dest_var = js_name_arr; \
4415 #define DEFINE_ACTION_BOOL(name, bit) \
4416 JAVASCRIPTIFY(js_name, name); \
4417 ch = get_char_param(parameters, js_name); \
4420 action->add |= bit; \
4421 action->mask |= bit; \
4423 else if (ch == 'N') \
4425 action->add &= ~bit; \
4426 action->mask &= ~bit; \
4428 else if (ch == 'X') \
4430 action->add &= ~bit; \
4431 action->mask |= bit; \
4434 #define DEFINE_ACTION_STRING(name, bit, index) \
4435 JAVASCRIPTIFY(js_name, name); \
4436 ch = get_char_param(parameters, js_name); \
4440 JAVASCRIPTIFY(js_name, name "-mode"); \
4441 if (!err) err = get_string_param(parameters, js_name, ¶m); \
4442 if ((param == NULL) || (0 == strcmp(param, "CUSTOM"))) \
4444 JAVASCRIPTIFY(js_name, name "-param"); \
4445 if (!err) err = get_string_param(parameters, js_name, ¶m); \
4447 if (param != NULL) \
4449 if (NULL == (param_dup = strdup(param))) \
4451 return JB_ERR_MEMORY; \
4453 freez(action->string[index]); \
4454 action->add |= bit; \
4455 action->mask |= bit; \
4456 action->string[index] = param_dup; \
4459 else if (ch == 'N') \
4461 if (action->add & bit) \
4463 freez(action->string[index]); \
4465 action->add &= ~bit; \
4466 action->mask &= ~bit; \
4468 else if (ch == 'X') \
4470 if (action->add & bit) \
4472 freez(action->string[index]); \
4474 action->add &= ~bit; \
4475 action->mask |= bit; \
4478 #define DEFINE_ACTION_MULTI(name, index) \
4479 JAVASCRIPTIFY(js_name, name); \
4480 ch = get_char_param(parameters, js_name); \
4485 else if (ch == 'N') \
4487 list_remove_all(action->multi_add[index]); \
4488 list_remove_all(action->multi_remove[index]); \
4489 action->multi_remove_all[index] = 1; \
4491 else if (ch == 'X') \
4493 list_remove_all(action->multi_add[index]); \
4494 list_remove_all(action->multi_remove[index]); \
4495 action->multi_remove_all[index] = 0; \
4498 #define DEFINE_ACTION_ALIAS 0 /* No aliases for URL parsing */
4500 #include "actionlist.h"
4502 #undef DEFINE_ACTION_MULTI
4503 #undef DEFINE_ACTION_STRING
4504 #undef DEFINE_ACTION_BOOL
4505 #undef DEFINE_ACTION_ALIAS
4506 #undef JAVASCRIPTIFY
4514 #endif /* def FEATURE_CGI_EDIT_ACTIONS */