1 const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.6 2001/10/29 03:48:09 david__schmidt Exp $";
2 /*********************************************************************
4 * File : $Source: /cvsroot/ijbswa/current/cgiedit.c,v $
6 * Purpose : CGI-based actionsfile editor.
8 * Functions declared include:
11 * Copyright : Written by and Copyright (C) 2001 the SourceForge
12 * IJBSWA team. http://ijbswa.sourceforge.net
14 * Based on the Internet Junkbuster originally written
15 * by and Copyright (C) 1997 Anonymous Coders and
16 * Junkbusters Corporation. http://www.junkbusters.com
18 * This program is free software; you can redistribute it
19 * and/or modify it under the terms of the GNU General
20 * Public License as published by the Free Software
21 * Foundation; either version 2 of the License, or (at
22 * your option) any later version.
24 * This program is distributed in the hope that it will
25 * be useful, but WITHOUT ANY WARRANTY; without even the
26 * implied warranty of MERCHANTABILITY or FITNESS FOR A
27 * PARTICULAR PURPOSE. See the GNU General Public
28 * License for more details.
30 * The GNU General Public License should be included with
31 * this file. If not, you can view it at
32 * http://www.gnu.org/copyleft/gpl.html
33 * or write to the Free Software Foundation, Inc., 59
34 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
38 * Revision 1.6 2001/10/29 03:48:09 david__schmidt
39 * OS/2 native needed a snprintf() routine. Added one to miscutil, brackedted
40 * by and __OS2__ ifdef.
42 * Revision 1.5 2001/10/25 03:40:48 david__schmidt
43 * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
44 * threads to call select() simultaneously. So, it's time to do a real, live,
45 * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__
46 * (native). Both versions will work, but using __OS2__ offers multi-threading.
48 * Revision 1.4 2001/10/23 21:48:19 jongfoster
49 * Cleaning up error handling in CGI functions - they now send back
50 * a HTML error page and should never cause a FATAL error. (Fixes one
51 * potential source of "denial of service" attacks).
53 * CGI actions file editor that works and is actually useful.
55 * Ability to toggle JunkBuster remotely using a CGI call.
57 * You can turn off both the above features in the main configuration
58 * file, e.g. if you are running a multi-user proxy.
60 * Revision 1.3 2001/10/14 22:12:49 jongfoster
61 * New version of CGI-based actionsfile editor.
62 * Major changes, including:
63 * - Completely new file parser and file output routines
64 * - edit-actions CGI renamed edit-actions-for-url
65 * - All CGIs now need a filename parameter, except for...
66 * - New CGI edit-actions which doesn't need a filename,
67 * to allow you to start the editor up.
68 * - edit-actions-submit now works, and now automatically
69 * redirects you back to the main edit-actions-list handler.
71 * Revision 1.2 2001/09/16 17:05:14 jongfoster
72 * Removing unused #include showarg.h
74 * Revision 1.1 2001/09/16 15:47:37 jongfoster
75 * First version of CGI-based edit interface. This is very much a
76 * work-in-progress, and you can't actually use it to edit anything
77 * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes
81 **********************************************************************/
87 * FIXME: Following includes copied from cgi.c - which are actually needed?
92 #include <sys/types.h>
100 #define snprintf _snprintf
101 #endif /* def _WIN32 */
106 #include "cgisimple.h"
110 #include "miscutil.h"
113 /* loadcfg.h is for g_bToggleIJB only */
115 const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION;
118 #ifdef FEATURE_CGI_EDIT_ACTIONS
122 struct file_line * next;
130 struct action_spec action[1];
139 /* Add more data types here... e.g.
142 struct url_spec url[1];
146 struct action_spec action[1];
155 #define FILE_LINE_UNPROCESSED 1
156 #define FILE_LINE_BLANK 2
157 #define FILE_LINE_ALIAS_HEADER 3
158 #define FILE_LINE_ALIAS_ENTRY 4
159 #define FILE_LINE_ACTION 5
160 #define FILE_LINE_URL 6
161 #define FILE_LINE_SETTINGS_HEADER 7
162 #define FILE_LINE_SETTINGS_ENTRY 8
163 #define FILE_LINE_DESCRIPTION_HEADER 9
164 #define FILE_LINE_DESCRIPTION_ENTRY 10
169 struct file_line * lines;
170 const char * filename; /* Full pathname - e.g. "/etc/junkbuster/wibble.action" */
171 const char * identifier; /* Filename stub - e.g. "wibble". Use for CGI param. */
172 const char * version_str; /* Last modification time, as a string. For CGI param */
173 unsigned version; /* Last modification time - prevents chaos with
174 * the browser's "back" button. Note that this is a
175 * time_t cast to an unsigned. When comparing, always
176 * cast the time_t to an unsigned, and *NOT* vice-versa.
177 * This may lose the top few bits, but they're not
178 * significant anyway.
180 struct file_line * parse_error; /* On parse error, this is the offending line. */
181 const char * parse_error_text; /* On parse error, this is the problem.
182 * (Statically allocated) */
185 /* FIXME: Following non-static functions should be prototyped in .h or made static */
187 /* Functions to read and write arbitrary config files */
188 jb_err edit_read_file(struct client_state *csp,
189 const struct map *parameters,
192 struct editable_file **pfile);
193 jb_err edit_write_file(struct editable_file * file);
194 void edit_free_file(struct editable_file * file);
196 /* Functions to read and write actions files */
197 jb_err edit_parse_actions_file(struct editable_file * file);
198 jb_err edit_read_actions_file(struct client_state *csp,
199 struct http_response *rsp,
200 const struct map *parameters,
202 struct editable_file **pfile);
205 jb_err cgi_error_modified(struct client_state *csp,
206 struct http_response *rsp,
207 const char *filename);
208 jb_err cgi_error_parse(struct client_state *csp,
209 struct http_response *rsp,
210 struct editable_file *file);
211 jb_err cgi_error_file(struct client_state *csp,
212 struct http_response *rsp,
213 const char *filename);
214 jb_err cgi_error_disabled(struct client_state *csp,
215 struct http_response *rsp);
217 /* Internal arbitrary config file support functions */
218 static jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile);
219 static void edit_free_file_lines(struct file_line * first_line);
220 static jb_err simple_read_line(char **dest, FILE *fp);
221 static jb_err edit_read_line(FILE *fp, char **raw_out, char **prefix_out, char **data_out);
223 /* Internal actions file support functions */
224 static int match_actions_file_header_line(const char * line, const char * name);
225 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue);
227 /* Internal parameter parsing functions */
228 static jb_err get_file_name_param(struct client_state *csp,
229 const struct map *parameters,
230 const char *param_name,
233 const char **pparam);
234 static jb_err get_number_param(struct client_state *csp,
235 const struct map *parameters,
239 /* Internal actionsfile <==> HTML conversion functions */
240 static jb_err map_radio(struct map * exports,
241 const char * optionname,
244 static jb_err actions_to_radio(struct map * exports,
245 const struct action_spec *action);
246 static jb_err actions_from_radio(const struct map * parameters,
247 struct action_spec *action);
250 static jb_err map_copy_parameter_html(struct map *out,
251 const struct map *in,
253 static jb_err map_copy_parameter_url(struct map *out,
254 const struct map *in,
258 /*********************************************************************
260 * Function : map_copy_parameter_html
262 * Description : Copy a CGI parameter from one map to another, HTML
266 * 1 : out = target map
267 * 2 : in = source map
268 * 3 : name = name of cgi parameter to copy
270 * Returns : JB_ERR_OK on success
271 * JB_ERR_MEMORY on out-of-memory
272 * JB_ERR_CGI_PARAMS if the parameter doesn't exist
275 *********************************************************************/
276 static jb_err map_copy_parameter_html(struct map *out,
277 const struct map *in,
287 value = lookup(in, name);
288 err = map(out, name, 1, html_encode(value), 0);
295 else if (*value == '\0')
297 return JB_ERR_CGI_PARAMS;
306 /*********************************************************************
308 * Function : map_copy_parameter_html
310 * Description : Copy a CGI parameter from one map to another, URL
314 * 1 : out = target map
315 * 2 : in = source map
316 * 3 : name = name of cgi parameter to copy
318 * Returns : JB_ERR_OK on success
319 * JB_ERR_MEMORY on out-of-memory
320 * JB_ERR_CGI_PARAMS if the parameter doesn't exist
323 *********************************************************************/
324 static jb_err map_copy_parameter_url(struct map *out,
325 const struct map *in,
335 value = lookup(in, name);
336 err = map(out, name, 1, url_encode(value), 0);
343 else if (*value == '\0')
345 return JB_ERR_CGI_PARAMS;
354 /*********************************************************************
356 * Function : cgi_edit_actions_url_form
358 * Description : CGI function that displays a form for
362 * 1 : csp = Current client state (buffers, headers, etc...)
363 * 2 : rsp = http_response data structure for output
364 * 3 : parameters = map of cgi parameters
367 * filename : Identifies the file to edit
368 * ver : File's last-modified time
369 * section : Line number of section to edit
370 * pattern : Line number of pattern to edit
371 * oldval : Current value for pattern
373 * Returns : JB_ERR_OK on success
374 * JB_ERR_MEMORY on out-of-memory
375 * JB_ERR_CGI_PARAMS if the CGI parameters are not
376 * specified or not valid.
378 *********************************************************************/
379 jb_err cgi_edit_actions_url_form(struct client_state *csp,
380 struct http_response *rsp,
381 const struct map *parameters)
390 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
392 return cgi_error_disabled(csp, rsp);
395 if (NULL == (exports = default_exports(csp, NULL)))
397 return JB_ERR_MEMORY;
400 err = map_copy_parameter_html(exports, parameters, "section");
401 if (!err) err = map_copy_parameter_html(exports, parameters, "pattern");
402 if (!err) err = map_copy_parameter_html(exports, parameters, "ver");
403 if (!err) err = map_copy_parameter_html(exports, parameters, "filename");
404 if (!err) err = map_copy_parameter_html(exports, parameters, "oldval");
412 return template_fill_for_cgi(csp, "edit-actions-url-form", exports, rsp);
416 /*********************************************************************
418 * Function : cgi_edit_actions_add_url_form
420 * Description : CGI function that displays a form for
424 * 1 : csp = Current client state (buffers, headers, etc...)
425 * 2 : rsp = http_response data structure for output
426 * 3 : parameters = map of cgi parameters
429 * filename : Identifies the file to edit
430 * ver : File's last-modified time
431 * section : Line number of section to edit
433 * Returns : JB_ERR_OK on success
434 * JB_ERR_MEMORY on out-of-memory
435 * JB_ERR_CGI_PARAMS if the CGI parameters are not
436 * specified or not valid.
438 *********************************************************************/
439 jb_err cgi_edit_actions_add_url_form(struct client_state *csp,
440 struct http_response *rsp,
441 const struct map *parameters)
450 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
452 return cgi_error_disabled(csp, rsp);
455 if (NULL == (exports = default_exports(csp, NULL)))
457 return JB_ERR_MEMORY;
460 err = map_copy_parameter_html(exports, parameters, "section");
461 if (!err) err = map_copy_parameter_html(exports, parameters, "ver");
462 if (!err) err = map_copy_parameter_html(exports, parameters, "filename");
470 return template_fill_for_cgi(csp, "edit-actions-add-url-form", exports, rsp);
474 /*********************************************************************
476 * Function : cgi_edit_actions_remove_url_form
478 * Description : CGI function that displays a form for
482 * 1 : csp = Current client state (buffers, headers, etc...)
483 * 2 : rsp = http_response data structure for output
484 * 3 : parameters = map of cgi parameters
487 * filename : Identifies the file to edit
488 * ver : File's last-modified time
489 * section : Line number of section to edit
490 * pattern : Line number of pattern to edit
491 * oldval : Current value for pattern
493 * Returns : JB_ERR_OK on success
494 * JB_ERR_MEMORY on out-of-memory
495 * JB_ERR_CGI_PARAMS if the CGI parameters are not
496 * specified or not valid.
498 *********************************************************************/
499 jb_err cgi_edit_actions_remove_url_form(struct client_state *csp,
500 struct http_response *rsp,
501 const struct map *parameters)
510 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
512 return cgi_error_disabled(csp, rsp);
515 if (NULL == (exports = default_exports(csp, NULL)))
517 return JB_ERR_MEMORY;
520 err = map_copy_parameter_url(exports, parameters, "section");
521 if (!err) err = map_copy_parameter_url(exports, parameters, "pattern");
522 if (!err) err = map_copy_parameter_url(exports, parameters, "ver");
523 if (!err) err = map_copy_parameter_url(exports, parameters, "filename");
524 if (!err) err = map_copy_parameter_html(exports, parameters, "oldval");
532 return template_fill_for_cgi(csp, "edit-actions-remove-url-form", exports, rsp);
536 /*********************************************************************
538 * Function : simple_read_line
540 * Description : Read a single line from a file and return it.
541 * This is basically a version of fgets() that malloc()s
542 * it's own line buffer. Note that the buffer will
543 * always be a multiple of BUFFER_SIZE bytes long.
544 * Therefore if you are going to keep the string for
545 * an extended period of time, you should probably
546 * strdup() it and free() the original, to save memory.
550 * 1 : dest = destination for newly malloc'd pointer to
551 * line data. Will be set to NULL on error.
552 * 2 : fp = File to read from
554 * Returns : JB_ERR_OK on success
555 * JB_ERR_MEMORY on out-of-memory
556 * JB_ERR_FILE on EOF.
558 *********************************************************************/
559 static jb_err simple_read_line(char **dest, FILE *fp)
570 if (NULL == (buf = malloc(BUFFER_SIZE)))
572 return JB_ERR_MEMORY;
581 if ((!fgets(newbuf, BUFFER_SIZE, fp)) || (*newbuf == '\0'))
583 /* (*newbuf == '\0') should never happen unless fgets fails */
596 if ((buf[len - 1] == '\n') || (buf[len - 1] == '\r'))
602 if (NULL == (newbuf = realloc(buf, len + BUFFER_SIZE)))
605 return JB_ERR_MEMORY;
612 /*********************************************************************
614 * Function : edit_read_line
616 * Description : Read a single non-empty line from a file and return
617 * it. Trims comments, leading and trailing whitespace
618 * and respects escaping of newline and comment char.
619 * Provides the line in 2 alternative forms: raw and
621 * - raw is the raw data read from the file. If the
622 * line is not modified, then this should be written
624 * - prefix is any comments and blank lines that were
625 * read from the file. If the line is modified, then
626 * this should be written out to the file followed
627 * by the modified data. (If this string is non-empty
628 * then it will have a newline at the end).
629 * - data is the actual data that will be parsed
630 * further by appropriate routines.
631 * On EOF, the 3 strings will all be set to NULL and
632 * 0 will be returned.
635 * 1 : fp = File to read from
636 * 2 : raw_out = destination for newly malloc'd pointer to
637 * raw line data. May be NULL if you don't want it.
638 * 3 : prefix_out = destination for newly malloc'd pointer to
639 * comments. May be NULL if you don't want it.
640 * 4 : data_out = destination for newly malloc'd pointer to
641 * line data with comments and leading/trailing spaces
642 * removed, and line continuation performed. May be
643 * NULL if you don't want it.
645 * Returns : JB_ERR_OK on success
646 * JB_ERR_MEMORY on out-of-memory
647 * JB_ERR_FILE on EOF.
649 *********************************************************************/
650 static jb_err edit_read_line(FILE *fp, char **raw_out, char **prefix_out, char **data_out)
652 char *p; /* Temporary pointer */
653 char *linebuf; /* Line read from file */
654 char *linestart; /* Start of linebuf, usually first non-whitespace char */
655 char newline[3]; /* Used to store the newline - "\n", "\r", or "\r\n" */
656 int contflag = 0; /* Nonzero for line continuation - i.e. line ends '\' */
657 char *raw; /* String to be stored in raw_out */
658 char *prefix; /* String to be stored in prefix_out */
659 char *data; /* String to be stored in data_out */
660 jb_err rval = JB_ERR_OK;
664 /* Set output parameters to NULL */
678 /* Set string variables to new, empty strings. */
684 if ((raw == NULL) || (prefix == NULL) || (data == NULL))
689 return JB_ERR_MEMORY;
696 /* Main loop. Loop while we need more data & it's not EOF. */
698 while ( (contflag || (*data == '\0'))
699 && (JB_ERR_OK == (rval = simple_read_line(&linebuf, fp))))
701 if (string_append(&raw,linebuf))
706 return JB_ERR_MEMORY;
709 /* Trim off newline */
710 p = linebuf + strlen(linebuf);
711 if ((p != linebuf) && ((p[-1] == '\r') || (p[-1] == '\n')))
714 if ((p != linebuf) && ((p[-1] == '\r') || (p[-1] == '\n')))
722 /* Line continuation? Trim escape and set flag. */
723 contflag = ((p != linebuf) && (*--p == '\\'));
729 /* Trim leading spaces if we're at the start of the line */
733 /* Trim leading spaces */
734 while (*linestart && isspace((int)(unsigned char)*linestart))
740 /* Handle comment characters. */
742 while ((p = strchr(p, '#')) != NULL)
744 /* Found a comment char.. */
745 if ((p != linebuf) && (*(p-1) == '\\'))
747 /* ..and it's escaped, left-shift the line over the escape. */
749 while ((*q = *(q + 1)) != '\0')
753 /* Now scan from just after the "#". */
757 /* Real comment. Save it... */
760 /* Special case: Line only contains a comment, so all the
761 * previous whitespace is considered part of the comment.
762 * Undo the whitespace skipping, if any.
767 string_append(&prefix,p);
768 if (string_append(&prefix,newline))
773 return JB_ERR_MEMORY;
777 /* ... and chop off the rest of the line */
780 } /* END while (there's a # character) */
782 /* Write to the buffer */
785 if (string_append(&data, linestart))
790 return JB_ERR_MEMORY;
795 } /* END while(we need more data) */
797 /* Handle simple_read_line() errors - ignore EOF */
798 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
809 /* Got at least some data */
811 /* Remove trailing whitespace */
824 *prefix_out = prefix;
842 /* EOF and no data there. */
853 /*********************************************************************
855 * Function : edit_write_file
857 * Description : Write a complete file to disk.
860 * 1 : filename = File to write to.
861 * 2 : file = Data structure to write.
863 * Returns : JB_ERR_OK on success
864 * JB_ERR_FILE on error writing to file.
865 * JB_ERR_MEMORY on out of memory
867 *********************************************************************/
868 jb_err edit_write_file(struct editable_file * file)
871 struct file_line * cur_line;
872 struct stat statbuf[1];
873 char version_buf[22]; /* 22 = ceil(log10(2^64)) + 2 = max number of
874 digits in time_t, assuming this is a 64-bit
875 machine, plus null terminator, plus one
879 assert(file->filename);
881 if (NULL == (fp = fopen(file->filename, "wt")))
886 cur_line = file->lines;
887 while (cur_line != NULL)
891 if (fputs(cur_line->raw, fp) < 0)
899 if (cur_line->prefix)
901 if (fputs(cur_line->prefix, fp) < 0)
907 if (cur_line->unprocessed)
909 if (fputs(cur_line->unprocessed, fp) < 0)
914 if (fputs("\n", fp) < 0)
922 /* FIXME: Write data from file->data->whatever */
926 cur_line = cur_line->next;
932 /* Update the version stamp in the file structure, since we just
933 * wrote to the file & changed it's date.
935 if (stat(file->filename, statbuf) < 0)
937 /* Error, probably file not found. */
940 file->version = (unsigned)statbuf->st_mtime;
942 /* Correct file->version_str */
943 freez(file->version_str);
944 snprintf(version_buf, 22, "%u", file->version);
945 version_buf[21] = '\0';
946 file->version_str = strdup(version_buf);
947 if (version_buf == NULL)
949 return JB_ERR_MEMORY;
956 /*********************************************************************
958 * Function : edit_free_file
960 * Description : Free a complete file in memory.
963 * 1 : file = Data structure to free.
967 *********************************************************************/
968 void edit_free_file(struct editable_file * file)
972 /* Silently ignore NULL pointer */
976 edit_free_file_lines(file->lines);
977 freez(file->filename);
978 freez(file->identifier);
979 freez(file->version_str);
981 file->parse_error_text = NULL; /* Statically allocated */
982 file->parse_error = NULL;
988 /*********************************************************************
990 * Function : edit_free_file
992 * Description : Free an entire linked list of file lines.
995 * 1 : first_line = Data structure to free.
999 *********************************************************************/
1000 static void edit_free_file_lines(struct file_line * first_line)
1002 struct file_line * next_line;
1004 while (first_line != NULL)
1006 next_line = first_line->next;
1007 first_line->next = NULL;
1008 freez(first_line->raw);
1009 freez(first_line->prefix);
1010 freez(first_line->unprocessed);
1011 switch(first_line->type)
1013 case 0: /* special case if memory zeroed */
1014 case FILE_LINE_UNPROCESSED:
1015 case FILE_LINE_BLANK:
1016 case FILE_LINE_ALIAS_HEADER:
1017 case FILE_LINE_SETTINGS_HEADER:
1018 case FILE_LINE_DESCRIPTION_HEADER:
1019 case FILE_LINE_DESCRIPTION_ENTRY:
1020 case FILE_LINE_ALIAS_ENTRY:
1022 /* No data is stored for these */
1025 case FILE_LINE_ACTION:
1026 free_action(first_line->data.action);
1029 case FILE_LINE_SETTINGS_ENTRY:
1030 freez(first_line->data.setting.name);
1031 freez(first_line->data.setting.svalue);
1034 /* Should never happen */
1038 first_line->type = 0; /* paranoia */
1040 first_line = next_line;
1045 /*********************************************************************
1047 * Function : match_actions_file_header_line
1049 * Description : Match an actions file {{header}} line
1052 * 1 : line - String from file
1053 * 2 : name - Header to match against
1055 * Returns : 0 iff they match.
1057 *********************************************************************/
1058 static int match_actions_file_header_line(const char * line, const char * name)
1066 if ((line[0] != '{') || (line[1] != '{'))
1072 /* Look for optional whitespace */
1073 while ( (*line == ' ') || (*line == '\t') )
1078 /* Look for the specified name (case-insensitive) */
1080 if (0 != strncmpic(line, name, len))
1086 /* Look for optional whitespace */
1087 while ( (*line == ' ') || (*line == '\t') )
1092 /* Look for "}}" and end of string*/
1093 if ((line[0] != '}') || (line[1] != '}') || (line[2] != '\0'))
1103 /*********************************************************************
1105 * Function : match_actions_file_header_line
1107 * Description : Match an actions file {{header}} line
1110 * 1 : line - String from file. Must not start with
1111 * whitespace (else infinite loop!)
1112 * 2 : name - Destination for name
1113 * 2 : name - Destination for value
1115 * Returns : JB_ERR_OK on success
1116 * JB_ERR_MEMORY on out-of-memory
1117 * JB_ERR_PARSE if there's no "=" sign, or if there's
1118 * nothing before the "=" sign (but empty
1119 * values *after* the "=" sign are legal).
1121 *********************************************************************/
1122 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue)
1124 const char * name_end;
1125 const char * value_start;
1131 assert(*line != ' ');
1132 assert(*line != '\t');
1137 value_start = strchr(line, '=');
1138 if ((value_start == NULL) || (value_start == line))
1140 return JB_ERR_PARSE;
1143 name_end = value_start - 1;
1145 /* Eat any whitespace before the '=' */
1146 while ((*name_end == ' ') || (*name_end == '\t'))
1149 * we already know we must have at least 1 non-ws char
1150 * at start of buf - no need to check
1155 name_len = name_end - line + 1; /* Length excluding \0 */
1156 if (NULL == (*pname = (char *) malloc(name_len + 1)))
1158 return JB_ERR_MEMORY;
1160 strncpy(*pname, line, name_len);
1161 (*pname)[name_len] = '\0';
1163 /* Eat any the whitespace after the '=' */
1165 while ((*value_start == ' ') || (*value_start == '\t'))
1170 if (NULL == (*pvalue = strdup(value_start)))
1174 return JB_ERR_MEMORY;
1181 /*********************************************************************
1183 * Function : edit_parse_actions_file
1185 * Description : Parse an actions file in memory.
1187 * Passed linked list must have the "data" member
1188 * zeroed, and must contain valid "next" and
1189 * "unprocessed" fields. The "raw" and "prefix"
1190 * fields are ignored, and "type" is just overwritten.
1192 * Note that on error the file may have been
1196 * 1 : file = Actions file to be parsed in-place.
1198 * Returns : JB_ERR_OK on success
1199 * JB_ERR_MEMORY on out-of-memory
1200 * JB_ERR_PARSE on error
1202 *********************************************************************/
1203 jb_err edit_parse_actions_file(struct editable_file * file)
1205 struct file_line * cur_line;
1207 const char * text; /* Text from a line */
1208 char * name; /* For lines of the form name=value */
1209 char * value; /* For lines of the form name=value */
1210 struct action_alias * alias_list = NULL;
1211 jb_err err = JB_ERR_OK;
1213 /* alias_list contains the aliases defined in this file.
1214 * It might be better to use the "file_line.data" fields
1215 * in the relavent places instead.
1218 cur_line = file->lines;
1220 /* A note about blank line support: Blank lines should only
1221 * ever occur as the last line in the file. This function
1222 * is more forgiving than that - FILE_LINE_BLANK can occur
1226 /* Skip leading blanks. Should only happen if file is
1227 * empty (which is valid, but pointless).
1229 while ( (cur_line != NULL)
1230 && (cur_line->unprocessed[0] == '\0') )
1233 cur_line->type = FILE_LINE_BLANK;
1234 cur_line = cur_line->next;
1237 if ( (cur_line != NULL)
1238 && (cur_line->unprocessed[0] != '{') )
1240 /* File doesn't start with a header */
1241 file->parse_error = cur_line;
1242 file->parse_error_text = "First (non-comment) line of the file must contain a header.";
1243 return JB_ERR_PARSE;
1246 if ( (cur_line != NULL) && (0 ==
1247 match_actions_file_header_line(cur_line->unprocessed, "settings") ) )
1249 cur_line->type = FILE_LINE_SETTINGS_HEADER;
1251 cur_line = cur_line->next;
1252 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1254 if (cur_line->unprocessed[0])
1256 cur_line->type = FILE_LINE_SETTINGS_ENTRY;
1258 err = split_line_on_equals(cur_line->unprocessed,
1259 &cur_line->data.setting.name,
1260 &cur_line->data.setting.svalue);
1261 if (err == JB_ERR_MEMORY)
1265 else if (err != JB_ERR_OK)
1267 /* Line does not contain a name=value pair */
1268 file->parse_error = cur_line;
1269 file->parse_error_text = "Expected a name=value pair on this {{description}} line, but couldn't find one.";
1270 return JB_ERR_PARSE;
1275 cur_line->type = FILE_LINE_BLANK;
1277 cur_line = cur_line->next;
1281 if ( (cur_line != NULL) && (0 ==
1282 match_actions_file_header_line(cur_line->unprocessed, "description") ) )
1284 cur_line->type = FILE_LINE_DESCRIPTION_HEADER;
1286 cur_line = cur_line->next;
1287 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1289 if (cur_line->unprocessed[0])
1291 cur_line->type = FILE_LINE_DESCRIPTION_ENTRY;
1295 cur_line->type = FILE_LINE_BLANK;
1297 cur_line = cur_line->next;
1301 if ( (cur_line != NULL) && (0 ==
1302 match_actions_file_header_line(cur_line->unprocessed, "alias") ) )
1304 cur_line->type = FILE_LINE_ALIAS_HEADER;
1306 cur_line = cur_line->next;
1307 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1309 if (cur_line->unprocessed[0])
1311 /* define an alias */
1312 struct action_alias * new_alias;
1314 cur_line->type = FILE_LINE_ALIAS_ENTRY;
1316 err = split_line_on_equals(cur_line->unprocessed, &name, &value);
1317 if (err == JB_ERR_MEMORY)
1321 else if (err != JB_ERR_OK)
1323 /* Line does not contain a name=value pair */
1324 file->parse_error = cur_line;
1325 file->parse_error_text = "Expected a name=value pair on this {{alias}} line, but couldn't find one.";
1326 return JB_ERR_PARSE;
1329 if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1334 free_alias_list(alias_list);
1335 return JB_ERR_MEMORY;
1338 err = get_actions(value, alias_list, new_alias->action);
1341 /* Invalid action or out of memory */
1345 free_alias_list(alias_list);
1346 if (err == JB_ERR_MEMORY)
1352 /* Line does not contain a name=value pair */
1353 file->parse_error = cur_line;
1354 file->parse_error_text = "This alias does not specify a valid set of actions.";
1355 return JB_ERR_PARSE;
1361 new_alias->name = name;
1364 new_alias->next = alias_list;
1365 alias_list = new_alias;
1369 cur_line->type = FILE_LINE_BLANK;
1371 cur_line = cur_line->next;
1375 /* Header done, process the main part of the file */
1376 while (cur_line != NULL)
1378 /* At this point, (cur_line->unprocessed[0] == '{') */
1379 assert(cur_line->unprocessed[0] == '{');
1380 text = cur_line->unprocessed + 1;
1381 len = strlen(text) - 1;
1382 if (text[len] != '}')
1384 /* No closing } on header */
1385 free_alias_list(alias_list);
1386 file->parse_error = cur_line;
1387 file->parse_error_text = "Headers starting with '{' must have a "
1388 "closing bracket ('}'). Headers starting with two brackets ('{{') "
1389 "must close with two brackets ('}}').";
1390 return JB_ERR_PARSE;
1395 /* An invalid {{ header. */
1396 free_alias_list(alias_list);
1397 file->parse_error = cur_line;
1398 file->parse_error_text = "Unknown or unexpected two-bracket header. "
1399 "Please remember that the system (two-bracket) headers must "
1400 "appear in the order {{settings}}, {{description}}, {{alias}}, "
1401 "and must appear before any actions (one-bracket) headers. "
1402 "Also note that system headers may not be repeated.";
1403 return JB_ERR_PARSE;
1406 while ( (*text == ' ') || (*text == '\t') )
1412 && ( (text[len - 1] == ' ')
1413 || (text[len - 1] == '\t') ) )
1418 cur_line->type = FILE_LINE_ACTION;
1420 /* Remove {} and make copy */
1421 if (NULL == (value = (char *) malloc(len + 1)))
1424 free_alias_list(alias_list);
1425 return JB_ERR_MEMORY;
1427 strncpy(value, text, len);
1431 err = get_actions(value, alias_list, cur_line->data.action);
1434 /* Invalid action or out of memory */
1436 free_alias_list(alias_list);
1437 if (err == JB_ERR_MEMORY)
1443 /* Line does not contain a name=value pair */
1444 file->parse_error = cur_line;
1445 file->parse_error_text = "This header does not specify a valid set of actions.";
1446 return JB_ERR_PARSE;
1450 /* Done with string - it was clobbered anyway */
1453 /* Process next line */
1454 cur_line = cur_line->next;
1456 /* Loop processing URL patterns */
1457 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1459 if (cur_line->unprocessed[0])
1461 /* Could parse URL here, but this isn't currently needed */
1463 cur_line->type = FILE_LINE_URL;
1467 cur_line->type = FILE_LINE_BLANK;
1469 cur_line = cur_line->next;
1471 } /* End main while(cur_line != NULL) loop */
1473 free_alias_list(alias_list);
1479 /*********************************************************************
1481 * Function : edit_read_file_lines
1483 * Description : Read all the lines of a file into memory.
1484 * Handles whitespace, comments and line continuation.
1487 * 1 : fp = File to read from. On return, this will be
1488 * at EOF but it will not have been closed.
1489 * 2 : pfile = Destination for a linked list of file_lines.
1490 * Will be set to NULL on error.
1492 * Returns : JB_ERR_OK on success
1493 * JB_ERR_MEMORY on out-of-memory
1495 *********************************************************************/
1496 jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile)
1498 struct file_line * first_line; /* Keep for return value or to free */
1499 struct file_line * cur_line; /* Current line */
1500 struct file_line * prev_line; /* Entry with prev_line->next = cur_line */
1508 cur_line = first_line = zalloc(sizeof(struct file_line));
1509 if (cur_line == NULL)
1511 return JB_ERR_MEMORY;
1514 cur_line->type = FILE_LINE_UNPROCESSED;
1516 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed);
1519 /* Out of memory or empty file. */
1520 /* Note that empty file is not an error we propogate up */
1522 return ((rval == JB_ERR_FILE) ? JB_ERR_OK : rval);
1527 prev_line = cur_line;
1528 cur_line = prev_line->next = zalloc(sizeof(struct file_line));
1529 if (cur_line == NULL)
1532 edit_free_file_lines(first_line);
1533 return JB_ERR_MEMORY;
1536 cur_line->type = FILE_LINE_UNPROCESSED;
1538 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed);
1539 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
1542 edit_free_file_lines(first_line);
1543 return JB_ERR_MEMORY;
1547 while (rval != JB_ERR_FILE);
1551 /* We allocated one too many - free it */
1552 prev_line->next = NULL;
1555 *pfile = first_line;
1560 /*********************************************************************
1562 * Function : edit_read_file
1564 * Description : Read a complete file into memory.
1565 * Handles CGI parameter parsing. If requested, also
1566 * checks the file's modification timestamp.
1569 * 1 : csp = Current client state (buffers, headers, etc...)
1570 * 2 : parameters = map of cgi parameters.
1571 * 3 : require_version = true to check "ver" parameter.
1572 * 4 : suffix = File extension, e.g. ".action".
1573 * 5 : pfile = Destination for the file. Will be set
1577 * filename : The name of the file to read, without the
1578 * path or ".action" extension.
1579 * ver : (Only if require_version is nonzero)
1580 * Timestamp of the actions file. If wrong, this
1581 * function fails with JB_ERR_MODIFIED.
1583 * Returns : JB_ERR_OK on success
1584 * JB_ERR_MEMORY on out-of-memory
1585 * JB_ERR_CGI_PARAMS if "filename" was not specified
1587 * JB_ERR_FILE if the file cannot be opened or
1589 * JB_ERR_MODIFIED if version checking was requested and
1590 * failed - the file was modified outside
1591 * of this CGI editor instance.
1593 *********************************************************************/
1594 jb_err edit_read_file(struct client_state *csp,
1595 const struct map *parameters,
1596 int require_version,
1598 struct editable_file **pfile)
1600 struct file_line * lines;
1604 const char * identifier;
1605 struct editable_file * file;
1606 unsigned version = 0;
1607 struct stat statbuf[1];
1608 char version_buf[22];
1616 err = get_file_name_param(csp, parameters, "filename", suffix,
1617 &filename, &identifier);
1623 if (stat(filename, statbuf) < 0)
1625 /* Error, probably file not found. */
1629 version = (unsigned) statbuf->st_mtime;
1631 if (require_version)
1633 unsigned specified_version;
1634 err = get_number_param(csp, parameters, "ver", &specified_version);
1641 if (version != specified_version)
1643 return JB_ERR_MODIFIED;
1647 if (NULL == (fp = fopen(filename,"rt")))
1653 err = edit_read_file_lines(fp, &lines);
1663 file = (struct editable_file *) zalloc(sizeof(*file));
1667 edit_free_file_lines(lines);
1671 file->lines = lines;
1672 file->filename = filename;
1673 file->version = version;
1674 file->identifier = strdup(identifier);
1676 if (file->identifier == NULL)
1678 edit_free_file(file);
1679 return JB_ERR_MEMORY;
1682 /* Correct file->version_str */
1683 freez(file->version_str);
1684 snprintf(version_buf, 22, "%u", file->version);
1685 version_buf[21] = '\0';
1686 file->version_str = strdup(version_buf);
1687 if (version_buf == NULL)
1689 edit_free_file(file);
1690 return JB_ERR_MEMORY;
1698 /*********************************************************************
1700 * Function : edit_read_actions_file
1702 * Description : Read a complete actions file into memory.
1703 * Handles CGI parameter parsing. If requested, also
1704 * checks the file's modification timestamp.
1706 * If this function detects an error in the categories
1707 * JB_ERR_FILE, JB_ERR_MODIFIED, or JB_ERR_PARSE,
1708 * then it handles it by filling in the specified
1709 * response structure and returning JB_ERR_FILE.
1712 * 1 : csp = Current client state (buffers, headers, etc...)
1713 * 2 : rsp = HTTP response. Only filled in on error.
1714 * 2 : parameters = map of cgi parameters.
1715 * 3 : require_version = true to check "ver" parameter.
1716 * 4 : pfile = Destination for the file. Will be set
1720 * filename : The name of the actions file to read, without the
1721 * path or ".action" extension.
1722 * ver : (Only if require_version is nonzero)
1723 * Timestamp of the actions file. If wrong, this
1724 * function fails with JB_ERR_MODIFIED.
1726 * Returns : JB_ERR_OK on success
1727 * JB_ERR_MEMORY on out-of-memory
1728 * JB_ERR_CGI_PARAMS if "filename" was not specified
1730 * JB_ERR_FILE if the file does not contain valid data,
1731 * or if file cannot be opened or
1732 * contains no data, or if version
1733 * checking was requested and failed.
1735 *********************************************************************/
1736 jb_err edit_read_actions_file(struct client_state *csp,
1737 struct http_response *rsp,
1738 const struct map *parameters,
1739 int require_version,
1740 struct editable_file **pfile)
1743 struct editable_file *file;
1751 err = edit_read_file(csp, parameters, require_version, ".action", &file);
1754 /* Try to handle if possible */
1755 if (err == JB_ERR_FILE)
1757 err = cgi_error_file(csp, rsp, lookup(parameters, "filename"));
1759 else if (err == JB_ERR_MODIFIED)
1761 err = cgi_error_modified(csp, rsp, lookup(parameters, "filename"));
1763 if (err == JB_ERR_OK)
1766 * Signal to higher-level CGI code that there was a problem but we
1767 * handled it, they should just return JB_ERR_OK.
1774 err = edit_parse_actions_file(file);
1777 if (err == JB_ERR_PARSE)
1779 err = cgi_error_parse(csp, rsp, file);
1780 if (err == JB_ERR_OK)
1783 * Signal to higher-level CGI code that there was a problem but we
1784 * handled it, they should just return JB_ERR_OK.
1789 edit_free_file(file);
1798 /*********************************************************************
1800 * Function : get_file_name_param
1802 * Description : Get the name of the file to edit from the parameters
1803 * passed to a CGI function. This function handles
1804 * security checks such as blocking urls containing
1805 * "/" or ".", prepending the config file directory,
1806 * and adding the specified suffix.
1808 * (This is an essential security check, otherwise
1809 * users may be able to pass "../../../etc/passwd"
1810 * and overwrite the password file [linux], "prn:"
1811 * and print random data [Windows], etc...)
1813 * This function only allows filenames contining the
1814 * characters '-', '_', 'A'-'Z', 'a'-'z', and '0'-'9'.
1815 * That's probably too restrictive but at least it's
1819 * 1 : csp = Current client state (buffers, headers, etc...)
1820 * 2 : parameters = map of cgi parameters
1821 * 3 : suffix = File extension, e.g. ".actions"
1822 * 4 : pfilename = destination for full filename. Caller
1823 * free()s. Set to NULL on error.
1824 * 5 : pparam = destination for partial filename,
1825 * suitable for use in another URL. Allocated as part
1826 * of the map "parameters", so don't free it.
1827 * Set to NULL if not specified.
1829 * Returns : JB_ERR_OK on success
1830 * JB_ERR_MEMORY on out-of-memory
1831 * JB_ERR_CGI_PARAMS if "filename" was not specified
1834 *********************************************************************/
1835 static jb_err get_file_name_param(struct client_state *csp,
1836 const struct map *parameters,
1837 const char *param_name,
1840 const char **pparam)
1858 param = lookup(parameters, param_name);
1861 return JB_ERR_CGI_PARAMS;
1866 len = strlen(param);
1867 if (len >= FILENAME_MAX)
1870 return JB_ERR_CGI_PARAMS;
1873 /* Check every character to see if it's legal */
1875 while ((ch = *s++) != '\0')
1877 if ( ((ch < 'A') || (ch > 'Z'))
1878 && ((ch < 'a') || (ch > 'z'))
1879 && ((ch < '0') || (ch > '9'))
1883 /* Probable hack attempt. */
1884 return JB_ERR_CGI_PARAMS;
1888 /* Append extension */
1889 name = malloc(len + strlen(suffix) + 1);
1892 return JB_ERR_MEMORY;
1894 strcpy(name, param);
1895 strcpy(name + len, suffix);
1898 fullpath = make_path(csp->config->confdir, name);
1900 if (fullpath == NULL)
1902 return JB_ERR_MEMORY;
1906 *pfilename = fullpath;
1912 /*********************************************************************
1914 * Function : get_number_param
1916 * Description : Get a non-negative integer from the parameters
1917 * passed to a CGI function.
1920 * 1 : csp = Current client state (buffers, headers, etc...)
1921 * 2 : parameters = map of cgi parameters
1922 * 3 : name = Name of CGI parameter to read
1923 * 4 : pvalue = destination for value.
1924 * Set to -1 on error.
1926 * Returns : JB_ERR_OK on success
1927 * JB_ERR_MEMORY on out-of-memory
1928 * JB_ERR_CGI_PARAMS if the parameter was not specified
1931 *********************************************************************/
1932 static jb_err get_number_param(struct client_state *csp,
1933 const struct map *parameters,
1948 param = lookup(parameters, name);
1951 return JB_ERR_CGI_PARAMS;
1954 /* We don't use atoi because I want to check this carefully... */
1957 while ((ch = *param++) != '\0')
1959 if ((ch < '0') || (ch > '9'))
1961 return JB_ERR_CGI_PARAMS;
1968 * <limits.h> defines UINT_MAX
1970 * (UINT_MAX - ch) / 10 is the largest number that
1971 * can be safely multiplied by 10 then have ch added.
1973 if (value > ((UINT_MAX - (unsigned)ch) / 10U))
1975 return JB_ERR_CGI_PARAMS;
1978 value = value * 10 + ch;
1988 /*********************************************************************
1990 * Function : map_radio
1992 * Description : Map a set of radio button values. E.g. if you have
1993 * 3 radio buttons, declare them as:
1994 * <option type="radio" name="xyz" @xyz-a@>
1995 * <option type="radio" name="xyz" @xyz-b@>
1996 * <option type="radio" name="xyz" @xyz-c@>
1997 * Then map one of the @xyz-?@ variables to "checked"
1998 * and all the others to empty by calling:
1999 * map_radio(exports, "xyz", "abc", sel)
2000 * Where 'sel' is 'a', 'b', or 'c'.
2003 * 1 : exports = Exports map to modify.
2004 * 2 : optionname = name for map
2005 * 3 : values = null-terminated list of values;
2006 * 4 : value = Selected value.
2008 * CGI Parameters : None
2010 * Returns : JB_ERR_OK on success
2011 * JB_ERR_MEMORY on out-of-memory
2013 *********************************************************************/
2014 static jb_err map_radio(struct map * exports,
2015 const char * optionname,
2016 const char * values,
2028 len = strlen(optionname);
2029 buf = malloc(len + 3);
2032 return JB_ERR_MEMORY;
2035 strcpy(buf, optionname);
2040 while ((c = *values++) != '\0')
2045 if (map(exports, buf, 1, "", 1))
2048 return JB_ERR_MEMORY;
2054 if (map(exports, buf, 0, "checked", 1))
2057 return JB_ERR_MEMORY;
2064 /*********************************************************************
2066 * Function : actions_to_radio
2068 * Description : Converts a actionsfile entry into settings for
2069 * radio buttons and edit boxes on a HTML form.
2072 * 1 : exports = List of substitutions to add to.
2073 * 2 : action = Action to read
2075 * Returns : JB_ERR_OK on success
2076 * JB_ERR_MEMORY on out-of-memory
2078 *********************************************************************/
2079 static jb_err actions_to_radio(struct map * exports,
2080 const struct action_spec *action)
2082 unsigned mask = action->mask;
2083 unsigned add = action->add;
2091 mask = action->mask;
2094 /* sanity - prevents "-feature +feature" */
2098 #define DEFINE_ACTION_BOOL(name, bit) \
2099 if (!(mask & bit)) \
2101 current_mode = 'n'; \
2103 else if (add & bit) \
2105 current_mode = 'y'; \
2109 current_mode = 'x'; \
2111 if (map_radio(exports, name, "ynx", current_mode)) \
2113 return JB_ERR_MEMORY; \
2116 #define DEFINE_ACTION_STRING(name, bit, index) \
2117 DEFINE_ACTION_BOOL(name, bit); \
2120 #define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default) \
2123 checked = !strcmp(action->string[index], value); \
2127 checked = is_default; \
2129 mapped_param |= checked; \
2130 if (map(exports, name "-param-" value, 1, (checked ? "checked" : ""), 1)) \
2132 return JB_ERR_MEMORY; \
2135 #define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val) \
2136 if (map(exports, name "-param-custom", 1, \
2137 ((!mapped_param) ? "checked" : ""), 1)) \
2139 return JB_ERR_MEMORY; \
2141 if (map(exports, name "-param", 1, \
2142 (((add & bit) && !mapped_param) ? \
2143 action->string[index] : default_val), 1)) \
2145 return JB_ERR_MEMORY; \
2148 #define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val) \
2149 if (map(exports, name "-param", 1, \
2150 ((add & bit) ? action->string[index] : default_val), 1)) \
2152 return JB_ERR_MEMORY; \
2155 #define DEFINE_ACTION_MULTI(name, index) \
2156 if (action->multi_add[index]->first) \
2158 current_mode = 'y'; \
2160 else if (action->multi_remove_all[index]) \
2162 current_mode = 'n'; \
2164 else if (action->multi_remove[index]->first) \
2166 current_mode = 'y'; \
2170 current_mode = 'x'; \
2172 if (map_radio(exports, name, "ynx", current_mode)) \
2174 return JB_ERR_MEMORY; \
2177 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
2179 #include "actionlist.h"
2181 #undef DEFINE_ACTION_MULTI
2182 #undef DEFINE_ACTION_STRING
2183 #undef DEFINE_ACTION_BOOL
2184 #undef DEFINE_ACTION_ALIAS
2185 #undef DEFINE_CGI_PARAM_CUSTOM
2186 #undef DEFINE_CGI_PARAM_RADIO
2187 #undef DEFINE_CGI_PARAM_NO_RADIO
2193 /*********************************************************************
2195 * Function : javascriptify
2197 * Description : Converts a string into a form JavaScript will like.
2199 * Netscape 4's JavaScript sucks - it doesn't use
2200 * "id" parameters, so you have to set the "name"
2201 * used to submit a form element to something JavaScript
2202 * will like. (Or access the elements by index in an
2203 * array. That array contains >60 elements and will
2204 * be changed whenever we add a new action to the
2205 * editor, so I'm NOT going to use indexes that have
2206 * to be figured out by hand.)
2208 * Currently the only thing we have to worry about
2209 * is "-" ==> "_" conversion.
2211 * This is a length-preserving operation so it is
2212 * carried out in-place, no memory is allocated
2216 * 1 : identifier = String to make JavaScript-friendly.
2220 *********************************************************************/
2221 static void javascriptify(char * identifier)
2223 char * p = identifier;
2224 while (NULL != (p = strchr(p, '-')))
2231 /*********************************************************************
2233 * Function : actions_from_radio
2235 * Description : Converts a map of parameters passed to a CGI function
2236 * into an actionsfile entry.
2239 * 1 : parameters = parameters to the CGI call
2240 * 2 : action = Action to change. Must be valid before
2241 * the call, actions not specified will be
2244 * Returns : JB_ERR_OK on success
2245 * JB_ERR_MEMORY on out-of-memory
2247 *********************************************************************/
2248 static jb_err actions_from_radio(const struct map * parameters,
2249 struct action_spec *action)
2251 static int first_time = 1;
2255 const char * js_name;
2260 /* Statics are generally a potential race condition,
2261 * but in this case we're safe and don't need semaphores.
2262 * Be careful if you modify this function.
2266 #define JAVASCRIPTIFY(dest_var, string) \
2268 static char js_name_arr[] = string; \
2271 javascriptify(js_name_arr); \
2273 dest_var = js_name_arr; \
2276 #define DEFINE_ACTION_BOOL(name, bit) \
2277 JAVASCRIPTIFY(js_name, name); \
2278 param = lookup(parameters, js_name); \
2279 ch = ijb_toupper(param[0]); \
2282 action->add |= bit; \
2283 action->mask |= bit; \
2285 else if (ch == 'N') \
2287 action->add &= ~bit; \
2288 action->mask &= ~bit; \
2290 else if (ch == 'X') \
2292 action->add &= ~bit; \
2293 action->mask |= bit; \
2296 #define DEFINE_ACTION_STRING(name, bit, index) \
2297 JAVASCRIPTIFY(js_name, name); \
2298 param = lookup(parameters, js_name); \
2299 ch = ijb_toupper(param[0]); \
2302 JAVASCRIPTIFY(js_name, name "-mode"); \
2303 param = lookup(parameters, js_name); \
2304 if ((*param == '\0') || (0 == strcmp(param, "CUSTOM"))) \
2306 JAVASCRIPTIFY(js_name, name "-param"); \
2307 param = lookup(parameters, js_name); \
2309 if (*param != '\0') \
2311 if (NULL == (param_dup = strdup(param))) \
2313 return JB_ERR_MEMORY; \
2315 freez(action->string[index]); \
2316 action->add |= bit; \
2317 action->mask |= bit; \
2318 action->string[index] = param_dup; \
2321 else if (ch == 'N') \
2323 if (action->add & bit) \
2325 freez(action->string[index]); \
2327 action->add &= ~bit; \
2328 action->mask &= ~bit; \
2330 else if (ch == 'X') \
2332 if (action->add & bit) \
2334 freez(action->string[index]); \
2336 action->add &= ~bit; \
2337 action->mask |= bit; \
2340 #define DEFINE_ACTION_MULTI(name, index) \
2341 JAVASCRIPTIFY(js_name, name); \
2342 param = lookup(parameters, js_name); \
2343 ch = ijb_toupper((int)param[0]); \
2348 else if (ch == 'N') \
2350 list_remove_all(action->multi_add[index]); \
2351 list_remove_all(action->multi_remove[index]); \
2352 action->multi_remove_all[index] = 1; \
2354 else if (ch == 'X') \
2356 list_remove_all(action->multi_add[index]); \
2357 list_remove_all(action->multi_remove[index]); \
2358 action->multi_remove_all[index] = 0; \
2361 #define DEFINE_ACTION_ALIAS 0 /* No aliases for URL parsing */
2363 #include "actionlist.h"
2365 #undef DEFINE_ACTION_MULTI
2366 #undef DEFINE_ACTION_STRING
2367 #undef DEFINE_ACTION_BOOL
2368 #undef DEFINE_ACTION_ALIAS
2369 #undef JAVASCRIPTIFY
2377 /*********************************************************************
2379 * Function : cgi_error_modified
2381 * Description : CGI function that is called when a file is modified
2382 * outside the CGI editor.
2385 * 1 : csp = Current client state (buffers, headers, etc...)
2386 * 2 : rsp = http_response data structure for output
2387 * 3 : filename = The file that was modified.
2389 * CGI Parameters : none
2391 * Returns : JB_ERR_OK on success
2392 * JB_ERR_MEMORY on out-of-memory error.
2394 *********************************************************************/
2395 jb_err cgi_error_modified(struct client_state *csp,
2396 struct http_response *rsp,
2397 const char *filename)
2399 struct map *exports;
2406 if (NULL == (exports = default_exports(csp, NULL)))
2408 return JB_ERR_MEMORY;
2411 err = map(exports, "filename", 1, filename, 1);
2418 return template_fill_for_cgi(csp, "cgi-error-modified", exports, rsp);
2422 /*********************************************************************
2424 * Function : cgi_error_parse
2426 * Description : CGI function that is called when a file cannot
2427 * be parsed by the CGI editor.
2430 * 1 : csp = Current client state (buffers, headers, etc...)
2431 * 2 : rsp = http_response data structure for output
2432 * 3 : file = The file that was modified.
2434 * CGI Parameters : none
2436 * Returns : JB_ERR_OK on success
2437 * JB_ERR_MEMORY on out-of-memory error.
2439 *********************************************************************/
2440 jb_err cgi_error_parse(struct client_state *csp,
2441 struct http_response *rsp,
2442 struct editable_file *file)
2444 struct map *exports;
2446 struct file_line *cur_line;
2452 if (NULL == (exports = default_exports(csp, NULL)))
2454 return JB_ERR_MEMORY;
2457 err = map(exports, "filename", 1, file->identifier, 1);
2458 if (!err) err = map(exports, "parse-error", 1, file->parse_error_text, 1);
2460 cur_line = file->parse_error;
2463 if (!err) err = map(exports, "line-raw", 1, html_encode(cur_line->raw), 0);
2464 if (!err) err = map(exports, "line-data", 1, html_encode(cur_line->unprocessed), 0);
2472 return template_fill_for_cgi(csp, "cgi-error-parse", exports, rsp);
2476 /*********************************************************************
2478 * Function : cgi_error_file
2480 * Description : CGI function that is called when a file cannot be
2481 * opened by the CGI editor.
2484 * 1 : csp = Current client state (buffers, headers, etc...)
2485 * 2 : rsp = http_response data structure for output
2486 * 3 : filename = The file that was modified.
2488 * CGI Parameters : none
2490 * Returns : JB_ERR_OK on success
2491 * JB_ERR_MEMORY on out-of-memory error.
2493 *********************************************************************/
2494 jb_err cgi_error_file(struct client_state *csp,
2495 struct http_response *rsp,
2496 const char *filename)
2498 struct map *exports;
2505 if (NULL == (exports = default_exports(csp, NULL)))
2507 return JB_ERR_MEMORY;
2510 err = map(exports, "filename", 1, filename, 1);
2517 return template_fill_for_cgi(csp, "cgi-error-file", exports, rsp);
2521 /*********************************************************************
2523 * Function : cgi_error_bad_param
2525 * Description : CGI function that is called if the parameters
2526 * (query string) for a CGI were wrong.
2529 * 1 : csp = Current client state (buffers, headers, etc...)
2530 * 2 : rsp = http_response data structure for output
2532 * CGI Parameters : none
2534 * Returns : JB_ERR_OK on success
2535 * JB_ERR_MEMORY on out-of-memory error.
2537 *********************************************************************/
2538 jb_err cgi_error_disabled(struct client_state *csp,
2539 struct http_response *rsp)
2541 struct map *exports;
2546 if (NULL == (exports = default_exports(csp, NULL)))
2548 return JB_ERR_MEMORY;
2551 return template_fill_for_cgi(csp, "cgi-error-disabled", exports, rsp);
2555 /*********************************************************************
2557 * Function : cgi_edit_actions
2559 * Description : CGI function that allows the user to choose which
2560 * actions file to edit.
2563 * 1 : csp = Current client state (buffers, headers, etc...)
2564 * 2 : rsp = http_response data structure for output
2565 * 3 : parameters = map of cgi parameters
2567 * CGI Parameters : None
2569 * Returns : JB_ERR_OK on success
2570 * JB_ERR_MEMORY on out-of-memory error
2572 *********************************************************************/
2573 jb_err cgi_edit_actions(struct client_state *csp,
2574 struct http_response *rsp,
2575 const struct map *parameters)
2578 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2580 return cgi_error_disabled(csp, rsp);
2583 /* FIXME: Incomplete */
2584 rsp->status = strdup("302 Local Redirect from Junkbuster");
2585 if (rsp->status == NULL)
2587 return JB_ERR_MEMORY;
2589 if (enlist_unique_header(rsp->headers, "Location", "http://ijbswa.sourceforge.net/config/edit-actions-list?filename=edit"))
2593 return JB_ERR_MEMORY;
2600 /*********************************************************************
2602 * Function : cgi_edit_actions_list
2604 * Description : CGI function that edits the actions list.
2605 * FIXME: This function shouldn't FATAL ever.
2606 * FIXME: This function doesn't check the retval of map()
2608 * 1 : csp = Current client state (buffers, headers, etc...)
2609 * 2 : rsp = http_response data structure for output
2610 * 3 : parameters = map of cgi parameters
2612 * CGI Parameters : filename
2614 * Returns : JB_ERR_OK on success
2615 * JB_ERR_MEMORY on out-of-memory
2616 * JB_ERR_FILE if the file cannot be opened or
2618 * JB_ERR_CGI_PARAMS if "filename" was not specified
2621 *********************************************************************/
2622 jb_err cgi_edit_actions_list(struct client_state *csp,
2623 struct http_response *rsp,
2624 const struct map *parameters)
2626 char * section_template;
2627 char * url_template;
2632 struct map * exports;
2633 struct map * section_exports;
2634 struct map * url_exports;
2635 struct editable_file * file;
2636 struct file_line * cur_line;
2637 unsigned line_number = 0;
2641 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2643 return cgi_error_disabled(csp, rsp);
2646 err = edit_read_actions_file(csp, rsp, parameters, 0, &file);
2649 /* No filename specified, can't read file, or out of memory. */
2650 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2653 if (NULL == (exports = default_exports(csp, NULL)))
2655 edit_free_file(file);
2656 return JB_ERR_MEMORY;
2659 err = map(exports, "filename", 1, file->identifier, 1);
2660 if (!err) err = map(exports, "ver", 1, file->version_str, 1);
2664 edit_free_file(file);
2669 /* Should do all global exports above this point */
2671 err = template_load(csp, §ion_template, "edit-actions-list-section");
2674 edit_free_file(file);
2676 if (err == JB_ERR_FILE)
2678 return cgi_error_no_template(csp, rsp, "edit-actions-list-section");
2683 err = template_load(csp, &url_template, "edit-actions-list-url");
2686 free(section_template);
2687 edit_free_file(file);
2689 if (err == JB_ERR_FILE)
2691 return cgi_error_no_template(csp, rsp, "edit-actions-list-url");
2696 err = template_fill(§ion_template, exports);
2700 edit_free_file(file);
2706 err = template_fill(&url_template, exports);
2709 free(section_template);
2710 edit_free_file(file);
2715 /* Find start of actions in file */
2716 cur_line = file->lines;
2718 while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION))
2720 cur_line = cur_line->next;
2724 if (NULL == (sections = strdup("")))
2726 free(section_template);
2728 edit_free_file(file);
2730 return JB_ERR_MEMORY;
2733 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_ACTION))
2735 if (NULL == (section_exports = new_map()))
2738 free(section_template);
2740 edit_free_file(file);
2742 return JB_ERR_MEMORY;
2745 snprintf(buf, 50, "%d", line_number);
2746 err = map(section_exports, "sectionid", 1, buf, 1);
2747 if (!err) err = map(section_exports, "actions", 1,
2748 actions_to_html(cur_line->data.action), 0);
2751 && (cur_line->next != NULL)
2752 && (cur_line->next->type == FILE_LINE_URL))
2754 /* This section contains at least one URL, don't allow delete */
2755 err = map_block_killer(section_exports, "empty-section");
2761 free(section_template);
2763 edit_free_file(file);
2765 free_map(section_exports);
2769 /* Should do all section-specific exports above this point */
2771 if (NULL == (urls = strdup("")))
2774 free(section_template);
2776 edit_free_file(file);
2778 free_map(section_exports);
2779 return JB_ERR_MEMORY;
2784 cur_line = cur_line->next;
2787 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL))
2789 if (NULL == (url_exports = new_map()))
2793 free(section_template);
2795 edit_free_file(file);
2797 free_map(section_exports);
2798 return JB_ERR_MEMORY;
2801 snprintf(buf, 50, "%d", line_number);
2802 err = map(url_exports, "urlid", 1, buf, 1);
2804 snprintf(buf, 50, "%d", url_1_2);
2805 if (!err) err = map(url_exports, "url-1-2", 1, buf, 1);
2807 if (!err) err = map(url_exports, "url-html", 1,
2808 html_encode(cur_line->unprocessed), 0);
2809 if (!err) err = map(url_exports, "url", 1,
2810 url_encode(cur_line->unprocessed), 0);
2816 free(section_template);
2818 edit_free_file(file);
2820 free_map(section_exports);
2821 free_map(url_exports);
2825 if (NULL == (s = strdup(url_template)))
2829 free(section_template);
2831 edit_free_file(file);
2833 free_map(section_exports);
2834 free_map(url_exports);
2835 return JB_ERR_MEMORY;
2838 err = template_fill(&s, section_exports);
2839 if (!err) err = template_fill(&s, url_exports);
2840 if (!err) err = string_append(&urls, s);
2842 free_map(url_exports);
2849 free(section_template);
2851 edit_free_file(file);
2853 free_map(section_exports);
2857 url_1_2 = 3 - url_1_2;
2859 cur_line = cur_line->next;
2863 err = map(section_exports, "urls", 1, urls, 0);
2868 free(section_template);
2870 edit_free_file(file);
2872 free_map(section_exports);
2876 /* Could also do section-specific exports here, but it wouldn't be as fast */
2878 if (NULL == (s = strdup(section_template)))
2881 free(section_template);
2883 edit_free_file(file);
2885 free_map(section_exports);
2886 return JB_ERR_MEMORY;
2889 err = template_fill(&s, section_exports);
2890 if (!err) err = string_append(§ions, s);
2893 free_map(section_exports);
2898 free(section_template);
2900 edit_free_file(file);
2906 edit_free_file(file);
2907 free(section_template);
2910 err = map(exports, "sections", 1, sections, 0);
2917 /* Could also do global exports here, but it wouldn't be as fast */
2919 return template_fill_for_cgi(csp, "edit-actions-list", exports, rsp);
2923 /*********************************************************************
2925 * Function : cgi_edit_actions
2927 * Description : CGI function that edits the Actions list.
2930 * 1 : csp = Current client state (buffers, headers, etc...)
2931 * 2 : rsp = http_response data structure for output
2932 * 3 : parameters = map of cgi parameters
2934 * CGI Parameters : None
2936 * Returns : JB_ERR_OK on success
2937 * JB_ERR_MEMORY on out-of-memory
2938 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2939 * specified or not valid.
2941 *********************************************************************/
2942 jb_err cgi_edit_actions_for_url(struct client_state *csp,
2943 struct http_response *rsp,
2944 const struct map *parameters)
2946 struct map * exports;
2948 struct editable_file * file;
2949 struct file_line * cur_line;
2950 unsigned line_number;
2953 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2955 return cgi_error_disabled(csp, rsp);
2958 err = get_number_param(csp, parameters, "section", §ionid);
2964 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2967 /* No filename specified, can't read file, modified, or out of memory. */
2968 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2971 cur_line = file->lines;
2973 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
2975 cur_line = cur_line->next;
2978 if ( (cur_line == NULL)
2979 || (line_number != sectionid)
2981 || (cur_line->type != FILE_LINE_ACTION))
2983 /* Invalid "sectionid" parameter */
2984 edit_free_file(file);
2985 return JB_ERR_CGI_PARAMS;
2988 if (NULL == (exports = default_exports(csp, NULL)))
2990 edit_free_file(file);
2991 return JB_ERR_MEMORY;
2994 err = map(exports, "filename", 1, file->identifier, 1);
2995 if (!err) err = map(exports, "ver", 1, file->version_str, 1);
2996 if (!err) err = map(exports, "section", 1, lookup(parameters, "section"), 1);
2998 if (!err) err = actions_to_radio(exports, cur_line->data.action);
3000 edit_free_file(file);
3008 return template_fill_for_cgi(csp, "edit-actions-for-url", exports, rsp);
3012 /*********************************************************************
3014 * Function : cgi_edit_actions_submit
3016 * Description : CGI function that actually edits the Actions list.
3019 * 1 : csp = Current client state (buffers, headers, etc...)
3020 * 2 : rsp = http_response data structure for output
3021 * 3 : parameters = map of cgi parameters
3023 * CGI Parameters : None
3025 * Returns : JB_ERR_OK on success
3026 * JB_ERR_MEMORY on out-of-memory
3027 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3028 * specified or not valid.
3030 *********************************************************************/
3031 jb_err cgi_edit_actions_submit(struct client_state *csp,
3032 struct http_response *rsp,
3033 const struct map *parameters)
3039 struct editable_file * file;
3040 struct file_line * cur_line;
3041 unsigned line_number;
3045 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3047 return cgi_error_disabled(csp, rsp);
3050 err = get_number_param(csp, parameters, "section", §ionid);
3056 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3059 /* No filename specified, can't read file, modified, or out of memory. */
3060 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3063 cur_line = file->lines;
3065 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
3067 cur_line = cur_line->next;
3070 if ( (cur_line == NULL)
3071 || (line_number != sectionid)
3073 || (cur_line->type != FILE_LINE_ACTION))
3075 /* Invalid "sectionid" parameter */
3076 edit_free_file(file);
3077 return JB_ERR_CGI_PARAMS;
3080 err = actions_from_radio(parameters, cur_line->data.action);
3084 edit_free_file(file);
3088 if (NULL == (actiontext = actions_to_text(cur_line->data.action)))
3091 edit_free_file(file);
3092 return JB_ERR_MEMORY;
3095 len = strlen(actiontext);
3099 * Empty action - must special-case this.
3100 * Simply setting len to 1 is sufficient...
3105 if (NULL == (newtext = malloc(len + 2)))
3109 edit_free_file(file);
3110 return JB_ERR_MEMORY;
3112 strcpy(newtext, actiontext);
3116 newtext[len + 1] = '\0';
3118 freez(cur_line->raw);
3119 freez(cur_line->unprocessed);
3120 cur_line->unprocessed = newtext;
3122 err = edit_write_file(file);
3125 /* Error writing file */
3126 edit_free_file(file);
3130 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3131 string_append(&target, file->identifier);
3133 edit_free_file(file);
3138 return JB_ERR_MEMORY;
3141 rsp->status = strdup("302 Local Redirect from Junkbuster");
3142 if (rsp->status == NULL)
3145 return JB_ERR_MEMORY;
3147 err = enlist_unique_header(rsp->headers, "Location", target);
3154 /*********************************************************************
3156 * Function : cgi_edit_actions_url
3158 * Description : CGI function that actually edits a URL pattern in
3162 * 1 : csp = Current client state (buffers, headers, etc...)
3163 * 2 : rsp = http_response data structure for output
3164 * 3 : parameters = map of cgi parameters
3167 * filename : Identifies the file to edit
3168 * ver : File's last-modified time
3169 * section : Line number of section to edit
3170 * pattern : Line number of pattern to edit
3171 * newval : New value for pattern
3173 * Returns : JB_ERR_OK on success
3174 * JB_ERR_MEMORY on out-of-memory
3175 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3176 * specified or not valid.
3178 *********************************************************************/
3179 jb_err cgi_edit_actions_url(struct client_state *csp,
3180 struct http_response *rsp,
3181 const struct map *parameters)
3185 const char * newval;
3187 struct editable_file * file;
3188 struct file_line * cur_line;
3189 unsigned line_number;
3193 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3195 return cgi_error_disabled(csp, rsp);
3198 err = get_number_param(csp, parameters, "section", §ionid);
3204 err = get_number_param(csp, parameters, "pattern", &patternid);
3210 newval = lookup(parameters, "newval");
3212 if ((*newval == '\0') || (sectionid < 1U) || (patternid < 1U))
3214 return JB_ERR_CGI_PARAMS;
3217 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3220 /* No filename specified, can't read file, modified, or out of memory. */
3221 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3225 cur_line = file->lines;
3227 while ((cur_line != NULL) && (line_number < sectionid))
3229 cur_line = cur_line->next;
3233 if ( (cur_line == NULL)
3234 || (cur_line->type != FILE_LINE_ACTION))
3236 /* Invalid "sectionid" parameter */
3237 edit_free_file(file);
3238 return JB_ERR_CGI_PARAMS;
3241 while (line_number < patternid)
3243 cur_line = cur_line->next;
3246 if ( (cur_line == NULL)
3247 || ( (cur_line->type != FILE_LINE_URL)
3248 && (cur_line->type != FILE_LINE_BLANK) ) )
3250 /* Invalid "patternid" parameter */
3251 edit_free_file(file);
3252 return JB_ERR_CGI_PARAMS;
3256 if (cur_line->type != FILE_LINE_URL)
3258 /* Invalid "patternid" parameter */
3259 edit_free_file(file);
3260 return JB_ERR_CGI_PARAMS;
3263 /* At this point, the line to edit is in cur_line */
3265 new_pattern = strdup(newval);
3266 if (NULL == new_pattern)
3268 edit_free_file(file);
3269 return JB_ERR_MEMORY;
3272 freez(cur_line->raw);
3273 freez(cur_line->unprocessed);
3274 cur_line->unprocessed = new_pattern;
3276 err = edit_write_file(file);
3279 /* Error writing file */
3280 edit_free_file(file);
3284 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3285 string_append(&target, file->identifier);
3287 edit_free_file(file);
3292 return JB_ERR_MEMORY;
3295 rsp->status = strdup("302 Local Redirect from Junkbuster");
3296 if (rsp->status == NULL)
3299 return JB_ERR_MEMORY;
3301 err = enlist_unique_header(rsp->headers, "Location", target);
3308 /*********************************************************************
3310 * Function : cgi_edit_actions_add_url
3312 * Description : CGI function that actually adds a URL pattern to
3316 * 1 : csp = Current client state (buffers, headers, etc...)
3317 * 2 : rsp = http_response data structure for output
3318 * 3 : parameters = map of cgi parameters
3321 * filename : Identifies the file to edit
3322 * ver : File's last-modified time
3323 * section : Line number of section to edit
3324 * newval : New pattern
3326 * Returns : JB_ERR_OK on success
3327 * JB_ERR_MEMORY on out-of-memory
3328 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3329 * specified or not valid.
3331 *********************************************************************/
3332 jb_err cgi_edit_actions_add_url(struct client_state *csp,
3333 struct http_response *rsp,
3334 const struct map *parameters)
3337 const char * newval;
3339 struct file_line * new_line;
3340 struct editable_file * file;
3341 struct file_line * cur_line;
3342 unsigned line_number;
3346 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3348 return cgi_error_disabled(csp, rsp);
3351 err = get_number_param(csp, parameters, "section", §ionid);
3357 newval = lookup(parameters, "newval");
3359 if ((*newval == '\0') || (sectionid < 1U))
3361 return JB_ERR_CGI_PARAMS;
3364 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3367 /* No filename specified, can't read file, modified, or out of memory. */
3368 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3372 cur_line = file->lines;
3374 while ((cur_line != NULL) && (line_number < sectionid))
3376 cur_line = cur_line->next;
3380 if ( (cur_line == NULL)
3381 || (cur_line->type != FILE_LINE_ACTION))
3383 /* Invalid "sectionid" parameter */
3384 edit_free_file(file);
3385 return JB_ERR_CGI_PARAMS;
3388 /* At this point, the section header is in cur_line - add after this. */
3390 new_pattern = strdup(newval);
3391 if (NULL == new_pattern)
3393 edit_free_file(file);
3394 return JB_ERR_MEMORY;
3397 /* Allocate the new line */
3398 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3399 if (new_line == NULL)
3402 edit_free_file(file);
3403 return JB_ERR_MEMORY;
3406 /* Fill in the data members of the new line */
3407 new_line->raw = NULL;
3408 new_line->prefix = NULL;
3409 new_line->unprocessed = new_pattern;
3410 new_line->type = FILE_LINE_URL;
3412 /* Link new_line into the list, after cur_line */
3413 new_line->next = cur_line->next;
3414 cur_line->next = new_line;
3416 /* Done making changes, now commit */
3418 err = edit_write_file(file);
3421 /* Error writing file */
3422 edit_free_file(file);
3426 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3427 string_append(&target, file->identifier);
3429 edit_free_file(file);
3434 return JB_ERR_MEMORY;
3437 rsp->status = strdup("302 Local Redirect from Junkbuster");
3438 if (rsp->status == NULL)
3441 return JB_ERR_MEMORY;
3443 err = enlist_unique_header(rsp->headers, "Location", target);
3450 /*********************************************************************
3452 * Function : cgi_edit_actions_remove_url
3454 * Description : CGI function that actually removes a URL pattern from
3458 * 1 : csp = Current client state (buffers, headers, etc...)
3459 * 2 : rsp = http_response data structure for output
3460 * 3 : parameters = map of cgi parameters
3463 * filename : Identifies the file to edit
3464 * ver : File's last-modified time
3465 * section : Line number of section to edit
3466 * pattern : Line number of pattern to edit
3468 * Returns : JB_ERR_OK on success
3469 * JB_ERR_MEMORY on out-of-memory
3470 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3471 * specified or not valid.
3473 *********************************************************************/
3474 jb_err cgi_edit_actions_remove_url(struct client_state *csp,
3475 struct http_response *rsp,
3476 const struct map *parameters)
3480 struct editable_file * file;
3481 struct file_line * cur_line;
3482 struct file_line * prev_line;
3483 unsigned line_number;
3487 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3489 return cgi_error_disabled(csp, rsp);
3492 err = get_number_param(csp, parameters, "section", §ionid);
3498 err = get_number_param(csp, parameters, "pattern", &patternid);
3505 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3508 /* No filename specified, can't read file, modified, or out of memory. */
3509 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3513 cur_line = file->lines;
3515 while ((cur_line != NULL) && (line_number < sectionid))
3517 cur_line = cur_line->next;
3521 if ( (cur_line == NULL)
3522 || (cur_line->type != FILE_LINE_ACTION))
3524 /* Invalid "sectionid" parameter */
3525 edit_free_file(file);
3526 return JB_ERR_CGI_PARAMS;
3530 while (line_number < patternid)
3532 prev_line = cur_line;
3533 cur_line = cur_line->next;
3536 if ( (cur_line == NULL)
3537 || ( (cur_line->type != FILE_LINE_URL)
3538 && (cur_line->type != FILE_LINE_BLANK) ) )
3540 /* Invalid "patternid" parameter */
3541 edit_free_file(file);
3542 return JB_ERR_CGI_PARAMS;
3546 if (cur_line->type != FILE_LINE_URL)
3548 /* Invalid "patternid" parameter */
3549 edit_free_file(file);
3550 return JB_ERR_CGI_PARAMS;
3555 /* At this point, the line to remove is in cur_line, and the previous
3556 * one is in prev_line
3559 /* Unlink cur_line */
3560 prev_line->next = cur_line->next;
3561 cur_line->next = NULL;
3564 edit_free_file_lines(cur_line);
3566 err = edit_write_file(file);
3569 /* Error writing file */
3570 edit_free_file(file);
3574 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3575 string_append(&target, file->identifier);
3577 edit_free_file(file);
3582 return JB_ERR_MEMORY;
3585 rsp->status = strdup("302 Local Redirect from Junkbuster");
3586 if (rsp->status == NULL)
3589 return JB_ERR_MEMORY;
3591 err = enlist_unique_header(rsp->headers, "Location", target);
3598 /*********************************************************************
3600 * Function : cgi_edit_actions_section_remove
3602 * Description : CGI function that actually removes a whole section from
3603 * the actions file. The section must be empty first
3604 * (else JB_ERR_CGI_PARAMS).
3607 * 1 : csp = Current client state (buffers, headers, etc...)
3608 * 2 : rsp = http_response data structure for output
3609 * 3 : parameters = map of cgi parameters
3612 * filename : Identifies the file to edit
3613 * ver : File's last-modified time
3614 * section : Line number of section to edit
3615 * pattern : Line number of pattern to edit
3617 * Returns : JB_ERR_OK on success
3618 * JB_ERR_MEMORY on out-of-memory
3619 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3620 * specified or not valid.
3622 *********************************************************************/
3623 jb_err cgi_edit_actions_section_remove(struct client_state *csp,
3624 struct http_response *rsp,
3625 const struct map *parameters)
3628 struct editable_file * file;
3629 struct file_line * cur_line;
3630 struct file_line * prev_line;
3631 unsigned line_number;
3635 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3637 return cgi_error_disabled(csp, rsp);
3640 err = get_number_param(csp, parameters, "section", §ionid);
3646 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3649 /* No filename specified, can't read file, modified, or out of memory. */
3650 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3654 cur_line = file->lines;
3657 while ((cur_line != NULL) && (line_number < sectionid))
3659 prev_line = cur_line;
3660 cur_line = cur_line->next;
3664 if ( (cur_line == NULL)
3665 || (cur_line->type != FILE_LINE_ACTION) )
3667 /* Invalid "sectionid" parameter */
3668 edit_free_file(file);
3669 return JB_ERR_CGI_PARAMS;
3672 if ( (cur_line->next != NULL)
3673 && (cur_line->next->type == FILE_LINE_URL) )
3675 /* Section not empty. */
3676 edit_free_file(file);
3677 return JB_ERR_CGI_PARAMS;
3680 /* At this point, the line to remove is in cur_line, and the previous
3681 * one is in prev_line
3684 /* Unlink cur_line */
3685 if (prev_line == NULL)
3687 /* Removing the first line from the file */
3688 file->lines = cur_line->next;
3692 prev_line->next = cur_line->next;
3694 cur_line->next = NULL;
3697 edit_free_file_lines(cur_line);
3699 err = edit_write_file(file);
3702 /* Error writing file */
3703 edit_free_file(file);
3707 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3708 string_append(&target, file->identifier);
3710 edit_free_file(file);
3715 return JB_ERR_MEMORY;
3718 rsp->status = strdup("302 Local Redirect from Junkbuster");
3719 if (rsp->status == NULL)
3722 return JB_ERR_MEMORY;
3724 err = enlist_unique_header(rsp->headers, "Location", target);
3731 /*********************************************************************
3733 * Function : cgi_edit_actions_section_add
3735 * Description : CGI function that adds a new empty section to
3739 * 1 : csp = Current client state (buffers, headers, etc...)
3740 * 2 : rsp = http_response data structure for output
3741 * 3 : parameters = map of cgi parameters
3744 * filename : Identifies the file to edit
3745 * ver : File's last-modified time
3746 * section : Line number of section to add after, 0 for start
3749 * Returns : JB_ERR_OK on success
3750 * JB_ERR_MEMORY on out-of-memory
3751 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3752 * specified or not valid.
3754 *********************************************************************/
3755 jb_err cgi_edit_actions_section_add(struct client_state *csp,
3756 struct http_response *rsp,
3757 const struct map *parameters)
3760 struct file_line * new_line;
3762 struct editable_file * file;
3763 struct file_line * cur_line;
3764 unsigned line_number;
3768 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3770 return cgi_error_disabled(csp, rsp);
3773 err = get_number_param(csp, parameters, "section", §ionid);
3779 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3782 /* No filename specified, can't read file, modified, or out of memory. */
3783 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3787 cur_line = file->lines;
3791 /* Add to start of file */
3792 if (cur_line != NULL)
3794 /* There's something in the file, find the line before the first
3797 while ( (cur_line->next != NULL)
3798 && (cur_line->next->type != FILE_LINE_ACTION) )
3800 cur_line = cur_line->next;
3807 /* Add after stated section. */
3808 while ((cur_line != NULL) && (line_number < sectionid))
3810 cur_line = cur_line->next;
3814 if ( (cur_line == NULL)
3815 || (cur_line->type != FILE_LINE_ACTION))
3817 /* Invalid "sectionid" parameter */
3818 edit_free_file(file);
3819 return JB_ERR_CGI_PARAMS;
3822 /* Skip through the section to find the last line in it. */
3823 while ( (cur_line->next != NULL)
3824 && (cur_line->next->type != FILE_LINE_ACTION) )
3826 cur_line = cur_line->next;
3831 /* At this point, the last line in the previous section is in cur_line
3832 * - add after this. (Or if we need to add as the first line, cur_line
3836 new_text = strdup("{}");
3837 if (NULL == new_text)
3839 edit_free_file(file);
3840 return JB_ERR_MEMORY;
3843 /* Allocate the new line */
3844 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3845 if (new_line == NULL)
3848 edit_free_file(file);
3849 return JB_ERR_MEMORY;
3852 /* Fill in the data members of the new line */
3853 new_line->raw = NULL;
3854 new_line->prefix = NULL;
3855 new_line->unprocessed = new_text;
3856 new_line->type = FILE_LINE_ACTION;
3858 if (cur_line != NULL)
3860 /* Link new_line into the list, after cur_line */
3861 new_line->next = cur_line->next;
3862 cur_line->next = new_line;
3866 /* Link new_line into the list, as first line */
3867 new_line->next = file->lines;
3868 file->lines = new_line;
3871 /* Done making changes, now commit */
3873 err = edit_write_file(file);
3876 /* Error writing file */
3877 edit_free_file(file);
3881 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3882 string_append(&target, file->identifier);
3884 edit_free_file(file);
3889 return JB_ERR_MEMORY;
3892 rsp->status = strdup("302 Local Redirect from Junkbuster");
3893 if (rsp->status == NULL)
3896 return JB_ERR_MEMORY;
3898 err = enlist_unique_header(rsp->headers, "Location", target);
3905 /*********************************************************************
3907 * Function : cgi_toggle
3909 * Description : CGI function that adds a new empty section to
3913 * 1 : csp = Current client state (buffers, headers, etc...)
3914 * 2 : rsp = http_response data structure for output
3915 * 3 : parameters = map of cgi parameters
3918 * set : If present, how to change toggle setting:
3919 * "enable", "disable", "toggle", or none (default).
3920 * mini : If present, use mini reply template.
3922 * Returns : JB_ERR_OK on success
3923 * JB_ERR_MEMORY on out-of-memory
3925 *********************************************************************/
3926 jb_err cgi_toggle(struct client_state *csp,
3927 struct http_response *rsp,
3928 const struct map *parameters)
3930 struct map *exports;
3932 const char *template_name;
3939 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_TOGGLE))
3941 return cgi_error_disabled(csp, rsp);
3944 if (NULL == (exports = default_exports(csp, "toggle")))
3946 return JB_ERR_MEMORY;
3949 mode = *(lookup(parameters, "set"));
3956 else if (mode == 'd')
3961 else if (mode == 't')
3964 g_bToggleIJB = !g_bToggleIJB;
3967 err = map_conditional(exports, "enabled", g_bToggleIJB);
3974 template_name = (*(lookup(parameters, "mini"))
3978 return template_fill_for_cgi(csp, template_name, exports, rsp);
3982 #endif /* def FEATURE_CGI_EDIT_ACTIONS */