1 const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.26 2002/03/26 22:59:17 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.26 2002/03/26 22:59:17 jongfoster
46 * Fixing /toggle to display status consistently.
48 * Revision 1.25 2002/03/26 22:29:54 swa
49 * we have a new homepage!
51 * Revision 1.24 2002/03/24 15:23:33 jongfoster
54 * Revision 1.23 2002/03/24 13:32:41 swa
55 * name change related issues
57 * Revision 1.22 2002/03/24 13:25:43 swa
58 * name change related issues
60 * Revision 1.21 2002/03/22 18:02:48 jongfoster
61 * Fixing remote toggle
63 * Revision 1.20 2002/03/16 20:28:34 oes
64 * Added descriptions to the filters so users will know what they select in the cgi editor
66 * Revision 1.19 2002/03/16 18:38:14 jongfoster
67 * Stopping stupid or malicious users from breaking the actions
68 * file using the web-based editor.
70 * Revision 1.18 2002/03/16 14:57:44 jongfoster
71 * Full support for enabling/disabling modular filters.
73 * Revision 1.17 2002/03/16 14:26:42 jongfoster
74 * First version of modular filters support - READ ONLY!
75 * Fixing a double-free bug in the out-of-memory handling in map_radio().
77 * Revision 1.16 2002/03/07 03:46:17 oes
78 * Fixed compiler warnings
80 * Revision 1.15 2002/03/06 22:54:35 jongfoster
81 * Automated function-comment nitpicking.
83 * Revision 1.14 2002/03/05 00:24:51 jongfoster
84 * Patch to always edit the current actions file.
86 * Revision 1.13 2002/03/04 02:07:59 david__schmidt
87 * Enable web editing of actions file on OS/2 (it had been broken all this time!)
89 * Revision 1.12 2002/03/03 09:18:03 joergs
90 * Made jumbjuster work on AmigaOS again.
92 * Revision 1.11 2002/01/23 01:03:31 jongfoster
93 * Fixing gcc [CygWin] compiler warnings
95 * Revision 1.10 2002/01/23 00:22:59 jongfoster
96 * Adding new function cgi_edit_actions_section_swap(), to reorder
99 * Adding get_url_spec_param() to get a validated URL pattern.
101 * Moving edit_read_line() out of this file and into loaders.c.
103 * Adding missing html_encode() to many CGI functions.
105 * Moving the functions that #include actionlist.h to the end of the file,
106 * because the Visual C++ 97 debugger gets extremely confused if you try
107 * to debug any code that comes after them in the file.
109 * Major optimizations in cgi_edit_actions_list() to reduce the size of
110 * the generated HTML (down 40% from 550k to 304k), with major side-effects
111 * throughout the editor and templates. In particular, the length of the
112 * URLs throughout the editor has been drastically reduced, by cutting
113 * paramater names down to 1 character and CGI names down to 3-4
114 * characters, by removing all non-essential CGI paramaters even at the
115 * expense of having to re-read the actions file for the most trivial
116 * page, and by using relative rather than absolute URLs. This means
117 * that this (typical example):
119 * <a href="http://ijbswa.sourceforge.net/config/edit-actions-url-form?
120 * filename=ijb&ver=1011487572&section=12&pattern=13
121 * &oldval=www.oesterhelt.org%2Fdeanimate-demo">
125 * <a href="eau?f=ijb&v=1011487572&p=13">
127 * Revision 1.9 2002/01/17 20:56:22 jongfoster
128 * Replacing hard references to the URL of the config interface
129 * with #defines from project.h
131 * Revision 1.8 2001/11/30 23:35:51 jongfoster
132 * Renaming actionsfile to ijb.action
134 * Revision 1.7 2001/11/13 00:28:24 jongfoster
135 * - Renaming parameters from edit-actions-for-url so that they only
136 * contain legal JavaScript characters. If we wanted to write
137 * JavaScript that worked with Netscape 4, this is nessacery.
138 * (Note that at the moment the JavaScript doesn't actually work
139 * with Netscape 4, but now this is purely a template issue, not
140 * one affecting code).
141 * - Adding new CGIs for use by non-JavaScript browsers:
142 * edit-actions-url-form
143 * edit-actions-add-url-form
144 * edit-actions-remove-url-form
147 * Revision 1.6 2001/10/29 03:48:09 david__schmidt
148 * OS/2 native needed a snprintf() routine. Added one to miscutil, brackedted
149 * by and __OS2__ ifdef.
151 * Revision 1.5 2001/10/25 03:40:48 david__schmidt
152 * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
153 * threads to call select() simultaneously. So, it's time to do a real, live,
154 * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__
155 * (native). Both versions will work, but using __OS2__ offers multi-threading.
157 * Revision 1.4 2001/10/23 21:48:19 jongfoster
158 * Cleaning up error handling in CGI functions - they now send back
159 * a HTML error page and should never cause a FATAL error. (Fixes one
160 * potential source of "denial of service" attacks).
162 * CGI actions file editor that works and is actually useful.
164 * Ability to toggle JunkBuster remotely using a CGI call.
166 * You can turn off both the above features in the main configuration
167 * file, e.g. if you are running a multi-user proxy.
169 * Revision 1.3 2001/10/14 22:12:49 jongfoster
170 * New version of CGI-based actionsfile editor.
171 * Major changes, including:
172 * - Completely new file parser and file output routines
173 * - edit-actions CGI renamed edit-actions-for-url
174 * - All CGIs now need a filename parameter, except for...
175 * - New CGI edit-actions which doesn't need a filename,
176 * to allow you to start the editor up.
177 * - edit-actions-submit now works, and now automatically
178 * redirects you back to the main edit-actions-list handler.
180 * Revision 1.2 2001/09/16 17:05:14 jongfoster
181 * Removing unused #include showarg.h
183 * Revision 1.1 2001/09/16 15:47:37 jongfoster
184 * First version of CGI-based edit interface. This is very much a
185 * work-in-progress, and you can't actually use it to edit anything
186 * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes
187 * to have any effect.
190 **********************************************************************/
196 * FIXME: Following includes copied from cgi.c - which are actually needed?
201 #include <sys/types.h>
206 #include <sys/stat.h>
209 #define snprintf _snprintf
210 #endif /* def _WIN32 */
215 #include "cgisimple.h"
219 #include "miscutil.h"
223 /* loadcfg.h is for g_bToggleIJB only */
224 #include "urlmatch.h"
226 const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION;
229 #ifdef FEATURE_CGI_EDIT_ACTIONS
233 struct file_line * next;
241 struct action_spec action[1];
250 /* Add more data types here... e.g.
253 struct url_spec url[1];
257 struct action_spec action[1];
266 #define FILE_LINE_UNPROCESSED 1
267 #define FILE_LINE_BLANK 2
268 #define FILE_LINE_ALIAS_HEADER 3
269 #define FILE_LINE_ALIAS_ENTRY 4
270 #define FILE_LINE_ACTION 5
271 #define FILE_LINE_URL 6
272 #define FILE_LINE_SETTINGS_HEADER 7
273 #define FILE_LINE_SETTINGS_ENTRY 8
274 #define FILE_LINE_DESCRIPTION_HEADER 9
275 #define FILE_LINE_DESCRIPTION_ENTRY 10
280 struct file_line * lines;
281 const char * filename; /* Full pathname - e.g. "/etc/privoxy/wibble.action" */
282 const char * identifier; /* Filename stub - e.g. "wibble". Use for CGI param. */
283 /* Pre-encoded with url_encode() for ease of use. */
284 const char * version_str; /* Last modification time, as a string. For CGI param */
285 /* Can be used in URL without using url_param(). */
286 unsigned version; /* Last modification time - prevents chaos with
287 * the browser's "back" button. Note that this is a
288 * time_t cast to an unsigned. When comparing, always
289 * cast the time_t to an unsigned, and *NOT* vice-versa.
290 * This may lose the top few bits, but they're not
291 * significant anyway.
293 int newline; /* Newline convention - one of the NEWLINE_xxx constants.
294 * Note that changing this after the file has been
295 * read in will cause a mess.
297 struct file_line * parse_error; /* On parse error, this is the offending line. */
298 const char * parse_error_text; /* On parse error, this is the problem.
299 * (Statically allocated) */
302 #define CGI_ACTION_PARAM_LEN_MAX 500
304 /* FIXME: Following non-static functions should be prototyped in .h or made static */
306 /* Functions to read and write arbitrary config files */
307 jb_err edit_read_file(struct client_state *csp,
308 const struct map *parameters,
311 struct editable_file **pfile);
312 jb_err edit_write_file(struct editable_file * file);
313 void edit_free_file(struct editable_file * file);
315 /* Functions to read and write actions files */
316 jb_err edit_parse_actions_file(struct editable_file * file);
317 jb_err edit_read_actions_file(struct client_state *csp,
318 struct http_response *rsp,
319 const struct map *parameters,
321 struct editable_file **pfile);
324 jb_err cgi_error_modified(struct client_state *csp,
325 struct http_response *rsp,
326 const char *filename);
327 jb_err cgi_error_parse(struct client_state *csp,
328 struct http_response *rsp,
329 struct editable_file *file);
330 jb_err cgi_error_file(struct client_state *csp,
331 struct http_response *rsp,
332 const char *filename);
333 jb_err cgi_error_disabled(struct client_state *csp,
334 struct http_response *rsp);
336 /* Internal arbitrary config file support functions */
337 static jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline);
338 static void edit_free_file_lines(struct file_line * first_line);
340 /* Internal actions file support functions */
341 static int match_actions_file_header_line(const char * line, const char * name);
342 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue);
344 /* Internal parameter parsing functions */
345 static jb_err get_file_name_param(struct client_state *csp,
346 const struct map *parameters,
347 const char *param_name,
350 const char **pparam);
351 static jb_err get_number_param(struct client_state *csp,
352 const struct map *parameters,
355 static jb_err get_url_spec_param(struct client_state *csp,
356 const struct map *parameters,
359 static jb_err get_string_param(const struct map *parameters,
360 const char *param_name,
361 const char **pparam);
363 /* Internal actionsfile <==> HTML conversion functions */
364 static jb_err map_radio(struct map * exports,
365 const char * optionname,
368 static jb_err actions_to_radio(struct map * exports,
369 const struct action_spec *action);
370 static jb_err actions_from_radio(const struct map * parameters,
371 struct action_spec *action);
374 static jb_err map_copy_parameter_html(struct map *out,
375 const struct map *in,
377 #if 0 /* unused function */
378 static jb_err map_copy_parameter_url(struct map *out,
379 const struct map *in,
381 #endif /* unused function */
383 /*********************************************************************
385 * Function : map_copy_parameter_html
387 * Description : Copy a CGI parameter from one map to another, HTML
391 * 1 : out = target map
392 * 2 : in = source map
393 * 3 : name = name of cgi parameter to copy
395 * Returns : JB_ERR_OK on success
396 * JB_ERR_MEMORY on out-of-memory
397 * JB_ERR_CGI_PARAMS if the parameter doesn't exist
400 *********************************************************************/
401 static jb_err map_copy_parameter_html(struct map *out,
402 const struct map *in,
412 value = lookup(in, name);
413 err = map(out, name, 1, html_encode(value), 0);
420 else if (*value == '\0')
422 return JB_ERR_CGI_PARAMS;
431 #if 0 /* unused function */
432 /*********************************************************************
434 * Function : map_copy_parameter_html
436 * Description : Copy a CGI parameter from one map to another, URL
440 * 1 : out = target map
441 * 2 : in = source map
442 * 3 : name = name of cgi parameter to copy
444 * Returns : JB_ERR_OK on success
445 * JB_ERR_MEMORY on out-of-memory
446 * JB_ERR_CGI_PARAMS if the parameter doesn't exist
449 *********************************************************************/
450 static jb_err map_copy_parameter_url(struct map *out,
451 const struct map *in,
461 value = lookup(in, name);
462 err = map(out, name, 1, url_encode(value), 0);
469 else if (*value == '\0')
471 return JB_ERR_CGI_PARAMS;
478 #endif /* 0 - unused function */
480 /*********************************************************************
482 * Function : cgi_edit_actions_url_form
484 * Description : CGI function that displays a form for
488 * 1 : csp = Current client state (buffers, headers, etc...)
489 * 2 : rsp = http_response data structure for output
490 * 3 : parameters = map of cgi parameters
493 * f : (filename) Identifies the file to edit
494 * v : (version) File's last-modified time
495 * p : (pattern) Line number of pattern to edit
497 * Returns : JB_ERR_OK on success
498 * JB_ERR_MEMORY on out-of-memory
499 * JB_ERR_CGI_PARAMS if the CGI parameters are not
500 * specified or not valid.
502 *********************************************************************/
503 jb_err cgi_edit_actions_url_form(struct client_state *csp,
504 struct http_response *rsp,
505 const struct map *parameters)
507 struct map * exports;
509 struct editable_file * file;
510 struct file_line * cur_line;
511 unsigned line_number;
518 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
520 return cgi_error_disabled(csp, rsp);
523 err = get_number_param(csp, parameters, "p", &patternid);
529 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
532 /* No filename specified, can't read file, modified, or out of memory. */
533 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
536 cur_line = file->lines;
538 for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++)
540 cur_line = cur_line->next;
543 if ( (cur_line == NULL)
544 || (line_number != patternid)
546 || (cur_line->type != FILE_LINE_URL))
548 /* Invalid "patternid" parameter */
549 edit_free_file(file);
550 return JB_ERR_CGI_PARAMS;
553 if (NULL == (exports = default_exports(csp, NULL)))
555 edit_free_file(file);
556 return JB_ERR_MEMORY;
559 err = map(exports, "f", 1, file->identifier, 1);
560 if (!err) err = map(exports, "v", 1, file->version_str, 1);
561 if (!err) err = map(exports, "p", 1, url_encode(lookup(parameters, "p")), 0);
562 if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0);
564 edit_free_file(file);
572 return template_fill_for_cgi(csp, "edit-actions-url-form", exports, rsp);
576 /*********************************************************************
578 * Function : cgi_edit_actions_add_url_form
580 * Description : CGI function that displays a form for
584 * 1 : csp = Current client state (buffers, headers, etc...)
585 * 2 : rsp = http_response data structure for output
586 * 3 : parameters = map of cgi parameters
589 * f : (filename) Identifies the file to edit
590 * v : (version) File's last-modified time
591 * s : (section) Line number of section to edit
593 * Returns : JB_ERR_OK on success
594 * JB_ERR_MEMORY on out-of-memory
595 * JB_ERR_CGI_PARAMS if the CGI parameters are not
596 * specified or not valid.
598 *********************************************************************/
599 jb_err cgi_edit_actions_add_url_form(struct client_state *csp,
600 struct http_response *rsp,
601 const struct map *parameters)
610 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
612 return cgi_error_disabled(csp, rsp);
615 if (NULL == (exports = default_exports(csp, NULL)))
617 return JB_ERR_MEMORY;
620 err = map_copy_parameter_html(exports, parameters, "f");
621 if (!err) err = map_copy_parameter_html(exports, parameters, "v");
622 if (!err) err = map_copy_parameter_html(exports, parameters, "s");
630 return template_fill_for_cgi(csp, "edit-actions-add-url-form", exports, rsp);
634 /*********************************************************************
636 * Function : cgi_edit_actions_remove_url_form
638 * Description : CGI function that displays a form for
642 * 1 : csp = Current client state (buffers, headers, etc...)
643 * 2 : rsp = http_response data structure for output
644 * 3 : parameters = map of cgi parameters
647 * f : (filename) Identifies the file to edit
648 * v : (version) File's last-modified time
649 * p : (pattern) Line number of pattern to edit
651 * Returns : JB_ERR_OK on success
652 * JB_ERR_MEMORY on out-of-memory
653 * JB_ERR_CGI_PARAMS if the CGI parameters are not
654 * specified or not valid.
656 *********************************************************************/
657 jb_err cgi_edit_actions_remove_url_form(struct client_state *csp,
658 struct http_response *rsp,
659 const struct map *parameters)
661 struct map * exports;
663 struct editable_file * file;
664 struct file_line * cur_line;
665 unsigned line_number;
672 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
674 return cgi_error_disabled(csp, rsp);
677 err = get_number_param(csp, parameters, "p", &patternid);
683 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
686 /* No filename specified, can't read file, modified, or out of memory. */
687 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
690 cur_line = file->lines;
692 for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++)
694 cur_line = cur_line->next;
697 if ( (cur_line == NULL)
698 || (line_number != patternid)
700 || (cur_line->type != FILE_LINE_URL))
702 /* Invalid "patternid" parameter */
703 edit_free_file(file);
704 return JB_ERR_CGI_PARAMS;
707 if (NULL == (exports = default_exports(csp, NULL)))
709 edit_free_file(file);
710 return JB_ERR_MEMORY;
713 err = map(exports, "f", 1, file->identifier, 1);
714 if (!err) err = map(exports, "v", 1, file->version_str, 1);
715 if (!err) err = map(exports, "s", 1, url_encode(lookup(parameters, "s")), 0);
716 if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0);
718 edit_free_file(file);
726 return template_fill_for_cgi(csp, "edit-actions-remove-url-form", exports, rsp);
730 /*********************************************************************
732 * Function : edit_write_file
734 * Description : Write a complete file to disk.
737 * 1 : filename = File to write to.
738 * 2 : file = Data structure to write.
740 * Returns : JB_ERR_OK on success
741 * JB_ERR_FILE on error writing to file.
742 * JB_ERR_MEMORY on out of memory
744 *********************************************************************/
745 jb_err edit_write_file(struct editable_file * file)
748 struct file_line * cur_line;
749 struct stat statbuf[1];
750 char version_buf[22]; /* 22 = ceil(log10(2^64)) + 2 = max number of
751 digits in time_t, assuming this is a 64-bit
752 machine, plus null terminator, plus one
756 assert(file->filename);
758 #if defined(AMIGA) || defined(__OS2__)
759 if (NULL == (fp = fopen(file->filename, "w")))
761 if (NULL == (fp = fopen(file->filename, "wt")))
762 #endif /* def AMIGA */
767 cur_line = file->lines;
768 while (cur_line != NULL)
772 if (fputs(cur_line->raw, fp) < 0)
780 if (cur_line->prefix)
782 if (fputs(cur_line->prefix, fp) < 0)
788 if (cur_line->unprocessed)
790 /* This should be a single line - sanity check. */
791 assert(NULL == strchr(cur_line->unprocessed, '\r'));
792 assert(NULL == strchr(cur_line->unprocessed, '\n'));
794 if (NULL != strchr(cur_line->unprocessed, '#'))
796 /* Must quote '#' characters */
803 /* Count number of # characters, so we know length of output string */
804 src = cur_line->unprocessed;
805 while (NULL != (src = strchr(src, '#')))
812 /* Allocate new memory for string */
813 len = strlen(cur_line->unprocessed);
814 if (NULL == (str = malloc((size_t) len + 1 + numhash)))
816 /* Uh oh, just trashed file! */
818 return JB_ERR_MEMORY;
821 /* Loop through string from end */
822 src = cur_line->unprocessed + len;
823 dest = str + len + numhash;
824 for ( ; len >= 0; len--)
826 if ((*dest-- = *src--) == '#')
830 assert(numhash >= 0);
833 assert(numhash == 0);
834 assert(src + 1 == cur_line->unprocessed);
835 assert(dest + 1 == str);
837 if (fputs(str, fp) < 0)
848 /* Can write without quoting '#' characters. */
849 if (fputs(cur_line->unprocessed, fp) < 0)
855 if (fputs(NEWLINE(file->newline), fp) < 0)
863 /* FIXME: Write data from file->data->whatever */
867 cur_line = cur_line->next;
873 /* Update the version stamp in the file structure, since we just
874 * wrote to the file & changed it's date.
876 if (stat(file->filename, statbuf) < 0)
878 /* Error, probably file not found. */
881 file->version = (unsigned)statbuf->st_mtime;
883 /* Correct file->version_str */
884 freez(file->version_str);
885 snprintf(version_buf, 22, "%u", file->version);
886 version_buf[21] = '\0';
887 file->version_str = strdup(version_buf);
888 if (version_buf == NULL)
890 return JB_ERR_MEMORY;
897 /*********************************************************************
899 * Function : edit_free_file
901 * Description : Free a complete file in memory.
904 * 1 : file = Data structure to free.
908 *********************************************************************/
909 void edit_free_file(struct editable_file * file)
913 /* Silently ignore NULL pointer */
917 edit_free_file_lines(file->lines);
918 freez(file->filename);
919 freez(file->identifier);
920 freez(file->version_str);
922 file->parse_error_text = NULL; /* Statically allocated */
923 file->parse_error = NULL;
929 /*********************************************************************
931 * Function : edit_free_file
933 * Description : Free an entire linked list of file lines.
936 * 1 : first_line = Data structure to free.
940 *********************************************************************/
941 static void edit_free_file_lines(struct file_line * first_line)
943 struct file_line * next_line;
945 while (first_line != NULL)
947 next_line = first_line->next;
948 first_line->next = NULL;
949 freez(first_line->raw);
950 freez(first_line->prefix);
951 freez(first_line->unprocessed);
952 switch(first_line->type)
954 case 0: /* special case if memory zeroed */
955 case FILE_LINE_UNPROCESSED:
956 case FILE_LINE_BLANK:
957 case FILE_LINE_ALIAS_HEADER:
958 case FILE_LINE_SETTINGS_HEADER:
959 case FILE_LINE_DESCRIPTION_HEADER:
960 case FILE_LINE_DESCRIPTION_ENTRY:
961 case FILE_LINE_ALIAS_ENTRY:
963 /* No data is stored for these */
966 case FILE_LINE_ACTION:
967 free_action(first_line->data.action);
970 case FILE_LINE_SETTINGS_ENTRY:
971 freez(first_line->data.setting.name);
972 freez(first_line->data.setting.svalue);
975 /* Should never happen */
979 first_line->type = 0; /* paranoia */
981 first_line = next_line;
986 /*********************************************************************
988 * Function : match_actions_file_header_line
990 * Description : Match an actions file {{header}} line
993 * 1 : line = String from file
994 * 2 : name = Header to match against
996 * Returns : 0 iff they match.
998 *********************************************************************/
999 static int match_actions_file_header_line(const char * line, const char * name)
1007 if ((line[0] != '{') || (line[1] != '{'))
1013 /* Look for optional whitespace */
1014 while ( (*line == ' ') || (*line == '\t') )
1019 /* Look for the specified name (case-insensitive) */
1021 if (0 != strncmpic(line, name, len))
1027 /* Look for optional whitespace */
1028 while ( (*line == ' ') || (*line == '\t') )
1033 /* Look for "}}" and end of string*/
1034 if ((line[0] != '}') || (line[1] != '}') || (line[2] != '\0'))
1044 /*********************************************************************
1046 * Function : match_actions_file_header_line
1048 * Description : Match an actions file {{header}} line
1051 * 1 : line = String from file. Must not start with
1052 * whitespace (else infinite loop!)
1053 * 2 : name = Destination for name
1054 * 2 : name = Destination for value
1056 * Returns : JB_ERR_OK on success
1057 * JB_ERR_MEMORY on out-of-memory
1058 * JB_ERR_PARSE if there's no "=" sign, or if there's
1059 * nothing before the "=" sign (but empty
1060 * values *after* the "=" sign are legal).
1062 *********************************************************************/
1063 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue)
1065 const char * name_end;
1066 const char * value_start;
1072 assert(*line != ' ');
1073 assert(*line != '\t');
1078 value_start = strchr(line, '=');
1079 if ((value_start == NULL) || (value_start == line))
1081 return JB_ERR_PARSE;
1084 name_end = value_start - 1;
1086 /* Eat any whitespace before the '=' */
1087 while ((*name_end == ' ') || (*name_end == '\t'))
1090 * we already know we must have at least 1 non-ws char
1091 * at start of buf - no need to check
1096 name_len = name_end - line + 1; /* Length excluding \0 */
1097 if (NULL == (*pname = (char *) malloc(name_len + 1)))
1099 return JB_ERR_MEMORY;
1101 strncpy(*pname, line, name_len);
1102 (*pname)[name_len] = '\0';
1104 /* Eat any the whitespace after the '=' */
1106 while ((*value_start == ' ') || (*value_start == '\t'))
1111 if (NULL == (*pvalue = strdup(value_start)))
1115 return JB_ERR_MEMORY;
1122 /*********************************************************************
1124 * Function : edit_parse_actions_file
1126 * Description : Parse an actions file in memory.
1128 * Passed linked list must have the "data" member
1129 * zeroed, and must contain valid "next" and
1130 * "unprocessed" fields. The "raw" and "prefix"
1131 * fields are ignored, and "type" is just overwritten.
1133 * Note that on error the file may have been
1137 * 1 : file = Actions file to be parsed in-place.
1139 * Returns : JB_ERR_OK on success
1140 * JB_ERR_MEMORY on out-of-memory
1141 * JB_ERR_PARSE on error
1143 *********************************************************************/
1144 jb_err edit_parse_actions_file(struct editable_file * file)
1146 struct file_line * cur_line;
1148 const char * text; /* Text from a line */
1149 char * name; /* For lines of the form name=value */
1150 char * value; /* For lines of the form name=value */
1151 struct action_alias * alias_list = NULL;
1152 jb_err err = JB_ERR_OK;
1154 /* alias_list contains the aliases defined in this file.
1155 * It might be better to use the "file_line.data" fields
1156 * in the relavent places instead.
1159 cur_line = file->lines;
1161 /* A note about blank line support: Blank lines should only
1162 * ever occur as the last line in the file. This function
1163 * is more forgiving than that - FILE_LINE_BLANK can occur
1167 /* Skip leading blanks. Should only happen if file is
1168 * empty (which is valid, but pointless).
1170 while ( (cur_line != NULL)
1171 && (cur_line->unprocessed[0] == '\0') )
1174 cur_line->type = FILE_LINE_BLANK;
1175 cur_line = cur_line->next;
1178 if ( (cur_line != NULL)
1179 && (cur_line->unprocessed[0] != '{') )
1181 /* File doesn't start with a header */
1182 file->parse_error = cur_line;
1183 file->parse_error_text = "First (non-comment) line of the file must contain a header.";
1184 return JB_ERR_PARSE;
1187 if ( (cur_line != NULL) && (0 ==
1188 match_actions_file_header_line(cur_line->unprocessed, "settings") ) )
1190 cur_line->type = FILE_LINE_SETTINGS_HEADER;
1192 cur_line = cur_line->next;
1193 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1195 if (cur_line->unprocessed[0])
1197 cur_line->type = FILE_LINE_SETTINGS_ENTRY;
1199 err = split_line_on_equals(cur_line->unprocessed,
1200 &cur_line->data.setting.name,
1201 &cur_line->data.setting.svalue);
1202 if (err == JB_ERR_MEMORY)
1206 else if (err != JB_ERR_OK)
1208 /* Line does not contain a name=value pair */
1209 file->parse_error = cur_line;
1210 file->parse_error_text = "Expected a name=value pair on this {{description}} line, but couldn't find one.";
1211 return JB_ERR_PARSE;
1216 cur_line->type = FILE_LINE_BLANK;
1218 cur_line = cur_line->next;
1222 if ( (cur_line != NULL) && (0 ==
1223 match_actions_file_header_line(cur_line->unprocessed, "description") ) )
1225 cur_line->type = FILE_LINE_DESCRIPTION_HEADER;
1227 cur_line = cur_line->next;
1228 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1230 if (cur_line->unprocessed[0])
1232 cur_line->type = FILE_LINE_DESCRIPTION_ENTRY;
1236 cur_line->type = FILE_LINE_BLANK;
1238 cur_line = cur_line->next;
1242 if ( (cur_line != NULL) && (0 ==
1243 match_actions_file_header_line(cur_line->unprocessed, "alias") ) )
1245 cur_line->type = FILE_LINE_ALIAS_HEADER;
1247 cur_line = cur_line->next;
1248 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1250 if (cur_line->unprocessed[0])
1252 /* define an alias */
1253 struct action_alias * new_alias;
1255 cur_line->type = FILE_LINE_ALIAS_ENTRY;
1257 err = split_line_on_equals(cur_line->unprocessed, &name, &value);
1258 if (err == JB_ERR_MEMORY)
1262 else if (err != JB_ERR_OK)
1264 /* Line does not contain a name=value pair */
1265 file->parse_error = cur_line;
1266 file->parse_error_text = "Expected a name=value pair on this {{alias}} line, but couldn't find one.";
1267 return JB_ERR_PARSE;
1270 if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1275 free_alias_list(alias_list);
1276 return JB_ERR_MEMORY;
1279 err = get_actions(value, alias_list, new_alias->action);
1282 /* Invalid action or out of memory */
1286 free_alias_list(alias_list);
1287 if (err == JB_ERR_MEMORY)
1293 /* Line does not contain a name=value pair */
1294 file->parse_error = cur_line;
1295 file->parse_error_text = "This alias does not specify a valid set of actions.";
1296 return JB_ERR_PARSE;
1302 new_alias->name = name;
1305 new_alias->next = alias_list;
1306 alias_list = new_alias;
1310 cur_line->type = FILE_LINE_BLANK;
1312 cur_line = cur_line->next;
1316 /* Header done, process the main part of the file */
1317 while (cur_line != NULL)
1319 /* At this point, (cur_line->unprocessed[0] == '{') */
1320 assert(cur_line->unprocessed[0] == '{');
1321 text = cur_line->unprocessed + 1;
1322 len = strlen(text) - 1;
1323 if (text[len] != '}')
1325 /* No closing } on header */
1326 free_alias_list(alias_list);
1327 file->parse_error = cur_line;
1328 file->parse_error_text = "Headers starting with '{' must have a "
1329 "closing bracket ('}'). Headers starting with two brackets ('{{') "
1330 "must close with two brackets ('}}').";
1331 return JB_ERR_PARSE;
1336 /* An invalid {{ header. */
1337 free_alias_list(alias_list);
1338 file->parse_error = cur_line;
1339 file->parse_error_text = "Unknown or unexpected two-bracket header. "
1340 "Please remember that the system (two-bracket) headers must "
1341 "appear in the order {{settings}}, {{description}}, {{alias}}, "
1342 "and must appear before any actions (one-bracket) headers. "
1343 "Also note that system headers may not be repeated.";
1344 return JB_ERR_PARSE;
1347 while ( (*text == ' ') || (*text == '\t') )
1353 && ( (text[len - 1] == ' ')
1354 || (text[len - 1] == '\t') ) )
1359 cur_line->type = FILE_LINE_ACTION;
1361 /* Remove {} and make copy */
1362 if (NULL == (value = (char *) malloc(len + 1)))
1365 free_alias_list(alias_list);
1366 return JB_ERR_MEMORY;
1368 strncpy(value, text, len);
1372 err = get_actions(value, alias_list, cur_line->data.action);
1375 /* Invalid action or out of memory */
1377 free_alias_list(alias_list);
1378 if (err == JB_ERR_MEMORY)
1384 /* Line does not contain a name=value pair */
1385 file->parse_error = cur_line;
1386 file->parse_error_text = "This header does not specify a valid set of actions.";
1387 return JB_ERR_PARSE;
1391 /* Done with string - it was clobbered anyway */
1394 /* Process next line */
1395 cur_line = cur_line->next;
1397 /* Loop processing URL patterns */
1398 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1400 if (cur_line->unprocessed[0])
1402 /* Could parse URL here, but this isn't currently needed */
1404 cur_line->type = FILE_LINE_URL;
1408 cur_line->type = FILE_LINE_BLANK;
1410 cur_line = cur_line->next;
1412 } /* End main while(cur_line != NULL) loop */
1414 free_alias_list(alias_list);
1420 /*********************************************************************
1422 * Function : edit_read_file_lines
1424 * Description : Read all the lines of a file into memory.
1425 * Handles whitespace, comments and line continuation.
1428 * 1 : fp = File to read from. On return, this will be
1429 * at EOF but it will not have been closed.
1430 * 2 : pfile = Destination for a linked list of file_lines.
1431 * Will be set to NULL on error.
1433 * Returns : JB_ERR_OK on success
1434 * JB_ERR_MEMORY on out-of-memory
1436 *********************************************************************/
1437 jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile, int *newline)
1439 struct file_line * first_line; /* Keep for return value or to free */
1440 struct file_line * cur_line; /* Current line */
1441 struct file_line * prev_line; /* Entry with prev_line->next = cur_line */
1449 cur_line = first_line = zalloc(sizeof(struct file_line));
1450 if (cur_line == NULL)
1452 return JB_ERR_MEMORY;
1455 cur_line->type = FILE_LINE_UNPROCESSED;
1457 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed, newline, NULL);
1460 /* Out of memory or empty file. */
1461 /* Note that empty file is not an error we propogate up */
1463 return ((rval == JB_ERR_FILE) ? JB_ERR_OK : rval);
1468 prev_line = cur_line;
1469 cur_line = prev_line->next = zalloc(sizeof(struct file_line));
1470 if (cur_line == NULL)
1473 edit_free_file_lines(first_line);
1474 return JB_ERR_MEMORY;
1477 cur_line->type = FILE_LINE_UNPROCESSED;
1479 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed, newline, NULL);
1480 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
1483 edit_free_file_lines(first_line);
1484 return JB_ERR_MEMORY;
1488 while (rval != JB_ERR_FILE);
1492 /* We allocated one too many - free it */
1493 prev_line->next = NULL;
1496 *pfile = first_line;
1501 /*********************************************************************
1503 * Function : edit_read_file
1505 * Description : Read a complete file into memory.
1506 * Handles CGI parameter parsing. If requested, also
1507 * checks the file's modification timestamp.
1510 * 1 : csp = Current client state (buffers, headers, etc...)
1511 * 2 : parameters = map of cgi parameters.
1512 * 3 : require_version = true to check "ver" parameter.
1513 * 4 : suffix = File extension, e.g. ".action".
1514 * 5 : pfile = Destination for the file. Will be set
1518 * filename : The name of the file to read, without the
1519 * path or ".action" extension.
1520 * ver : (Only if require_version is nonzero)
1521 * Timestamp of the actions file. If wrong, this
1522 * function fails with JB_ERR_MODIFIED.
1524 * Returns : JB_ERR_OK on success
1525 * JB_ERR_MEMORY on out-of-memory
1526 * JB_ERR_CGI_PARAMS if "filename" was not specified
1528 * JB_ERR_FILE if the file cannot be opened or
1530 * JB_ERR_MODIFIED if version checking was requested and
1531 * failed - the file was modified outside
1532 * of this CGI editor instance.
1534 *********************************************************************/
1535 jb_err edit_read_file(struct client_state *csp,
1536 const struct map *parameters,
1537 int require_version,
1539 struct editable_file **pfile)
1541 struct file_line * lines;
1545 const char * identifier;
1546 struct editable_file * file;
1547 unsigned version = 0;
1548 struct stat statbuf[1];
1549 char version_buf[22];
1550 int newline = NEWLINE_UNKNOWN;
1558 err = get_file_name_param(csp, parameters, "f", suffix,
1559 &filename, &identifier);
1565 if (stat(filename, statbuf) < 0)
1567 /* Error, probably file not found. */
1571 version = (unsigned) statbuf->st_mtime;
1573 if (require_version)
1575 unsigned specified_version;
1576 err = get_number_param(csp, parameters, "v", &specified_version);
1583 if (version != specified_version)
1585 return JB_ERR_MODIFIED;
1589 #if defined(AMIGA) || defined(__OS2__)
1590 if (NULL == (fp = fopen(filename,"r")))
1592 if (NULL == (fp = fopen(filename,"rt")))
1593 #endif /* def AMIGA */
1599 err = edit_read_file_lines(fp, &lines, &newline);
1609 file = (struct editable_file *) zalloc(sizeof(*file));
1613 edit_free_file_lines(lines);
1617 file->lines = lines;
1618 file->newline = newline;
1619 file->filename = filename;
1620 file->version = version;
1621 file->identifier = url_encode(identifier);
1623 if (file->identifier == NULL)
1625 edit_free_file(file);
1626 return JB_ERR_MEMORY;
1629 /* Correct file->version_str */
1630 freez(file->version_str);
1631 snprintf(version_buf, 22, "%u", file->version);
1632 version_buf[21] = '\0';
1633 file->version_str = strdup(version_buf);
1634 if (version_buf == NULL)
1636 edit_free_file(file);
1637 return JB_ERR_MEMORY;
1645 /*********************************************************************
1647 * Function : edit_read_actions_file
1649 * Description : Read a complete actions file into memory.
1650 * Handles CGI parameter parsing. If requested, also
1651 * checks the file's modification timestamp.
1653 * If this function detects an error in the categories
1654 * JB_ERR_FILE, JB_ERR_MODIFIED, or JB_ERR_PARSE,
1655 * then it handles it by filling in the specified
1656 * response structure and returning JB_ERR_FILE.
1659 * 1 : csp = Current client state (buffers, headers, etc...)
1660 * 2 : rsp = HTTP response. Only filled in on error.
1661 * 2 : parameters = map of cgi parameters.
1662 * 3 : require_version = true to check "ver" parameter.
1663 * 4 : pfile = Destination for the file. Will be set
1667 * filename : The name of the actions file to read, without the
1668 * path or ".action" extension.
1669 * ver : (Only if require_version is nonzero)
1670 * Timestamp of the actions file. If wrong, this
1671 * function fails with JB_ERR_MODIFIED.
1673 * Returns : JB_ERR_OK on success
1674 * JB_ERR_MEMORY on out-of-memory
1675 * JB_ERR_CGI_PARAMS if "filename" was not specified
1677 * JB_ERR_FILE if the file does not contain valid data,
1678 * or if file cannot be opened or
1679 * contains no data, or if version
1680 * checking was requested and failed.
1682 *********************************************************************/
1683 jb_err edit_read_actions_file(struct client_state *csp,
1684 struct http_response *rsp,
1685 const struct map *parameters,
1686 int require_version,
1687 struct editable_file **pfile)
1690 struct editable_file *file;
1698 err = edit_read_file(csp, parameters, require_version, ".action", &file);
1701 /* Try to handle if possible */
1702 if (err == JB_ERR_FILE)
1704 err = cgi_error_file(csp, rsp, lookup(parameters, "f"));
1706 else if (err == JB_ERR_MODIFIED)
1708 err = cgi_error_modified(csp, rsp, lookup(parameters, "f"));
1710 if (err == JB_ERR_OK)
1713 * Signal to higher-level CGI code that there was a problem but we
1714 * handled it, they should just return JB_ERR_OK.
1721 err = edit_parse_actions_file(file);
1724 if (err == JB_ERR_PARSE)
1726 err = cgi_error_parse(csp, rsp, file);
1727 if (err == JB_ERR_OK)
1730 * Signal to higher-level CGI code that there was a problem but we
1731 * handled it, they should just return JB_ERR_OK.
1736 edit_free_file(file);
1745 /*********************************************************************
1747 * Function : get_file_name_param
1749 * Description : Get the name of the file to edit from the parameters
1750 * passed to a CGI function. This function handles
1751 * security checks such as blocking urls containing
1752 * "/" or ".", prepending the config file directory,
1753 * and adding the specified suffix.
1755 * (This is an essential security check, otherwise
1756 * users may be able to pass "../../../etc/passwd"
1757 * and overwrite the password file [linux], "prn:"
1758 * and print random data [Windows], etc...)
1760 * This function only allows filenames contining the
1761 * characters '-', '_', 'A'-'Z', 'a'-'z', and '0'-'9'.
1762 * That's probably too restrictive but at least it's
1766 * 1 : csp = Current client state (buffers, headers, etc...)
1767 * 2 : parameters = map of cgi parameters
1768 * 3 : param_name = The name of the parameter to read
1769 * 4 : suffix = File extension, e.g. ".actions"
1770 * 5 : pfilename = destination for full filename. Caller
1771 * free()s. Set to NULL on error.
1772 * 6 : pparam = destination for partial filename,
1773 * suitable for use in another URL. Allocated as part
1774 * of the map "parameters", so don't free it.
1775 * Set to NULL if not specified.
1777 * Returns : JB_ERR_OK on success
1778 * JB_ERR_MEMORY on out-of-memory
1779 * JB_ERR_CGI_PARAMS if "filename" was not specified
1782 *********************************************************************/
1783 static jb_err get_file_name_param(struct client_state *csp,
1784 const struct map *parameters,
1785 const char *param_name,
1788 const char **pparam)
1792 #if 0 /* Patch to make 3.0.0 work properly. */
1794 #endif /* 0 - Patch to make 3.0.0 work properly. */
1808 param = lookup(parameters, param_name);
1811 return JB_ERR_CGI_PARAMS;
1816 len = strlen(param);
1817 if (len >= FILENAME_MAX)
1820 return JB_ERR_CGI_PARAMS;
1823 /* Check every character to see if it's legal */
1825 while ((ch = *s++) != '\0')
1827 if ( ((ch < 'A') || (ch > 'Z'))
1828 && ((ch < 'a') || (ch > 'z'))
1829 && ((ch < '0') || (ch > '9'))
1833 /* Probable hack attempt. */
1834 return JB_ERR_CGI_PARAMS;
1839 * FIXME Following is a hack to make 3.0.0 work properly.
1840 * Change "#if 0" --> "#if 1" below when we have modular action
1844 #if 0 /* Patch to make 3.0.0 work properly. */
1845 /* Append extension */
1846 name = malloc(len + strlen(suffix) + 1);
1849 return JB_ERR_MEMORY;
1851 strcpy(name, param);
1852 strcpy(name + len, suffix);
1855 fullpath = make_path(csp->config->confdir, name);
1857 #else /* 1 - Patch to make 3.0.0 work properly. */
1858 if ((csp->actions_list == NULL)
1859 || (csp->actions_list->filename == NULL))
1861 return JB_ERR_CGI_PARAMS;
1864 fullpath = ( (csp->actions_list && csp->actions_list->filename)
1865 ? strdup(csp->actions_list->filename) : NULL);
1866 #endif /* 1 - Patch to make 3.0.0 work properly. */
1867 if (fullpath == NULL)
1869 return JB_ERR_MEMORY;
1873 *pfilename = fullpath;
1879 /*********************************************************************
1881 * Function : get_char_param
1883 * Description : Get a single-character parameter passed to a CGI
1887 * 1 : parameters = map of cgi parameters
1888 * 2 : param_name = The name of the parameter to read
1890 * Returns : Uppercase character on success, '\0' on error.
1892 *********************************************************************/
1893 static char get_char_param(const struct map *parameters,
1894 const char *param_name)
1901 ch = *(lookup(parameters, param_name));
1902 if ((ch >= 'a') && (ch <= 'z'))
1904 ch = ch - 'a' + 'A';
1911 /*********************************************************************
1913 * Function : get_string_param
1915 * Description : Get a string paramater, to be used as an
1916 * ACTION_STRING or ACTION_MULTI paramater.
1917 * Validates the input to prevent stupid/malicious
1918 * users from corrupting their action file.
1921 * 1 : parameters = map of cgi parameters
1922 * 2 : param_name = The name of the parameter to read
1923 * 3 : pparam = destination for paramater. Allocated as
1924 * part of the map "parameters", so don't free it.
1925 * Set to NULL if not specified.
1927 * Returns : JB_ERR_OK on success, or if the paramater
1928 * was not specified.
1929 * JB_ERR_MEMORY on out-of-memory.
1930 * JB_ERR_CGI_PARAMS if the paramater is not valid.
1932 *********************************************************************/
1933 static jb_err get_string_param(const struct map *parameters,
1934 const char *param_name,
1935 const char **pparam)
1947 param = lookup(parameters, param_name);
1953 if (strlen(param) >= CGI_ACTION_PARAM_LEN_MAX)
1958 * Note that the length limit is arbitrary, it just seems
1959 * sensible to limit it to *something*. There's no
1960 * technical reason for any limit at all.
1962 return JB_ERR_CGI_PARAMS;
1965 /* Check every character to see if it's legal */
1967 while ((ch = *s++) != '\0')
1969 if ( ((unsigned char)ch < (unsigned char)' ')
1972 /* Probable hack attempt, or user accidentally used '}'. */
1973 return JB_ERR_CGI_PARAMS;
1984 /*********************************************************************
1986 * Function : get_number_param
1988 * Description : Get a non-negative integer from the parameters
1989 * passed to a CGI function.
1992 * 1 : csp = Current client state (buffers, headers, etc...)
1993 * 2 : parameters = map of cgi parameters
1994 * 3 : name = Name of CGI parameter to read
1995 * 4 : pvalue = destination for value.
1996 * Set to -1 on error.
1998 * Returns : JB_ERR_OK on success
1999 * JB_ERR_MEMORY on out-of-memory
2000 * JB_ERR_CGI_PARAMS if the parameter was not specified
2003 *********************************************************************/
2004 static jb_err get_number_param(struct client_state *csp,
2005 const struct map *parameters,
2020 param = lookup(parameters, name);
2023 return JB_ERR_CGI_PARAMS;
2026 /* We don't use atoi because I want to check this carefully... */
2029 while ((ch = *param++) != '\0')
2031 if ((ch < '0') || (ch > '9'))
2033 return JB_ERR_CGI_PARAMS;
2040 * <limits.h> defines UINT_MAX
2042 * (UINT_MAX - ch) / 10 is the largest number that
2043 * can be safely multiplied by 10 then have ch added.
2045 if (value > ((UINT_MAX - (unsigned)ch) / 10U))
2047 return JB_ERR_CGI_PARAMS;
2050 value = value * 10 + ch;
2061 /*********************************************************************
2063 * Function : get_url_spec_param
2065 * Description : Get a URL pattern from the parameters
2066 * passed to a CGI function. Removes leading/trailing
2067 * spaces and validates it.
2070 * 1 : csp = Current client state (buffers, headers, etc...)
2071 * 2 : parameters = map of cgi parameters
2072 * 3 : name = Name of CGI parameter to read
2073 * 4 : pvalue = destination for value. Will be malloc()'d.
2074 * Set to NULL on error.
2076 * Returns : JB_ERR_OK on success
2077 * JB_ERR_MEMORY on out-of-memory
2078 * JB_ERR_CGI_PARAMS if the parameter was not specified
2081 *********************************************************************/
2082 static jb_err get_url_spec_param(struct client_state *csp,
2083 const struct map *parameters,
2087 const char *orig_param;
2090 struct url_spec compiled[1];
2100 orig_param = lookup(parameters, name);
2103 return JB_ERR_CGI_PARAMS;
2106 /* Copy and trim whitespace */
2107 param = strdup(orig_param);
2110 return JB_ERR_MEMORY;
2114 /* Must be non-empty, and can't allow 1st character to be '{' */
2115 if (param[0] == '\0' || param[0] == '{')
2118 return JB_ERR_CGI_PARAMS;
2121 /* Check for embedded newlines */
2122 for (s = param; *s != '\0'; s++)
2124 if ((*s == '\r') || (*s == '\n'))
2127 return JB_ERR_CGI_PARAMS;
2131 /* Check that regex is valid */
2136 return JB_ERR_MEMORY;
2138 err = create_url_spec(compiled, s);
2143 return (err == JB_ERR_MEMORY) ? JB_ERR_MEMORY : JB_ERR_CGI_PARAMS;
2145 free_url_spec(compiled);
2147 if (param[strlen(param) - 1] == '\\')
2150 * Must protect trailing '\\' from becoming line continuation character.
2151 * Two methods: 1) If it's a domain only, add a trailing '/'.
2152 * 2) For path, add the do-nothing PCRE expression (?:) to the end
2154 if (strchr(param, '/') == NULL)
2156 err = string_append(¶m, "/");
2160 err = string_append(¶m, "(?:)");
2167 /* Check that the modified regex is valid */
2172 return JB_ERR_MEMORY;
2174 err = create_url_spec(compiled, s);
2179 return (err == JB_ERR_MEMORY) ? JB_ERR_MEMORY : JB_ERR_CGI_PARAMS;
2181 free_url_spec(compiled);
2188 /*********************************************************************
2190 * Function : map_radio
2192 * Description : Map a set of radio button values. E.g. if you have
2193 * 3 radio buttons, declare them as:
2194 * <option type="radio" name="xyz" @xyz-a@>
2195 * <option type="radio" name="xyz" @xyz-b@>
2196 * <option type="radio" name="xyz" @xyz-c@>
2197 * Then map one of the @xyz-?@ variables to "checked"
2198 * and all the others to empty by calling:
2199 * map_radio(exports, "xyz", "abc", sel)
2200 * Where 'sel' is 'a', 'b', or 'c'.
2203 * 1 : exports = Exports map to modify.
2204 * 2 : optionname = name for map
2205 * 3 : values = null-terminated list of values;
2206 * 4 : value = Selected value.
2208 * CGI Parameters : None
2210 * Returns : JB_ERR_OK on success
2211 * JB_ERR_MEMORY on out-of-memory
2213 *********************************************************************/
2214 static jb_err map_radio(struct map * exports,
2215 const char * optionname,
2216 const char * values,
2228 len = strlen(optionname);
2229 buf = malloc(len + 3);
2232 return JB_ERR_MEMORY;
2235 strcpy(buf, optionname);
2240 while ((c = *values++) != '\0')
2245 if (map(exports, buf, 1, "", 1))
2247 return JB_ERR_MEMORY;
2253 return map(exports, buf, 0, "checked", 1);
2257 /*********************************************************************
2259 * Function : cgi_error_modified
2261 * Description : CGI function that is called when a file is modified
2262 * outside the CGI editor.
2265 * 1 : csp = Current client state (buffers, headers, etc...)
2266 * 2 : rsp = http_response data structure for output
2267 * 3 : filename = The file that was modified.
2269 * CGI Parameters : none
2271 * Returns : JB_ERR_OK on success
2272 * JB_ERR_MEMORY on out-of-memory error.
2274 *********************************************************************/
2275 jb_err cgi_error_modified(struct client_state *csp,
2276 struct http_response *rsp,
2277 const char *filename)
2279 struct map *exports;
2286 if (NULL == (exports = default_exports(csp, NULL)))
2288 return JB_ERR_MEMORY;
2291 err = map(exports, "f", 1, html_encode(filename), 0);
2298 return template_fill_for_cgi(csp, "cgi-error-modified", exports, rsp);
2302 /*********************************************************************
2304 * Function : cgi_error_parse
2306 * Description : CGI function that is called when a file cannot
2307 * be parsed by the CGI editor.
2310 * 1 : csp = Current client state (buffers, headers, etc...)
2311 * 2 : rsp = http_response data structure for output
2312 * 3 : file = The file that was modified.
2314 * CGI Parameters : none
2316 * Returns : JB_ERR_OK on success
2317 * JB_ERR_MEMORY on out-of-memory error.
2319 *********************************************************************/
2320 jb_err cgi_error_parse(struct client_state *csp,
2321 struct http_response *rsp,
2322 struct editable_file *file)
2324 struct map *exports;
2326 struct file_line *cur_line;
2332 if (NULL == (exports = default_exports(csp, NULL)))
2334 return JB_ERR_MEMORY;
2337 err = map(exports, "f", 1, file->identifier, 1);
2338 if (!err) err = map(exports, "parse-error", 1, html_encode(file->parse_error_text), 0);
2340 cur_line = file->parse_error;
2343 if (!err) err = map(exports, "line-raw", 1, html_encode(cur_line->raw), 0);
2344 if (!err) err = map(exports, "line-data", 1, html_encode(cur_line->unprocessed), 0);
2352 return template_fill_for_cgi(csp, "cgi-error-parse", exports, rsp);
2356 /*********************************************************************
2358 * Function : cgi_error_file
2360 * Description : CGI function that is called when a file cannot be
2361 * opened by the CGI editor.
2364 * 1 : csp = Current client state (buffers, headers, etc...)
2365 * 2 : rsp = http_response data structure for output
2366 * 3 : filename = The file that was modified.
2368 * CGI Parameters : none
2370 * Returns : JB_ERR_OK on success
2371 * JB_ERR_MEMORY on out-of-memory error.
2373 *********************************************************************/
2374 jb_err cgi_error_file(struct client_state *csp,
2375 struct http_response *rsp,
2376 const char *filename)
2378 struct map *exports;
2385 if (NULL == (exports = default_exports(csp, NULL)))
2387 return JB_ERR_MEMORY;
2390 err = map(exports, "f", 1, html_encode(filename), 0);
2397 return template_fill_for_cgi(csp, "cgi-error-file", exports, rsp);
2401 /*********************************************************************
2403 * Function : cgi_error_bad_param
2405 * Description : CGI function that is called if the parameters
2406 * (query string) for a CGI were wrong.
2409 * 1 : csp = Current client state (buffers, headers, etc...)
2410 * 2 : rsp = http_response data structure for output
2412 * CGI Parameters : none
2414 * Returns : JB_ERR_OK on success
2415 * JB_ERR_MEMORY on out-of-memory error.
2417 *********************************************************************/
2418 jb_err cgi_error_disabled(struct client_state *csp,
2419 struct http_response *rsp)
2421 struct map *exports;
2426 if (NULL == (exports = default_exports(csp, NULL)))
2428 return JB_ERR_MEMORY;
2431 return template_fill_for_cgi(csp, "cgi-error-disabled", exports, rsp);
2435 /*********************************************************************
2437 * Function : cgi_edit_actions
2439 * Description : CGI function that allows the user to choose which
2440 * actions file to edit.
2443 * 1 : csp = Current client state (buffers, headers, etc...)
2444 * 2 : rsp = http_response data structure for output
2445 * 3 : parameters = map of cgi parameters
2447 * CGI Parameters : None
2449 * Returns : JB_ERR_OK on success
2450 * JB_ERR_MEMORY on out-of-memory error
2452 *********************************************************************/
2453 jb_err cgi_edit_actions(struct client_state *csp,
2454 struct http_response *rsp,
2455 const struct map *parameters)
2458 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2460 return cgi_error_disabled(csp, rsp);
2463 /* FIXME: Incomplete */
2464 rsp->status = strdup("302 Local Redirect from Privoxy");
2465 if (rsp->status == NULL)
2467 return JB_ERR_MEMORY;
2469 if (enlist_unique_header(rsp->headers, "Location",
2470 CGI_PREFIX "edit-actions-list?f=default"))
2474 return JB_ERR_MEMORY;
2481 /*********************************************************************
2483 * Function : cgi_edit_actions_list
2485 * Description : CGI function that edits the actions list.
2486 * FIXME: This function shouldn't FATAL ever.
2487 * FIXME: This function doesn't check the retval of map()
2489 * 1 : csp = Current client state (buffers, headers, etc...)
2490 * 2 : rsp = http_response data structure for output
2491 * 3 : parameters = map of cgi parameters
2493 * CGI Parameters : filename
2495 * Returns : JB_ERR_OK on success
2496 * JB_ERR_MEMORY on out-of-memory
2497 * JB_ERR_FILE if the file cannot be opened or
2499 * JB_ERR_CGI_PARAMS if "filename" was not specified
2502 *********************************************************************/
2503 jb_err cgi_edit_actions_list(struct client_state *csp,
2504 struct http_response *rsp,
2505 const struct map *parameters)
2507 char * section_template;
2508 char * url_template;
2513 struct map * exports;
2514 struct map * section_exports;
2515 struct map * url_exports;
2516 struct editable_file * file;
2517 struct file_line * cur_line;
2518 unsigned line_number = 0;
2519 unsigned prev_section_line_number = ((unsigned) (-1));
2523 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2525 return cgi_error_disabled(csp, rsp);
2528 err = edit_read_actions_file(csp, rsp, parameters, 0, &file);
2531 /* No filename specified, can't read file, or out of memory. */
2532 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2535 if (NULL == (exports = default_exports(csp, NULL)))
2537 edit_free_file(file);
2538 return JB_ERR_MEMORY;
2541 err = map(exports, "f", 1, file->identifier, 1);
2542 if (!err) err = map(exports, "v", 1, file->version_str, 1);
2546 edit_free_file(file);
2551 /* Should do all global exports above this point */
2553 err = template_load(csp, §ion_template, "edit-actions-list-section");
2556 edit_free_file(file);
2558 if (err == JB_ERR_FILE)
2560 return cgi_error_no_template(csp, rsp, "edit-actions-list-section");
2565 err = template_load(csp, &url_template, "edit-actions-list-url");
2568 free(section_template);
2569 edit_free_file(file);
2571 if (err == JB_ERR_FILE)
2573 return cgi_error_no_template(csp, rsp, "edit-actions-list-url");
2578 err = template_fill(§ion_template, exports);
2582 edit_free_file(file);
2588 err = template_fill(&url_template, exports);
2591 free(section_template);
2592 edit_free_file(file);
2597 /* Find start of actions in file */
2598 cur_line = file->lines;
2600 while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION))
2602 cur_line = cur_line->next;
2606 if (NULL == (sections = strdup("")))
2608 free(section_template);
2610 edit_free_file(file);
2612 return JB_ERR_MEMORY;
2615 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_ACTION))
2617 if (NULL == (section_exports = new_map()))
2620 free(section_template);
2622 edit_free_file(file);
2624 return JB_ERR_MEMORY;
2627 snprintf(buf, 50, "%d", line_number);
2628 err = map(section_exports, "s", 1, buf, 1);
2629 if (!err) err = map(section_exports, "actions", 1,
2630 actions_to_html(cur_line->data.action), 0);
2633 && (cur_line->next != NULL)
2634 && (cur_line->next->type == FILE_LINE_URL))
2636 /* This section contains at least one URL, don't allow delete */
2637 err = map_block_killer(section_exports, "empty-section");
2641 if (!err) err = map_block_keep(section_exports, "empty-section");
2644 if (prev_section_line_number != ((unsigned)(-1)))
2646 /* Not last section */
2647 snprintf(buf, 50, "%d", prev_section_line_number);
2648 if (!err) err = map(section_exports, "s-prev", 1, buf, 1);
2649 if (!err) err = map_block_keep(section_exports, "s-prev-exists");
2654 if (!err) err = map_block_killer(section_exports, "s-prev-exists");
2656 prev_section_line_number = line_number;
2661 free(section_template);
2663 edit_free_file(file);
2665 free_map(section_exports);
2669 /* Should do all section-specific exports above this point */
2671 if (NULL == (urls = strdup("")))
2674 free(section_template);
2676 edit_free_file(file);
2678 free_map(section_exports);
2679 return JB_ERR_MEMORY;
2684 cur_line = cur_line->next;
2687 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL))
2689 if (NULL == (url_exports = new_map()))
2693 free(section_template);
2695 edit_free_file(file);
2697 free_map(section_exports);
2698 return JB_ERR_MEMORY;
2701 snprintf(buf, 50, "%d", line_number);
2702 err = map(url_exports, "p", 1, buf, 1);
2704 snprintf(buf, 50, "%d", url_1_2);
2705 if (!err) err = map(url_exports, "url-1-2", 1, buf, 1);
2707 if (!err) err = map(url_exports, "url-html", 1,
2708 html_encode(cur_line->unprocessed), 0);
2709 if (!err) err = map(url_exports, "url", 1,
2710 url_encode(cur_line->unprocessed), 0);
2716 free(section_template);
2718 edit_free_file(file);
2720 free_map(section_exports);
2721 free_map(url_exports);
2725 if (NULL == (s = strdup(url_template)))
2729 free(section_template);
2731 edit_free_file(file);
2733 free_map(section_exports);
2734 free_map(url_exports);
2735 return JB_ERR_MEMORY;
2738 err = template_fill(&s, section_exports);
2739 if (!err) err = template_fill(&s, url_exports);
2740 if (!err) err = string_append(&urls, s);
2742 free_map(url_exports);
2749 free(section_template);
2751 edit_free_file(file);
2753 free_map(section_exports);
2757 url_1_2 = 3 - url_1_2;
2759 cur_line = cur_line->next;
2763 err = map(section_exports, "urls", 1, urls, 0);
2765 /* Could also do section-specific exports here, but it wouldn't be as fast */
2767 if ( (cur_line != NULL)
2768 && (cur_line->type == FILE_LINE_ACTION))
2770 /* Not last section */
2771 snprintf(buf, 50, "%d", line_number);
2772 if (!err) err = map(section_exports, "s-next", 1, buf, 1);
2773 if (!err) err = map_block_keep(section_exports, "s-next-exists");
2778 if (!err) err = map_block_killer(section_exports, "s-next-exists");
2784 free(section_template);
2786 edit_free_file(file);
2788 free_map(section_exports);
2792 if (NULL == (s = strdup(section_template)))
2795 free(section_template);
2797 edit_free_file(file);
2799 free_map(section_exports);
2800 return JB_ERR_MEMORY;
2803 err = template_fill(&s, section_exports);
2804 if (!err) err = string_append(§ions, s);
2807 free_map(section_exports);
2812 free(section_template);
2814 edit_free_file(file);
2820 edit_free_file(file);
2821 free(section_template);
2824 err = map(exports, "sections", 1, sections, 0);
2831 /* Could also do global exports here, but it wouldn't be as fast */
2833 return template_fill_for_cgi(csp, "edit-actions-list", exports, rsp);
2837 /*********************************************************************
2839 * Function : cgi_edit_actions
2841 * Description : CGI function that edits the Actions list.
2844 * 1 : csp = Current client state (buffers, headers, etc...)
2845 * 2 : rsp = http_response data structure for output
2846 * 3 : parameters = map of cgi parameters
2848 * CGI Parameters : None
2850 * Returns : JB_ERR_OK on success
2851 * JB_ERR_MEMORY on out-of-memory
2852 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2853 * specified or not valid.
2855 *********************************************************************/
2856 jb_err cgi_edit_actions_for_url(struct client_state *csp,
2857 struct http_response *rsp,
2858 const struct map *parameters)
2860 struct map * exports;
2862 struct editable_file * file;
2863 struct file_line * cur_line;
2864 unsigned line_number;
2866 struct file_list *filter_file;
2867 struct re_filterfile_spec *filter_group;
2869 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2871 return cgi_error_disabled(csp, rsp);
2874 err = get_number_param(csp, parameters, "s", §ionid);
2880 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2883 /* No filename specified, can't read file, modified, or out of memory. */
2884 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2887 cur_line = file->lines;
2889 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
2891 cur_line = cur_line->next;
2894 if ( (cur_line == NULL)
2895 || (line_number != sectionid)
2897 || (cur_line->type != FILE_LINE_ACTION))
2899 /* Invalid "sectionid" parameter */
2900 edit_free_file(file);
2901 return JB_ERR_CGI_PARAMS;
2904 if (NULL == (exports = default_exports(csp, NULL)))
2906 edit_free_file(file);
2907 return JB_ERR_MEMORY;
2910 err = map(exports, "f", 1, file->identifier, 1);
2911 if (!err) err = map(exports, "v", 1, file->version_str, 1);
2912 if (!err) err = map(exports, "s", 1, url_encode(lookup(parameters, "s")), 0);
2914 if (!err) err = actions_to_radio(exports, cur_line->data.action);
2916 filter_file = csp->rlist;
2917 filter_group = ((filter_file != NULL) ? filter_file->f : NULL);
2919 if (!err) err = map_conditional(exports, "any-filters-defined", (filter_group != NULL));
2923 edit_free_file(file);
2928 if (filter_group == NULL)
2930 err = map(exports, "filter-params", 1, "", 1);
2934 /* We have some entries in the filter list */
2937 char * filter_template;
2939 err = template_load(csp, &filter_template, "edit-actions-for-url-filter");
2942 edit_free_file(file);
2944 if (err == JB_ERR_FILE)
2946 return cgi_error_no_template(csp, rsp, "edit-actions-for-url-filter");
2951 result = strdup("");
2953 for (;(!err) && (filter_group != NULL); filter_group = filter_group->next)
2955 char current_mode = 'x';
2956 struct list_entry *filter_name;
2958 struct map *line_exports;
2961 filter_name = cur_line->data.action->multi_add[ACTION_MULTI_FILTER]->first;
2962 while ((filter_name != NULL)
2963 && (0 != strcmp(filter_group->name, filter_name->str)))
2965 filter_name = filter_name->next;
2968 if (filter_name != NULL)
2974 filter_name = cur_line->data.action->multi_remove[ACTION_MULTI_FILTER]->first;
2975 while ((filter_name != NULL)
2976 && (0 != strcmp(filter_group->name, filter_name->str)))
2978 filter_name = filter_name->next;
2980 if (filter_name != NULL)
2986 /* Generate a unique serial number */
2987 snprintf(number, sizeof(number), "%x", index++);
2988 number[sizeof(number) - 1] = '\0';
2990 line_exports = new_map();
2991 if (line_exports == NULL)
2993 err = JB_ERR_MEMORY;
2998 if (!err) err = map(line_exports, "index", 1, number, 1);
2999 if (!err) err = map(line_exports, "name", 1, filter_group->name, 1);
3000 if (!err) err = map(line_exports, "description", 1, filter_group->description, 1);
3001 if (!err) err = map_radio(line_exports, "this-filter", "ynx", current_mode);
3006 this_line = strdup(filter_template);
3007 if (this_line == NULL) err = JB_ERR_MEMORY;
3009 if (!err) err = template_fill(&this_line, line_exports);
3010 string_join(&result, this_line);
3012 free_map(line_exports);
3017 err = map(exports, "filter-params", 1, result, 0);
3025 if (!err) err = map_radio(exports, "filter-all", "nx",
3026 (cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] ? 'n' : 'x'));
3028 edit_free_file(file);
3036 return template_fill_for_cgi(csp, "edit-actions-for-url", exports, rsp);
3040 /*********************************************************************
3042 * Function : cgi_edit_actions_submit
3044 * Description : CGI function that actually edits the Actions list.
3047 * 1 : csp = Current client state (buffers, headers, etc...)
3048 * 2 : rsp = http_response data structure for output
3049 * 3 : parameters = map of cgi parameters
3051 * CGI Parameters : None
3053 * Returns : JB_ERR_OK on success
3054 * JB_ERR_MEMORY on out-of-memory
3055 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3056 * specified or not valid.
3058 *********************************************************************/
3059 jb_err cgi_edit_actions_submit(struct client_state *csp,
3060 struct http_response *rsp,
3061 const struct map *parameters)
3067 struct editable_file * file;
3068 struct file_line * cur_line;
3069 unsigned line_number;
3075 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3077 return cgi_error_disabled(csp, rsp);
3080 err = get_number_param(csp, parameters, "s", §ionid);
3086 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3089 /* No filename specified, can't read file, modified, or out of memory. */
3090 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3093 cur_line = file->lines;
3095 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
3097 cur_line = cur_line->next;
3100 if ( (cur_line == NULL)
3101 || (line_number != sectionid)
3103 || (cur_line->type != FILE_LINE_ACTION))
3105 /* Invalid "sectionid" parameter */
3106 edit_free_file(file);
3107 return JB_ERR_CGI_PARAMS;
3110 err = actions_from_radio(parameters, cur_line->data.action);
3114 edit_free_file(file);
3118 ch = get_char_param(parameters, "filter_all");
3121 list_remove_all(cur_line->data.action->multi_add[ACTION_MULTI_FILTER]);
3122 list_remove_all(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER]);
3123 cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] = 1;
3127 cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER] = 0;
3130 for (index = 0; !err; index++)
3137 /* Generate the keys */
3138 snprintf(key_value, sizeof(key_value), "filter_r%x", index);
3139 key_value[sizeof(key_value) - 1] = '\0';
3140 snprintf(key_name, sizeof(key_name), "filter_n%x", index);
3141 key_name[sizeof(key_name) - 1] = '\0';
3143 err = get_string_param(parameters, key_name, &name);
3153 value = get_char_param(parameters, key_value);
3156 list_remove_item(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name);
3157 if (!err) err = enlist(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name);
3158 list_remove_item(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name);
3160 else if (value == 'N')
3162 list_remove_item(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name);
3163 if (!cur_line->data.action->multi_remove_all[ACTION_MULTI_FILTER])
3165 list_remove_item(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name);
3166 if (!err) err = enlist(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name);
3169 else if (value == 'X')
3171 list_remove_item(cur_line->data.action->multi_add[ACTION_MULTI_FILTER], name);
3172 list_remove_item(cur_line->data.action->multi_remove[ACTION_MULTI_FILTER], name);
3179 edit_free_file(file);
3183 if (NULL == (actiontext = actions_to_text(cur_line->data.action)))
3186 edit_free_file(file);
3187 return JB_ERR_MEMORY;
3190 len = strlen(actiontext);
3194 * Empty action - must special-case this.
3195 * Simply setting len to 1 is sufficient...
3200 if (NULL == (newtext = malloc(len + 2)))
3204 edit_free_file(file);
3205 return JB_ERR_MEMORY;
3207 strcpy(newtext, actiontext);
3211 newtext[len + 1] = '\0';
3213 freez(cur_line->raw);
3214 freez(cur_line->unprocessed);
3215 cur_line->unprocessed = newtext;
3217 err = edit_write_file(file);
3220 /* Error writing file */
3221 edit_free_file(file);
3225 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3226 string_append(&target, file->identifier);
3228 edit_free_file(file);
3233 return JB_ERR_MEMORY;
3236 rsp->status = strdup("302 Local Redirect from Privoxy");
3237 if (rsp->status == NULL)
3240 return JB_ERR_MEMORY;
3242 err = enlist_unique_header(rsp->headers, "Location", target);
3249 /*********************************************************************
3251 * Function : cgi_edit_actions_url
3253 * Description : CGI function that actually edits a URL pattern in
3257 * 1 : csp = Current client state (buffers, headers, etc...)
3258 * 2 : rsp = http_response data structure for output
3259 * 3 : parameters = map of cgi parameters
3262 * filename : Identifies the file to edit
3263 * ver : File's last-modified time
3264 * section : Line number of section to edit
3265 * pattern : Line number of pattern to edit
3266 * newval : New value for pattern
3268 * Returns : JB_ERR_OK on success
3269 * JB_ERR_MEMORY on out-of-memory
3270 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3271 * specified or not valid.
3273 *********************************************************************/
3274 jb_err cgi_edit_actions_url(struct client_state *csp,
3275 struct http_response *rsp,
3276 const struct map *parameters)
3280 struct editable_file * file;
3281 struct file_line * cur_line;
3282 unsigned line_number;
3286 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3288 return cgi_error_disabled(csp, rsp);
3291 err = get_number_param(csp, parameters, "p", &patternid);
3298 return JB_ERR_CGI_PARAMS;
3301 err = get_url_spec_param(csp, parameters, "u", &new_pattern);
3307 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3310 /* No filename specified, can't read file, modified, or out of memory. */
3312 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3316 cur_line = file->lines;
3318 while ((cur_line != NULL) && (line_number < patternid))
3320 cur_line = cur_line->next;
3324 if ( (cur_line == NULL)
3325 || (cur_line->type != FILE_LINE_URL))
3327 /* Invalid "patternid" parameter */
3329 edit_free_file(file);
3330 return JB_ERR_CGI_PARAMS;
3333 /* At this point, the line to edit is in cur_line */
3335 freez(cur_line->raw);
3336 freez(cur_line->unprocessed);
3337 cur_line->unprocessed = new_pattern;
3339 err = edit_write_file(file);
3342 /* Error writing file */
3343 edit_free_file(file);
3347 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3348 string_append(&target, file->identifier);
3350 edit_free_file(file);
3355 return JB_ERR_MEMORY;
3358 rsp->status = strdup("302 Local Redirect from Privoxy");
3359 if (rsp->status == NULL)
3362 return JB_ERR_MEMORY;
3364 err = enlist_unique_header(rsp->headers, "Location", target);
3371 /*********************************************************************
3373 * Function : cgi_edit_actions_add_url
3375 * Description : CGI function that actually adds a URL pattern to
3379 * 1 : csp = Current client state (buffers, headers, etc...)
3380 * 2 : rsp = http_response data structure for output
3381 * 3 : parameters = map of cgi parameters
3384 * filename : Identifies the file to edit
3385 * ver : File's last-modified time
3386 * section : Line number of section to edit
3387 * newval : New pattern
3389 * Returns : JB_ERR_OK on success
3390 * JB_ERR_MEMORY on out-of-memory
3391 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3392 * specified or not valid.
3394 *********************************************************************/
3395 jb_err cgi_edit_actions_add_url(struct client_state *csp,
3396 struct http_response *rsp,
3397 const struct map *parameters)
3401 struct file_line * new_line;
3402 struct editable_file * file;
3403 struct file_line * cur_line;
3404 unsigned line_number;
3408 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3410 return cgi_error_disabled(csp, rsp);
3413 err = get_number_param(csp, parameters, "s", §ionid);
3420 return JB_ERR_CGI_PARAMS;
3423 err = get_url_spec_param(csp, parameters, "u", &new_pattern);
3429 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3432 /* No filename specified, can't read file, modified, or out of memory. */
3434 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3438 cur_line = file->lines;
3440 while ((cur_line != NULL) && (line_number < sectionid))
3442 cur_line = cur_line->next;
3446 if ( (cur_line == NULL)
3447 || (cur_line->type != FILE_LINE_ACTION))
3449 /* Invalid "sectionid" parameter */
3451 edit_free_file(file);
3452 return JB_ERR_CGI_PARAMS;
3455 /* At this point, the section header is in cur_line - add after this. */
3457 /* Allocate the new line */
3458 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3459 if (new_line == NULL)
3462 edit_free_file(file);
3463 return JB_ERR_MEMORY;
3466 /* Fill in the data members of the new line */
3467 new_line->raw = NULL;
3468 new_line->prefix = NULL;
3469 new_line->unprocessed = new_pattern;
3470 new_line->type = FILE_LINE_URL;
3472 /* Link new_line into the list, after cur_line */
3473 new_line->next = cur_line->next;
3474 cur_line->next = new_line;
3476 /* Done making changes, now commit */
3478 err = edit_write_file(file);
3481 /* Error writing file */
3482 edit_free_file(file);
3486 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3487 string_append(&target, file->identifier);
3489 edit_free_file(file);
3494 return JB_ERR_MEMORY;
3497 rsp->status = strdup("302 Local Redirect from Privoxy");
3498 if (rsp->status == NULL)
3501 return JB_ERR_MEMORY;
3503 err = enlist_unique_header(rsp->headers, "Location", target);
3510 /*********************************************************************
3512 * Function : cgi_edit_actions_remove_url
3514 * Description : CGI function that actually removes a URL pattern from
3518 * 1 : csp = Current client state (buffers, headers, etc...)
3519 * 2 : rsp = http_response data structure for output
3520 * 3 : parameters = map of cgi parameters
3523 * f : (filename) Identifies the file to edit
3524 * v : (version) File's last-modified time
3525 * p : (pattern) Line number of pattern to remove
3527 * Returns : JB_ERR_OK on success
3528 * JB_ERR_MEMORY on out-of-memory
3529 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3530 * specified or not valid.
3532 *********************************************************************/
3533 jb_err cgi_edit_actions_remove_url(struct client_state *csp,
3534 struct http_response *rsp,
3535 const struct map *parameters)
3538 struct editable_file * file;
3539 struct file_line * cur_line;
3540 struct file_line * prev_line;
3541 unsigned line_number;
3545 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3547 return cgi_error_disabled(csp, rsp);
3550 err = get_number_param(csp, parameters, "p", &patternid);
3556 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3559 /* No filename specified, can't read file, modified, or out of memory. */
3560 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3565 cur_line = file->lines;
3567 while ((cur_line != NULL) && (line_number < patternid))
3569 prev_line = cur_line;
3570 cur_line = cur_line->next;
3574 if ( (cur_line == NULL)
3575 || (prev_line == NULL)
3576 || (cur_line->type != FILE_LINE_URL))
3578 /* Invalid "patternid" parameter */
3579 edit_free_file(file);
3580 return JB_ERR_CGI_PARAMS;
3583 /* At this point, the line to remove is in cur_line, and the previous
3584 * one is in prev_line
3587 /* Unlink cur_line */
3588 prev_line->next = cur_line->next;
3589 cur_line->next = NULL;
3592 edit_free_file_lines(cur_line);
3594 err = edit_write_file(file);
3597 /* Error writing file */
3598 edit_free_file(file);
3602 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3603 string_append(&target, file->identifier);
3605 edit_free_file(file);
3610 return JB_ERR_MEMORY;
3613 rsp->status = strdup("302 Local Redirect from Privoxy");
3614 if (rsp->status == NULL)
3617 return JB_ERR_MEMORY;
3619 err = enlist_unique_header(rsp->headers, "Location", target);
3626 /*********************************************************************
3628 * Function : cgi_edit_actions_section_remove
3630 * Description : CGI function that actually removes a whole section from
3631 * the actions file. The section must be empty first
3632 * (else JB_ERR_CGI_PARAMS).
3635 * 1 : csp = Current client state (buffers, headers, etc...)
3636 * 2 : rsp = http_response data structure for output
3637 * 3 : parameters = map of cgi parameters
3640 * f : (filename) Identifies the file to edit
3641 * v : (version) File's last-modified time
3642 * s : (section) Line number of section to edit
3644 * Returns : JB_ERR_OK on success
3645 * JB_ERR_MEMORY on out-of-memory
3646 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3647 * specified or not valid.
3649 *********************************************************************/
3650 jb_err cgi_edit_actions_section_remove(struct client_state *csp,
3651 struct http_response *rsp,
3652 const struct map *parameters)
3655 struct editable_file * file;
3656 struct file_line * cur_line;
3657 struct file_line * prev_line;
3658 unsigned line_number;
3662 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3664 return cgi_error_disabled(csp, rsp);
3667 err = get_number_param(csp, parameters, "s", §ionid);
3673 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3676 /* No filename specified, can't read file, modified, or out of memory. */
3677 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3681 cur_line = file->lines;
3684 while ((cur_line != NULL) && (line_number < sectionid))
3686 prev_line = cur_line;
3687 cur_line = cur_line->next;
3691 if ( (cur_line == NULL)
3692 || (cur_line->type != FILE_LINE_ACTION) )
3694 /* Invalid "sectionid" parameter */
3695 edit_free_file(file);
3696 return JB_ERR_CGI_PARAMS;
3699 if ( (cur_line->next != NULL)
3700 && (cur_line->next->type == FILE_LINE_URL) )
3702 /* Section not empty. */
3703 edit_free_file(file);
3704 return JB_ERR_CGI_PARAMS;
3707 /* At this point, the line to remove is in cur_line, and the previous
3708 * one is in prev_line
3711 /* Unlink cur_line */
3712 if (prev_line == NULL)
3714 /* Removing the first line from the file */
3715 file->lines = cur_line->next;
3719 prev_line->next = cur_line->next;
3721 cur_line->next = NULL;
3724 edit_free_file_lines(cur_line);
3726 err = edit_write_file(file);
3729 /* Error writing file */
3730 edit_free_file(file);
3734 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3735 string_append(&target, file->identifier);
3737 edit_free_file(file);
3742 return JB_ERR_MEMORY;
3745 rsp->status = strdup("302 Local Redirect from Privoxy");
3746 if (rsp->status == NULL)
3749 return JB_ERR_MEMORY;
3751 err = enlist_unique_header(rsp->headers, "Location", target);
3758 /*********************************************************************
3760 * Function : cgi_edit_actions_section_add
3762 * Description : CGI function that adds a new empty section to
3766 * 1 : csp = Current client state (buffers, headers, etc...)
3767 * 2 : rsp = http_response data structure for output
3768 * 3 : parameters = map of cgi parameters
3771 * f : (filename) Identifies the file to edit
3772 * v : (version) File's last-modified time
3773 * s : (section) Line number of section to add after, 0 for
3776 * Returns : JB_ERR_OK on success
3777 * JB_ERR_MEMORY on out-of-memory
3778 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3779 * specified or not valid.
3781 *********************************************************************/
3782 jb_err cgi_edit_actions_section_add(struct client_state *csp,
3783 struct http_response *rsp,
3784 const struct map *parameters)
3787 struct file_line * new_line;
3789 struct editable_file * file;
3790 struct file_line * cur_line;
3791 unsigned line_number;
3795 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3797 return cgi_error_disabled(csp, rsp);
3800 err = get_number_param(csp, parameters, "s", §ionid);
3806 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3809 /* No filename specified, can't read file, modified, or out of memory. */
3810 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3814 cur_line = file->lines;
3818 /* Add to start of file */
3819 if (cur_line != NULL)
3821 /* There's something in the file, find the line before the first
3824 while ( (cur_line->next != NULL)
3825 && (cur_line->next->type != FILE_LINE_ACTION) )
3827 cur_line = cur_line->next;
3834 /* Add after stated section. */
3835 while ((cur_line != NULL) && (line_number < sectionid))
3837 cur_line = cur_line->next;
3841 if ( (cur_line == NULL)
3842 || (cur_line->type != FILE_LINE_ACTION))
3844 /* Invalid "sectionid" parameter */
3845 edit_free_file(file);
3846 return JB_ERR_CGI_PARAMS;
3849 /* Skip through the section to find the last line in it. */
3850 while ( (cur_line->next != NULL)
3851 && (cur_line->next->type != FILE_LINE_ACTION) )
3853 cur_line = cur_line->next;
3858 /* At this point, the last line in the previous section is in cur_line
3859 * - add after this. (Or if we need to add as the first line, cur_line
3863 new_text = strdup("{}");
3864 if (NULL == new_text)
3866 edit_free_file(file);
3867 return JB_ERR_MEMORY;
3870 /* Allocate the new line */
3871 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3872 if (new_line == NULL)
3875 edit_free_file(file);
3876 return JB_ERR_MEMORY;
3879 /* Fill in the data members of the new line */
3880 new_line->raw = NULL;
3881 new_line->prefix = NULL;
3882 new_line->unprocessed = new_text;
3883 new_line->type = FILE_LINE_ACTION;
3885 if (cur_line != NULL)
3887 /* Link new_line into the list, after cur_line */
3888 new_line->next = cur_line->next;
3889 cur_line->next = new_line;
3893 /* Link new_line into the list, as first line */
3894 new_line->next = file->lines;
3895 file->lines = new_line;
3898 /* Done making changes, now commit */
3900 err = edit_write_file(file);
3903 /* Error writing file */
3904 edit_free_file(file);
3908 target = strdup(CGI_PREFIX "edit-actions-list?f=");
3909 string_append(&target, file->identifier);
3911 edit_free_file(file);
3916 return JB_ERR_MEMORY;
3919 rsp->status = strdup("302 Local Redirect from Privoxy");
3920 if (rsp->status == NULL)
3923 return JB_ERR_MEMORY;
3925 err = enlist_unique_header(rsp->headers, "Location", target);
3932 /*********************************************************************
3934 * Function : cgi_edit_actions_section_swap
3936 * Description : CGI function that swaps the order of two sections
3937 * in the actions file. Note that this CGI can actually
3938 * swap any two arbitrary sections, but the GUI interface
3939 * currently only allows consecutive sections to be
3943 * 1 : csp = Current client state (buffers, headers, etc...)
3944 * 2 : rsp = http_response data structure for output
3945 * 3 : parameters = map of cgi parameters
3948 * f : (filename) Identifies the file to edit
3949 * v : (version) File's last-modified time
3950 * s1 : (section1) Line number of first section to swap
3951 * s2 : (section2) Line number of second section to swap
3953 * Returns : JB_ERR_OK on success
3954 * JB_ERR_MEMORY on out-of-memory
3955 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3956 * specified or not valid.
3958 *********************************************************************/
3959 jb_err cgi_edit_actions_section_swap(struct client_state *csp,
3960 struct http_response *rsp,
3961 const struct map *parameters)
3965 struct editable_file * file;
3966 struct file_line * cur_line;
3967 struct file_line * prev_line;
3968 struct file_line * line_before_section1;
3969 struct file_line * line_start_section1;
3970 struct file_line * line_end_section1;
3971 struct file_line * line_after_section1;
3972 struct file_line * line_before_section2;
3973 struct file_line * line_start_section2;
3974 struct file_line * line_end_section2;
3975 struct file_line * line_after_section2;
3976 unsigned line_number;
3980 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3982 return cgi_error_disabled(csp, rsp);
3985 err = get_number_param(csp, parameters, "s1", §ion1);
3986 if (!err) err = get_number_param(csp, parameters, "s2", §ion2);
3992 if (section1 > section2)
3994 unsigned temp = section2;
3995 section2 = section1;
3999 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
4002 /* No filename specified, can't read file, modified, or out of memory. */
4003 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
4006 /* Start at the beginning... */
4008 cur_line = file->lines;
4011 /* ... find section1 ... */
4012 while ((cur_line != NULL) && (line_number < section1))
4014 prev_line = cur_line;
4015 cur_line = cur_line->next;
4019 if ( (cur_line == NULL)
4020 || (cur_line->type != FILE_LINE_ACTION) )
4022 /* Invalid "section1" parameter */
4023 edit_free_file(file);
4024 return JB_ERR_CGI_PARAMS;
4027 /* If no-op, we've validated params and can skip the rest. */
4028 if (section1 != section2)
4030 /* ... find the end of section1 ... */
4031 line_before_section1 = prev_line;
4032 line_start_section1 = cur_line;
4035 prev_line = cur_line;
4036 cur_line = cur_line->next;
4039 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL));
4040 line_end_section1 = prev_line;
4041 line_after_section1 = cur_line;
4043 /* ... find section2 ... */
4044 while ((cur_line != NULL) && (line_number < section2))
4046 prev_line = cur_line;
4047 cur_line = cur_line->next;
4051 if ( (cur_line == NULL)
4052 || (cur_line->type != FILE_LINE_ACTION) )
4054 /* Invalid "section2" parameter */
4055 edit_free_file(file);
4056 return JB_ERR_CGI_PARAMS;
4059 /* ... find the end of section2 ... */
4060 line_before_section2 = prev_line;
4061 line_start_section2 = cur_line;
4064 prev_line = cur_line;
4065 cur_line = cur_line->next;
4068 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL));
4069 line_end_section2 = prev_line;
4070 line_after_section2 = cur_line;
4072 /* Now have all the pointers we need. Do the swap. */
4074 /* Change the pointer to section1 to point to section2 instead */
4075 if (line_before_section1 == NULL)
4077 file->lines = line_start_section2;
4081 line_before_section1->next = line_start_section2;
4084 if (line_before_section2 == line_end_section1)
4086 /* Consecutive sections */
4087 line_end_section2->next = line_start_section1;
4091 line_end_section2->next = line_after_section1;
4092 line_before_section2->next = line_start_section1;
4095 /* Set the pointer from the end of section1 to the rest of the file */
4096 line_end_section1->next = line_after_section2;
4098 err = edit_write_file(file);
4101 /* Error writing file */
4102 edit_free_file(file);
4105 } /* END if (section1 != section2) */
4107 target = strdup(CGI_PREFIX "edit-actions-list?f=");
4108 string_append(&target, file->identifier);
4110 edit_free_file(file);
4115 return JB_ERR_MEMORY;
4118 rsp->status = strdup("302 Local Redirect from Privoxy");
4119 if (rsp->status == NULL)
4122 return JB_ERR_MEMORY;
4124 err = enlist_unique_header(rsp->headers, "Location", target);
4131 /*********************************************************************
4133 * Function : cgi_toggle
4135 * Description : CGI function that adds a new empty section to
4139 * 1 : csp = Current client state (buffers, headers, etc...)
4140 * 2 : rsp = http_response data structure for output
4141 * 3 : parameters = map of cgi parameters
4144 * set : If present, how to change toggle setting:
4145 * "enable", "disable", "toggle", or none (default).
4146 * mini : If present, use mini reply template.
4148 * Returns : JB_ERR_OK on success
4149 * JB_ERR_MEMORY on out-of-memory
4151 *********************************************************************/
4152 jb_err cgi_toggle(struct client_state *csp,
4153 struct http_response *rsp,
4154 const struct map *parameters)
4156 struct map *exports;
4158 const char *template_name;
4165 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_TOGGLE))
4167 return cgi_error_disabled(csp, rsp);
4170 mode = get_char_param(parameters, "set");
4177 else if (mode == 'D')
4182 else if (mode == 'T')
4185 g_bToggleIJB = !g_bToggleIJB;
4188 if (NULL == (exports = default_exports(csp, "toggle")))
4190 return JB_ERR_MEMORY;
4193 template_name = (get_char_param(parameters, "mini")
4197 return template_fill_for_cgi(csp, template_name, exports, rsp);
4201 /*********************************************************************
4203 * Function : javascriptify
4205 * Description : Converts a string into a form JavaScript will like.
4207 * Netscape 4's JavaScript sucks - it doesn't use
4208 * "id" parameters, so you have to set the "name"
4209 * used to submit a form element to something JavaScript
4210 * will like. (Or access the elements by index in an
4211 * array. That array contains >60 elements and will
4212 * be changed whenever we add a new action to the
4213 * editor, so I'm NOT going to use indexes that have
4214 * to be figured out by hand.)
4216 * Currently the only thing we have to worry about
4217 * is "-" ==> "_" conversion.
4219 * This is a length-preserving operation so it is
4220 * carried out in-place, no memory is allocated
4224 * 1 : identifier = String to make JavaScript-friendly.
4228 *********************************************************************/
4229 static void javascriptify(char * identifier)
4231 char * p = identifier;
4232 while (NULL != (p = strchr(p, '-')))
4239 /*********************************************************************
4241 * Function : actions_to_radio
4243 * Description : Converts a actionsfile entry into settings for
4244 * radio buttons and edit boxes on a HTML form.
4247 * 1 : exports = List of substitutions to add to.
4248 * 2 : action = Action to read
4250 * Returns : JB_ERR_OK on success
4251 * JB_ERR_MEMORY on out-of-memory
4253 *********************************************************************/
4254 static jb_err actions_to_radio(struct map * exports,
4255 const struct action_spec *action)
4257 unsigned mask = action->mask;
4258 unsigned add = action->add;
4266 mask = action->mask;
4269 /* sanity - prevents "-feature +feature" */
4273 #define DEFINE_ACTION_BOOL(name, bit) \
4274 if (!(mask & bit)) \
4276 current_mode = 'n'; \
4278 else if (add & bit) \
4280 current_mode = 'y'; \
4284 current_mode = 'x'; \
4286 if (map_radio(exports, name, "ynx", current_mode)) \
4288 return JB_ERR_MEMORY; \
4291 #define DEFINE_ACTION_STRING(name, bit, index) \
4292 DEFINE_ACTION_BOOL(name, bit); \
4295 #define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default) \
4298 checked = !strcmp(action->string[index], value); \
4302 checked = is_default; \
4304 mapped_param |= checked; \
4305 if (map(exports, name "-param-" value, 1, (checked ? "checked" : ""), 1)) \
4307 return JB_ERR_MEMORY; \
4310 #define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val) \
4311 if (map(exports, name "-param-custom", 1, \
4312 ((!mapped_param) ? "checked" : ""), 1)) \
4314 return JB_ERR_MEMORY; \
4316 if (map(exports, name "-param", 1, \
4317 (((add & bit) && !mapped_param) ? \
4318 action->string[index] : default_val), 1)) \
4320 return JB_ERR_MEMORY; \
4323 #define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val) \
4324 if (map(exports, name "-param", 1, \
4325 ((add & bit) ? action->string[index] : default_val), 1)) \
4327 return JB_ERR_MEMORY; \
4330 #define DEFINE_ACTION_MULTI(name, index) \
4331 if (action->multi_add[index]->first) \
4333 current_mode = 'y'; \
4335 else if (action->multi_remove_all[index]) \
4337 current_mode = 'n'; \
4339 else if (action->multi_remove[index]->first) \
4341 current_mode = 'y'; \
4345 current_mode = 'x'; \
4347 if (map_radio(exports, name, "ynx", current_mode)) \
4349 return JB_ERR_MEMORY; \
4352 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
4354 #include "actionlist.h"
4356 #undef DEFINE_ACTION_MULTI
4357 #undef DEFINE_ACTION_STRING
4358 #undef DEFINE_ACTION_BOOL
4359 #undef DEFINE_ACTION_ALIAS
4360 #undef DEFINE_CGI_PARAM_CUSTOM
4361 #undef DEFINE_CGI_PARAM_RADIO
4362 #undef DEFINE_CGI_PARAM_NO_RADIO
4368 /*********************************************************************
4370 * Function : actions_from_radio
4372 * Description : Converts a map of parameters passed to a CGI function
4373 * into an actionsfile entry.
4376 * 1 : parameters = parameters to the CGI call
4377 * 2 : action = Action to change. Must be valid before
4378 * the call, actions not specified will be
4381 * Returns : JB_ERR_OK on success
4382 * JB_ERR_MEMORY on out-of-memory
4384 *********************************************************************/
4385 static jb_err actions_from_radio(const struct map * parameters,
4386 struct action_spec *action)
4388 static int first_time = 1;
4392 const char * js_name;
4393 jb_err err = JB_ERR_OK;
4398 /* Statics are generally a potential race condition,
4399 * but in this case we're safe and don't need semaphores.
4400 * Be careful if you modify this function.
4404 #define JAVASCRIPTIFY(dest_var, string) \
4406 static char js_name_arr[] = string; \
4409 javascriptify(js_name_arr); \
4411 dest_var = js_name_arr; \
4414 #define DEFINE_ACTION_BOOL(name, bit) \
4415 JAVASCRIPTIFY(js_name, name); \
4416 ch = get_char_param(parameters, js_name); \
4419 action->add |= bit; \
4420 action->mask |= bit; \
4422 else if (ch == 'N') \
4424 action->add &= ~bit; \
4425 action->mask &= ~bit; \
4427 else if (ch == 'X') \
4429 action->add &= ~bit; \
4430 action->mask |= bit; \
4433 #define DEFINE_ACTION_STRING(name, bit, index) \
4434 JAVASCRIPTIFY(js_name, name); \
4435 ch = get_char_param(parameters, js_name); \
4439 JAVASCRIPTIFY(js_name, name "-mode"); \
4440 if (!err) err = get_string_param(parameters, js_name, ¶m); \
4441 if ((param == NULL) || (0 == strcmp(param, "CUSTOM"))) \
4443 JAVASCRIPTIFY(js_name, name "-param"); \
4444 if (!err) err = get_string_param(parameters, js_name, ¶m); \
4446 if (param != NULL) \
4448 if (NULL == (param_dup = strdup(param))) \
4450 return JB_ERR_MEMORY; \
4452 freez(action->string[index]); \
4453 action->add |= bit; \
4454 action->mask |= bit; \
4455 action->string[index] = param_dup; \
4458 else if (ch == 'N') \
4460 if (action->add & bit) \
4462 freez(action->string[index]); \
4464 action->add &= ~bit; \
4465 action->mask &= ~bit; \
4467 else if (ch == 'X') \
4469 if (action->add & bit) \
4471 freez(action->string[index]); \
4473 action->add &= ~bit; \
4474 action->mask |= bit; \
4477 #define DEFINE_ACTION_MULTI(name, index) \
4478 JAVASCRIPTIFY(js_name, name); \
4479 ch = get_char_param(parameters, js_name); \
4484 else if (ch == 'N') \
4486 list_remove_all(action->multi_add[index]); \
4487 list_remove_all(action->multi_remove[index]); \
4488 action->multi_remove_all[index] = 1; \
4490 else if (ch == 'X') \
4492 list_remove_all(action->multi_add[index]); \
4493 list_remove_all(action->multi_remove[index]); \
4494 action->multi_remove_all[index] = 0; \
4497 #define DEFINE_ACTION_ALIAS 0 /* No aliases for URL parsing */
4499 #include "actionlist.h"
4501 #undef DEFINE_ACTION_MULTI
4502 #undef DEFINE_ACTION_STRING
4503 #undef DEFINE_ACTION_BOOL
4504 #undef DEFINE_ACTION_ALIAS
4505 #undef JAVASCRIPTIFY
4513 #endif /* def FEATURE_CGI_EDIT_ACTIONS */